1 #include <u.h> 2 #include <libc.h> 3 #include <ctype.h> 4 5 void doalldirs(void); 6 void dodir(char*); 7 void dofile(Dir*); 8 void rundir(char*); 9 char* file(char*, char); 10 void warning(char*, void*); 11 void error(char*, void*); 12 int returnmail(char**, char*, char*); 13 14 #define HUNK 32 15 char *cmd; 16 char *root; 17 int debug; 18 int giveup = 2*24*60*60; /* 19 20 /* the current directory */ 21 Dir *dirbuf; 22 long ndirbuf = 0; 23 int nfiles; 24 25 26 27 void 28 usage(void) 29 { 30 fprint(2, "usage: runq [-a] q-root cmd\n"); 31 exits(""); 32 } 33 34 void 35 main(int argc, char **argv) 36 { 37 char *user; 38 int all; 39 40 user = getenv("user"); 41 all = 0; 42 43 ARGBEGIN{ 44 case 'a': 45 all++; 46 break; 47 case 'd': 48 debug++; 49 break; 50 case 't': 51 giveup = 60*60*atoi(ARGF()); 52 break; 53 }ARGEND; 54 55 if(argc != 2) 56 usage(); 57 58 root = argv[0]; 59 cmd = argv[1]; 60 61 if(chdir(root) < 0) 62 error("can't cd to %s", root); 63 64 if(all) 65 doalldirs(); 66 else 67 dodir(user); 68 exits(0); 69 } 70 71 void 72 setuser(char *name) 73 { 74 int fd; 75 char buf[256]; 76 77 /* 78 * become THAT user 79 */ 80 fd = create("/dev/user", OWRITE, 0600); 81 if(fd < 0) 82 error("opening /dev/user", 0); 83 if(write(fd, name, strlen(name))<0) 84 error("couldn't set user name %s", name); 85 close(fd); 86 87 /* 88 * mount /srv/boot /mnt 89 */ 90 fd = open("/srv/boot", 2); 91 if(fd < 0) 92 error("opening /srv/boot", 0); 93 if(mount(fd, "/mnt", MREPL, "") < 0) 94 error("mounting", 0); 95 close(fd); 96 97 /* 98 * bind /mnt/<root> /<root> 99 */ 100 sprint(buf, "/mnt/%s", root); 101 if(bind(buf, root, MREPL) < 0) 102 error("binding", 0); 103 } 104 105 int 106 emptydir(char *name) 107 { 108 int fd; 109 long n; 110 Dir db2; 111 112 fd = open(name, OREAD); 113 if(fd < 0) 114 return 1; 115 n = read(fd, &db2, sizeof db2); 116 close(fd); 117 if(n <= 0) 118 return 1; 119 return 0; 120 } 121 122 /* 123 * run all user directories, must be bootes to do this 124 */ 125 void 126 doalldirs(void) 127 { 128 Dir db[HUNK]; 129 int fd; 130 long i, n; 131 132 fd = open(".", OREAD); 133 if(fd == -1){ 134 warning("reading %s", root); 135 return; 136 } 137 while((n=dirread(fd, db, sizeof db)) > 0){ 138 n /= sizeof(Dir); 139 for(i=0; i<n; i++){ 140 if(db[i].qid.path&CHDIR){ 141 if(emptydir(db[i].name)) 142 continue; 143 switch(fork()){ 144 case -1: 145 warning("couldn't fork", 0); 146 break; 147 case 0: 148 if(rfork(RFENVG|RFNAMEG|RFNOTEG)<0) 149 error("rfork failed", 0); 150 /* setuser(db[i].name); /**/ 151 dodir(db[i].name); 152 exits(0); 153 default: 154 wait(0); 155 break; 156 } 157 } 158 } 159 } 160 close(fd); 161 } 162 163 /* 164 * Read a whole directory before removing anything as the holes formed 165 * by removing affect the read offset. 166 */ 167 long 168 readdirect(int fd) 169 { 170 enum 171 { 172 N = 32 173 }; 174 long m, n; 175 176 m = 1; /* prime the loop */ 177 for(n=0; m>0; n+=m/sizeof(Dir)){ 178 if(n == ndirbuf){ 179 dirbuf = realloc(dirbuf, (ndirbuf+N)*sizeof(Dir)); 180 if(dirbuf == 0){ 181 warning("memory allocation", 0); 182 return 0; 183 } 184 ndirbuf += N; 185 } 186 m = dirread(fd, dirbuf+n, (ndirbuf-n)*sizeof(Dir)); 187 } 188 return n; 189 } 190 191 /* 192 * cd to a user directory and run it 193 */ 194 void 195 dodir(char *name) 196 { 197 if(chdir(name) < 0){ 198 warning("cd to %s", name); 199 return; 200 } 201 if(debug) 202 fprint(2, "running %s\n", name); 203 rundir(name); 204 chdir(".."); 205 } 206 207 /* 208 * run the current directory 209 */ 210 void 211 rundir(char *name) 212 { 213 int fd; 214 long i; 215 216 fd = open(".", OREAD); 217 if(fd == -1){ 218 warning("reading %s", name); 219 return; 220 } 221 nfiles = readdirect(fd); 222 close(fd); 223 224 for(i=0; i<nfiles; i++){ 225 if(dirbuf[i].name[0]!='C' || dirbuf[i].name[1]!='.') 226 continue; 227 dofile(&dirbuf[i]); 228 } 229 } 230 231 /* 232 * free files matching name in the current directory 233 */ 234 void 235 remmatch(char *name) 236 { 237 long i; 238 239 for(i=0; i<nfiles; i++){ 240 if(strcmp(&dirbuf[i].name[1], &name[1]) == 0) 241 remove(dirbuf[i].name); 242 } 243 244 /* error file (may have) appeared after we read the directory */ 245 remove(file(name, 'E')); 246 } 247 248 /* 249 * try a message 250 */ 251 void 252 dofile(Dir *dp) 253 { 254 Dir d; 255 int dfd; 256 int cfd; 257 char *buf; 258 char *cp; 259 int ac; 260 char **av; 261 Waitmsg wm; 262 int dtime; 263 int efd; 264 265 if(debug) 266 fprint(2, "dofile %s\n", dp->name); 267 268 /* 269 * if no data file, just clean up 270 */ 271 if(dirstat(file(dp->name, 'D'), &d) < 0){ 272 remmatch(dp->name); 273 return; 274 } 275 dtime = d.mtime; 276 277 /* 278 * retry times depend on the age of the errors file 279 */ 280 if(dirstat(file(dp->name, 'E'), &d) >= 0){ 281 if(d.mtime - dtime < 60*60){ 282 /* up to the first hour, try every 15 minutes */ 283 if(time(0) - d.mtime < 15*60) 284 return; 285 } else { 286 /* after the first hour, try once an hour */ 287 if(time(0) - d.mtime < 60*60) 288 return; 289 } 290 } 291 292 /* 293 * open control and data 294 */ 295 cfd = open(file(dp->name, 'C'), OREAD); 296 if(cfd < 0) 297 return; 298 dfd = open(file(dp->name, 'D'), OREAD); 299 if(dfd < 0){ 300 close(cfd); 301 return; 302 } 303 304 /* 305 * make arg list 306 * - read args into (malloc'd) buffer 307 * - count args 308 * - malloc a vector and copy pointers to args into it 309 */ 310 buf = malloc(dp->length+1); 311 if(read(cfd, buf, dp->length) != dp->length){ 312 warning("reading control file %s\n", dp->name); 313 goto out; 314 } 315 buf[dp->length] = 0; 316 for(ac = 0, cp = buf; *cp; ac++){ 317 while(isspace(*cp)) 318 cp++; 319 while(*cp && !isspace(*cp)) 320 cp++; 321 } 322 av = malloc((ac+2)*sizeof(char *)); 323 av[0] = cmd; 324 for(ac = 1, cp = buf; *cp; ac++){ 325 while(isspace(*cp)) 326 *cp++ = 0; 327 av[ac] = cp; 328 if(*cp == '"'){ 329 cp++; 330 while(*cp && *cp!='"') 331 cp++; 332 if(*cp) 333 cp++; 334 } else { 335 while(*cp && !isspace(*cp)) 336 cp++; 337 } 338 while(isspace(*cp)) 339 *cp++ = 0; 340 } 341 av[ac] = 0; 342 343 if(time(0) - dtime > giveup){ 344 if(returnmail(av, dp->name, wm.msg) == 0) 345 remmatch(dp->name); 346 return; 347 } 348 349 /* 350 * transfer 351 */ 352 switch(fork()){ 353 case -1: 354 return; 355 case 0: 356 close(0); 357 dup(dfd, 0); 358 close(2); 359 efd = open(dp->name, OWRITE); 360 if(efd < 0) 361 efd = create(file(dp->name, 'E'), OWRITE, 0664); 362 if(efd < 0) 363 exits(""); 364 seek(efd, 0, 2); 365 close(dfd); 366 close(cfd); 367 exec(cmd, av); 368 error("can't exec %s", cmd); 369 break; 370 default: 371 wait(&wm); 372 if(wm.msg[0]){ 373 if(debug) 374 fprint(2, "wm.msg == %s\n", wm.msg); 375 if(strstr(wm.msg, "Retry")==0){ 376 /* return the message and remove it */ 377 if(returnmail(av, dp->name, wm.msg) == 0) 378 remmatch(dp->name); 379 } else { 380 /* try again later */ 381 } 382 } else { 383 /* it worked remove the message */ 384 remmatch(dp->name); 385 } 386 387 } 388 return; 389 out: 390 close(cfd); 391 close(dfd); 392 } 393 394 /* 395 * return a name starting with the given character 396 */ 397 char* 398 file(char *name, char type) 399 { 400 static char nname[NAMELEN+1]; 401 402 strcpy(nname, name); 403 nname[0] = type; 404 return nname; 405 } 406 407 /* 408 * send back the mail with an error message 409 * 410 * return 0 if successful 411 */ 412 int 413 returnmail(char **av, char *name, char *msg) 414 { 415 int pfd[2]; 416 Waitmsg wm; 417 char *sender; 418 int fd; 419 char buf[256]; 420 int i; 421 long n; 422 423 sender = av[2]; 424 425 if(pipe(pfd) < 0) 426 return -1; 427 428 switch(fork()){ 429 case -1: 430 return -1; 431 case 0: 432 close(pfd[1]); 433 close(0); 434 dup(pfd[0], 0); 435 close(pfd[0]); 436 execl("/bin/upas/sendmail", "sendmail", sender, 0); 437 error("can't exec", 0); 438 break; 439 default: 440 break; 441 } 442 443 close(pfd[0]); 444 if(av[1]){ 445 fprint(pfd[1], "Your request ``%20.20s ", av[1]); 446 for(n = 3; av[n]; n++) 447 fprint(pfd[1], "%s ", av[n]); 448 } 449 fprint(pfd[1], "'' failed (code %s).\nThe symptom was:\n\n", msg); 450 fd = open(file(name, 'E'), OREAD); 451 if(fd >= 0){ 452 for(;;){ 453 n = read(fd, buf, sizeof(buf)); 454 if(n <= 0) 455 break; 456 if(write(pfd[1], buf, n) != n){ 457 close(fd); 458 goto out; 459 } 460 } 461 close(fd); 462 } 463 fprint(pfd[1], "\nThe request began:\n\n"); 464 fd = open(file(name, 'D'), OREAD); 465 if(fd >= 0){ 466 for(i=0; i<4*16; i++){ 467 n = read(fd, buf, sizeof(buf)); 468 if(n <= 0) 469 break; 470 if(write(pfd[1], buf, n) != n){ 471 close(fd); 472 goto out; 473 } 474 } 475 close(fd); 476 } 477 close(pfd[1]); 478 out: 479 wait(&wm); 480 return wm.msg[0] ? -1 : 0; 481 } 482 483 /* 484 * print a warning and continue 485 */ 486 void 487 warning(char *f, void *a) 488 { 489 char err[65]; 490 char buf[256]; 491 492 errstr(err); 493 sprint(buf, f, a); 494 fprint(2, "runq: %s: %s\n", buf, err); 495 } 496 497 /* 498 * print an error and die 499 */ 500 void 501 error(char *f, void *a) 502 { 503 char err[ERRLEN+1]; 504 char buf[256]; 505 506 errstr(err); 507 sprint(buf, f, a); 508 fprint(2, "runq: %s: %s\n", buf, err); 509 exits(buf); 510 } 511 512