This source file includes following definitions.
- die
- main
- parse_args
- list_cmd
- delete_cmd
- check_error
- tmp_path
- host_specific_filename
- edit_cmd
- replace_cmd
- hostset_cmd
- hostget_cmd
- poke_daemon
- die
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 #include "config.h"
33
34 #define MAIN_PROGRAM
35
36 #include <errno.h>
37 #include <locale.h>
38 #include <pwd.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/wait.h>
46 #include <unistd.h>
47 #include <utime.h>
48
49 #ifdef WITH_PAM
50 # include <security/pam_appl.h>
51 #endif
52
53 #ifdef WITH_SELINUX
54 # include <selinux/selinux.h>
55 # include <selinux/context.h>
56 #endif
57
58 #include "cronie_common.h"
59 #include "bitstring.h"
60 #include "externs.h"
61 #include "funcs.h"
62 #include "globals.h"
63 #include "macros.h"
64 #include "pathnames.h"
65 #include "structs.h"
66
67 #define NHEADER_LINES 0
68
69 enum opt_t {opt_unknown, opt_list, opt_delete, opt_edit, opt_replace, opt_hostset, opt_hostget};
70
71 #if DEBUGGING
72 static const char *Options[] = {"???", "list", "delete", "edit", "replace", "hostset", "hostget"};
73
74 # ifdef WITH_SELINUX
75 static const char *getoptargs = "u:lerisncx:V";
76 # else
77 static const char *getoptargs = "u:lerincx:V";
78 # endif
79 #else
80 # ifdef WITH_SELINUX
81 static const char *getoptargs = "u:lerisncV";
82 # else
83 static const char *getoptargs = "u:lerincV";
84 # endif
85 #endif
86 #ifdef WITH_SELINUX
87 static char *selinux_context = 0;
88 #endif
89
90 static PID_T Pid;
91 static char User[MAX_UNAME], RealUser[MAX_UNAME];
92 static char Filename[MAX_FNAME], TempFilename[MAX_FNAME];
93 static char Host[MAXHOSTNAMELEN];
94 static FILE *NewCrontab;
95 static int CheckErrorCount;
96 static int PromptOnDelete;
97 static int HostSpecified;
98 static enum opt_t Option;
99 static struct passwd *pw;
100 static void list_cmd(void),
101 delete_cmd(void),
102 edit_cmd(void),
103 poke_daemon(void),
104 check_error(const char *), parse_args(int c, char *v[]), die(int) ATTRIBUTE_NORETURN;
105 static int replace_cmd(void), hostset_cmd(void), hostget_cmd(void);
106 static char *host_specific_filename(const char *prefix, const char *suffix);
107 static const char *tmp_path(void);
108
109 static void usage(const char *msg) ATTRIBUTE_NORETURN;
110 static void usage(const char *msg) {
111 fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg);
112 fprintf(stderr, "Usage:\n");
113 fprintf(stderr, " %s [options] file\n", ProgramName);
114 fprintf(stderr, " %s [options]\n", ProgramName);
115 fprintf(stderr, " %s -n [hostname]\n", ProgramName);
116 fprintf(stderr, "\n");
117 fprintf(stderr, "Options:\n");
118 fprintf(stderr, " -u <user> define user\n");
119 fprintf(stderr, " -e edit user's crontab\n");
120 fprintf(stderr, " -l list user's crontab\n");
121 fprintf(stderr, " -r delete user's crontab\n");
122 fprintf(stderr, " -i prompt before deleting\n");
123 fprintf(stderr, " -n <host> set host in cluster to run users' crontabs\n");
124 fprintf(stderr, " -c get host in cluster to run users' crontabs\n");
125 #ifdef WITH_SELINUX
126 fprintf(stderr, " -s selinux context\n");
127 #endif
128 fprintf(stderr, " -V print version and exit\n");
129 #ifdef DEBUGGING
130 fprintf(stderr, " -x <mask> enable debugging\n");
131 #endif
132 fprintf(stderr, "\nDefault operation is replace, per 1003.2\n");
133 exit(ERROR_EXIT);
134 }
135
136 int main(int argc, char *argv[]) {
137 int exitstatus;
138 char n[] = "-";
139 char *nargv[] = { argv[0], n, NULL };
140
141 if ((ProgramName=strrchr(argv[0], '/')) == NULL) {
142 ProgramName = argv[0];
143 }
144 else {
145 ++ProgramName;
146 }
147
148 Pid = getpid();
149 MailCmd[0] = '\0';
150 cron_default_mail_charset[0] = '\0';
151
152 setlocale(LC_ALL, "");
153
154 #if defined(BSD)
155 setlinebuf(stderr);
156 #endif
157
158 if (argv[1] == NULL) {
159 argv = nargv;
160 }
161 parse_args(argc, argv);
162 check_spool_dir();
163 if (!allowed(RealUser, CRON_ALLOW, CRON_DENY)) {
164 fprintf(stderr,
165 "You (%s) are not allowed to use this program (%s)\n",
166 User, ProgramName);
167 fprintf(stderr, "See crontab(1) for more information\n");
168 log_it(RealUser, Pid, "AUTH", "crontab command not allowed", 0);
169 exit(ERROR_EXIT);
170 }
171
172 #if defined(WITH_PAM)
173 if (getuid() != 0 && cron_start_pam(pw) != PAM_SUCCESS) {
174 fprintf(stderr,
175 "You (%s) are not allowed to access to (%s) because of pam configuration.\n",
176 User, ProgramName);
177 exit(ERROR_EXIT);
178 };
179 #endif
180
181 exitstatus = OK_EXIT;
182 switch (Option) {
183 case opt_unknown:
184 exitstatus = ERROR_EXIT;
185 break;
186 case opt_list:
187 list_cmd();
188 break;
189 case opt_delete:
190 delete_cmd();
191 break;
192 case opt_edit:
193 edit_cmd();
194 break;
195 case opt_replace:
196 if (replace_cmd() < 0)
197 exitstatus = ERROR_EXIT;
198 break;
199 case opt_hostset:
200 if (hostset_cmd() < 0)
201 exitstatus = ERROR_EXIT;
202 break;
203 case opt_hostget:
204 if (hostget_cmd() < 0)
205 exitstatus = ERROR_EXIT;
206 break;
207 default:
208 abort();
209 }
210 #ifdef WITH_PAM
211 cron_close_pam();
212 #endif
213 exit(exitstatus);
214 }
215
216 static void parse_args(int argc, char *argv[]) {
217 int argch;
218
219 if (!(pw = getpwuid(getuid()))) {
220 fprintf(stderr, "%s: your UID isn't in the passwd file.\n",
221 ProgramName);
222 fprintf(stderr, "bailing out.\n");
223 exit(ERROR_EXIT);
224 }
225 if (strlen(pw->pw_name) >= sizeof User) {
226 fprintf(stderr, "username too long\n");
227 exit(ERROR_EXIT);
228 }
229 strcpy(User, pw->pw_name);
230 strcpy(RealUser, User);
231 Filename[0] = '\0';
232 Option = opt_unknown;
233 PromptOnDelete = 0;
234 HostSpecified = 0;
235 while (-1 != (argch = getopt(argc, argv, getoptargs))) {
236 switch (argch) {
237 #if DEBUGGING
238 case 'x':
239 if (!set_debug_flags(optarg))
240 usage("bad debug option");
241 break;
242 #endif
243 case 'u':
244 if (MY_UID(pw) != ROOT_UID) {
245 fprintf(stderr, "must be privileged to use -u\n");
246 exit(ERROR_EXIT);
247 }
248 #ifdef WITH_SELINUX
249 if (crontab_security_access() != 0) {
250 fprintf(stderr,
251 "Access denied by SELinux, must be privileged to use -u\n");
252 exit(ERROR_EXIT);
253 }
254 #endif
255 if (Option == opt_hostset || Option == opt_hostget) {
256 fprintf(stderr,
257 "cannot use -u with -n or -c\n");
258 exit(ERROR_EXIT);
259 }
260
261 if (!(pw = getpwnam(optarg))) {
262 fprintf(stderr, "%s: user `%s' unknown\n",
263 ProgramName, optarg);
264 exit(ERROR_EXIT);
265 }
266 if (strlen(optarg) >= sizeof User)
267 usage("username too long");
268 (void) strcpy(User, optarg);
269 break;
270 case 'l':
271 if (Option != opt_unknown)
272 usage("only one operation permitted");
273 Option = opt_list;
274 break;
275 case 'r':
276 if (Option != opt_unknown)
277 usage("only one operation permitted");
278 Option = opt_delete;
279 break;
280 case 'e':
281 if (Option != opt_unknown)
282 usage("only one operation permitted");
283 Option = opt_edit;
284 break;
285 case 'i':
286 PromptOnDelete = 1;
287 break;
288 #ifdef WITH_SELINUX
289 case 's':
290 if (getprevcon((security_context_t *) & (selinux_context))) {
291 fprintf(stderr, "Cannot obtain SELinux process context\n");
292 exit(ERROR_EXIT);
293 }
294 break;
295 #endif
296 case 'n':
297 if (MY_UID(pw) != ROOT_UID) {
298 fprintf(stderr,
299 "must be privileged to set host with -n\n");
300 exit(ERROR_EXIT);
301 }
302 if (Option != opt_unknown)
303 usage("only one operation permitted");
304 if (strcmp(User, RealUser) != 0) {
305 fprintf(stderr,
306 "cannot use -u with -n or -c\n");
307 exit(ERROR_EXIT);
308 }
309 Option = opt_hostset;
310 break;
311 case 'c':
312 if (Option != opt_unknown)
313 usage("only one operation permitted");
314 if (strcmp(User, RealUser) != 0) {
315 fprintf(stderr,
316 "cannot use -u with -n or -c\n");
317 exit(ERROR_EXIT);
318 }
319 Option = opt_hostget;
320 break;
321 case 'V':
322 puts(PACKAGE_STRING);
323 exit(EXIT_SUCCESS);
324 default:
325 usage("unrecognized option");
326 }
327 }
328
329 endpwent();
330
331 if (Option == opt_hostset && argv[optind] != NULL) {
332 HostSpecified = 1;
333 if (strlen(argv[optind]) >= sizeof Host)
334 usage("hostname too long");
335 (void) strcpy(Host, argv[optind]);
336 optind++;
337 }
338
339 if (Option != opt_unknown) {
340 if (argv[optind] != NULL)
341 usage("no arguments permitted after this option");
342 }
343 else {
344 if (argv[optind] != NULL) {
345 Option = opt_replace;
346 if (strlen(argv[optind]) >= sizeof Filename)
347 usage("filename too long");
348 (void) strcpy(Filename, argv[optind]);
349 }
350 else
351 usage("file name must be specified for replace");
352 }
353
354 if (Option == opt_replace) {
355 if (!strcmp(Filename, "-"))
356 NewCrontab = stdin;
357 else {
358
359
360
361
362
363
364
365 struct stat sb;
366
367 if (swap_uids() < OK) {
368 perror("swapping uids");
369 exit(ERROR_EXIT);
370 }
371 if (!(NewCrontab = fopen(Filename, "r"))) {
372 perror(Filename);
373 exit(ERROR_EXIT);
374 }
375 if (fstat(fileno(NewCrontab), &sb) < 0) {
376 perror(Filename);
377 exit(ERROR_EXIT);
378 }
379 if ((sb.st_mode & S_IFMT) == S_IFDIR) {
380 fprintf(stderr,
381 "cannot replace crontab with a directory: %s\n",
382 Filename);
383 fclose(NewCrontab);
384 exit(ERROR_EXIT);
385 }
386 if (swap_uids_back() < OK) {
387 perror("swapping uids back");
388 exit(ERROR_EXIT);
389 }
390 }
391 }
392
393 Debug(DMISC, ("user=%s, file=%s, option=%s\n",
394 User, Filename, Options[(int) Option]));
395 }
396
397 static void list_cmd(void) {
398 char n[MAX_FNAME];
399 FILE *f;
400 int ch;
401
402 log_it(RealUser, Pid, "LIST", User, 0);
403 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
404 fprintf(stderr, "path too long\n");
405 exit(ERROR_EXIT);
406 }
407 if (!(f = fopen(n, "r"))) {
408 if (errno == ENOENT)
409 fprintf(stderr, "no crontab for %s\n", User);
410 else
411 perror(n);
412 exit(ERROR_EXIT);
413 }
414
415
416
417 Set_LineNum(1)
418 while (EOF != (ch = get_char(f)))
419 putchar(ch);
420 fclose(f);
421 }
422
423 static void delete_cmd(void) {
424 char n[MAX_FNAME] = "";
425 if (PromptOnDelete == 1) {
426 printf("crontab: really delete %s's crontab? ", User);
427 fflush(stdout);
428 if ((fgets(n, MAX_FNAME - 1, stdin) == 0L)
429 || ((n[0] != 'Y') && (n[0] != 'y'))
430 )
431 exit(0);
432 }
433
434 log_it(RealUser, Pid, "DELETE", User, 0);
435 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
436 fprintf(stderr, "path too long\n");
437 exit(ERROR_EXIT);
438 }
439 if (unlink(n) != 0) {
440 if (errno == ENOENT)
441 fprintf(stderr, "no crontab for %s\n", User);
442 else
443 perror(n);
444 exit(ERROR_EXIT);
445 }
446 poke_daemon();
447 }
448
449 static void check_error(const char *msg) {
450 CheckErrorCount++;
451 fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber - 1, msg);
452 }
453
454 static const char *tmp_path(void) {
455 const char *tmpdir = NULL;
456
457 if ((getuid() == geteuid()) && (getgid() == getegid())) {
458 tmpdir = getenv("TMPDIR");
459 }
460 return tmpdir ? tmpdir : "/tmp";
461 }
462
463 static char *host_specific_filename(const char *prefix, const char *suffix)
464 {
465
466
467
468
469
470
471 static char safename[MAX_FNAME];
472 char hostname[MAX_FNAME];
473
474 if (gethostname(hostname, sizeof hostname) != 0)
475 return NULL;
476
477 if (prefix) {
478 if (!glue_strings(safename, sizeof safename, prefix, hostname, '.'))
479 return NULL;
480 strcpy(hostname, safename);
481 }
482 if (suffix) {
483 if (!glue_strings(safename, sizeof safename, hostname, suffix, '.'))
484 return NULL;
485 }
486
487 return safename;
488 }
489
490 static void edit_cmd(void) {
491 char n[MAX_FNAME], q[MAX_TEMPSTR];
492 const char *editor;
493 FILE *f;
494 int ch = '\0', t;
495 struct stat statbuf;
496 struct utimbuf utimebuf;
497 WAIT_T waiter;
498 PID_T pid, xpid;
499
500 log_it(RealUser, Pid, "BEGIN EDIT", User, 0);
501 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
502 fprintf(stderr, "path too long\n");
503 exit(ERROR_EXIT);
504 }
505 if (!(f = fopen(n, "r"))) {
506 if (errno != ENOENT) {
507 perror(n);
508 exit(ERROR_EXIT);
509 }
510 fprintf(stderr, "no crontab for %s - using an empty one\n", User);
511 if (!(f = fopen(_PATH_DEVNULL, "r"))) {
512 perror(_PATH_DEVNULL);
513 exit(ERROR_EXIT);
514 }
515 }
516
517
518 (void) signal(SIGHUP, SIG_IGN);
519 (void) signal(SIGINT, SIG_IGN);
520 (void) signal(SIGQUIT, SIG_IGN);
521
522 if (!glue_strings(Filename, sizeof Filename, tmp_path(),
523 "crontab.XXXXXX", '/')) {
524 fprintf(stderr, "path too long\n");
525 exit(ERROR_EXIT);
526 }
527 if (swap_uids() == -1) {
528 perror("swapping uids");
529 exit(ERROR_EXIT);
530 }
531 if (-1 == (t = mkstemp(Filename))) {
532 perror(Filename);
533 goto fatal;
534 }
535
536 if (swap_uids_back() == -1) {
537 perror("swapping uids back");
538 goto fatal;
539 }
540 if (!(NewCrontab = fdopen(t, "r+"))) {
541 perror("fdopen");
542 goto fatal;
543 }
544
545 Set_LineNum(1)
546
547
548
549
550
551
552 if (EOF != ch)
553 while (EOF != (ch = get_char(f)))
554 putc(ch, NewCrontab);
555
556 #ifdef WITH_SELINUX
557 if (selinux_context) {
558 context_t ccon = NULL;
559 const char *level = NULL;
560
561 if (!(ccon = context_new(selinux_context))) {
562 fprintf(stderr, "context_new failed\n");
563 goto fatal;
564 }
565
566 if (!(level = context_range_get(ccon))) {
567 fprintf(stderr, "context_range failed\n");
568 goto fatal;
569 }
570
571 fprintf(NewCrontab, "MLS_LEVEL=%s\n", level);
572 context_free(ccon);
573 freecon(selinux_context);
574 selinux_context = NULL;
575 }
576 #endif
577
578 fclose(f);
579 if (fflush(NewCrontab) < OK) {
580 perror(Filename);
581 exit(ERROR_EXIT);
582 }
583 if (swap_uids() == -1) {
584 perror("swapping uids");
585 exit(ERROR_EXIT);
586 }
587
588 utimebuf.actime = 0;
589 utimebuf.modtime = 0;
590 utime(Filename, &utimebuf);
591 if (swap_uids_back() == -1) {
592 perror("swapping uids");
593 exit(ERROR_EXIT);
594 }
595 again:
596 rewind(NewCrontab);
597 if (ferror(NewCrontab)) {
598 fprintf(stderr, "%s: error while writing new crontab to %s\n",
599 ProgramName, Filename);
600 fatal:
601 unlink(Filename);
602 exit(ERROR_EXIT);
603 }
604
605 if (((editor = getenv("VISUAL")) == NULL || *editor == '\0') &&
606 ((editor = getenv("EDITOR")) == NULL || *editor == '\0')) {
607 editor = EDITOR;
608 }
609
610
611
612
613
614
615
616
617
618 switch (pid = fork()) {
619 case -1:
620 perror("fork");
621 goto fatal;
622 case 0:
623
624 if (setgid(MY_GID(pw)) < 0) {
625 perror("setgid(getgid())");
626 exit(ERROR_EXIT);
627 }
628 if (setuid(MY_UID(pw)) < 0) {
629 perror("setuid(getuid())");
630 exit(ERROR_EXIT);
631 }
632 if (!glue_strings(q, sizeof q, editor, Filename, ' ')) {
633 fprintf(stderr, "%s: editor command line too long\n", ProgramName);
634 exit(ERROR_EXIT);
635 }
636 execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, (char *) 0);
637 perror(editor);
638 exit(ERROR_EXIT);
639 default:
640
641 break;
642 }
643
644
645 for (;;) {
646 xpid = waitpid(pid, &waiter, 0);
647 if (xpid == -1) {
648 if (errno != EINTR)
649 fprintf(stderr,
650 "%s: waitpid() failed waiting for PID %ld from \"%s\": %s\n",
651 ProgramName, (long) pid, editor, strerror(errno));
652 }
653 else if (xpid != pid) {
654 fprintf(stderr, "%s: wrong PID (%ld != %ld) from \"%s\"\n",
655 ProgramName, (long) xpid, (long) pid, editor);
656 goto fatal;
657 }
658 else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
659 fprintf(stderr, "%s: \"%s\" exited with status %d\n",
660 ProgramName, editor, WEXITSTATUS(waiter));
661 goto fatal;
662 }
663 else if (WIFSIGNALED(waiter)) {
664 fprintf(stderr,
665 "%s: \"%s\" killed; signal %d (%score dumped)\n",
666 ProgramName, editor, WTERMSIG(waiter),
667 WCOREDUMP(waiter) ? "" : "no ");
668 goto fatal;
669 }
670 else
671 break;
672 }
673 (void) signal(SIGHUP, SIG_DFL);
674 (void) signal(SIGINT, SIG_DFL);
675 (void) signal(SIGQUIT, SIG_DFL);
676
677
678
679
680 if (lstat(Filename, &statbuf) < 0) {
681 perror("lstat");
682 goto fatal;
683 }
684
685 if (!S_ISREG(statbuf.st_mode)) {
686 fprintf(stderr, "%s: illegal crontab\n", ProgramName);
687 goto remove;
688 }
689
690 if (statbuf.st_mtime == 0) {
691 fprintf(stderr, "%s: no changes made to crontab\n", ProgramName);
692 goto remove;
693 }
694
695 fprintf(stderr, "%s: installing new crontab\n", ProgramName);
696 fclose(NewCrontab);
697 if (swap_uids() < OK) {
698 perror("swapping uids");
699 goto remove;
700 }
701 if (!(NewCrontab = fopen(Filename, "r+"))) {
702 perror("cannot read new crontab");
703 goto remove;
704 }
705 if (swap_uids_back() < OK) {
706 perror("swapping uids back");
707 exit(ERROR_EXIT);
708 }
709 if (NewCrontab == 0L) {
710 perror("fopen");
711 goto fatal;
712 }
713 switch (replace_cmd()) {
714 case 0:
715 break;
716 case -1:
717 for (;;) {
718 printf("Do you want to retry the same edit? ");
719 fflush(stdout);
720 q[0] = '\0';
721 if (fgets(q, sizeof q, stdin) == 0L)
722 continue;
723 switch (q[0]) {
724 case 'y':
725 case 'Y':
726 goto again;
727 case 'n':
728 case 'N':
729 goto abandon;
730 default:
731 fprintf(stderr, "Enter Y or N\n");
732 }
733 }
734 case -2:
735 abandon:
736 fprintf(stderr, "%s: edits left in %s\n", ProgramName, Filename);
737 goto done;
738 default:
739 fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n",
740 ProgramName);
741 goto fatal;
742 }
743 remove:
744 unlink(Filename);
745 done:
746 log_it(RealUser, Pid, "END EDIT", User, 0);
747 }
748
749
750
751
752
753 static int replace_cmd(void) {
754 char n[MAX_FNAME], envstr[MAX_ENVSTR];
755 FILE *tmp;
756 int ch, eof, fd;
757 int error = 0;
758 entry *e;
759 uid_t file_owner;
760 char **envp;
761 char *safename;
762
763
764 safename = host_specific_filename("#tmp", "XXXXXXXXXX");
765 if (!safename || !glue_strings(TempFilename, sizeof TempFilename, SPOOL_DIR,
766 safename, '/')) {
767 TempFilename[0] = '\0';
768 fprintf(stderr, "path too long\n");
769 return (-2);
770 }
771 if ((fd = mkstemp(TempFilename)) == -1 || !(tmp = fdopen(fd, "w+"))) {
772 perror(TempFilename);
773 if (fd != -1) {
774 close(fd);
775 unlink(TempFilename);
776 }
777 TempFilename[0] = '\0';
778 return (-2);
779 }
780
781 (void) signal(SIGHUP, die);
782 (void) signal(SIGINT, die);
783 (void) signal(SIGQUIT, die);
784
785
786
787
788
789
790
791
792
793 #ifdef WITH_SELINUX
794 if (selinux_context)
795 fprintf(tmp, "SELINUX_ROLE_TYPE=%s\n", selinux_context);
796 #endif
797
798
799
800 rewind(NewCrontab);
801 Set_LineNum(1)
802 while (EOF != (ch = get_char(NewCrontab)))
803 putc(ch, tmp);
804 if (ftruncate(fileno(tmp), ftell(tmp)) == -1) {
805 fprintf(stderr, "%s: error while writing new crontab to %s\n",
806 ProgramName, TempFilename);
807 fclose(tmp);
808 error = -2;
809 goto done;
810 }
811 fflush(tmp);
812 rewind(tmp);
813 if (ferror(tmp)) {
814 fprintf(stderr, "%s: error while writing new crontab to %s\n",
815 ProgramName, TempFilename);
816 fclose(tmp);
817 error = -2;
818 goto done;
819 }
820
821
822
823
824
825
826
827
828 Set_LineNum(1 - NHEADER_LINES)
829 CheckErrorCount = 0;
830 eof = FALSE;
831
832 envp = env_init();
833 if (envp == NULL) {
834 fprintf(stderr, "%s: Cannot allocate memory.\n", ProgramName);
835 fclose(tmp);
836 error = -2;
837 goto done;
838 }
839
840 while (!CheckErrorCount && !eof) {
841 switch (load_env(envstr, tmp)) {
842 case ERR:
843
844 if (envstr[0] != '\0') {
845 Set_LineNum(LineNumber + 1);
846 check_error("premature EOF");
847 }
848 eof = TRUE;
849 break;
850 case FALSE:
851 e = load_entry(tmp, check_error, pw, envp);
852 if (e)
853 free_entry(e);
854 break;
855 case TRUE:
856 break;
857 }
858 }
859 env_free(envp);
860
861 if (CheckErrorCount != 0) {
862 fprintf(stderr, "errors in crontab file, can't install.\n");
863 fclose(tmp);
864 error = -1;
865 goto done;
866 }
867
868 file_owner = (getgid() == geteuid() && getgid() == getegid()) ? ROOT_UID : pw->pw_uid;
869
870 #ifdef HAVE_FCHOWN
871 if (fchown(fileno(tmp), file_owner, (gid_t)-1) < OK) {
872 perror("fchown");
873 fclose(tmp);
874 error = -2;
875 goto done;
876 }
877 #else
878 if (chown(TempFilename, file_owner, (gid_t)-1) < OK) {
879 perror("chown");
880 fclose(tmp);
881 error = -2;
882 goto done;
883 }
884 #endif
885
886 if (fclose(tmp) == EOF) {
887 perror("fclose");
888 error = -2;
889 goto done;
890 }
891
892 if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) {
893 fprintf(stderr, "path too long\n");
894 error = -2;
895 goto done;
896 }
897 if (rename(TempFilename, n)) {
898 fprintf(stderr, "%s: error renaming %s to %s\n",
899 ProgramName, TempFilename, n);
900 perror("rename");
901 error = -2;
902 goto done;
903 }
904 TempFilename[0] = '\0';
905 log_it(RealUser, Pid, "REPLACE", User, 0);
906
907 poke_daemon();
908
909 done:
910 (void) signal(SIGHUP, SIG_DFL);
911 (void) signal(SIGINT, SIG_DFL);
912 (void) signal(SIGQUIT, SIG_DFL);
913 if (TempFilename[0]) {
914 (void) unlink(TempFilename);
915 TempFilename[0] = '\0';
916 }
917 return (error);
918 }
919
920 static int hostset_cmd(void) {
921 char n[MAX_FNAME];
922 FILE *tmp;
923 int fd;
924 int error = 0;
925 char *safename;
926
927 if (!HostSpecified)
928 gethostname(Host, sizeof Host);
929
930 safename = host_specific_filename("#tmp", "XXXXXXXXXX");
931 if (!safename || !glue_strings(TempFilename, sizeof TempFilename, SPOOL_DIR,
932 safename, '/')) {
933 TempFilename[0] = '\0';
934 fprintf(stderr, "path too long\n");
935 return (-2);
936 }
937 if ((fd = mkstemp(TempFilename)) == -1 || !(tmp = fdopen(fd, "w"))) {
938 perror(TempFilename);
939 if (fd != -1) {
940 close(fd);
941 unlink(TempFilename);
942 }
943 TempFilename[0] = '\0';
944 return (-2);
945 }
946
947 (void) signal(SIGHUP, die);
948 (void) signal(SIGINT, die);
949 (void) signal(SIGQUIT, die);
950
951 (void) fchmod(fd, 0600);
952
953 if (fprintf(tmp, "%s\n", Host) < 0 || fclose(tmp) == EOF) {
954 fprintf(stderr, "%s: error while writing to %s\n",
955 ProgramName, TempFilename);
956 error = -2;
957 goto done;
958 }
959
960 if (!glue_strings(n, sizeof n, SPOOL_DIR, CRON_HOSTNAME, '/')) {
961 fprintf(stderr, "path too long\n");
962 error = -2;
963 goto done;
964 }
965
966 if (rename(TempFilename, n)) {
967 fprintf(stderr, "%s: error renaming %s to %s\n",
968 ProgramName, TempFilename, n);
969 perror("rename");
970 error = -2;
971 goto done;
972 }
973 TempFilename[0] = '\0';
974 log_it(RealUser, Pid, "SET HOST", Host, 0);
975
976 poke_daemon();
977
978 done:
979 (void) signal(SIGHUP, SIG_DFL);
980 (void) signal(SIGINT, SIG_DFL);
981 (void) signal(SIGQUIT, SIG_DFL);
982 if (TempFilename[0]) {
983 (void) unlink(TempFilename);
984 TempFilename[0] = '\0';
985 }
986 return (error);
987 }
988
989 static int hostget_cmd(void) {
990 char n[MAX_FNAME];
991 FILE *f;
992
993 if (!glue_strings(n, sizeof n, SPOOL_DIR, CRON_HOSTNAME, '/')) {
994 fprintf(stderr, "path too long\n");
995 return (-2);
996 }
997
998 if (!(f = fopen(n, "r"))) {
999 if (errno == ENOENT)
1000 fprintf(stderr, "File %s not found\n", n);
1001 else
1002 perror(n);
1003 return (-2);
1004 }
1005
1006 if (get_string(Host, sizeof Host, f, "\n") == EOF) {
1007 fprintf(stderr, "Error reading from %s\n", n);
1008 fclose(f);
1009 return (-2);
1010 }
1011
1012 fclose(f);
1013
1014 printf("%s\n", Host);
1015 fflush(stdout);
1016
1017 log_it(RealUser, Pid, "GET HOST", Host, 0);
1018 return (0);
1019 }
1020
1021 static void poke_daemon(void) {
1022 if (utime(SPOOL_DIR, NULL) < OK) {
1023 fprintf(stderr, "crontab: can't update mtime on spooldir\n");
1024 perror(SPOOL_DIR);
1025 return;
1026 }
1027 }
1028
1029 static void die(int x) {
1030 if (TempFilename[0])
1031 (void) unlink(TempFilename);
1032 _exit(ERROR_EXIT);
1033 }