Logo Search packages:      
Sourcecode: scigraphica version File versions  Download package

subshell.c

/*
 * Subprocess activation under a pseudo terminal support for zvtterm
 *
 * Copyright (C) 1994, 1995, 1998 Dugan Porter
 * Copyright (C) 1998 Michael Zucchi
 * Copyrithg (C) 1995, 1998 Miguel de Icaza
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of Version 2 of the GNU Library General Public
 * License, as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <config.h>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <stdlib.h>
#include <termios.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* GNU autoconf alloca incantation */
/* AIX requires this to be the first thing in the file.  */
#ifdef __GNUC__
# ifndef alloca
#  define alloca __builtin_alloca
# endif
#else
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
#   endif
#  endif
# endif
#endif

/* For PATH_MAX on BSD-like systems. */
#ifdef HAVE_SYS_SYSLIMITS_H
#include <sys/syslimits.h>
#endif


#include "subshell-includes.h"
#define ZVT_TERM_DO_UTMP_LOG 1
#define ZVT_TERM_DO_WTMP_LOG 2
#define ZVT_TERM_DO_LASTLOG  4

/* Pid of the helper SUID process */
static pid_t helper_pid;

/* Whether sigchld signal handler has been established yet */
static int sigchld_inited = 0;

/* Points to a possibly previously installed sigchld handler */
static struct sigaction old_sigchld_handler;

/* The socketpair used for the protocol */
int helper_socket_protocol  [2];

/* The paralell socketpair used to transfer file descriptors */
int helper_socket_fdpassing [2];

/* List of all subshells we're watching */
static GSList *children;

typedef struct {
      pid_t pid;
      int fd;
      int listen_fd;
      int exit_status;  /* waitpid return value */
      gboolean dead;          /* if the process is already dead  */
} child_info_t;

/*
 *  Fork the subshell, and set up many, many things.
 *
 */

static void
sigchld_handler (int signo)
{
      GSList *l;
      int status;

      if (waitpid (helper_pid, &status, WNOHANG) == helper_pid){
            helper_pid = 0;
            return;
      }

      for (l = children; l; l = l->next){
            child_info_t *child = l->data;
            
            if (waitpid (child->pid, &status, WNOHANG) == child->pid){
                  child->exit_status = status;
                  child->dead = 1;
                  write (child->fd, "D", 1);
                  return;
            }
      }
      /* No children of ours, chain */
      if (old_sigchld_handler.sa_handler)
            (*old_sigchld_handler.sa_handler)(signo);
}

#ifdef HAVE_SENDMSG
#include <sys/socket.h>
#include <sys/uio.h>

#ifdef HAVE_SYS_UN_H /* Linux libc5 */
#include <sys/un.h>
#endif

#ifndef CMSG_DATA /* Linux libc5 */
/* Ancillary data object manipulation macros.  */
#if !defined __STRICT_ANSI__ && defined __GNUC__ && __GNUC__ >= 2
# define CMSG_DATA(cmsg) ((cmsg)->cmsg_data)
#else
# define CMSG_DATA(cmsg) ((unsigned char *) ((struct cmsghdr *) (cmsg) + 1))
#endif
#endif /* CMSG_DATA */

static struct cmsghdr *cmptr;
#define CONTROLLEN  sizeof (struct cmsghdr) + sizeof (int)

static int
receive_fd (int helper_fd)
{
      struct iovec iov [1];
      struct msghdr msg;
      char buf [32];
      
      iov [0].iov_base = buf;
      iov [0].iov_len  = sizeof (buf);
      msg.msg_iov      = iov;
      msg.msg_iovlen   = 1;
      msg.msg_name     = NULL;
      msg.msg_namelen  = 0;

      if (cmptr == NULL && (cmptr = g_malloc (CONTROLLEN)) == NULL)
            return -1;
      msg.msg_control = (caddr_t) cmptr;
      msg.msg_controllen = CONTROLLEN;

      if (recvmsg (helper_fd, &msg, 0) <= 0)
            return -1;

      return *(int *) CMSG_DATA (cmptr);
}

static int
s_pipe (int fd [2])
{
      return socketpair (AF_UNIX, SOCK_STREAM, 0, fd);
}

#elif defined(__sgi) && !defined(HAVE_SENDMSG)

/* 
 * IRIX 6.2 is like 4.3BSD; it will not have HAVE_SENDMSG set, 
 * because msghdr used msg_accrights and msg_accrightslen rather 
 * than the newer msg_control and msg_controllen fields configure
 * checks.  The SVR4 code below doesn't work because pipe()
 * semantics are controlled by the svr3pipe systune variable, 
 * which defaults to uni-directional pipes.  Also sending
 * file descriptors through pipes isn't implemented.
 */

#include <sys/socket.h>
#include <sys/uio.h>

static int
receive_fd (int helper_fd)
{
      struct iovec iov [1];
      struct msghdr msg;
      char buf [32];
      int fd;

      iov [0].iov_base = buf;
      iov [0].iov_len  = sizeof (buf);
      msg.msg_iov      = iov;
      msg.msg_iovlen   = 1;
      msg.msg_name     = NULL;
      msg.msg_namelen  = 0;

      msg.msg_accrights = (caddr_t) &fd;
      msg.msg_accrightslen = sizeof(fd);

      if (recvmsg (helper_fd, &msg, 0) <= 0)
            return -1;

      return fd;
}

