V10/cmd/upas/smtp/smtpsched.i


















































typedef	unsigned char	 u_char;
typedef	unsigned short	 u_short;
typedef	unsigned int	 u_int;
typedef	unsigned long	 u_long;
typedef	unsigned short	 ushort;		



typedef	struct	 _physadr { int  r[1]; } * physadr;




typedef	struct	 label_t	{
	int	val[12];
}  label_t;

typedef	struct	 _quad { long val[2]; }  quad;
typedef	long	 daddr_t;
typedef	char *	 caddr_t;
typedef	 u_long	 ino_t;
typedef	long	 swblk_t;
typedef	long	 size_t;
typedef	long	 time_t;
typedef	short	 dev_t;
typedef	long	 off_t;
typedef	 u_short	 uid_t;
typedef	 u_short	 gid_t;












typedef long	 fd_mask;





typedef	struct  fd_set {
	 fd_mask	fds_bits[	(((256)+(( (sizeof( fd_mask) * 8		)	)-1))/( (sizeof( fd_mask) * 8		)	))];
}  fd_set;












































extern	struct	 _iobuf {
	int	_cnt;

	unsigned char	*_ptr;
	unsigned char	*_base;

	int	_bufsiz;
	short	_flag;
	char	_file;		

	char	*_name;		

}  _iob[];






































struct  _iobuf	* fopen();
struct  _iobuf	* fdopen();
struct  _iobuf	* freopen();
long		 ftell();
char		* fgets();

struct  _iobuf	* popen();
char		* gets();

























































extern	char	 _ctype_[];








































































































	




























int	(* signal())();





struct	 sigvec {
	int	(*sv_handler)();	
	int	sv_mask;		
	int	sv_flags;		
};







struct	 sigstack {
	char	*ss_sp;			
	int	ss_onstack;		
};











struct  sigcontext {
	



	int	sc_onstack;		
	int	sc_mask;		
	int	sc_pc;			
	


	int	sc_regs[32];	
	int	sc_mdlo;	
	int	sc_mdhi;	
	


	int	sc_ownedfp;	
	int	sc_fpregs[32];	
	int	sc_fpc_csr;	
	int	sc_fpc_eir;	
	



	


	int	sc_cause;	
	int	sc_badvaddr;	
	int	sc_badpaddr;	
};


































































































struct	 stat
{
	 dev_t	st_dev;
	 ino_t	st_ino;
	unsigned 	short st_mode;
	short		st_nlink;
	 uid_t	st_uid;
	 gid_t	st_gid;
	 dev_t	st_rdev;
	 off_t	st_size;
	 time_t	st_atime;
	int		st_spare1;
	 time_t	st_mtime;
	int		st_spare2;
	 time_t	st_ctime;
	int		st_spare3;
	long		st_blksize;
	long		st_blocks;
	long		st_spare4[2];
};






































































































































































extern char *strcat();
extern char *strncat();
extern char *strcpy();
extern char *strncpy();
extern int strlen();
extern int strcmp();
extern int strncmp();
extern char *strchr();
extern char *strrchr();


typedef struct string {
	char *base;	
	char *end;	
	char *ptr;	
} string;










extern string *s_new();
extern void s_free();
extern string *s_append();
extern string *s_nappend();
extern string *s_array();
extern string *s_copy();
extern string *s_seq_read();
extern char *s_read_line();
extern int s_read_to_eof();
extern string *s_parse();















typedef struct namelist namelist;
struct namelist {
	namelist *next;
	char *name;
};


































































































































































































































































































































































































































































































































struct	 direct {
	 u_long	d_fileno;		
	 u_short	d_reclen;		
	 u_short	d_namlen;		
	char	d_name[255 + 1];	
};

















typedef struct  _dirdesc {
	int	dd_fd;
	long	dd_loc;
	long	dd_size;
	long	dd_bbase;
	long	dd_entno;
	long	dd_bsize;
	char	*dd_buf;
}  DIR;


extern	 DIR * opendir();
extern	struct  direct * readdir();
extern	long  telldir();
extern	void  seekdir();

extern	void  closedir();





























typedef int (*SIG_TYP)();















extern char *UPASROOT;
int warn = -1;
int remove = -1;
int cleanup;
int verbose;
int testmode;
int Xonly;
int Conly;
string *replyaddr;
string *dest;
int mypid;
char **getcmd();



struct {
	string	*dest;
	time_t	time;
} destlist[50		];		
int	ndest = 0;

