/usr.bin/at/at.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 890 lines · 613 code · 155 blank · 122 comment · 181 complexity · 990f9f66d43fd13971d5e6ab21ca1039 MD5 · raw file

  1. /*
  2. * at.c : Put file into atrun queue
  3. * Copyright (C) 1993, 1994 Thomas Koenig
  4. *
  5. * Atrun & Atq modifications
  6. * Copyright (C) 1993 David Parsons
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. The name of the author(s) may not be used to endorse or promote
  14. * products derived from this software without specific prior written
  15. * permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
  18. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
  21. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. #include <sys/cdefs.h>
  29. __FBSDID("$FreeBSD$");
  30. #define _USE_BSD 1
  31. /* System Headers */
  32. #include <sys/param.h>
  33. #include <sys/stat.h>
  34. #include <sys/time.h>
  35. #include <sys/wait.h>
  36. #include <ctype.h>
  37. #include <dirent.h>
  38. #include <err.h>
  39. #include <errno.h>
  40. #include <fcntl.h>
  41. #ifndef __FreeBSD__
  42. #include <getopt.h>
  43. #endif
  44. #ifdef __FreeBSD__
  45. #include <locale.h>
  46. #endif
  47. #include <pwd.h>
  48. #include <signal.h>
  49. #include <stddef.h>
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <string.h>
  53. #include <time.h>
  54. #include <unistd.h>
  55. /* Local headers */
  56. #include "at.h"
  57. #include "panic.h"
  58. #include "parsetime.h"
  59. #include "perm.h"
  60. #define MAIN
  61. #include "privs.h"
  62. /* Macros */
  63. #ifndef ATJOB_DIR
  64. #define ATJOB_DIR "/usr/spool/atjobs/"
  65. #endif
  66. #ifndef LFILE
  67. #define LFILE ATJOB_DIR ".lockfile"
  68. #endif
  69. #ifndef ATJOB_MX
  70. #define ATJOB_MX 255
  71. #endif
  72. #define ALARMC 10 /* Number of seconds to wait for timeout */
  73. #define SIZE 255
  74. #define TIMESIZE 50
  75. enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */
  76. /* File scope variables */
  77. static const char *no_export[] = {
  78. "TERM", "TERMCAP", "DISPLAY", "_"
  79. };
  80. static int send_mail = 0;
  81. static char *atinput = NULL; /* where to get input from */
  82. static char atqueue = 0; /* which queue to examine for jobs (atq) */
  83. /* External variables */
  84. extern char **environ;
  85. int fcreated;
  86. char atfile[] = ATJOB_DIR "12345678901234";
  87. char atverify = 0; /* verify time instead of queuing job */
  88. char *namep;
  89. /* Function declarations */
  90. static void sigc(int signo);
  91. static void alarmc(int signo);
  92. static char *cwdname(void);
  93. static void writefile(time_t runtimer, char queue);
  94. static void list_jobs(long *, int);
  95. static long nextjob(void);
  96. static time_t ttime(const char *arg);
  97. static int in_job_list(long, long *, int);
  98. static long *get_job_list(int, char *[], int *);
  99. /* Signal catching functions */
  100. static void sigc(int signo __unused)
  101. {
  102. /* If the user presses ^C, remove the spool file and exit
  103. */
  104. if (fcreated)
  105. {
  106. PRIV_START
  107. unlink(atfile);
  108. PRIV_END
  109. }
  110. _exit(EXIT_FAILURE);
  111. }
  112. static void alarmc(int signo __unused)
  113. {
  114. char buf[1024];
  115. /* Time out after some seconds. */
  116. strlcpy(buf, namep, sizeof(buf));
  117. strlcat(buf, ": file locking timed out\n", sizeof(buf));
  118. write(STDERR_FILENO, buf, strlen(buf));
  119. sigc(0);
  120. }
  121. /* Local functions */
  122. static char *cwdname(void)
  123. {
  124. /* Read in the current directory; the name will be overwritten on
  125. * subsequent calls.
  126. */
  127. static char *ptr = NULL;
  128. static size_t size = SIZE;
  129. if (ptr == NULL)
  130. if ((ptr = malloc(size)) == NULL)
  131. errx(EXIT_FAILURE, "virtual memory exhausted");
  132. while (1)
  133. {
  134. if (ptr == NULL)
  135. panic("out of memory");
  136. if (getcwd(ptr, size-1) != NULL)
  137. return ptr;
  138. if (errno != ERANGE)
  139. perr("cannot get directory");
  140. free (ptr);
  141. size += SIZE;
  142. if ((ptr = malloc(size)) == NULL)
  143. errx(EXIT_FAILURE, "virtual memory exhausted");
  144. }
  145. }
  146. static long
  147. nextjob(void)
  148. {
  149. long jobno;
  150. FILE *fid;
  151. if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != NULL) {
  152. if (fscanf(fid, "%5lx", &jobno) == 1) {
  153. rewind(fid);
  154. jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */
  155. fprintf(fid, "%05lx\n", jobno);
  156. }
  157. else
  158. jobno = EOF;
  159. fclose(fid);
  160. return jobno;
  161. }
  162. else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != NULL) {
  163. fprintf(fid, "%05lx\n", jobno = 1);
  164. fclose(fid);
  165. return 1;
  166. }
  167. return EOF;
  168. }
  169. static void
  170. writefile(time_t runtimer, char queue)
  171. {
  172. /* This does most of the work if at or batch are invoked for writing a job.
  173. */
  174. long jobno;
  175. char *ap, *ppos, *mailname;
  176. struct passwd *pass_entry;
  177. struct stat statbuf;
  178. int fdes, lockdes, fd2;
  179. FILE *fp, *fpin;
  180. struct sigaction act;
  181. char **atenv;
  182. int ch;
  183. mode_t cmask;
  184. struct flock lock;
  185. #ifdef __FreeBSD__
  186. (void) setlocale(LC_TIME, "");
  187. #endif
  188. /* Install the signal handler for SIGINT; terminate after removing the
  189. * spool file if necessary
  190. */
  191. act.sa_handler = sigc;
  192. sigemptyset(&(act.sa_mask));
  193. act.sa_flags = 0;
  194. sigaction(SIGINT, &act, NULL);
  195. ppos = atfile + strlen(ATJOB_DIR);
  196. /* Loop over all possible file names for running something at this
  197. * particular time, see if a file is there; the first empty slot at any
  198. * particular time is used. Lock the file LFILE first to make sure
  199. * we're alone when doing this.
  200. */
  201. PRIV_START
  202. if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0)
  203. perr("cannot open lockfile " LFILE);
  204. lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0;
  205. lock.l_len = 0;
  206. act.sa_handler = alarmc;
  207. sigemptyset(&(act.sa_mask));
  208. act.sa_flags = 0;
  209. /* Set an alarm so a timeout occurs after ALARMC seconds, in case
  210. * something is seriously broken.
  211. */
  212. sigaction(SIGALRM, &act, NULL);
  213. alarm(ALARMC);
  214. fcntl(lockdes, F_SETLKW, &lock);
  215. alarm(0);
  216. if ((jobno = nextjob()) == EOF)
  217. perr("cannot generate job number");
  218. sprintf(ppos, "%c%5lx%8lx", queue,
  219. jobno, (unsigned long) (runtimer/60));
  220. for(ap=ppos; *ap != '\0'; ap ++)
  221. if (*ap == ' ')
  222. *ap = '0';
  223. if (stat(atfile, &statbuf) != 0)
  224. if (errno != ENOENT)
  225. perr("cannot access " ATJOB_DIR);
  226. /* Create the file. The x bit is only going to be set after it has
  227. * been completely written out, to make sure it is not executed in the
  228. * meantime. To make sure they do not get deleted, turn off their r
  229. * bit. Yes, this is a kluge.
  230. */
  231. cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
  232. if ((fdes = creat(atfile, O_WRONLY)) == -1)
  233. perr("cannot create atjob file");
  234. if ((fd2 = dup(fdes)) <0)
  235. perr("error in dup() of job file");
  236. if(fchown(fd2, real_uid, real_gid) != 0)
  237. perr("cannot give away file");
  238. PRIV_END
  239. /* We no longer need suid root; now we just need to be able to write
  240. * to the directory, if necessary.
  241. */
  242. REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
  243. /* We've successfully created the file; let's set the flag so it
  244. * gets removed in case of an interrupt or error.
  245. */
  246. fcreated = 1;
  247. /* Now we can release the lock, so other people can access it
  248. */
  249. lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0;
  250. lock.l_len = 0;
  251. fcntl(lockdes, F_SETLKW, &lock);
  252. close(lockdes);
  253. if((fp = fdopen(fdes, "w")) == NULL)
  254. panic("cannot reopen atjob file");
  255. /* Get the userid to mail to, first by trying getlogin(),
  256. * then from LOGNAME, finally from getpwuid().
  257. */
  258. mailname = getlogin();
  259. if (mailname == NULL)
  260. mailname = getenv("LOGNAME");
  261. if ((mailname == NULL) || (mailname[0] == '\0')
  262. || (strlen(mailname) >= MAXLOGNAME) || (getpwnam(mailname)==NULL))
  263. {
  264. pass_entry = getpwuid(real_uid);
  265. if (pass_entry != NULL)
  266. mailname = pass_entry->pw_name;
  267. }
  268. if (atinput != (char *) NULL)
  269. {
  270. fpin = freopen(atinput, "r", stdin);
  271. if (fpin == NULL)
  272. perr("cannot open input file");
  273. }
  274. fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %*s %d\n",
  275. (long) real_uid, (long) real_gid, MAXLOGNAME - 1, mailname,
  276. send_mail);
  277. /* Write out the umask at the time of invocation
  278. */
  279. fprintf(fp, "umask %lo\n", (unsigned long) cmask);
  280. /* Write out the environment. Anything that may look like a
  281. * special character to the shell is quoted, except for \n, which is
  282. * done with a pair of "'s. Don't export the no_export list (such
  283. * as TERM or DISPLAY) because we don't want these.
  284. */
  285. for (atenv= environ; *atenv != NULL; atenv++)
  286. {
  287. int export = 1;
  288. char *eqp;
  289. eqp = strchr(*atenv, '=');
  290. if (ap == NULL)
  291. eqp = *atenv;
  292. else
  293. {
  294. size_t i;
  295. for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++)
  296. {
  297. export = export
  298. && (strncmp(*atenv, no_export[i],
  299. (size_t) (eqp-*atenv)) != 0);
  300. }
  301. eqp++;
  302. }
  303. if (export)
  304. {
  305. fwrite(*atenv, sizeof(char), eqp-*atenv, fp);
  306. for(ap = eqp;*ap != '\0'; ap++)
  307. {
  308. if (*ap == '\n')
  309. fprintf(fp, "\"\n\"");
  310. else
  311. {
  312. if (!isalnum(*ap)) {
  313. switch (*ap) {
  314. case '%': case '/': case '{': case '[':
  315. case ']': case '=': case '}': case '@':
  316. case '+': case '#': case ',': case '.':
  317. case ':': case '-': case '_':
  318. break;
  319. default:
  320. fputc('\\', fp);
  321. break;
  322. }
  323. }
  324. fputc(*ap, fp);
  325. }
  326. }
  327. fputs("; export ", fp);
  328. fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp);
  329. fputc('\n', fp);
  330. }
  331. }
  332. /* Cd to the directory at the time and write out all the
  333. * commands the user supplies from stdin.
  334. */
  335. fprintf(fp, "cd ");
  336. for (ap = cwdname(); *ap != '\0'; ap++)
  337. {
  338. if (*ap == '\n')
  339. fprintf(fp, "\"\n\"");
  340. else
  341. {
  342. if (*ap != '/' && !isalnum(*ap))
  343. fputc('\\', fp);
  344. fputc(*ap, fp);
  345. }
  346. }
  347. /* Test cd's exit status: die if the original directory has been
  348. * removed, become unreadable or whatever
  349. */
  350. fprintf(fp, " || {\n\t echo 'Execution directory "
  351. "inaccessible' >&2\n\t exit 1\n}\n");
  352. while((ch = getchar()) != EOF)
  353. fputc(ch, fp);
  354. fprintf(fp, "\n");
  355. if (ferror(fp))
  356. panic("output error");
  357. if (ferror(stdin))
  358. panic("input error");
  359. fclose(fp);
  360. /* Set the x bit so that we're ready to start executing
  361. */
  362. if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
  363. perr("cannot give away file");
  364. close(fd2);
  365. fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno);
  366. }
  367. static int
  368. in_job_list(long job, long *joblist, int len)
  369. {
  370. int i;
  371. for (i = 0; i < len; i++)
  372. if (job == joblist[i])
  373. return 1;
  374. return 0;
  375. }
  376. static void
  377. list_jobs(long *joblist, int len)
  378. {
  379. /* List all a user's jobs in the queue, by looping through ATJOB_DIR,
  380. * or everybody's if we are root
  381. */
  382. struct passwd *pw;
  383. DIR *spool;
  384. struct dirent *dirent;
  385. struct stat buf;
  386. struct tm runtime;
  387. unsigned long ctm;
  388. char queue;
  389. long jobno;
  390. time_t runtimer;
  391. char timestr[TIMESIZE];
  392. int first=1;
  393. #ifdef __FreeBSD__
  394. (void) setlocale(LC_TIME, "");
  395. #endif
  396. PRIV_START
  397. if (chdir(ATJOB_DIR) != 0)
  398. perr("cannot change to " ATJOB_DIR);
  399. if ((spool = opendir(".")) == NULL)
  400. perr("cannot open " ATJOB_DIR);
  401. /* Loop over every file in the directory
  402. */
  403. while((dirent = readdir(spool)) != NULL) {
  404. if (stat(dirent->d_name, &buf) != 0)
  405. perr("cannot stat in " ATJOB_DIR);
  406. /* See it's a regular file and has its x bit turned on and
  407. * is the user's
  408. */
  409. if (!S_ISREG(buf.st_mode)
  410. || ((buf.st_uid != real_uid) && ! (real_uid == 0))
  411. || !(S_IXUSR & buf.st_mode || atverify))
  412. continue;
  413. if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3)
  414. continue;
  415. /* If jobs are given, only list those jobs */
  416. if (joblist && !in_job_list(jobno, joblist, len))
  417. continue;
  418. if (atqueue && (queue != atqueue))
  419. continue;
  420. runtimer = 60*(time_t) ctm;
  421. runtime = *localtime(&runtimer);
  422. strftime(timestr, TIMESIZE, "%+", &runtime);
  423. if (first) {
  424. printf("Date\t\t\t\tOwner\t\tQueue\tJob#\n");
  425. first=0;
  426. }
  427. pw = getpwuid(buf.st_uid);
  428. printf("%s\t%-16s%c%s\t%ld\n",
  429. timestr,
  430. pw ? pw->pw_name : "???",
  431. queue,
  432. (S_IXUSR & buf.st_mode) ? "":"(done)",
  433. jobno);
  434. }
  435. PRIV_END
  436. closedir(spool);
  437. }
  438. static void
  439. process_jobs(int argc, char **argv, int what)
  440. {
  441. /* Delete every argument (job - ID) given
  442. */
  443. int i;
  444. struct stat buf;
  445. DIR *spool;
  446. struct dirent *dirent;
  447. unsigned long ctm;
  448. char queue;
  449. long jobno;
  450. PRIV_START
  451. if (chdir(ATJOB_DIR) != 0)
  452. perr("cannot change to " ATJOB_DIR);
  453. if ((spool = opendir(".")) == NULL)
  454. perr("cannot open " ATJOB_DIR);
  455. PRIV_END
  456. /* Loop over every file in the directory
  457. */
  458. while((dirent = readdir(spool)) != NULL) {
  459. PRIV_START
  460. if (stat(dirent->d_name, &buf) != 0)
  461. perr("cannot stat in " ATJOB_DIR);
  462. PRIV_END
  463. if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3)
  464. continue;
  465. for (i=optind; i < argc; i++) {
  466. if (atoi(argv[i]) == jobno) {
  467. if ((buf.st_uid != real_uid) && !(real_uid == 0))
  468. errx(EXIT_FAILURE, "%s: not owner", argv[i]);
  469. switch (what) {
  470. case ATRM:
  471. PRIV_START
  472. if (unlink(dirent->d_name) != 0)
  473. perr(dirent->d_name);
  474. PRIV_END
  475. break;
  476. case CAT:
  477. {
  478. FILE *fp;
  479. int ch;
  480. PRIV_START
  481. fp = fopen(dirent->d_name,"r");
  482. PRIV_END
  483. if (!fp) {
  484. perr("cannot open file");
  485. }
  486. while((ch = getc(fp)) != EOF) {
  487. putchar(ch);
  488. }
  489. fclose(fp);
  490. }
  491. break;
  492. default:
  493. errx(EXIT_FAILURE, "internal error, process_jobs = %d",
  494. what);
  495. }
  496. }
  497. }
  498. }
  499. closedir(spool);
  500. } /* delete_jobs */
  501. #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
  502. static time_t
  503. ttime(const char *arg)
  504. {
  505. /*
  506. * This is pretty much a copy of stime_arg1() from touch.c. I changed
  507. * the return value and the argument list because it's more convenient
  508. * (IMO) to do everything in one place. - Joe Halpin
  509. */
  510. struct timeval tv[2];
  511. time_t now;
  512. struct tm *t;
  513. int yearset;
  514. char *p;
  515. if (gettimeofday(&tv[0], NULL))
  516. panic("Cannot get current time");
  517. /* Start with the current time. */
  518. now = tv[0].tv_sec;
  519. if ((t = localtime(&now)) == NULL)
  520. panic("localtime");
  521. /* [[CC]YY]MMDDhhmm[.SS] */
  522. if ((p = strchr(arg, '.')) == NULL)
  523. t->tm_sec = 0; /* Seconds defaults to 0. */
  524. else {
  525. if (strlen(p + 1) != 2)
  526. goto terr;
  527. *p++ = '\0';
  528. t->tm_sec = ATOI2(p);
  529. }
  530. yearset = 0;
  531. switch(strlen(arg)) {
  532. case 12: /* CCYYMMDDhhmm */
  533. t->tm_year = ATOI2(arg);
  534. t->tm_year *= 100;
  535. yearset = 1;
  536. /* FALLTHROUGH */
  537. case 10: /* YYMMDDhhmm */
  538. if (yearset) {
  539. yearset = ATOI2(arg);
  540. t->tm_year += yearset;
  541. } else {
  542. yearset = ATOI2(arg);
  543. t->tm_year = yearset + 2000;
  544. }
  545. t->tm_year -= 1900; /* Convert to UNIX time. */
  546. /* FALLTHROUGH */
  547. case 8: /* MMDDhhmm */
  548. t->tm_mon = ATOI2(arg);
  549. --t->tm_mon; /* Convert from 01-12 to 00-11 */
  550. t->tm_mday = ATOI2(arg);
  551. t->tm_hour = ATOI2(arg);
  552. t->tm_min = ATOI2(arg);
  553. break;
  554. default:
  555. goto terr;
  556. }
  557. t->tm_isdst = -1; /* Figure out DST. */
  558. tv[0].tv_sec = tv[1].tv_sec = mktime(t);
  559. if (tv[0].tv_sec != -1)
  560. return tv[0].tv_sec;
  561. else
  562. terr:
  563. panic(
  564. "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
  565. }
  566. static long *
  567. get_job_list(int argc, char *argv[], int *joblen)
  568. {
  569. int i, len;
  570. long *joblist;
  571. char *ep;
  572. joblist = NULL;
  573. len = argc;
  574. if (len > 0) {
  575. if ((joblist = malloc(len * sizeof(*joblist))) == NULL)
  576. panic("out of memory");
  577. for (i = 0; i < argc; i++) {
  578. errno = 0;
  579. if ((joblist[i] = strtol(argv[i], &ep, 10)) < 0 ||
  580. ep == argv[i] || *ep != '\0' || errno)
  581. panic("invalid job number");
  582. }
  583. }
  584. *joblen = len;
  585. return joblist;
  586. }
  587. int
  588. main(int argc, char **argv)
  589. {
  590. int c;
  591. char queue = DEFAULT_AT_QUEUE;
  592. char queue_set = 0;
  593. char *pgm;
  594. int program = AT; /* our default program */
  595. const char *options = "q:f:t:rmvldbc"; /* default options for at */
  596. time_t timer;
  597. long *joblist;
  598. int joblen;
  599. joblist = NULL;
  600. joblen = 0;
  601. timer = -1;
  602. RELINQUISH_PRIVS
  603. /* Eat any leading paths
  604. */
  605. if ((pgm = strrchr(argv[0], '/')) == NULL)
  606. pgm = argv[0];
  607. else
  608. pgm++;
  609. namep = pgm;
  610. /* find out what this program is supposed to do
  611. */
  612. if (strcmp(pgm, "atq") == 0) {
  613. program = ATQ;
  614. options = "q:v";
  615. }
  616. else if (strcmp(pgm, "atrm") == 0) {
  617. program = ATRM;
  618. options = "";
  619. }
  620. else if (strcmp(pgm, "batch") == 0) {
  621. program = BATCH;
  622. options = "f:q:mv";
  623. }
  624. /* process whatever options we can process
  625. */
  626. opterr=1;
  627. while ((c=getopt(argc, argv, options)) != -1)
  628. switch (c) {
  629. case 'v': /* verify time settings */
  630. atverify = 1;
  631. break;
  632. case 'm': /* send mail when job is complete */
  633. send_mail = 1;
  634. break;
  635. case 'f':
  636. atinput = optarg;
  637. break;
  638. case 'q': /* specify queue */
  639. if (strlen(optarg) > 1)
  640. usage();
  641. atqueue = queue = *optarg;
  642. if (!(islower(queue)||isupper(queue)))
  643. usage();
  644. queue_set = 1;
  645. break;
  646. case 'd':
  647. warnx("-d is deprecated; use -r instead");
  648. /* fall through to 'r' */
  649. case 'r':
  650. if (program != AT)
  651. usage();
  652. program = ATRM;
  653. options = "";
  654. break;
  655. case 't':
  656. if (program != AT)
  657. usage();
  658. timer = ttime(optarg);
  659. break;
  660. case 'l':
  661. if (program != AT)
  662. usage();
  663. program = ATQ;
  664. options = "q:";
  665. break;
  666. case 'b':
  667. if (program != AT)
  668. usage();
  669. program = BATCH;
  670. options = "f:q:mv";
  671. break;
  672. case 'c':
  673. program = CAT;
  674. options = "";
  675. break;
  676. default:
  677. usage();
  678. break;
  679. }
  680. /* end of options eating
  681. */
  682. /* select our program
  683. */
  684. if(!check_permission())
  685. errx(EXIT_FAILURE, "you do not have permission to use this program");
  686. switch (program) {
  687. case ATQ:
  688. REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
  689. if (queue_set == 0)
  690. joblist = get_job_list(argc - optind, argv + optind, &joblen);
  691. list_jobs(joblist, joblen);
  692. break;
  693. case ATRM:
  694. REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
  695. process_jobs(argc, argv, ATRM);
  696. break;
  697. case CAT:
  698. process_jobs(argc, argv, CAT);
  699. break;
  700. case AT:
  701. /*
  702. * If timer is > -1, then the user gave the time with -t. In that
  703. * case, it's already been set. If not, set it now.
  704. */
  705. if (timer == -1)
  706. timer = parsetime(argc, argv);
  707. if (atverify)
  708. {
  709. struct tm *tm = localtime(&timer);
  710. fprintf(stderr, "%s\n", asctime(tm));
  711. }
  712. writefile(timer, queue);
  713. break;
  714. case BATCH:
  715. if (queue_set)
  716. queue = toupper(queue);
  717. else
  718. queue = DEFAULT_BATCH_QUEUE;
  719. if (argc > optind)
  720. timer = parsetime(argc, argv);
  721. else
  722. timer = time(NULL);
  723. if (atverify)
  724. {
  725. struct tm *tm = localtime(&timer);
  726. fprintf(stderr, "%s\n", asctime(tm));
  727. }
  728. writefile(timer, queue);
  729. break;
  730. default:
  731. panic("internal error");
  732. break;
  733. }
  734. exit(EXIT_SUCCESS);
  735. }