|
|
1.1 root 1: /*
2: * Copyright (c) 1983 Regents of the University of California.
3: * All rights reserved. The Berkeley software License Agreement
4: * specifies the terms and conditions for redistribution.
5: */
6:
7: #ifndef lint
8: char copyright[] =
9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10: All rights reserved.\n";
11: #endif not lint
12:
13: #ifndef lint
14: static char sccsid[] = "@(#)atrun.c 5.4 (Berkeley) 5/28/86";
15: #endif not lint
16:
17: /*
18: * Synopsis: atrun
19: *
20: *
21: * Run jobs created by at(1)
22: *
23: *
24: * Modifications by: Steve Wall
25: * Computer Systems Research Group
26: * University of California @ Berkeley
27: *
28: */
29: # include <stdio.h>
30: # include <sys/types.h>
31: # include <sys/dir.h>
32: # include <sys/file.h>
33: # include <sys/time.h>
34: # include <sys/param.h>
35: #ifdef notdef
36: # include <sys/quota.h>
37: #endif
38: # include <sys/stat.h>
39: # include <pwd.h>
40:
41: # define ATDIR "/usr/spool/at" /* spooling area */
42: # define TMPDIR "/tmp" /* area for temporary files */
43: # define MAILER "/bin/mail" /* program to use for sending
44: mail */
45: # define NORMAL 0 /* job exited normally */
46: # define ABNORMAL 1 /* job exited abnormally */
47: # define PASTDIR "/usr/spool/at/past" /* area to run jobs from */
48: # define LASTFILE "/usr/spool/at/lasttimedone" /* update time file */
49:
50:
51: char nowtime[11]; /* time it is right now (yy.ddd.hhmm) */
52: char errfile[25]; /* file where we redirect errors to */
53:
54:
55: main(argc, argv)
56: char **argv;
57: {
58:
59: int i; /* for loop index */
60: int numjobs; /* number of jobs to be run */
61: int should_be_run(); /* should a job be run? */
62: struct direct **jobqueue; /* queue of jobs to be run */
63:
64:
65: /*
66: * Move to the spooling area.
67: */
68: chdir(ATDIR);
69:
70: /*
71: * Create a filename that represents the time it is now. This is used
72: * to determine if the execution time for a job has arrived.
73: */
74: makenowtime(nowtime);
75:
76: /*
77: * Create a queue of the jobs that should be run.
78: */
79: if ((numjobs = scandir(".",&jobqueue,should_be_run, 0)) < 0) {
80: perror(ATDIR);
81: exit(1);
82: }
83:
84: /*
85: * If there are jobs to be run, run them.
86: */
87: if (numjobs > 0) {
88: for (i = 0; i < numjobs; ++i) {
89: run(jobqueue[i]->d_name);
90: }
91: }
92:
93: /*
94: * Record the last update time.
95: */
96: updatetime();
97:
98: }
99:
100: /*
101: * Create a string with the syntax yy.ddd.hhmm that represents the
102: * time it is right now. This string is used to determine whether a
103: * job should be run.
104: */
105: makenowtime(nowtime)
106: char *nowtime;
107: {
108: struct tm *now; /* broken down representation of the
109: time it is right now */
110: struct timeval time; /* number of seconds since 1/1/70 */
111: struct timezone zone; /* time zone we're in (NOT USED) */
112:
113: /*
114: * Get the time of day.
115: */
116: if (gettimeofday(&time,&zone) < 0) {
117: perror("gettimeofday");
118: exit(1);
119: }
120:
121: /*
122: * Get a broken down representation of the time it is right now.
123: */
124: now = localtime(&time.tv_sec);
125:
126: /*
127: * Create a string to be used in determining whether or not a job
128: * should be run. The syntax is yy.ddd.hhmm .
129: */
130: sprintf(nowtime,"%d.%03d.%02d%02d",now->tm_year,
131: now->tm_yday,
132: now->tm_hour,
133: now->tm_min);
134: return;
135: }
136:
137: /*
138: * Run a job.
139: */
140: run(spoolfile)
141: char *spoolfile;
142: {
143: int i; /* scratch variable */
144: int pid; /* process id of forked shell */
145: int exitstatus; /* exit status of the job */
146: int notifybymail; /* should we notify the owner of the
147: job after the job is run? */
148: char shell[4]; /* shell to run the job under */
149: char *getname(); /* get a uname from using a uid */
150: char mailvar[4]; /* send mail variable ("yes" or "no") */
151: char runfile[100]; /* file sent to forked shell for exec-
152: ution */
153: char owner[128]; /* owner of job we're going to run */
154: char jobname[128]; /* name of job we're going to run */
155: char whichshell[100]; /* which shell should we fork off? */
156: struct passwd *pwdbuf; /* password info of the owner of job */
157: struct stat errbuf; /* stats on error file */
158: struct stat jobbuf; /* stats on job file */
159: FILE *infile; /* I/O stream to spoolfile */
160:
161:
162: /*
163: * First we fork a child so that the main can run other jobs.
164: */
165: if (pid = fork())
166: return;
167:
168: /*
169: * Open the spoolfile.
170: */
171: if ((infile = fopen(spoolfile,"r")) == NULL) {
172: perror(spoolfile);
173: exit(1);
174: }
175:
176: /*
177: * Grab the 4-line header out of the spoolfile.
178: */
179: if (
180: (fscanf(infile,"# owner: %127s%*[^\n]\n",owner) != 1) ||
181: (fscanf(infile,"# jobname: %127s%*[^\n]\n",jobname) != 1) ||
182: (fscanf(infile,"# shell: %3s%*[^\n]\n",shell) != 1) ||
183: (fscanf(infile,"# notify by mail: %3s%*[^\n]\n",mailvar) != 1)
184: ) {
185: fprintf(stderr, "%s: bad spool header\n", spoolfile);
186: exit(1);
187: }
188:
189: /*
190: * Check to see if we should send mail to the owner.
191: */
192: notifybymail = (strcmp(mailvar, "yes") == 0);
193: fclose(infile);
194:
195: /*
196: * Change the ownership of the spoolfile from "daemon" to the owner
197: * of the job.
198: */
199: pwdbuf = getpwnam(owner);
200: if (pwdbuf == NULL) {
201: fprintf(stderr, "%s: could not find owner in passwd file\n",
202: spoolfile);
203: exit(1);
204: }
205: if (chown(spoolfile,pwdbuf->pw_uid,pwdbuf->pw_gid) == -1) {
206: perror(spoolfile);
207: exit(1);
208: }
209:
210: /*
211: * Move the spoolfile to the directory where jobs are run from and
212: * then move into that directory.
213: */
214: sprintf(runfile,"%s/%s",PASTDIR,spoolfile);
215: rename(spoolfile, runfile);
216: chdir(PASTDIR);
217:
218: /*
219: * Create a temporary file where we will redirect errors to.
220: * Just to make sure we've got a unique file, we'll run an "access"
221: * check on the file.
222: */
223: for (i = 0; i <= 1000; i += 2) {
224: sprintf(errfile,"%s/at.err%d",TMPDIR,(getpid() + i));
225:
226: if (access(errfile, F_OK))
227: break;
228:
229: if (i == 1000) {
230: fprintf(stderr, "couldn't create errorfile.\n");
231: exit(1);
232: }
233: }
234:
235: /*
236: * Get the stats of the job being run.
237: */
238: if (stat(runfile, &jobbuf) == -1) {
239: perror(runfile);
240: exit(1);
241: }
242:
243: /*
244: * Fork another child that will run the job.
245: */
246: if (pid = fork()) {
247:
248: /*
249: * If the child fails, save the job so that it gets
250: * rerun the next time "atrun" is executed and then exit.
251: */
252: if (pid == -1) {
253: chdir(ATDIR);
254: rename(runfile, spoolfile);
255: exit(1);
256: }
257:
258: /*
259: * Wait for the child to terminate.
260: */
261: wait((int *)0);
262:
263: /*
264: * Get the stats of the error file and determine the exit
265: * status of the child. We assume that if there is anything
266: * in the error file then the job ran into some errors.
267: */
268: if (stat(errfile,&errbuf) != 0) {
269: perror(errfile);
270: exit(1);
271: }
272: exitstatus = ((errbuf.st_size == 0) ? NORMAL : ABNORMAL);
273:
274: /* If errors occurred, then we send mail to the owner
275: * telling him/her that we ran into trouble.
276: *
277: * (NOTE: this could easily be modified so that if any
278: * errors occurred while running a job, mail is sent regard-
279: * less of whether the -m flag was set or not.
280: *
281: * i.e. rather than:
282: *
283: * "if (notifybymail)" use
284: * use:
285: *
286: * "if ((exitstatus == ABNORMAL) || (notifybymail))"
287: *
288: * It's up to you if you want to implement this.
289: *
290: */
291: if (exitstatus == ABNORMAL || notifybymail)
292: sendmailto(getname(jobbuf.st_uid),jobname,exitstatus);
293:
294: /*
295: * Remove the errorfile and the jobfile.
296: */
297: if (unlink(errfile) == -1)
298: perror(errfile);
299: if (unlink(runfile) == -1)
300: perror(runfile);
301:
302: exit(0);
303: }
304:
305: /*
306: * HERE'S WHERE WE SET UP AND FORK THE SHELL.
307: */
308:
309: /*
310: * Run the job as the owner of the jobfile
311: */
312: #ifdef notdef
313: /* This is no longer needed with the new, stripped-down quota system */
314: quota(Q_SETUID,jobbuf.st_uid,0,0);
315: #endif
316: setgid(jobbuf.st_gid);
317: initgroups(getname(jobbuf.st_uid),jobbuf.st_gid);
318: setuid(jobbuf.st_uid);
319:
320: /*
321: * Close all open files so that we can reopen a temporary file
322: * for stdout and sterr.
323: */
324: for (i = getdtablesize(); --i >= 0;)
325: close(i);
326:
327: /*
328: * Reposition stdin, stdout, and stderr.
329: *
330: * stdin = /dev/null
331: * stout = /dev/null
332: * stderr = /tmp/at.err{pid}
333: *
334: */
335: open("/dev/null", 0);
336: open("/dev/null", 1);
337: open(errfile,O_CREAT|O_WRONLY,00644);
338:
339: /*
340: * Now we fork the shell.
341: *
342: * See if the shell is in /bin
343: */
344: sprintf(whichshell,"/bin/%s",shell);
345: execl(whichshell,shell,runfile, 0);
346:
347: /*
348: * If not in /bin, look for the shell in /usr/bin.
349: */
350: sprintf(whichshell,"/usr/bin/%s",shell);
351: execl(whichshell,shell,runfile, 0);
352:
353: /*
354: * If not in /bin, look for the shell in /usr/new.
355: */
356: sprintf(whichshell,"/usr/new/%s",shell);
357: execl(whichshell,shell,runfile, 0);
358:
359: /*
360: * If we don't succeed by now, we're really having troubles,
361: * so we'll send the owner some mail.
362: */
363: fprintf(stderr, "%s: Can't execl shell\n",shell);
364: exit(1);
365: }
366:
367: /*
368: * Send mail to the owner of the job.
369: */
370: sendmailto(user,jobname,exitstatus)
371: char *user;
372: char *jobname;
373: int exitstatus;
374: {
375: char ch; /* scratch variable */
376: char mailtouser[100]; /* the process we use to send mail */
377: FILE *mailptr; /* I/O stream to the mail process */
378: FILE *errptr; /* I/O stream to file containing error
379: messages */
380: FILE *popen(); /* initiate I/O to a process */
381:
382:
383: /*
384: * Create the full name for the mail process.
385: */
386: sprintf(mailtouser,"%s %s",MAILER, user);
387:
388: /*
389: * Open a stream to the mail process.
390: */
391: if ((mailptr = popen(mailtouser,"w")) == NULL) {
392: perror(MAILER);
393: exit(1);
394: }
395:
396: /*
397: * Send the letter. If the job exited normally, just send a
398: * quick letter notifying the owner that everthing went ok.
399: */
400: if (exitstatus == NORMAL) {
401: fprintf(mailptr,"Your job \"%s\" was run without ",jobname);
402: fprintf(mailptr,"any errors.\n");
403: }
404:
405: /*
406: * If the job exited abnormally, send a letter notifying the user
407: * that the job didn't run proberly. Also, send a copy of the errors
408: * that occurred to the user.
409: */
410: else {
411: if (exitstatus == ABNORMAL) {
412:
413: /*
414: * Write the intro to the letter.
415: */
416: fprintf(mailptr,"\n\nThe job you submitted to at, ");
417: fprintf(mailptr,"\"%s\", ",jobname);
418: fprintf(mailptr,"exited abnormally.\nA list of the ");
419: fprintf(mailptr," errors that occurred follows:\n\n\n");
420:
421: /*
422: * Open the file containing a log of the errors that
423: * occurred.
424: */
425: if ((errptr = fopen(errfile,"r")) == NULL) {
426: perror(errfile);
427: exit(1);
428: }
429:
430: /*
431: * Send the copy of the errors to the owner.
432: */
433: fputc('\t',mailptr);
434: while ((ch = fgetc(errptr)) != EOF) {
435: fputc(ch,mailptr);
436: if (ch == '\n')
437: fputc('\t',mailptr);
438: }
439: fclose(errptr);
440: }
441: }
442:
443: /*
444: * Sign the letter.
445: */
446: fprintf(mailptr,"\n\n-----------------\n");
447: fprintf(mailptr,"The Atrun Program\n");
448:
449: /*
450: * Close the stream to the mail process.
451: */
452: pclose(mailptr);
453: return;
454: }
455:
456: /*
457: * Do we want to include a file in the job queue? (used by "scandir")
458: * We are looking for files whose "value" (its name) is less than or
459: * equal to the time it is right now (represented by "nowtime").
460: * We'll only consider files with three dots in their name since these
461: * are the only files that represent jobs to be run.
462: */
463: should_be_run(direntry)
464: struct direct *direntry;
465: {
466: int numdot = 0; /* number of dots found in a filename */
467: char *filename; /* pointer for scanning a filename */
468:
469:
470: filename = direntry->d_name;
471:
472: /*
473: * Count the number of dots found in the directory entry.
474: */
475: while (*filename)
476: numdot += (*(filename++) == '.');
477:
478: /*
479: * If the directory entry doesn't represent a job, just return a 0.
480: */
481: if (numdot != 3)
482: return(0);
483:
484: /*
485: * If a directory entry represents a job, determine if it's time to
486: * run it.
487: */
488: return(strncmp(direntry->d_name, nowtime,11) <= 0);
489: }
490:
491: /*
492: * Record the last time that "atrun" was run.
493: */
494: updatetime()
495: {
496:
497: struct timeval time; /* number of seconds since 1/1/70 */
498: struct timezone zone; /* time zone we're in (NOT USED) */
499: FILE *lastimefile; /* file where recored is kept */
500:
501: /*
502: * Get the time of day.
503: */
504: if (gettimeofday(&time,&zone) < 0) {
505: perror("gettimeofday");
506: exit(1);
507: }
508:
509: /*
510: * Open the record file.
511: */
512: if ((lastimefile = fopen(LASTFILE, "w")) == NULL) {
513: fprintf(stderr, "can't update lastfile: ");
514: perror(LASTFILE);
515: exit(1);
516: }
517:
518: /*
519: * Record the last update time (in seconds since 1/1/70).
520: */
521: fprintf(lastimefile, "%d\n", (u_long) time.tv_sec);
522:
523: /*
524: * Close the record file.
525: */
526: fclose(lastimefile);
527: }
528:
529: /*
530: * Get the full login name of a person using his/her user id.
531: */
532: char *
533: getname(uid)
534: int uid;
535: {
536: struct passwd *pwdinfo; /* password info structure */
537:
538:
539: if ((pwdinfo = getpwuid(uid)) == 0) {
540: perror(uid);
541: exit(1);
542: }
543: return(pwdinfo->pw_name);
544: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.