home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1621 / pty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  10.3 KB  |  385 lines

  1. /* Copyright 1990, Daniel J. Bernstein. All rights reserved. */
  2.  
  3. /*
  4. pty.c: run a program under a pty session
  5. */
  6.  
  7. #include <stdio.h>
  8. extern unsigned short getuid(); /* grrrr */
  9. extern unsigned short geteuid(); /* grrrr */
  10. #include "config.h"
  11. #include "getopt.h"
  12. #include "err.h"
  13. #include "pty.h"
  14. #include "tty.h"
  15. #include "texts.h"
  16. #include "sig.h"
  17. #include "sigler.h"
  18. #include "master.h"
  19. #include "slave.h"
  20. #include "file.h"
  21. #include "logs.h"
  22. #include "misc.h"
  23.  
  24. int flagpcbreak = 0; /* -pc, character-at-a-time */
  25. int flagpnew = 1; /* -pd, new line discipline---traditionally off to start */
  26. int flagpecho = 1; /* -pe, echo characters */
  27. int flagpcrmod = 1; /* -pn, munge carriage returns */
  28. int flagpraw = 0; /* -pr, raw mode */
  29. int flagpcrt = 1; /* -ps, screen */
  30.  
  31. getfreepty(fnmty,fnsty,pty1,pty2)
  32. register char fnmty[sizeof(DEVMTY)];
  33. register char fnsty[sizeof(DEVSTY)];
  34. register char pty1[sizeof(PTY1)];
  35. register char pty2[sizeof(PTY2)];
  36. {
  37.  register char *c1;
  38.  register char *c2;
  39.  register char *c1start; /* for ``random'' pty searching */
  40.  register char *c2start;
  41.  int e;
  42.  
  43.  if (flagxrandom)
  44.   {
  45.    c1start = pty1 + (pid % (sizeof(PTY1) - 1));
  46.    c2start = pty2 + ((pid + date) % (sizeof(PTY2) - 1));
  47.   }
  48.  else
  49.   {
  50.    c1start = pty1;
  51.    c2start = pty2;
  52.   }
  53.  
  54.  c1 = c1start;
  55.  do
  56.   {
  57.    fnmty[sizeof(DEVMTY) - 3] = *c1;
  58.    fnmty[sizeof(DEVMTY) - 2] = pty2[0];
  59.    if (!access(fnmty,F_OK))
  60.     {
  61.      c2 = c2start;
  62.      fnsty[sizeof(DEVSTY) - 3] = *c1;
  63.      fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2;
  64.      do
  65.       {
  66. #ifdef DESPERATE_ALARMS
  67.        sig_startring();
  68. #endif
  69.  
  70. /* Some other process could come along and mess up our test by opening */
  71. /* the master side before we do. But in that case they'll get the pty */
  72. /* anyway, and we'll move on to another possibility without comment. */
  73.        if (flagxchkopen)
  74.     {
  75. #ifdef DONT_NDELAY
  76.          fdsty = open(fnsty,O_RDWR);
  77. #else
  78.          fdsty = open(fnsty,O_RDWR | O_NDELAY);
  79. #endif
  80.          e = errno;
  81.          fdmty = open(fnmty,O_RDWR);
  82.     }
  83.        else
  84.     {
  85.          fdmty = open(fnmty,O_RDWR);
  86.          fdsty = open(fnsty,O_RDWR);
  87.      e = errno;
  88.     }
  89.  
  90. #ifdef DESPERATE_ALARMS
  91.        sig_stopring();
  92. #endif
  93.  
  94.        if (fdmty != -1)
  95.     {
  96.      if (flagxskipopen && (fdsty != -1))
  97.        warnerr2("pty: warning: slave %s still in use\n",fnsty);
  98.      else
  99.       {
  100.        if ((fdsty == -1) && (e != EINTR) && (e != EWOULDBLOCK))
  101.          fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e);
  102.        if (flagxchkopen)
  103.          if (fdsty == -1)
  104.           {
  105.            fdsty = open(fnsty,O_RDWR);
  106.            e = errno;
  107.           }
  108.          else
  109.            warnerr2("pty: warning: slave %s still in use\n",fnsty);
  110.        if (fdsty == -1)
  111.          fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e);
  112.        else
  113.         {
  114.          if (flagxchkopen)
  115.            if (fcntl(fdsty,F_SETFL,0) == -1)
  116.          fatalerrp(6,"pty: fatal: can't fcntl pty",e);
  117.          return 0;
  118.         }
  119.       }
  120.     }
  121.  
  122.        if (fdmty != -1) (void) close(fdmty);
  123.        if (fdsty != -1) (void) close(fdsty);
  124.        if (!(*(++c2)))
  125.      c2 = pty2;
  126.        fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2;
  127.       }
  128.      while (c2 != c2start);
  129.     }
  130.    if (!(*(++c1)))
  131.      c1 = pty1;
  132.   }
  133.  while (c1 != c1start);
  134.  return -1;
  135. }
  136.  
  137. char fnmty[sizeof(DEVMTY)] = DEVMTY;
  138. char fnsty[sizeof(DEVSTY)] = DEVSTY;
  139. char pty1[sizeof(PTY1)] = PTY1;
  140. char pty2[sizeof(PTY2)] = PTY2;
  141.  
  142. main(argc,argv)
  143. int argc;
  144. char *argv[];
  145. {
  146.  int opt;
  147.  int f;
  148.  
  149.  uid = getuid();
  150.  euid = geteuid();
  151.  pid = getpid();
  152.  pgrp = getpgrp(0);
  153.  date = now();
  154.  setusername();
  155.  
  156.  while ((opt = getopt(argc,argv,"qQvdDe3Ef:FjJsStTp:x:0ACHUVW")) != EOF)
  157.    switch(opt)
  158.     {
  159.      case 'A': fatalinfo(1,ptyauthor);
  160.      case 'C': fatalinfo(1,ptycopyright);
  161.      case 'H': fatalinfo(1,ptyhelp);
  162.      case 'U': fatalinfo(1,ptyusage);
  163.      case 'V': fatalinfo(1,ptyversion);
  164.      case 'W': fatalinfo(1,ptywarranty);
  165.      case '?': fatalinfo(1,ptyusage);
  166.      case 'q': flagquiet = 1; break;
  167.      case 'Q': flagquiet = 0; flagverbose = 0; break;
  168.      case 'v': flagverbose = 1; break;
  169.      case 'd': flagdetached = 1; flagjobctrl = 0; flagttymodes = 0; break;
  170.      case 'D': flagdetached = 0; flagjobctrl = 1; flagttymodes = 1; break;
  171.      case 'e': flagsameerr = 2; break;
  172.      case '3': flagsameerr = 1; break;
  173.      case 'E': flagsameerr = 0; break;
  174.      case 'f': flagfdpass = 1; 
  175.            if (sscanf(optarg,"%d",&fdpass) < 1) fatalinfo(1,ptyusage);
  176.            break;
  177.      case 'F': flagfdpass = 0; break;
  178.      case 'j': flagjobctrl = 1; break;
  179.      case 'J': flagjobctrl = 0; break;
  180.      case 's': flagsession = 1; flagxutmp = 1; break;
  181.      case 'S': flagsession = 0; flagxutmp = 0; break;
  182.      case 't': flagttymodes = 1; break;
  183.      case 'T': flagttymodes = 0; break;
  184.      case '0': flagsameerr = 2; flagsession = 0; flagttymodes = 0;
  185.            flagxutmp = 0; flagxwtmp = 0;
  186.            flagpcbreak = 3; flagpraw = 3; flagpecho = 2; flagpnew = 2;
  187.            break;
  188.      case 'p': while (opt = *(optarg++))
  189.          switch(opt)
  190.           {
  191.            case 'c': flagpcbreak = 3; break;
  192.            case 'C': flagpcbreak = 2; break;
  193.            case 'd': flagpnew = 3; break;
  194.            case 'D': flagpnew = 2; break;
  195.            case 'e': flagpecho = 3; break;
  196.            case 'E': flagpecho = 2; break;
  197.            case 'n': flagpcrmod = 3; break;
  198.            case 'N': flagpcrmod = 2; break;
  199.            case 'r': flagpraw = 3; break;
  200.            case 'R': flagpraw = 2; break;
  201.            case 's': flagpcrt = 3; break;
  202.            case 'S': flagpcrt = 2; break;
  203.            case '0': flagpcbreak = 3; flagpraw = 3;
  204.                  flagpecho = 2; flagpnew = 2;
  205.                  break;
  206.            default: fatalinfo(1,ptyusage); break;
  207.           }
  208.                break;
  209.      case 'x': while (opt = *(optarg++))
  210.          switch(opt)
  211.           {
  212.            case 'c': flagxchown = 1; break;
  213.            case 'C': flagxchown = 0; break;
  214.            case 'u': flagxutmp = 1; break;
  215.            case 'U': flagxutmp = 0; break;
  216.            case 'w': flagxwtmp = 1; break;
  217.            case 'W': flagxwtmp = 0; break;
  218.            case 'x': flagxexcl = 1; break;
  219.            case 'X': flagxexcl = 0; break;
  220.            case 'e': flagxerrwo = 1; break;
  221.            case 'E': flagxerrwo = 0; break;
  222.            case 'n': flagxchkopen = 1; break;
  223.            case 'N': flagxchkopen = 0; break;
  224.            case 'o': flagxskipopen = 1; break;
  225.            case 'O': flagxskipopen = 0; break;
  226.            case 'r': flagxrandom = 1; break;
  227.            case 'R': flagxrandom = 0; break;
  228.            case 's': flagxsetuid = 1; break;
  229.            case 'S': flagxsetuid = 0; break;
  230.            default: fatalinfo(1,ptyusage); break;
  231.           }
  232.                break;
  233.     }
  234.  argv += optind, argc -= optind;
  235.  
  236.  if (!*argv)
  237.    fatalinfo(1,ptyusage);
  238.  
  239.  /* Option forcing. */
  240. #ifdef NO_UTMP
  241.   if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced off\n");
  242.   flagxutmp = 0;
  243. #endif
  244. #ifdef NO_WTMP
  245.   if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced off\n");
  246.   flagxwtmp = 0;
  247. #endif
  248. #ifdef NO_CHOWN
  249.   if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced off\n");
  250.   flagxchown = 0;
  251. #endif
  252. #ifdef NO_SESSION
  253.   if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced off\n");
  254.   flagsession = 0;
  255. #endif
  256. #ifdef MUST_UTMP
  257.   if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced on\n");
  258.   flagxutmp = 1;
  259. #endif
  260. #ifdef MUST_WTMP
  261.   if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced on\n");
  262.   flagxwtmp = 1;
  263. #endif
  264. #ifdef MUST_CHOWN
  265.   if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced on\n");
  266.   flagxchown = 1;
  267. #endif
  268. #ifdef MUST_SESSION
  269.   if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced on\n");
  270.   flagsession = 1;
  271. #endif
  272. #ifdef NO_FDPASSING
  273.   if (flagfdpass) if (flagverbose) warnerr2("%s","pty: fd passing forced off\n");
  274.   flagfdpass = 0;
  275. #endif
  276.  
  277.  /* Option munging. */
  278.  if (flagsession) flagsameerr = 0;
  279.  if (flagdetached) flagttymodes = 0;
  280.  if (flagxskipopen) flagxchkopen = 1;
  281.  
  282.  if (!flagxsetuid)
  283.   {
  284.    (void) setreuid(uid,uid);
  285.    euid = uid;
  286.   }
  287.  
  288.  
  289.  sig_init();
  290.  sig_sethandler(SIGALRM,nothing); sig_handle(SIGALRM);
  291.  sig_default(SIGTTIN);
  292.  sig_default(SIGTTOU);
  293.  
  294.  if (fdpass == -1) /* wow, was this a source of bugs. */
  295.   {
  296.    fdin = 0;
  297.    fdout = 1;
  298.   }
  299.  
  300.  if (flagdetached)
  301.   {
  302.    tty_initmodes(&tmopty,flagpcbreak,flagpnew,flagpecho,
  303.              flagpcrmod,flagpraw,flagpcrt);
  304.   }
  305.  else
  306.   {
  307.    if ((fdtty = tty_getctrl()) == -1)
  308.      fatalerr(2,"pty: fatal: cannot find control terminal; try -d?\n");
  309.    if (tty_getmodes(fdtty,&tmotty) == -1)
  310.      fatalerr(3,"pty: fatal: cannot get current tty modes\n");
  311.      /* XXX: is there a way to recover more gracefully? */
  312.    tty_copymodes(&tmopty,&tmotty);
  313.    tty_mungemodes(&tmopty,flagpcbreak,flagpnew,flagpecho,
  314.               flagpcrmod,flagpraw,flagpcrt);
  315.    tty_copymodes(&tmochartty,&tmotty);
  316.    if (flagttymodes)
  317.      tty_charmode(&tmochartty);
  318.   }
  319.  
  320.  /* XXX: Here would be a good spot to include pty limits, say through */
  321.  /* the file PTYDIR/LIMITS. Lines of the form user group num, saying */
  322.  /* that user in that group is limited to num ptys, with * for all. */
  323.  /* All pty use would have to be logged somewhere. Anyway, with a */
  324.  /* streams-based pty, there wouldn't be much point to limits. */
  325.  
  326.  if (getfreepty(fnmty,fnsty,pty1,pty2) == -1)
  327.    fatalerr(5,"pty: fatal: no ptys available\n");
  328.  
  329.  if (flagverbose)
  330.    warnerr2("pty: successfully opened pty %s\n",fnsty);
  331.  
  332.  if (tty_modifymodes(fdtty,&tmochartty,&tmotty) == -1)
  333.   {
  334.    (void) tty_setmodes(fdtty,&tmotty); /* XXX --- gasp */
  335.    fatalerr(4,"pty: fatal: cannot set modes of original tty\n");
  336.   }
  337.  
  338. /* In general, BSD systems check MAXUPRC against the effective uid, */
  339. /* rather than the real uid; and they check it during a fork(). */
  340. /* The combination of these annoying behaviors means that we have */
  341. /* to switch uids while forking, hence possibly losing any security */
  342. /* measures we may have set up before the fork(). Grrrr. */
  343.  
  344.  (void) setreuid(euid,uid);
  345.  if ((f = fork()) == -1)
  346.   {
  347.    (void) tty_modifymodes(fdtty,&tmotty,&tmochartty);
  348.    fatalerr(7,"pty: fatal: cannot fork once\n");
  349.    /* After this, the signaller will handle tty modes. */
  350.   }
  351.  else if (f == 0)
  352.    if ((f = fork()) == -1)
  353.     {
  354.      (void) kill(pid,SIGTERM); /*XXX*/
  355.      fatalerr(7,"pty: fatal: cannot fork twice\n");
  356.     }
  357.    else if (f == 0)
  358.     {
  359.      (void) setreuid(uid,euid);
  360.      slave(fnsty,argv);
  361.     }
  362.    else
  363.     {
  364.      (void) setreuid(uid,euid);
  365.      if (flagsession)
  366.        if (sessdir() == -1)
  367.          fatal(1);
  368.      master(fnsty,f);
  369.     }
  370.  else
  371.   {
  372.    (void) setreuid(uid,euid);
  373.    if (flagsession)
  374.      if (sessdir() == -1)
  375.       {
  376.        fatalerr(8,"pty: fatal: cannot change to session directory\n");
  377.        (void) tty_modifymodes(fdtty,&tmotty,&tmochartty);
  378.       }
  379.    sigler(fnsty,f);
  380.   }
  381.  
  382.  fatal(9); /* just in case */
  383.  /*NOTREACHED*/
  384. }
  385.