int	debug;





















usage()
{
	fprintf((& _iob[2]), "smtpsched [-cvt] [-w #days] [-r #days] [-s #scheds] [dir]\n");
	exit(1);
}

main(ac, av)
	int ac;
	char *av[];
{
	direct *dirp;
	direct d;
	direct *dp;
	int max=0;
	int c;
	extern int optind;
	extern char *optarg;

	mypid = getpid();

	


	signal(13	, (int (*)())1);
	signal(1	, (int (*)())1);

	while ((c = getopt(ac, av, "XCcvtr:w:s:d")) != (-1))
		switch (c) {
		case 'X':
			Xonly = 1;
			break;
		case 'C':
			Conly = 1;
			break;
		case 't':
			testmode = 1;
			break;
		case 'v':
			verbose = 1;
			break;
		case 'c':
			cleanup = 1;
			break;
		case 'r':
			remove = atoi(optarg);
			break;
		case 's':
			max = atoi(optarg);
			break;
		case 'w':
			warn = atoi(optarg);
			break;
		case 'd':
			debug = 1;
			break;
		default:
			usage();
		}
	openlog();

	


	if(chdir("/usr/spool/smtpq")<0){
		log("can't chdir to %s\n", "/usr/spool/smtpq");
		exit(1);
	}

	


	if(max && toomany(max)<0)
		exit(0);

	



	if(optind!=ac){
		for(; optind<ac; optind++)
			while(dodir(av[optind], 1		) && !warn && !remove)
				;
		return 0;
	}

	





	dirp = opendir(".");
	if(dirp<0){
		log("couldn't read %s\n", "/usr/spool/smtpq");
		exit(1);
	}
	while(dp = readdir(dirp)){
		d = *dp;	
		if(strcmp(d.d_name, ".")!=0 && strcmp(d.d_name, "..")!=0)
			dodir(d.d_name, (warn>=0 || remove>=0) ? 2	 : 1		);
	}
	closedir(dirp);
	return 0;
}




dodir(dname, action)
	char *dname;
{
	int i;

	i = dodirdir(dname, action, "X.");
	i += dodirdir(dname, action, "C.");
	return i;
}





dodirdir(dname, action, direction)
	char *dname;
	char *direction;
{
	DIR *dirp;
	direct *dp;
	int i;
	int changed=0;
	int ents=0;
	static string *ds;
	static string *ls;
	extern int errno;

	ds =  (ds ? (*(ds->ptr = ds->base) = '\0' , ds) : s_new());
	s_append(ds, dname);
	ls =  (ls ? (*(ls->ptr = ls->base) = '\0' , ls) : s_new());
	s_append(ls, direction);
	s_append(ls, dname);

	


	switch(action){
	case 0		:
		for(i=0; i<3; i++){
			if(lock( ls->base)==0)
				break;
			sleep(5);
		}
		if(i==3)
			return changed;
		break;
	case 1		:
		if(lock( ls->base)<0){
			log("couldn't lock %s\n", dname);
			return changed;
		}
		break;
	case 2	:
		break;
	}

	



	if(chdir( ds->base)<0){
		if(action != 2	)
			unlock( ls->base);
		return changed;
	}

	


	dirp = opendir(".");
	if(dirp<0){
		log("couldn't read directory %s\n", dname);
		if(chdir("..")<0){
			log("can't chdir ..\n");
			fflush((& _iob[1]));
			exit(1);
		}
		if(action != 2	)
			unlock( ls->base);
		return changed;
	}
	while(dp = readdir(dirp)){
		if(strcmp(dp->d_name, ".")!=0 && strcmp(dp->d_name, "..")!=0
		&& dp->d_name[0] == *direction) {
			switch(dofile(dname, dp->d_name)){
			case 0:
				
				changed = 1;
				break;
			case 1:
				
				ents += 1;
				break;
			}
		}
	}
	closedir(dirp);

	


	if(chdir("..")<0){
		log("can't chdir ..\n");
		fflush((& _iob[1]));
		exit(1);
	}

	


	if(cleanup && ents==0){
		log("%s empty\n",  ds->base);
		if(rmdir( ds->base)<0)
			log("can't unlink: %d\n", errno);
	}

	if(action != 2	)
		unlock( ls->base);
	return changed;
}







