home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / pty4 / part07 / ptymain.c next >
Encoding:
C/C++ Source or Header  |  1992-02-18  |  15.7 KB  |  613 lines

  1. #include <signal.h>
  2. #include "sigsched.h"
  3. #include "getoptquiet.h"
  4. #include "ralloc.h"
  5. #include "env.h"
  6. #include "fmt.h"
  7. #include "config/ptydir.h"
  8. #include "config/ttyopts.h"
  9. #include "config/ptyopts.h"
  10. #include "config/posix.h"
  11. #include "ptyget.h"
  12. #include "ptytty.h"
  13. #include "ptylogs.h"
  14. #include "ptymisc.h"
  15. #include "ptytexts.h"
  16. #include "ptycomm.h"
  17. #include "ptymaster.h"
  18. #include "ptysigler.h"
  19. #include "ptyerr.h"
  20. #include "ptyslave.h"
  21. #include "sesslog.h"
  22. #include "sessconnlog.h"
  23.  
  24. #define verbose 0, /*XXX*/
  25.  
  26. /* XXX Exported to other files: */
  27. int flagxutmp = 0;
  28. int flagxwtmp = 0;
  29.  
  30. /* Private flags: */
  31. static int flagxchown = 0;
  32. static int flagxexcl = 0; /* should be 1, but that'd break write & friends */
  33. static int flagxerrwo = 0; /* should be 1, but that'd break csh & more */
  34. static int flagxrandom = 1;
  35. static int flagxflowctl = 0; /* shouldn't have to exist */
  36. static int flagxonlysecure = 0; /* XXX: which one is right? */
  37.  
  38. static int flagreading = 1;
  39. static int flagdetached = 0;
  40. static int flagverbose = 1;
  41. static int flagjobctrl = 1;
  42. static int flagttymodes = 1;
  43. static int flagsameerr = 0;
  44. static int flagsession = 0;
  45.  
  46. static int flagpcbreak = 0; /* -pc, character-at-a-time */
  47. static int flagpnew = 1; /* -pd, new line discipline---traditionally off to start */
  48. static int flagpecho = 1; /* -pe, echo characters */
  49. static int flagpcrmod = 1; /* -pn, munge carriage returns */
  50. static int flagpraw = 0; /* -pr, raw mode */
  51. static int flagpcrt = 1; /* -ps, screen */
  52. static int flagp8bit = 1; /* -p8, 8-bit data path---traditionally off */
  53.  
  54. static int uid = -1; /* no harm in being safe */
  55. static char *username;
  56. static char *host;
  57. static char *remote;
  58. static char **program;
  59.  
  60. void setupsessionfiles(fdmty,fdsty,ext) /* XXX: move into master? */
  61. int fdmty;
  62. int fdsty;
  63. char *ext;
  64. {
  65.  struct sesslog sl;
  66.  long t;
  67.  t = now();
  68.  if (utmp_on(ext,username,host,t) == -1)
  69.   {
  70.    warn("warning","cannot write utmp entry");
  71.   }
  72.  if (wtmp_on(ext,username,host,t) == -1)
  73.   {
  74.    warn("warning","cannot write wtmp entry");
  75.    utmp_off(ext,host,t); /* if this fails, too bad */
  76.   }
  77.  sesslog_fill(&sl,ext,username,uid,getpid(),t);
  78.  if (sesslog(&sl) == -1)
  79.   {
  80.    warn("warning","cannot write sesslog entry");
  81.    utmp_off(ext,host,t);
  82.    wtmp_off(ext,host,t);
  83.   }
  84.  /* sessconn will be handled later---remember, we start out disconnected! */
  85. }
  86.  
  87. static int eachpty(ext)
  88. char *ext;
  89. {
  90.  int fdtty;
  91.  
  92.  verbose("trying %c%c",ext[0],ext[1]);
  93.  
  94.  /* Note that there are several situations in which dissociation will fail. */
  95.  /* Fortunately, pty doesn't really care. */
  96.  fdtty = tty_getctrl();
  97.  /* note that we do this whether or not flagdetached */
  98.  if (tty_dissoc(fdtty) == -1)
  99.    warn("warning","cannot dissociate from current tty");
  100.  if (fdtty != -1)
  101.    close(fdtty);
  102.  return 0;
  103. }
  104.  
  105. /* for sigler if flagttymodes: */
  106. static struct ttymodes tmotty; /* original tty modes */
  107. static struct ttymodes tmottyzero; /* zero tty modes */
  108.  
  109. void startup(n)
  110. int n;
  111. {
  112.  int pims[2];
  113.  int pimc[2];
  114.  int pid;
  115.  int fdmty;
  116.  int fdsty;
  117.  char ext[2];
  118.  int r1;
  119.  int r2;
  120.  char ch;
  121.  int fdcomm;
  122.  struct ttymodes ptymodes;
  123.  
  124.  r1 = 0;
  125.  r2 = 0;
  126.  if (flagxrandom)
  127.   {
  128.    r1 = getpid();
  129.    r2 = 37 * getpid() + (int) now;
  130.   }
  131.  
  132.  if (pipe(pims) == -1)
  133.   {
  134.    warn("fatal","cannot create internal pipe");
  135.    die(DIE_IMPOSSIBLE);
  136.   }
  137.  if (pipe(pimc) == -1)
  138.   {
  139.    warn("fatal","cannot create internal pipe");
  140.    die(DIE_IMPOSSIBLE);
  141.   }
  142.  
  143.  if (!flagdetached)
  144.   {
  145.    int fdtty;
  146.  
  147.    if ((fdtty = tty_getctrl()) == -1)
  148.     {
  149.      warn("fatal","cannot find controlling tty; try -d?");
  150.      die(DIE_NOCTTY);
  151.     }
  152.  
  153.    if (flagreading)
  154.      if (tty_forcefg(fdtty) == -1)
  155.       {
  156.        warn("fatal","cannot force myself into foreground; try -R?");
  157.        die(DIE_SETMODES);
  158.       }
  159.    /* The concept of !flagreading has a major problem: It's unsafe. We may */
  160.    /* end up with someone else's tty modes. */
  161.    if (tty_getmodes(fdtty,&ptymodes) == -1)
  162.     {
  163.      warn("fatal","cannot get modes of original tty");
  164.      die(DIE_GETMODES);
  165.     }
  166.  
  167.    close(fdtty);
  168.   }
  169.  else
  170.   {
  171.    tty_initmodes(&ptymodes);
  172.    flagpcbreak |= 2; flagpnew |= 2; flagpecho |= 2;
  173.    flagpcrmod |= 2; flagpraw |= 2; flagpcrt |= 2;
  174.    flagp8bit |= 2;
  175.   }
  176.  
  177.  tty_mungemodes(&ptymodes,
  178.    flagpcbreak,flagpnew,flagpecho,flagpcrmod,flagpraw,flagpcrt,flagp8bit);
  179.  
  180.  switch(pid = fork())
  181.   {
  182.    case -1:
  183.      warn("fatal","cannot fork master");
  184.      die(DIE_FORK);
  185.    case 0: /* master-slave */
  186. #ifdef POSIX_SILLINESS
  187.      if (setsid() == -1) /* cannot fail---we're not a pgrp leader after fork */
  188.       {
  189.        warn("fatal","cannot setsid");
  190.        die(DIE_IMPOSSIBLE);
  191.       }
  192. #endif
  193.      if (flagxonlysecure == -1)
  194.       {
  195.        if (getfreepty(&fdmty,&fdsty,ext,r1,r2,eachpty,flagxchown,1) == -1)
  196.     {
  197.          warn("fatal","no ptys available");
  198.      ch = DIE_NOPTYS; write(pims[1],&ch,1); /* XXX */
  199.          die(DIE_NOPTYS);
  200.     }
  201.       }
  202.      else if (getfreepty(&fdmty,&fdsty,ext,r1,r2,eachpty,flagxchown,0) == -1)
  203.       {
  204.        warn("warning","no secure ptys available");
  205.        if ((flagxonlysecure == 1) ||
  206.          (getfreepty(&fdmty,&fdsty,ext,r1,r2,eachpty,flagxchown,1) == -1))
  207.     {
  208.          warn("fatal","no ptys available");
  209.      ch = DIE_NOPTYS; write(pims[1],&ch,1); /* XXX */
  210.          die(DIE_NOPTYS);
  211.     }
  212.       }
  213.      if (flagverbose > 1)
  214.       {
  215.        char buf[50]; char *t; t = buf;
  216.        t += fmt_strncpy(t,"using pty ",0);
  217.        *t++ = ext[0]; *t++ = ext[1]; *t = 0;
  218.        warn("info",buf);
  219.       }
  220.      switch(pid = fork())
  221.       {
  222.        case -1:
  223.      warn("fatal","cannot fork slave");
  224.      ch = DIE_FORK; write(pims[1],&ch,1); /* XXX */
  225.      die(DIE_FORK);
  226.        case 0: /* slave */
  227.      signal(SIGTTOU,SIG_DFL); /* XXX: restore to original? */
  228.      signal(SIGTTIN,SIG_DFL);
  229.      signal(SIGPIPE,SIG_DFL);
  230.          close(pims[0]);
  231.      close(pims[1]);
  232.      close(pimc[1]);
  233.      close(fdmty);
  234.      verbose("slave waiting...");
  235.      if ((read(pimc[0],&ch,1) < 1) || (ch != 'k'))
  236.       {
  237.        warn("fatal","slave unable to read success code from master");
  238.        die(1);
  239.       }
  240.      close(pimc[0]);
  241.      verbose("slave starting...");
  242.      slave(fdsty,ext,program,flagxerrwo,flagsameerr,uid,flagverbose,flagxexcl);
  243.      return;
  244.        default: /* master */
  245.      if (setreuid(geteuid(),geteuid()) == -1)
  246.       {
  247.        warn("fatal","master unable to set its uids");
  248.        ch = DIE_IMPOSSIBLE; write(pims[1],&ch,1); /* XXX */
  249.        die(DIE_IMPOSSIBLE);
  250.       }
  251.          signal(SIGHUP,SIG_IGN);
  252.          signal(SIGTSTP,SIG_IGN);
  253.          signal(SIGINT,SIG_IGN);
  254.          signal(SIGQUIT,SIG_IGN);
  255. #ifdef TTY_WINDOWS
  256.          signal(SIGWINCH,SIG_IGN);
  257. #endif
  258.          /* We are now completely isolated from tty and I/O signals. */
  259.      verbose("master setting modes...");
  260.      if (tty_setmodes(fdsty,&ptymodes) == -1)
  261.       {
  262.        warn("fatal","master unable to set modes of pseudo-tty");
  263.        ch = DIE_SETMODES; write(pims[1],&ch,1); /* XXX */
  264.        die(DIE_SETMODES);
  265.       }
  266.      if (chdir(PTYDIR) == -1)
  267.       {
  268.        warn("fatal","master cannot chdir to pty directory; is it set up correctly?");
  269.        ch = DIE_PTYDIR; write(pims[1],&ch,1); /* XXX */
  270.        die(DIE_PTYDIR);
  271.       }
  272.      if ((fdcomm = comm_read(ext,uid)) == -1)
  273.       {
  274.        warn("fatal","master cannot create socket; is pty dir set up correctly?");
  275.        ch = DIE_ELSE; write(pims[1],&ch,1); /* XXX */
  276.        die(DIE_ELSE);
  277.       }
  278.      setupsessionfiles(fdmty,fdsty,ext);
  279.      if (write(pims[1],ext,2) != 2)
  280.        ; /* wtf? can't break pipe---we haven't closed it! */
  281.          close(pims[0]);
  282.      close(pims[1]);
  283.      close(pimc[0]);
  284.      close(0);
  285.      close(1);
  286.      close(2); /* XXX: this leaves master without a good error fd */
  287.      verbose("master starting...");
  288.      master(fdcomm,fdmty,fdsty,ext,uid,pid,flagsession,pimc[1],username,flagxflowctl);
  289.      return;
  290.       }
  291.      break;
  292.    default: /* sigler */
  293.      signal(SIGTTOU,SIG_DFL);
  294.      signal(SIGTTIN,SIG_DFL);
  295.      signal(SIGPIPE,SIG_DFL);
  296.      if (chdir(PTYDIR) == -1)
  297.       {
  298.        warn("fatal","signaller cannot chdir to pty directory; is it set up?");
  299.        die(DIE_PTYDIR);
  300.       }
  301.      close(pims[1]);
  302.      close(pimc[0]);
  303.      close(pimc[1]);
  304.      switch(read(pims[0],ext,2))
  305.       {
  306.        case 1: /* XXX: master has already printed an error */
  307.      die((int) ext[0]); /* XXX */
  308.        case 2:
  309.      break;
  310.        default:
  311.          warn("fatal","signaller cannot read success code from master");
  312.          die(DIE_COMM);
  313.      break;
  314.       }
  315.      if (flagttymodes) /* which implies flagreading && !flagdetached */
  316.       {
  317.        int fdtty;
  318.  
  319.        if ((fdtty = tty_getctrl()) == -1)
  320.     {
  321.      warn("fatal","signaller cannot find controlling tty; try -T?");
  322.      die(DIE_NOCTTY);
  323.     }
  324.        if (tty_getmodes(fdtty,&tmotty) == -1)
  325.     {
  326.      warn("fatal","signaller cannot get modes of original tty; try -T?");
  327.      die(DIE_GETMODES);
  328.     }
  329.        tty_copymodes(&tmottyzero,&tmotty);
  330.        tty_zeromode(&tmottyzero);
  331.        if (tty_setmodes(fdtty,&tmottyzero) == -1)
  332.     {
  333.      tty_setmodes(fdtty,&tmotty); /* worth a try... */
  334.      warn("fatal","signaller cannot set modes of original tty; try -T?");
  335.      die(DIE_SETMODES);
  336.     }
  337.        close(fdtty);
  338.       }
  339.      close(pims[0]);
  340.      sigler(ext,uid,pid,flagttymodes,&tmotty,&tmottyzero,flagreading,flagjobctrl,remote);
  341.   }
  342. }
  343.  
  344. static void outofmem(n)
  345. unsigned int n; /* number of bytes in failing malloc */
  346. {
  347.  static char buf[] = "pty: fatal: out of memory\n";
  348.  /* XXX: if this is from master, we may die silently! */
  349.  bwrite(2,buf,sizeof(buf) - 1); /*XXXX*/
  350.  die(DIE_NOMEM);
  351. }
  352.  
  353. static void usageerr(why,opt)
  354. int why;
  355. int opt;
  356. {
  357.  static char buf[100];
  358.  char *t; t = buf;
  359.  switch(why)
  360.   {
  361.    case 'a':
  362.      warn("fatal","what program do you want to run?");
  363.      break;
  364.    case 'p':
  365.      t += fmt_strncpy(t,"unrecognized terminal mode option -p",0);
  366.      *t++ = opt; *t = 0;
  367.      warn("fatal",buf);
  368.      break;
  369.    case 'x':
  370.      t += fmt_strncpy(t,"unrecognized security option -x",0);
  371.      *t++ = opt; *t = 0;
  372.      warn("fatal",buf);
  373.      break;
  374.    case 'u':
  375.      t += fmt_strncpy(t,"unrecognized option -",0);
  376.      *t++ = optproblem; *t = 0;
  377.      warn("fatal",buf);
  378.      break;
  379.    case 'o':
  380.      t += fmt_strncpy(t,"option -",0);
  381.      *t++ = optproblem;
  382.      t += fmt_strncpy(t," requires an argument",0);
  383.      *t = 0;
  384.      warn("fatal",buf);
  385.      break;
  386.   }
  387.  info(ptyusage);
  388.  die(DIE_USAGE);
  389. }
  390.  
  391. main(argc,argv,envp)
  392. int argc;
  393. char *argv[];
  394. char *envp[];
  395. {
  396.  int opt;
  397.  char *s;
  398.  int i;
  399.  char *proto;
  400.  char *protoremote;
  401.  
  402. /* Stage 1: Initial security checks. */
  403.  if (forceopen(0) || forceopen(1) || forceopen(2) || forceopen(3))
  404.   {
  405.    warn("fatal","cannot set up open descriptors");
  406.    die(DIE_SETUP);
  407.   }
  408.  
  409. /* Stage 2: Figure out userid and username. */
  410.  uid = getuid();
  411.  /* Preserve LOGNAME or USER if it's accurate. */
  412.  s = env_get("LOGNAME");
  413.  if (!s)
  414.    s = env_get("USER");
  415.  if (s && (username2uid(s,&i) != -1) && (i == uid))
  416.    username = s;
  417.  else
  418.    uid2username(uid,&username);
  419.  
  420. /* Stage 3: Guess at host and remote for system logs. */
  421.  host = 0;
  422.  remote = 0;
  423.  
  424.  proto = env_get("PROTO");
  425.  if (proto)
  426.   {
  427.    protoremote = ralloc(strlen(proto) + 10);
  428.    if (protoremote)
  429.     {
  430.      s = protoremote;
  431.      s += fmt_strncpy(s,proto,0);
  432.      fmt_strncpy(s,"REMOTE",0);
  433.      s = env_get(protoremote);
  434.      rfree(protoremote);
  435.      if (s)
  436.       {
  437.        remote = ralloc(strlen(proto) + strlen(s) + 10);
  438.        if (remote)
  439.     {
  440.      char *t;
  441.      t = remote;
  442.      t += fmt_strncpy(t,proto,0);
  443.      t += fmt_strncpy(t,":",0);
  444.      fmt_strncpy(t,s,0);
  445.      if (proto[0] == 'T' && proto[1] == 'C' && proto[2] == 'P' && !proto[3])
  446.       { /* might as well assign host as well */
  447.        t = remote;
  448.        while (*t != '@') ++t; ++t;
  449.        while (*t != '(') ++t; ++t;
  450.        host = ralloc(strlen(t) + 4);
  451.        if (host)
  452.         {
  453.          fmt_strncpy(host,t,0);
  454.          t = host;
  455.          while (*t != ')') ++t;
  456.          *t = 0;
  457.         }
  458.       }
  459.     }
  460.        else
  461.      remote = 0;
  462.       }
  463.     }
  464.   }
  465.  if (!host)
  466.    host = "pty4.0";
  467.  if (!remote)
  468.    remote = "(unknown)";
  469.  
  470. /* Stage 4: Process options. */
  471.  while ((opt = getopt(argc,argv,"ACHUVWqQvdDe3EjJsStTp:x:0rRh:O:")) != opteof)
  472.    switch(opt)
  473.     {
  474.      case 'A': info(ptyauthor); die(DIE_USAGE); break;
  475.      case 'C': info(ptycopyright); die(DIE_USAGE); break;
  476.      case 'H': info(ptyhelp); die(DIE_USAGE); break;
  477.      case 'U': info(ptyusage); die(DIE_USAGE); break;
  478.      case 'V': info(ptyversion); die(DIE_USAGE); break;
  479.      case 'W': info(ptywarranty); die(DIE_USAGE); break;
  480.      case 'h': host = optarg; break;
  481.      case 'O': remote = optarg; break;
  482.      case 'q': flagverbose = 0; break;
  483.      case 'Q': flagverbose = 1; break;
  484.      case 'v': flagverbose = 2; break;
  485.      case 'd': flagdetached = 1; flagjobctrl = 0; flagttymodes = 0; break;
  486.      case 'D': flagdetached = 0; flagjobctrl = 1; flagttymodes = 1; break;
  487.      case 'e': flagsameerr = 2; break;
  488.      case '3': flagsameerr = 1; break;
  489.      case 'E': flagsameerr = 0; break;
  490.      case 'j': flagjobctrl = 1; break;
  491.      case 'J': flagjobctrl = 0; break;
  492.      case 'r': flagreading = 1; break;
  493.      case 'R': flagreading = 0; break;
  494.      case 's': flagsession = 1; flagxutmp = 1; break;
  495.      case 'S': flagsession = 0; flagxutmp = 0; break;
  496.      case 't': flagttymodes = 1; break;
  497.      case 'T': flagttymodes = 0; break;
  498.      case '0': flagsameerr = 2; flagsession = 0; flagttymodes = 0;
  499.            flagxutmp = 0; /* XXX: also flagxwtmp = 0? */
  500.            flagpcbreak = 3; flagpraw = 3; flagpecho = 2; flagpnew = 2;
  501.            flagpcrmod = 2;
  502.            /* XXXXXX: is this sensible behavior? */
  503.            break;
  504.      case 'p':
  505.        while (opt = *(optarg++))
  506.      switch(opt)
  507.       {
  508.        case 'c': flagpcbreak = 3; break;
  509.        case 'C': flagpcbreak = 2; break;
  510.        case 'd': flagpnew = 3; break;
  511.        case 'D': flagpnew = 2; break;
  512.        case 'e': flagpecho = 3; break;
  513.        case 'E': flagpecho = 2; break;
  514.        case '7': flagp8bit = 2; break;
  515.        case '8': flagp8bit = 3; break;
  516.        case 'n': flagpcrmod = 3; break;
  517.        case 'N': flagpcrmod = 2; break;
  518.        case 'r': flagpraw = 3; break;
  519.        case 'R': flagpraw = 2; break;
  520.        case 's': flagpcrt = 3; break;
  521.        case 'S': flagpcrt = 2; break;
  522.        case '0': flagpcbreak = 3; flagpraw = 3;
  523.              flagpecho = 2; flagpnew = 2;
  524.              flagpcrmod = 2;
  525.              break;
  526.        default: usageerr('p',opt); break;
  527.       }
  528.        break;
  529.      case 'x':
  530.        while (opt = *(optarg++))
  531.      switch(opt)
  532.       {
  533.        case 'c': flagxchown = 1; break;
  534.        case 'C': flagxchown = 0; break;
  535.        case 'f': flagxflowctl = 1; break;
  536.        case 'F': flagxflowctl = 0; break;
  537.        case 'u': flagxutmp = 1; break;
  538.        case 'U': flagxutmp = 0; break;
  539.        case 'w': flagxwtmp = 1; break;
  540.        case 'W': flagxwtmp = 0; break;
  541.        case 'x': flagxexcl = 1; break;
  542.        case 'X': flagxexcl = 0; break;
  543.        case 'e': flagxerrwo = 1; break;
  544.        case 'E': flagxerrwo = 0; break;
  545.        case 'r': flagxrandom = 1; break;
  546.        case 'R': flagxrandom = 0; break;
  547.        case 's': flagxonlysecure = 1; break;
  548.        case 'S': flagxonlysecure = 0; break;
  549.        case 'i': flagxonlysecure = -1; break;
  550.        default: usageerr('x',opt); break;
  551.       }
  552.        break;
  553.      case '?':
  554.      default:
  555.        usageerr(argv[optind] ? 'u' : 'o',opt); break;
  556.     }
  557.  argc -= optind;
  558.  argv += optind;
  559.  
  560.  program = argv;
  561.  if (!*program)
  562.    usageerr('a',opt);
  563.  
  564. /* Stage 5: Munge options. */
  565.  
  566. #ifdef MUSTNOT_SESSION
  567.  if (flagsession) { flagsession = 0; warn("info","-s forced off"); }
  568. #endif
  569. #ifdef MUSTNOT_UTMPHOST
  570.  host = "pty4.0";
  571. #endif
  572. #ifdef MUSTNOT_UTMP
  573.  if (flagxutmp) { flagxutmp = 0; warn("info","-xu forced off"); }
  574. #endif
  575. #ifdef MUSTNOT_WTMP
  576.  if (flagxwtmp) { flagxwtmp = 0; warn("info","-xw forced off"); }
  577. #endif
  578. #ifdef MUSTNOT_CHOWN
  579.  if (flagxchown) { flagxchown = 0; warn("info","-xc forced off"); }
  580. #endif
  581.  
  582.  if (flagsession) flagsameerr = 0;
  583.  if (flagdetached) flagttymodes = 0;
  584.  if (!flagreading) flagttymodes = 0;
  585.  
  586.  if (!flagsession)
  587.   {
  588.    sesslog_disable();
  589.    sessconnlog_disable();
  590.   }
  591.  if (!flagverbose)
  592.    warn_disable();
  593.  
  594. /* Stage 6: Set up signals and enter sigsched. */
  595.  rallocneverfail(outofmem);
  596.  
  597.  ss_addsig(SIGCHLD);
  598.  ss_addsig(SIGHUP);
  599.  ss_addsig(SIGTSTP);
  600.  ss_addsig(SIGINT);
  601.  ss_addsig(SIGQUIT);
  602. #ifdef TTY_WINDOWS
  603.  ss_addsig(SIGWINCH);
  604. #endif
  605.  signal(SIGTTOU,SIG_IGN);
  606.  signal(SIGTTIN,SIG_IGN);
  607.  signal(SIGPIPE,SIG_IGN);
  608.  
  609.  ss_schedonce(ss_asap(),startup,0);
  610.  ss_exec();
  611.  die(0);
  612. }
  613.