Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
     5         kx 
     5         kx /*
     5         kx  * CRONTAB.C
     5         kx  *
     5         kx  * crontab [-u user] [-c dir] [-l|-e|-d|file|-]
     5         kx  * usually run as setuid root
     5         kx  * -u and -c options only work if getuid() == geteuid()
     5         kx  *
     5         kx  * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com)
     5         kx  * Copyright 2009-2011 James Pryor <profjim@jimpryor.net>
     5         kx  * May be distributed under the GNU General Public License
     5         kx  */
     5         kx 
     5         kx #include "defs.h"
     5         kx 
     5         kx Prototype void printlogf(int level, const char *ctl, ...);
     5         kx 
     5         kx void Usage(void);
     5         kx int GetReplaceStream(const char *user, const char *file);
     5         kx void EditFile(const char *user, const char *file);
     5         kx 
     5         kx const char *CDir = CRONTABS;
     5         kx int   UserId;
     5         kx 
     5         kx 
     5         kx int
     5         kx main(int ac, char **av)
     5         kx {
     5         kx 	enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
     5         kx 	struct passwd *pas;
     5         kx 	char *repFile = NULL;
     5         kx 	int repFd = 0;
     5         kx 	int i;
     5         kx 	char caller[SMALL_BUFFER];		/* user that ran program */
     5         kx 
     5         kx 	UserId = getuid();
     5         kx 	if ((pas = getpwuid(UserId)) == NULL) {
     5         kx 		perror("getpwuid");
     5         kx 		exit(1);
     5         kx 	}
     5         kx 	/* [v]snprintf write at most size including \0; they'll null-terminate, even when they truncate */
     5         kx 	/* return value >= size means result was truncated */
     5         kx 	if (snprintf(caller, sizeof(caller), "%s", pas->pw_name) >= sizeof(caller)) {
     5         kx 		printlogf(0, "username '%s' too long", caller);
     5         kx 		exit(1);
     5         kx 	}
     5         kx 
     5         kx 	opterr = 0;
     5         kx 	while ((i=getopt(ac,av,"ledu:c:")) != -1) {
     5         kx 		switch(i) {
     5         kx 			case 'l':
     5         kx 				if (option != NONE)
     5         kx 					Usage();
     5         kx 				else
     5         kx 					option = LIST;
     5         kx 				break;
     5         kx 			case 'e':
     5         kx 				if (option != NONE)
     5         kx 					Usage();
     5         kx 				else
     5         kx 					option = EDIT;
     5         kx 				break;
     5         kx 			case 'd':
     5         kx 				if (option != NONE)
     5         kx 					Usage();
     5         kx 				else
     5         kx 					option = DELETE;
     5         kx 				break;
     5         kx 			case 'u':
     5         kx 				/* getopt guarantees optarg != 0 here */
     5         kx 				if (*optarg != 0 && getuid() == geteuid()) {
     5         kx 					pas = getpwnam(optarg);
     5         kx 					if (pas) {
     5         kx 						UserId = pas->pw_uid;
     5         kx 						/* paranoia */
     5         kx 						if ((pas = getpwuid(UserId)) == NULL) {
     5         kx 							perror("getpwuid");
     5         kx 							exit(1);
     5         kx 						}
     5         kx 					} else {
     5         kx 						printlogf(0, "user '%s' unknown", optarg);
     5         kx 						exit(1);
     5         kx 					}
     5         kx 				} else {
     5         kx 					printlogf(0, "-u option: superuser only");
     5         kx 					exit(1);
     5         kx 				}
     5         kx 				break;
     5         kx 			case 'c':
     5         kx 				/* getopt guarantees optarg != 0 here */
     5         kx 				if (*optarg != 0 && getuid() == geteuid()) {
     5         kx 					CDir = optarg;
     5         kx 				} else {
     5         kx 					printlogf(0, "-c option: superuser only");
     5         kx 					exit(1);
     5         kx 				}
     5         kx 				break;
     5         kx 			default:
     5         kx 				/* unrecognized -X */
     5         kx 				option = NONE;
     5         kx 		}
     5         kx 	}
     5         kx 
     5         kx 	if (option == NONE && optind == ac - 1) {
     5         kx 		if (av[optind][0] != '-') {
     5         kx 			option = REPLACE;
     5         kx 			repFile = av[optind];
     5         kx 			optind++;
     5         kx 		} else if (av[optind][1] == 0) {
     5         kx 			option = REPLACE;
     5         kx 			optind++;
     5         kx 		}
     5         kx 	}
     5         kx 	if (option == NONE || optind != ac) {
     5         kx 		Usage();
     5         kx 	}
     5         kx 
     5         kx 	/*
     5         kx 	 * If there is a replacement file, obtain a secure descriptor to it.
     5         kx 	 */
     5         kx 
     5         kx 	if (repFile) {
     5         kx 		repFd = GetReplaceStream(caller, repFile);
     5         kx 		if (repFd < 0) {
     5         kx 			printlogf(0, "unable to read replacement file %s", repFile);
     5         kx 			exit(1);
     5         kx 		}
     5         kx 	}
     5         kx 
     5         kx 	/*
     5         kx 	 * Change directory to our crontab directory
     5         kx 	 */
     5         kx 
     5         kx 	if (chdir(CDir) < 0) {
     5         kx 		printlogf(0, "cannot change dir to %s: %s", CDir, strerror(errno));
     5         kx 		exit(1);
     5         kx 	}
     5         kx 
     5         kx 	/*
     5         kx 	 * Handle options as appropriate
     5         kx 	 */
     5         kx 
     5         kx 	switch(option) {
     5         kx 		case LIST:
     5         kx 			{
     5         kx 				FILE *fi;
     5         kx 				char buf[RW_BUFFER];
     5         kx 
     5         kx 				if ((fi = fopen(pas->pw_name, "r"))) {
     5         kx 					while (fgets(buf, sizeof(buf), fi) != NULL)
     5         kx 						fputs(buf, stdout);
     5         kx 					fclose(fi);
     5         kx 				} else {
     5         kx 					fprintf(stderr, "no crontab for %s\n", pas->pw_name);
     5         kx 					/* no error code */
     5         kx 				}
     5         kx 			}
     5         kx 			break;
     5         kx 		case EDIT:
     5         kx 			{
     5         kx 				FILE *fi;
     5         kx 				int fd;
     5         kx 				int n;
     5         kx 				char tmp[] = TMPDIR "/crontab.XXXXXX";
     5         kx 				char buf[RW_BUFFER];
     5         kx 
     5         kx 				/*
     5         kx 				 * Create temp file with perm 0600 and O_EXCL flag, ensuring that this call creates the file
     5         kx 				 * Read from fi for "$CDir/$USER", write to fd for temp file
     5         kx 				 * EditFile changes user if necessary, and runs editor on temp file
     5         kx 				 * Then we delete the temp file, keeping its fd as repFd
     5         kx 				 */
     5         kx 				if ((fd = mkstemp(tmp)) >= 0) {
     5         kx 					chown(tmp, getuid(), getgid());
     5         kx 					if ((fi = fopen(pas->pw_name, "r"))) {
     5         kx 						while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
     5         kx 							write(fd, buf, n);
     5         kx 					}
     5         kx 					EditFile(caller, tmp);
     5         kx 					remove(tmp);
     5         kx 					lseek(fd, 0L, 0);
     5         kx 					repFd = fd;
     5         kx 				} else {
     5         kx 					printlogf(0, "unable to create %s: %s", tmp, strerror(errno));
     5         kx 					exit(1);
     5         kx 				}
     5         kx 
     5         kx 			}
     5         kx 			option = REPLACE;
     5         kx 			/* fall through */
     5         kx 		case REPLACE:
     5         kx 			{
     5         kx 				char buf[RW_BUFFER];
     5         kx 				char path[SMALL_BUFFER];
     5         kx 				int fd;
     5         kx 				int n;
     5         kx 
     5         kx 				/*
     5         kx 				 * Read from repFd, write to fd for "$CDir/$USER.new"
     5         kx 				 */
     5         kx 				snprintf(path, sizeof(path), "%s.new", pas->pw_name);
     5         kx 				if ((fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600)) >= 0) {
     5         kx 					while ((n = read(repFd, buf, sizeof(buf))) > 0) {
     5         kx 						write(fd, buf, n);
     5         kx 					}
     5         kx 					close(fd);
     5         kx 					rename(path, pas->pw_name);
     5         kx 				} else {
     5         kx 					fprintf(stderr, "unable to create %s/%s: %s\n",
     5         kx 							CDir,
     5         kx 							path,
     5         kx 							strerror(errno)
     5         kx 						   );
     5         kx 				}
     5         kx 				close(repFd);
     5         kx 			}
     5         kx 			break;
     5         kx 		case DELETE:
     5         kx 			remove(pas->pw_name);
     5         kx 			break;
     5         kx 		case NONE:
     5         kx 		default:
     5         kx 			break;
     5         kx 	}
     5         kx 
     5         kx 	/*
     5         kx 	 *  Bump notification file.  Handle window where crond picks file up
     5         kx 	 *  before we can write our entry out.
     5         kx 	 */
     5         kx 
     5         kx 	if (option == REPLACE || option == DELETE) {
     5         kx 		FILE *fo;
     5         kx 		struct stat st;
     5         kx 
     5         kx 		while ((fo = fopen(CRONUPDATE, "a"))) {
     5         kx 			fprintf(fo, "%s\n", pas->pw_name);
     5         kx 			fflush(fo);
     5         kx 			if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
     5         kx 				fclose(fo);
     5         kx 				break;
     5         kx 			}
     5         kx 			fclose(fo);
     5         kx 			/* loop */
     5         kx 		}
     5         kx 		if (fo == NULL) {
     5         kx 			fprintf(stderr, "unable to append to %s/%s\n", CDir, CRONUPDATE);
     5         kx 		}
     5         kx 	}
     5         kx 	exit(0);
     5         kx 	/* not reached */
     5         kx }
     5         kx 
     5         kx void
     5         kx printlogf(int level, const char *ctl, ...)
     5         kx {
     5         kx 	va_list va;
     5         kx 	char buf[LOG_BUFFER];
     5         kx 
     5         kx 	va_start(va, ctl);
     5         kx 	vsnprintf(buf, sizeof(buf), ctl, va);
     5         kx 	write(2, buf, strlen(buf));
     5         kx 	va_end(va);
     5         kx }
     5         kx 
     5         kx void
     5         kx Usage(void)
     5         kx {
     5         kx 	/*
     5         kx 	 * parse error
     5         kx 	 */
     5         kx 	printf("crontab " VERSION "\n");
     5         kx 	printf("crontab file [-u user]  replace crontab from file\n");
     5         kx 	printf("crontab -  [-u user]    replace crontab from stdin\n");
     5         kx 	printf("crontab -l [-u user]    list crontab\n");
     5         kx 	printf("crontab -e [-u user]    edit crontab\n");
     5         kx 	printf("crontab -d [-u user]    delete crontab\n");
     5         kx 	printf("crontab -c dir <opts>   specify crontab directory\n");
     5         kx 	exit(2);
     5         kx }
     5         kx 
     5         kx int
     5         kx GetReplaceStream(const char *user, const char *file)
     5         kx {
     5         kx 	int filedes[2];
     5         kx 	int pid;
     5         kx 	int fd;
     5         kx 	int n;
     5         kx 	char buf[RW_BUFFER];
     5         kx 
     5         kx 	if (pipe(filedes) < 0) {
     5         kx 		perror("pipe");
     5         kx 		return(-1);
     5         kx 	}
     5         kx 	if ((pid = fork()) < 0) {
     5         kx 		perror("fork");
     5         kx 		return(-1);
     5         kx 	}
     5         kx 	if (pid > 0) {
     5         kx 		/*
     5         kx 		 * PARENT
     5         kx 		 * Read from pipe[0], return it (or -1 if it's empty)
     5         kx 		 */
     5         kx 
     5         kx 		close(filedes[1]);
     5         kx 		if (read(filedes[0], buf, 1) != 1) {
     5         kx 			close(filedes[0]);
     5         kx 			filedes[0] = -1;
     5         kx 		}
     5         kx 		return(filedes[0]);
     5         kx 	}
     5         kx 
     5         kx 	/*
     5         kx 	 * CHILD
     5         kx 	 * Read from fd for "$file", write to pipe[1]
     5         kx 	 */
     5         kx 
     5         kx 	close(filedes[0]);
     5         kx 
     5         kx 	if (ChangeUser(user, NULL) < 0)
     5         kx 		exit(0);
     5         kx 
     5         kx 	fd = open(file, O_RDONLY);
     5         kx 	if (fd < 0) {
     5         kx 		printlogf(0, "unable to open %s: %s", file, strerror(errno));
     5         kx 		exit(1);
     5         kx 	}
     5         kx 	buf[0] = 0;
     5         kx 	write(filedes[1], buf, 1);
     5         kx 	while ((n = read(fd, buf, sizeof(buf))) > 0) {
     5         kx 		write(filedes[1], buf, n);
     5         kx 	}
     5         kx 	exit(0);
     5         kx }
     5         kx 
     5         kx void
     5         kx EditFile(const char *user, const char *file)
     5         kx {
     5         kx 	int pid;
     5         kx 
     5         kx 	if ((pid = fork()) == 0) {
     5         kx 		/*
     5         kx 		 * CHILD - change user and run editor on "$file"
     5         kx 		 */
     5         kx 		const char *ptr;
     5         kx 		char visual[SMALL_BUFFER];
     5         kx 
     5         kx 		if (ChangeUser(user, TMPDIR) < 0)
     5         kx 			exit(0);
     5         kx 		if ((ptr = getenv("EDITOR")) == NULL || strlen(ptr) >= sizeof(visual))
     5         kx 			if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) >= sizeof(visual))
     5         kx 				ptr = PATH_VI;
     5         kx 
     5         kx 		/* [v]snprintf write at most size including \0; they'll null-terminate, even when they truncate */
     5         kx 		/* return value >= size means result was truncated */
     5         kx 		if (snprintf(visual, sizeof(visual), "%s %s", ptr, file) < sizeof(visual))
     5         kx 			execl("/bin/sh", "/bin/sh", "-c", visual, NULL);
     5         kx 		printlogf(0, "couldn't exec %s", visual);
     5         kx 		exit(1);
     5         kx 	}
     5         kx 	if (pid < 0) {
     5         kx 		/*
     5         kx 		 * PARENT - failure
     5         kx 		 */
     5         kx 		perror("fork");
     5         kx 		exit(1);
     5         kx 	}
     5         kx 	waitpid(pid, NULL, 0);
     5         kx }
     5         kx