dofile(dname, name)
	char *dname;
	char *name;
{
	int rv;
	int fd, ofd;
	char *ef;
	extern char *fileoftype();
	struct stat sb;
	time_t now, Edate, Cdate;

	rv = -1;

	


	if(cleanup && inconsistent(name)){
		log("%s/%s inconsistent\n", dname, name);
		unlink(name);
		return 0;
	}

	


	if(name[1]!='.' || (name[0]!='C' && name[0]!='X'))
		return 1;

	



	if(warn>=0 || remove>=0) {
		if(checkage(name)==0) {
			log("%s/%s too old\n", dname, name);
			doremove(name);
			return 0;
		}
		return 1;
	}

	


	if(cleanup)
		return 1;

	


	ef = fileoftype('E', name);
	if (name[0]=='C') {
		now = time((time_t *)0);
		Cdate = now;
		Edate = now-4*3600L		-1;
		if (stat(name, &sb)==0)
			Cdate = sb.st_ctime;
		if (stat(ef, &sb)==0)
			Edate = sb.st_mtime;
		if (now-Cdate>6*3600L		 && now-Edate<4*3600L		
		 || now-Cdate>1*3600L		  && now-Edate<1*3600L		) {
			if (verbose)
				log("ignore %s/%s: not time yet\n", dname, name);
			if (debug==0)
				return 1;
		}
	}

	


	if (testmode) {
		log("would process %s/%s\n", dname, name);
		return 1;
	}
	


	ofd = dup(2);
	close(2);
	fd = open(ef, 1);
	if(fd<0)
		fd = creat(ef, 0666);
	if(fd>=0){
		lseek(fd, 0l, 2);
	
		


		if(name[0]=='C') {
			rv = dosmtp(dname, name);
		} else if(name[0]=='X') {
			rv = dormail(dname, name);
		}

		


		close(2);
		(void) dup(ofd);
		close(ofd);
	}

	


	if(rv==0) {
		doremove(name);
		return 0;
	}
	return 1;
}




doremove(ctl)
	char *ctl;
{
	fflush((& _iob[1]));
	unlink(fileoftype('E', ctl));
	unlink(ctl);
	unlink(fileoftype('D', ctl));
}





dormail(dname, ctl)
	char *dname;
	char *ctl;
{
	char **av;

	log("dormail %s/%s\n", dname, ctl);

	


	av = getcmd(ctl, "/bin/rmail");
	if(av && docmd(ctl, av)==0){
		log("success\n");
		return 0;
	} else {
		log("failure\n");
		return -1;
	}
}





dosmtp(dname, ctl)
	char *dname;
	char *ctl;
{
	static string *cmd;
	int status, i;
	char **av;
	time_t t0, t1;

	log("dosmtp %s/%s\n", dname, ctl);

	


	cmd =  (cmd ? (*(cmd->ptr = cmd->base) = '\0' , cmd) : s_new());
	s_append(cmd, UPASROOT);
	s_append(cmd, "/smtp");
	av = getcmd(ctl,  cmd->base);
	if (av==0) {
		log("cmdfail\n", 0);
		return -1;
	}
	



	for (i=0; i<ndest; i++) {
		if (strcmp( dest->base,  destlist[i].dest->base)==0) {
			if (destlist[i].time > 5*60		) {
				log("passed %s (%d sec)\n",  dest->base, destlist[i].time);
				fprintf((& _iob[2]), "can't contact destination\n");
				return -1;
			}
			break;
		}
	}
	if (i==ndest) {
		if (ndest<50		)
			ndest++;
		else
			i = 0;		
		destlist[i].dest = s_copy( dest->base);
		destlist[i].time = 0;
	}
	if (debug)
		log("time %d for %s\n", destlist[i].time,  dest->base);
	time(&t0);
	switch(status=docmd(ctl, av)){
	case 0:		
		log("success\n");
		destlist[i].time = 0;
		return 0;

	case 69	:	
	case 	77	:		
	case 	67	:		
	case 	68	:		
		log("fail %d\n", status);
		returnmail(ctl, 1);		
		destlist[i].time = 0;
		return 0;

	case 73	:	
	case 	74	:		
	case 	71	:		
	case 	72	:		
	case 70	:	
	case 	64	:		
	case 	65	:	
	case 	66	:	
	case 76	:	
	case 75	:	
		log("fail %d\n", status);
		time(&t1);			
		destlist[i].time += t1-t0;
		return -1;

	default:	
		log("fail %d\n", status);
		time(&t1);
		destlist[i].time += t1-t0;
		return -1;
	}
}







