7 * usually setuid root, -c option only works if getuid() == geteuid()
9 * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com)
10 * Copyright 2009 James Pryor <profjim@jimpryor.net>
11 * May be distributed under the GNU General Public License
16 const char *CDir
= CRONTABS
;
18 /* not used in this program, but subs.c needs */
19 short LogLevel
= LOG_NOTICE
;
20 short ForegroundOpt
= 0;
22 const char *LogFile
= LOG_FILE
;
23 char *TempDir
= TMPDIR
;
25 void EditFile(const char *user
, const char *file
);
26 int GetReplaceStream(const char *user
, const char *file
);
29 main(int ac
, char **av
)
31 enum { NONE
, EDIT
, LIST
, REPLACE
, DELETE
} option
= NONE
;
36 char caller
[256]; /* user that ran program */
39 if ((pas
= getpwuid(UserId
)) == NULL
) {
43 snprintf(caller
, sizeof(caller
), "%s", pas
->pw_name
);
47 if (av
[1][0] == '-' && av
[1][1] == 0) {
50 } else if (av
[1][0] != '-') {
59 while ((i
=getopt(ac
,av
,"ledu:c:")) != EOF
) {
71 if (*optarg
!= 0 && getuid() == geteuid()) {
72 pas
= getpwnam(optarg
);
76 errx(1, "user %s unknown", optarg
);
79 errx(1, "only the superuser may specify a user");
83 if (*optarg
!= 0 && getuid() == geteuid()) {
86 errx(1, "-c option: superuser only");
98 printf("crontab " VERSION
"\n");
99 printf("crontab file <opts> replace crontab from file\n");
100 printf("crontab - <opts> replace crontab from stdin\n");
101 printf("crontab -u user specify user\n");
102 printf("crontab -l [user] list crontab for user\n");
103 printf("crontab -e [user] edit crontab for user\n");
104 printf("crontab -d [user] delete crontab for user\n");
105 printf("crontab -c dir specify crontab directory\n");
113 if ((pas
= getpwuid(UserId
)) == NULL
) {
119 * If there is a replacement file, obtain a secure descriptor to it.
123 repFd
= GetReplaceStream(caller
, repFile
);
125 errx(1, "unable to read replacement file");
130 * Change directory to our crontab directory
133 if (chdir(CDir
) < 0) {
134 errx(1, "cannot change dir to %s: %s", CDir
, strerror(errno
));
138 * Handle options as appropriate
147 if ((fi
= fopen(pas
->pw_name
, "r"))) {
148 while (fgets(buf
, sizeof(buf
), fi
) != NULL
)
152 fprintf(stderr
, "no crontab for %s\n", pas
->pw_name
);
161 char tmp
[] = TMPDIR
"/crontab.XXXXXX";
164 if ((fd
= mkstemp(tmp
)) >= 0) {
165 chown(tmp
, getuid(), getgid());
166 if ((fi
= fopen(pas
->pw_name
, "r"))) {
167 while ((n
= fread(buf
, 1, sizeof(buf
), fi
)) > 0)
170 EditFile(caller
, tmp
);
175 errx(1, "unable to create %s", tmp
);
188 snprintf(path
, sizeof(path
), "%s.new", pas
->pw_name
);
189 if ((fd
= open(path
, O_CREAT
|O_TRUNC
|O_EXCL
|O_APPEND
|O_WRONLY
, 0600)) >= 0) {
190 while ((n
= read(repFd
, buf
, sizeof(buf
))) > 0) {
194 rename(path
, pas
->pw_name
);
196 fprintf(stderr
, "unable to create %s/%s: %s\n",
206 remove(pas
->pw_name
);
214 * Bump notification file. Handle window where crond picks file up
215 * before we can write our entry out.
218 if (option
== REPLACE
|| option
== DELETE
) {
222 while ((fo
= fopen(CRONUPDATE
, "a"))) {
223 fprintf(fo
, "%s\n", pas
->pw_name
);
225 if (fstat(fileno(fo
), &st
) != 0 || st
.st_nlink
!= 0) {
233 fprintf(stderr
, "unable to append to %s/%s\n", CDir
, CRONUPDATE
);
236 (volatile void)exit(0);
241 GetReplaceStream(const char *user
, const char *file
)
249 if (pipe(filedes
) < 0) {
253 if ((pid
= fork()) < 0) {
263 if (read(filedes
[0], buf
, 1) != 1) {
276 if (ChangeUser(user
, 0) < 0)
279 fd
= open(file
, O_RDONLY
);
281 errx(0, "unable to open %s", file
);
283 write(filedes
[1], buf
, 1);
284 while ((n
= read(fd
, buf
, sizeof(buf
))) > 0) {
285 write(filedes
[1], buf
, n
);
291 EditFile(const char *user
, const char *file
)
295 if ((pid
= fork()) == 0) {
297 * CHILD - change user and run editor
302 if (ChangeUser(user
, 1) < 0)
304 if ((ptr
= getenv("EDITOR")) == NULL
|| strlen(ptr
) > 256)
305 if ((ptr
= getenv("VISUAL")) == NULL
|| strlen(ptr
) > 256)
308 snprintf(visual
, sizeof(visual
), "%s %s", ptr
, file
);
309 execl("/bin/sh", "/bin/sh", "-c", visual
, NULL
);
320 wait4(pid
, NULL
, 0, NULL
);