static int
s_pipe (int fd [2])
{
      return socketpair (AF_UNIX, SOCK_STREAM, 0, fd);
}

#else

static int
receive_fd (int helper_fd)
{
      struct strrecvfd recvfd;
      
      if (ioctl (helper_fd, I_RECVFD, &recvfd) < 0)
            return -1;

      return recvfd.fd;
}

static int
s_pipe (int fd [2])
{
      return pipe (fd);
}
#endif

static int
path_max (void)
{
#ifdef _PC_PATH_MAX
      return pathconf ("/", _PC_PATH_MAX);
#else
#  ifdef PATH_MAX
      return PATH_MAX;
#  else
      return 1024;
#  endif
#endif
}


static struct termios*
init_term_with_defaults(struct termios* term)
{
      /*
       *    openpty assumes POSIX termios so this should be portable.
       *    Don't change this to a structure init - POSIX doesn't say
       *    anything about field order.
       */
      memset(term, 0, sizeof(struct termios));

      term->c_iflag = 0
#ifdef BRKINT
        | BRKINT
#endif
#ifdef ICRNL
        | ICRNL
#endif
#ifdef IMAXBEL
        | IMAXBEL
#endif
#ifdef IXON
        | IXON
#endif
#ifdef IXANY
        | IXANY
#endif
        ;
      term->c_oflag = 0
#ifdef OPOST
        | OPOST
#endif
#ifdef ONLCR
        | ONLCR
#endif
#ifdef NL0
        | NL0
#endif
#ifdef CR0
        | CR0
#endif
#ifdef TAB0
        | TAB0
#endif
#ifdef BS0
        | BS0
#endif
#ifdef VT0
        | VT0
#endif
#ifdef FF0
        | FF0
#endif
        ;
      term->c_cflag = 0
#ifdef CREAD
        | CREAD
#endif
#ifdef CS8
        | CS8
#endif
#ifdef HUPCL
        | HUPCL
#endif
        ;
#ifdef EXTB
      cfsetispeed(term, EXTB);
      cfsetospeed(term, EXTB);
#else
#   ifdef B38400
        cfsetispeed(term, B38400);
        cfsetospeed(term, B38400);
#   else
#       ifdef B9600
        cfsetispeed(term, B9600);
        cfsetospeed(term, B9600);
#       endif
#   endif
#endif /* EXTB */

      term->c_lflag = 0
#ifdef ECHO
        | ECHO
#endif
#ifdef ICANON
        | ICANON
#endif
#ifdef ISIG
        | ISIG
#endif
#ifdef IEXTEN
        | IEXTEN
#endif
#ifdef ECHOE
        | ECHOE
#endif
#ifdef ECHOKE
        | ECHOKE
#endif
#ifdef ECHOCTL
        | ECHOCTL
#endif
        ;

#ifdef N_TTY
      /* should really be a check for c_line, but maybe this is good enough */
      term->c_line = N_TTY;
#endif

      /* These two may overlap so set them first */
      /* That setup means, that read() will be blocked untill */
      /* at least 1 symbol will be read.                      */
      term->c_cc[VMIN]  = 1;
      term->c_cc[VTIME] = 0;
      
      /*
       * Now set the characters. This is of course a religious matter
       * but we use the defaults, with erase bound to the key gnome-terminal
       * maps.
       *
       * These are the ones set by "stty sane".
       */
         
      term->c_cc[VINTR]  = 'C'-64;
      term->c_cc[VQUIT]  = '\\'-64;
      term->c_cc[VERASE] = 127;
      term->c_cc[VKILL]  = 'U'-64;
      term->c_cc[VEOF]   = 'D'-64;
#ifdef VSWTC
      term->c_cc[VSWTC]  = 255;
#endif
      term->c_cc[VSTART] = 'Q'-64;
      term->c_cc[VSTOP]  = 'S'-64;
      term->c_cc[VSUSP]  = 'Z'-64;
      term->c_cc[VEOL]   = 255;
      
      /*
       *    Extended stuff.
       */
       
#ifdef VREPRINT   
      term->c_cc[VREPRINT] = 'R'-64;
#endif
#ifdef VSTATUS
      term->c_cc[VSTATUS]  = 'T'-64;
#endif
#ifdef VDISCARD   
      term->c_cc[VDISCARD] = 'O'-64;
#endif
#ifdef VWERASE
      term->c_cc[VWERASE]  = 'W'-64;
#endif      
#ifdef VLNEXT
      term->c_cc[VLNEXT]   = 'V'-64;
#endif
#ifdef VDSUSP
      term->c_cc[VDSUSP]   = 'Y'-64;
#endif
#ifdef VEOL2      
      term->c_cc[VEOL2]    = 255;
#endif
    return(term);
}

