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