448 lines
10 KiB
C
448 lines
10 KiB
C
/* Copyright (c) 1984 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)crontab.c 1.1 94/10/31 SMI"; /* from S5R3 1.8 */
|
|
#endif
|
|
|
|
/**************************************************************************
|
|
*** C r o n t a b . c ***
|
|
**************************************************************************
|
|
|
|
date: 7/2/82
|
|
description: This program implements crontab (see cron(1)).
|
|
This program should be set-uid to root.
|
|
files:
|
|
/usr/lib/cron drwxr-xr-x root sys
|
|
/usr/lib/cron/cron.allow -rw-r--r-- root sys
|
|
/usr/lib/cron/cron.deny -rw-r--r-- root sys
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <pwd.h>
|
|
#include "cron.h"
|
|
|
|
#define TMPFILE "_cron" /* prefix for tmp file */
|
|
#define CRMODE 0400 /* mode for creating crontabs */
|
|
|
|
#define BADCREATE "can't create your crontab file in the crontab directory."
|
|
#define BADOPEN "can't open your crontab file"
|
|
#define BADREAD "error reading your crontab file"
|
|
#define BADUSAGE "proper usage is: \n crontab [file | -e [user] | -l [user] | -r [user] ]"
|
|
#define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)."
|
|
#define UINVALIDUSER "the specified user is not a valid user (no entry in /etc/passwd)."
|
|
#define NOTALLOWED "you are not authorized to use cron. Sorry."
|
|
#define NOTROOT "you must be super-user to change or read another user's crontab file"
|
|
#define UNOTALLOWED "the specified user is not authorized to use cron."
|
|
#define EOLN "unexpected end of line."
|
|
#define UNEXPECT "unexpected character found in line."
|
|
#define OUTOFBOUND "number out of bounds."
|
|
|
|
extern int opterr, optind;
|
|
extern char *optarg, *xmalloc();
|
|
extern int errno;
|
|
extern char *errmsg();
|
|
int err,cursor,catch();
|
|
char *cf,*tnam,line[CTLINESIZE];
|
|
char edtemp[5+13+1];
|
|
|
|
main(argc,argv)
|
|
char **argv;
|
|
{
|
|
int c, eflag, rflag, lflag, errflg;
|
|
int ruid, euid;
|
|
char login[UNAMESIZE],*getuser();
|
|
extern char *getenv();
|
|
register char *pp;
|
|
register FILE *fp;
|
|
register FILE *tmpfp;
|
|
struct stat stbuf;
|
|
time_t omodtime;
|
|
char *editor;
|
|
char buf[BUFSIZ];
|
|
|
|
eflag = 0;
|
|
rflag = 0;
|
|
lflag = 0;
|
|
errflg = 0;
|
|
while ((c=getopt(argc, argv, "elr")) != EOF)
|
|
switch (c) {
|
|
case 'e':
|
|
if (lflag || rflag)
|
|
errflg++;
|
|
else
|
|
eflag++;
|
|
break;
|
|
case 'l':
|
|
if (eflag || rflag)
|
|
errflg++;
|
|
else
|
|
lflag++;
|
|
break;
|
|
case 'r':
|
|
if (eflag || lflag)
|
|
errflg++;
|
|
else
|
|
rflag++;
|
|
break;
|
|
case '?':
|
|
errflg++;
|
|
break;
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
if (errflg || argc > 1)
|
|
crabort(BADUSAGE);
|
|
euid = geteuid();
|
|
ruid = getuid();
|
|
if ((eflag || lflag || rflag) && argc == 1) {
|
|
if (ruid != 0)
|
|
crabort(NOTROOT);
|
|
pp = *argv++;
|
|
argc--;
|
|
if (getpwnam(pp) == NULL)
|
|
crabort(UINVALIDUSER);
|
|
if (!allowed(pp,CRONALLOW,CRONDENY))
|
|
crabort(UNOTALLOWED);
|
|
} else {
|
|
pp = getuser(ruid);
|
|
if(pp == NULL)
|
|
crabort(INVALIDUSER);
|
|
if (!allowed(pp,CRONALLOW,CRONDENY))
|
|
crabort(NOTALLOWED);
|
|
}
|
|
strcpy(login,pp);
|
|
|
|
cf = xmalloc(strlen(CRONDIR)+strlen(login)+2);
|
|
strcat(strcat(strcpy(cf,CRONDIR),"/"),login);
|
|
if (rflag) {
|
|
unlink(cf);
|
|
sendmsg(DELETE,login);
|
|
exit(0);
|
|
}
|
|
if (lflag) {
|
|
if((fp = fopen(cf,"r")) == NULL)
|
|
crabortperror(BADOPEN);
|
|
while(fgets(line,CTLINESIZE,fp) != NULL) {
|
|
fputs(line,stdout);
|
|
if(ferror(stdout))
|
|
crabortperror("write error on standard output");
|
|
}
|
|
fclose(fp);
|
|
if (fflush(stdout) == EOF)
|
|
crabortperror("write error on standard output");
|
|
exit(0);
|
|
}
|
|
|
|
if (eflag) {
|
|
if((fp = fopen(cf,"r")) == NULL) {
|
|
if(errno != ENOENT)
|
|
crabortperror(BADOPEN);
|
|
}
|
|
/*
|
|
* Temporarily give up privileges.
|
|
*/
|
|
setreuid(-1, ruid);
|
|
(void)strcpy(edtemp, "/tmp/crontabXXXXXX");
|
|
(void)mktemp(edtemp);
|
|
if((tmpfp = fopen(edtemp,"w")) == NULL)
|
|
crabortperror("can't create temporary file");
|
|
if(fp != NULL) {
|
|
/*
|
|
* Copy user's crontab file to temporary file.
|
|
*/
|
|
while(fgets(line,CTLINESIZE,fp) != NULL) {
|
|
fputs(line,tmpfp);
|
|
if(ferror(tmpfp)) {
|
|
fclose(fp);
|
|
fclose(tmpfp);
|
|
crabortperror("write error on temporary file");
|
|
}
|
|
}
|
|
if (ferror(fp)) {
|
|
fclose(fp);
|
|
fclose(tmpfp);
|
|
crabortperror(BADREAD);
|
|
}
|
|
fclose(fp);
|
|
}
|
|
if(fclose(tmpfp) == EOF)
|
|
crabortperror("write error on temporary file");
|
|
if(stat(edtemp, &stbuf) < 0)
|
|
crabortperror("can't stat temporary file");
|
|
omodtime = stbuf.st_mtime;
|
|
editor = getenv("VISUAL");
|
|
if (editor == NULL)
|
|
editor = getenv("EDITOR");
|
|
if (editor == NULL)
|
|
editor = "vi";
|
|
(void)sprintf(buf, "%s %s", editor, edtemp);
|
|
sleep(1); /* To fix bug on line 205, where
|
|
the mod time can be same even though
|
|
file was changed, because time
|
|
resolution is 1 second. */
|
|
if (system(buf) == 0) {
|
|
/* sanity checks */
|
|
if((tmpfp = fopen(edtemp, "r")) == NULL)
|
|
crabortperror("can't open temporary file");
|
|
if(fstat(fileno(tmpfp), &stbuf) < 0)
|
|
crabortperror("can't stat temporary file");
|
|
if(stbuf.st_size == 0)
|
|
crabort("temporary file empty");
|
|
if(omodtime == stbuf.st_mtime) {
|
|
(void)unlink(edtemp);
|
|
fprintf(stderr,
|
|
"The crontab file was not changed.\n");
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
* Reclaim privileges.
|
|
*/
|
|
setreuid(-1, euid);
|
|
copycron(tmpfp);
|
|
(void)unlink(edtemp);
|
|
} else {
|
|
/*
|
|
* Couldn't run editor.
|
|
*/
|
|
(void)unlink(edtemp);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
if (argc==0)
|
|
copycron(stdin);
|
|
else {
|
|
if (access(argv[0],04) || (fp=fopen(argv[0],"r"))==NULL)
|
|
crabortperror(argv[0]);
|
|
copycron(fp);
|
|
}
|
|
}
|
|
sendmsg(ADD,login);
|
|
exit(0);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
|
|
/******************/
|
|
copycron(fp)
|
|
/******************/
|
|
FILE *fp;
|
|
{
|
|
FILE *tfp,*fdopen();
|
|
char pid[6],*strcat(),*strcpy();
|
|
int t;
|
|
|
|
sprintf(pid,"%-5d",getpid());
|
|
tnam=xmalloc(strlen(CRONDIR)+strlen(TMPFILE)+7);
|
|
strcat(strcat(strcat(strcpy(tnam,CRONDIR),"/"),TMPFILE),pid);
|
|
/* catch SIGINT, SIGHUP, SIGQUIT signals */
|
|
if (signal(SIGINT,catch) == SIG_IGN) signal(SIGINT,SIG_IGN);
|
|
if (signal(SIGHUP,catch) == SIG_IGN) signal(SIGHUP,SIG_IGN);
|
|
if (signal(SIGQUIT,catch) == SIG_IGN) signal(SIGQUIT,SIG_IGN);
|
|
if (signal(SIGTERM,catch) == SIG_IGN) signal(SIGTERM,SIG_IGN);
|
|
if ((t=creat(tnam,CRMODE))==-1) crabortperror(BADCREATE);
|
|
if ((tfp=fdopen(t,"w"))==NULL)
|
|
crabort(BADCREATE);
|
|
err=0; /* if errors found, err set to 1 */
|
|
while (fgets(line,CTLINESIZE,fp) != NULL) {
|
|
cursor=0;
|
|
while(line[cursor] == ' ' || line[cursor] == '\t')
|
|
cursor++;
|
|
if(line[cursor] == '#')
|
|
goto cont;
|
|
if (next_field(0,59)) continue;
|
|
if (next_field(0,23)) continue;
|
|
if (next_field(1,31)) continue;
|
|
if (next_field(1,12)) continue;
|
|
if (next_field(0,7)) continue;
|
|
if (line[++cursor] == '\0') {
|
|
cerror(EOLN);
|
|
continue;
|
|
}
|
|
cont:
|
|
if (fputs(line,tfp) == EOF)
|
|
crabortperror(BADCREATE);
|
|
}
|
|
if (ferror(fp)) {
|
|
fclose(fp);
|
|
/*
|
|
* Don't do an "fclose", as that may do a "write" which may
|
|
* fail, and cause the wrong "perror" error message to be
|
|
* printed.
|
|
*/
|
|
(void)close(fileno(tfp));
|
|
crabortperror(BADREAD);
|
|
}
|
|
fclose(fp);
|
|
if (fclose(tfp) == EOF)
|
|
crabortperror(BADCREATE);
|
|
if (!err) {
|
|
/* make file tfp the new crontab */
|
|
if (rename(tnam,cf)==-1)
|
|
crabortperror(BADCREATE);
|
|
} else
|
|
unlink(tnam);
|
|
tnam = NULL; /* file no longer exists */
|
|
}
|
|
|
|
|
|
/*****************/
|
|
next_field(lower,upper)
|
|
/*****************/
|
|
int lower,upper;
|
|
{
|
|
int num,num2;
|
|
|
|
while ((line[cursor]==' ') || (line[cursor]=='\t')) cursor++;
|
|
if (line[cursor] == '\0') {
|
|
cerror(EOLN);
|
|
return(1);
|
|
}
|
|
if (line[cursor] == '*') {
|
|
cursor++;
|
|
if ((line[cursor]!=' ') && (line[cursor]!='\t')) {
|
|
cerror(UNEXPECT);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
while (TRUE) {
|
|
if (!isdigit(line[cursor])) {
|
|
cerror(UNEXPECT);
|
|
return(1);
|
|
}
|
|
num = 0;
|
|
do {
|
|
num = num*10 + (line[cursor]-'0');
|
|
} while (isdigit(line[++cursor]));
|
|
if ((num<lower) || (num>upper)) {
|
|
cerror(OUTOFBOUND);
|
|
return(1);
|
|
}
|
|
if (line[cursor]=='-') {
|
|
if (!isdigit(line[++cursor])) {
|
|
cerror(UNEXPECT);
|
|
return(1);
|
|
}
|
|
num2 = 0;
|
|
do {
|
|
num2 = num2*10 + (line[cursor]-'0');
|
|
} while (isdigit(line[++cursor]));
|
|
if ((num2<lower) || (num2>upper)) {
|
|
cerror(OUTOFBOUND);
|
|
return(1);
|
|
}
|
|
}
|
|
if ((line[cursor]==' ') || (line[cursor]=='\t')) break;
|
|
if (line[cursor]=='\0') {
|
|
cerror(EOLN);
|
|
return(1);
|
|
}
|
|
if (line[cursor++]!=',') {
|
|
cerror(UNEXPECT);
|
|
return(1);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
/**********/
|
|
cerror(msg)
|
|
/**********/
|
|
char *msg;
|
|
{
|
|
fprintf(stderr,"%scrontab: error on previous line; %s\n",line,msg);
|
|
err=1;
|
|
}
|
|
|
|
|
|
/**********/
|
|
catch()
|
|
/**********/
|
|
{
|
|
unlink(tnam);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/**********/
|
|
crabort(msg)
|
|
/**********/
|
|
char *msg;
|
|
{
|
|
int sverrno;
|
|
|
|
if (strcmp(edtemp, "") != 0) {
|
|
sverrno = errno;
|
|
(void)unlink(edtemp);
|
|
errno = sverrno;
|
|
}
|
|
if (tnam != NULL) {
|
|
sverrno = errno;
|
|
(void)unlink(tnam);
|
|
errno = sverrno;
|
|
}
|
|
fprintf(stderr,"crontab: %s\n",msg);
|
|
exit(1);
|
|
}
|
|
|
|
/****************/
|
|
crabortperror(msg)
|
|
/****************/
|
|
char *msg;
|
|
{
|
|
int sverrno;
|
|
|
|
if (strcmp(edtemp, "") != 0) {
|
|
sverrno = errno;
|
|
(void)unlink(edtemp);
|
|
errno = sverrno;
|
|
}
|
|
fprintf(stderr,"crontab: %s: %s\n", msg, errmsg(errno));
|
|
exit(1);
|
|
}
|
|
|
|
/***********/
|
|
sendmsg(action,fname)
|
|
/****************/
|
|
char action;
|
|
char *fname;
|
|
{
|
|
|
|
static int msgfd = -2;
|
|
struct message *pmsg;
|
|
register int i;
|
|
|
|
pmsg = &msgbuf;
|
|
if(msgfd == -2)
|
|
if((msgfd = open(FIFO,O_WRONLY|O_NDELAY)) < 0) {
|
|
if(errno == ENXIO || errno == ENOENT)
|
|
fprintf(stderr,"cron may not be running - call your system administrator\n");
|
|
else
|
|
perror("crontab: error in message queue open");
|
|
return;
|
|
}
|
|
pmsg->etype = CRON;
|
|
pmsg->action = action;
|
|
strncpy(pmsg->fname,fname,FLEN);
|
|
if((i = write(msgfd,pmsg,sizeof(struct message))) < 0)
|
|
perror("crontab: error in message send");
|
|
else if (i != sizeof(struct message))
|
|
fprintf(stderr,"crontab: error in message send: Premature EOF\n");
|
|
}
|