static void *
get_ptys (int *master, int *slave, int update_wutmp)
{
      GnomePtyOps op;
      int result, n;
      void *tag;
      struct termios term;    
        char *term_name;

/*
//    *master = receive_fd (helper_socket_fdpassing [0]);
//    *slave  = receive_fd (helper_socket_fdpassing [0]);
*/

        term_name = ((char *) alloca (path_max())) + 1;
      init_term_with_defaults(&term);
      openpty (master, slave, term_name, &term, NULL);
        tag=master;
      return tag;
}

/**
 * zvt_init_subshell:
 * @vt:       the terminal emulator object.
 * @pty_name: Name of the pseudo terminal opened.
 * @log:      if TRUE, then utmp/wtmp records are updated
 *
 * Returns the pid of the subprocess.
 */
int
zvt_init_subshell (struct vt_em *vt, char *pty_name, int log)
{
      int slave_pty, master_pty;
      struct sigaction sa;
      child_info_t *child;
      pid_t pid;
      int status;
      int p[2];

      g_return_val_if_fail (vt != NULL, -1);

      if (!sigchld_inited){
            sigset_t sigset;

            sigemptyset(&sigset);
            sigaddset(&sigset, SIGPIPE);
            sigaddset(&sigset, SIGCHLD);
            sigprocmask(SIG_UNBLOCK, &sigset, NULL);

            memset(&sa, 0, sizeof(sa));
            sa.sa_handler = sigchld_handler;
            sigaction (SIGCHLD, &sa, &old_sigchld_handler);
            sigchld_inited = 1;
      }

      if ((vt->pty_tag = get_ptys (&master_pty, &slave_pty, log)) == NULL)
            return -1;

      /* Fork the subshell */

      vt->childpid = fork ();
      
      if (vt->childpid == -1)
            return -1;

      if (vt->childpid == 0)  /* We are in the child process */
      {
            close (master_pty);
            login_tty (slave_pty);

            /*
             * Reset the child signal handlers
             */
            signal (SIGINT,  SIG_DFL);
            signal (SIGQUIT, SIG_DFL);
            signal (SIGCHLD, SIG_DFL);
            signal (SIGPIPE, SIG_DFL);
            signal (SIGHUP,  SIG_DFL);
            
            /*
             * These should be turned off.  Login does turn them off
             * If the user shells supports these, they will be turned
             * back on
             */
            signal (SIGTSTP, SIG_IGN);
            signal (SIGTTIN, SIG_IGN);
            signal (SIGTTOU, SIG_IGN);
      } else {
            close (slave_pty);

            pipe(p);

            vt->msgfd = p [0];
            
            child = g_new (child_info_t, 1);
            child->pid = vt->childpid;
            child->fd = p[1];
            child->listen_fd = p [0];
            child->dead = 0;
            child->exit_status = 0;
            children = g_slist_prepend (children, child);
            
            /* We could have received the SIGCHLD signal for the subshell 
             * before installing the init_sigchld
             */
            pid = waitpid (vt->childpid, &status, WUNTRACED | WNOHANG);
            if (pid == vt->childpid && child->pid >= 0){
                  child->pid = 0;
                  write (child->fd, "D", 1);
                  return -1;
            }
            
            vt->keyfd = vt->childfd = master_pty;

      }
      
      return vt->childpid;
}

int zvt_resize_subshell (int fd, int col, int row, int xpixel, int ypixel)
{
#if defined TIOCSWINSZ && !defined SCO_FLAVOR
    struct winsize tty_size;

    tty_size.ws_row = row;
    tty_size.ws_col = col;
    tty_size.ws_xpixel = xpixel;
    tty_size.ws_ypixel = ypixel;

    return (ioctl (fd, TIOCSWINSZ, &tty_size));
#endif
}

/**
 * zvt_shutdown_subsheel:
 * @vt: The terminal emulator object
 * 
 * Shuts down the subshell process.
 *
 * Return value: The exit status of the process, or -1 on error.
 **/
int
zvt_shutdown_subshell (struct vt_em *vt)
{
      GnomePtyOps op;
      GSList *l;

      g_return_val_if_fail (vt != NULL, -1);

      /* shutdown pty through helper */
      if (vt->pty_tag) {
            op = GNOME_PTY_CLOSE_PTY;

            write (helper_socket_protocol [0], &op, sizeof (op));
            write (helper_socket_protocol [0], &vt->pty_tag, sizeof (vt->pty_tag));
            vt->pty_tag = NULL;
      }

      /* close the child comms link(s) */
      close(vt->childfd);
      if (vt->keyfd != vt->childfd)
            close(vt->keyfd);
      vt->msgfd = vt->childfd = -1;

      /* remove the child node */
      for (l = children; l; l = l->next){
            child_info_t *child = l->data;

            if (child->pid == vt->childpid){
                  int status;
                  
                  if (!child->dead){
                        /* make sure the child has quit! */
                        kill (vt->childpid, SIGHUP);
                        waitpid (vt->childpid, &child->exit_status, 0);
                  }
                  
                  status = child->exit_status;
                  close (child->fd);
                  g_free (child);
                  children = g_slist_remove (children, l->data);

                  return WEXITSTATUS(status);
            }
      }

      /* unknown child? */
      return -1;
}

Generated by  Doxygen 1.6.0   Back to index