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