minit

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

serdo.c (5289B)


      1 #include <unistd.h>
      2 #include <fcntl.h>
      3 #include <sys/stat.h>
      4 #include <ctype.h>
      5 #include <sys/wait.h>
      6 #include <sys/resource.h>
      7 #include <string.h>
      8 
      9 #include <libowfat/str.h>
     10 #include <libowfat/byte.h>
     11 #include <libowfat/scan.h>
     12 #include <libowfat/errmsg.h>
     13 
     14 extern char** environ;
     15 
     16 #define MAXENV 256
     17 char* envp[MAXENV+2];
     18 int envc;
     19 
     20 int continueonerror;
     21 
     22 int envset(char* s) {
     23   int i,l;
     24   if (s[l=str_chr(s,'=')]!='=') return -1;
     25   ++l;
     26   for (i=0; i<envc; ++i)
     27     if (byte_equal(envp[i],l,s)) {
     28       envp[i]=s;
     29       return 0;
     30     }
     31   if (envc<MAXENV) {
     32     envp[envc]=s;
     33     envp[++envc]=0;
     34     return 0;
     35   }
     36   return -1;
     37 }
     38 
     39 int spawn(char** argv, int last) {
     40   int i,ignore;
     41   ignore=(argv[0][0]=='-');
     42   if (ignore) ++argv[0];
     43   if (str_equal(argv[0],"cd")) {
     44     if (chdir(argv[1])==-1) {
     45       carpsys("chdir failed");
     46 failornot:
     47       return ignore?0:-1;
     48     }
     49     return 0;
     50   } else if (str_equal(argv[0],"export")) {
     51     for (i=1; argv[i]; ++i) envset(argv[i]);
     52     return 0;
     53   } else if (str_equal(argv[0],"exec")) {
     54     ++argv;
     55     last=1;
     56   } else if (str_equal(argv[0],"ulimit")) {
     57     struct rlimit rl;
     58     for (i=1; argv[i] && argv[i+1]; i+=2) {
     59       int id=-1;
     60       if (argv[i][0]!='-') {
     61 ulimitsyntax:
     62 	carp("ulimit syntax error: ",argv[i]);
     63 	continue;
     64       }
     65       switch(argv[i][1]) {
     66 	case 'c': id=RLIMIT_CORE; break;
     67 	case 'd': id=RLIMIT_DATA; break;
     68 	case 'e': id=RLIMIT_NICE; break;
     69 	case 'f': id=RLIMIT_FSIZE; break;
     70 	case 'i': id=RLIMIT_SIGPENDING; break;
     71 	case 'l': id=RLIMIT_MEMLOCK; break;
     72 	case 'm': id=RLIMIT_RSS; break;
     73 	case 'n': id=RLIMIT_NOFILE; break;
     74 	case 'r': id=RLIMIT_RTPRIO; break;
     75 	case 's': id=RLIMIT_STACK; break;
     76 	case 't': id=RLIMIT_CPU; break;
     77 	case 'u': id=RLIMIT_NPROC; break;
     78 	case 'v': id=RLIMIT_AS; break;
     79 	case 'x': id=RLIMIT_LOCKS; break;
     80 	default: goto ulimitsyntax;
     81       }
     82       if (!strcmp(argv[i+1],"unlimited")) {
     83 	rl.rlim_cur=rl.rlim_max=RLIM_INFINITY;
     84       } else {
     85 	unsigned long ul;
     86 	if (argv[i+1][scan_ulong(argv[i+1],&ul)])
     87 	  goto ulimitsyntax;
     88 	rl.rlim_cur=rl.rlim_max=ul;
     89       }
     90       if (setrlimit(id,&rl)==-1) {
     91 	carpsys("ulimit failed");
     92 	goto failornot;
     93       }
     94     }
     95     return 0;
     96   }
     97   if (!last) {
     98     if ((i=fork())==-1) diesys(1,"cannot fork");
     99   } else i=0;
    100   if (!i) {
    101     /* child */
    102     environ=envp;
    103     _exit(execvp(argv[0],argv));
    104   }
    105   if (waitpid(i,&i,0)==-1) diesys(1,"waitpid failed");
    106   if (ignore) return 0;
    107   if (!WIFEXITED(i))
    108     return -1;
    109   return WEXITSTATUS(i);
    110 }
    111 
    112 int run(char* s,int last) {
    113   int i,spaces;
    114   char** argv,**next;
    115   for (i=spaces=0; s[i]; ++i) if (s[i]==' ') ++spaces;
    116   next=argv=alloca((spaces+2)*sizeof(char*));
    117 
    118   while (*s) {
    119     while (*s && isspace(*s)) ++s;
    120     if (*s=='"') {
    121       ++s;
    122       *next=s;
    123       while (*s && (*s != '"' || s[-1] == '\\')) ++s;
    124       if (!*s) {
    125 	--*next;
    126 	break;
    127       }
    128       *s=0;
    129       ++s;
    130     } else if (*s=='\'') {
    131       ++s;
    132       *next=s;
    133       while (*s && (*s != '\'' || s[-1] == '\\')) ++s;
    134       if (!*s) {
    135 	--*next;
    136 	break;
    137       }
    138       *s=0;
    139       ++s;
    140     } else {
    141       *next=s;
    142       while (*s && *s!=' ' && *s!='\t') ++s;
    143       if (*s) {
    144 	*s=0;
    145 	++s;
    146       }
    147     }
    148     ++next;
    149   }
    150   *next=0;
    151 
    152   return spawn(argv,last);
    153 }
    154 
    155 int execute(char* s,int dontexeclast) {
    156   char* start;
    157   int r;
    158   r=0;
    159   while (*s) {
    160     int last;
    161     while (isspace(*s)) ++s;
    162     if (*s == '#') {
    163       while (*s && *s != '\n') ++s;
    164       continue;
    165     }
    166     start=s;
    167 
    168     while (*s && *s != '\n') ++s;
    169     /* advance to the end of the line */
    170     if (*s) {
    171       /* not the last line in the file */
    172       char* tmp;
    173       *s=0;
    174       ++s;
    175       /* If it's the last command in the file, we do not fork+exec but
    176        * we execve it directly.  So we need to find out here if this is
    177        * the last command in the file.  For that we need to skip all the
    178        * lines after it that are comments */
    179       for (tmp=s; *tmp; ++tmp)
    180 	if (!isspace(*tmp) && *tmp=='#') {
    181 	  for (++tmp; *tmp && *tmp!='\n'; ++tmp) ;
    182 	} else
    183 	  break;
    184       last=(*tmp==0);
    185     } else
    186       last=1;
    187     if (dontexeclast) last=0;
    188     r=run(start,last);
    189     if (r!=0 && !continueonerror)
    190       break;
    191   }
    192   return r;
    193 }
    194 
    195 int batch(char* s,int dontexeclast) {
    196   struct stat ss;
    197   int fd=open(s,O_RDONLY);
    198   char* map;
    199   if (fd==-1) diesys(1,"could not open ",s);
    200   if (fstat(fd,&ss)==-1) diesys(1,"could not stat ",s);
    201   if (ss.st_size>32768) die(1,"file ",s," is too large");
    202   map=alloca(ss.st_size+1);
    203   if (read(fd,map,ss.st_size)!=(long)ss.st_size) diesys(1,"read error");
    204   map[ss.st_size]=0;
    205   close(fd);
    206 
    207   return execute(map,dontexeclast);
    208 }
    209 
    210 int main(int argc,char* argv[],char* env[]) {
    211   static char* fakeargv[]={0,"script",0};
    212   int r;
    213   int failing=0;
    214   int havefail=!access("fail",O_RDONLY);
    215   (void)argc;
    216   if (argc<2) {
    217     if (!access("script",O_RDONLY))
    218       argv=fakeargv;
    219     else
    220       die(1,"usage: serdo [-c] filename");
    221   }
    222   errmsg_iam("serdo");
    223   for (envc=0; envc<MAXENV && env[envc]; ++envc) envp[envc]=env[envc];
    224   envp[envc]=0;
    225   if (str_equal(argv[1],"-c")) {
    226     continueonerror=1;
    227     ++argv;
    228   }
    229   while (*++argv) {
    230     if ((r=batch(*argv,!!argv[1] || (havefail && !failing)))) {
    231       if (!failing && havefail) {
    232 	fakeargv[1]="fail";
    233 	argv=fakeargv;
    234 	failing=1;
    235 	continue;
    236       }
    237       return r;
    238     }
    239   }
    240   return 0;
    241 }