app_term.c
author viric@mandarina
Mon, 28 Apr 2008 21:37:25 +0200
changeset 90 f172b95795d8
parent 82 5cbe47923060
permissions -rw-r--r--
Moving to version 0.4.1

/*
    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 <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <termios.h>
#ifdef __APPLE__
  #include <util.h>
#else
  #include <pty.h>
#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);
}

void get_term_options(struct termios *tios, struct winsize *wsize)
{
    int res;

    /* Try to get the config from the actual terminal's stdout */
    res = tcgetattr(1, tios);
    if (res == -1)
    {
      /* Create the termios structure from scratch */
      memset(tios, 0, sizeof(*tios));
      cfmakeraw(tios);
    }

    /* if starting using shells' &, it doesn't get ECHO set.
     * Let's force it. */
    tios->c_lflag |= ECHO | ECHOE | ECHOK | ECHONL;
    /* Try to emulate something compatible with xterm */
    tios->c_oflag |= OPOST | ONLCR;
    tios->c_lflag |= ISIG | ICANON | ECHOCTL | ECHOKE;
    tios->c_iflag |= BRKINT | IGNPAR | IMAXBEL;

    /* Get the original term's winsize */
    res = ioctl(1, TIOCGWINSZ, wsize);
    if (res == -1)
    {
      /* If we don't have original term, create an arbitrary winsize */
      wsize->ws_row = 25;
      wsize->ws_col = 80;
    }
}

static void give_terminal(int parent[], int child[])
{
    struct termios tios;
    struct winsize wsize;
    int res;
    int master, slave;

    get_term_options(&tios, &wsize);

    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;
}