xref: /original-bsd/usr.bin/uucp/uuxqt/uuxqt.c (revision bdd86a84)
1 #ifndef lint
2 static char sccsid[] = "@(#)uuxqt.c	5.12	(Berkeley) 05/13/88";
3 #endif
4 
5 #include "uucp.h"
6 #include <sys/stat.h>
7 #ifdef	NDIR
8 #include "ndir.h"
9 #else
10 #include <sys/dir.h>
11 #endif
12 #include <signal.h>
13 
14 #define BADCHARS	"&^|(`\\<>;\"{}\n'"
15 #define RECHECKTIME	60*10	/* 10 minutes */
16 
17 #define APPCMD(d) {\
18 char *p;\
19 for (p = d; *p != '\0';) *cmdp++ = *p++; *cmdp++ = ' '; *cmdp = '\0';}
20 
21 extern char Filent[LLEN][NAMESIZE];
22 
23 /*
24  *	uuxqt will execute commands set up by a uux command,
25  *	usually from a remote machine - set by uucp.
26  */
27 
28 #define	NCMDS	50
29 char *Cmds[NCMDS+1];
30 int Notify[NCMDS+1];
31 #define	NT_YES	0	/* if should notify on execution */
32 #define	NT_ERR	1	/* if should notify if non-zero exit status (-z equivalent) */
33 #define	NT_NO	2	/* if should not notify ever (-n equivalent) */
34 
35 extern int Nfiles;
36 
37 int TransferSucceeded = 1;
38 int notiok = 1;
39 int nonzero = 0;
40 
41 struct timeb Now;
42 
43 char PATH[MAXFULLNAME] = "PATH=/bin:/usr/bin:/usr/ucb";
44 char UU_MACHINE[MAXFULLNAME];
45 char Shell[MAXFULLNAME];
46 char HOME[MAXFULLNAME];
47 
48 extern char **environ;
49 char *nenv[] = {
50 	PATH,
51 	Shell,
52 	HOME,
53 	UU_MACHINE,
54 	0
55 };
56 
57 /*  to remove restrictions from uuxqt
58  *  define ALLOK 1
59  *
60  *  to add allowable commands, add to the file CMDFILE
61  *  A line of form "PATH=..." changes the search path
62  */
63 main(argc, argv)
64 char *argv[];
65 {
66 	char xcmd[BUFSIZ*2];
67 	int argnok;
68 	int notiflg;
69 	char xfile[MAXFULLNAME], user[MAXFULLNAME], buf[BUFSIZ*2];
70 	char lbuf[MAXFULLNAME];
71 	char cfile[NAMESIZE], dfile[MAXFULLNAME];
72 	char file[NAMESIZE];
73 	char fin[MAXFULLNAME], sysout[NAMESIZE], fout[MAXFULLNAME];
74 	register FILE *xfp, *fp;
75 	FILE *dfp;
76 	char path[MAXFULLNAME];
77 	char cmd[BUFSIZ*2];
78 	char *cmdp, prm[1000], *ptr;
79 	char *getprm(), *lastpart();
80 	int uid, ret, badfiles;
81 	register int i;
82 	int stcico = 0;
83 	time_t xstart, xnow;
84 	char retstat[30];
85 	extern char *optarg;
86 	extern int optind;
87 
88 	strcpy(Progname, "uuxqt");
89 	uucpname(Myname);
90 	strcpy(Rmtname, Myname);
91 
92 	umask(WFMASK);
93 	Ofn = 1;
94 	Ifn = 0;
95 	while ((i = getopt(argc, argv, "x:S:")) != EOF)
96 		switch(i) {
97 		case 'x':
98 			chkdebug();
99 			Debug = atoi(optarg);
100 			if (Debug <= 0)
101 				Debug = 1;
102 			break;
103 		case 'S':
104 			Spool = optarg;
105 			DEBUG(1, "Spool set to %s", Spool);
106 			break;
107 		case '?':
108 		default:
109 			fprintf(stderr, "unknown flag %s\n", argv[optind-1]);
110 				break;
111 		}
112 
113 	DEBUG(4, "\n\n** START **\n", CNULL);
114 	if (subchdir(Spool) < 0) {
115 		syslog(LOG_WARNING, "chdir(%s) failed: %m", Spool);
116 		cleanup(1);
117 	}
118 	strcpy(Wrkdir, Spool);
119 	uid = getuid();
120 	if (guinfo(uid, User, path) != SUCCESS) {
121 		syslog(LOG_WARNING, "Can't find username for uid %d", uid);
122 		DEBUG(1, "Using username", "uucp");
123 		strcpy(User, "uucp");
124 	}
125 	setgid(getegid());
126 	setuid(geteuid());
127 
128 	DEBUG(4, "User - %s\n", User);
129 	if (ulockf(X_LOCK, X_LOCKTIME) != 0)
130 		exit(0);
131 
132 	fp = fopen(CMDFILE, "r");
133 	if (fp == NULL) {
134 		logent(CANTOPEN, CMDFILE);
135 		Cmds[0] = "rmail";
136 		Cmds[1] = "rnews";
137 		Cmds[2] = "ruusend";
138 		Cmds[3] = NULL;
139 		goto doprocess;
140 	}
141 	DEBUG(5, "%s opened\n", CMDFILE);
142 	for (i=0; i<NCMDS && cfgets(xcmd, sizeof(xcmd), fp) != NULL; i++) {
143 		int j;
144 		/* strip trailing whitespace */
145 		for (j = strlen(xcmd)-1; j >= 0; --j)
146 			if (xcmd[j] == '\n' || xcmd[j] == ' ' || xcmd[j] == '\t')
147 				xcmd[j] = '\0';
148 			else
149 				break;
150 		/* look for imbedded whitespace */
151 		for (; j >= 0; --j)
152 			if (xcmd[j] == '\n' || xcmd[j] == ' ' || xcmd[j] == '\t')
153 				break;
154 		/* skip this entry if it has embedded whitespace */
155 		/* This defends against a bad PATH=, for example */
156 		if (j >= 0) {
157 			logent(xcmd, "BAD WHITESPACE");
158 			continue;
159 		}
160 		if (strncmp(xcmd, "PATH=", 5) == 0) {
161 			strcpy(PATH, xcmd);
162 			i--;	/*kludge */
163 			continue;
164 		}
165 		DEBUG(5, "xcmd = %s\n", xcmd);
166 
167 		if ((ptr = index(xcmd, ',')) != NULL) {
168 			*ptr++ = '\0';
169 			if (strncmp(ptr, "Err", 3) == SAME)
170 				Notify[i] = NT_ERR;
171 			else if (strcmp(ptr, "No") == SAME)
172 				Notify[i] = NT_NO;
173 			else
174 				Notify[i] = NT_YES;
175 		} else
176 			Notify[i] = NT_YES;
177 		if ((Cmds[i] = malloc((unsigned)(strlen(xcmd)+1))) == NULL) {
178 			DEBUG(1, "MALLOC FAILED", CNULL);
179 			break;
180 		}
181 		strcpy(Cmds[i], xcmd);
182 	}
183 	Cmds[i] = CNULL;
184 	fclose(fp);
185 
186 doprocess:
187 
188 	(void) sprintf(HOME, "HOME=%s", Spool);
189 	(void) sprintf(Shell, "SHELL=%s", SHELL);
190 	environ = nenv; /* force use if our environment */
191 
192 	DEBUG(11,"path = %s\n", getenv("PATH"));
193 
194 	DEBUG(4, "process %s\n", CNULL);
195 
196 	time(&xstart);
197 	while (gtxfile(xfile) > 0) {
198 		/* if /etc/nologin exists, exit cleanly */
199 #if defined(BSD4_2) || defined(USG)
200 		if (access(NOLOGIN) == 0) {
201 #else !BSD4_2 && ! USG
202 		ultouch();
203 		if (nologinflag) {
204 #endif !BSD4_2 && !USG
205 			logent(NOLOGIN, "UUXQT SHUTDOWN");
206 			if (Debug)
207 				logent("debugging", "continuing anyway");
208 			else
209 				break;
210 		}
211 		DEBUG(4, "xfile - %s\n", xfile);
212 
213 		xfp = fopen(subfile(xfile), "r");
214 		if (xfp == NULL) {
215 			syslog(LOG_ERR, "fopen(%s) failed: %m", subfile(xfile));
216 			cleanup(1);
217 		}
218 
219 		/*  initialize to default  */
220 		strcpy(user, User);
221 		strcpy(fin, DEVNULL);
222 		strcpy(fout, DEVNULL);
223 		strcpy(sysout, Myname);
224 		badfiles = 0;
225 		while (fgets(buf, BUFSIZ, xfp) != NULL) {
226 			if(buf[0] != '\0' && buf[0] != '#' &&
227 			    buf[1] != ' ' && buf[1] != '\0' && buf[1] != '\n') {
228 				char *bnp, cfilename[BUFSIZ];
229 				DEBUG(4, "uuxqt: buf = %s\n", buf);
230 				bnp = rindex(xfile, '/');
231 				sprintf(cfilename, "%s/%s", CORRUPT,
232 					bnp ? bnp + 1 : xfile);
233 				DEBUG(4, "uuxqt: move %s to ", xfile);
234 				DEBUG(4, "%s\n", cfilename);
235 				xmv(xfile, cfilename);
236 				syslog(LOG_WARNING, "%s: X. FILE CORRUPTED",
237 					xfile);
238 				fclose(xfp);
239 				goto doprocess;
240 			}
241 			switch (buf[0]) {
242 			case X_USER: {
243 				char ORmtname[MAXFULLNAME];
244 				strcpy(ORmtname, Rmtname);
245 				sscanf(&buf[1], "%s %s", user, Rmtname);
246 				sprintf(UU_MACHINE, "UU_MACHINE=%s", Rmtname);
247 				if (strcmp(ORmtname, Rmtname) != 0)
248 					logcls();
249 				break;}
250 			case X_RETURNTO:
251 				sscanf(&buf[1], "%s", user);
252 				break;
253 			case X_STDIN:
254 				sscanf(&buf[1], "%s", fin);
255 				i = expfile(fin);
256 				/* rti!trt: do not check permissions of
257 				 * vanilla spool file */
258 				if (i != 0
259 				 && (chkpth("", "", fin) || anyread(fin) != 0))
260 					badfiles = 1;
261 				break;
262 			case X_STDOUT:
263 				sscanf(&buf[1], "%s%s", fout, sysout);
264 				sysout[MAXBASENAME] = '\0';
265 				/* rti!trt: do not check permissions of
266 				 * vanilla spool file.  DO check permissions
267 				 * of writing on a non-vanilla file */
268 				i = 1;
269 				if (fout[0] != '~' || prefix(sysout, Myname))
270 					i = expfile(fout);
271 				if (i != 0
272 				 && (chkpth("", "", fout)
273 					|| chkperm(fout, (char *)1)))
274 					badfiles = 1;
275 				break;
276 			case X_CMD:
277 				strcpy(cmd, &buf[2]);
278 				if (*(cmd + strlen(cmd) - 1) == '\n')
279 					*(cmd + strlen(cmd) - 1) = '\0';
280 				break;
281 			case X_NONOTI:
282 				notiok = 0;
283 				break;
284 			case X_NONZERO:
285 				nonzero = 1;
286 				break;
287 			default:
288 				break;
289 			}
290 		}
291 
292 		fclose(xfp);
293 		DEBUG(4, "fin - %s, ", fin);
294 		DEBUG(4, "fout - %s, ", fout);
295 		DEBUG(4, "sysout - %s, ", sysout);
296 		DEBUG(4, "user - %s\n", user);
297 		DEBUG(4, "cmd - %s\n", cmd);
298 
299 		/*  command execution  */
300 		if (strcmp(fout, DEVNULL) == SAME)
301 			strcpy(dfile,DEVNULL);
302 		else
303 			gename(DATAPRE, sysout, 'O', dfile);
304 
305 		/* expand file names where necessary */
306 		expfile(dfile);
307 		cmdp = buf;
308 		ptr = cmd;
309 		xcmd[0] = '\0';
310 		argnok = 0;
311 		while ((ptr = getprm(ptr, prm)) != NULL) {
312 			if (prm[0] == ';' || prm[0] == '^'
313 			  || prm[0] == '&'  || prm[0] == '|') {
314 				xcmd[0] = '\0';
315 				APPCMD(prm);
316 				continue;
317 			}
318 
319 			if ((argnok = argok(xcmd, prm)) != SUCCESS)
320 				/*  command not valid  */
321 				break;
322 
323 			if (prm[0] == '~')
324 				expfile(prm);
325 			APPCMD(prm);
326 		}
327 		/*
328 		 * clean up trailing ' ' in command.
329 		 */
330 		if (cmdp > buf && cmdp[0] == '\0' && cmdp[-1] == ' ')
331 			*--cmdp = '\0';
332 		if (argnok || badfiles) {
333 			sprintf(lbuf, "%s XQT DENIED", user);
334 			logent(cmd, lbuf);
335 			DEBUG(4, "bad command %s\n", prm);
336 			notify(user, Rmtname, cmd, "DENIED");
337 			goto rmfiles;
338 		}
339 		sprintf(lbuf, "%s XQT", user);
340 		logent(buf, lbuf);
341 		DEBUG(4, "cmd %s\n", buf);
342 
343 		mvxfiles(xfile);
344 		if (subchdir(XQTDIR) < 0) {
345 			syslog(LOG_ERR, "chdir(%s) failed: %m", XQTDIR);
346 			cleanup(1);
347 		}
348 		ret = shio(buf, fin, dfile);
349 		sprintf(retstat, "signal %d, exit %d", ret & 0377,
350 		  (ret>>8) & 0377);
351 		if (strcmp(xcmd, "rmail") == SAME)
352 			notiok = 0;
353 		if (strcmp(xcmd, "rnews") == SAME)
354 			nonzero = 1;
355 		notiflg = chknotify(xcmd);
356 		if (notiok && notiflg != NT_NO &&
357 		   (ret != 0 || (!nonzero && notiflg == NT_YES)))
358 			notify(user, Rmtname, cmd, retstat);
359 		else if (ret != 0 && strcmp(xcmd, "rmail") == SAME) {
360 			/* mail failed - return letter to sender  */
361 #ifdef	DANGEROUS
362 			/* NOT GUARANTEED SAFE!!! */
363 			if (!nonzero)
364 				retosndr(user, Rmtname, fin);
365 #else
366 			notify(user, Rmtname, cmd, retstat);
367 #endif
368 			sprintf(buf, "%s (%s) from %s!%s", buf, retstat, Rmtname, user);
369 			logent("MAIL FAIL", buf);
370 		}
371 		DEBUG(4, "exit cmd - %d\n", ret);
372 		if (subchdir(Spool) < 0) {
373 			syslog(LOG_ERR, "chdir(%s) failed: %m", Spool);
374 			cleanup(1);
375 		}
376 		rmxfiles(xfile);
377 		if (ret != 0) {
378 			/*  exit status not zero */
379 			dfp = fopen(subfile(dfile), "a");
380 			if (dfp == NULL) {
381 				syslog(LOG_ERR, "fopen(%s) failed: %m",
382 					subfile(dfile));
383 				cleanup(1);
384 			}
385 			fprintf(dfp, "exit status %d", ret);
386 			fclose(dfp);
387 		}
388 		if (strcmp(fout, DEVNULL) != SAME) {
389 			if (prefix(sysout, Myname)) {
390 				xmv(dfile, fout);
391 				chmod(fout, BASEMODE);
392 			} else {
393 				char *cp = rindex(user, '!');
394 				gename(CMDPRE, sysout, 'O', cfile);
395 				fp = fopen(subfile(cfile), "w");
396 				if (fp == NULL) {
397 					syslog(LOG_ERR, "fopen(%s) failed: %m",
398 						subfile(cfile));
399 					cleanup(1);
400 				}
401 				fprintf(fp, "S %s %s %s - %s 0666\n", dfile,
402 					fout, cp ? cp : user, lastpart(dfile));
403 				fclose(fp);
404 			}
405 		}
406 	rmfiles:
407 		xfp = fopen(subfile(xfile), "r");
408 		if (xfp == NULL) {
409 			syslog(LOG_ERR, "fopen(%s) failed: %m",
410 				subfile(xfile));
411 			cleanup(1);
412 		}
413 		while (fgets(buf, BUFSIZ, xfp) != NULL) {
414 			if (buf[0] != X_RQDFILE)
415 				continue;
416 			sscanf(&buf[1], "%s", file);
417 			unlink(subfile(file));
418 		}
419 		unlink(subfile(xfile));
420 		fclose(xfp);
421 
422 		/* rescan X. for new work every RECHECKTIME seconds */
423 		time(&xnow);
424 		if (xnow > (xstart + RECHECKTIME)) {
425 			extern int Nfiles;
426 			Nfiles = 0; 	/*force rescan for new work */
427 		}
428 		xstart = xnow;
429 	}
430 
431 	if (stcico)
432 		xuucico("");
433 	cleanup(0);
434 }
435 
436 
437 cleanup(code)
438 int code;
439 {
440 	logcls();
441 	rmlock(CNULL);
442 #ifdef	VMS
443 	/*
444 	 *	Since we run as a BATCH job we must wait for all processes to
445 	 *	to finish
446 	 */
447 	while(wait(0) != -1)
448 		;
449 #endif VMS
450 	exit(code);
451 }
452 
453 
454 /*
455  *	get a file to execute
456  *
457  *	return codes:  0 - no file  |  1 - file to execute
458  */
459 
460 gtxfile(file)
461 register char *file;
462 {
463 	char pre[3];
464 	register int rechecked, i;
465 	time_t ystrdy;		/* yesterday */
466 	struct stat stbuf;	/* for X file age */
467 
468 	pre[0] = XQTPRE;
469 	pre[1] = '.';
470 	pre[2] = '\0';
471 	rechecked = 0;
472 retry:
473 	if (Nfiles-- <= 0) {
474 		Nfiles = 0;
475 		if (rechecked)
476 			return 0;
477 		rechecked = 1;
478 		DEBUG(4, "iswrk\n", CNULL);
479 		return iswrk(file, "get", Spool, pre);
480 	}
481 	sprintf(file, "%s/%s", Spool, Filent[0]);
482 	for (i=0; i<Nfiles;i++)
483 		strcpy(Filent[i], Filent[i+1]);
484 
485 	DEBUG(4, "file - %s\n", file);
486 	/* skip spurious subdirectories */
487 	if (strcmp(pre, file) == SAME)
488 		goto retry;
489 	if (gotfiles(file))
490 		return 1;
491 	/* check for old X. file with no work files and remove them. */
492 	if (Nfiles > LLEN/2) {
493 	    time(&ystrdy);
494 	    ystrdy -= (4 * 3600L);		/* 4 hours ago */
495 	    DEBUG(4, "gtxfile: Nfiles > LLEN/2\n", CNULL);
496 	    while (Nfiles-- > 0) {
497 		sprintf(file, "%s/%s", Spool, Filent[0]);
498 		for (i=0; i<Nfiles; i++)
499 			strcpy(Filent[i], Filent[i+1]);
500 
501 		if (gotfiles(file))
502 			return 1;
503 		if (stat(subfile(file), &stbuf) == 0)
504 		    if (stbuf.st_mtime <= ystrdy) {
505 			char *bnp, cfilename[NAMESIZE];
506 			DEBUG(4, "gtxfile: move %s to CORRUPT \n", file);
507 			bnp = rindex(file, '/');
508 			sprintf(cfilename, "%s/%s", CORRUPT,
509 				bnp ? bnp + 1 : file);
510 			xmv(file, cfilename);
511 			syslog(LOG_WARNING, "%s: X. FILE MISSING FILES", file);
512 		    }
513 	    }
514  	    Nfiles = 0;
515 	    DEBUG(4, "iswrk\n", CNULL);
516 	    if (!iswrk(file, "get", Spool, pre))
517 		return 0;
518 	}
519 	goto retry;
520 }
521 
522 /*
523  *	check for needed files
524  *
525  *	return codes:  0 - not ready  |  1 - all files ready
526  */
527 
528 gotfiles(file)
529 register char *file;
530 {
531 	struct stat stbuf;
532 	register FILE *fp;
533 	char buf[BUFSIZ], rqfile[MAXFULLNAME];
534 
535 	fp = fopen(subfile(file), "r");
536 	if (fp == NULL)
537 		return 0;
538 
539 	while (fgets(buf, BUFSIZ, fp) != NULL) {
540 		DEBUG(4, "%s\n", buf);
541 		if (buf[0] != X_RQDFILE)
542 			continue;
543 		sscanf(&buf[1], "%s", rqfile);
544 		expfile(rqfile);
545 		if (stat(subfile(rqfile), &stbuf) == -1) {
546 			fclose(fp);
547 			return 0;
548 		}
549 	}
550 
551 	fclose(fp);
552 	return 1;
553 }
554 
555 
556 /*
557  *	remove execute files to x-directory
558  */
559 
560 rmxfiles(xfile)
561 register char *xfile;
562 {
563 	register FILE *fp;
564 	char buf[BUFSIZ], file[NAMESIZE], tfile[NAMESIZE];
565 	char tfull[MAXFULLNAME];
566 
567 	if((fp = fopen(subfile(xfile), "r")) == NULL)
568 		return;
569 
570 	while (fgets(buf, BUFSIZ, fp) != NULL) {
571 		if (buf[0] != X_RQDFILE)
572 			continue;
573 		if (sscanf(&buf[1], "%s%s", file, tfile) < 2)
574 			continue;
575 		sprintf(tfull, "%s/%s", XQTDIR, tfile);
576 		unlink(subfile(tfull));
577 	}
578 	fclose(fp);
579 	return;
580 }
581 
582 
583 /*
584  *	move execute files to x-directory
585  */
586 
587 mvxfiles(xfile)
588 char *xfile;
589 {
590 	register FILE *fp;
591 	char buf[BUFSIZ], ffile[MAXFULLNAME], tfile[NAMESIZE];
592 	char tfull[MAXFULLNAME];
593 
594 	if((fp = fopen(subfile(xfile), "r")) == NULL)
595 		return;
596 
597 	while (fgets(buf, BUFSIZ, fp) != NULL) {
598 		if (buf[0] != X_RQDFILE)
599 			continue;
600 		if (sscanf(&buf[1], "%s%s", ffile, tfile) < 2)
601 			continue;
602 		expfile(ffile);
603 		sprintf(tfull, "%s/%s", XQTDIR, tfile);
604 		unlink(subfile(tfull));
605 		if (xmv(ffile, tfull) != 0) {
606 			syslog(LOG_WARNING, "xmv(%s,%s) failed: %m",
607 				ffile, tfull);
608 			cleanup(1);
609 		}
610 	}
611 	fclose(fp);
612 }
613 
614 /*
615  *	check for valid command/argument
616  *	*NOTE - side effect is to set xc to the	command to be executed.
617  *
618  *	return 0 - ok | 1 nok
619  */
620 
621 argok(xc, cmd)
622 register char *xc, *cmd;
623 {
624 	register char **ptr;
625 
626 #ifndef ALLOK
627 	if (strpbrk(cmd, BADCHARS) != NULL) {
628 		DEBUG(1,"MAGIC CHARACTER FOUND\n", CNULL);
629 		logent(cmd, "NASTY MAGIC CHARACTER FOUND");
630 		return FAIL;
631 	}
632 #endif !ALLOK
633 
634 	if (xc[0] != '\0')
635 		return SUCCESS;
636 
637 #ifndef ALLOK
638 	ptr = Cmds;
639 	DEBUG(9, "Compare %s and\n", cmd);
640 	while(*ptr != NULL) {
641 		DEBUG(9, "\t%s\n", *ptr);
642 		if (strcmp(cmd, *ptr) == SAME)
643 			break;
644 		ptr++;
645 	}
646 	if (*ptr == NULL) {
647 		DEBUG(1,"COMMAND NOT FOUND\n", CNULL);
648 		return FAIL;
649 	}
650 #endif
651 	strcpy(xc, cmd);
652 	DEBUG(9, "MATCHED %s\n", xc);
653 	return SUCCESS;
654 }
655 
656 
657 /*
658  *	if notification should be sent for successful execution of cmd
659  *
660  *	return NT_YES - do notification
661  *	       NT_ERR - do notification if exit status != 0
662  *	       NT_NO  - don't do notification ever
663  */
664 
665 chknotify(cmd)
666 char *cmd;
667 {
668 	register char **ptr;
669 	register int *nptr;
670 
671 	ptr = Cmds;
672 	nptr = Notify;
673 	while (*ptr != NULL) {
674 		if (strcmp(cmd, *ptr) == SAME)
675 			return *nptr;
676 		ptr++;
677 		nptr++;
678 	}
679 	return NT_YES;		/* "shouldn't happen" */
680 }
681 
682 
683 
684 /*
685  *	send mail to user giving execution results
686  */
687 
688 notify(user, rmt, cmd, str)
689 char *user, *rmt, *cmd, *str;
690 {
691 	char text[BUFSIZ*2];
692 	char ruser[MAXFULLNAME];
693 
694 	if (strpbrk(user, BADCHARS) != NULL) {
695 		char lbuf[MAXFULLNAME];
696 		sprintf(lbuf, "%s INVALID CHARACTER IN USERNAME", user);
697 		logent(cmd, lbuf);
698 		strcpy(user, "postmaster");
699 	}
700 	sprintf(text, "uuxqt cmd (%s) status (%s)", cmd, str);
701 	if (prefix(rmt, Myname))
702 		strcpy(ruser, user);
703 	else
704 		sprintf(ruser, "%s!%s", rmt, user);
705 	mailst(ruser, text, CNULL);
706 }
707 
708 /*
709  *	return mail to sender
710  *
711  */
712 retosndr(user, rmt, file)
713 char *user, *rmt, *file;
714 {
715 	char ruser[MAXFULLNAME];
716 
717 	if (strpbrk(user, BADCHARS) != NULL) {
718 		char lbuf[MAXFULLNAME];
719 		sprintf(lbuf, "%s INVALID CHARACTER IN USERNAME", user);
720 		logent(file, lbuf);
721 		strcpy(user, "postmaster");
722 	}
723 	if (strcmp(rmt, Myname) == SAME)
724 		strcpy(ruser, user);
725 	else
726 		sprintf(ruser, "%s!%s", rmt, user);
727 
728 	if (anyread(file) == 0)
729 		mailst(ruser, "Mail failed.  Letter returned to sender.\n", file);
730 	else
731 		mailst(ruser, "Mail failed.  Letter returned to sender.\n", CNULL);
732 	return;
733 }
734 
735 /*
736  *	execute shell of command with fi and fo as standard input/output
737  */
738 
739 shio(cmd, fi, fo)
740 char *cmd, *fi, *fo;
741 {
742 	int status, f;
743 	int pid, ret;
744 	char *args[256];
745 	extern int errno;
746 
747 	if (fi == NULL)
748 		fi = DEVNULL;
749 	if (fo == NULL)
750 		fo = DEVNULL;
751 
752 	getargs(cmd, args, 256);
753 	DEBUG(3, "shio - %s\n", cmd);
754 #ifdef SIGCHLD
755 	signal(SIGCHLD, SIG_IGN);
756 #endif SIGCHLD
757 	if ((pid = fork()) == 0) {
758 		signal(SIGINT, SIG_IGN);
759 		signal(SIGHUP, SIG_IGN);
760 		signal(SIGQUIT, SIG_IGN);
761 		close(Ifn);
762 		close(Ofn);
763 		close(0);
764 		setuid(getuid());
765 		f = open(subfile(fi), 0);
766 		if (f != 0) {
767 			logent(fi, "CAN'T READ");
768 			exit(-errno);
769 		}
770 		close(1);
771 		f = creat(subfile(fo), 0666);
772 		if (f != 1) {
773 			logent(fo, "CAN'T WRITE");
774 			exit(-errno);
775 		}
776 		execvp(args[0], args);
777 		exit(100+errno);
778 	}
779 	while ((ret = wait(&status)) != pid && ret != -1)
780 		;
781 	DEBUG(3, "status %d\n", status);
782 	return status;
783 }
784