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}