1 /*- 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.proprietary.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1983, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)at.c 8.1 (Berkeley) 06/06/93"; 16 #endif /* not lint */ 17 18 /* 19 * Synopsis: at [-s] [-c] [-m] time [filename] 20 * 21 * 22 * 23 * Execute commands at a later date. 24 * 25 * 26 * Modifications by: Steve Wall 27 * Computer Systems Research Group 28 * University of California @ Berkeley 29 * 30 */ 31 #include <sys/param.h> 32 #include <sys/time.h> 33 #include <sys/file.h> 34 #include <sys/signal.h> 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <pwd.h> 38 #include "pathnames.h" 39 40 #define HOUR 100 /* 1 hour (using military time) */ 41 #define HALFDAY (12 * HOUR) /* half a day (12 hours) */ 42 #define FULLDAY (24 * HOUR) /* a full day (24 hours) */ 43 44 #define WEEK 1 /* day requested is 'week' */ 45 #define DAY 2 /* day requested is a weekday */ 46 #define MONTH 3 /* day requested is a month */ 47 48 #define NODATEFOUND -1 /* no date was given on command line */ 49 50 #define LINSIZ 256 /* length of input buffer */ 51 52 /* 53 * A table to identify potential command line values for "time". 54 * 55 * We need this so that we can do some decent error checking on the 56 * command line arguments. (This was inspired by the old "at", which 57 * accepted "at 900 jan 55" as valid input and other small bugs. 58 */ 59 struct datetypes { 60 int type; 61 char *name; 62 } dates_info[22] = { 63 { DAY, "sunday" }, 64 { DAY, "monday" }, 65 { DAY, "tuesday" }, 66 { DAY, "wednesday" }, 67 { DAY, "thursday" }, 68 { DAY, "friday" }, 69 { DAY, "saturday" }, 70 { MONTH, "january" }, 71 { MONTH, "february" }, 72 { MONTH, "march" }, 73 { MONTH, "april" }, 74 { MONTH, "may" }, 75 { MONTH, "june" }, 76 { MONTH, "july" }, 77 { MONTH, "august" }, 78 { MONTH, "september" }, 79 { MONTH, "october" }, 80 { MONTH, "november" }, 81 { MONTH, "december" }, 82 { 0, ""}, 83 }; 84 85 /* 86 * A table of the number of days in each month of the year. 87 * 88 * yeartable[0] -- normal year 89 * yeartable[1] -- leap year 90 */ 91 static int yeartable[2][13] = { 92 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 93 { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 94 }; 95 96 /* 97 * Structure holding the relevant values needed to create a spoolfile. 98 * "attime" will contain the info about when a job is to be run, and 99 * "nowtime" will contain info about what time the "at" command is in- 100 * voked. 101 */ 102 struct times { 103 int year; /* year that job is to be run */ 104 int yday; /* day of year that job is to be run */ 105 int mon; /* month of year that job is to be run*/ 106 int mday; /* day of month that job is to be run */ 107 int wday; /* day of week that job is to be run */ 108 int hour; /* hour of day that job is to be run */ 109 int min; /* min. of hour that job is to be run */ 110 } attime, nowtime; 111 112 char atfile[100]; /* name of spoolfile "yy.ddd.hhhh.??" */ 113 char *getenv(); /* get info on user's environment */ 114 char **environ; /* user's environment */ 115 FILE *spoolfile; /* spool file */ 116 FILE *inputfile; /* input file ("stdin" or "filename") */ 117 char *getwd(); /* used to get current directory info */ 118 119 120 main(argc, argv) 121 int argc; 122 char **argv; 123 { 124 int c; /* scratch variable */ 125 int usage(); /* print usage info and exit */ 126 int cleanup(); /* do cleanup on an interrupt signal */ 127 int dateindex = NODATEFOUND; /* if a day is specified, what option 128 is it? (mon day, week, dayofweek) */ 129 char *shell = _PATH_BSHELL; /* what shell do we use to run job? */ 130 int shflag = 0; /* override the current shell and run 131 job using the Bourne Shell */ 132 int cshflag = 0; /* override the current shell and run 133 job using the Cshell */ 134 int mailflag = 0; /* send mail after a job has been run?*/ 135 int standardin = 0; /* are we reading from stardard input */ 136 char line[LINSIZ]; /* a line from input file */ 137 char pwbuf[MAXPATHLEN]; /* the current working directory */ 138 char *jobfile = "stdin"; /* file containing job to be run */ 139 char *getname(); /* get the login name of a user */ 140 int pid; /* For forking for security reasons */ 141 142 143 144 argv++; argc--; 145 146 /* 147 * Interpret command line flags if they exist. 148 */ 149 while (argc > 0 && **argv == '-') { 150 (*argv)++; 151 while (**argv) switch (*(*argv)++) { 152 153 case 'c' : cshflag++; 154 shell = _PATH_CSHELL; 155 break; 156 157 case 's' : shflag++; 158 shell = _PATH_BSHELL; 159 break; 160 161 case 'm' : mailflag++; 162 break; 163 164 default : usage(); 165 166 } 167 --argc, ++argv; 168 } 169 if (shflag && cshflag) { 170 fprintf(stderr,"ambiguous shell request.\n"); 171 exit(1); 172 } 173 174 /* 175 * Get the time it is when "at" is invoked. We set both nowtime and 176 * attime to this value so that as we interpret the time the job is to 177 * be run we can compare the two values to determine such things as 178 * whether of not the job should be run the same day the "at" command 179 * is given, whether a job is to be run next year, etc. 180 */ 181 getnowtime(&nowtime, &attime); 182 183 #ifdef DEBUG 184 printit(); 185 #endif 186 187 if (argc <= 0) 188 usage(); 189 190 /* 191 * Interpret argv[1] and create the time of day that the job is to 192 * be run. This is the same function that was used in the old "at" 193 */ 194 maketime(&attime, *argv); 195 --argc; ++argv; 196 197 #ifdef DEBUG 198 printf("\n\nAFTER MAKETIME\n"); 199 printit(); 200 #endif 201 202 /* 203 * If argv[(2)] exists, this is a request to run a job on a certain 204 * day of year or a certain day of week. 205 * 206 * We send argv to the function "getdateindex" which returns the 207 * index value of the requested day in the table "dates_info" 208 * (see line 50 for table). If 'getdateindex" returns a NODATEFOUND, 209 * then the requested day format was not found in the table (usually 210 * this means that the argument is a "filename"). If the requested 211 * day is found, we continue to process command line arguments. 212 */ 213 if (argc > 0) { 214 if ((dateindex = getdateindex(*argv)) != NODATEFOUND) { 215 216 ++argv; --argc; 217 218 /* 219 * Determine the day of year that the job will be run 220 * depending on the value of argv. 221 */ 222 makedayofyear(dateindex, &argv, &argc); 223 } 224 } 225 226 /* 227 * If we get to this point and "dateindex" is set to NODATEFOUND, 228 * then we are dealing with a request with only a "time" specified 229 * (i.e. at 400p) and perhaps 'week' specified (i.e. at 400p week). 230 * If 'week' is specified, we just set excecution for 7 days in the 231 * future. Otherwise, we need to check to see if the requested time 232 * has already passed for the current day. If it has, then we add 233 * one to the day of year that the job will be executed. 234 */ 235 if (dateindex == NODATEFOUND) { 236 int daysinyear; 237 if ((argc > 0) && (strcmp(*argv,"week") == 0)) { 238 attime.yday += 7; 239 ++argv; --argc; 240 } else if (istomorrow()) 241 ++attime.yday; 242 243 daysinyear = isleap(attime.year) ? 366 : 365; 244 if (attime.yday >= daysinyear) { 245 attime.yday -= daysinyear; 246 ++attime.year; 247 } 248 } 249 250 /* 251 * If no more arguments exist, then we are reading 252 * from standard input. Thus, we set the standard 253 * input flag (++standardin). 254 */ 255 if (argc <= 0) 256 ++standardin; 257 258 259 #ifdef DEBUG 260 printf("\n\nAFTER ADDDAYS\n"); 261 printit(); 262 #endif 263 264 /* 265 * Start off assuming we're going to read from standard input, 266 * but if a filename has been given to read from, we will open it 267 * later. 268 */ 269 inputfile = stdin; 270 271 /* 272 * Create the filename for the spoolfile. 273 */ 274 makeatfile(atfile,attime.year,attime.yday,attime.hour,attime.min); 275 276 /* 277 * Open the spoolfile for writing. 278 */ 279 if ((c = open(atfile, O_WRONLY|O_CREAT|O_EXCL, 0400)) < 0 || 280 (spoolfile = fdopen(c, "w")) == NULL) { 281 perror(atfile); 282 exit(1); 283 } 284 285 /* 286 * The protection mechanism works like this: 287 * We are running ruid=user, euid=spool owner. So far we have been 288 * messing around in the spool directory, so we needed to run 289 * as the owner of the spool directory. 290 * We now need to switch to the user's effective uid 291 * to simplify permission checking. However, we fork first, 292 * so that we can clean up if interrupted. 293 */ 294 (void)signal(SIGINT, SIG_IGN); 295 pid = fork(); 296 if (pid == -1) { 297 perror("fork"); 298 exit(1); 299 } 300 if (pid) { 301 int wpid, status; 302 303 /* 304 * We are the parent. If the kid has problems, 305 * cleanup the spool directory. 306 */ 307 wpid = wait(&status); 308 if (wpid != pid || status) { 309 cleanup(); 310 exit(1); 311 } 312 /* 313 * The kid should have alread flushed the buffers. 314 */ 315 _exit(0); 316 } 317 318 /* 319 * Exit on interrupt. 320 */ 321 signal(SIGINT, SIG_DFL); 322 323 /* 324 * We are the kid, give up special permissions. 325 */ 326 setuid(getuid()); 327 328 /* 329 * Open the input file with the user's permissions. 330 */ 331 if (!standardin) { 332 jobfile = *argv; 333 if ((inputfile = fopen(jobfile, "r")) == NULL) { 334 perror(jobfile); 335 exit(1); 336 } 337 } 338 339 /* 340 * Determine what shell we should use to run the job. If the user 341 * didn't explicitly request that his/her current shell be over- 342 * ridden (shflag of cshflag) then we use the current shell. 343 */ 344 if ((!shflag) && (!cshflag) && (getenv("SHELL") != NULL)) 345 shell = "$SHELL"; 346 347 /* 348 * Put some standard information at the top of the spoolfile. 349 * This info is used by the other "at"-oriented programs (atq, 350 * atrm, atrun). 351 */ 352 fprintf(spoolfile, "# owner: %.127s\n",getname(getuid())); 353 fprintf(spoolfile, "# jobname: %.127s\n",jobfile); 354 fprintf(spoolfile, "# shell: sh\n"); 355 fprintf(spoolfile, "# notify by mail: %s\n",(mailflag) ? "yes" : "no"); 356 fprintf(spoolfile, "\n"); 357 358 /* 359 * Set the modes for any files created by the job being run. 360 */ 361 c = umask(0); 362 umask(c); 363 fprintf(spoolfile, "umask %.1o\n", c); 364 365 /* 366 * Get the current working directory so we know what directory to 367 * run the job from. 368 */ 369 if (getwd(pwbuf) == NULL) { 370 fprintf(stderr, "at: can't get working directory\n"); 371 exit(1); 372 } 373 fprintf(spoolfile, "cd %s\n", pwbuf); 374 375 /* 376 * Copy the user's environment to the spoolfile. 377 */ 378 if (environ) { 379 copyenvironment(&spoolfile); 380 } 381 382 /* 383 * Put in a line to run the proper shell using the rest of 384 * the file as input. Note that 'exec'ing the shell will 385 * cause sh() to leave a /tmp/sh### file around. The quotes 386 * also guarantee a quoting of the lines before EOF. 387 */ 388 fprintf(spoolfile, "%s << 'QAZWSXEDCRFVTGBYHNUJMIKOLP'\n", shell); 389 390 /* 391 * Now that we have all the files set up, we can start reading in 392 * the job. 393 */ 394 while (fgets(line, LINSIZ, inputfile) != NULL) 395 fputs(line, spoolfile); 396 397 /* don't put on single quotes, csh doesn't like it */ 398 fprintf(spoolfile, "QAZWSXEDCRFVTGBYHNUJMIKOLP\n"); 399 fclose(inputfile); 400 fclose(spoolfile); 401 402 exit(0); 403 404 } 405 406 /* 407 * Copy the user's environment to the spoolfile in the syntax of the 408 * Bourne shell. After the environment is set up, the proper shell 409 * will be invoked. 410 */ 411 copyenvironment(spoolfile) 412 FILE **spoolfile; 413 { 414 char *tmp; /* scratch pointer */ 415 char **environptr = environ; /* pointer to an environment setting */ 416 417 while(*environptr) { 418 tmp = *environptr; 419 420 /* 421 * We don't want the termcap or terminal entry so skip them. 422 */ 423 if ((strncmp(tmp,"TERM=",5) == 0) || 424 (strncmp(tmp,"TERMCAP=",8) == 0)) { 425 ++environptr; 426 continue; 427 } 428 429 /* 430 * Set up the proper syntax. 431 */ 432 while (*tmp != '=') 433 fputc(*tmp++,*spoolfile); 434 fputc('=', *spoolfile); 435 fputc('\'' , *spoolfile); 436 ++tmp; 437 438 /* 439 * Now copy the entry. 440 */ 441 while (*tmp) { 442 if (*tmp == '\'') 443 fputs("'\\''", *spoolfile); 444 else if (*tmp == '\n') 445 fputs("\\",*spoolfile); 446 else 447 fputc(*tmp, *spoolfile); 448 ++tmp; 449 } 450 fputc('\'' , *spoolfile); 451 452 /* 453 * We need to "export" environment settings. 454 */ 455 fprintf(*spoolfile, "\nexport "); 456 tmp = *environptr; 457 while (*tmp != '=') 458 fputc(*tmp++,*spoolfile); 459 fputc('\n',*spoolfile); 460 ++environptr; 461 } 462 return; 463 } 464 465 /* 466 * Create the filename for the spoolfile. The format is "yy.ddd.mmmm.??" 467 * where "yy" is the year the job will be run, "ddd" the day of year, 468 * "mmmm" the hour and minute, and "??" a scratch value used to dis- 469 * tinguish between two files that are to be run at the same time. 470 */ 471 makeatfile(atfile,year,dayofyear,hour,minute) 472 int year; 473 int hour; 474 int minute; 475 int dayofyear; 476 char *atfile; 477 { 478 int i; /* scratch variable */ 479 480 for (i=0; ; i += 53) { 481 sprintf(atfile, "%s/%02d.%03d.%02d%02d.%02d", _PATH_ATDIR, 482 year, dayofyear, hour, minute, (getpid() + i) % 100); 483 484 /* 485 * Make sure that the file name that we've created is unique. 486 */ 487 if (access(atfile, F_OK) == -1) 488 return; 489 } 490 } 491 492 /* 493 * Has the requested time already passed for the currrent day? If so, we 494 * will run the job "tomorrow". 495 */ 496 istomorrow() 497 { 498 if (attime.hour < nowtime.hour) 499 return(1); 500 if ((attime.hour == nowtime.hour) && (attime.min < nowtime.min)) 501 return(1); 502 503 return(0); 504 } 505 506 /* 507 * Debugging wreckage. 508 */ 509 printit() 510 { 511 printf("YEAR\tnowtime: %d\tattime: %d\n",nowtime.year,attime.year); 512 printf("YDAY\tnowtime: %d\tattime: %d\n",nowtime.yday,attime.yday); 513 printf("MON\tnowtime: %d\tattime: %d\n",nowtime.mon,attime.mon); 514 printf("MONDAY\tnowtime: %d\tattime: %d\n",nowtime.mday,attime.mday); 515 printf("WDAY\tnowtime: %d\tattime: %d\n",nowtime.wday,attime.wday); 516 printf("HOUR\tnowtime: %d\tattime: %d\n",nowtime.hour,attime.hour); 517 printf("MIN\tnowtime: %d\tattime: %d\n",nowtime.min,attime.min); 518 } 519 520 /* 521 * Calculate the day of year that the job will be executed. 522 * The av,ac arguments are ptrs to argv,argc; updated as necessary. 523 */ 524 makedayofyear(dateindex, av, ac) 525 int dateindex; 526 char ***av; 527 int *ac; 528 { 529 char **argv = *av; /* imitate argc,argv and update args at end */ 530 int argc = *ac; 531 char *ptr; /* scratch pointer */ 532 struct datetypes *daterequested; /* pointer to information about 533 the type of date option 534 we're dealing with */ 535 536 daterequested = &dates_info[dateindex]; 537 538 /* 539 * If we're dealing with a day of week, determine the number of days 540 * in the future the next day of this type will fall on. Add this 541 * value to "attime.yday". 542 */ 543 if (daterequested->type == DAY) { 544 if (attime.wday < dateindex) 545 attime.yday += dateindex - attime.wday; 546 else if(attime.wday > dateindex) 547 attime.yday += (7 - attime.wday) + dateindex; 548 else attime.yday += 7; 549 } 550 551 /* 552 * If we're dealing with a month and day of month, determine the 553 * day of year that this date will fall on. 554 */ 555 if (daterequested->type == MONTH) { 556 557 /* 558 * If a day of month isn't specified, print a message 559 * and exit. 560 */ 561 if (argc <= 0) { 562 fprintf(stderr,"day of month not specified.\n"); 563 exit(1); 564 } 565 566 /* 567 * Scan the day of month value and make sure that it 568 * has no characters in it. If characters are found or 569 * the day requested is zero, print a message and exit. 570 */ 571 ptr = *argv; 572 while (isdigit(*ptr)) 573 ++ptr; 574 if ((*ptr != '\0') || (atoi(*argv) == 0)) { 575 fprintf(stderr,"\"%s\": illegal day of month\n",*argv); 576 exit(1); 577 } 578 579 /* 580 * Set the month of year and day of month values. Since 581 * the first 7 values in our dateinfo table do not deal 582 * with month names, we subtract 7 from the month of year 583 * value. 584 */ 585 attime.mon = (dateindex - 7); 586 attime.mday = (atoi(*argv) - 1); 587 588 /* 589 * Test the day of month value to make sure that the 590 * value is legal. 591 */ 592 if ((attime.mday + 1) > 593 yeartable[isleap(attime.year)][attime.mon + 1]) { 594 fprintf(stderr,"\"%s\": illegal day of month\n",*argv); 595 exit(1); 596 } 597 598 /* 599 * Finally, we determine the day of year. 600 */ 601 attime.yday = (countdays()); 602 ++argv; --argc; 603 } 604 605 /* 606 * If 'week' is specified, add 7 to the day of year. 607 */ 608 if ((argc > 0) && (strcmp(*argv,"week") == 0)) { 609 attime.yday += 7; 610 ++argv; --argc; 611 } 612 613 /* 614 * Now that all that is done, see if the requested execution time 615 * has already passed for this year, and if it has, set execution 616 * for next year. 617 */ 618 if (isnextyear()) 619 ++attime.year; 620 621 /* 622 * Finally, reflect the updated argc,argv to the caller 623 */ 624 *av = argv; 625 *ac = argc; 626 } 627 628 /* 629 * Should the job be run next year? We check for the following situations: 630 * 631 * 1) the requested time has already passed for the current year. 632 * 2) the day of year is greater than the number of days in the year. 633 * 634 * If either of these tests succeed, we increment "attime.year" by 1. 635 * If #2 is true, we also subtract the number of days in the current year 636 * from "attime.yday". #2 can only occur if someone specifies a job to 637 * be run "tomorrow" on Dec. 31 or if they specify a job to be run a 638 * 'week' later and the date is at least Dec. 24. (I think so anyway) 639 */ 640 isnextyear() 641 { register daysinyear; 642 if (attime.yday < nowtime.yday) 643 return(1); 644 645 if ((attime.yday == nowtime.yday) && (attime.hour < nowtime.hour)) 646 return(1); 647 648 daysinyear = isleap(attime.year) ? 366 : 365; 649 if (attime.yday >= daysinyear) { 650 attime.yday -= daysinyear; 651 return(1); 652 } 653 if (attime.yday > (isleap(attime.year) ? 366 : 365)) { 654 attime.yday -= (isleap(attime.year) ? 366 : 365); 655 return(1); 656 } 657 658 return(0); 659 } 660 661 /* 662 * Determine the day of year given a month and day of month value. 663 */ 664 countdays() 665 { 666 int leap; /* are we dealing with a leap year? */ 667 int dayofyear; /* the day of year after conversion */ 668 int monthofyear; /* the month of year that we are 669 dealing with */ 670 671 /* 672 * Are we dealing with a leap year? 673 */ 674 leap = isleap(attime.year); 675 676 monthofyear = attime.mon; 677 dayofyear = attime.mday; 678 679 /* 680 * Determine the day of year. 681 */ 682 while (monthofyear > 0) 683 dayofyear += yeartable[leap][monthofyear--]; 684 685 return(dayofyear); 686 } 687 688 /* 689 * Is a year a leap year? 690 */ 691 isleap(year) 692 int year; 693 694 { 695 return((year%4 == 0 && year%100 != 0) || year%100 == 0); 696 } 697 698 getdateindex(date) 699 char *date; 700 { 701 int i = 0; 702 struct datetypes *ptr; 703 704 ptr = dates_info; 705 706 for (ptr = dates_info; ptr->type != 0; ptr++, i++) { 707 if (isprefix(date, ptr->name)) 708 return(i); 709 } 710 return(NODATEFOUND); 711 } 712 713 isprefix(prefix, fullname) 714 char *prefix, *fullname; 715 { 716 char ch; 717 char *ptr; 718 char *ptr1; 719 720 ptr = prefix; 721 ptr1 = fullname; 722 723 while (*ptr) { 724 ch = *ptr; 725 if (isupper(ch)) 726 ch = tolower(ch); 727 728 if (ch != *ptr1++) 729 return(0); 730 731 ++ptr; 732 } 733 return(1); 734 } 735 736 getnowtime(nowtime, attime) 737 struct times *nowtime; 738 struct times *attime; 739 { 740 struct tm *now; 741 struct timeval time; 742 struct timezone zone; 743 744 if (gettimeofday(&time,&zone) < 0) { 745 perror("gettimeofday"); 746 exit(1); 747 } 748 now = localtime(&time.tv_sec); 749 750 attime->year = nowtime->year = now->tm_year; 751 attime->yday = nowtime->yday = now->tm_yday; 752 attime->mon = nowtime->mon = now->tm_mon; 753 attime->mday = nowtime->mday = now->tm_mday; 754 attime->wday = nowtime->wday = now->tm_wday; 755 attime->hour = nowtime->hour = now->tm_hour; 756 attime->min = nowtime->min = now->tm_min; 757 } 758 759 /* 760 * This is the same routine used in the old "at", so I won't bother 761 * commenting it. It'll give you an idea of what the code looked 762 * like when I got it. 763 */ 764 maketime(attime,ptr) 765 char *ptr; 766 struct times *attime; 767 { 768 int val; 769 char *p; 770 771 p = ptr; 772 val = 0; 773 while(isdigit(*p)) { 774 val = val*10+(*p++ -'0'); 775 } 776 if (p-ptr < 3) 777 val *= HOUR; 778 779 for (;;) { 780 switch(*p) { 781 782 case ':': 783 ++p; 784 if (isdigit(*p)) { 785 if (isdigit(p[1])) { 786 val +=(10* *p + p[1] - 11*'0'); 787 p += 2; 788 continue; 789 } 790 } 791 fprintf(stderr, "bad time format:\n"); 792 exit(1); 793 794 case 'A': 795 case 'a': 796 if (val >= HALFDAY+HOUR) 797 val = FULLDAY+1; /* illegal */ 798 if (val >= HALFDAY && val <(HALFDAY+HOUR)) 799 val -= HALFDAY; 800 break; 801 802 case 'P': 803 case 'p': 804 if (val >= HALFDAY+HOUR) 805 val = FULLDAY+1; /* illegal */ 806 if (val < HALFDAY) 807 val += HALFDAY; 808 break; 809 810 case 'n': 811 case 'N': 812 if ((val == 0) || (val == HALFDAY)) 813 val = HALFDAY; 814 else 815 val = FULLDAY+1; /* illegal */ 816 break; 817 818 case 'M': 819 case 'm': 820 if ((val == 0) || (val == HALFDAY)) 821 val = 0; 822 else 823 val = FULLDAY+1; /* illegal */ 824 break; 825 826 827 case '\0': 828 case ' ': 829 break; 830 831 default: 832 fprintf(stderr, "bad time format\n"); 833 exit(1); 834 835 } 836 break; 837 } 838 if (val < 0 || val >= FULLDAY) { 839 fprintf(stderr, "time out of range\n"); 840 exit(1); 841 } 842 if (val%HOUR >= 60) { 843 fprintf(stderr, "illegal minute field\n"); 844 exit(1); 845 } 846 attime->hour = val/HOUR; 847 attime->min = val%HOUR; 848 } 849 850 /* 851 * Get the full login name of a person using his/her user id. 852 */ 853 char * 854 getname(uid) 855 int uid; 856 { 857 struct passwd *pwdinfo; /* password info structure */ 858 char *logname, *getlogin(); 859 860 logname = getlogin(); 861 if (logname == NULL || (pwdinfo = getpwnam(logname)) == NULL || 862 pwdinfo->pw_uid != uid) 863 pwdinfo = getpwuid(uid); 864 if (pwdinfo == 0) { 865 fprintf(stderr, "no name for uid %d?\n", uid); 866 exit(1); 867 } 868 return(pwdinfo->pw_name); 869 } 870 871 /* 872 * Do general cleanup. 873 */ 874 cleanup() 875 { 876 if (unlink(atfile) == -1) 877 perror(atfile); 878 exit(1); 879 } 880 881 /* 882 * Print usage info and exit. 883 */ 884 usage() 885 { 886 fprintf(stderr,"usage: at [-csm] time [date] [filename]\n"); 887 exit(1); 888 } 889 890