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