check for null pointers; bug report 4.3BSD/etc/35
[unix-history] / usr / src / sbin / init / init.c
index 9b36c65..51a392b 100644 (file)
+/*
+ * Copyright (c) 1980,1986 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)init.c     5.8 (Berkeley) %G%";
+#endif not lint
+
 #include <signal.h>
 #include <sys/types.h>
 #include <utmp.h>
 #include <setjmp.h>
 #include <sys/reboot.h>
 #include <signal.h>
 #include <sys/types.h>
 #include <utmp.h>
 #include <setjmp.h>
 #include <sys/reboot.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <ttyent.h>
+#include <sys/syslog.h>
+#include <sys/stat.h>
 
 #define        LINSIZ  sizeof(wtmp.ut_line)
 
 #define        LINSIZ  sizeof(wtmp.ut_line)
-#define        TABSIZ  100
-#define        ALL     p = &itab[0]; p < &itab[TABSIZ]; p++
+#define        CMDSIZ  200     /* max string length for getty or window command*/
+#define        ALL     p = itab; p ; p = p->next
 #define        EVER    ;;
 #define SCPYN(a, b)    strncpy(a, b, sizeof(a))
 #define SCMPN(a, b)    strncmp(a, b, sizeof(a))
 
 char   shell[] = "/bin/sh";
 #define        EVER    ;;
 #define SCPYN(a, b)    strncpy(a, b, sizeof(a))
 #define SCMPN(a, b)    strncmp(a, b, sizeof(a))
 
 char   shell[] = "/bin/sh";
-char   getty[]  = "/etc/getty";
 char   minus[] = "-";
 char   runc[]  = "/etc/rc";
 char   minus[] = "-";
 char   runc[]  = "/etc/rc";
-char   ifile[] = "/etc/ttys";
-char   utmp[]  = "/etc/utmp";
+char   utmpf[] = "/etc/utmp";
 char   wtmpf[] = "/usr/adm/wtmp";
 char   ctty[]  = "/dev/console";
 char   wtmpf[] = "/usr/adm/wtmp";
 char   ctty[]  = "/dev/console";
-char   dev[]   = "/dev/";
 
 struct utmp wtmp;
 
 struct utmp wtmp;
-struct
-{
-       char    line[LINSIZ];
-       char    comn;
-       char    flag;
-} line;
 struct tab
 {
        char    line[LINSIZ];
 struct tab
 {
        char    line[LINSIZ];
-       char    comn;
+       char    comn[CMDSIZ];
        char    xflag;
        int     pid;
        char    xflag;
        int     pid;
-} itab[TABSIZ];
+       int     wpid;           /* window system pid for SIGHUP */
+       char    wcmd[CMDSIZ];   /* command to start window system process */
+       time_t  gettytime;
+       int     gettycnt;
+       time_t  windtime;
+       int     windcnt;
+       struct  tab *next;
+} *itab;
 
 int    fi;
 int    mergflag;
 char   tty[20];
 
 int    fi;
 int    mergflag;
 char   tty[20];
-jmp_buf        sjbuf;
+jmp_buf        sjbuf, shutpass;
+time_t time0;
 
 int    reset();
 
 int    reset();
+int    idle();
 char   *strcpy(), *strcat();
 long   lseek();
 
 char   *strcpy(), *strcat();
 long   lseek();
 
