source: MondoRescue/branches/3.3/mindi-busybox/miscutils/crontab.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 4.9 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 GPLv2 or later, see file LICENSE in this source tree.
11 */
12
13//usage:#define crontab_trivial_usage
14//usage: "[-c DIR] [-u USER] [-ler]|[FILE]"
15//usage:#define crontab_full_usage "\n\n"
16//usage: " -c Crontab directory"
17//usage: "\n -u User"
18//usage: "\n -l List crontab"
19//usage: "\n -e Edit crontab"
20//usage: "\n -r Delete crontab"
21//usage: "\n FILE Replace crontab by FILE ('-': stdin)"
22
23#include "libbb.h"
24
25#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
26#ifndef CRONUPDATE
27#define CRONUPDATE "cron.update"
28#endif
29
30static void edit_file(const struct passwd *pas, const char *file)
31{
32 const char *ptr;
33 pid_t pid;
34
35 pid = xvfork();
36 if (pid) { /* parent */
37 wait4pid(pid);
38 return;
39 }
40
41 /* CHILD - change user and run editor */
42 /* initgroups, setgid, setuid */
43 change_identity(pas);
44 setup_environment(pas->pw_shell,
45 SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP,
46 pas);
47 ptr = getenv("VISUAL");
48 if (!ptr) {
49 ptr = getenv("EDITOR");
50 if (!ptr)
51 ptr = "vi";
52 }
53
54 BB_EXECLP(ptr, ptr, file, NULL);
55 bb_perror_msg_and_die("can't execute '%s'", ptr);
56}
57
58int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
59int crontab_main(int argc UNUSED_PARAM, char **argv)
60{
61 const struct passwd *pas;
62 const char *crontab_dir = CRONTABS;
63 char *tmp_fname;
64 char *new_fname;
65 char *user_name; /* -u USER */
66 int fd;
67 int src_fd;
68 int opt_ler;
69
70 /* file [opts] Replace crontab from file
71 * - [opts] Replace crontab from stdin
72 * -u user User
73 * -c dir Crontab directory
74 * -l List crontab for user
75 * -e Edit crontab for user
76 * -r Delete crontab for user
77 * bbox also supports -d == -r, but most other crontab
78 * implementations do not. Deprecated.
79 */
80 enum {
81 OPT_u = (1 << 0),
82 OPT_c = (1 << 1),
83 OPT_l = (1 << 2),
84 OPT_e = (1 << 3),
85 OPT_r = (1 << 4),
86 OPT_ler = OPT_l + OPT_e + OPT_r,
87 };
88
89 opt_complementary = "?1:dr"; /* max one argument; -d implies -r */
90 opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir);
91 argv += optind;
92
93 if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */
94 /* Run by non-root */
95 if (opt_ler & (OPT_u|OPT_c))
96 bb_error_msg_and_die(bb_msg_you_must_be_root);
97 }
98
99 if (opt_ler & OPT_u) {
100 pas = xgetpwnam(user_name);
101 } else {
102 pas = xgetpwuid(getuid());
103 }
104
105#define user_name DONT_USE_ME_BEYOND_THIS_POINT
106
107 /* From now on, keep only -l, -e, -r bits */
108 opt_ler &= OPT_ler;
109 if ((opt_ler - 1) & opt_ler) /* more than one bit set? */
110 bb_show_usage();
111
112 /* Read replacement file under user's UID/GID/group vector */
113 src_fd = STDIN_FILENO;
114 if (!opt_ler) { /* Replace? */
115 if (!argv[0])
116 bb_show_usage();
117 if (NOT_LONE_DASH(argv[0])) {
118 src_fd = xopen_as_uid_gid(argv[0], O_RDONLY, pas->pw_uid, pas->pw_gid);
119 }
120 }
121
122 /* cd to our crontab directory */
123 xchdir(crontab_dir);
124
125 tmp_fname = NULL;
126
127 /* Handle requested operation */
128 switch (opt_ler) {
129
130 default: /* case OPT_r: Delete */
131 unlink(pas->pw_name);
132 break;
133
134 case OPT_l: /* List */
135 {
136 char *args[2] = { pas->pw_name, NULL };
137 return bb_cat(args);
138 /* list exits,
139 * the rest go play with cron update file */
140 }
141
142 case OPT_e: /* Edit */
143 tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid());
144 /* No O_EXCL: we don't want to be stuck if earlier crontabs
145 * were killed, leaving stale temp file behind */
146 src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
147 fchown(src_fd, pas->pw_uid, pas->pw_gid);
148 fd = open(pas->pw_name, O_RDONLY);
149 if (fd >= 0) {
150 bb_copyfd_eof(fd, src_fd);
151 close(fd);
152 xlseek(src_fd, 0, SEEK_SET);
153 }
154 close_on_exec_on(src_fd); /* don't want editor to see this fd */
155 edit_file(pas, tmp_fname);
156 /* fall through */
157
158 case 0: /* Replace (no -l, -e, or -r were given) */
159 new_fname = xasprintf("%s.new", pas->pw_name);
160 fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600);
161 if (fd >= 0) {
162 bb_copyfd_eof(src_fd, fd);
163 close(fd);
164 xrename(new_fname, pas->pw_name);
165 } else {
166 bb_error_msg("can't create %s/%s",
167 crontab_dir, new_fname);
168 }
169 if (tmp_fname)
170 unlink(tmp_fname);
171 /*free(tmp_fname);*/
172 /*free(new_fname);*/
173 } /* switch */
174
175 /* Bump notification file. Handle window where crond picks file up
176 * before we can write our entry out.
177 */
178 while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) {
179 struct stat st;
180
181 fdprintf(fd, "%s\n", pas->pw_name);
182 if (fstat(fd, &st) != 0 || st.st_nlink != 0) {
183 /*close(fd);*/
184 break;
185 }
186 /* st.st_nlink == 0:
187 * file was deleted, maybe crond missed our notification */
188 close(fd);
189 /* loop */
190 }
191 if (fd < 0) {
192 bb_error_msg("can't append to %s/%s",
193 crontab_dir, CRONUPDATE);
194 }
195 return 0;
196}
Note: See TracBrowser for help on using the repository browser.