/[pkg-cron]/tags/debian_version_3.0pl1-115/crontab.c
ViewVC logotype

Contents of /tags/debian_version_3.0pl1-115/crontab.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 619 - (hide annotations) (download)
Sat Oct 16 11:16:28 2010 UTC (2 years, 7 months ago) by jfs
Original Path: branches/sid-squeeze/crontab.c
File MIME type: text/plain
File size: 24779 byte(s)
Preparation for -115 upload
1 steveg 2 /* Copyright 1988,1990,1993,1994 by Paul Vixie
2     * All rights reserved
3     *
4     * Distribute freely, except: don't remove my name from the source or
5     * documentation (don't take credit for my work), mark your changes (don't
6     * get me blamed for your possible bugs), don't alter or remove this
7     * notice. May be sold if buildable source is provided to buyer. No
8     * warrantee of any kind, express or implied, is included with this
9     * software; use at your own risk, responsibility for damages (if any) to
10     * anyone resulting from the use of this software rests entirely with the
11     * user.
12     *
13     * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
14     * I'll try to keep a version up to date. I can be reached as follows:
15     * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
16     */
17    
18     #if !defined(lint) && !defined(LINT)
19     static char rcsid[] = "$Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $";
20     #endif
21    
22     /* crontab - install and manage per-user crontab files
23     * vix 02may87 [RCS has the rest of the log]
24     * vix 26jan87 [original]
25     */
26    
27    
28     #define MAIN_PROGRAM
29    
30    
31     #include "cron.h"
32     #include <errno.h>
33     #include <fcntl.h>
34 steveg 62 #include <signal.h>
35 steveg 2 #include <sys/file.h>
36     #include <sys/stat.h>
37     #ifdef USE_UTIMES
38     # include <sys/time.h>
39     #else
40     # include <time.h>
41     # include <utime.h>
42     #endif
43     #if defined(POSIX)
44     # include <locale.h>
45     #endif
46    
47    
48     #define NHEADER_LINES 3
49    
50     enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
51    
52     #if DEBUGGING
53     static char *Options[] = { "???", "list", "delete", "edit", "replace" };
54     #endif
55    
56    
57     static PID_T Pid;
58 steveg 166 static char *User, *RealUser;
59 steveg 2 static char Filename[MAX_FNAME];
60 steveg 215 static char Directory[MAX_FNAME];
61 steveg 99 static FILE *NewCrontab = NULL;
62 steveg 2 static int CheckErrorCount;
63 jfs 385 static int PromptOnDelete;
64 steveg 2 static enum opt_t Option;
65     static struct passwd *pw;
66     static void list_cmd __P((void)),
67     delete_cmd __P((void)),
68     edit_cmd __P((void)),
69     poke_daemon __P((void)),
70     check_error __P((char *)),
71     parse_args __P((int c, char *v[]));
72     static int replace_cmd __P((void));
73    
74 steveg 215 /* Support edit command */
75     static int create_tmp_crontab __P((void));
76     static int open_tmp_crontab __P((struct stat *fsbuf));
77     static void cleanup_tmp_crontab __P((void));
78 steveg 2
79     static void
80     usage(msg)
81     char *msg;
82     {
83     fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg);
84     fprintf(stderr, "usage:\t%s [-u user] file\n", ProgramName);
85 jfs 445 fprintf(stderr, "\t%s [ -u user ] [ -i ] { -e | -l | -r }\n", ProgramName);
86 steveg 2 fprintf(stderr, "\t\t(default operation is replace, per 1003.2)\n");
87     fprintf(stderr, "\t-e\t(edit user's crontab)\n");
88     fprintf(stderr, "\t-l\t(list user's crontab)\n");
89     fprintf(stderr, "\t-r\t(delete user's crontab)\n");
90 jfs 385 fprintf(stderr, "\t-i\t(prompt before deleting user's crontab)\n");
91 steveg 2 exit(ERROR_EXIT);
92     }
93    
94    
95     int
96     main(argc, argv)
97     int argc;
98     char *argv[];
99     {
100     int exitstatus;
101    
102     Pid = getpid();
103     ProgramName = argv[0];
104    
105     #if defined(POSIX)
106     setlocale(LC_ALL, "");
107     #endif
108    
109     #if defined(BSD)
110     setlinebuf(stderr);
111     #endif
112 jfs 419 if (argv[1] == NULL) {
113     argv[1] = "-";
114     }
115 steveg 2 parse_args(argc, argv); /* sets many globals, opens a file */
116     set_cron_cwd();
117     if (!allowed(User)) {
118 jfs 438 if ( getuid() != 0 ) {
119     fprintf(stderr,
120     "You (%s) are not allowed to use this program (%s)\n",
121     User, ProgramName);
122     fprintf(stderr, "See crontab(1) for more information\n");
123     log_it(RealUser, Pid, "AUTH", "crontab command not allowed");
124     } else {
125     /* If the user is not allowed but root is running the
126     * program warn but do not log */
127     fprintf(stderr,
128     "The user %s cannot use this program (%s)\n",
129     User, ProgramName);
130     }
131 steveg 2 exit(ERROR_EXIT);
132     }
133     exitstatus = OK_EXIT;
134     switch (Option) {
135     case opt_list: list_cmd();
136     break;
137     case opt_delete: delete_cmd();
138     break;
139     case opt_edit: edit_cmd();
140     break;
141     case opt_replace: if (replace_cmd() < 0)
142     exitstatus = ERROR_EXIT;
143     break;
144 steveg 99 /* The following was added to shut
145     -Wall up, but it will never be hit,
146     because the option parser will catch
147     it */
148     case opt_unknown: usage("unknown option specified");
149     break;
150 steveg 2 }
151 steveg 278 exit(exitstatus);
152 steveg 2 /*NOTREACHED*/
153     }
154    
155 steveg 62 #if DEBUGGING
156 jfs 385 char *getoptarg = "u:lerix:";
157 steveg 62 #else
158 jfs 385 char *getoptarg = "u:leri";
159 steveg 62 #endif
160 steveg 2
161 steveg 62
162 steveg 2 static void
163     parse_args(argc, argv)
164     int argc;
165     char *argv[];
166     {
167     int argch;
168 steveg 121 struct stat statbuf;
169 steveg 2
170     if (!(pw = getpwuid(getuid()))) {
171     fprintf(stderr, "%s: your UID isn't in the passwd file.\n",
172     ProgramName);
173     fprintf(stderr, "bailing out.\n");
174     exit(ERROR_EXIT);
175     }
176 steveg 166 if (((User=strdup(pw->pw_name)) == NULL) ||
177     ((RealUser=strdup(pw->pw_name)) == NULL)) {
178     fprintf(stderr, "Memory allocation error\n");
179     exit(ERROR_EXIT);
180     }
181 steveg 2 Filename[0] = '\0';
182     Option = opt_unknown;
183 jfs 385 PromptOnDelete = 0;
184 steveg 62
185     while (EOF != (argch = getopt(argc, argv, getoptarg))) {
186 steveg 2 switch (argch) {
187 steveg 62 #if DEBUGGING
188 steveg 2 case 'x':
189     if (!set_debug_flags(optarg))
190     usage("bad debug option");
191 steveg 62 usage("unrecognized option");
192 steveg 2 break;
193 steveg 62 #endif
194 steveg 2 case 'u':
195     if (!(pw = getpwnam(optarg)))
196     {
197     fprintf(stderr, "%s: user `%s' unknown\n",
198     ProgramName, optarg);
199     exit(ERROR_EXIT);
200     }
201 steveg 16 if ((getuid() != ROOT_UID) &&
202     (getuid() != pw->pw_uid))
203     {
204     fprintf(stderr,
205     "must be privileged to use -u\n");
206     exit(ERROR_EXIT);
207     }
208 steveg 166 free(User);
209     if ((User=strdup(pw->pw_name)) == NULL) {
210     fprintf(stderr, "Memory allocation error\n");
211     exit(ERROR_EXIT);
212     }
213 steveg 2 break;
214     case 'l':
215     if (Option != opt_unknown)
216     usage("only one operation permitted");
217     Option = opt_list;
218     break;
219     case 'r':
220     if (Option != opt_unknown)
221     usage("only one operation permitted");
222     Option = opt_delete;
223     break;
224     case 'e':
225     if (Option != opt_unknown)
226     usage("only one operation permitted");
227     Option = opt_edit;
228     break;
229 jfs 385 case 'i':
230     PromptOnDelete = 1;
231     break;
232 steveg 2 default:
233     usage("unrecognized option");
234     }
235     }
236    
237     endpwent();
238    
239     if (Option != opt_unknown) {
240     if (argv[optind] != NULL) {
241     usage("no arguments permitted after this option");
242     }
243     } else {
244     if (argv[optind] != NULL) {
245     Option = opt_replace;
246 steveg 6 (void) strncpy (Filename, argv[optind], (sizeof Filename)-1);
247     Filename[(sizeof Filename)-1] = '\0';
248    
249 steveg 2 } else {
250     usage("file name must be specified for replace");
251     }
252     }
253    
254     if (Option == opt_replace) {
255     /* we have to open the file here because we're going to
256     * chdir(2) into /var/cron before we get around to
257     * reading the file.
258     */
259     if (!strcmp(Filename, "-")) {
260     NewCrontab = stdin;
261     } else {
262     /* relinquish the setuid status of the binary during
263     * the open, lest nonroot users read files they should
264     * not be able to read. we can't use access() here
265     * since there's a race condition. thanks go out to
266     * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting
267     * the race.
268     */
269    
270     if (swap_uids() < OK) {
271     perror("swapping uids");
272     exit(ERROR_EXIT);
273     }
274     if (!(NewCrontab = fopen(Filename, "r"))) {
275     perror(Filename);
276     exit(ERROR_EXIT);
277     }
278 steveg 121 /* Make sure we opened a normal file. */
279     if (fstat(fileno(NewCrontab), &statbuf) < 0) {
280     perror("fstat");
281     exit(ERROR_EXIT);
282     }
283     if (!S_ISREG(statbuf.st_mode)) {
284     fprintf(stderr, "%s: Not a regular file.\n", Filename);
285     exit(ERROR_EXIT);
286     }
287 steveg 6 if (swap_uids_back() < OK) {
288 steveg 2 perror("swapping uids back");
289     exit(ERROR_EXIT);
290     }
291     }
292     }
293    
294     Debug(DMISC, ("user=%s, file=%s, option=%s\n",
295     User, Filename, Options[(int)Option]))
296     }
297    
298    
299     static void
300     list_cmd() {
301     char n[MAX_FNAME];
302     FILE *f;
303     int ch;
304 steveg 14 #ifdef DEBIAN
305     int x;
306     char *ctnh;
307     #endif
308 steveg 2
309     log_it(RealUser, Pid, "LIST", User);
310 steveg 22 (void) snprintf(n, MAX_FNAME, CRON_TAB(User));
311 steveg 2 if (!(f = fopen(n, "r"))) {
312 jfs 386 if (errno == ENOENT)
313 steveg 2 fprintf(stderr, "no crontab for %s\n", User);
314 jfs 386 else {
315 jfs 437 fprintf(stderr, "%s/: fopen: %s\n", n, strerror(errno));
316 jfs 386 }
317 steveg 2 exit(ERROR_EXIT);
318     }
319    
320     /* file is open. copy to stdout, close.
321     */
322     Set_LineNum(1)
323 steveg 14 #ifdef DEBIAN
324 steveg 35 /* DEBIAN: Don't list header lines unless CRONTAB_NOHEADER is
325     'N'. */
326 steveg 14 /* ignore the top few comments since we probably put them there.
327     */
328 steveg 35 if (!(ctnh = getenv("CRONTAB_NOHEADER")) ||
329     toupper(*ctnh) != 'N')
330     {
331 steveg 14 for (x = 0; x < NHEADER_LINES; x++) {
332     ch = get_char(f);
333     if (EOF == ch)
334     break;
335     if ('#' != ch) {
336 steveg 99 putchar(ch);
337 steveg 14 break;
338     }
339     while (EOF != (ch = get_char(f)))
340     if (ch == '\n')
341     break;
342     if (EOF == ch)
343     break;
344     }
345     }
346     #endif
347 steveg 2 while (EOF != (ch = get_char(f)))
348     putchar(ch);
349     fclose(f);
350     }
351    
352    
353     static void
354     delete_cmd() {
355     char n[MAX_FNAME];
356 jfs 388 char q[MAX_TEMPSTR];
357     int ans;
358 jfs 449 struct stat fsbuf;
359 steveg 2
360 jfs 449 /* Check if the user has a crontab file first */
361     (void) snprintf(n, MAX_FNAME, CRON_TAB(User));
362     if (stat(n, &fsbuf) < 0) {
363     fprintf(stderr, "no crontab for %s\n", User);
364     exit(ERROR_EXIT);
365     }
366    
367 jfs 388 if( PromptOnDelete == 1 )
368     {
369 jfs 461 printf("crontab: really delete %s's crontab? (y/n) ", User);
370 jfs 388 fflush(stdout);
371     ans = 0;
372     q[0] = '\0';
373     while ( ans == 0 ) {
374     (void) fgets(q, sizeof q, stdin);
375     switch (islower(q[0]) ? q[0] : tolower(q[0])) {
376     case 'y':
377     case 'n':
378     ans = 1;
379     break;
380     default:
381     fprintf(stderr, "Please enter Y or N: ");
382     }
383     }
384     if ( (q[0] == 'N') || (q[0] == 'n') )
385     exit(OK_EXIT);
386     }
387    
388 steveg 2 log_it(RealUser, Pid, "DELETE", User);
389     if (unlink(n)) {
390     if (errno == ENOENT)
391     fprintf(stderr, "no crontab for %s\n", User);
392 jfs 386 else {
393 jfs 437 fprintf(stderr, "%s/: unlink: %s\n", CRONDIR, strerror(errno));
394 jfs 386 }
395 steveg 2 exit(ERROR_EXIT);
396     }
397     poke_daemon();
398     }
399    
400    
401     static void
402     check_error(msg)
403     char *msg;
404     {
405     CheckErrorCount++;
406     fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg);
407     }
408    
409    
410 steveg 215 /* The next several function implement 'crontab -e' */
411    
412     /* Returns -1 on error, or fd to tempfile. */
413     static int
414     create_tmp_crontab()
415     {
416     const char *template = "/crontab.XXXXXX";
417     int nfd;
418     char *tmp;
419    
420     /* Create the temp directory. Note that since crontab is
421     setuid(root), TMPDIR only work for root. */
422     if ((tmp=getenv("TMPDIR")) && strlen(tmp) < MAX_FNAME) {
423     strcpy(Directory, tmp);
424     } else {
425     strcpy(Directory,"/tmp");
426     }
427    
428     if (strlen(Directory) + strlen(template) < MAX_FNAME) {
429     strcat(Directory, template);
430     } else {
431     fprintf(stderr, "TMPDIR value is to long -- exiting\n");
432     Directory[0] = '\0';
433     return -1;
434     }
435    
436     if (!mkdtemp(Directory)) {
437     perror(Directory);
438     Directory[0] = '\0';
439     return -1;
440     }
441    
442     /* Now create the actual temporary crontab file */
443     if (snprintf(Filename, MAX_FNAME, "%s/crontab", Directory)
444     >= MAX_FNAME) {
445     fprintf(stderr, "Temporary filename too long - aborting\n");
446     Filename[0] = '\0';
447     return -1;
448     }
449     if ((nfd=open(Filename, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1) {
450     perror(Filename);
451     Filename[0] = '\0';
452     return -1;
453     }
454     return nfd;
455     }
456    
457     /* Re-open the new (temporary) crontab, and check to make sure that
458     no-one is playing games. Return 0 on success, -1 on error. (Why not
459     just fopen() and stat()? Because there's no guarantee that you
460     fopen()ed the file you stat()ed.) */
461     static int
462     open_tmp_crontab(fsbuf)
463     struct stat *fsbuf;
464     {
465     int t;
466     struct stat statbuf;
467    
468     if ((t=open(Filename, O_RDONLY)) < 0) {
469     perror("Can't open tempfile after edit");
470     return -1;
471     }
472    
473     if (fstat(t, &statbuf) < 0) {
474     perror("fstat");
475     return -1;
476     }
477 steveg 232 if (statbuf.st_uid != getuid()) {
478 steveg 215 fprintf(stderr, "Temporary crontab no longer owned by you.\n");
479     return -1;;
480     }
481    
482     if (!S_ISREG(statbuf.st_mode)) {
483     fprintf(stderr, "The temporary crontab must remain a regular file");
484     return -1;
485     }
486    
487     if (statbuf.st_mtime == fsbuf->st_mtime) {
488     return 1; /* No change to file */
489     }
490    
491     NewCrontab = fdopen(t, "r");
492     if (!NewCrontab) {
493     perror("fdopen(): after edit");
494     return -1;
495     }
496     return 0;
497     }
498    
499     /* We can't just delete Filename, because the editor might have
500     created other temporary files in there. If there's an error, we
501     just bail, and let the user/admin deal with it.*/
502    
503 steveg 2 static void
504 steveg 215 cleanup_tmp_crontab(void)
505     {
506     DIR *dp;
507     struct dirent *ep;
508     char fname[MAX_FNAME];
509    
510     if (Directory[0] == '\0') {
511     return;
512     }
513    
514     /* Delete contents */
515     dp = opendir (Directory);
516     if (dp == NULL) {
517     perror(Directory);
518     return;
519     }
520    
521 steveg 226 while ((ep = readdir (dp))) {
522 steveg 215 if (!strcmp(ep->d_name, ".") ||
523     !strcmp(ep->d_name, "..")) {
524     continue;
525     }
526     if (snprintf(fname, MAX_FNAME, "%s/%s",
527     Directory, ep->d_name) >= MAX_FNAME) {
528     fprintf(stderr, "filename too long to delete: %s/%s",
529     Directory, ep->d_name);
530     return;
531     }
532     if (unlink(fname)) {
533     perror(ep->d_name);
534     return;
535     }
536     }
537     (void) closedir (dp);
538    
539     if (rmdir(Directory)) {
540     perror(Directory);
541     return;
542     }
543     return;
544     }
545    
546     static void
547 steveg 2 edit_cmd() {
548     char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
549     FILE *f;
550     int ch, t, x;
551 steveg 226 struct stat fsbuf;
552 steveg 2 WAIT_T waiter;
553     PID_T pid, xpid;
554 steveg 6 mode_t um;
555 jfs 303 int add_help_text = 0;
556 steveg 2
557     log_it(RealUser, Pid, "BEGIN EDIT", User);
558 steveg 22 (void) snprintf(n, MAX_FNAME, CRON_TAB(User));
559 steveg 2 if (!(f = fopen(n, "r"))) {
560     if (errno != ENOENT) {
561 jfs 437 fprintf(stderr, "%s/: fdopen: %s", n, strerror(errno));
562 steveg 2 exit(ERROR_EXIT);
563     }
564     fprintf(stderr, "no crontab for %s - using an empty one\n",
565     User);
566     if (!(f = fopen("/dev/null", "r"))) {
567     perror("/dev/null");
568     exit(ERROR_EXIT);
569     }
570 jfs 303 add_help_text = 1;
571 steveg 2 }
572    
573 steveg 6 um = umask(077);
574 steveg 215
575     if ((t=create_tmp_crontab()) < 0) {
576     fprintf(stderr, "Creation of temporary crontab file failed - aborting\n");
577     (void) umask(um);
578 steveg 2 goto fatal;
579     }
580 steveg 215
581 steveg 6 (void) umask(um);
582 steveg 215 if (!(NewCrontab = fdopen(t, "w"))) {
583 steveg 2 perror("fdopen");
584     goto fatal;
585     }
586    
587     Set_LineNum(1)
588    
589 jfs 303 if (add_help_text) {
590 jfs 449 fprintf(NewCrontab,
591     "# Edit this file to introduce tasks to be run by cron.\n"
592     "# \n"
593     "# Each task to run has to be defined through a single line\n"
594     "# indicating with different fields when the task will be run\n"
595     "# and what command to run for the task\n"
596     "# \n"
597     "# To define the time you can provide concrete values for\n"
598     "# minute (m), hour (h), day of month (dom), month (mon),\n"
599     "# and day of week (dow) or use '*' in these fields (for 'any')."
600     "# \n"
601     "# Notice that tasks will be started based on the cron's system\n"
602     "# daemon's notion of time and timezones.\n"
603     "# \n"
604     "# Output of the crontab jobs (including errors) is sent through\n"
605     "# email to the user the crontab file belongs to (unless redirected).\n"
606     "# \n"
607     "# For example, you can run a backup of all your user accounts\n"
608     "# at 5 a.m every week with:\n"
609     "# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/\n"
610     "# \n"
611     "# For more information see the manual pages of crontab(5) and cron(8)\n"
612     "# \n"
613     "# m h dom mon dow command\n" );
614 jfs 303 }
615    
616 steveg 2 /* ignore the top few comments since we probably put them there.
617     */
618     for (x = 0; x < NHEADER_LINES; x++) {
619     ch = get_char(f);
620     if (EOF == ch)
621     break;
622     if ('#' != ch) {
623     putc(ch, NewCrontab);
624     break;
625     }
626     while (EOF != (ch = get_char(f)))
627     if (ch == '\n')
628     break;
629     if (EOF == ch)
630     break;
631     }
632    
633     /* copy the rest of the crontab (if any) to the temp file.
634     */
635     if (EOF != ch)
636     while (EOF != (ch = get_char(f)))
637     putc(ch, NewCrontab);
638     fclose(f);
639 steveg 215
640 steveg 2 if (ferror(NewCrontab)) {
641     fprintf(stderr, "%s: error while writing new crontab to %s\n",
642     ProgramName, Filename);
643     }
644 steveg 215
645     if (fstat(t, &fsbuf) < 0) {
646     perror("unable to stat temp file");
647 steveg 2 goto fatal;
648     }
649 steveg 136
650 steveg 2
651 steveg 215
652     /* Okay, edit the file */
653    
654 steveg 220 if ((!((editor = getenv("VISUAL")) && strlen(editor)))
655     && (!((editor = getenv("EDITOR")) && strlen(editor)))
656 steveg 2 ) {
657     editor = EDITOR;
658     }
659    
660    
661 jfs 419 /* Close before cleanup_tmp_crontab is called or otherwise
662     * (on NFS mounted /) will get renamed on unlink */
663 steveg 215 if (fclose(NewCrontab) != 0) {
664     perror(Filename);
665     goto fatal;
666     }
667    
668 steveg 232 again: /* Loop point for retrying edit after error */
669    
670 steveg 62 /* Turn off signals. */
671     (void)signal(SIGHUP, SIG_IGN);
672     (void)signal(SIGINT, SIG_IGN);
673     (void)signal(SIGQUIT, SIG_IGN);
674 steveg 232
675     /* Give up privileges while editing */
676     swap_uids();
677    
678 steveg 2 switch (pid = fork()) {
679     case -1:
680     perror("fork");
681     goto fatal;
682     case 0:
683     /* child */
684 steveg 232 if (setgid(getgid()) < 0) {
685     perror("setgid(getgid())");
686     exit(ERROR_EXIT);
687     }
688     if (setuid(getuid()) < 0) {
689     perror("setuid(getuid())");
690     exit(ERROR_EXIT);
691     }
692 steveg 2 if (chdir("/tmp") < 0) {
693     perror("chdir(/tmp)");
694     exit(ERROR_EXIT);
695     }
696     if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR) {
697     fprintf(stderr, "%s: editor or filename too long\n",
698     ProgramName);
699     exit(ERROR_EXIT);
700     }
701 steveg 22 snprintf(q, MAX_TEMPSTR, "%s %s", editor, Filename);
702 steveg 2 execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, NULL);
703     perror(editor);
704     exit(ERROR_EXIT);
705     /*NOTREACHED*/
706     default:
707     /* parent */
708     break;
709     }
710    
711     /* parent */
712 steveg 62 while (1) {
713     xpid = waitpid(pid, &waiter, WUNTRACED);
714     if (xpid == -1) {
715     fprintf(stderr, "%s: waitpid() failed waiting for PID %d from \"%s\": %s\n",
716     ProgramName, pid, editor, strerror(errno));
717     } else if (xpid != pid) {
718     fprintf(stderr, "%s: wrong PID (%d != %d) from \"%s\"\n",
719     ProgramName, xpid, pid, editor);
720     goto fatal;
721     } else if (WIFSTOPPED(waiter)) {
722 steveg 78 /* raise(WSTOPSIG(waiter)); Not needed and breaks in job control shell*/
723 steveg 62 } else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
724     fprintf(stderr, "%s: \"%s\" exited with status %d\n",
725     ProgramName, editor, WEXITSTATUS(waiter));
726     goto fatal;
727     } else if (WIFSIGNALED(waiter)) {
728     fprintf(stderr,
729     "%s: \"%s\" killed; signal %d (%score dumped)\n",
730     ProgramName, editor, WTERMSIG(waiter),
731     WCOREDUMP(waiter) ?"" :"no ");
732     goto fatal;
733     } else
734     break;
735 steveg 2 }
736 steveg 62 (void)signal(SIGHUP, SIG_DFL);
737     (void)signal(SIGINT, SIG_DFL);
738     (void)signal(SIGQUIT, SIG_DFL);
739 steveg 78 (void)signal(SIGTSTP, SIG_DFL);
740 steveg 215
741 steveg 232 /* Need privs again */
742     swap_uids_back();
743    
744 steveg 215 switch (open_tmp_crontab(&fsbuf)) {
745     case -1:
746     fprintf(stderr, "Error while editing crontab\n");
747     goto fatal;
748     case 1:
749     fprintf(stderr, "No modification made\n");
750     goto remove;
751     case 0:
752     break;
753     default:
754     fprintf(stderr,
755 jfs 343 "cron@packages.debian.org fscked up. Send him a nasty note\n");
756 steveg 215 break;
757     }
758    
759 steveg 2 fprintf(stderr, "%s: installing new crontab\n", ProgramName);
760     switch (replace_cmd()) {
761     case 0:
762     break;
763     case -1:
764     for (;;) {
765 chrisk-guest 474 printf("Do you want to retry the same edit? (y/n) ");
766 steveg 2 fflush(stdout);
767     q[0] = '\0';
768     (void) fgets(q, sizeof q, stdin);
769     switch (islower(q[0]) ? q[0] : tolower(q[0])) {
770     case 'y':
771     goto again;
772     case 'n':
773     goto abandon;
774     default:
775     fprintf(stderr, "Enter Y or N\n");
776     }
777     }
778     /*NOTREACHED*/
779     case -2:
780     abandon:
781     fprintf(stderr, "%s: edits left in %s\n",
782     ProgramName, Filename);
783     goto done;
784     default:
785 steveg 6 fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n",
786     ProgramName);
787 steveg 2 goto fatal;
788     }
789 steveg 215
790 jfs 419 if (fclose(NewCrontab) != 0) {
791     perror(Filename);
792     }
793    
794 steveg 2 remove:
795 steveg 215 cleanup_tmp_crontab();
796 steveg 2 done:
797     log_it(RealUser, Pid, "END EDIT", User);
798 steveg 215 return;
799     fatal:
800     cleanup_tmp_crontab();
801     unlink(Filename);
802     exit(ERROR_EXIT);
803 steveg 2 }
804    
805 steveg 121 static char tn[MAX_FNAME];
806    
807     static void sig_handler(int x)
808     {
809     unlink(tn);
810     exit(1);
811     }
812    
813 steveg 2 /* returns 0 on success
814     * -1 on syntax error
815     * -2 on install error
816     */
817     static int
818     replace_cmd() {
819 steveg 121 char n[MAX_FNAME], envstr[MAX_ENVSTR];
820 steveg 2 FILE *tmp;
821 steveg 121 int ch, eof, fd;
822 chrisk-guest 475 int nl = FALSE;
823 steveg 2 entry *e;
824     time_t now = time(NULL);
825     char **envp = env_init();
826 steveg 121 mode_t um;
827 steveg 2
828 steveg 6 if (envp == NULL) {
829     fprintf(stderr, "%s: Cannot allocate memory.\n", ProgramName);
830     return (-2);
831     }
832    
833 steveg 121
834     /* Assumes Linux-style signal handlers (takes int, returns void) */
835     /* Signal handlers, to ensure we do not leave temp files in the
836     spool dir. We don't remove these on exiting this function;
837     but that's OK, we exit immediately afterwards anyway. */
838     signal(SIGHUP, sig_handler);
839     signal(SIGINT, sig_handler);
840     signal(SIGQUIT, sig_handler);
841     signal(SIGTSTP, SIG_IGN);
842    
843     (void) snprintf(tn, MAX_FNAME, CRON_TAB("tmp.XXXXXX"));
844     um = umask(077);
845     fd = mkstemp(tn);
846 steveg 232 if (fd < 0) {
847 jfs 437 fprintf(stderr, "%s/: mkstemp: %s\n", CRONDIR, strerror(errno));
848 steveg 121 return(-2);
849     }
850     tmp = fdopen(fd, "w+");
851     if (!tmp) {
852 jfs 437 fprintf(stderr, "%s/: fdopen: %s\n", CRONDIR, strerror(errno));
853 steveg 2 return (-2);
854     }
855 steveg 121 (void) umask(um);
856 steveg 2
857     /* write a signature at the top of the file.
858     *
859     * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code.
860     */
861     fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n");
862     fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
863     fprintf(tmp, "# (Cron version -- %s)\n", rcsid);
864    
865     /* copy the crontab to the tmp
866     */
867     rewind(NewCrontab);
868     Set_LineNum(1)
869     while (EOF != (ch = get_char(NewCrontab)))
870     putc(ch, tmp);
871    
872 steveg 183 if (ferror(tmp) || fflush(tmp) || fsync(fd)) {
873 jfs 437 fprintf(stderr, "%s: %s: %s\n",
874     ProgramName, tn, strerror(errno));
875 steveg 2 fclose(tmp); unlink(tn);
876     return (-2);
877     }
878    
879     /* check the syntax of the file being installed.
880     */
881 steveg 183 rewind(tmp);
882 steveg 2 /* BUG: was reporting errors after the EOF if there were any errors
883     * in the file proper -- kludged it by stopping after first error.
884     * vix 31mar87
885     */
886     Set_LineNum(1 - NHEADER_LINES)
887     CheckErrorCount = 0; eof = FALSE;
888     while (!CheckErrorCount && !eof) {
889     switch (load_env(envstr, tmp)) {
890     case ERR:
891     eof = TRUE;
892 chrisk-guest 475 if (envstr[0] == '\0')
893     nl = TRUE;
894 steveg 2 break;
895     case FALSE:
896     e = load_entry(tmp, check_error, pw, envp);
897     if (e)
898     free(e);
899     break;
900     case TRUE:
901     break;
902     }
903     }
904    
905 chrisk-guest 486 if (CheckErrorCount != 0) {
906     fprintf(stderr, "errors in crontab file, can't install.\n");
907 chrisk-guest 475 fclose(tmp); unlink(tn);
908     return (-1);
909     }
910    
911 chrisk-guest 486 if (nl == FALSE) {
912     fprintf(stderr, "new crontab file is missing newline before "
913     "EOF, can't install.\n");
914 steveg 2 fclose(tmp); unlink(tn);
915     return (-1);
916     }
917    
918    
919     #ifdef HAS_FCHMOD
920     if (fchmod(fileno(tmp), 0600) < OK)
921     #else
922     if (chmod(tn, 0600) < OK)
923     #endif
924     {
925 steveg 232 perror("chmod");
926 steveg 2 fclose(tmp); unlink(tn);
927     return (-2);
928     }
929    
930 steveg 232
931 steveg 2 if (fclose(tmp) == EOF) {
932     perror("fclose");
933     unlink(tn);
934     return (-2);
935     }
936    
937 steveg 232 /* Root on behalf of another user must set file owner to that user */
938     if (getuid() == ROOT_UID && strcmp(User, RealUser) != 0) {
939     if (chown(tn, pw->pw_uid, -1) != 0) {
940     perror("chown");
941     unlink(tn);
942     return -2;
943     }
944     }
945    
946 steveg 22 (void) snprintf(n, sizeof(n), CRON_TAB(User));
947 steveg 2 if (rename(tn, n)) {
948 jfs 437 fprintf(stderr, "%s: %s: rename: %s\n",
949     ProgramName, n, strerror(errno));
950 steveg 2 unlink(tn);
951     return (-2);
952     }
953 steveg 232
954    
955 steveg 2 log_it(RealUser, Pid, "REPLACE", User);
956    
957     poke_daemon();
958    
959     return (0);
960     }
961    
962    
963     static void
964     poke_daemon() {
965     #ifdef USE_UTIMES
966     struct timeval tvs[2];
967     struct timezone tz;
968    
969     (void) gettimeofday(&tvs[0], &tz);
970     tvs[1] = tvs[0];
971     if (utimes(SPOOL_DIR, tvs) < OK) {
972 jfs 437 fprintf(stderr, "%s/: utimes: %s", CRONDIR, strerror(errno));
973     fputs("crontab: can't update mtime on spooldir\n", stderr);
974 steveg 2 return;
975     }
976     #else
977     if (utime(SPOOL_DIR, NULL) < OK) {
978 jfs 437 fprintf(stderr, "%s: utime: %s\n", CRONDIR, strerror(errno));
979     fputs("crontab: can't update mtime on spooldir\n", stderr);
980 steveg 2 return;
981     }
982     #endif /*USE_UTIMES*/
983     }

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC 1.1.5