+struct sigvec rvec = { reset, sigmask(SIGHUP), 0 };
+
+
+#if defined(vax) || defined(tahoe)
 main()
 {
 main()
 {
+#if defined(tahoe)
+       register int r12;               /* make sure r11 gets bootflags */
+#endif
        register int r11;               /* passed thru from boot */
        register int r11;               /* passed thru from boot */
+#else
+main(argc, argv)
+       char **argv;
+{
+#endif
        int howto, oldhowto;
 
        int howto, oldhowto;
 
+       time0 = time(0);
+#if defined(vax) || defined(tahoe)
        howto = r11;
        howto = r11;
-       setjmp(sjbuf);
-       signal(SIGTERM, reset);
+#else
+       if (argc > 1 && argv[1][0] == '-') {
+               char *cp;
+
+               howto = 0;
+               cp = &argv[1][1];
+               while (*cp) switch (*cp++) {
+               case 'a':
+                       howto |= RB_ASKNAME;
+                       break;
+               case 's':
+                       howto |= RB_SINGLE;
+                       break;
+               }
+       } else {
+               howto = RB_SINGLE;
+       }
+#endif
+       openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
+       sigvec(SIGTERM, &rvec, (struct sigvec *)0);
+       signal(SIGTSTP, idle);
        signal(SIGSTOP, SIG_IGN);
        signal(SIGSTOP, SIG_IGN);
-       signal(SIGTSTP, SIG_IGN);
        signal(SIGTTIN, SIG_IGN);
        signal(SIGTTOU, SIG_IGN);
        signal(SIGTTIN, SIG_IGN);
        signal(SIGTTOU, SIG_IGN);
-       for(EVER) {
+       (void) setjmp(sjbuf);
+       for (EVER) {
                oldhowto = howto;
                howto = RB_SINGLE;
                oldhowto = howto;
                howto = RB_SINGLE;
-               shutdown();
+               if (setjmp(shutpass) == 0)
+                       shutdown();
                if (oldhowto & RB_SINGLE)
                        single();
                if (runcom(oldhowto) == 0) 
                if (oldhowto & RB_SINGLE)
                        single();
                if (runcom(oldhowto) == 0) 
@@ -70,49 +119,95 @@ main()
        }
 }
 
        }
 }
 
+int    shutreset();
+
 shutdown()
 {
        register i;
 shutdown()
 {
        register i;
-       register struct tab *p;
+       register struct tab *p, *p1;
 
 
-       close(creat(utmp, 0644));
+       close(creat(utmpf, 0644));
        signal(SIGHUP, SIG_IGN);
        signal(SIGHUP, SIG_IGN);
-       for(ALL) {
+       for (p = itab; p ; ) {
                term(p);
                term(p);
-               p->line[0] = 0;
+               p1 = p->next;
+               free(p);
+               p = p1;
        }
        }
-       signal(SIGALRM, reset);
-       alarm(60);
-       for(i=0; i<5; i++)
+       itab = (struct tab *)0;
+       signal(SIGALRM, shutreset);
+       (void) kill(-1, SIGTERM);       /* one chance to catch it */
+       sleep(5);
+       alarm(30);
+       for (i = 0; i < 5; i++)
                kill(-1, SIGKILL);
                kill(-1, SIGKILL);
-       while(wait((int *)0) != -1)
+       while (wait((int *)0) != -1)
                ;
        alarm(0);
                ;
        alarm(0);
+       shutend();
+}
+
+char shutfailm[] = "WARNING: Something is hung (won't die); ps axl advised\n";
+
+shutreset()
+{
+       int status;
+
+       if (fork() == 0) {
+               int ct = open(ctty, 1);
+               write(ct, shutfailm, sizeof (shutfailm));
+               sleep(5);
+               exit(1);
+       }
+       sleep(5);
+       shutend();
+       longjmp(shutpass, 1);
+}
+
+shutend()
+{
+       register i, f;
+
+       acct(0);
        signal(SIGALRM, SIG_DFL);
        signal(SIGALRM, SIG_DFL);
-       for(i=0; i<10; i++)
+       for (i = 0; i < 10; i++)
                close(i);
                close(i);
+       f = open(wtmpf, O_WRONLY|O_APPEND);
+       if (f >= 0) {
+               SCPYN(wtmp.ut_line, "~");
+               SCPYN(wtmp.ut_name, "shutdown");
+               SCPYN(wtmp.ut_host, "");
+               time(&wtmp.ut_time);
+               write(f, (char *)&wtmp, sizeof(wtmp));
+               close(f);
+       }
+       return (1);
 }
 
 single()
 {
        register pid;
 }
 
 single()
 {
        register pid;
-
-       pid = fork();
-       if(pid == 0) {
-/*
-               alarm(300);
-*/
-               signal(SIGTERM, SIG_DFL);
-               signal(SIGHUP, SIG_DFL);
-               signal(SIGALRM, SIG_DFL);
-               open(ctty, 2);
-               dup(0);
-               dup(0);
-               execl(shell, minus, (char *)0);
-               exit(0);
-       }
-       while(wait((int *)0) != pid)
-               ;
+       register xpid;
+       extern  errno;
+
+       do {
+               pid = fork();
+               if (pid == 0) {
+                       signal(SIGTERM, SIG_DFL);
+                       signal(SIGHUP, SIG_DFL);
+                       signal(SIGALRM, SIG_DFL);
+                       signal(SIGTSTP, SIG_IGN);
+                       (void) open(ctty, O_RDWR);
+                       dup2(0, 1);
+                       dup2(0, 2);
+                       execl(shell, minus, (char *)0);
+                       perror(shell);
+                       exit(0);
+               }
+               while ((xpid = wait((int *)0)) != pid)
+                       if (xpid == -1 && errno == ECHILD)
+                               break;
+       } while (xpid == -1);
 }
 
 runcom(oldhowto)
 }
 
 runcom(oldhowto)
@@ -122,226 +217,399 @@ runcom(oldhowto)
        int status;
 
        pid = fork();
        int status;
 
        pid = fork();
-       if(pid == 0) {
-               open("/", 0);
-               dup(0);
-               dup(0);
+       if (pid == 0) {
+               (void) open("/", O_RDONLY);
+               dup2(0, 1);
+               dup2(0, 2);
                if (oldhowto & RB_SINGLE)
                        execl(shell, shell, runc, (char *)0);
                else
                        execl(shell, shell, runc, "autoboot", (char *)0);
                exit(1);
        }
                if (oldhowto & RB_SINGLE)
                        execl(shell, shell, runc, (char *)0);
                else
                        execl(shell, shell, runc, "autoboot", (char *)0);
                exit(1);
        }
-       while(wait(&status) != pid)
+       while (wait(&status) != pid)
                ;
                ;
-       if(status)
-               return(0);
-       f = open(wtmpf, 1);
+       if (status)
+               return (0);
+       f = open(wtmpf, O_WRONLY|O_APPEND);
        if (f >= 0) {
        if (f >= 0) {
-               lseek(f, 0L, 2);
                SCPYN(wtmp.ut_line, "~");
                SCPYN(wtmp.ut_name, "reboot");
                SCPYN(wtmp.ut_line, "~");
                SCPYN(wtmp.ut_name, "reboot");
-               time(&wtmp.ut_time);
+               SCPYN(wtmp.ut_host, "");
+               if (time0) {
+                       wtmp.ut_time = time0;
+                       time0 = 0;
+               } else
+                       time(&wtmp.ut_time);
                write(f, (char *)&wtmp, sizeof(wtmp));
                close(f);
        }
                write(f, (char *)&wtmp, sizeof(wtmp));
                close(f);
        }
-       return(1);
-}
-
-setmerge()
-{
-
-       signal(SIGHUP, setmerge);
-       mergflag = 1;
+       return (1);
 }
 
 }
 
