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