unix/minit

serdo.c in master
Repositories | Summary | Log | Files | README | COPYING

serdo.c (5289B) download


  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
 14extern char** environ;
 15
 16#define MAXENV 256
 17char* envp[MAXENV+2];
 18int envc;
 19
 20int continueonerror;
 21
 22int 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
 39int 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");
 46failornot:
 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]!='-') {
 61ulimitsyntax:
 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
112int 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
155int 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
195int 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
210int 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}