+struct sigvec  mvec = { merge, sigmask(SIGTERM), 0 };
+/*
+ * Multi-user.  Listen for users leaving, SIGHUP's
+ * which indicate ttys has changed, and SIGTERM's which
+ * are used to shutdown the system.
+ */
 multiple()
 {
        register struct tab *p;
        register pid;
 multiple()
 {
        register struct tab *p;
        register pid;
+       int omask;
 
 
-loop:
-       mergflag = 0;
-       signal(SIGHUP, setmerge);
-       for(EVER) {
+       sigvec(SIGHUP, &mvec, (struct sigvec *)0);
+       for (EVER) {
                pid = wait((int *)0);
                pid = wait((int *)0);
-               if(mergflag) {
-                       merge();
-                       goto loop;
-               }
-               if(pid == -1)
+               if (pid == -1)
                        return;
                        return;
-               for(ALL)
-                       if(p->pid == pid || p->pid == -1) {
+               omask = sigblock(sigmask(SIGHUP));
+               for (ALL) {
+                       /* must restart window system BEFORE emulator */
+                       if (p->wpid == pid || p->wpid == -1)
+                               wstart(p);
+                       if (p->pid == pid || p->pid == -1) {
+                               /* disown the window system */
+                               if (p->wpid)
+                                       kill(p->wpid, SIGHUP);
                                rmut(p);
                                dfork(p);
                        }
                                rmut(p);
                                dfork(p);
                        }
+               }
+               sigsetmask(omask);
        }
 }
 
        }
 }
 
