Files
haiku-beta6/src/apps/terminal/MYOB/spawn.cpp
T
Axel Dörfler f87938dc2e It's now using setenv() under Haiku instead of its own stuff - and that will
now mimic setenv() instead of some strangely different thing :)
Rearranged and corrected the code that opens the PTY, and TTY.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13478 a95241bf-73f2-0310-859d-f6bbb57e9c96
2005-07-06 03:30:19 +00:00

463 lines
11 KiB
C++

/*
* Copyright (c) 2003-4 Kian Duffy <[email protected]>
* Copyright (c) 2004 Daniel Furrer <[email protected]>
* Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files or portions
* thereof (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice
* in the binary, as well as this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <dirent.h>
#include <sys/param.h>
#include <sys/file.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <kernel/OS.h>
#include "TermConst.h"
#include "spawn.h"
#include "PrefHandler.h"
extern PrefHandler *gTermPref;
/* default shell command and options. */
#define SHELL_COMMAND "/bin/sh -login"
extern char **environ;
char spawn_alert_msg [] = \
"alert --stop " \
"'Haiku Terminal Error!!\n\
Cannot execute shell [%s].\n\
Check \"Shell\" in preferance panel.' "\
"'Exec /bin/sh' 'Abort'";
/*
* Set environment varriable.
*/
#ifndef __HAIKU__
int
setenv(const char *var, const char *value, bool overwrite)
{
int envindex = 0;
const int len = strlen(var);
const int val_len = strlen (value);
while (environ[envindex] != NULL) {
if (!strncmp(environ[envindex], var, len)) {
/* found it */
if (overwrite) {
environ[envindex] = (char *)malloc((unsigned)len + val_len + 2);
sprintf(environ[envindex], "%s=%s", var, value);
}
return 0;
}
envindex++;
}
environ[envindex] = (char *)malloc((unsigned)len + val_len + 2);
sprintf(environ[envindex], "%s=%s", var, value);
environ[++envindex] = NULL;
return 0;
}
#endif
/*
* reapchild. Child process is out there, let's catch its terminaltion.
*/
/*
* spawn_shell(): spawn child process, create pty master/slave device and
* execute SHELL program.
*/
/* This pipe use handshake on exec. */
int pc_pipe[2]; /* this pipe is used for parent to child transfer */
int cp_pipe[2]; /* this pipe is used for child to parent transfer */
/* handshake interface */
typedef struct
{
int status; /* status of child */
char msg[256]; /* error message */
int row; /* terminal rows */
int col; /* Terminal columns */
} handshake_t;
/* status of handshake */
#define PTY_OK 0 /* pty open and set termios OK */
#define PTY_NG 1 /* pty open or set termios NG */
#define PTY_WS 2 /* pty need WINSIZE (row and col ) */
/* global varriables */
pid_t sh_pid;
char tty_name[B_PATH_NAME_LENGTH];
int
spawn_shell(int row, int col, const char *command, const char *coding)
{
int done = 0;
pid_t pgrp;
int master = -1;
int slave;
struct termios tio;
struct winsize ws;
handshake_t handshake;
int i = 0;
time_t now_time_t;
struct tm *now_time;
int now_hour;
char *args[16];
char com_line[256];
char *ptr;
char err_msg[256];
signal(SIGTTOU, SIG_IGN);
/*
* Get a psuedo-tty. We do this by cycling through files in the
* directory. The oparationg system will not allow us to open a master
* which is already in use, so we simply go until the open succeeds.
*/
DIR *dir = opendir("/dev/pt/");
if (dir != NULL) {
struct dirent *dirEntry;
while ((dirEntry = readdir(dir)) != NULL) {
// skip '.' and '..'
if (dirEntry->d_name[0] == '.')
continue;
char ptyName[B_PATH_NAME_LENGTH];
sprintf(ptyName, "/dev/pt/%s", dirEntry->d_name);
master = open(ptyName, O_RDWR);
if (master >= 0) {
// Set the tty that corresponds to the pty we found
sprintf(tty_name, "/dev/tt/%s", dirEntry->d_name);
break;
} else {
fprintf(stderr, "could not open %s: %s\n", ptyName, strerror(errno));
}
}
closedir(dir);
}
// If master is still < 0 then we haven't found a tty we can use
if (master < 0) {
printf("didn't find any available pesudo ttys.");
return -1;
}
/*
* Get the modes of the current terminal. We will duplicates these
* on the pseudo terminal.
*/
/* Create pipe that be use to handshake */
if (pipe(pc_pipe) || pipe(cp_pipe)) {
printf ("Could not create handshake pipe.");
return -1;
}
/* Fork a child process. */
if ((sh_pid = fork()) < 0) {
close(master);
return -1;
}
if (sh_pid == 0) {
// Now in child process.
/*
* Make our controlling tty the pseudo tty. This hapens because
* we cleared our original controlling terminal above.
*/
// ToDo: why two of them in the first place?
close(cp_pipe[0]);
close(pc_pipe[1]);
/* Set process session leader */
if ((pgrp = setsid()) < 0) {
handshake.status = PTY_NG;
sprintf(handshake.msg, "could not set session leader.");
write(cp_pipe[1], (char *)&handshake, sizeof (handshake));
exit(1);
}
/* change pty owner and asscess mode. */
chown(tty_name, getuid(), getgid());
chmod(tty_name, S_IRUSR | S_IWUSR);
/* open slave pty */
if ((slave = open(tty_name, O_RDWR)) < 0) {
handshake.status = PTY_NG;
sprintf(handshake.msg, "can't open tty (%s).", tty_name);
write(cp_pipe[1], (char *)&handshake, sizeof (handshake));
exit(1);
}
/* get tty termios (don't necessary). */
tcgetattr(slave, &tio);
/* set signal default */
signal(SIGCHLD, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
/*
* Set Terminal interface.
*/
tio.c_line = 0;
tio.c_lflag |= ECHOE;
/* input: nl->nl, cr->nl */
tio.c_iflag &= ~(INLCR|IGNCR);
tio.c_iflag |= ICRNL;
tio.c_iflag &= ~ISTRIP;
/* output: cr->cr, nl in not retrun, no delays, ln->cr/ln */
tio.c_oflag &= ~(OCRNL|ONLRET|NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
tio.c_oflag |= ONLCR;
tio.c_oflag |= OPOST;
/* baud rate is 19200 (equal beterm) */
tio.c_cflag &= ~(CBAUD);
tio.c_cflag |= B19200;
tio.c_cflag &= ~CSIZE;
tio.c_cflag |= CS8;
tio.c_cflag |= CREAD;
tio.c_cflag |= HUPCL;
tio.c_iflag &= ~(IGNBRK|BRKINT);
/*
* enable signals, canonical processing (erase, kill, etc), echo.
*/
tio.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHONL;
tio.c_lflag &= ~(ECHOK | IEXTEN);
/* set control charactors. */
tio.c_cc[VINTR] = 'C' & 0x1f; /* '^C' */
tio.c_cc[VQUIT] = CQUIT; /* '^\' */
tio.c_cc[VERASE] = 0x08; /* '^H' */
tio.c_cc[VKILL] = 'U' & 0x1f; /* '^U' */
tio.c_cc[VEOF] = CEOF; /* '^D' */
tio.c_cc[VEOL] = CEOL; /* '^@' */
tio.c_cc[VMIN] = 4;
tio.c_cc[VTIME] = 0;
tio.c_cc[VEOL2] = CEOL; /* '^@' */
tio.c_cc[VSWTCH] = CSWTCH; /* '^@' */
tio.c_cc[VSTART] = CSTART; /* '^S' */
tio.c_cc[VSTOP] = CSTOP; /* '^Q' */
tio.c_cc[VSUSP] = CSUSP; /* '^@' */
/*
* change control tty.
*/
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
/* close old slave fd. */
if (slave > 2)
close(slave);
/*
* set terminal interface.
*/
if (tcsetattr (0, TCSANOW, &tio) == -1) {
handshake.status = PTY_NG;
sprintf(handshake.msg, "failed set terminal interface (TERMIOS).");
write(cp_pipe[1], (char *)&handshake, sizeof (handshake));
exit(1);
}
/*
* set window size.
*/
handshake.status = PTY_WS;
write(cp_pipe[1], (char *)&handshake, sizeof (handshake));
read(pc_pipe[0], (char *)&handshake, sizeof (handshake));
if (handshake.status != PTY_WS) {
handshake.status = PTY_NG;
sprintf(handshake.msg, "missmatch handshake.");
write(cp_pipe[1], (char *)&handshake, sizeof (handshake));
exit(1);
}
ws.ws_row = handshake.row;
ws.ws_col = handshake.col;
ioctl(0, TIOCSWINSZ, &ws);
/*
* Set process group ID to process, and Terminal Process group ID
* to this process group ID (equal process ID).
*/
pgrp = getpid();
setpgid(pgrp, pgrp);
tcsetpgrp(0, pgrp);
/* mark the pipes as close on exec */
fcntl(cp_pipe[1], F_SETFD, 1);
fcntl(pc_pipe[0], F_SETFD, 1);
/* pty open and set termios successful. */
handshake.status = PTY_OK;
write(cp_pipe[1], (char *)&handshake, sizeof (handshake));
/*
* setenv TERM and TTY.
*/
setenv("TERM", "xterm", true);
setenv("TTY", tty_name, true);
setenv("TTYPE", coding, true);
/*
* If don't set command args, exec SHELL_COMMAND.
*/
if (command == NULL)
command = SHELL_COMMAND;
memcpy (com_line, command, 256);
ptr = com_line;
/*
* split up the arguments in the command into an artv-like structure.
*/
while (*ptr) {
/* Skip white space */
while ((*ptr == ' ') || (*ptr == '\t'))
*ptr++ = 0;
args[i++] = ptr;
/* Skip over this word to next white space. */
while ((*ptr != 0) && (*ptr != ' ') && (*ptr != '\t'))
ptr++;
}
args[i] = NULL;
setenv("SHELL", *args, true);
/*
* Print Welcome Message.
* (But, Only print message when MuTerminal coding is UTF8.)
*/
now_time_t = time(NULL);
now_time = localtime (&now_time_t);
if (now_time->tm_hour >= 5 && now_time->tm_hour < 11) {
now_hour = 0;
} else if (now_time->tm_hour >= 11 && now_time->tm_hour <= 18 ) {
now_hour = 1;
} else {
now_hour = 2;
}
execve(*args, args, environ);
/*
* Exec failed.
*/
sleep(1);
sprintf(err_msg, spawn_alert_msg, com_line);
if (system(err_msg) == 0)
execl("/bin/sh", "/bin/sh", "-login", NULL);
exit(1);
}
/*
* In parent Process, Set up the input and output file pointers so
* that they can write and read the pseudo terminal.
*/
/* close childs's side of the pipe */
close(cp_pipe[1]);
close(pc_pipe[0]);
/*
* close parent control tty.
*/
while (!done) {
read (cp_pipe[0], (char *)&handshake, sizeof (handshake));
switch (handshake.status) {
case PTY_OK:
done = 1;
break;
case PTY_NG:
printf("%s\n", handshake.msg);
done = -1;
break;
case PTY_WS:
handshake.row = row;
handshake.col = col;
handshake.status = PTY_WS;
write(pc_pipe[1], (char *)&handshake, sizeof (handshake));
break;
}
}
return (done > 0) ? master : -1;
}