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