vshost-util-vserver

Build script and sources for util-vserver.
git clone https://ccx.te2000.cz/git/vshost-util-vserver
Log | Files | Refs

vlogin.c (6362B)


      1 // $Id$
      2 
      3 // Copyright (C) 2006 Benedikt Böhm <hollow@gentoo.org>
      4 // Based on vserver-utils' vlogin program.
      5 //  
      6 // This program is free software; you can redistribute it and/or modify
      7 // it under the terms of the GNU General Public License as published by
      8 // the Free Software Foundation; version 2 of the License.
      9 //  
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU General Public License for more details.
     14 //  
     15 // You should have received a copy of the GNU General Public License
     16 // along with this program; if not, write to the Free Software
     17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     18 
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #  include <config.h>
     22 #endif
     23 
     24 #include "util.h"
     25 #include <lib/vserver.h>
     26 #include <lib/fmt.h>
     27 
     28 #include <stdlib.h>
     29 #include <getopt.h>
     30 #include <stdint.h>
     31 #include <string.h>
     32 #include <errno.h>
     33 #include <sys/stat.h>
     34 #include <sys/ioctl.h>
     35 #include <sys/wait.h>
     36 #include <sys/socket.h>
     37 #include <termios.h>
     38 #include <signal.h>
     39 #include <pty.h>
     40 #include <fcntl.h>
     41 
     42 #define ENSC_WRAPPERS_PREFIX	"vlogin: "
     43 #define ENSC_WRAPPERS_IOCTL	1
     44 #define ENSC_WRAPPERS_UNISTD	1
     45 #define ENSC_WRAPPERS_SOCKET	1
     46 #define ENSC_WRAPPERS_IO	1
     47 #define ENSC_WRAPPERS_TERMIOS	1
     48 #define ENSC_WRAPPERS_FCNTL	1
     49 #include <wrappers.h>
     50 
     51 struct terminal {
     52   int fd;                           /* terminal file descriptor */
     53   struct termios term;              /* terminal settings */
     54   struct winsize ws;                /* terminal size */
     55   pid_t pid;                        /* terminal process id */
     56   struct termios termo;             /* original terminal settings */
     57   enum { TS_RESET, TS_RAW } state;  /* terminal state */
     58 };
     59 
     60 static struct terminal t;
     61 extern int wrapper_exit_code;
     62 
     63 /* set terminal to raw mode */
     64 static void
     65 terminal_raw(void)
     66 {
     67   struct termios buf;
     68 
     69   /* save original terminal settings */
     70   Etcgetattr(STDIN_FILENO, &t.termo);
     71 
     72   buf = t.termo;
     73   
     74   /* convert terminal settings to raw mode */
     75   cfmakeraw(&buf);
     76 
     77   /* apply raw terminal settings */
     78   Etcsetattr(STDIN_FILENO, TCSAFLUSH, &buf);
     79 
     80   t.state = TS_RAW;
     81 }
     82 
     83 /* reset terminal to original state */
     84 static void
     85 terminal_reset(void)
     86 {
     87   if (t.state != TS_RAW)
     88     return;
     89 
     90   Etcsetattr(STDIN_FILENO, TCSAFLUSH, &t.termo);
     91 
     92   t.state = TS_RESET;
     93 }
     94 
     95 /* send signal to terminal */
     96 static void
     97 terminal_kill(int sig)
     98 {
     99   pid_t pgrp = -1;
    100 
    101   /* try to get process group leader */
    102   if (ioctl(t.fd, TIOCGPGRP, &pgrp) >= 0 &&
    103       pgrp != -1 &&
    104       kill(-pgrp, sig) != -1)
    105     return;
    106 
    107   /* fallback using terminal pid */
    108   kill(-t.pid, sig);
    109 }
    110 
    111 /* redraw the terminal screen */
    112 static void
    113 terminal_redraw(void)
    114 {
    115   /* get winsize from stdin */
    116   if (ioctl(STDIN_FILENO, TIOCGWINSZ, &t.ws) == -1)
    117     return;
    118 
    119   /* set winsize in terminal */
    120   ioctl(t.fd, TIOCSWINSZ, &t.ws);
    121 
    122   /* set winsize change signal to terminal */
    123   terminal_kill(SIGWINCH);
    124 }
    125 
    126 /* copy terminal activities */
    127 static ssize_t
    128 terminal_copy(int src, int dst)
    129 {
    130   char buf[64];
    131   ssize_t len;
    132 
    133   /* read terminal activity */
    134   len = read(src, buf, sizeof(buf));
    135   if (len == -1 && errno != EINTR) {
    136     perror("read()");
    137     terminal_kill(SIGTERM);
    138     exit(1);
    139   } else if (len == -1)
    140     return -1;
    141 
    142   /* write activity to user */
    143   EwriteAll(dst, buf, len);
    144 
    145   return len;
    146 }
    147 
    148 /* shuffle all output, and reset the terminal */
    149 static void
    150 terminal_end(void)
    151 {
    152   char buf[64];
    153   ssize_t len;
    154   long options;
    155 
    156   options = Efcntl(t.fd, F_GETFL, 0) | O_NONBLOCK;
    157   Efcntl(t.fd, F_SETFL, options);
    158   for (;;) {
    159     len = read(t.fd, buf, sizeof(buf));
    160     if (len == 0 || len == -1)
    161       break;
    162     EwriteAll(STDOUT_FILENO, buf, len);
    163   }
    164 
    165   /* in case atexit hasn't been setup yet */
    166   terminal_reset();
    167 }
    168 
    169 /* catch signals */
    170 static void
    171 signal_handler(int sig)
    172 {
    173   int status;
    174 
    175   switch(sig) {
    176     /* catch interrupt */
    177     case SIGINT:
    178       terminal_kill(sig);
    179       break;
    180 
    181     /* terminal died */
    182     case SIGCHLD:
    183       terminal_end();
    184       wait(&status);
    185       exit(WEXITSTATUS(status));
    186       break;
    187 
    188     /* window size has changed */
    189     case SIGWINCH:
    190       terminal_redraw();
    191       break;
    192 
    193     default:
    194       exit(0);
    195   }
    196 
    197 }
    198 
    199 void do_vlogin(int argc, char *argv[], int ind)
    200 {
    201   int slave;
    202   pid_t pid;
    203   int n, i;
    204   fd_set rfds;
    205 
    206   if (!isatty(0) || !isatty(1)) {
    207     execvp(argv[ind], argv+ind);
    208     return;
    209   }
    210 
    211   /* set terminal to raw mode */
    212   terminal_raw();
    213 
    214   /* reset terminal to its original mode */
    215   atexit(terminal_reset);
    216 
    217   /* fork new pseudo terminal */
    218   if (openpty(&t.fd, &slave, NULL, NULL, NULL) == -1) {
    219     perror(ENSC_WRAPPERS_PREFIX "openpty()");
    220     exit(EXIT_FAILURE);
    221   }
    222 
    223   /* setup SIGCHLD here, so we're sure to get the signal */
    224   signal(SIGCHLD, signal_handler);
    225 
    226   pid = Efork();
    227 
    228   if (pid == 0) {
    229     /* we don't need the master side of the terminal */
    230     close(t.fd);
    231 
    232     /* login_tty() stupid dietlibc doesn't have it */
    233     Esetsid();
    234 
    235     Eioctl(slave, TIOCSCTTY, NULL);
    236 
    237     Edup2(slave, 0);
    238     Edup2(slave, 1);
    239     Edup2(slave, 2);
    240 
    241     if (slave > 2)
    242       close(slave);
    243 
    244     Eexecvp(argv[ind], argv+ind);
    245   }
    246 
    247   /* setup SIGINT and SIGWINCH here, as they can cause loops in the child */
    248   signal(SIGWINCH, signal_handler);
    249   signal(SIGINT, signal_handler);
    250 
    251   /* save terminals pid */
    252   t.pid = pid;
    253 
    254   /* set process title for ps */
    255   n = strlen(argv[0]);
    256 
    257   for (i = 0; i < argc; i++)
    258     memset(argv[i], '\0', strlen(argv[i]));
    259 
    260   strncpy(argv[0], "login", n);
    261 
    262   /* we want a redraw */
    263   terminal_redraw();
    264 
    265   /* main loop */
    266   for (;;) {
    267     /* init file descriptors for select */
    268     FD_ZERO(&rfds);
    269     FD_SET(STDIN_FILENO, &rfds);
    270     FD_SET(t.fd, &rfds);
    271     n = t.fd;
    272 
    273     /* wait for something to happen */
    274     while (select(n + 1, &rfds, NULL, NULL, NULL) == -1) {
    275       if (errno == EINTR || errno == EAGAIN)
    276 	continue;
    277       perror(ENSC_WRAPPERS_PREFIX "select()");
    278       exit(wrapper_exit_code);
    279     }
    280 
    281     if (FD_ISSET(STDIN_FILENO, &rfds)) {
    282       /* EOF */
    283       if (terminal_copy(STDIN_FILENO, t.fd) == 0) {
    284 	terminal_kill(SIGHUP);
    285 	exit(0);
    286       }
    287     }
    288 
    289     if (FD_ISSET(t.fd, &rfds)) {
    290       /* EOF */
    291       if (terminal_copy(t.fd, STDOUT_FILENO) == 0) {
    292 	terminal_kill(SIGHUP);
    293 	exit(0);
    294       }
    295     }
    296   }
    297 
    298   /* never get here, signal handler exits */
    299 }