-term(p)
-register struct tab *p;
-{
-
-       if(p->pid != 0) {
-               rmut(p);
-               kill(p->pid, SIGKILL);
-       }
-       p->pid = 0;
-}
-
-rline()
-{
-       register c, i;
-
-loop:
-       c = get();
-       if(c < 0)
-               return(0);
-       if(c == 0)
-               goto loop;
-       line.flag = c;
-       c = get();
-       if(c <= 0)
-               goto loop;
-       line.comn = c;
-       SCPYN(line.line, "");
-       for (i=0; i<LINSIZ; i++) {
-               c = get();
-               if(c <= 0)
-                       break;
-               line.line[i] = c;
-       }
-       while(c > 0)
-               c = get();
-       if(line.line[0] == 0)
-               goto loop;
-       if(line.flag == '0')
-               goto loop;
-       strcpy(tty, dev);
-       strncat(tty, line.line, LINSIZ);
-       if(access(tty, 06) < 0)
-               goto loop;
-       return(1);
-}
-
-get()
-{
-       char b;
-
-       if(read(fi, &b, 1) != 1)
-               return(-1);
-       if(b == '\n')
-               return(0);
-       return(b);
-}
-
+/*
+ * Merge current contents of ttys file
+ * into in-core table of configured tty lines.
+ * Entered as signal handler for SIGHUP.
+ */
 #define        FOUND   1
 #define        CHANGE  2
 #define        FOUND   1
 #define        CHANGE  2
+#define WCHANGE 4
 
 merge()
 {
        register struct tab *p;
 
 merge()
 {
        register struct tab *p;
+       register struct ttyent *t;
+       register struct tab *p1;
 
 
-       fi = open(ifile, 0);
-       if(fi < 0)
-               return;
-       for(ALL)
+       for (ALL)
                p->xflag = 0;
                p->xflag = 0;
-       while(rline()) {
-               for(ALL) {
-                       if (SCMPN(p->line, line.line))
+       setttyent();
+       while (t = getttyent()) {
+               if ((t->ty_status & TTY_ON) == 0)
+                       continue;
+               for (ALL) {
+                       if (SCMPN(p->line, t->ty_name))
                                continue;
                        p->xflag |= FOUND;
                                continue;
                        p->xflag |= FOUND;
-                       if(line.comn != p->comn) {
+                       if (SCMPN(p->comn, t->ty_getty)) {
                                p->xflag |= CHANGE;
                                p->xflag |= CHANGE;
-                               p->comn = line.comn;
+                               SCPYN(p->comn, t->ty_getty);
+                       }
+                       if (SCMPN(p->wcmd, t->ty_window ? t->ty_window : "")) {
+                               p->xflag |= WCHANGE|CHANGE;
+                               SCPYN(p->wcmd, t->ty_window);
                        }
                        goto contin1;
                }
                        }
                        goto contin1;
                }
-               for(ALL) {
-                       if(p->line[0] != 0)
-                               continue;
-                       SCPYN(p->line, line.line);
-                       p->xflag |= FOUND|CHANGE;
-                       p->comn = line.comn;
+
+               /*
+                * Make space for a new one
+                */
+               p1 = (struct tab *)calloc(1, sizeof(*p1));
+               if (!p1) {
+                       syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name);
                        goto contin1;
                }
                        goto contin1;
                }
+               /*
+                * Put new terminal at the end of the linked list.
+                */
+               if (itab) {
+                       for (p = itab; p->next ; p = p->next)
+                               ;
+                       p->next = p1;
+               } else
+                       itab = p1;
+
+               p = p1;
+               SCPYN(p->line, t->ty_name);
+               p->xflag |= FOUND|CHANGE;
+               SCPYN(p->comn, t->ty_getty);
+               if (t->ty_window && strcmp(t->ty_window, "") != 0) {
+                       p->xflag |= WCHANGE;
+                       SCPYN(p->wcmd, t->ty_window);
+               }
        contin1:
                ;
        }
        contin1:
                ;
        }
-       close(fi);
-       for(ALL) {
-               if((p->xflag&FOUND) == 0) {
-                       term(p);
-                       p->line[0] = 0;
-               }
-               if((p->xflag&CHANGE) != 0) {
+       endttyent();
+       p1 = (struct tab *)0;
+       for (ALL) {
+               if ((p->xflag&FOUND) == 0) {
                        term(p);
                        term(p);
-                       dfork(p);
+                       wterm(p);
+                       if (p1)
+                               p1->next = p->next;
+                       else
+                               itab = p->next;
+                       free(p);
+                       p = p1 ? p1 : itab;
+               } else {
+                       /* window system should be started first */
+                       if (p->xflag&WCHANGE) {
+                               wterm(p);
+                               wstart(p);
+                       }
+                       if (p->xflag&CHANGE) {
+                               term(p);
+                               dfork(p);
+                       }
                }
                }
+               p1 = p;
        }
 }
 
        }
 }
 
