|
|
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.