summaryrefslogtreecommitdiff
path: root/miscutils/crontab.c
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2002-10-22 12:24:59 +0000
committerEric Andersen <andersen@codepoet.org>2002-10-22 12:24:59 +0000
commitf6f7bfb8e0a257137f8c2dad83ae4ed826b4e4bb (patch)
tree7ea00c66d341d324294df54238acff2c1795d72b /miscutils/crontab.c
parent44608e9693b03661fbab5e27650bb040c6871d11 (diff)
downloadbusybox-w32-f6f7bfb8e0a257137f8c2dad83ae4ed826b4e4bb.tar.gz
busybox-w32-f6f7bfb8e0a257137f8c2dad83ae4ed826b4e4bb.tar.bz2
busybox-w32-f6f7bfb8e0a257137f8c2dad83ae4ed826b4e4bb.zip
last_patch63 from vodz: add in crond and crontab applets
Diffstat (limited to 'miscutils/crontab.c')
-rw-r--r--miscutils/crontab.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/miscutils/crontab.c b/miscutils/crontab.c
new file mode 100644
index 000000000..c86990653
--- /dev/null
+++ b/miscutils/crontab.c
@@ -0,0 +1,393 @@
1/*
2 * CRONTAB
3 *
4 * usually setuid root, -c option only works if getuid() == geteuid()
5 *
6 * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
7 * May be distributed under the GNU General Public License
8 *
9 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <stdarg.h>
16#include <string.h>
17#include <errno.h>
18#include <time.h>
19#include <dirent.h>
20#include <fcntl.h>
21#include <pwd.h>
22#include <unistd.h>
23#include <grp.h>
24#include <syslog.h>
25#include <signal.h>
26#include <getopt.h>
27#include <sys/ioctl.h>
28#include <sys/wait.h>
29#include <sys/stat.h>
30#include <sys/resource.h>
31
32#ifndef CRONTABS
33#define CRONTABS "/var/spool/cron/crontabs"
34#endif
35#ifndef TMPDIR
36#define TMPDIR "/var/spool/cron"
37#endif
38#ifndef CRONUPDATE
39#define CRONUPDATE "cron.update"
40#endif
41#ifndef PATH_VI
42#define PATH_VI "/usr/bin/vi" /* location of vi */
43#endif
44
45#include "busybox.h"
46
47static const char *CDir = CRONTABS;
48
49static void EditFile(const char *user, const char *file);
50static int GetReplaceStream(const char *user, const char *file);
51static int ChangeUser(const char *user, short dochdir);
52
53int
54crontab_main(int ac, char **av)
55{
56 enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
57 const struct passwd *pas;
58 const char *repFile = NULL;
59 int repFd = 0;
60 int i;
61 char caller[256]; /* user that ran program */
62 int UserId;
63
64 UserId = getuid();
65 if ((pas = getpwuid(UserId)) == NULL)
66 perror_msg_and_die("getpwuid");
67
68 strncpy(caller, pas->pw_name, sizeof(caller));
69
70 i = 1;
71 if (ac > 1) {
72 if (av[1][0] == '-' && av[1][1] == 0) {
73 option = REPLACE;
74 ++i;
75 } else if (av[1][0] != '-') {
76 option = REPLACE;
77 ++i;
78 repFile = av[1];
79 }
80 }
81
82 for (; i < ac; ++i) {
83 char *ptr = av[i];
84
85 if (*ptr != '-')
86 break;
87 ptr += 2;
88
89 switch(ptr[-1]) {
90 case 'l':
91 if (ptr[-1] == 'l')
92 option = LIST;
93 /* fall through */
94 case 'e':
95 if (ptr[-1] == 'e')
96 option = EDIT;
97 /* fall through */
98 case 'd':
99 if (ptr[-1] == 'd')
100 option = DELETE;
101 /* fall through */
102 case 'u':
103 if (i + 1 < ac && av[i+1][0] != '-') {
104 ++i;
105 if (getuid() == geteuid()) {
106 pas = getpwnam(av[i]);
107 if (pas) {
108 UserId = pas->pw_uid;
109 } else {
110 error_msg_and_die("user %s unknown", av[i]);
111 }
112 } else {
113 error_msg_and_die("only the superuser may specify a user");
114 }
115 }
116 break;
117 case 'c':
118 if (getuid() == geteuid()) {
119 CDir = (*ptr) ? ptr : av[++i];
120 } else {
121 error_msg_and_die("-c option: superuser only");
122 }
123 break;
124 default:
125 i = ac;
126 break;
127 }
128 }
129 if (i != ac || option == NONE)
130 show_usage();
131
132 /*
133 * Get password entry
134 */
135
136 if ((pas = getpwuid(UserId)) == NULL)
137 perror_msg_and_die("getpwuid");
138
139 /*
140 * If there is a replacement file, obtain a secure descriptor to it.
141 */
142
143 if (repFile) {
144 repFd = GetReplaceStream(caller, repFile);
145 if (repFd < 0)
146 error_msg_and_die("unable to read replacement file");
147 }
148
149 /*
150 * Change directory to our crontab directory
151 */
152
153 if (chdir(CDir) < 0)
154 perror_msg_and_die("cannot change dir to %s", CDir);
155
156 /*
157 * Handle options as appropriate
158 */
159
160 switch(option) {
161 case LIST:
162 {
163 FILE *fi;
164 char buf[1024];
165
166 if ((fi = fopen(pas->pw_name, "r"))) {
167 while (fgets(buf, sizeof(buf), fi) != NULL)
168 fputs(buf, stdout);
169 fclose(fi);
170 } else {
171 error_msg("no crontab for %s", pas->pw_name);
172 }
173 }
174 break;
175 case EDIT:
176 {
177 FILE *fi;
178 int fd;
179 int n;
180 char tmp[128];
181 char buf[1024];
182
183 snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid());
184 if ((fd = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) >= 0) {
185 chown(tmp, getuid(), getgid());
186 if ((fi = fopen(pas->pw_name, "r"))) {
187 while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
188 write(fd, buf, n);
189 }
190 EditFile(caller, tmp);
191 remove(tmp);
192 lseek(fd, 0L, 0);
193 repFd = fd;
194 } else {
195 error_msg_and_die("unable to create %s", tmp);
196 }
197
198 }
199 option = REPLACE;
200 /* fall through */
201 case REPLACE:
202 {
203 char buf[1024];
204 char path[1024];
205 int fd;
206 int n;
207
208 snprintf(path, sizeof(path), "%s.new", pas->pw_name);
209 if ((fd = open(path, O_CREAT|O_TRUNC|O_EXCL|O_APPEND|O_WRONLY, 0600)) >= 0) {
210 while ((n = read(repFd, buf, sizeof(buf))) > 0) {
211 write(fd, buf, n);
212 }
213 close(fd);
214 rename(path, pas->pw_name);
215 } else {
216 error_msg("unable to create %s/%s", CDir, buf);
217 }
218 close(repFd);
219 }
220 break;
221 case DELETE:
222 remove(pas->pw_name);
223 break;
224 case NONE:
225 default:
226 break;
227 }
228
229 /*
230 * Bump notification file. Handle window where crond picks file up
231 * before we can write our entry out.
232 */
233
234 if (option == REPLACE || option == DELETE) {
235 FILE *fo;
236 struct stat st;
237
238 while ((fo = fopen(CRONUPDATE, "a"))) {
239 fprintf(fo, "%s\n", pas->pw_name);
240 fflush(fo);
241 if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
242 fclose(fo);
243 break;
244 }
245 fclose(fo);
246 /* loop */
247 }
248 if (fo == NULL) {
249 error_msg("unable to append to %s/%s", CDir, CRONUPDATE);
250 }
251 }
252 return 0;
253}
254
255static int
256GetReplaceStream(const char *user, const char *file)
257{
258 int filedes[2];
259 int pid;
260 int fd;
261 int n;
262 char buf[1024];
263
264 if (pipe(filedes) < 0) {
265 perror("pipe");
266 return(-1);
267 }
268 if ((pid = fork()) < 0) {
269 perror("fork");
270 return(-1);
271 }
272 if (pid > 0) {
273 /*
274 * PARENT
275 */
276
277 close(filedes[1]);
278 if (read(filedes[0], buf, 1) != 1) {
279 close(filedes[0]);
280 filedes[0] = -1;
281 }
282 return(filedes[0]);
283 }
284
285 /*
286 * CHILD
287 */
288
289 close(filedes[0]);
290
291 if (ChangeUser(user, 0) < 0)
292 exit(0);
293
294 fd = open(file, O_RDONLY);
295 if (fd < 0) {
296 error_msg("unable to open %s", file);
297 exit(0);
298 }
299 buf[0] = 0;
300 write(filedes[1], buf, 1);
301 while ((n = read(fd, buf, sizeof(buf))) > 0) {
302 write(filedes[1], buf, n);
303 }
304 exit(0);
305}
306
307static void
308EditFile(const char *user, const char *file)
309{
310 int pid;
311
312 if ((pid = fork()) == 0) {
313 /*
314 * CHILD - change user and run editor
315 */
316 char *ptr;
317 char visual[1024];
318
319 if (ChangeUser(user, 1) < 0)
320 exit(0);
321 if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) > 256)
322 ptr = PATH_VI;
323
324 snprintf(visual, sizeof(visual), "%s %s", ptr, file);
325 execl("/bin/sh", "/bin/sh", "-c", visual, NULL);
326 perror("exec");
327 exit(0);
328 }
329 if (pid < 0) {
330 /*
331 * PARENT - failure
332 */
333 perror_msg_and_die("fork");
334 }
335 wait4(pid, NULL, 0, NULL);
336}
337
338static void
339log(const char *ctl, ...)
340{
341 va_list va;
342 char buf[1024];
343
344 va_start(va, ctl);
345 vsnprintf(buf, sizeof(buf), ctl, va);
346 syslog(LOG_NOTICE, "%s",buf );
347 va_end(va);
348}
349
350static int
351ChangeUser(const char *user, short dochdir)
352{
353 struct passwd *pas;
354
355 /*
356 * Obtain password entry and change privilages
357 */
358
359 if ((pas = getpwnam(user)) == 0) {
360 log("failed to get uid for %s", user);
361 return(-1);
362 }
363 setenv("USER", pas->pw_name, 1);
364 setenv("HOME", pas->pw_dir, 1);
365 setenv("SHELL", "/bin/sh", 1);
366
367 /*
368 * Change running state to the user in question
369 */
370
371 if (initgroups(user, pas->pw_gid) < 0) {
372 log("initgroups failed: %s %m", user);
373 return(-1);
374 }
375 if (setregid(pas->pw_gid, pas->pw_gid) < 0) {
376 log("setregid failed: %s %d", user, pas->pw_gid);
377 return(-1);
378 }
379 if (setreuid(pas->pw_uid, pas->pw_uid) < 0) {
380 log("setreuid failed: %s %d", user, pas->pw_uid);
381 return(-1);
382 }
383 if (dochdir) {
384 if (chdir(pas->pw_dir) < 0) {
385 if (chdir(TMPDIR) < 0) {
386 log("chdir failed: %s %s", user, pas->pw_dir);
387 log("chdir failed: %s " TMPDIR, user);
388 return(-1);
389 }
390 }
391 }
392 return(pas->pw_uid);
393}