+term(p)
+       register struct tab *p;
+{
+
+       if (p->pid != 0) {
+               rmut(p);
+               kill(p->pid, SIGKILL);
+       }
+       p->pid = 0;
+       /* send SIGHUP to get rid of connections */
+       if (p->wpid > 0)
+               kill(p->wpid, SIGHUP);
+}
+
 dfork(p)
 dfork(p)
-struct tab *p;
+       struct tab *p;
 {
        register pid;
 {
        register pid;
-
+       time_t t;
+       int dowait = 0;
+
+       time(&t);
+       p->gettycnt++;
+       if ((t - p->gettytime) >= 60) {
+               p->gettytime = t;
+               p->gettycnt = 1;
+       } else if (p->gettycnt >= 5) {
+               dowait = 1;
+               p->gettytime = t;
+               p->gettycnt = 1;
+       }
        pid = fork();
        pid = fork();
-       if(pid == 0) {
+       if (pid == 0) {
                signal(SIGTERM, SIG_DFL);
                signal(SIGHUP, SIG_IGN);
                signal(SIGTERM, SIG_DFL);
                signal(SIGHUP, SIG_IGN);
-               strcpy(tty, dev);
-               strncat(tty, p->line, LINSIZ);
-               chown(tty, 0, 0);
-               chmod(tty, 0622);
-               open(tty, 2);
-               vhangup();
-               signal(SIGHUP, SIG_DFL);
-               open(tty, 2);
-               close(0);
-               dup(1);
-               dup(0);
-               tty[0] = p->comn;
-               tty[1] = 0;
-               execl(getty, minus, tty, (char *)0);
+               sigsetmask(0);  /* since can be called from masked code */
+               if (dowait) {
+                       syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line);
+                       closelog();
+                       sleep(30);
+               }
+               execit(p->comn, p->line);
                exit(0);
        }
        p->pid = pid;
 }
 
                exit(0);
        }
        p->pid = pid;
 }
 
+/*
+ * Remove utmp entry.
+ */
 rmut(p)
 rmut(p)
-register struct tab *p;
+       register struct tab *p;
 {
        register f;
 {
        register f;
-
-       f = open(utmp, 2);
-       if(f >= 0) {
-               while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
-                       if (SCMPN(wtmp.ut_line, p->line))
-                               continue;
-                       lseek(f, -(long)sizeof(wtmp), 1);
+       int found = 0;
+       static unsigned utmpsize;
+       static struct utmp *utmp;
+       register struct utmp *u;
+       int nutmp;
+       struct stat statbf;
+
+       f = open(utmpf, O_RDWR);
+       if (f >= 0) {
+               fstat(f, &statbf);
+               if (utmpsize < statbf.st_size) {
+                       utmpsize = statbf.st_size + 10 * sizeof(struct utmp);
+                       if (utmp)
+                               utmp = (struct utmp *)realloc(utmp, utmpsize);
+                       else
+                               utmp = (struct utmp *)malloc(utmpsize);
+                       if (!utmp)
+                               syslog(LOG_ERR, "utmp malloc failed");
+               }
+               if (statbf.st_size && utmp) {
+                       nutmp = read(f, utmp, statbf.st_size);
+                       nutmp /= sizeof(struct utmp);
+                       for (u = utmp ; u < &utmp[nutmp] ; u++) {
+                               if (SCMPN(u->ut_line, p->line) ||
+                                   u->ut_name[0]==0)
+                                       continue;
+                               lseek(f, ((long)u)-((long)utmp), L_SET);
+                               SCPYN(u->ut_name, "");
+                               SCPYN(u->ut_host, "");
+                               time(&u->ut_time);
+                               write(f, (char *)u, sizeof(*u));
+                               found++;
+                       }
+               }
+               close(f);
+       }
+       if (found) {
+               f = open(wtmpf, O_WRONLY|O_APPEND);
+               if (f >= 0) {
+                       SCPYN(wtmp.ut_line, p->line);
                        SCPYN(wtmp.ut_name, "");
                        SCPYN(wtmp.ut_name, "");
+                       SCPYN(wtmp.ut_host, "");
                        time(&wtmp.ut_time);
                        write(f, (char *)&wtmp, sizeof(wtmp));
                        time(&wtmp.ut_time);
                        write(f, (char *)&wtmp, sizeof(wtmp));
+                       close(f);
                }
                }
-               close(f);
-       }
-       f = open(wtmpf, 1);
-       if (f >= 0) {
-               SCPYN(wtmp.ut_line, p->line);
-               SCPYN(wtmp.ut_name, "");
-               time(&wtmp.ut_time);
-               lseek(f, (long)0, 2);
-               write(f, (char *)&wtmp, sizeof(wtmp));
-               close(f);
+               /*
+                * After a proper login force reset
+                * of error detection code in dfork.
+                */
+               p->gettytime = 0;
+               p->windtime = 0;
        }
 }
 
 reset()
 {
        }
 }
 
 reset()
 {
+
        longjmp(sjbuf, 1);
 }
 
        longjmp(sjbuf, 1);
 }
 
