示例#1
0
void tab_parse(char *file, char *user)
/* Parse a crontab file and add its data to the tables.  Handle errors by
 * yourself.  Table is owned by 'user' if non-null.
 */
{
	crontab_t **atab, *tab;
	cronjob_t **ajob, *job;
	int fd;
	struct stat st;
	char *p, *q;
	size_t n;
	ssize_t r;
	int ok, wc;

	for (atab= &crontabs; (tab= *atab) != nil; atab= &tab->next) {
		if (strcmp(file, tab->file) == 0) break;
	}

	/* Try to open the file. */
	if ((fd= open(file, O_RDONLY)) < 0 || fstat(fd, &st) < 0) {
		if (errno != ENOENT) {
			log(LOG_ERR, "%s: %s\n", file, strerror(errno));
		}
		if (fd != -1) close(fd);
		return;
	}

	/* Forget it if the file is awfully big. */
	if (st.st_size > TAB_MAX) {
		log(LOG_ERR, "%s: %lu bytes is bigger than my %lu limit\n",
			file,
			(unsigned long) st.st_size,
			(unsigned long) TAB_MAX);
		return;
	}

	/* If the file is the same as before then don't bother. */
	if (tab != nil && st.st_mtime == tab->mtime) {
		close(fd);
		tab->current= 1;
		return;
	}

	/* Create a new table structure. */
	tab= allocate(sizeof(*tab));
	tab->file= allocate((strlen(file) + 1) * sizeof(tab->file[0]));
	strcpy(tab->file, file);
	tab->user= nil;
	if (user != nil) {
		tab->user= allocate((strlen(user) + 1) * sizeof(tab->user[0]));
		strcpy(tab->user, user);
	}
	tab->data= allocate((st.st_size + 1) * sizeof(tab->data[0]));
	tab->jobs= nil;
	tab->mtime= st.st_mtime;
	tab->current= 0;
	tab->next= *atab;
	*atab= tab;

	/* Pull a new table in core. */
	n= 0;
	while (n < st.st_size) {
		if ((r = read(fd, tab->data + n, st.st_size - n)) < 0) {
			log(LOG_CRIT, "%s: %s", file, strerror(errno));
			close(fd);
			return;
		}
		if (r == 0) break;
		n+= r;
	}
	close(fd);
	tab->data[n]= 0;
	if (strlen(tab->data) < n) {
		log(LOG_ERR, "%s contains a null character\n", file);
		return;
	}

	/* Parse the file. */
	ajob= &tab->jobs;
	p= tab->data;
	ok= 1;
	while (ok && *p != 0) {
		q= get_token(&p);
		if (*q == '#' || q == p) {
			/* Comment or empty. */
			while (*p != 0 && *p++ != '\n') {}
			continue;
		}

		/* One new job coming up. */
		*ajob= job= allocate(sizeof(*job));
		*(ajob= &job->next)= nil;
		job->tab= tab;

		if (!range_parse(file, q, job->min, 0, 59, &wc)) {
			ok= 0;
			break;
		}

		q= get_token(&p);
		if (!range_parse(file, q, job->hour, 0, 23, &wc)) {
			ok= 0;
			break;
		}

		q= get_token(&p);
		if (!range_parse(file, q, job->mday, 1, 31, &wc)) {
			ok= 0;
			break;
		}
		job->do_mday= !wc;

		q= get_token(&p);
		if (!range_parse(file, q, job->mon, 1, 12, &wc)) {
			ok= 0;
			break;
		}
		job->do_mday |= !wc;

		q= get_token(&p);
		if (!range_parse(file, q, job->wday, 0, 7, &wc)) {
			ok= 0;
			break;
		}
		job->do_wday= !wc;

		/* 7 is Sunday, but 0 is a common mistake because it is in the
		 * tm_wday range.  We allow and even prefer it internally.
		 */
		if (bit_isset(job->wday, 7)) {
			bit_clr(job->wday, 7);
			bit_set(job->wday, 0);
		}

		/* The month range is 1-12, but tm_mon likes 0-11. */
		job->mon[0] >>= 1;
		if (bit_isset(job->mon, 8)) bit_set(job->mon, 7);
		job->mon[1] >>= 1;

		/* Scan for options. */
		job->user= nil;
		while (q= get_token(&p), *q == '-') {
			q++;
			if (q[0] == '-' && q+1 == p) {
				/* -- */
				q= get_token(&p);
				break;
			}
			while (q < p) switch (*q++) {
			case 'u':
				if (q == p) q= get_token(&p);
				if (q == p) goto usage;
				memmove(q-1, q, p-q);	/* gross... */
				p[-1]= 0;
				job->user= q-1;
				q= p;
				break;
			default:
			usage:
				log(LOG_ERR,
			"%s: bad option -%c, good options are: -u username\n",
					file, q[-1]);
				ok= 0;
				goto endtab;
			}
		}

		/* A crontab owned by a user can only do things as that user. */
		if (tab->user != nil) job->user= tab->user;

		/* Inspect the first character of the command. */
		job->cmd= q;
		if (q == p || *q == '#') {
			/* Rest of the line is empty, i.e. the commands are on
			 * the following lines indented by one tab.
			 */
			while (*p != 0 && *p++ != '\n') {}
			if (*p++ != '\t') {
				log(LOG_ERR, "%s: contains an empty command\n",
					file);
				ok= 0;
				goto endtab;
			}
			while (*p != 0) {
				if ((*q = *p++) == '\n') {
					if (*p != '\t') break;
					p++;
				}
				q++;
			}
		} else {
			/* The command is on this line.  Alas we must now be
			 * backwards compatible and change %'s to newlines.
			 */
			p= q;
			while (*p != 0) {
				if ((*q = *p++) == '\n') break;
				if (*q == '%') *q= '\n';
				q++;
			}
		}
		*q= 0;
		job->rtime= now;
		job->late= 0;		/* It is on time. */
		job->atjob= 0;		/* True cron job. */
		job->pid= IDLE_PID;	/* Not running yet. */
		tab_reschedule(job);	/* Compute next time to run. */
	}
  endtab:

	if (ok) tab->current= 1;
}
示例#2
0
文件: cron.c 项目: AjeyBohare/minix
static void run_job(cronjob_t *job)
/* Execute a cron job.  Register its pid in the job structure.  If a job's
 * crontab has an owner then its output is mailed to that owner, otherwise
 * no special provisions are made, so the output will go where cron's output
 * goes.  This keeps root's mailbox from filling up.
 */
{
	pid_t pid;
	int need_mailer;
	int mailfd[2], errfd[2];
	struct passwd *pw;
	crontab_t *tab= job->tab;

	need_mailer= (tab->user != nil);

	if (job->atjob) {
		struct stat st;

		need_mailer= 1;
		if (rename(tab->file, tab->data) < 0) {
			if (errno == ENOENT) {
				/* Normal error, job deleted. */
				need_reload= 1;
			} else {
				/* Bad error, halt processing AT jobs. */
				log(LOG_CRIT, "Can't rename %s: %s\n",
					tab->file, strerror(errno));
				tab_reschedule(job);
			}
			return;
		}
		/* Will need to determine the next AT job. */
		need_reload= 1;

		if (stat(tab->data, &st) < 0) {
			log(LOG_ERR, "Can't stat %s: %s\n",
						tab->data, strerror(errno));
			tab_reschedule(job);
			return;
		}
		if ((pw= getpwuid(st.st_uid)) == nil) {
			log(LOG_ERR, "Unknown owner for uid %lu of AT job %s\n",
				(unsigned long) st.st_uid, job->cmd);
			tab_reschedule(job);
			return;
		}
	} else {
		pw= nil;
		if (job->user != nil && (pw= getpwnam(job->user)) == nil) {
			log(LOG_ERR, "%s: Unknown user\n", job->user);
			tab_reschedule(job);
			return;
		}
	}

	if (need_mailer) {
		errfd[0]= -1;
		if (pipe(errfd) < 0 || pipe(mailfd) < 0) {
			log(LOG_ERR, "pipe() call failed: %s\n",
							strerror(errno));
			if (errfd[0] != -1) {
				close(errfd[0]);
				close(errfd[1]);
			}
			tab_reschedule(job);
			return;
		}
		(void) fcntl(errfd[1], F_SETFD,
				fcntl(errfd[1], F_GETFD) | FD_CLOEXEC);

		if ((pid= fork()) == -1) {
			log(LOG_ERR, "fork() call failed: %s\n",
							strerror(errno));
			close(errfd[0]);
			close(errfd[1]);
			close(mailfd[0]);
			close(mailfd[1]);
			tab_reschedule(job);
			return;
		}

		if (pid == 0) {
			/* Child that is to be the mailer. */
			char subject[70+20], *ps;

			close(errfd[0]);
			close(mailfd[1]);
			if (mailfd[0] != 0) {
				dup2(mailfd[0], 0);
				close(mailfd[0]);
			}

			memset(subject, 0, sizeof(subject));
			sprintf(subject,
				"Output from your %s job: %.50s",
				job->atjob ? "AT" : "cron",
				job->cmd);
			if (subject[70] != 0) {
				strcpy(subject+70-3, "...");
			}
			for (ps= subject; *ps != 0; ps++) {
				if (*ps == '\n') *ps= '%';
			}

			execl("/usr/bin/mail", "mail", "-s", subject,
						pw->pw_name, (char *) nil);
			write(errfd[1], &errno, sizeof(errno));
			_exit(1);
		}

		close(mailfd[0]);
		close(errfd[1]);
		if (read(errfd[0], &errno, sizeof(errno)) > 0) {
			log(LOG_ERR, "can't execute /usr/bin/mail: %s\n",
							strerror(errno));
			close(errfd[0]);
			close(mailfd[1]);
			tab_reschedule(job);
			return;
		}
		close(errfd[0]);
	}

	if (pipe(errfd) < 0) {
		log(LOG_ERR, "pipe() call failed: %s\n", strerror(errno));
		if (need_mailer) close(mailfd[1]);
		tab_reschedule(job);
		return;
	}
	(void) fcntl(errfd[1], F_SETFD, fcntl(errfd[1], F_GETFD) | FD_CLOEXEC);

	if ((pid= fork()) == -1) {
		log(LOG_ERR, "fork() call failed: %s\n", strerror(errno));
		close(errfd[0]);
		close(errfd[1]);
		if (need_mailer) close(mailfd[1]);
		tab_reschedule(job);
		return;
	}

	if (pid == 0) {
		/* Child that is to be the cron job. */
		close(errfd[0]);
		if (need_mailer) {
			if (mailfd[1] != 1) {
				dup2(mailfd[1], 1);
				close(mailfd[1]);
			}
			dup2(1, 2);
		}

		if (pw != nil) {
			/* Change id to the owner of the job. */
			initgroups(pw->pw_name, pw->pw_gid);
			setgid(pw->pw_gid);
			setuid(pw->pw_uid);
			chdir(pw->pw_dir);
			if (setenv("USER", pw->pw_name, 1) < 0) goto bad;
			if (setenv("LOGNAME", pw->pw_name, 1) < 0) goto bad;
			if (setenv("HOME", pw->pw_dir, 1) < 0) goto bad;
			if (setenv("SHELL", pw->pw_shell[0] == 0 ? "/bin/sh"
						: pw->pw_shell, 1) < 0) goto bad;
		}

		if (job->atjob) {
			execl("/bin/sh", "sh", tab->data, (char *) nil);
		} else {
			execl("/bin/sh", "sh", "-c", job->cmd, (char *) nil);
		}
	    bad:
		write(errfd[1], &errno, sizeof(errno));
		_exit(1);
	}

	if (need_mailer) close(mailfd[1]);
	close(errfd[1]);
	if (read(errfd[0], &errno, sizeof(errno)) > 0) {
		log(LOG_ERR, "can't execute /bin/sh: %s\n", strerror(errno));
		close(errfd[0]);
		tab_reschedule(job);
		return;
	}
	close(errfd[0]);
	job->pid= pid;
	if (debug >= 1) fprintf(stderr, "executing >%s<, pid = %ld\n",
						job->cmd, (long) job->pid);
}