Contents of /trunk/mkinitrd-magellan/busybox/miscutils/crontab.c
Parent Directory | Revision Log
Revision 1123 -
(show annotations)
(download)
Wed Aug 18 21:56:57 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 5183 byte(s)
Wed Aug 18 21:56:57 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 5183 byte(s)
-updated to busybox-1.17.1
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 "libbb.h" |
14 | |
15 | #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" |
16 | #ifndef CRONUPDATE |
17 | #define CRONUPDATE "cron.update" |
18 | #endif |
19 | |
20 | static void edit_file(const struct passwd *pas, const char *file) |
21 | { |
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 | |
47 | static 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 | |
69 | int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
70 | int crontab_main(int argc UNUSED_PARAM, char **argv) |
71 | { |
72 | const struct passwd *pas; |
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);*/ |
199 | break; |
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); |
209 | } |
210 | return 0; |
211 | } |