struct  _iobuf *
parseline1(ctl)
	char *ctl;
{
	struct  _iobuf *fp;
	static string *line;

	fp = fopen(ctl, "r");
	if(fp==0)
		return 0;

	


	line =  (line ? (*(line->ptr = line->base) = '\0' , line) : s_new());
	if(s_read_line(fp, line)==0){
		fprintf((& _iob[2]), "smtpsched: error reading ctl file %s\n", ctl);
		fclose(fp);
		return 0;
	}
	replyaddr = s_parse( (line->ptr = line->base , line),  (replyaddr ? (*(replyaddr->ptr = replyaddr->base) = '\0' , replyaddr) : s_new()));
	dest = s_parse(line,  (dest ? (*(dest->ptr = dest->base) = '\0' , dest) : s_new()));
	return fp;
}








char **
getcmd(ctl, cmd)
	char *cmd;
{
	static string *args;
	struct  _iobuf *fp;
	static char *av[1024];
	int ac=0;
	char *cp;

	fp = parseline1(ctl);
	if (fp==0)
		return 0;

	


	av[ac++] = cmd;
	args =  (args ? (*(args->ptr = args->base) = '\0' , args) : s_new());
	if(s_read_line(fp, args)==0){
		fprintf((& _iob[2]), "smtpsched: error reading ctl file %s\n", ctl);
		fclose(fp);
		return 0;
	}
	fclose(fp);
	for(cp =  args->base; *cp && ac<1023;){
		av[ac++] = cp++;
		while(*cp && !	(( _ctype_+1)[*cp]&010))
			cp++;
		while(	(( _ctype_+1)[*cp]&010))
			*cp++ = 0;
	}
	av[ac] = 0;
	return av;
}




docmd(ctl, av)
	char *ctl;
	char **av;
{
	int fd;
	int pid, status;
	int n;

	


	switch(pid = fork()){
	case -1:
		return 1;
	case 0:
		


		close(0);
		fd = open(fileoftype('D', ctl), 0);
		if(fd<0){
			perror("smtpsched: error reading data file:\n");
			exit(1);
		}
	
		


		close(1);
		fd = dup(2);
	
		


		execvp(av[0], av);
		exit(1);
	default:
		


		while((n = wait(&status))>=0)
			if(n == pid)
				break;
		if(status&0xff)
			return -1;
		else
			return (status>>8)&0xff;
	}

}







toomany(max)
	int max;
{
	struct  _iobuf *ifp=0;
	struct  _iobuf *ofp=0;
	int cur=0;
	int pid;

	


	if(lock(".smtpscheds")<0)
		return -1;

	


	ofp = fopen(".nsmtpscheds", "w");
	if(ofp==0){
		fprintf((& _iob[2]), "can't open %s\n", ".nsmtpscheds");
		goto error;
	}
	ifp = fopen(".smtpscheds", "r");
	if(ifp!=0){
		


		while(fscanf(ifp, "%d", &pid)==1){
			if(kill(pid, 0) == 0){
				cur++;
				if(fprintf(ofp, "%d\n", pid)<0){
					fprintf((& _iob[2]), "error writing %s\n", ".nsmtpscheds");
					goto error;
				}
			}
		}
		if(cur >= max)
			goto error;
	}

	


	if(fprintf(ofp, "%d\n", getpid())<0){
		fprintf((& _iob[2]), "error writing %s\n", ".nsmtpscheds");
		goto error;
	}
	if(ifp!=0)
		fclose(ifp);
	if(fclose(ofp)==(-1))
		goto error;
	unlink(".smtpscheds");
	if(link(".nsmtpscheds", ".smtpscheds")<0)
		fprintf((& _iob[2]), "can't link %s to %s file\n", ".smtpscheds", ".nsmtpscheds");
	unlink(".nsmtpscheds");
	unlock(".smtpscheds");
	return 0;
error:
	


	if(ifp!=0)
		fclose(ifp);
	if(ofp!=0)
		fclose(ofp);
	unlink(".nsmtpscheds");
	unlock(".smtpscheds");
	return -1;
}








inconsistent(file)
	char *file;
{
	struct stat s;
	int days;

	


	switch(file[0]){
	case 'C':
	case 'X':
		


		if(stat(fileoftype('D', file), &s)<0)
			return 1;
		break;
	case 'E':
		


		if(stat(fileoftype('X', file), &s)<0
		&& stat(fileoftype('C', file), &s)<0)
			return 1;

		


		if(stat(fileoftype('D', file), &s)<0)
			return 1;
		break;
	case 'D':
		


		if(stat(fileoftype('X', file), &s)==0
		|| stat(fileoftype('C', file), &s)==0)
			break;

		


		if(stat(file, &s)<0)
			return 0;
		days = (time((long *)0) - s.st_ctime)/(24*60*60);
		if(days>0)
			return 1;
		break;
	default:
		break;
	}
	return 0;
}





