/* Terminal Mixer - multi-point multi-user access to terminal applications Copyright (C) 2007 LluĂ­s Batlle i Rossell Please find the license in the provided COPYING file. */ #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #else #include #endif #include "main.h" struct winsize app_winsize; /* for xterm.c */ enum { STDIN, STDOUT, STDERR }; static void set_close_on_exec(int fd) { int res; res = fcntl(fd, F_SETFD, FD_CLOEXEC); if (res == -1) error("Set close-on-exec failed"); } void pass_winsize_to_slave() { int res; res = ioctl(0, TIOCGWINSZ, (char *) &app_winsize); if (res >= 0) ioctl(app_stdin, TIOCSWINSZ, (char *) &app_winsize); } static void give_terminal(int parent[], int child[]) { struct termios tios; struct winsize wsize; int res; int master, slave; res = tcgetattr(1, &tios); if (res == -1) error("tcgetatttr"); /* if starting using shells' &, it doesn't get ECHO set. * Let's force it. */ tios.c_lflag |= ECHO | ECHOE | ECHOK | ECHONL; res = ioctl(1, TIOCGWINSZ, &wsize); if (res == -1) error("ioctl TIOCGWINSZ"); app_winsize = wsize; res = openpty(&master, &slave, 0, &tios, &wsize); if (res == -1) error("openpty"); parent[STDIN] = master; parent[STDOUT] = master; parent[STDERR] = master; child[STDIN] = slave; child[STDOUT] = slave; child[STDERR] = slave; set_close_on_exec(master); set_close_on_exec(slave); } static void give_pipes(int parent[], int child[]) { int p_stdin[2]; /* from us to mpg321 */ int p_stdout[2]; /* from mpg321 to us */ int p_stderr[2]; /* from mpg321 to us, its stderr */ pipe(p_stdin); pipe(p_stdout); pipe(p_stderr); parent[STDIN] = p_stdin[1]; parent[STDOUT] = p_stdout[0]; parent[STDERR] = p_stderr[0]; child[STDIN] = p_stdin[0]; child[STDOUT] = p_stdout[1]; child[STDERR] = p_stderr[1]; set_close_on_exec(parent[STDIN]); set_close_on_exec(parent[STDOUT]); set_close_on_exec(parent[STDERR]); set_close_on_exec(child[STDIN]); set_close_on_exec(child[STDOUT]); set_close_on_exec(child[STDERR]); } int fork_app(char * const command[]) { int p_parent[3]; int p_child[3]; int pid; int res; if (command_line.s_param.run_in_subterminal) { give_terminal(p_parent, p_child); } else give_pipes(p_parent, p_child); /* globals */ app_stdin = p_parent[STDIN]; /* For us to write */ app_stdout = p_parent[STDOUT]; /* For us to read */ app_stderr = p_parent[STDERR]; /* For us to read */ pid = fork(); switch(pid) { case 0: /* child */ if (command_line.s_param.nohup) ignore_sighup(); res = dup2(p_child[STDIN], 0); if (res == -1) error("Dup2 stdin"); res = dup2(p_child[STDOUT], 1); if (res == -1) error("Dup2 stdout"); res = dup2(p_child[STDERR], 2); if (res == -1) error("Dup2 stderr"); if (command_line.s_param.run_in_subterminal) { int res; res = setsid(); if (res < 0) error("failed setsid()"); res = ioctl(0, TIOCSCTTY, 0); if (res < 0) error("failed ioctl(0,TIOCSCTTY,0)"); } execvp(command[0], command); error("Cannot execvp %s", command[0]); case -1: error("Failed fork"); default: /* parent */ close(p_child[STDIN]); close(p_child[STDOUT]); close(p_child[STDERR]); } return pid; }