Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/miscutils/crontab.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/miscutils/crontab.c
r821 r1770 11 11 */ 12 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> 13 #include "libbb.h" 30 14 31 15 #ifndef CRONTABS … … 39 23 #endif 40 24 #ifndef PATH_VI 41 #define PATH_VI "/bin/vi" /* location of vi 25 #define PATH_VI "/bin/vi" /* location of vi */ 42 26 #endif 43 27 44 static const char 28 static const char *CDir = CRONTABS; 45 29 46 30 static void EditFile(const char *user, const char *file); 47 31 static int GetReplaceStream(const char *user, const char *file); 48 static int 49 50 int 51 crontab_main(int ac, char **av)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) 52 36 { 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; 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: 121 207 default: 122 i = ac;123 break;124 }125 }126 if (i != ac || option == NONE)127 bb_show_usage();128 129 /*130 * Get password entry131 */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 directory148 */149 150 bb_xchdir(CDir);151 152 /*153 * Handle options as appropriate154 */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 up223 * 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 208 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; 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; 245 235 } 246 236 247 static int 248 GetReplaceStream(const char *user, const char *file) 237 static int GetReplaceStream(const char *user, const char *file) 249 238 { 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) 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 } 284 283 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 284 } 295 285 296 static void 297 EditFile(const char *user, const char *file) 286 static void EditFile(const char *user, const char *file) 298 287 { 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); 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); 325 315 } 326 316 327 static int 328 ChangeUser(const char *user, short dochdir) 317 static int ChangeUser(const char *user, short dochdir) 329 318 { 330 331 332 333 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 341 342 343 344 345 346 347 348 349 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);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; 356 345 }
Note:
See TracChangeset
for help on using the changeset viewer.