1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <sys/resource.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 37 #include <dirent.h> 38 #include <string.h> 39 #include <stdlib.h> 40 #include <fcntl.h> 41 #include <pwd.h> 42 #include <stdio.h> 43 #include <ctype.h> 44 #include <time.h> 45 #include <signal.h> 46 #include <errno.h> 47 #include <limits.h> 48 #include <ulimit.h> 49 #include <unistd.h> 50 #include <locale.h> 51 #include <libintl.h> 52 #include <tzfile.h> 53 #include <project.h> 54 55 #include "cron.h" 56 57 #define TMPFILE "_at" /* prefix for temporary files */ 58 /* 59 * Mode for creating files in ATDIR. 60 * Setuid bit on so that if an owner of a file gives that file 61 * away to someone else, the setuid bit will no longer be set. 62 * If this happens, atrun will not execute the file 63 */ 64 #define ATMODE (S_ISUID | S_IRUSR | S_IRGRP | S_IROTH) 65 #define ROOT 0 /* user-id of super-user */ 66 #define MAXTRYS 100 /* max trys to create at job file */ 67 68 #define BADTIME "bad time specification" 69 #define BADQUEUE "queue name must be a single character a-z" 70 #define NOTCQUEUE "queue c is reserved for cron entries" 71 #define BADSHELL "because your login shell isn't /usr/bin/sh,"\ 72 "you can't use at" 73 #define WARNSHELL "commands will be executed using %s\n" 74 #define CANTCD "can't change directory to the at directory" 75 #define CANTCHOWN "can't change the owner of your job to you" 76 #define CANTCREATE "can't create a job for you" 77 #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)" 78 #define NOOPENDIR "can't open the at directory" 79 #define NOTALLOWED "you are not authorized to use at. Sorry." 80 #define USAGE\ 81 "usage: at [-c|-k|-s] [-m] [-f file] [-p project] [-q queuename] "\ 82 "-t time\n"\ 83 " at [-c|-k|-s] [-m] [-f file] [-p project] [-q queuename] "\ 84 "timespec\n"\ 85 " at -l [-p project] [-q queuename] [at_job_id...]\n"\ 86 " at -r at_job_id ...\n" 87 88 #define FORMAT "%a %b %e %H:%M:%S %Y" 89 90 /* Macros used in format for fscanf */ 91 #define AQ(x) #x 92 #define BUFFMT(p) AQ(p) 93 94 static int leap(int); 95 static int atoi_for2(char *); 96 static int check_queue(char *, int); 97 static int list_jobs(int, char **, int, int); 98 static int remove_jobs(int, char **, char *); 99 static void usage(void); 100 static void catch(int); 101 static void copy(char *, FILE *, int); 102 static void atime(struct tm *, struct tm *); 103 static int not_this_project(char *); 104 static char *mkjobname(time_t); 105 static time_t parse_time(char *); 106 static time_t gtime(struct tm *); 107 void atabort(char *)__NORETURN; 108 void yyerror(void); 109 extern int yyparse(void); 110 111 extern void audit_at_delete(char *, char *, int); 112 extern int audit_at_create(char *, int); 113 extern int audit_cron_is_anc_name(char *); 114 extern int audit_cron_delete_anc_file(char *, char *); 115 116 /* 117 * Error in getdate(3G) 118 */ 119 static char *errlist[] = { 120 /* 0 */ "", 121 /* 1 */ "getdate: The DATEMSK environment variable is not set", 122 /* 2 */ "getdate: Error on \"open\" of the template file", 123 /* 3 */ "getdate: Error on \"stat\" of the template file", 124 /* 4 */ "getdate: The template file is not a regular file", 125 /* 5 */ "getdate: An error is encountered while reading the template", 126 /* 6 */ "getdate: Malloc(3C) failed", 127 /* 7 */ "getdate: There is no line in the template that matches the input", 128 /* 8 */ "getdate: Invalid input specification" 129 }; 130 131 int gmtflag = 0; 132 int mday[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 133 uid_t user; 134 struct tm *tp, at, rt; 135 static int cshflag = 0; 136 static int kshflag = 0; 137 static int shflag = 0; 138 static int mflag = 0; 139 static int pflag = 0; 140 static char *Shell; 141 static char *tfname; 142 static char pname[80]; 143 static char pname1[80]; 144 static short jobtype = ATEVENT; /* set to 1 if batch job */ 145 extern char *argp; 146 extern int per_errno; 147 static projid_t project; 148 149 int 150 main(int argc, char **argv) 151 { 152 FILE *inputfile; 153 int i, fd; 154 int try = 0; 155 int fflag = 0; 156 int lflag = 0; 157 int qflag = 0; 158 int rflag = 0; 159 int tflag = 0; 160 int c; 161 int tflen; 162 char *file; 163 char *login; 164 char *job; 165 char *jobfile = NULL; /* file containing job to be run */ 166 char argpbuf[LINE_MAX], timebuf[80]; 167 time_t now; 168 time_t when = 0; 169 struct tm *ct; 170 char *proj; 171 struct project prj, *pprj; 172 char mybuf[PROJECT_BUFSZ]; 173 char ipbuf[PROJECT_BUFSZ]; 174 175 (void) setlocale(LC_ALL, ""); 176 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 177 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 178 #endif 179 (void) textdomain(TEXT_DOMAIN); 180 181 user = getuid(); 182 login = getuser(user); 183 if (login == NULL) { 184 if (per_errno == 2) 185 atabort(BADSHELL); 186 else 187 atabort(INVALIDUSER); 188 } 189 190 if (!allowed(login, ATALLOW, ATDENY)) 191 atabort(NOTALLOWED); 192 193 while ((c = getopt(argc, argv, "cklmsrf:p:q:t:")) != EOF) 194 switch (c) { 195 case 'c': 196 cshflag++; 197 break; 198 case 'f': 199 fflag++; 200 jobfile = optarg; 201 break; 202 case 'k': 203 kshflag++; 204 break; 205 case 'l': 206 lflag++; 207 break; 208 case 'm': 209 mflag++; 210 break; 211 case 'p': 212 proj = optarg; 213 pprj = &prj; 214 if ((pprj = getprojbyname(proj, pprj, 215 (void *)&mybuf, sizeof (mybuf))) != NULL) { 216 project = pprj->pj_projid; 217 if (inproj(login, pprj->pj_name, 218 (void *)&ipbuf, sizeof (ipbuf))) 219 pflag++; 220 else { 221 (void) fprintf(stderr, 222 gettext("at: user %s is " 223 "not a member of " 224 "project %s (%d)\n"), 225 login, pprj->pj_name, 226 project); 227 exit(2); 228 } 229 break; 230 } 231 pprj = &prj; 232 if (isdigit(proj[0]) && 233 (pprj = getprojbyid(atoi(proj), pprj, 234 (void *)&mybuf, sizeof (mybuf))) != NULL) { 235 project = pprj->pj_projid; 236 if (inproj(login, pprj->pj_name, 237 (void *)&ipbuf, sizeof (ipbuf))) 238 pflag++; 239 else { 240 (void) fprintf(stderr, 241 gettext("at: user %s is " 242 "not a member of " 243 "project %s (%d)\n"), 244 login, pprj->pj_name, 245 project); 246 exit(2); 247 } 248 break; 249 } 250 (void) fprintf(stderr, gettext("at: project " 251 "%s not found.\n"), proj); 252 exit(2); 253 break; 254 case 'q': 255 qflag++; 256 if (optarg[1] != '\0') 257 atabort(BADQUEUE); 258 jobtype = *optarg - 'a'; 259 if ((jobtype < 0) || (jobtype > 25)) 260 atabort(BADQUEUE); 261 if (jobtype == 2) 262 atabort(NOTCQUEUE); 263 break; 264 case 'r': 265 rflag++; 266 break; 267 case 's': 268 shflag++; 269 break; 270 case 't': 271 tflag++; 272 when = parse_time(optarg); 273 break; 274 default: 275 usage(); 276 } 277 278 argc -= optind; 279 argv += optind; 280 281 if (lflag + rflag > 1) 282 usage(); 283 284 if (lflag) { 285 if (cshflag || kshflag || shflag || mflag || 286 fflag || tflag || rflag) 287 usage(); 288 return (list_jobs(argc, argv, qflag, jobtype)); 289 } 290 291 if (rflag) { 292 if (cshflag || kshflag || shflag || mflag || 293 fflag || tflag || qflag) 294 usage(); 295 return (remove_jobs(argc, argv, login)); 296 } 297 298 if ((argc + tflag == 0) && (jobtype != BATCHEVENT)) 299 usage(); 300 301 if (cshflag + kshflag + shflag > 1) 302 atabort("ambiguous shell request"); 303 304 time(&now); 305 306 if (jobtype == BATCHEVENT) 307 when = now; 308 309 if (when == 0) { /* figure out what time to run the job */ 310 int argplen = sizeof (argpbuf) - 1; 311 312 argpbuf[0] = '\0'; 313 argp = argpbuf; 314 i = 0; 315 while (i < argc) { 316 /* guard against buffer overflow */ 317 argplen -= strlen(argv[i]) + 1; 318 if (argplen < 0) 319 atabort(BADTIME); 320 321 strcat(argp, argv[i]); 322 strcat(argp, " "); 323 i++; 324 } 325 if ((file = getenv("DATEMSK")) == 0 || file[0] == '\0') { 326 tp = localtime(&now); 327 /* 328 * Fix for 1047182 - we have to let yyparse 329 * check bounds on mday[] first, then fixup 330 * the leap year case. 331 */ 332 yyparse(); 333 334 mday[1] = 28 + leap(at.tm_year); 335 336 if (at.tm_mday > mday[at.tm_mon]) 337 atabort("bad date"); 338 339 atime(&at, &rt); 340 when = gtime(&at); 341 if (!gmtflag) { 342 when += timezone; 343 if (localtime(&when)->tm_isdst) 344 when -= (timezone-altzone); 345 } 346 } else { /* DATEMSK is set */ 347 if ((ct = getdate(argpbuf)) == NULL) 348 atabort(errlist[getdate_err]); 349 else 350 when = mktime(ct); 351 } 352 } 353 354 if (when < now) /* time has already past */ 355 atabort("too late"); 356 357 tflen = strlen(ATDIR) + 1 + strlen(TMPFILE) + 358 10 + 1; /* 10 for an INT_MAX pid */ 359 tfname = xmalloc(tflen); 360 snprintf(tfname, tflen, "%s/%s%d", ATDIR, TMPFILE, getpid()); 361 362 /* catch INT, HUP, TERM and QUIT signals */ 363 if (signal(SIGINT, catch) == SIG_IGN) 364 signal(SIGINT, SIG_IGN); 365 if (signal(SIGHUP, catch) == SIG_IGN) 366 signal(SIGHUP, SIG_IGN); 367 if (signal(SIGQUIT, catch) == SIG_IGN) 368 signal(SIGQUIT, SIG_IGN); 369 if (signal(SIGTERM, catch) == SIG_IGN) 370 signal(SIGTERM, SIG_IGN); 371 if ((fd = open(tfname, O_CREAT|O_EXCL|O_WRONLY, ATMODE)) < 0) 372 atabort(CANTCREATE); 373 if (chown(tfname, user, getgid()) == -1) { 374 unlink(tfname); 375 atabort(CANTCHOWN); 376 } 377 close(1); 378 dup(fd); 379 close(fd); 380 sprintf(pname, "%s", PROTO); 381 sprintf(pname1, "%s.%c", PROTO, 'a'+jobtype); 382 383 /* 384 * Open the input file with the user's permissions. 385 */ 386 if (jobfile != NULL) { 387 if ((seteuid(user) < 0) || 388 (inputfile = fopen(jobfile, "r")) == NULL) { 389 unlink(tfname); 390 fprintf(stderr, "at: %s: %s\n", jobfile, errmsg(errno)); 391 exit(1); 392 } 393 else 394 seteuid(0); 395 } else 396 inputfile = stdin; 397 398 copy(jobfile, inputfile, when); 399 while (rename(tfname, job = mkjobname(when)) == -1) { 400 sleep(1); 401 if (++try > MAXTRYS / 10) { 402 unlink(tfname); 403 atabort(CANTCREATE); 404 } 405 } 406 unlink(tfname); 407 if (audit_at_create(job, 0)) 408 atabort(CANTCREATE); 409 410 cron_sendmsg(ADD, login, strrchr(job, '/')+1, AT); 411 if (per_errno == 2) 412 fprintf(stderr, gettext(WARNSHELL), Shell); 413 cftime(timebuf, FORMAT, &when); 414 fprintf(stderr, gettext("job %s at %s\n"), 415 strrchr(job, '/')+1, timebuf); 416 if (when - MINUTE < HOUR) 417 fprintf(stderr, gettext( 418 "at: this job may not be executed at the proper time.\n")); 419 return (0); 420 } 421 422 423 static char * 424 mkjobname(t) 425 time_t t; 426 { 427 int i, fd; 428 char *name; 429 430 name = xmalloc(200); 431 for (i = 0; i < MAXTRYS; i++) { 432 sprintf(name, "%s/%ld.%c", ATDIR, t, 'a'+jobtype); 433 /* fix for 1099183, 1116833 - create file here, avoid race */ 434 if ((fd = open(name, O_CREAT | O_EXCL, ATMODE)) > 0) { 435 close(fd); 436 return (name); 437 } 438 t += 1; 439 } 440 atabort("queue full"); 441 /* NOTREACHED */ 442 } 443 444 445 static void 446 catch(int x) 447 { 448 unlink(tfname); 449 exit(1); 450 } 451 452 453 void 454 atabort(msg) 455 char *msg; 456 { 457 fprintf(stderr, "at: %s\n", gettext(msg)); 458 459 exit(1); 460 } 461 462 int 463 yywrap(void) 464 { 465 return (1); 466 } 467 468 void 469 yyerror(void) 470 { 471 atabort(BADTIME); 472 } 473 474 /* 475 * add time structures logically 476 */ 477 static void 478 atime(struct tm *a, struct tm *b) 479 { 480 if ((a->tm_sec += b->tm_sec) >= 60) { 481 b->tm_min += a->tm_sec / 60; 482 a->tm_sec %= 60; 483 } 484 if ((a->tm_min += b->tm_min) >= 60) { 485 b->tm_hour += a->tm_min / 60; 486 a->tm_min %= 60; 487 } 488 if ((a->tm_hour += b->tm_hour) >= 24) { 489 b->tm_mday += a->tm_hour / 24; 490 a->tm_hour %= 24; 491 } 492 a->tm_year += b->tm_year; 493 if ((a->tm_mon += b->tm_mon) >= 12) { 494 a->tm_year += a->tm_mon / 12; 495 a->tm_mon %= 12; 496 } 497 a->tm_mday += b->tm_mday; 498 mday[1] = 28 + leap(a->tm_year); 499 while (a->tm_mday > mday[a->tm_mon]) { 500 a->tm_mday -= mday[a->tm_mon++]; 501 if (a->tm_mon > 11) { 502 a->tm_mon = 0; 503 mday[1] = 28 + leap(++a->tm_year); 504 } 505 } 506 507 } 508 509 static int 510 leap(int year) 511 { 512 return (isleap(year + TM_YEAR_BASE)); 513 } 514 515 /* 516 * return time from time structure 517 */ 518 static time_t 519 gtime(tptr) 520 struct tm *tptr; 521 { 522 int i; 523 long tv; 524 int dmsize[12] = 525 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 526 527 528 tv = 0; 529 for (i = 1970; i != tptr->tm_year+TM_YEAR_BASE; i++) 530 tv += (365 + isleap(i)); 531 /* 532 * We call isleap since leap() adds 533 * 1900 onto any value passed 534 */ 535 536 if (!leap(tptr->tm_year) && at.tm_mday == 29 && at.tm_mon == 1) 537 atabort("bad date - not a leap year"); 538 539 if ((leap(tptr->tm_year)) && tptr->tm_mon >= 2) 540 ++tv; 541 542 for (i = 0; i < tptr->tm_mon; ++i) 543 tv += dmsize[i]; 544 tv += tptr->tm_mday - 1; 545 tv = 24 * tv + tptr->tm_hour; 546 tv = 60 * tv + tptr->tm_min; 547 tv = 60 * tv + tptr->tm_sec; 548 return (tv); 549 } 550 551 /* 552 * make job file from proto + stdin 553 */ 554 static void 555 copy(char *jobfile, FILE *inputfile, int when) 556 { 557 int c; 558 FILE *pfp; 559 FILE *xfp; 560 char *shell; 561 char dirbuf[PATH_MAX + 1]; 562 char line[LINE_MAX]; 563 char **ep; 564 mode_t um; 565 char *val; 566 extern char **environ; 567 int pfd[2]; 568 pid_t pid; 569 uid_t realusr; 570 int ttyinput; 571 int ulimit_flag = 0; 572 struct rlimit rlp; 573 struct project prj, *pprj; 574 char pbuf[PROJECT_BUFSZ]; 575 char pbuf2[PROJECT_BUFSZ]; 576 char *user; 577 578 /* 579 * Fix for 1099381: 580 * If the inputfile is from a tty, then turn on prompting, and 581 * put out a prompt now, instead of waiting for a lot of file 582 * activity to complete. 583 */ 584 ttyinput = isatty(fileno(inputfile)); 585 if (ttyinput) { 586 fputs("at> ", stderr); 587 fflush(stderr); 588 } 589 590 /* 591 * Fix for 1053807: 592 * Determine what shell we should use to run the job. If the user 593 * didn't explicitly request that his/her current shell be over- 594 * ridden (shflag or cshflag), then we use the current shell. 595 */ 596 if (cshflag) 597 Shell = shell = "/bin/csh"; 598 else if (kshflag) { 599 Shell = shell = "/bin/ksh"; 600 ulimit_flag = 1; 601 } else if (shflag) { 602 Shell = shell = "/bin/sh"; 603 ulimit_flag = 1; 604 } else if (((Shell = val = getenv("SHELL")) != NULL) && 605 (*val != '\0')) { 606 shell = "$SHELL"; 607 if ((strstr(val, "/sh") != NULL) || 608 (strstr(val, "/ksh") != NULL)) 609 ulimit_flag = 1; 610 } else { 611 /* SHELL is NULL or unset, therefore use default */ 612 #ifdef XPG4 613 Shell = shell = "/usr/xpg4/bin/sh"; 614 #else 615 Shell = shell = "/bin/sh"; 616 #endif /* XPG4 */ 617 ulimit_flag = 1; 618 } 619 620 printf(": %s job\n", jobtype ? "batch" : "at"); 621 printf(": jobname: %.127s\n", (jobfile == NULL) ? "stdin" : jobfile); 622 printf(": notify by mail: %s\n", (mflag) ? "yes" : "no"); 623 624 if (pflag) { 625 (void) printf(": project: %d\n", project); 626 } else { 627 /* 628 * Check if current user is a member of current project. 629 * This check is done here to avoid setproject() failure 630 * later when the job gets executed. If current user does 631 * not belong to current project, user's default project 632 * will be used instead. This is achieved by not specifying 633 * the project (": project: <project>\n") in the job file. 634 */ 635 if ((user = getuser(getuid())) == NULL) 636 atabort(INVALIDUSER); 637 project = getprojid(); 638 pprj = getprojbyid(project, &prj, pbuf, sizeof (pbuf)); 639 if (pprj != NULL) { 640 if (inproj(user, pprj->pj_name, pbuf2, sizeof (pbuf2))) 641 (void) printf(": project: %d\n", project); 642 } 643 } 644 645 for (ep = environ; *ep; ep++) { 646 if (strchr(*ep, '\'') != NULL) 647 continue; 648 if ((val = strchr(*ep, '=')) == NULL) 649 continue; 650 *val++ = '\0'; 651 printf("export %s; %s='%s'\n", *ep, *ep, val); 652 *--val = '='; 653 } 654 if ((pfp = fopen(pname1, "r")) == NULL && 655 (pfp = fopen(pname, "r")) == NULL) 656 atabort("no prototype"); 657 /* 658 * Put in a line to run the proper shell using the rest of 659 * the file as input. Note that 'exec'ing the shell will 660 * cause sh() to leave a /tmp/sh### file around. (1053807) 661 */ 662 printf("%s << '...the rest of this file is shell input'\n", shell); 663 664 um = umask(0); 665 while ((c = getc(pfp)) != EOF) { 666 if (c != '$') 667 putchar(c); 668 else switch (c = getc(pfp)) { 669 case EOF: 670 goto out; 671 case 'd': 672 /* 673 * fork off a child with submitter's permissions, 674 * otherwise, when IFS=/, /usr/bin/pwd would be parsed 675 * by the shell as file "bin". The shell would 676 * then search according to the submitter's PATH 677 * and run the file bin with root permission 678 */ 679 680 (void) fflush(stdout); 681 dirbuf[0] = NULL; 682 if (pipe(pfd) != 0) 683 atabort("pipe open failed"); 684 realusr = getuid(); /* get realusr before the fork */ 685 if ((pid = fork()) == (pid_t)-1) 686 atabort("fork failed"); 687 if (pid == 0) { /* child process */ 688 (void) close(pfd[0]); 689 /* remove setuid for pwd */ 690 (void) setuid(realusr); 691 if ((xfp = popen("/usr/bin/pwd", "r")) 692 != NULL) { 693 fscanf(xfp, "%" BUFFMT(PATH_MAX) "s", 694 dirbuf); 695 (void) pclose(xfp); 696 xfp = fdopen(pfd[1], "w"); 697 fprintf(xfp, "%s", dirbuf); 698 (void) fclose(xfp); 699 } 700 _exit(0); 701 } 702 (void) close(pfd[1]); /* parent process */ 703 xfp = fdopen(pfd[0], "r"); 704 fscanf(xfp, "%" BUFFMT(PATH_MAX) "s", dirbuf); 705 printf("%s", dirbuf); 706 (void) fclose(xfp); 707 break; 708 case 'm': 709 printf("%o", um); 710 break; 711 case '<': 712 if (ulimit_flag) { 713 if (getrlimit(RLIMIT_FSIZE, &rlp) == 0) { 714 if (rlp.rlim_cur == RLIM_INFINITY) 715 printf("ulimit unlimited\n"); 716 else 717 printf("ulimit %lld\n", 718 rlp.rlim_cur / 512); 719 } 720 } 721 /* 722 * fix for 1113572 - use fputs() so that a 723 * newline isn't appended to the one returned 724 * with fgets(); 1099381 - prompt for input. 725 */ 726 while (fgets(line, LINE_MAX, inputfile) != NULL) { 727 fputs(line, stdout); 728 if (ttyinput) 729 fputs("at> ", stderr); 730 } 731 if (ttyinput) /* clean up the final output */ 732 fputs("<EOT>\n", stderr); 733 break; 734 case 't': 735 printf(":%lu", when); 736 break; 737 default: 738 putchar(c); 739 } 740 } 741 out: 742 fclose(pfp); 743 fflush(NULL); 744 } 745 746 static int 747 remove_jobs(int argc, char **argv, char *login) 748 /* remove jobs that are specified */ 749 { 750 int i, r; 751 int error = 0; 752 struct stat buf; 753 struct passwd *pw; 754 755 pw = getpwuid(user); 756 if (pw == NULL) { 757 atabort("Invalid user.\n"); 758 } 759 760 if (argc == 0) 761 usage(); 762 if (chdir(ATDIR) == -1) 763 atabort(CANTCD); 764 for (i = 0; i < argc; i++) 765 if (strchr(argv[i], '/') != NULL) { 766 fprintf(stderr, "at: %s: not a valid job-id\n", 767 argv[i]); 768 } else if (stat(argv[i], &buf)) { 769 fprintf(stderr, "at: %s: ", argv[i]); 770 perror(""); 771 } else if ((user != buf.st_uid) && 772 (!chkauthattr(CRONADMIN_AUTH, pw->pw_name))) { 773 fprintf(stderr, "at: you don't own %s\n", 774 argv[i]); 775 error = 1; 776 } else { 777 if (chkauthattr(CRONADMIN_AUTH, pw->pw_name)) { 778 login = getuser((uid_t)buf.st_uid); 779 if (login == NULL) { 780 if (per_errno == 2) 781 atabort(BADSHELL); 782 else 783 atabort(INVALIDUSER); 784 } 785 } 786 cron_sendmsg(DELETE, login, argv[i], AT); 787 r = unlink(argv[i]); 788 audit_at_delete(argv[i], ATDIR, r); 789 } 790 return (error); 791 } 792 793 794 795 static int 796 list_jobs(int argc, char **argv, int qflag, int queue) 797 { 798 DIR *dir; 799 int i; 800 int error = 0; 801 char *patdir, *atdir, *ptr; 802 char timebuf[80]; 803 time_t t; 804 struct stat buf, st1, st2; 805 struct dirent *dentry; 806 struct passwd *pw; 807 unsigned int atdirlen; 808 int r; 809 struct passwd *pwd, pwds; 810 char buf_pwd[1024]; 811 char job_file[PATH_MAX]; 812 813 pwd = getpwuid_r(user, &pwds, buf_pwd, sizeof (buf_pwd)); 814 if (pwd == NULL) { 815 atabort("Invalid user.\n"); 816 } 817 818 /* list jobs for user */ 819 if (chdir(ATDIR) == -1) 820 atabort(CANTCD); 821 822 atdirlen = strlen(ATDIR); 823 atdir = xmalloc(atdirlen + 1); 824 strcpy(atdir, ATDIR); 825 patdir = strrchr(atdir, '/'); 826 *patdir = '\0'; 827 if (argc == 0) { 828 /* list all jobs for a user */ 829 if (stat(ATDIR, &st1) != 0 || stat(atdir, &st2) != 0) 830 atabort("Can not get status of spooling" 831 "directory for at"); 832 if ((dir = opendir(ATDIR)) == NULL) 833 atabort(NOOPENDIR); 834 while (1) { 835 if ((dentry = readdir(dir)) == NULL) 836 break; 837 if ((dentry->d_ino == st1.st_ino) || 838 (dentry->d_ino == st2.st_ino)) 839 continue; 840 if ((r = audit_cron_is_anc_name(dentry->d_name)) == 1) 841 continue; 842 if (stat(dentry->d_name, &buf)) { 843 unlink(dentry->d_name); 844 audit_cron_delete_anc_file(dentry->d_name, 845 NULL); 846 continue; 847 } 848 if ((!chkauthattr(CRONADMIN_AUTH, pwd->pw_name)) && 849 (buf.st_uid != user)) 850 continue; 851 ptr = dentry->d_name; 852 if (((t = num(&ptr)) == 0) || (*ptr != '.')) 853 continue; 854 strcpy(job_file, patdir); 855 strcat(job_file, dentry->d_name); 856 if (pflag && not_this_project(job_file)) 857 continue; 858 ascftime(timebuf, FORMAT, localtime(&t)); 859 if ((chkauthattr(CRONADMIN_AUTH, pwd->pw_name)) && 860 ((pw = getpwuid(buf.st_uid)) != NULL)) { 861 if (!qflag || (qflag && 862 check_queue(ptr, queue))) 863 printf("user = %s\t%s\t%s\n", 864 pw->pw_name, dentry->d_name, 865 timebuf); 866 } else 867 if (!qflag || (qflag && 868 check_queue(ptr, queue))) 869 printf("%s\t%s\n", 870 dentry->d_name, timebuf); 871 } 872 (void) closedir(dir); 873 } else /* list particular jobs for user */ 874 for (i = 0; i < argc; i++) { 875 ptr = argv[i]; 876 strlcpy(job_file, patdir, PATH_MAX); 877 strlcat(job_file, ptr, PATH_MAX); 878 if (((t = num(&ptr)) == 0) || (*ptr != '.')) { 879 fprintf(stderr, gettext( 880 "at: invalid job name %s\n"), argv[i]); 881 error = 1; 882 } else if (stat(argv[i], &buf)) { 883 fprintf(stderr, "at: %s: ", argv[i]); 884 perror(""); 885 error = 1; 886 } else if ((user != buf.st_uid) && 887 (!chkauthattr(CRONADMIN_AUTH, pwd->pw_name))) { 888 fprintf(stderr, gettext( 889 "at: you don't own %s\n"), argv[i]); 890 error = 1; 891 } else if (pflag && not_this_project(job_file)) { 892 continue; 893 } else { 894 if (!qflag || (qflag && 895 check_queue(ptr, queue))) { 896 ascftime(timebuf, FORMAT, 897 localtime(&t)); 898 printf("%s\t%s\n", argv[i], timebuf); 899 } 900 } 901 } 902 return (error); 903 } 904 905 /* 906 * open the command file and read the project id line 907 * compare to the project number provided via -p on the command line 908 * return 0 if they match, 1 if they don't match or an error occurs. 909 */ 910 #define SKIPCOUNT 3 /* lines to skip to get to project line in file */ 911 912 static int 913 not_this_project(char *filename) 914 { 915 FILE *fp; 916 projid_t sproj; 917 int i; 918 919 if ((fp = fopen(filename, "r")) == NULL) 920 return (1); 921 922 for (i = 0; i < SKIPCOUNT; i++) 923 fscanf(fp, "%*[^\n]\n"); 924 925 fscanf(fp, ": project: %d\n", &sproj); 926 fclose(fp); 927 928 return (sproj == project ? 0 : 1); 929 } 930 931 static int 932 check_queue(char *name, int queue) 933 { 934 if ((name[strlen(name) - 1] - 'a') == queue) 935 return (1); 936 else 937 return (0); 938 } 939 940 static time_t 941 parse_time(char *t) 942 { 943 int century = 0; 944 int seconds = 0; 945 char *p; 946 time_t when = 0; 947 struct tm tm; 948 949 /* 950 * time in the following format (defined by the touch(1) spec): 951 * [[CC]YY]MMDDhhmm[.SS] 952 */ 953 if ((p = strchr(t, '.')) != NULL) { 954 if (strchr(p+1, '.') != NULL) 955 atabort(BADTIME); 956 seconds = atoi_for2(p+1); 957 *p = '\0'; 958 } 959 960 memset(&tm, 0, sizeof (struct tm)); 961 when = time(0); 962 tm.tm_year = localtime(&when)->tm_year; 963 964 switch (strlen(t)) { 965 case 12: /* CCYYMMDDhhmm */ 966 century = atoi_for2(t); 967 t += 2; 968 case 10: /* YYMMDDhhmm */ 969 tm.tm_year = atoi_for2(t); 970 t += 2; 971 if (century == 0) { 972 if (tm.tm_year < 69) 973 tm.tm_year += 100; 974 } else 975 tm.tm_year += (century - 19) * 100; 976 case 8: /* MMDDhhmm */ 977 tm.tm_mon = atoi_for2(t) - 1; 978 t += 2; 979 tm.tm_mday = atoi_for2(t); 980 t += 2; 981 tm.tm_hour = atoi_for2(t); 982 t += 2; 983 tm.tm_min = atoi_for2(t); 984 t += 2; 985 tm.tm_sec = seconds; 986 break; 987 default: 988 atabort(BADTIME); 989 } 990 991 if ((when = mktime(&tm)) == -1) 992 atabort(BADTIME); 993 if (tm.tm_isdst) 994 when -= (timezone-altzone); 995 return (when); 996 } 997 998 static int 999 atoi_for2(char *p) { 1000 int value; 1001 1002 value = (*p - '0') * 10 + *(p+1) - '0'; 1003 if ((value < 0) || (value > 99)) 1004 atabort(BADTIME); 1005 return (value); 1006 } 1007 1008 static void 1009 usage(void) 1010 { 1011 fprintf(stderr, USAGE); 1012 exit(1); 1013 } 1014