minit

A small yet feature-complete init (http://fefe.de/minit/)
Log | Files | Refs | README | LICENSE

minit.c (17432B)


      1 #define _GNU_SOURCE
      2 
      3 #include <sys/types.h>
      4 #include <time.h>
      5 #include <string.h>
      6 #include <fcntl.h>
      7 #include <unistd.h>
      8 #include <limits.h>
      9 #include <errno.h>
     10 #include <sys/socket.h>
     11 #include <sys/un.h>
     12 #include <sys/poll.h>
     13 #include <signal.h>
     14 #include <sys/wait.h>
     15 #include <stdio.h>
     16 #include <linux/kd.h>
     17 #include <sys/ioctl.h>
     18 #include <stdlib.h>
     19 #include <alloca.h>
     20 #include <sys/reboot.h>
     21 #include <write12.h>
     22 
     23 #include <libowfat/fmt.h>
     24 #include <libowfat/str.h>
     25 #include <libowfat/compiletimeassert.h>
     26 
     27 #include "minit.h"
     28 
     29 compiletimeassert(sizeof(MINITROOT) + 64 < PATH_MAX);
     30 
     31 #define MALLOC_TEST
     32 #if !defined(__dietlibc__) && !defined(__GLIBC__)
     33 #undef MALLOC_TEST
     34 #endif
     35 
     36 #ifdef MALLOC_TEST
     37 extern void* __libc_malloc(size_t size);
     38 extern void* __libc_realloc(void* x,size_t size);
     39 extern void __libc_free(void* x);
     40 static char malloc_buf[1024];
     41 static unsigned long n;
     42 static struct process procbuf[50];
     43 void *malloc(size_t size) {
     44   if (n+size<sizeof(malloc_buf)) {
     45     char* tmp=malloc_buf+n;
     46     n+=size;
     47     n=(n+3)&~3;
     48     return tmp;
     49   }
     50   return __libc_malloc(size);
     51 }
     52 void free(void* x) {
     53   if ((char*)x>=malloc_buf && (char*)x<malloc_buf+sizeof(malloc_buf)) return;
     54   __libc_free(x);
     55 }
     56 void* realloc(void* x,size_t size) {
     57   if (x==0 || x==procbuf) {
     58     void* y;
     59     if (size<=sizeof(procbuf))
     60       return procbuf;
     61     y=__libc_malloc(size);
     62     if (!y) return 0;
     63     memcpy(y,x,size);
     64     return y;
     65   }
     66   return __libc_realloc(x,size);
     67 }
     68 #endif
     69 
     70 char** Argv;
     71 
     72 #undef printf
     73 extern int printf(const char *format,...);
     74 
     75 extern void opendevconsole();
     76 
     77 #define UPDATE
     78 #ifdef UPDATE
     79 static int doupdate;
     80 #endif
     81 
     82 static int i_am_init;
     83 
     84 extern int openreadclose(char *fn, char **buf, size_t *len);
     85 extern char **split(char *buf,int c,size_t* len,size_t plus,size_t ofs);
     86 
     87 extern char **environ;
     88 
     89 #define HISTORY 10
     90 #ifdef HISTORY
     91 int history[HISTORY];
     92 #endif
     93 
     94 /* return index of service in process data structure or -1 if not found */
     95 int findservice(char *service) {
     96   int i;
     97   for (i=0; i<=maxprocess; ++i) {
     98     if (!strcmp(root[i].name,service))
     99       return i;
    100   }
    101   return -1;
    102 }
    103 
    104 /* look up process index in data structure by PID */
    105 int findbypid(pid_t pid) {
    106   int i;
    107   for (i=0; i<=maxprocess; ++i) {
    108     if (root[i].pid == pid)
    109       return i;
    110   }
    111   return -1;
    112 }
    113 
    114 /* clear circular dependency detection flags */
    115 void circsweep() {
    116   int i;
    117   for (i=0; i<=maxprocess; ++i)
    118     root[i].circular=0;
    119 }
    120 
    121 /* add process to data structure, return index or -1 */
    122 int addprocess(struct process *p) {
    123   if (maxprocess+1>=processalloc) {
    124     struct process *fump;
    125     processalloc+=8;
    126     if ((fump=(struct process *)realloc(root,processalloc*sizeof(struct process)))==0) return -1;
    127     root=fump;
    128   }
    129   memmove(&root[++maxprocess],p,sizeof(struct process));
    130   return maxprocess;
    131 }
    132 
    133 /* load a service into the process data structure and return index or -1
    134  * if failed */
    135 int loadservice(char *service) {
    136   struct process tmp;
    137   int fd;
    138   if (*service==0) return -1;
    139   fd=findservice(service);
    140   if (fd>=0) return fd;
    141   if (chdir(MINITROOT) || chdir(service)) return -1;
    142   if (!(tmp.name=strdup(service))) return -1;
    143   tmp.pid=0;
    144   fd=open("respawn",O_RDONLY);
    145   if (fd>=0) {
    146     tmp.respawn=1;
    147     close(fd);
    148   } else
    149     tmp.respawn=0;
    150   tmp.startedat=0;
    151   tmp.circular=0;
    152   tmp.__stdin=0; tmp.__stdout=1;
    153   {
    154     char *logservice=alloca(str_len(service)+5);
    155     strcpy(logservice,service);
    156     strcat(logservice,"/log");
    157     tmp.logservice=loadservice(logservice);
    158     if (tmp.logservice>=0) {
    159       int pipefd[2];
    160       if (pipe(pipefd)) return -1;
    161       fcntl(pipefd[0],F_SETFD,FD_CLOEXEC);
    162       fcntl(pipefd[1],F_SETFD,FD_CLOEXEC);
    163       root[tmp.logservice].__stdin=pipefd[0];
    164       tmp.__stdout=pipefd[1];
    165     }
    166   }
    167   return addprocess(&tmp);
    168 }
    169 
    170 /* usage: isup(findservice("sshd")).
    171  * returns nonzero if process is up */
    172 int isup(int service) {
    173   if (service<0) return 0;
    174   return (root[service].pid!=0);
    175 }
    176 
    177 int startservice(int service,int pause,int father);
    178 
    179 void sulogin() {	/* exiting on an initialization failure is not a good idea for init */
    180   char *argv[]={"sulogin",0};
    181   if (i_am_init)
    182     execve("/sbin/sulogin",argv,environ);
    183   _exit(1);
    184 }
    185 
    186 #undef debug
    187 void handlekilled(pid_t killed) {
    188   int i;
    189 #ifdef debug
    190   {
    191     char buf[50];
    192     snprintf(buf,50," %d\n",killed);
    193     write(2,buf,str_len(buf));
    194   }
    195 #endif
    196   if (killed == (pid_t)-1) {
    197     static int saidso;
    198     if (!saidso) { write(2,"all services exited.\n",21); saidso=1; }
    199     if (i_am_init) sulogin(); else exit(0);
    200   }
    201   if (killed==0) return;
    202   i=findbypid(killed);
    203 #if 0
    204   printf("%d exited, idx %d -> service %s\n",killed,i,i>=0?root[i].name:"[unknown]");
    205 #endif
    206   if (i>=0) {
    207     root[i].pid=0;
    208     if (root[i].respawn) {
    209 #if 0
    210       printf("restarting %s\n",root[i].name);
    211 #endif
    212       circsweep();
    213       startservice(i,time(0)-root[i].startedat<1,root[i].father);
    214     } else {
    215       root[i].startedat=time(0);
    216       root[i].pid=1;
    217     }
    218   }
    219 }
    220 
    221 /* called from inside the service directory, return the PID or 0 on error */
    222 pid_t forkandexec(int pause,int service) {
    223   char **argv=0;
    224   int count=0;
    225   pid_t p;
    226   int fd;
    227   size_t len;
    228   int islink;
    229   char *s=0;
    230   size_t argc;
    231   char *argv0=0;
    232 again:
    233   switch (p=fork()) {
    234   case (pid_t)-1:
    235     if (count>3) return 0;
    236     sleep(++count*2);
    237     goto again;
    238   case 0:
    239     /* child */
    240 
    241     if (i_am_init) {
    242       ioctl(0, TIOCNOTTY, 0);
    243       if (setsid() == -1) {
    244 	__write2("setsid failed unexpectedly.\n");
    245 	// This should never fail. init is run as root.
    246 	// If it does fail, don't exit for fear of bricking the system
    247       }
    248       opendevconsole();
    249 /*      ioctl(0, TIOCSCTTY, 1); */
    250       int r = tcsetpgrp(0, getpgrp());
    251       if (r == -1 && errno!=ENOTTY) {	// will get this error for log services
    252 	__write2("tcsetpgrp failed unexpectedly: ");
    253 	switch (errno) {
    254 	case EBADF: __write2("EBADF\n"); break;
    255 	case EINVAL: __write2("EINVAL\n"); break;
    256 //	case ENOTTY: __write2("ENOTTY\n"); break;
    257 	case EPERM: __write2("EPERM\n"); break;
    258 	default: __write2("unhandled\n");
    259 	}
    260 	// This should never fail. init is run as root.
    261 	// If it does fail, don't exit for fear of bricking the system
    262       }
    263     }
    264 
    265     if (pause) {
    266       struct timespec req;
    267       req.tv_sec=0;
    268       req.tv_nsec=500000000;
    269       nanosleep(&req,0);
    270     }
    271     if ((fd=open("in",O_RDONLY))!=-1) {
    272       if (dup2(fd,0) != 0) {
    273 	__write2("dup2 failed unexpectedly.\n");
    274 	// This should never fail. init is run as root.
    275 	// If it does fail, don't exit for fear of bricking the system
    276       }
    277       fcntl(0,F_SETFD,0);
    278     }
    279     if ((fd=open("out",O_WRONLY))!=-1) {
    280       if (dup2(fd,1) != 1 || dup2(fd,2) != 2) {
    281 	__write2("dup2 failed unexpectedly.\n");
    282 	// This should never fail. init is run as root.
    283 	// If it does fail, don't exit for fear of bricking the system
    284       }
    285       fcntl(1,F_SETFD,0);
    286       fcntl(2,F_SETFD,0);
    287     }
    288     // openreadclose and split allocate memory.
    289     // We leak it here, because we are in the child process that is
    290     // about to execve somebody else, at which point the OS frees all.
    291     if (!openreadclose("nice",&s,&len)) {
    292       int n=atoi(s);
    293       nice(n);
    294       s=0;
    295     }
    296     if (!openreadclose("params",&s,&len)) {
    297       argv=split(s,'\n',&argc,2,1);
    298       if (argc && argv[argc-1][0]==0) --argc;	// if params ended on newline, don't pass empty last arg
    299       argv[argc]=0;
    300     } else {
    301       argv=(char**)alloca(2*sizeof(char*));
    302       argv[1]=0;
    303     }
    304     argv0=(char*)alloca(PATH_MAX+1);
    305     if (!argv) _exit(1);
    306     if (readlink("run",argv0,PATH_MAX)<0) {
    307       if (errno!=EINVAL) _exit(1);	/* not a symbolic link */
    308       strcpy(argv0,"run");
    309       islink=0;
    310     } else
    311       islink=1;
    312     argv[0]=strrchr(argv0,'/');
    313     if (argv[0])
    314       argv[0]++;
    315     else
    316       argv[0]=argv0;
    317     argv[0]=strdupa(argv[0]);
    318     if (islink && argv0[0]!='/') {
    319       char* c=alloca(strlen(argv0)+sizeof(MINITROOT)+strlen(root[service].name+2));
    320       char* d;
    321       int fd=open(".",O_RDONLY|O_DIRECTORY);
    322       stpcpy(stpcpy(stpcpy(stpcpy(c,MINITROOT "/"),root[service].name),"/"),argv0);
    323       if (fd!=-1) {
    324 	/* c is now something like /etc/minit/default/../../../var/whatever/doit
    325 	* attempt to clean it up a little */
    326 	d=strrchr(c,'/');
    327 	*d=0;
    328 	if (chdir(c)==0) {
    329 	  *d='/';
    330 	  if (getcwd(argv0,PATH_MAX))
    331 	    strncat(argv0,d,PATH_MAX);
    332 	  fchdir(fd);
    333 	  close(fd);
    334 	} else {
    335 	  *d='/';
    336 	  argv0=c;
    337 	}
    338       } else
    339 	argv0=c;
    340     }
    341     if (root[service].__stdin != 0) {
    342       if (dup2(root[service].__stdin,0) != 0) {
    343 	__write2("dup2 failed unexpectedly.\n");
    344 	// This should never fail. init is run as root.
    345 	// If it does fail, don't exit for fear of bricking the system
    346       }
    347       fcntl(0,F_SETFD,0);
    348     }
    349     if (root[service].__stdout != 1) {
    350       if (dup2(root[service].__stdout,1) != 1 || dup2(root[service].__stdout,2) != 2) {
    351 	__write2("dup2 failed unexpectedly.\n");
    352 	// This should never fail. init is run as root.
    353 	// If it does fail, don't exit for fear of bricking the system
    354       }
    355       fcntl(1,F_SETFD,0);
    356       fcntl(2,F_SETFD,0);
    357     }
    358     {
    359       int i;
    360       for (i=3; i<1024; ++i) close(i);
    361     }
    362     chdir("root");
    363     execve(argv0,argv,environ);
    364     _exit(1);
    365   default:
    366     fd=open("sync",O_RDONLY);
    367     if (fd>=0) {
    368       close(fd);
    369       waitpid(p,0,0);
    370       return 1;
    371     }
    372     return p;
    373   }
    374 }
    375 
    376 /* start a service, return nonzero on error */
    377 int startnodep(int service,int pause) {
    378   /* step 1: see if the process is already up */
    379   if (isup(service)) return 0;
    380   /* step 2: fork and exec service, put PID in data structure */
    381   if (chdir(MINITROOT) || chdir(root[service].name)) return -1;
    382   root[service].startedat=time(0);
    383   root[service].pid=forkandexec(pause,service);
    384   return root[service].pid;
    385 }
    386 
    387 int startservice(int service,int pause,int father) {
    388   int dir=-1;
    389   unsigned long len;
    390   char *s=0;
    391   pid_t pid=0;
    392   if (service<0) return 0;
    393   if (root[service].circular)
    394     return 0;
    395   root[service].circular=1;
    396 #if 0
    397   printf("setting father of %d (%s) to %d (%s)\n",
    398 	 service,root[service].name,father,father>=0?root[father].name:"[msvc]");
    399 #endif
    400   root[service].father=father;
    401 #ifdef HISTORY
    402   {
    403     memmove(history+1,history,sizeof(int)*((HISTORY)-1));
    404     history[0]=service;
    405   }
    406 #endif
    407   if (root[service].logservice>=0)
    408     startservice(root[service].logservice,pause,service);
    409   if (chdir(MINITROOT) || chdir(root[service].name)) return -1;
    410   if ((dir=open(".",O_RDONLY))>=0) {
    411     // openreadclose allocates memory and reads the file contents into it. 
    412     // Need to free(s) independent of openreadclose return value
    413     if (!openreadclose("depends",&s,&len)) {
    414       char **deps=0;
    415       size_t depc,i;
    416       deps=split(s,'\n',&depc,0,0);
    417       for (i=0; i<depc; i++) {
    418 	int Service,blacklisted,j;
    419 	if (deps[i][0]=='#') continue;
    420 	Service=loadservice(deps[i]);
    421 
    422 #if 1
    423 	for (j=blacklisted=0; Argv[j]; ++j)
    424 	  if (Argv[j][0]=='-' && !strcmp(Argv[j]+1,deps[i])) {
    425 	    blacklisted=1;
    426 	    ++Argv[j];
    427 	    break;
    428 	  }
    429 #endif
    430 
    431 	if (Service>=0 && root[Service].pid!=1 && !blacklisted)
    432 	  startservice(Service,0,service);
    433       }
    434       fchdir(dir);
    435       free(deps);
    436     }
    437     free(s);
    438     pid=startnodep(service,pause);
    439 
    440 #if 0
    441     write(1,"started service ",17);
    442     write(1,root[service].name,str_len(root[service].name));
    443     write(1," -> ",4);
    444     {
    445       char buf[10];
    446       snprintf(buf,10,"%d\n",pid);
    447       write(1,buf,str_len(buf));
    448     }
    449 #endif
    450     close(dir);
    451     dir=-1;
    452   }
    453   chdir(MINITROOT);
    454   return pid;
    455 }
    456 
    457 static void _puts(const char* s) {
    458   write(1,s,str_len(s));
    459 }
    460 
    461 void childhandler() {
    462   int status;
    463   pid_t killed;
    464 #ifdef debug
    465   write(2,"wait...",7);
    466 #endif
    467 #if 0
    468   if (getpid()!=1) {
    469     char buf[100];
    470     _puts("childhandler() called from pid ");
    471     buf[fmt_ulong(buf,getpid())]=0;
    472     _puts(buf);
    473     _puts("\n");
    474     return;
    475   }
    476 #endif
    477 #ifdef UPDATE
    478 if (doupdate) return;
    479 #endif
    480 
    481   do {
    482     killed=waitpid(-1,&status,WNOHANG);
    483     handlekilled(killed);
    484   } while (killed && killed!=(pid_t)-1);
    485 }
    486 
    487 static volatile int dowinch=0;
    488 static volatile int doint=0;
    489 
    490 void sigchild(int sig) { (void)sig; }
    491 void sigwinch(int sig) { (void)sig; dowinch=1; }
    492 void sigint(int sig) { (void)sig; doint=1; }
    493 
    494 int main(int argc, char *argv[]) {
    495   /* Schritt 1: argv[1] als Service nehmen und starten */
    496   int count=0;
    497   int i;
    498   struct pollfd pfd;
    499   time_t last=time(0);
    500   int nfds=1;
    501 
    502 #ifdef HISTORY
    503   for (i=0; i<HISTORY; ++i)
    504     history[i]=-1;
    505 #endif
    506 
    507   Argv=argv;
    508 
    509   infd=open(MINITROOT "/in",O_RDWR);
    510   outfd=open(MINITROOT "/out",O_RDWR|O_NONBLOCK);
    511 
    512   if (getpid()==1) {
    513     int fd;
    514     i_am_init=1;
    515     reboot(RB_DISABLE_CAD);
    516     if ((fd=open("/dev/console",O_RDWR|O_NOCTTY))!=-1) {
    517       ioctl(fd, KDSIGACCEPT, SIGWINCH);
    518       close(fd);
    519     } else
    520       ioctl(0, KDSIGACCEPT, SIGWINCH);
    521   }
    522 /*  signal(SIGPWR,sighandler); don't know what to do about it */
    523 /*  signal(SIGHUP,sighandler); ??? */
    524   {
    525     static struct sigaction sa;
    526     sigemptyset(&sa.sa_mask);
    527     errno=0;
    528     sa.sa_sigaction=0;
    529     sa.sa_flags=SA_RESTART | SA_NOCLDSTOP;
    530     sa.sa_handler=sigchild; sigaction(SIGCHLD,&sa,0);
    531     sa.sa_flags=SA_RESTART;
    532     if (i_am_init) {
    533       sa.sa_handler=sigint; sigaction(SIGINT,&sa,0);	/* ctrl-alt-del */
    534       sa.sa_handler=sigwinch; sigaction(SIGWINCH,&sa,0);	/* keyboard request */
    535     }
    536     if (errno) _puts("sigaction failed!\n");
    537   }
    538 
    539   if (infd<0 || outfd<0) {
    540     _puts("minit: could not open " MINITROOT "/in or " MINITROOT "/out\n");
    541     sulogin();
    542     nfds=0;
    543   } else
    544     pfd.fd=infd;
    545   pfd.events=POLLIN;
    546 
    547   fcntl(infd,F_SETFD,FD_CLOEXEC);
    548   fcntl(outfd,F_SETFD,FD_CLOEXEC);
    549 
    550 #ifdef UPDATE
    551   {
    552    struct flock fl;
    553    fl.l_whence=SEEK_CUR;
    554    fl.l_start=0;
    555    fl.l_len=0;
    556    fl.l_pid=0;
    557    if ( (0 == fcntl(infd,F_GETLK,&fl)) &&
    558    		(fl.l_type != F_UNLCK )) doupdate=1;
    559   }
    560 
    561   if(!doupdate) {
    562 #endif
    563   for (i=1; i<argc; i++) {
    564     circsweep();
    565     if (startservice(loadservice(argv[i]),0,-1)) count++;
    566    }
    567    circsweep();
    568    if (!count) startservice(loadservice("default"),0,-1);
    569 #ifdef UPDATE
    570   }
    571 #endif
    572   for (;;) {
    573     int i;
    574     char buf[1501];
    575     time_t now;
    576     if (doint) {
    577       doint=0;
    578       startservice(loadservice("ctrlaltdel"),0,-1);
    579     }
    580     if (dowinch) {
    581       dowinch=0;
    582       startservice(loadservice("kbreq"),0,-1);
    583     }
    584     childhandler();
    585     now=time(0);
    586     if (now<last || now-last>30) {
    587       /* The system clock was reset.  Compensate. */
    588       long diff=last-now;
    589       int j;
    590 
    591       for (j=0; j<=maxprocess; ++j)
    592 	root[j].startedat-=diff;
    593     }
    594     last=now;
    595     switch (poll(&pfd,nfds,5000)) {
    596     case -1:
    597       if (errno==EINTR) {
    598 	childhandler();
    599 	break;
    600       }
    601       opendevconsole();
    602       _puts("poll failed!\n");
    603       sulogin();
    604       /* what should we do if poll fails?! */
    605       break;
    606     case 1:
    607       i=read(infd,buf,1500);
    608       if (i>1) {
    609 	int idx=0,tmp;
    610 	buf[i]=0;
    611 
    612 /*	write(1,buf,str_len(buf)); write(1,"\n",1); */
    613 #ifdef UPDATE
    614 	if(!strcmp(buf,"update")) {
    615 	  execve("/sbin/minit",argv, environ);
    616 	}
    617 
    618 	if (((buf[0]!='U') && buf[0]!='s') && ((idx=findservice(buf+1))<0)
    619 	    && strcmp(buf,"d-"))
    620 #else
    621 	if (buf[0]!='s' && ((idx=findservice(buf+1))<0) && strcmp(buf,"d-") )
    622 #endif
    623 error:
    624 	  write(outfd,"0",1);
    625 	else {
    626 	  switch (buf[0]) {
    627 	  case 'p':
    628 	    write(outfd,buf,fmt_ulong(buf,root[idx].pid));
    629 	    break;
    630 #ifdef UPDATE
    631 	  case 'D':
    632 	    doupdate=1;
    633 	    write(outfd, &root[idx], sizeof(struct process));
    634 	    break;
    635 	  case 'U':
    636 	    doupdate=1;
    637 	    write(outfd,"1",1);
    638 	    if (1==poll(&pfd,nfds,5000)) {
    639 	      struct process tmp;
    640 	      if (read(infd,&tmp,sizeof tmp) != sizeof(tmp)) {
    641 		__write2("update failed, struct size mismatch.\n");
    642 		goto ok;
    643 	      }
    644 	      tmp.name=strdup(buf+1);
    645 	      addprocess(&tmp);
    646 	    }
    647 	    goto ok;
    648 #endif
    649 	  case 'r':
    650 	    root[idx].respawn=0;
    651 	    goto ok;
    652 	  case 'R':
    653 	    root[idx].respawn=1;
    654 	    goto ok;
    655 	  case 'C':
    656 	    if (kill(root[idx].pid,0)) {	/* check if still active */
    657 	      handlekilled(root[idx].pid);	/* no!?! remove form active list */
    658 	      goto error;
    659 	    }
    660 	    goto ok;
    661 	  case 'P':
    662 	    {
    663 	      unsigned char *x=(unsigned char*)buf+str_len(buf)+1;
    664 	      unsigned char c;
    665 	      tmp=0;
    666 	      while ((c=*x++-'0')<10) tmp=tmp*10+c;
    667 	    }
    668 	    if (tmp>0) {
    669 	      if (kill(tmp,0)) goto error;
    670 	    }
    671 	    root[idx].pid=tmp;
    672 	    goto ok;
    673 	  case 's':
    674 	    idx=loadservice(buf+1);
    675 	    if (idx<0) goto error;
    676 	    if (root[idx].pid<2) {
    677 	      root[idx].pid=0;
    678 	      circsweep();
    679 	      idx=startservice(idx,0,-1);
    680 	      if (idx==0) {
    681 		write(outfd,"0",1);
    682 		break;
    683 	      }
    684 	    }
    685 ok:
    686 	    write(outfd,"1",1);
    687 	    break;
    688 	  case 'u':
    689 	    write(outfd,buf,fmt_ulong(buf,time(0)-root[idx].startedat));
    690 	    break;
    691 	  case 'd':
    692 	    write(outfd,"1:",2);
    693 	    {
    694 	      int i;
    695 #if 0
    696 	      printf("looking for father==%d\n",idx);
    697 #endif
    698 	      for (i=0; i<=maxprocess; ++i) {
    699 #if 0
    700 		printf("pid of %d(%s) is %lu, father is %d\n",
    701 		       i,root[i].name?root[i].name:"[none]",root[i].pid,root[i].father);
    702 #endif
    703 		if (root[i].father==idx)
    704 		  write(outfd,root[i].name,str_len(root[i].name)+1);
    705 	      }
    706 	      write(outfd,"\0",2);
    707 	    }
    708 	    break;
    709 	  }
    710 	}
    711       } else {
    712 	if (buf[0]=='h') {
    713 #ifdef HISTORY
    714 	  write(outfd,"1:",2);
    715 	  {
    716 	    int i;
    717 	    for (i=0; i<HISTORY; ++i)
    718 	      if (history[i]!=-1)
    719 		write(outfd,root[history[i]].name,str_len(root[history[i]].name)+1);
    720 	    write(outfd,"\0",2);
    721 	  }
    722 #else
    723 	  write(outfd,"0",1);
    724 #endif
    725 	}
    726       }
    727       break;
    728     default:
    729 #ifdef UPDATE
    730       doupdate=0;
    731 #endif
    732       break;
    733     }
    734   }
    735 }