Ultrix-3.1/src/cmd/uucp/uuxqt.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
static char Sccsid[] = "@(#)uuxqt.c 3.0 4/22/86";
/*
* uuxqt will execute commands set up by a uux command,
* usually from a remote machine - set by uucp.
*/
/*****************
* Mods:
* decvax!larry - handle subdirectories properly.
* - add concurrency on command type basis via the
* -c option
* - create lock files for each command to prevent
* other uuxqt from executing the same command.
* - handle system V.? return address
*****************/
#include "uucp.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef NDIR
#include "ndir.h"
#else
#include "sys/dir.h"
#endif
#define APPCMD(d) {\
char *p;\
for (p = d; *p != '\0';) *cmdp++ = *p++;\
*cmdp++ = ' ';\
*cmdp = '\0';}
#define NCMDS 50
struct command {
char *cmd;
int xeqlevel;
} Cmds[NCMDS];
int notiok = 1;
int nonzero = 0;
int getpid();
int getepid();
int Euid; /* effective user id */
char PATH[MAXFULLNAME] = "PATH=/bin:/usr/bin";
/* to remove restrictions from uuxqt
* define ALLOK 1
*
* to add allowable commands, add to the file CMDFILE
* A line of form "PATH=..." changes the search path
*/
main(argc, argv)
char *argv[];
{
char xcmd[MAXFULLNAME];
int argnok;
char xfile[MAXFULLNAME], user[MAXFULLNAME], buf[BUFSIZ];
char lbuf[MAXFULLNAME];
char cfile[MAXFULLNAME], dfile[MAXFULLNAME];
char file[MAXFULLNAME];
char fin[MAXFULLNAME], sysout[NAMESIZE], fout[MAXFULLNAME];
char ferr[MAXFULLNAME];
char xsys[NAMESIZE];
FILE *xfp, *dfp, *fp;
char path[MAXFULLNAME];
char cmdtype[MAXFULLNAME];
char lock[MAXFULLNAME];
char xlock[MAXFULLNAME];
char retaddr[MAXFULLNAME];
int ret_input;
struct stat stbuf;
char cmd[BUFSIZ];
/* set size of prm to something large -- cmcl2!salkind */
char *cmdp, prm[1000], *ptr;
char *getprm(), *lastpart();
int uid, ret, badfiles, i;
int stcico = 0;
char retstat[30];
int orig_uid = getuid();
char *argp[2];
int narg = 0;
strcpy(Progname, "uuxqt");
uucpname(Myname);
/* Try to run as uucp -- rti!trt */
setgid(getegid());
setuid(geteuid());
Euid = geteuid();
umask(WFMASK);
Ofn = 1;
Ifn = 0;
cmdtype[0]='\0';
while (argc>1 && argv[1][0] == '-') {
switch(argv[1][1]){
case 'x':
chkdebug(orig_uid);
Debug = atoi(&argv[1][2]);
if (Debug <= 0)
Debug = 1;
break;
case 'c':
sprintf(cmdtype, "%s", &argv[1][2]);
break;
default:
fprintf(stderr, "unknown flag %s\n", argv[1]);
break;
}
--argc; argv++;
}
DEBUG(4, "\n\n** %s **\n", "START");
uid = getuid();
guinfo(uid, User, path);
DEBUG(4, "User - %s\n", User);
sprintf(lock,"%s%s",X_LOCK,cmdtype);
DEBUG(5, "the lock is :%s:\n",lock);
if (ulockf(lock, (time_t) X_LOCKTIME) != 0)
exit(0);
fp = fopen(CMDFILE, "r");
if (fp == NULL) {
/* Fall-back if CMDFILE missing. Sept 1982, rti!trt */
logent("CAN'T OPEN", CMDFILE);
Cmds[0].cmd = "rmail";
Cmds[0].xeqlevel = 1;
Cmds[1].cmd = "rnews";
Cmds[1].xeqlevel = 1;
Cmds[2].cmd = NULL;
goto doprocess;
}
DEBUG(5, "%s opened\n", CMDFILE);
for (i=0; i<NCMDS-1 && cfgets(cmd, sizeof(cmd), fp) != NULL; i++) {
/* get command */
narg = getargs(cmd, argp);
if (strncmp(argp[0], "PATH=", 5) == 0) {
/* change path in L.cmd file */
strcpy(PATH, argp[0]);
i--;
continue;
}
Cmds[i].cmd = malloc((unsigned)(strlen(argp[0])+1));
strcpy(Cmds[i].cmd, argp[0]);
DEBUG(5, "cmd = %s\n", Cmds[i].cmd);
if (narg < 2 || argp[1][0] != 'X') {
DEBUG(5, "default xeq level provided %d\n", 9);
Cmds[i].xeqlevel = 9;
}
else
Cmds[i].xeqlevel = atoi(&argp[1][1]);
DEBUG(4, "xeq level for command is %d\n", Cmds[i].xeqlevel);
}
Cmds[i].cmd = 0;
fclose(fp);
/* make temporary file for standard error output */
sprintf(ferr,"%s/LTMP.ERR.%d", SPOOL, getpid);
xlock[0] = '\0';
nextsys: /* switch to another per system spool directory */
while(gnsys(xsys, XQTPRE)) {
DEBUG(5, "nextsys: %s\n", xsys);
mkspname(xsys);
DEBUG(5, "spooldir: %s\n", Spool);
subchdir(Spool);
strcpy(Wrkdir, Spool);
doprocess: /* process work in the spool directory */
DEBUG(4, "process %s\n", "");
while (gtxfile(xfile) > 0) {
if (xlock[0] != '\0') { /* remove lock for previous X.file */
rmlock(xlock);
xlock[0] = '\0';
}
ultouch(); /* rti!trt */
DEBUG(4, "xfile - %s\n", xfile);
xfp = fopen(subfile(xfile), "r");
ASSERT(xfp != NULL, "CAN'T OPEN", xfile, 0);
/* initialize to default */
strcpy(user, User);
strcpy(fin, "/dev/null");
strcpy(fout, "/dev/null");
sprintf(sysout, "%.7s", Myname);
retaddr[0] = '\0';
badfiles = 0; /* this was missing -- rti!trt */
notiok = 1;
nonzero = 0;
ret_input = 0;
while (fgets(buf, BUFSIZ, xfp) != NULL) {
switch (buf[0]) {
case X_USER:
sscanf(&buf[1], "%s%s", user, Rmtname);
break;
case X_STDIN:
sscanf(&buf[1], "%s", fin);
i = expfile(fin);
/* rti!trt: do not check permissions of
* vanilla spool file */
if (i != 0
&& (chkpth("", "", fin) || anyread(fin) != 0))
badfiles = 1;
break;
case X_STDOUT:
sscanf(&buf[1], "%s%s", fout, sysout);
sysout[7] = '\0';
/* rti!trt: do not check permissions of
* vanilla spool file. DO check permissions
* of writing on a non-vanilla file */
i = 1;
if (fout[0] != '~' || prefix(sysout, Myname))
i = expfile(fout);
if (i != 0
&& (chkpth("", "", fout)
|| chkperm(fout, (char *)1)))
badfiles = 1;
break;
case X_CMD:
strcpy(cmd, &buf[2]);
if (*(cmd + strlen(cmd) - 1) == '\n')
*(cmd + strlen(cmd) - 1) = '\0';
break;
case X_NONOTI:
notiok = 0;
break;
case X_NONZERO:
nonzero = 1;
break;
case X_INPUT: /* not used yet */
ret_input = 1;
break;
case X_RETURNADDR:
sscanf(&buf[1], "%s", retaddr);
break;
default:
break;
}
}
fclose(xfp);
DEBUG(4, "fin - %s, ", fin);
DEBUG(4, "fout - %s, ", fout);
DEBUG(4, "sysout - %s, ", sysout);
DEBUG(4, "user - %s\n", user);
DEBUG(4, "cmd - %s\n", cmd);
/* command execution */
if (strcmp(fout, "/dev/null") == SAME)
strcpy(dfile,"/dev/null");
else
gename(DATAPRE, sysout, 'O', dfile);
/* expand file names where necessary */
expfile(dfile);
strcpy(buf, PATH);
strcat(buf, ";export PATH;");
cmdp = buf + strlen(buf);
ptr = cmd;
xcmd[0] = '\0';
argnok = 0;
while ((ptr = getprm(ptr, prm)) != NULL) {
if (prm[0] == ';' || prm[0] == '^'
|| prm[0] == '&' || prm[0] == '|') {
xcmd[0] = '\0';
APPCMD(prm);
continue;
}
if ((argnok = argok(xcmd, prm)) != 0)
/* command not valid */
break;
/* if LCK.XQTxcmd exists then another
* uuxqt daemon is running specifically
* for the command specified by xcmd
*/
/* a null cmdtype implies a general uuxqt */
if (cmdtype[0] == '\0') {
sprintf(lock,"%s%s",X_LOCK,xcmd);
if (stat(lock,&stbuf) != -1) {
DEBUG(3,"uuxqt running for command: %s\n", xcmd);
goto doprocess;
}
}
else /* only xqt commands of type cmdtype */
if (strncmp(cmdtype,xcmd,strlen(cmdtype))) {
DEBUG(3,"xcmd=%s not the desired type\n",xcmd);
goto doprocess;
}
if (prm[0] == '~')
expfile(prm);
APPCMD(prm);
}
/* prevent concurrent uuxqts from working on the same
* X.file. This can only happen when a command specific
* uuxqt starts up after a general uuxqt.
* This locking mechanism should permit concurrent general
* uuxqts.
*/
sprintf(xlock,"%s/LCK.%s",SPOOL,xfile);
if (ulockf(xlock, (time_t) X_LOCKTIME) != 0) {
/* dont remove another daemons lock file */
xlock[0] = '\0';
DEBUG(8,"this xfile already processed: %s\n",
xfile);
goto doprocess;
}
if (argnok || badfiles) {
sprintf(lbuf, "%s XQT DENIED", user);
logent(cmd, lbuf);
DEBUG(4, "bad command %s\n", prm);
notify(user, Rmtname, cmd, "DENIED", ferr, retaddr);
goto rmfiles;
}
sprintf(lbuf, "%s XQT", user);
logent(buf, lbuf);
DEBUG(4, "cmd %s\n", buf);
mvxfiles(xfile);
subchdir(XQTDIR);
ret = shio(buf, fin, dfile, (char *)NULL, ferr);
/* watcgl.11, dmmartindale, signal and exit values were reversed */
sprintf(retstat, "signal %d, exit %d", ret & 0377,
(ret>>8) & 0377);
DEBUG(5,"retstat is %s: \n", retstat);
/* don't return exit status for mail commands */
if (strcmp(xcmd, "rmail") == SAME)
notiok = 0;
/* only return nonzero (fail) exit status for news commands */
if (strcmp(xcmd, "rnews") == SAME)
nonzero = 1;
/* notify user of exit status if:
* 1. ok to notify user && ok to return any exit status
* 2. ok to notify user && exit status was non zero
* 3. mail command failed - return mail to sender
*/
if (notiok && (!nonzero || (nonzero && ret != 0)))
notify(user, Rmtname, cmd, retstat, ferr, retaddr);
else if (ret != 0 && strcmp(xcmd, "rmail") == SAME) {
/* mail failed - return letter to sender */
retosndr(user, Rmtname, fin, ferr, retaddr);
sprintf(buf, "ret (%o) from %s!%s", ret, Rmtname, user);
logent("MAIL FAIL", buf);
}
DEBUG(4, "exit cmd - %d\n", ret);
subchdir(Spool);
rmxfiles(xfile);
if (ret != 0) {
/* exit status not zero */
dfp = fopen(subfile(dfile), "a");
ASSERT(dfp != NULL, "CAN'T OPEN", dfile, 0);
fprintf(dfp, "exit status %d", ret);
fclose(dfp);
sprintf(buf, "cmd: %s; ret: %s", xcmd, retstat);
logent("CMD FAILED", buf);
}
if (strcmp(fout, "/dev/null") != SAME) {
if (prefix(sysout, Myname)) {
xmv(dfile, fout);
chmod(fout, BASEMODE);
}
else {
gename(CMDPRE, sysout, 'O', cfile);
fp = fopen(subfile(cfile), "w");
ASSERT(fp != NULL, "OPEN", cfile, 0);
fprintf(fp, "S %s %s %s - %s 0666\n",
dfile, fout, user, lastpart(dfile));
fclose(fp);
}
}
rmfiles:
xfp = fopen(subfile(xfile), "r");
ASSERT_NOFAIL(xfp != NULL, "CAN NOT OPEN", xfile, 0);
/* on the chance that another uuxqt was working
* on this file and removed it, keep going
* - with the addition of per command uuxqts
* it is possible for overlap when another daemon
* starts up.
*/
if (xfp == NULL)
continue;
while (fgets(buf, BUFSIZ, xfp) != NULL) {
if (buf[0] != X_RQDFILE)
continue;
sscanf(&buf[1], "%s", file);
unlink(subfile(file));
}
unlink(subfile(xfile));
fclose(xfp);
unlink(subfile(ferr));
} /* end doprocess loop */
} /* end nextsys loop */
if (stcico)
xuucico("");
cleanup(0);
}
cleanup(code)
int code;
{
logcls();
rmlock(CNULL);
exit(code);
}
/*******
* gtxfile(file) get a file to execute
* char *file;
*
* return codes: 0 - no file | 1 - file to execute
* Mod to recheck for X-able files. Sept 1982, rti!trt.
* Should use stuff like bldflst to keep files in sequence
* Suggested by utzoo.2458 (utzoo!henry)
*/
gtxfile(file)
char *file;
{
static int reopened;
static DIR *dirp;
char pre[3];
retry:
if (dirp == NULL) {
dirp = opendir(subdir(Spool, XQTPRE));
ASSERT(dirp != NULL, "GTXFILE CAN'T OPEN", Spool, 0);
}
pre[0] = XQTPRE;
pre[1] = '.';
pre[2] = '\0';
while (gnamef(dirp, file) != 0) {
DEBUG(4, "file - %s\n", file);
if (!prefix(pre, file))
continue;
#ifndef UUDIR
/* Skip spurious subdirectories */
if (strcmp(pre, file) == SAME)
continue;
#endif
if (gotfiles(file))
/* return file to execute */
return(1);
}
closedir(dirp);
dirp = NULL;
if (!reopened) {
reopened++;
goto retry;
}
return(0);
}
/***
* gotfiles(file) check for needed files
* char *file;
*
* return codes: 0 - not ready | 1 - all files ready
*/
gotfiles(file)
char *file;
{
struct stat stbuf;
FILE *fp;
char buf[BUFSIZ], rqfile[MAXFULLNAME];
fp = fopen(subfile(file), "r");
if (fp == NULL)
return(0);
while (fgets(buf, BUFSIZ, fp) != NULL) {
DEBUG(4, "%s\n", buf);
if (buf[0] != X_RQDFILE)
continue;
sscanf(&buf[1], "%s", rqfile);
expfile(rqfile);
if (stat(subfile(rqfile), &stbuf) == -1) {
fclose(fp);
return(0);
}
}
fclose(fp);
return(1);
}
/***
* rmxfiles(xfile) remove execute files to x-directory
* char *xfile;
*
* return codes - none
*/
rmxfiles(xfile)
char *xfile;
{
FILE *fp;
char buf[BUFSIZ], file[NAMESIZE], tfile[NAMESIZE];
char tfull[MAXFULLNAME];
if((fp = fopen(subfile(xfile), "r")) == NULL)
return;
while (fgets(buf, BUFSIZ, fp) != NULL) {
if (buf[0] != X_RQDFILE)
continue;
if (sscanf(&buf[1], "%s%s", file, tfile) < 2)
continue;
sprintf(tfull, "%s/%s", XQTDIR, tfile);
unlink(subfile(tfull));
}
fclose(fp);
return;
}
/***
* mvxfiles(xfile) move execute files to x-directory
* char *xfile;
*
* return codes - none
*/
mvxfiles(xfile)
char *xfile;
{
FILE *fp;
char buf[BUFSIZ], ffile[MAXFULLNAME], tfile[NAMESIZE];
char tfull[MAXFULLNAME];
int ret;
if((fp = fopen(subfile(xfile), "r")) == NULL)
return;
while (fgets(buf, BUFSIZ, fp) != NULL) {
if (buf[0] != X_RQDFILE)
continue;
if (sscanf(&buf[1], "%s%s", ffile, tfile) < 2)
continue;
expfile(ffile);
sprintf(tfull, "%s/%s", XQTDIR, tfile);
unlink(subfile(tfull));
ret = link(subfile(ffile), subfile(tfull));
ASSERT(ret == 0, "LINK ERROR", "", ret);
/*
unlink(subfile(ffile));
*/
}
fclose(fp);
return;
}
/***
* argok(xc, cmd) check for valid command/argumanet
* *NOTE - side effect is to set xc to the
* command to be executed.
* char *xc, *cmd;
*
* return 0 - ok | 1 nok
*/
argok(xc, cmd)
char *xc, *cmd;
{
struct command *ptr;
#ifndef ALLOK
/* don't allow sh command strings `....` */
/* don't allow redirection of standard in or out */
/* don't allow other funny stuff */
/* but there are probably total holes here */
/* post-script. ittvax!swatt has a uuxqt that solves this. */
/* This version of uuxqt will shortly disappear */
if (index(cmd, '`') != NULL
|| index(cmd, '>') != NULL
|| index(cmd, ';') != NULL
|| index(cmd, '^') != NULL
|| index(cmd, '&') != NULL
|| index(cmd, '|') != NULL
|| index(cmd, '<') != NULL)
return(1);
#endif
if (xc[0] != '\0')
return(0);
#ifndef ALLOK
ptr = Cmds;
while(ptr->cmd != NULL) {
if (strcmp(cmd, ptr->cmd) == SAME)
break;
ptr++;
}
if (ptr->cmd == NULL) {
DEBUG(4, "command not found: %s\n", cmd);
return(1);
}
/*
* If the USERFILE execution level for the remote system is less
* than the execution level specified in L.cmds then do not
* allow remote execution to proceed.
*/
if (getxeqlevel(Rmtname) < ptr->xeqlevel) {
DEBUG(4, "Remote site (%s) can not execute command\n", Rmtname);
logent("execute level too low", Rmtname);
return(1);
}
#endif
strcpy(xc, cmd);
return(0);
}
/***
* notify send mail to user giving execution results
* return code - none
* This program assumes new mail command - send remote mail
*/
notify(user, rmt, cmd, str, ferr, raddr)
char *user, *rmt, *cmd, *str, *ferr, *raddr;
{
char text[MAXFULLNAME];
char ruser[MAXFULLNAME];
sprintf(text, "uuxqt cmd (%.50s) status (%s)", cmd, str);
if (prefix(rmt, Myname))
strcpy(ruser, user);
else
sprintf(ruser, "%s!%s", rmt, user);
/* use return address if supplied */
if (raddr[0] != '\0')
strcpy(ruser, raddr);
mailst(ruser, text, "", ferr);
return;
}
/***
* retosndr - return mail to sender
*
* return code - none
*/
retosndr(user, rmt, file, ferr, raddr)
char *user, *rmt, *file, *ferr, *raddr;
{
struct stat stbuf;
char ruser[100];
int ret;
if (strcmp(rmt, Myname) == SAME)
strcpy(ruser, user);
else
sprintf(ruser, "%s!%s", rmt, user);
/* use return address if supplied */
if (raddr[0] != '\0')
strcpy(ruser, raddr);
ret = stat(subfile(file), &stbuf);
if ((anyread(file) == 0) || (ret==0 && stbuf.st_uid == Euid))
mailst(ruser, "Mail failed. Letter returned to sender.\n",
file, ferr);
else
mailst(ruser, "Mail failed. uuxqt can not read the letter \n",
"", ferr);
return;
}