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