Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/miscutils/crontab.c

    r1765 r2725  
    88 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
    99 *
    10  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     10 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    1111 */
    1212
    1313#include "libbb.h"
    1414
    15 #ifndef CRONTABS
    16 #define CRONTABS        "/var/spool/cron/crontabs"
    17 #endif
    18 #ifndef TMPDIR
    19 #define TMPDIR          "/var/spool/cron"
    20 #endif
     15#define CRONTABS        CONFIG_FEATURE_CROND_DIR "/crontabs"
    2116#ifndef CRONUPDATE
    2217#define CRONUPDATE      "cron.update"
    2318#endif
    24 #ifndef PATH_VI
    25 #define PATH_VI         "/bin/vi"   /* location of vi */
    26 #endif
    27 
    28 static const char *CDir = CRONTABS;
    29 
    30 static void EditFile(const char *user, const char *file);
    31 static int GetReplaceStream(const char *user, const char *file);
    32 static int ChangeUser(const char *user, short dochdir);
    33 
    34 int crontab_main(int ac, char **av);
    35 int crontab_main(int ac, char **av)
     19
     20static void edit_file(const struct passwd *pas, const char *file)
    3621{
    37     enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
     22    const char *ptr;
     23    int pid = xvfork();
     24
     25    if (pid) { /* parent */
     26        wait4pid(pid);
     27        return;
     28    }
     29
     30    /* CHILD - change user and run editor */
     31    /* initgroups, setgid, setuid */
     32    change_identity(pas);
     33    setup_environment(DEFAULT_SHELL,
     34            SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP,
     35            pas);
     36    ptr = getenv("VISUAL");
     37    if (!ptr) {
     38        ptr = getenv("EDITOR");
     39        if (!ptr)
     40            ptr = "vi";
     41    }
     42
     43    BB_EXECLP(ptr, ptr, file, NULL);
     44    bb_perror_msg_and_die("exec %s", ptr);
     45}
     46
     47static int open_as_user(const struct passwd *pas, const char *file)
     48{
     49    pid_t pid;
     50    char c;
     51
     52    pid = xvfork();
     53    if (pid) { /* PARENT */
     54        if (wait4pid(pid) == 0) {
     55            /* exitcode 0: child says it can read */
     56            return open(file, O_RDONLY);
     57        }
     58        return -1;
     59    }
     60
     61    /* CHILD */
     62    /* initgroups, setgid, setuid */
     63    change_identity(pas);
     64    /* We just try to read one byte. If it works, file is readable
     65     * under this user. We signal that by exiting with 0. */
     66    _exit(safe_read(xopen(file, O_RDONLY), &c, 1) < 0);
     67}
     68
     69int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     70int crontab_main(int argc UNUSED_PARAM, char **argv)
     71{
    3872    const struct passwd *pas;
    39     const char *repFile = NULL;
    40     int repFd = 0;
    41     int i;
    42     char caller[256];           /* user that ran program */
    43     char buf[1024];
    44     int UserId;
    45 
    46     UserId = getuid();
    47     pas = getpwuid(UserId);
    48     if (pas == NULL)
    49         bb_perror_msg_and_die("getpwuid");
    50 
    51     safe_strncpy(caller, pas->pw_name, sizeof(caller));
    52 
    53     i = 1;
    54     if (ac > 1) {
    55         if (LONE_DASH(av[1])) {
    56             option = REPLACE;
    57             ++i;
    58         } else if (av[1][0] != '-') {
    59             option = REPLACE;
    60             ++i;
    61             repFile = av[1];
    62         }
    63     }
    64 
    65     for (; i < ac; ++i) {
    66         char *ptr = av[i];
    67 
    68         if (*ptr != '-')
     73    const char *crontab_dir = CRONTABS;
     74    char *tmp_fname;
     75    char *new_fname;
     76    char *user_name;  /* -u USER */
     77    int fd;
     78    int src_fd;
     79    int opt_ler;
     80
     81    /* file [opts]     Replace crontab from file
     82     * - [opts]        Replace crontab from stdin
     83     * -u user         User
     84     * -c dir          Crontab directory
     85     * -l              List crontab for user
     86     * -e              Edit crontab for user
     87     * -r              Delete crontab for user
     88     * bbox also supports -d == -r, but most other crontab
     89     * implementations do not. Deprecated.
     90     */
     91    enum {
     92        OPT_u = (1 << 0),
     93        OPT_c = (1 << 1),
     94        OPT_l = (1 << 2),
     95        OPT_e = (1 << 3),
     96        OPT_r = (1 << 4),
     97        OPT_ler = OPT_l + OPT_e + OPT_r,
     98    };
     99
     100    opt_complementary = "?1:dr"; /* max one argument; -d implies -r */
     101    opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir);
     102    argv += optind;
     103
     104    if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */
     105        /* Run by non-root */
     106        if (opt_ler & (OPT_u|OPT_c))
     107            bb_error_msg_and_die(bb_msg_you_must_be_root);
     108    }
     109
     110    if (opt_ler & OPT_u) {
     111        pas = xgetpwnam(user_name);
     112    } else {
     113        pas = xgetpwuid(getuid());
     114    }
     115
     116#define user_name DONT_USE_ME_BEYOND_THIS_POINT
     117
     118    /* From now on, keep only -l, -e, -r bits */
     119    opt_ler &= OPT_ler;
     120    if ((opt_ler - 1) & opt_ler) /* more than one bit set? */
     121        bb_show_usage();
     122
     123    /* Read replacement file under user's UID/GID/group vector */
     124    src_fd = STDIN_FILENO;
     125    if (!opt_ler) { /* Replace? */
     126        if (!argv[0])
     127            bb_show_usage();
     128        if (NOT_LONE_DASH(argv[0])) {
     129            src_fd = open_as_user(pas, argv[0]);
     130            if (src_fd < 0)
     131                bb_error_msg_and_die("user %s cannot read %s",
     132                        pas->pw_name, argv[0]);
     133        }
     134    }
     135
     136    /* cd to our crontab directory */
     137    xchdir(crontab_dir);
     138
     139    tmp_fname = NULL;
     140
     141    /* Handle requested operation */
     142    switch (opt_ler) {
     143
     144    default: /* case OPT_r: Delete */
     145        unlink(pas->pw_name);
     146        break;
     147
     148    case OPT_l: /* List */
     149        {
     150            char *args[2] = { pas->pw_name, NULL };
     151            return bb_cat(args);
     152            /* list exits,
     153             * the rest go play with cron update file */
     154        }
     155
     156    case OPT_e: /* Edit */
     157        tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid());
     158        /* No O_EXCL: we don't want to be stuck if earlier crontabs
     159         * were killed, leaving stale temp file behind */
     160        src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
     161        fchown(src_fd, pas->pw_uid, pas->pw_gid);
     162        fd = open(pas->pw_name, O_RDONLY);
     163        if (fd >= 0) {
     164            bb_copyfd_eof(fd, src_fd);
     165            close(fd);
     166            xlseek(src_fd, 0, SEEK_SET);
     167        }
     168        close_on_exec_on(src_fd); /* don't want editor to see this fd */
     169        edit_file(pas, tmp_fname);
     170        /* fall through */
     171
     172    case 0: /* Replace (no -l, -e, or -r were given) */
     173        new_fname = xasprintf("%s.new", pas->pw_name);
     174        fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600);
     175        if (fd >= 0) {
     176            bb_copyfd_eof(src_fd, fd);
     177            close(fd);
     178            xrename(new_fname, pas->pw_name);
     179        } else {
     180            bb_error_msg("can't create %s/%s",
     181                    crontab_dir, new_fname);
     182        }
     183        if (tmp_fname)
     184            unlink(tmp_fname);
     185        /*free(tmp_fname);*/
     186        /*free(new_fname);*/
     187
     188    } /* switch */
     189
     190    /* Bump notification file.  Handle window where crond picks file up
     191     * before we can write our entry out.
     192     */
     193    while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) {
     194        struct stat st;
     195
     196        fdprintf(fd, "%s\n", pas->pw_name);
     197        if (fstat(fd, &st) != 0 || st.st_nlink != 0) {
     198            /*close(fd);*/
    69199            break;
    70         ptr += 2;
    71 
    72         switch (ptr[-1]) {
    73         case 'l':
    74             if (ptr[-1] == 'l')
    75                 option = LIST;
    76             /* fall through */
    77         case 'e':
    78             if (ptr[-1] == 'e')
    79                 option = EDIT;
    80             /* fall through */
    81         case 'd':
    82             if (ptr[-1] == 'd')
    83                 option = DELETE;
    84             /* fall through */
    85         case 'u':
    86             if (i + 1 < ac && av[i+1][0] != '-') {
    87                 ++i;
    88                 if (getuid() == geteuid()) {
    89                     pas = getpwnam(av[i]);
    90                     if (pas) {
    91                         UserId = pas->pw_uid;
    92                     } else {
    93                         bb_error_msg_and_die("user %s unknown", av[i]);
    94                     }
    95                 } else {
    96                     bb_error_msg_and_die("only the superuser may specify a user");
    97                 }
    98             }
    99             break;
    100         case 'c':
    101             if (getuid() == geteuid()) {
    102                 CDir = (*ptr) ? ptr : av[++i];
    103             } else {
    104                 bb_error_msg_and_die("-c option: superuser only");
    105             }
    106             break;
    107         default:
    108             i = ac;
    109             break;
    110         }
    111     }
    112     if (i != ac || option == NONE)
    113         bb_show_usage();
    114 
    115     /*
    116      * Get password entry
    117      */
    118 
    119     pas = getpwuid(UserId);
    120     if (pas == NULL)
    121         bb_perror_msg_and_die("getpwuid");
    122 
    123     /*
    124      * If there is a replacement file, obtain a secure descriptor to it.
    125      */
    126 
    127     if (repFile) {
    128         repFd = GetReplaceStream(caller, repFile);
    129         if (repFd < 0)
    130             bb_error_msg_and_die("cannot read replacement file");
    131     }
    132 
    133     /*
    134      * Change directory to our crontab directory
    135      */
    136 
    137     xchdir(CDir);
    138 
    139     /*
    140      * Handle options as appropriate
    141      */
    142 
    143     switch (option) {
    144     case LIST:
    145         {
    146             FILE *fi;
    147 
    148             fi = fopen(pas->pw_name, "r");
    149             if (fi) {
    150                 while (fgets(buf, sizeof(buf), fi) != NULL)
    151                     fputs(buf, stdout);
    152                 fclose(fi);
    153             } else {
    154                 bb_error_msg("no crontab for %s", pas->pw_name);
    155             }
    156         }
    157         break;
    158     case EDIT:
    159         {
    160 /* FIXME: messy code here! we have file copying helpers for this! */
    161             FILE *fi;
    162             int fd;
    163             int n;
    164             char tmp[128];
    165 
    166             snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid());
    167             fd = xopen3(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600);
    168 /* race, use fchown */
    169             chown(tmp, getuid(), getgid());
    170             fi = fopen(pas->pw_name, "r");
    171             if (fi) {
    172                 while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
    173                     full_write(fd, buf, n);
    174             }
    175             EditFile(caller, tmp);
    176             remove(tmp);
    177             lseek(fd, 0L, SEEK_SET);
    178             repFd = fd;
    179         }
    180         option = REPLACE;
    181         /* fall through */
    182     case REPLACE:
    183         {
    184 /* same here */
    185             char path[1024];
    186             int fd;
    187             int n;
    188 
    189             snprintf(path, sizeof(path), "%s.new", pas->pw_name);
    190             fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600);
    191             if (fd >= 0) {
    192                 while ((n = read(repFd, buf, sizeof(buf))) > 0) {
    193                     full_write(fd, buf, n);
    194                 }
    195                 close(fd);
    196                 rename(path, pas->pw_name);
    197             } else {
    198                 bb_error_msg("cannot create %s/%s", CDir, path);
    199             }
    200             close(repFd);
    201         }
    202         break;
    203     case DELETE:
    204         remove(pas->pw_name);
    205         break;
    206     case NONE:
    207     default:
    208         break;
    209     }
    210 
    211     /*
    212      *  Bump notification file.  Handle window where crond picks file up
    213      *  before we can write our entry out.
    214      */
    215 
    216     if (option == REPLACE || option == DELETE) {
    217         FILE *fo;
    218         struct stat st;
    219 
    220         while ((fo = fopen(CRONUPDATE, "a"))) {
    221             fprintf(fo, "%s\n", pas->pw_name);
    222             fflush(fo);
    223             if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
    224                 fclose(fo);
    225                 break;
    226             }
    227             fclose(fo);
    228             /* loop */
    229         }
    230         if (fo == NULL) {
    231             bb_error_msg("cannot append to %s/%s", CDir, CRONUPDATE);
    232         }
     200        }
     201        /* st.st_nlink == 0:
     202         * file was deleted, maybe crond missed our notification */
     203        close(fd);
     204        /* loop */
     205    }
     206    if (fd < 0) {
     207        bb_error_msg("can't append to %s/%s",
     208                crontab_dir, CRONUPDATE);
    233209    }
    234210    return 0;
    235211}
    236 
    237 static int GetReplaceStream(const char *user, const char *file)
    238 {
    239     int filedes[2];
    240     int pid;
    241     int fd;
    242     int n;
    243     char buf[1024];
    244 
    245     if (pipe(filedes) < 0) {
    246         perror("pipe");
    247         return -1;
    248     }
    249     pid = fork();
    250     if (pid < 0) {
    251         perror("fork");
    252         return -1;
    253     }
    254     if (pid > 0) {
    255         /*
    256          * PARENT
    257          */
    258 
    259         close(filedes[1]);
    260         if (read(filedes[0], buf, 1) != 1) {
    261             close(filedes[0]);
    262             filedes[0] = -1;
    263         }
    264         return filedes[0];
    265     }
    266 
    267     /*
    268      * CHILD
    269      */
    270 
    271     close(filedes[0]);
    272 
    273     if (ChangeUser(user, 0) < 0)
    274         exit(0);
    275 
    276     xfunc_error_retval = 0;
    277     fd = xopen(file, O_RDONLY);
    278     buf[0] = 0;
    279     write(filedes[1], buf, 1);
    280     while ((n = read(fd, buf, sizeof(buf))) > 0) {
    281         write(filedes[1], buf, n);
    282     }
    283     exit(0);
    284 }
    285 
    286 static void EditFile(const char *user, const char *file)
    287 {
    288     int pid = fork();
    289 
    290     if (pid == 0) {
    291         /*
    292          * CHILD - change user and run editor
    293          */
    294         const char *ptr;
    295 
    296         if (ChangeUser(user, 1) < 0)
    297             exit(0);
    298         ptr = getenv("VISUAL");
    299         if (ptr == NULL)
    300             ptr = getenv("EDITOR");
    301         if (ptr == NULL)
    302             ptr = PATH_VI;
    303 
    304         ptr = xasprintf("%s %s", ptr, file);
    305         execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", ptr, NULL);
    306         bb_perror_msg_and_die("exec");
    307     }
    308     if (pid < 0) {
    309         /*
    310          * PARENT - failure
    311          */
    312         bb_perror_msg_and_die("fork");
    313     }
    314     wait4(pid, NULL, 0, NULL);
    315 }
    316 
    317 static int ChangeUser(const char *user, short dochdir)
    318 {
    319     struct passwd *pas;
    320 
    321     /*
    322      * Obtain password entry and change privileges
    323      */
    324 
    325     pas = getpwnam(user);
    326     if (pas == NULL) {
    327         bb_perror_msg_and_die("failed to get uid for %s", user);
    328     }
    329     setenv("USER", pas->pw_name, 1);
    330     setenv("HOME", pas->pw_dir, 1);
    331     setenv("SHELL", DEFAULT_SHELL, 1);
    332 
    333     /*
    334      * Change running state to the user in question
    335      */
    336     change_identity(pas);
    337 
    338     if (dochdir) {
    339         if (chdir(pas->pw_dir) < 0) {
    340             bb_perror_msg("chdir(%s) by %s failed", pas->pw_dir, user);
    341             xchdir(TMPDIR);
    342         }
    343     }
    344     return pas->pw_uid;
    345 }
Note: See TracChangeset for help on using the changeset viewer.