|
|
1.1 root 1: /*
2: * At takes a list of commands and arranges for them to be executed at
3: * a specified time. When the commands are executed, the user id, group
4: * id, exported shell variables and current directory will all be as they
5: * were when at was executed.
6: * The format of the at command is
7: * at [-v] [-c command] time [week] [file]
8: * or
9: * at [-v] [-c command] time day_of_week [week] [file]
10: * or
11: * at [-v] [-c command] time month day_of_month [file]
12: * Here the presence of `week' implies that the command should occur
13: * on the next week. If the v-flag is specified, then at prints out
14: * when the command will be executed. If the c-flag is specified
15: * then the command come from the string `command'. If a file is
16: * specified, then they come from that file. If none of these is
17: * done, then standard input is read.
18: * Note that at should not be set uid or set gid. Anyone can write
19: * in the at spooling directory. Security is maintained because at
20: * sets the set uid, set gid and owner execute bits. Without all of
21: * these being set, atrun will not run the script.
22: */
23: #include <stdio.h>
24: #include <ctype.h>
25: #include <sys/types.h>
26: #include <time.h>
27: #include <sys/dir.h>
28: #include <signal.h>
29:
30:
31: #define NUL '\0'
32: #define TRUE (0 == 0)
33: #define FALSE (0 != 0)
34: #define STDOUT 1
35: #define WEEK 7
36: #define YEAR 365
37: #define LEAPM 1 /* leap month */
38: #define YBASE 1900 /* Year bias in struct tm */
39: #define FSTDAY 0 /* Week day of 1st day of year 0 */
40: #define UNKNOWN -1
41: #define MODE 06500 /* set uid, set gid, owner read-exec */
42: #define OUTNL 13 /* "yymmddhhmm.aa" */
43:
44:
45: struct tm td = { /* time to do command */
46: 0, UNKNOWN, UNKNOWN,
47: UNKNOWN, UNKNOWN, UNKNOWN,
48: UNKNOWN, UNKNOWN, UNKNOWN
49: };
50: int wflg = FALSE, /* `week' flag */
51: vflg = FALSE; /* verify flag */
52: char *cstr = NULL, /* c-flag command string */
53: outn[] = "/usr/spool/at/yymmddhhmm.aa";
54: FILE *outfp = NULL; /* file to place commands into */
55: extern char **environ;
56:
57:
58: main(argc, argv)
59: int argc;
60: char *argv[];
61: {
62: setsigs();
63: options(argv);
64: if (vflg)
65: printf("%s", asctime(&td));
66: makefile();
67: copyenv();
68: copyumask();
69: copywd();
70: copycmds();
71: return (0);
72: }
73:
74:
75: /*
76: * Setsigs simply assures that on an interrupt we will clean up after
77: * ourselves so as to avoid leaving junk in at's spooling directory.
78: */
79: setsigs()
80: {
81: register int (*fnc)();
82: int (*signal())(),
83: clean();
84:
85: fnc = signal(SIGINT, SIG_IGN);
86: if (fnc != SIG_IGN)
87: signal(SIGINT, clean);
88: }
89:
90:
91: /*
92: * Clean is called when an interrupt occurs, or on any error. Its only
93: * function is to unlink the partial file in at's spooling directory.
94: */
95: clean()
96: {
97: if (outfp != NULL) {
98: fclose(outfp);
99: unlink(outn);
100: }
101: exit(1);
102: }
103:
104:
105: /*
106: * Makefile makes a file in the correct directory with a name of the
107: * form
108: * yymmddhhmm.xx
109: * where
110: * yy is the year (mod 100)
111: * mm is the month
112: * dd is the day of the month
113: * hh is the hour
114: * and
115: * mm is the minute
116: * at which the command is to be execute. The xx is simply to make
117: * the file name unique. The name is placed in outn and the stream
118: * is in outfp. Also, the mode of the file is execute for owner only.
119: * An initial line is written on the file to remove itself.
120: */
121: makefile()
122: {
123: register char *cp;
124:
125: cp = &outn[(sizeof outn) - OUTNL - 1];
126: sprintf(cp, "%02d%02d%02d%02d%02d.aa", td.tm_year % 100,
127: 1 + td.tm_mon, td.tm_mday, td.tm_hour, td.tm_min);
128: cp = &outn[(sizeof outn) - 2];
129: while (access(outn, 0) == 0)
130: if (++*cp > 'z') {
131: *cp = 'a';
132: if (++cp[-1] > 'z')
133: die("Too many things to do at the same time");
134: }
135: outfp = fopen(outn, "w");
136: if (outfp == NULL)
137: die("Can't create %s\n", outn);
138: chmod(outn, MODE);
139: fprintf(outfp, "rm -f %s\n", outn);
140: }
141:
142:
143: /*
144: * Copyenv writes onto outfp a pair of lines for each item in the environment.
145: * These lines are of the form
146: * var=value
147: * export var
148: * This will cause all exported variables to be reset when the file
149: * is executed by the shell.
150: */
151: copyenv()
152: {
153: register char **vp,
154: *cp,
155: ch;
156: char *end;
157:
158: for (vp=environ; (cp=*vp) != NULL; ++vp) {
159: while ((ch=*cp++) != '=' && ch != NUL)
160: putc(ch, outfp);
161: end = cp - 1;
162: fprintf(outfp, "='");
163: if (ch != '=')
164: --cp;
165: while ((ch=*cp++) != NUL)
166: if (ch == '\'')
167: fprintf(outfp, "'\\''");
168: else
169: putc(ch, outfp);
170: fprintf(outfp, "'\n");
171: fprintf(outfp, "export %.*s\n", end-*vp, *vp);
172: }
173: }
174:
175:
176: /*
177: * Copyumask writes out a line onto the stream outfp of the form
178: * umask number
179: * where number is the current umask. This will cause the shell
180: * to execute the commands with the current umask.
181: */
182: copyumask()
183: {
184: register int um;
185:
186: um = umask(0777);
187: umask(um);
188: fprintf(outfp, "umask %03o\n", um);
189: }
190:
191:
192: /*
193: * Copywd writes a line onto the file outfp of the form
194: * cd current working directory
195: * This will cause the current directory to be reset when the file
196: * is executed by the shell.
197: */
198: copywd()
199: {
200: register int pid;
201: int stat;
202: static char *args[] = {
203: "pwd",
204: NULL
205: };
206:
207: fprintf(outfp, "cd ");
208: fflush(outfp);
209: pid = fork();
210: if (pid == 0) {
211: dup2(fileno(outfp), STDOUT);
212: fclose(outfp);
213: execv("/bin/pwd", args);
214: execv("/usr/bin/pwd", args);
215: exit(1);
216: }
217: if (pid < 0)
218: die("Try again");
219: wait(&stat);
220: if (stat != 0)
221: die("Can't find pwd");
222: }
223:
224:
225: /*
226: * Copycmds simply copies from stdin until EOF.
227: */
228: copycmds()
229: {
230: register int ch;
231:
232: if (cstr != NULL)
233: fprintf(outfp, "%s\n", cstr);
234: else
235: while ((ch=getchar()) != EOF)
236: putc(ch, outfp);
237: }
238:
239:
240: /*
241: * Options cracks the command line options.
242: */
243: options(argv)
244: register char *argv[];
245: {
246: char **mopts(),
247: **getdate();
248:
249: argv = mopts(++argv);
250: if (*argv == NULL)
251: usage();
252: gettime(*argv++);
253: argv = mopts(argv);
254: argv = getdate(argv);
255: argv = mopts(argv);
256: if (*argv != NULL) {
257: if (cstr != NULL)
258: usage();
259: if (freopen(*argv, "r", stdin) == NULL)
260: die("Can't open %s", *argv);
261: argv = mopts(++argv);
262: }
263: if (*argv != NULL)
264: usage();
265: cyday();
266: }
267:
268:
269: /*
270: * Mopts cracks the `minus' options. It returns the first
271: * unused argument.
272: */
273: char **
274: mopts(argv)
275: register char *argv[];
276: {
277: register char *str,
278: ch;
279:
280: for (str=*argv; str != NULL && *str++ == '-'; str=*++argv)
281: for (ch=*str++; ch != NUL; ch=*str++)
282: switch (ch) {
283: case 'c':
284: if ((cstr = *++argv) == NULL)
285: usage();
286: break;
287: case 'v':
288: vflg = TRUE;
289: break;
290: default:
291: usage();
292: }
293: return (argv);
294: }
295:
296:
297: /*
298: * Usage give the usage message and dies.
299: */
300: usage()
301: {
302: static char umsg[] =
303: "usage: at [-v] [-c command] time [month month_day | week_day] [file]";
304:
305: die(umsg);
306: }
307:
308:
309: /*
310: * Die prints a message on stderr and exits with status one.
311: */
312: die(str)
313: char *str;
314: {
315: fprintf(stderr, "%r\n", &str);
316: clean();
317: }
318:
319:
320: /*
321: * Gettime takes the string `str' and sets the td minute and
322: * hour fields appropriately. A time is:
323: * either
324: * 0-2 digits (hours)
325: * or
326: * 3-4 digits (hours and minutes)
327: * possibly followed by a string starting with
328: * a for A.M.
329: * p for P.M.
330: * n for noon
331: * m for mid-night.
332: */
333: gettime(str)
334: char *str;
335: {
336: register int ch,
337: hour;
338: int min;
339: char *chp;
340:
341: hour = 0;
342: chp = str;
343: for (ch=*chp++; isascii(ch) && isdigit(ch); ch = *chp++)
344: hour = 10*hour + ch - '0';
345: if (chp - str - 1 <= 2)
346: hour *= 100;
347: min = hour % 100;
348: hour /= 100;
349: if (hour >= 24 || min >= 60)
350: usage();
351: switch (ch) {
352: case NUL:
353: break;
354: case 'a':
355: if (hour == 12)
356: hour = 0;
357: break;
358: case 'p':
359: if (hour < 12)
360: hour += 12;
361: break;
362: case 'n':
363: hour = 12;
364: break;
365: case 'm':
366: hour = 0;
367: break;
368: default:
369: usage();
370: }
371: td.tm_hour = hour;
372: td.tm_min = min;
373: }
374:
375:
376: /*
377: * Getdate fills in either the month and month-day or the week-day
378: * fields of td from the date pointed to by `argv'. It returns
379: * a pointer to the next argument.
380: */
381: char **
382: getdate(argv)
383: register char *argv[];
384: {
385: register int idx;
386: static char *day[] = {
387: "sunday", "monday",
388: "tuesday", "wednesday",
389: "thursday", "friday",
390: "saturday", NULL
391: },
392: *month[] = {
393: "january", "february",
394: "march", "april",
395: "may", "june",
396: "july", "august",
397: "september", "october",
398: "november", "december",
399: NULL
400: };
401:
402: if (*argv == NULL)
403: return (argv);
404: idx = mut(*argv, month);
405: if (idx != EOF) {
406: td.tm_mon = idx;
407: if (*++argv == NULL || (td.tm_mday=atoi(*argv++)) == 0)
408: usage();
409: return (argv);
410: }
411: idx = mut(*argv, day);
412: if (idx != EOF) {
413: td.tm_wday = idx;
414: ++argv;
415: }
416: if (*argv != NULL && (wflg = strcmp(*argv, "week")==0))
417: ++argv;
418: return (argv);
419: }
420:
421:
422: /*
423: * Mut searches the table `tbl' for entrys which may be trunctated to
424: * `str'. If there is exactly one such entry, it returns its ordinal.
425: * Otherwise it returns EOF. Note that `tbl' should be NULL-terminated.
426: */
427: mut(str, tbl)
428: register char *str;
429: char *tbl[];
430: {
431: register char **tp;
432: register int len;
433: int res;
434:
435: tp = tbl;
436: len = strlen(str);
437: do {
438: if (*tp == NULL)
439: return (EOF);
440: } while (strncmp(*tp++, str, len) != 0);
441: res = tp - tbl - 1;
442: do {
443: if (*tp == NULL)
444: return (res);
445: } while (strncmp(*tp++, str, len) != 0);
446: return (EOF);
447: }
448:
449:
450: /*
451: * Cyday computes the year and year-day on which the command should be
452: * performed. It does this on the basis of wflg (which is true iff
453: * we should delay by a week) and td.
454: * The result is placed in td.
455: */
456: cyday()
457: {
458: register int late,
459: len;
460: register struct tm *ct; /* current time */
461: time_t now;
462:
463: time(&now);
464: ct = localtime(&now);
465: td.tm_yday = ct->tm_yday;
466: td.tm_year = ct->tm_year;
467: if (td.tm_mon != UNKNOWN) {
468: late = td.tm_mon <= ct->tm_mon;
469: late &= td.tm_mon < ct->tm_mon
470: || td.tm_mday < ct->tm_mday;
471: if (late)
472: ++td.tm_year;
473: td.tm_yday = mdtoyd(td.tm_year + YBASE, td.tm_mon, td.tm_mday);
474: td.tm_wday = ydtowd(td.tm_year + YBASE, td.tm_yday);
475: } else {
476: late = td.tm_hour <= ct->tm_hour;
477: late &= td.tm_hour < ct->tm_hour
478: || td.tm_min <= ct->tm_min;
479: if (td.tm_wday != UNKNOWN) {
480: td.tm_yday += (td.tm_wday + WEEK - ct->tm_wday) % WEEK;
481: late &= td.tm_wday == ct->tm_wday;
482: if (late | wflg)
483: td.tm_yday += WEEK;
484: } else {
485: td.tm_wday = ct->tm_wday;
486: if (wflg)
487: td.tm_yday += WEEK;
488: else if (late) {
489: ++td.tm_yday;
490: td.tm_wday = (td.tm_wday+1) % WEEK;
491: }
492: }
493: len = (isleap(td.tm_year + YBASE)) ? YEAR+1 : YEAR;
494: if (td.tm_yday >= len) {
495: td.tm_yday -= len;
496: ++td.tm_year;
497: }
498: ydtom(td.tm_year + YBASE, td.tm_yday, &td.tm_mon, &td.tm_mday);
499: }
500: }
501:
502:
503: /*
504: * Isleap returns TRUE iff the year `year' is a leap year.
505: */
506: isleap(year)
507: register int year;
508: {
509: return (year%4 == 0 && (year%100 != 0 || year%400 == 0));
510: }
511:
512:
513: /*
514: * The array mlen is an array of month lengths, indexed by the
515: * month number (0 - 11).
516: */
517: static int mlen[] = { /* month lengths */
518: 31, 28, 31, 30,
519: 31, 30, 31, 31,
520: 30, 31, 30, 31
521: };
522:
523:
524: /*
525: * Mdtoyd returns the year-day for the date with year `year', month
526: * `month' and month-day `mday'.
527: */
528: mdtoyd(year, month, mday)
529: register int month;
530: int year,
531: mday;
532: {
533: register int res,
534: leap;
535:
536: res = mday - 1;
537: if (leap = isleap(year))
538: ++mlen[LEAPM];
539: while (--month >= 0)
540: res += mlen[month];
541: if (leap)
542: --mlen[LEAPM];
543: return (res);
544: }
545:
546:
547: /*
548: * Ydtowd returns the week day for the date with year `year' and
549: * year-day `yday'.
550: */
551: ydtowd(year, yday)
552: register int year;
553: int yday;
554: {
555: register int wday;
556:
557: --year;
558: wday = FSTDAY + year * (YEAR%WEEK) + yday%WEEK;
559: wday += (year+4) / 4;
560: wday -= (year+100) / 100;
561: wday += (year+400) / 400;
562: wday %= WEEK;
563: return (wday);
564: }
565:
566:
567: /*
568: * Ydtom sets `pmonth' and `pmday' to the month and month-day of the
569: * date with year `year' and year-day `yday'.
570: */
571: ydtom(year, yday, pmonth, pmday)
572: int year,
573: yday,
574: *pmonth,
575: *pmday;
576: {
577: register int mday,
578: month;
579: int leap;
580:
581: if (leap = isleap(year))
582: ++mlen[LEAPM];
583: mday = yday;
584: for (month=0; mday >= mlen[month]; ++month)
585: mday -= mlen[month];
586: *pmonth = month;
587: *pmday = mday + 1;
588: if (leap)
589: --mlen[LEAPM];
590: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.