diff options
author | Eric Andersen <andersen@codepoet.org> | 2002-10-22 12:24:59 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2002-10-22 12:24:59 +0000 |
commit | f6f7bfb8e0a257137f8c2dad83ae4ed826b4e4bb (patch) | |
tree | 7ea00c66d341d324294df54238acff2c1795d72b /miscutils/crontab.c | |
parent | 44608e9693b03661fbab5e27650bb040c6871d11 (diff) | |
download | busybox-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.c | 393 |
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 | |||
47 | static const char *CDir = CRONTABS; | ||
48 | |||
49 | static void EditFile(const char *user, const char *file); | ||
50 | static int GetReplaceStream(const char *user, const char *file); | ||
51 | static int ChangeUser(const char *user, short dochdir); | ||
52 | |||
53 | int | ||
54 | crontab_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 | |||
255 | static int | ||
256 | GetReplaceStream(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 | |||
307 | static void | ||
308 | EditFile(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 | |||
338 | static void | ||
339 | log(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 | |||
350 | static int | ||
351 | ChangeUser(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 | } | ||