source: branches/stable/mindi-busybox/miscutils/crontab.c @ 1247

Last change on this file since 1247 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

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