checkage(ctl)
	char *ctl;
{
	struct stat s;
	int days;
	char buf[256];
	struct  _iobuf *fp;

	


	if(stat(ctl, &s)<0)
		return -1;
	days = (time((long *)0) - s.st_ctime)/(24*60*60);

	


	if(remove>=0 && days>=remove){
		fp = parseline1(ctl);
		if(fp==0)
			return -1;
		fclose(fp);

		log("r %d %d\n", days, remove);
		return returnmail(ctl, 1);
	}

	


	if(warn>=0 && days>=warn){
		fp = parseline1(ctl);
		if(fp==0)
			return -1;
		fclose(fp);

		log("w %d %d\n", days, warn);
		returnmail(ctl, 0);
	}

	return -1;
}




returnmail(ctl, warn)
	char *ctl;
{
	int pid, status;
	string *cmd;
	int pfd[2];
	char buf[132];
	int fd, n;
	int reads;
	struct  _iobuf *fp;
	struct  _iobuf *ifp;
	long now;
	char asct[27];

	log("returnmail %s to %s about %s\n", ctl,  replyaddr->base,
		 dest->base);

	if(pipe(pfd)<0)
		return -1;

	switch(pid=fork()){
	case -1:
		close(pfd[0]);
		close(pfd[1]);
		return -1;
	case 0:
		


		close(0);
		dup(pfd[0]);
		close(pfd[1]);
		execl("/bin/rmail", "/bin/rmail",   replyaddr->base, 0);
		exit(1);
	default:
		


		close(pfd[0]);
		fp = fdopen(pfd[1], "w");
		if(fp==0) {
			close(pfd[1]);
			break;
		}

		


		now = time((long *)0);
		strcpy(asct, ctime(&now));
		asct[24] = 0;
		fprintf(fp, "From postmaster %s remote from \n", asct);

		


		if(warn) {
			fprintf(fp, "Subject: smtp mail failed\n\n");
			fprintf(fp, "Your mail to %s is undeliverable.\n",
				 dest->base);
		} else {
			fprintf(fp, "Subject: smtp mail warning\n\n");
			fprintf(fp, "Your mail to %s is not yet delivered.\n",
				 dest->base);
			fprintf(fp, "Delivery attempts continue.\n");
		}

		


		fprintf(fp, "---------- diagnosis ----------\n");
		ifp = fopen(fileoftype('E', ctl), "r");
		if(ifp!=0){
			for(reads=0; reads<20; reads++) {
				if(fgets(buf, sizeof(buf), ifp)==0)
					break;
				fputs(buf, fp);
			}
			fclose(ifp);
		}

		


		fprintf(fp, "---------- unsent mail ----------\n");
		ifp = fopen(fileoftype('D', ctl), "r");
		if(ifp!=0){
			for(reads=0; reads<50; reads++) {
				if(fgets(buf, sizeof(buf), ifp)==0)
					break;
				fputs(buf, fp);
			}
			fclose(ifp);
		}
		fclose(fp);

		


		while((n = wait(&status))>=0)
			if(n == pid)
				break;
		return status ? -1 : 0;
	}
	close(pfd[1]);
	return -1;
}

openlog()
{
	string *f;

	if (debug)
		return;
	f = s_new();
	s_append(f, "/usr/spool/smtpq");
	s_append(f, "/smtpsched.log");
	close(0);
	if(open("/dev/null", 0)==0){
		close(1);
		close(2);
		close(3);
		if(open( f->base, 2)==1
		|| creat( f->base, 0666)==1){
			lseek(1, 0L, 2);
			dup(1);
			dup(1);
		}
	}
	s_free(f);
}

log(f, a, b, c)
	char *f;
{
	char *dp;
	struct tm *bp;
	long thetime;
	extern struct tm *localtime();
	extern char *asctime();
	extern long time();

	thetime = time((long *)0);
	bp = localtime(&thetime);
	dp = asctime(bp);

	lseek(1, 0L, 2);
	printf("%d %.12s: ", mypid, dp+4);
	printf(f, a, b, c);
	fflush((& _iob[1]));
}