Fri Apr 2 06:31:53 2021 UTC ()
Add an XXX reminder to convert at(1) to use parsedate(3) in <util.h>.


(simonb)
diff -r1.31 -r1.32 src/usr.bin/at/at.c

cvs diff -r1.31 -r1.32 src/usr.bin/at/at.c (switch to unified diff)

--- src/usr.bin/at/at.c 2016/03/13 00:32:09 1.31
+++ src/usr.bin/at/at.c 2021/04/02 06:31:53 1.32
@@ -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
67enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */ 67enum { 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
72static char rcsid[] = "$OpenBSD: at.c,v 1.15 1998/06/03 16:20:26 deraadt Exp $"; 72static 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
78const char *no_export[] = {"TERM", "TERMCAP", "DISPLAY", "_"}; 78const char *no_export[] = {"TERM", "TERMCAP", "DISPLAY", "_"};
79static int send_mail = 0; 79static int send_mail = 0;
80 80
81/* External variables */ 81/* External variables */
82 82
83extern char **environ; 83extern char **environ;
84bool fcreated = false; 84bool fcreated = false;
85char atfile[FILENAME_MAX]; 85char atfile[FILENAME_MAX];
86 86
87char *atinput = NULL; /* where to get input from */ 87char *atinput = NULL; /* where to get input from */
88unsigned char atqueue = 0; /* which queue to examine for jobs (atq) */ 88unsigned char atqueue = 0; /* which queue to examine for jobs (atq) */
89char atverify = 0; /* verify time instead of queuing job */ 89char 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);
95static char *cwdname (void); 95static char *cwdname (void);
96static int nextjob (void); 96static int nextjob (void);
97static void writefile (time_t, unsigned char); 97static void writefile (time_t, unsigned char);
98static void list_jobs (void); 98static void list_jobs (void);
99static void process_jobs (int, char **, int); 99static void process_jobs (int, char **, int);
100 100
101/* Signal catching functions */ 101/* Signal catching functions */
102 102
103/*ARGSUSED*/ 103/*ARGSUSED*/
104static void 104static void
105sigc(int signo) 105sigc(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*/
119static void 119static void
120alarmc(int signo) 120alarmc(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
130static char * 130static char *
131cwdname(void) 131cwdname(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
143static int 143static int
144nextjob(void) 144nextjob(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
166static void 166static void
167writefile(time_t runtimer, unsigned char queue) 167writefile(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
419static void 419static void
420list_jobs(void) 420list_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
498static void 498static void
499process_jobs(int argc, char **argv, int what) 499process_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
581int 581int
582main(int argc, char **argv) 582main(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}