+jmp_buf        idlebuf;
 
 
+idlehup()
+{
+
+       longjmp(idlebuf, 1);
+}
+
+idle()
+{
+       register struct tab *p;
+       register pid;
+
+       signal(SIGHUP, idlehup);
+       for (EVER) {
+               if (setjmp(idlebuf))
+                       return;
+               pid = wait((int *) 0);
+               if (pid == -1) {
+                       sigpause(0);
+                       continue;
+               }
+               for (ALL) {
+                       /* if window system dies, mark it for restart */
+                       if (p->wpid == pid)
+                               p->wpid = -1;
+                       if (p->pid == pid) {
+                               rmut(p);
+                               p->pid = -1;
+                       }
+               }
+       }
+}
+
+wterm(p)
+       register struct tab *p;
+{
+       if (p->wpid != 0) {
+               kill(p->wpid, SIGKILL);
+       }
+       p->wpid = 0;
+}
+
+wstart(p)
+       register struct tab *p;
+{
+       register pid;
+       time_t t;
+       int dowait = 0;
+
+       time(&t);
+       p->windcnt++;
+       if ((t - p->windtime) >= 60) {
+               p->windtime = t;
+               p->windcnt = 1;
+       } else if (p->windcnt >= 5) {
+               dowait = 1;
+               p->windtime = t;
+               p->windcnt = 1;
+       }
+
+       pid = fork();
+
+       if (pid == 0) {
+               signal(SIGTERM, SIG_DFL);
+               signal(SIGHUP,  SIG_IGN);
+               sigsetmask(0);  /* since can be called from masked code */
+               if (dowait) {
+                       syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line);
+                       closelog();
+                       sleep(30);
+               }
+               execit(p->wcmd, p->line);
+               exit(0);
+       }
+       p->wpid = pid;
+}
+
+#define NARGS  20      /* must be at least 4 */
+#define ARGLEN 512     /* total size for all the argument strings */
+
+execit(s, arg)
+       char *s;
+       char *arg;      /* last argument on line */
+{
+       char *argv[NARGS], args[ARGLEN], *envp[1];
+       register char *sp = s;
+       register char *ap = args;
+       register char c;
+       register int i;
+
+       /*
+        * First we have to set up the argument vector.
+        * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2"). 
+        */
+       for (i = 1; i < NARGS - 2; i++) {
+               argv[i] = ap;
+               for (EVER) {
+                       if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) {
+                               *ap = '\0';
+                               goto done;
+                       }
+                       if (c == ' ') {
+                               *ap++ = '\0';
+                               while (*sp == ' ')
+                                       sp++;
+                               if (*sp == '\0')
+                                       goto done;
+                               break;
+                       }
+                       *ap++ = c;
+               }
+       }
+done:
+       argv[0] = argv[1];
+       argv[1] = "-";
+       argv[i+1] = arg;
+       argv[i+2] = 0;
+       envp[0] = 0;
+       execve(argv[0], &argv[1], envp);
+       /* report failure of exec */
+       syslog(LOG_ERR, "%s: %m", argv[0]);
+       closelog();
+       sleep(10);      /* prevent failures from eating machine */
+}