source: MondoRescue/branches/2.2.5/mindi-busybox/miscutils/crontab.c@ 1765

Last change on this file since 1765 was 1765, checked in by Bruno Cornec, 16 years ago

Update to busybox 1.7.2

File size: 6.4 KB
Line 
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#ifndef CRONTABS
16#define CRONTABS "/var/spool/cron/crontabs"
17#endif
18#ifndef TMPDIR
19#define TMPDIR "/var/spool/cron"
20#endif
21#ifndef CRONUPDATE
22#define CRONUPDATE "cron.update"
23#endif
24#ifndef PATH_VI
25#define PATH_VI "/bin/vi" /* location of vi */
26#endif
27
28static const char *CDir = CRONTABS;
29
30static void EditFile(const char *user, const char *file);
31static int GetReplaceStream(const char *user, const char *file);
32static int ChangeUser(const char *user, short dochdir);
33
34int crontab_main(int ac, char **av);
35int crontab_main(int ac, char **av)
36{
37 enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
38 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 != '-')
69 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 }
233 }
234 return 0;
235}
236
237static 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
286static 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
317static 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 TracBrowser for help on using the repository browser.