home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume22 / pty / part02 / master.c < prev    next >
C/C++ Source or Header  |  1990-10-09  |  18KB  |  712 lines

  1. /* Copyright 1990, Daniel J. Bernstein. All rights reserved. */
  2.  
  3. #include <sys/types.h>
  4. #include <sys/time.h>
  5. #include <sys/resource.h>
  6. #include <sys/wait.h>
  7. #include <stdio.h>
  8. #include "err.h"
  9. #include "config.h"
  10. #include "pty.h"
  11. #include "master.h"
  12. #include "sig.h"
  13. #include "tty.h"
  14. #include "file.h"
  15. #include "sock.h"
  16. #include "logs.h"
  17. #include "misc.h"
  18.  
  19. static char fnre[20];
  20.  
  21. static char fnsess[20];
  22. static int fdsess;
  23.  
  24. static char *glfnsty;
  25.  
  26. static char soutbuf[OUTBUFSIZE];
  27. static char sptybuf[OUTBUFSIZE];
  28.  
  29. static struct ttymodes tmowinpty;
  30. static struct ttymodes tmowintty;
  31.  
  32. static char *outbuf = soutbuf;
  33. static int outbufsiz = OUTBUFSIZE;
  34. static int outbuflen = 0;
  35. static char *ptybuf = sptybuf;
  36. static int ptybufsiz = OUTBUFSIZE;
  37. static int ptybuflen = 0;
  38.  
  39. static int flagconnected = 1; /* 0: disconnected. 2: idling for stop. */
  40.                               /* 3: idling for stop but child is dead. */
  41. static int flagchild = 1; /* 0: dead. 2: stopped. */
  42. static int childsig; /* signal that stopped/killed child */
  43. static int flagsigler = 1; /* 0: dead. */
  44. static int siglerpid; /* only defined if flagconnected */
  45. static int slavepid;
  46.  
  47. static int flagqwinch = 0;
  48.  
  49. static void quickdeath(i)
  50. int i;
  51. {
  52.  /* All exits from master() go through here. */
  53.  if (flagsession) (void) unlink(fnsess);
  54.  if (flagxchown)
  55.    (void) fchown(fdsty,PTYOWNER,PTYGROUP);
  56.  (void) fchmod(fdsty,UNUSEDPTYMODE);
  57.  date = now();
  58.  if (flagxutmp)
  59.    if (utmp(glfnsty + PTYUTMP_OFFSET,"","",date) == -1)
  60.      ; /* too bad. */
  61.  if (flagxwtmp)
  62.    if (wtmp(glfnsty + PTYWTMP_OFFSET,"","",date) == -1)
  63.      ; /* too bad. */
  64.  fatal(i);
  65. }
  66.  
  67. static void death(i)
  68. int i;
  69. {
  70.  (void) kill(siglerpid,SIGTERM);
  71.  /* XXX: should wait while flagsigler */
  72.  quickdeath(i);
  73. }
  74.  
  75. /*ARGSUSED*/
  76. static void sig_force(i)
  77. sig_num i;
  78. {
  79.  /* Forced death, presumably from the sesskill program. */
  80.  sig_ignore(SIGCHLD);
  81.  /* XXX: Should we test for !flagchild here? sesskill does. */
  82.  flagchild = 0;
  83.  quickdeath(SIGCHLD);
  84. }
  85.  
  86. /*ARGSUSED*/
  87. static void sig_usr2(i)
  88. sig_num i;
  89. {
  90.  if (flagsession)
  91.   {
  92.    int newuid = uid;
  93.    char newsuid[10];
  94.    char foo[100];
  95.  
  96.    /* XXX: We should have some error recovery here! */
  97.  
  98.    (void) lseek(fdsess,(long) 0,0);
  99.    (void) read(fdsess,(char *) &newuid,sizeof(int));
  100.    (void) sprintf(newsuid,"%d",newuid);
  101.  
  102.    (void) chdir("..");
  103.    if (chdir(newsuid) == -1)
  104.     {
  105.      (void) mkdir(newsuid,0700);
  106.      (void) chdir(newsuid);
  107.     }
  108.  
  109.    (void) sprintf(foo,"../%d/%s",uid,fnsess);
  110.    (void) rename(foo,fnsess);
  111.  
  112.    (void) sprintf(foo,"../%d/%s",uid,fnre);
  113.    (void) rename(foo,fnre); /* in case we're already disconnected */
  114.  
  115.    uid = newuid;
  116.    (void) setreuid(uid,euid);
  117.    setusername();
  118.  
  119.    if (flagxutmp)
  120.      if (utmp(glfnsty + PTYUTMP_OFFSET,username,PTYUTMP_SWHOST,date) == -1)
  121.        ; /* too bad. */
  122.    if (flagxwtmp)
  123.      if (wtmp(glfnsty + PTYWTMP_OFFSET,username,PTYWTMP_SWHOST,date) == -1)
  124.        ; /* too bad. */
  125.    if (flagsigler)
  126.      (void) kill(siglerpid,SIGUSR2);
  127.   }
  128. }
  129.  
  130. /*ARGSUSED*/
  131. static void sig_pipe(i)
  132. sig_num i;
  133. {
  134.  flagsigler = 0; /* XXX: is this appropriate? race? */
  135.  /* Will end up giving child HUP. */
  136. }
  137.  
  138. /*ARGSUSED*/
  139. static void sig_chld(i)
  140. sig_num i;
  141. {
  142.  union wait w;
  143.  
  144.  if (wait3(&w,WNOHANG | WUNTRACED,(struct rusage *) 0) <= 0)
  145.    return; /* why'd we get the CHLD? it must have stopped & restarted? */
  146.  
  147.  if (w.w_stopval == WSTOPPED)
  148.   {
  149.    childsig = w.w_stopsig;
  150.    flagchild = 2;
  151.   }
  152.  else
  153.   {
  154.    childsig = w.w_termsig; /* can't do much with this */
  155.    flagchild = 0;
  156.   }
  157. }
  158.  
  159. /*ARGSUSED*/
  160. static void sig_term(i)
  161. sig_num i;
  162. {
  163.  flagsigler = 0;
  164. }
  165.  
  166. /* If we have made it to being master, we should never get TTIN or TTOU, */
  167. /* except possibly while restarting after a stop (e.g., if the user puts */
  168. /* us back into the background). But we let the signaller handle putting */
  169. /* the tty modes back before restarting us, so we should never, ever, */
  170. /* ever get a TTIN or TTOU. If the user is messing around and we do get */
  171. /* a TTIN or TTOU, we'll just pretend the child died and hope we get */
  172. /* around to telling the signaller about it. */
  173.  
  174. /*ARGSUSED*/
  175. static void sig_ttin(i)
  176. sig_num i;
  177. {
  178.  if (flagchild)
  179.   {
  180.    childsig = SIGTTIN;
  181.    flagchild = 2;
  182.   }
  183. }
  184.  
  185. /*ARGSUSED*/
  186. static void sig_ttou(i)
  187. sig_num i;
  188. {
  189.  if (flagchild)
  190.   {
  191.    childsig = SIGTTOU;
  192.    flagchild = 2;
  193.   }
  194. }
  195.  
  196. /*ARGSUSED*/
  197. static void sig_tstp(i)
  198. sig_num i;
  199. {
  200.  if (flagchild)
  201.   {
  202.    childsig = SIGCONT;
  203.    flagchild = 2;
  204.   }
  205. }
  206.  
  207. /* Most job-control shells (including csh) behave absolutely miserably. */
  208. /* (Well, that goes without saying.) In particular, rather than sending */
  209. /* a CONT to every one of their children in the process group, they feel */
  210. /* a need to kill the entire process group. Grrrr. Because of this, we */
  211. /* are forced to use the nonintuitive USR1 to communicate CONT, and ignore */
  212. /* CONT entirely. Anyway, read cont as usr1 where necessary. */
  213.  
  214. /* We can only get USR1 from the signaller (or from us after reconnect). */
  215. /* By convention, the signaller handles setting the tty modes back to */
  216. /* chartty, even though we handled restoring the modes before stop. */
  217.  
  218. /*ARGSUSED*/
  219. static void sig_cont(i)
  220. sig_num i;
  221. {
  222.  if (flagchild)
  223.   {
  224.    flagchild = 1;
  225.    (void) kill(slavepid,SIGCONT);
  226.    (void) kill(pid,SIGWINCH);
  227.   }
  228.  if (flagconnected == 3)
  229.    flagconnected = 1; /* XXX: should be internal to master() */
  230.  (void) setpgrp(0,pgrp);
  231. }
  232.  
  233. /* If it weren't for WINCH, which must be in the master if NO_FDPASSING, */
  234. /* and for the stupid conventions surrounding a process's control tty, */
  235. /* then all mention of fdtty could disappear from master. This would */
  236. /* slightly complicate the signaller's T{STP,TIN,TOU} handling but make */
  237. /* reconnect a lot simpler. Sigh. */
  238.  
  239. /*ARGSUSED*/
  240. static void sig_winch(i)
  241. sig_num i;
  242. {
  243.  int pg;
  244.  
  245.  flagqwinch = 0;
  246. #ifdef TTY_WINDOWS
  247. /* An unfortunate but slight race: Another handler could change the pgrp */
  248. /* if the child suddenly stops and we're queued for delivery. So we have */
  249. /* to change it back. */
  250.  pg = getpgrp(0);
  251.  (void) setpgrp(0,pgrp);
  252.  if (!flagsigler)
  253.    flagqwinch = 1;
  254.  else
  255.    if (tty_getmodes(fdsty,&tmopty) == 0)
  256.      if (tty_getmodes(fdtty,&tmowintty) == 0)
  257.       {
  258.        tty_copymodes(&tmowinpty,&tmopty);
  259.        tty_copywin(&tmowinpty,&tmowintty);
  260.        (void) tty_modifymodes(fdsty,&tmowinpty,&tmopty);
  261.       }
  262.  (void) setpgrp(0,pg);
  263. #endif
  264. }
  265.  
  266. static int disconnect(fnsty)
  267. char *fnsty;
  268. {
  269.  if (fdtty != -1)
  270.   {
  271.    (void) tty_dissoc(fdtty); /* must succeed */
  272.    (void) close(fdtty);
  273.    fdtty = -1;
  274.   }
  275.  if (fdpass != -1)
  276.   {
  277.    /* We used to write the dot to fdpass here. It's in sigler now, to */
  278.    /* prevent a race condition. */
  279.    (void) close(fdpass);
  280.    fdpass = -1;
  281.   }
  282.  if (fdin != -1)
  283.   {
  284.    (void) close(fdin);
  285.    fdin = -1;
  286.   }
  287.  if (fdout != -1)
  288.   {
  289.    (void) close(fdout);
  290.    fdout = -1;
  291.   }
  292.  if (fdre != -1)
  293.   {
  294.    (void) close(fdre);
  295.    fdre = -1;
  296.   }
  297.  
  298.  fdre = pty_readsock(fnsty,fnre);
  299.  if (fdre == -1)
  300.    return -1; /* damn. */
  301.  return 0;
  302. }
  303.  
  304. static int reconnect()
  305. {
  306.  int t;
  307.  char buf[1];
  308.  char fntty[TTYNAMELEN]; /* sigh */
  309.  int flags = 0;
  310.  
  311.  t = pty_acceptsock(fdre);
  312.  (void) close(fdre);
  313.  fdre = t;
  314.  if (fdre == -1)
  315.    return -1;
  316.  
  317. #define VCF (void) close(fdre)
  318. #define BONK(xxx,yyy) if ((xxx) == -1) { VCF; return -1; } else (yyy);
  319.  
  320. /* What about fd 2 for warnings & errors? No, master doesn't use them. */
  321.  
  322. /* Must have: in, out, siglerpid, pgrp, flagjobctrl. 1, 2, 16, 32, 256. */
  323. /* Except if NO_FDPASSING: just flagjobctrl in that case. */
  324. /* If fdtty, must have also tmochartty, tmotty, fntty. 8: 64, 128, 1024. */
  325. /* Finally, fdpass is independent of all the rest. */
  326.  
  327. /* CHANGE: With fdpass, fdin and fdout are irrelevant. */
  328.  
  329.  if (pty_sendint(fdre,'p',&pid) == -1)
  330.   {
  331.    VCF;
  332.    return -1;
  333.   }
  334.  
  335.  while (pty_getch(fdre,buf) == 0)
  336.    switch(buf[0])
  337.     {
  338. #ifdef NO_FDPASSING
  339.      case 's': BONK(pty_putgetstr(fdre,'s',fntty),flags |= 8) break;
  340. #else
  341.      case '0': BONK(pty_putgetfd(fdre,'0',&fdin),flags |= 1) break;
  342.      case '1': BONK(pty_putgetfd(fdre,'1',&fdout),flags |= 2) break;
  343.      case 'f': BONK(pty_putgetfd(fdre,'f',&fdpass),flags |= 4) break;
  344.      case 't': BONK(pty_putgetfd(fdre,'t',&fdtty),flags |= 8) break;
  345.      case 's': BONK(pty_putgetstr(fdre,'s',fntty),flags |= 1024) break;
  346. #endif
  347.      case 'p': BONK(pty_putgetint(fdre,'p',&siglerpid),flags |= 16) break;
  348.      case 'g': BONK(pty_putgetint(fdre,'g',&pgrp),flags |= 32) break;
  349.      case 'c': BONK(pty_putgettty(fdre,'c',&tmochartty),flags |= 64) break;
  350.      case 'n': BONK(pty_putgettty(fdre,'n',&tmotty),flags |= 128) break;
  351.      case 'j': BONK(pty_putgetint(fdre,'j',&flagjobctrl),flags |= 256) break;
  352. #ifdef NO_FDPASSING
  353.      case ' ': if ((flags & 256) != 256) { VCF; return -1; }
  354. #else
  355.      case ' ': if (flags & 4) flags |= 3;
  356.            if ((flags & 307) != 307) { VCF; return -1; }
  357.            if (flags & 8) if ((flags & 1024) != 1024) { VCF; return -1; }
  358. #endif
  359.            if (flags & 8) if ((flags & 192) != 192) { VCF; return -1; }
  360.  
  361. #ifdef NO_FDPASSING
  362.                if ((fdtty = open(fntty,O_RDWR)) == -1)
  363.          return -1;
  364.            if ((fdin = dup(fdre)) == -1)
  365.         {
  366.          (void) close(fdtty);
  367.          fdtty = -1;
  368.          return -1;
  369.         }
  370.            if ((fdout = dup(fdre)) == -1)
  371.         {
  372.          (void) close(fdtty);
  373.          fdtty = -1;
  374.          (void) close(fdout);
  375.          fdout = -1;
  376.          return -1;
  377.         }
  378. #endif
  379.            VCF; /* yahoo! */
  380.            (void) close(open(fntty,O_RDWR));
  381.            /* XXX: do we really have to reattach? */
  382.            /* I wish there were no concept of controlling tty. */
  383.            /* Instead, an ioctl on /dev/tty (i.e., fd 3) would */
  384.            /* return a session identifier. */
  385.  
  386.            if (fdpass != -1)
  387.         {
  388.          if (pty_sendint(fdpass,'G',&siglerpid) == -1)
  389.            return -1;
  390.            /* XXX: death(1) might be more intuitive. Then */
  391.            /* again, it may also be much more destructive. */
  392.          if (pty_sendfd(fdpass,'m',&fdmty) == -1)
  393.            return -1;
  394.          if (pty_sendfd(fdpass,'s',&fdsty) == -1)
  395.            return -1;
  396.         }
  397.  
  398.            /* So that we can disconnect again, we have to reset the */
  399.            /* siglerpid in fdsess. That done, we've totally severed */
  400.            /* our previous link to a connection. */
  401.                (void) lseek(fdsess,(long) sizeof(int),0);
  402.                (void) write(fdsess,(char *) &siglerpid,sizeof(int));
  403.  
  404.            flagsigler = 1;
  405.            (void) setpgrp(0,pgrp);
  406.            (void) kill(pid,SIGUSR1); /* grrrr */
  407.            return 0;
  408.      default: (void) pty_putch(fdre," "); break;
  409.     }
  410.  VCF;
  411.  return -1;
  412. }
  413.  
  414. struct timeval instant = { 0, 0 };
  415.  
  416. void master(fnsty,child)
  417. char *fnsty;
  418. int child;
  419. {
  420.  fd_set rfds;
  421.  fd_set wfds;
  422.  int fdnum;
  423.  int r;
  424.  
  425.  /* XXX: is it a race for child to set pty modes? */
  426.  
  427.  /* Note that we don't close fdsty. */
  428.  
  429.  siglerpid = getppid();
  430.  slavepid = child;
  431.  pid = getpid();
  432.  glfnsty = fnsty;
  433.  
  434.  if (flagsession)
  435.   {
  436.    /* Security note: This is the only file we actually create, */
  437.    /* not counting the reconnect socket. */
  438.    (void) sprintf(fnsess,"sess.%s",fnsty + sizeof(DEVSTY) - 3);
  439.    fdsess = open(fnsess,O_RDWR | O_CREAT | O_TRUNC,0600);
  440.    (void) write(fdsess,(char *) &uid,sizeof(int));
  441.    (void) write(fdsess,(char *) &siglerpid,sizeof(int));
  442.    (void) write(fdsess,(char *) &pid,sizeof(int));
  443.    (void) write(fdsess,(char *) &slavepid,sizeof(int));
  444.    /* We'll never actually bother closing fdsess. Who cares? */
  445.   }
  446.  
  447.  sig_ignore(SIGURG);
  448.  sig_ignore(SIGIO);
  449.  sig_ignore(SIGHUP);
  450.  sig_ignore(SIGQUIT);
  451.  sig_ignore(SIGINT);
  452.  sig_sethandler(SIGXCPU,sig_force); sig_handle(SIGXCPU);
  453.  sig_ignore(SIGXFSZ);
  454.  sig_ignore(SIGPROF);
  455.  sig_ignore(SIGVTALRM);
  456.  
  457.  sig_default(SIGEMT); /* XXX: really dump? */
  458.  sig_default(SIGIOT);
  459.  sig_default(SIGTRAP);
  460.  sig_default(SIGSYS);
  461.  sig_default(SIGFPE);
  462.  sig_default(SIGILL);
  463.  sig_default(SIGSEGV);
  464.  
  465.  sig_default(SIGSTOP);
  466.  
  467.  sig_sethandler(SIGTTIN,sig_ttin); sig_handle(SIGTTIN);
  468.  sig_sethandler(SIGTTOU,sig_ttou); sig_handle(SIGTTOU);
  469.  sig_sethandler(SIGTSTP,sig_tstp); sig_handle(SIGTSTP);
  470.  sig_sethandler(SIGUSR1,sig_cont); sig_handle(SIGUSR1);
  471.  sig_ignore(SIGCONT); /* grrrr. see explanation above sig_cont. */
  472.  sig_sethandler(SIGPIPE,sig_pipe); sig_handle(SIGPIPE);
  473.  
  474.  sig_sethandler(SIGCHLD,sig_chld); sig_handle(SIGCHLD);
  475.  
  476.  sig_sethandler(SIGTERM,sig_term); sig_handle(SIGTERM);
  477.  sig_sethandler(SIGWINCH,sig_winch); sig_handle(SIGWINCH);
  478.  
  479.  sig_sethandler(SIGUSR2,sig_usr2); sig_handle(SIGUSR2);
  480.  
  481.  if (fdpass != -1)
  482.   {
  483.    if (pty_sendint(fdpass,'G',&siglerpid) == -1)
  484.      death(1);
  485.    if (pty_sendfd(fdpass,'m',&fdmty) == -1)
  486.      death(1);
  487.    if (pty_sendfd(fdpass,'s',&fdsty) == -1)
  488.      death(1);
  489.   }
  490.  
  491. #define SET_FDNUM fdnum = fdin; if (fdout > fdnum) fdnum = fdout; \
  492. if (fdmty > fdnum) fdnum = fdmty; fdnum++;
  493.  
  494.  SET_FDNUM
  495.  
  496.  if (fdpass == -1)
  497.    (void) fcntl(fdmty,F_SETFL,FNDELAY);
  498.    /* If it doesn't work, too bad. */
  499.  
  500. #ifdef SIGINTERRUPT
  501.  sig_interrupt();
  502. #endif
  503.  
  504.  for (;;)
  505.   {
  506.    /* Stage 1: Mangle internal states. This could be made into a */
  507.    /* critical section, but there's no point. */
  508.  
  509.    if ((flagconnected == 2) && (flagchild != 2))
  510.      flagconnected = 1 + 2 * (flagchild == 0);
  511.    if ((flagconnected != 0) && (flagsigler == 0))
  512.     {
  513.      flagconnected = 0;
  514.      if (flagsession)
  515.       {
  516.        (void) kill(siglerpid,SIGTERM);
  517. #ifdef NO_SESSION
  518.        ; /* impossible */
  519. #else
  520.        if (disconnect(fnsty) == -1)
  521.      quickdeath(1); /* XXX: sigh */
  522.        if (fdnum <= fdre)
  523.      fdnum = fdre + 1;
  524. #endif
  525.       }
  526.     }
  527.  
  528.    /* Stage 2: Prepare fds, and select(). */
  529.  
  530.    FD_ZERO(&rfds);
  531.    FD_ZERO(&wfds);
  532.  
  533.    if ((fdpass == -1) && (outbuflen < outbufsiz))
  534.      FD_SET(fdmty,&rfds);
  535.    if ((fdpass == -1) && ptybuflen)
  536.      FD_SET(fdmty,&wfds);
  537.    if ((fdpass == -1)
  538.      &&(ptybuflen < ptybufsiz) && (flagsigler == 1)
  539.      &&(flagconnected == 1) && (flagchild == 1))
  540.      FD_SET(fdin,&rfds);
  541.    if ((fdpass == -1)
  542.      &&(outbuflen) && (flagsigler == 1) && (flagconnected == 1))
  543.      FD_SET(fdout,&wfds);
  544.  
  545.    if (flagsession && (flagconnected == 0))
  546.      FD_SET(fdre,&rfds);
  547.  
  548.    /* The times to flush buffers: when the child has stopped and we're */
  549.    /* connected; when the child has died and we're connected; when the */
  550.    /* signaller has died and we don't support sessions. */
  551.    if (((flagconnected == 1) && (flagchild != 1))
  552.      ||((flagconnected == 0) && (flagsession == 0)))
  553.      r = select(fdnum,&rfds,&wfds,(fd_set *) 0,&instant);
  554.    else
  555.      r = select(fdnum,&rfds,&wfds,(fd_set *) 0,(struct timeval *) 0);
  556.  
  557.  
  558.    /* Stage 3: Interpret the results and handle special cases. */
  559.  
  560.    if (r <= 0)
  561.      if (r == -1)
  562.        switch(errno)
  563.         {
  564.          case EBADF: death(1);
  565.                      break;
  566.          case EINTR: break; /* fine. */
  567.          case EINVAL: break; /* impossible. */
  568.          default: break; /* say what? */
  569.         }
  570.      else /* r is 0 */
  571.       {
  572.        if (flagconnected == 1) /* flagchild is 0 or 2 */
  573.      if (flagchild == 0)
  574.        break; /* That's it! Child died, and we're outta here! */
  575.      else
  576.       { /* done with flush, time to stop sigler & idle */
  577.        if (flagjobctrl)
  578.         {
  579.          /* As usual, if we don't have a tty, tmotty == tmochartty
  580.         and it won't matter that fdtty is undefined. */
  581.          (void) setpgrp(0,pgrp);
  582.            if (tty_modifymodes(fdtty,&tmotty,&tmochartty) == -1)
  583.            ; /* XXX: what to do? */
  584.          (void) setpgrp(0,pid);
  585.          switch(childsig)
  586.           {
  587.            case SIGSTOP: (void) kill(siglerpid,SIGSTOP); break;
  588.            case SIGTTOU: (void) kill(siglerpid,SIGTTOU); break;
  589.            case SIGTTIN: (void) kill(siglerpid,SIGTTIN); break;
  590.            case SIGTSTP: (void) kill(siglerpid,SIGTSTP); break;
  591.            case SIGCONT: break; /* special case---see sig_tstp */
  592.            default: (void) kill(siglerpid,SIGSTOP); break;
  593.           }
  594.          flagconnected = 2;
  595.         }
  596.       }
  597.        else if (flagconnected == 0) /* non-session, sigler dead */
  598.      break; /* Giving pty pgrp a HUP, ho hum */
  599.      /* Most pgrp-based killing would be more logically done */
  600.      /* one process at a time, i.e., we should give our child */
  601.      /* a signal specially. But nobody else does, so we won't. */
  602.       }
  603.    else
  604.     {
  605. #ifndef NO_SESSION
  606.      if (flagconnected == 0)
  607.        if (FD_ISSET(fdre,&rfds))
  608.      if (reconnect() == -1)
  609.       {
  610.        if (disconnect(fnsty) == -1)
  611.          quickdeath(1); /* sigh */
  612.            if (fdnum <= fdre)
  613.          fdnum = fdre + 1;
  614.           }
  615.      else
  616.       {
  617.        flagconnected = 1; /* yay! */
  618.        SET_FDNUM
  619.        continue; /* XXX */
  620.       }
  621. #endif
  622.  
  623.  
  624.    /* Stage 4: Do normal I/O. */
  625.  
  626. #ifdef SIGINTERRUPT
  627.      sig_startring(); /* blocking? never heard of it */
  628. #endif
  629.  
  630.      if (FD_ISSET(fdin,&rfds))
  631.       {
  632.        /* ptybuflen must be smaller than ptybufsiz. */
  633.        r = read(fdin,ptybuf + ptybuflen,ptybufsiz - ptybuflen);
  634.        if (r == -1)
  635.      switch(errno)
  636.       {
  637.        case EINTR: case EWOULDBLOCK: break; /* fine */
  638.        default: death(1);
  639.       }
  640.        else if (r == 0) /* EOF */
  641.     {
  642.      ; /* XXX: there's no way to pass an EOF */
  643.     }
  644.        else
  645.      ptybuflen += r;
  646.       }
  647.      if (FD_ISSET(fdmty,&rfds))
  648.       {
  649.        /* outbuflen must be smaller than outbufsiz. */
  650.        r = read(fdmty,outbuf + outbuflen,outbufsiz - outbuflen);
  651.        if (r == -1)
  652.      switch(errno)
  653.       {
  654.        case EINTR: case EWOULDBLOCK: break; /* fine */
  655.        default: death(1);
  656.       }
  657.        else if (r == 0) /* EOF */
  658.     {
  659.      ; /* This can't happen. The slave can't pass an EOF. */
  660.      /* XXX: Should we close fdout anyway? */
  661.     }
  662.        else
  663.      outbuflen += r;
  664.       }
  665.      if (FD_ISSET(fdout,&wfds))
  666.       {
  667.        r = write(fdout,outbuf,outbuflen);
  668.        if (r == -1)
  669.      switch(errno)
  670.       {
  671.        case EINTR: case EWOULDBLOCK: break; /* fine */
  672.        default: death(1);
  673.       }
  674.        else if (r == 0) /* ? */
  675.      ; /* impossible */
  676.        else if (r == outbuflen)
  677.      outbuflen = 0;
  678.        else
  679.     {
  680.      outbuflen -= r;
  681.          copy(outbuf,outbuf + r,outbuflen);
  682.     }
  683.       }
  684.      if (FD_ISSET(fdmty,&wfds))
  685.       {
  686.        r = write(fdmty,ptybuf,ptybuflen);
  687.        if (r == -1)
  688.      switch(errno)
  689.       {
  690.        case EINTR: case EWOULDBLOCK: break; /* fine */
  691.        default: death(1);
  692.       }
  693.        else if (r == 0) /* ? */
  694.      ; /* impossible */
  695.        else if (r == ptybuflen)
  696.      ptybuflen = 0;
  697.        else
  698.     {
  699.      ptybuflen -= r;
  700.          copy(ptybuf,ptybuf + r,ptybuflen);
  701.     }
  702.       }
  703.  
  704. #ifdef SIGINTERRUPT
  705.      sig_stopring();
  706. #endif
  707.     }
  708.   }
  709.  
  710.  death(0);
  711. }
  712.