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 }