V10/cmd/uucp/uux.c
/* /sccs/src/cmd/uucp/s.uux.c
uux.c 1.12 8/30/84 17:38:15
*/
#include "uucp.h"
VERSION(@(#)uux.c 1.12);
#define NOSYSPART 0
#define HASSYSPART 1
#define GENSEND(f, a, b, c) {\
ASSERT(fprintf(f, "S %s %s %s -%s %s 0666 %s %s\n", a, b, retaddr, _Statop?"o":"", c, User, _Sfile) >= 0, Ct_WRITE, "", errno);\
}
#define GENRCV(f, a, b) {\
ASSERT(fprintf(f, "R %s %s %s - %s 0666 %s\n", a, b, retaddr, *_Sfile ? _Sfile : "dummy", User) >= 0, \
Ct_WRITE, "", errno);\
}
#define USAGE "[-aNAME] [-b] [-c] [-C] [-j] [-gGRADE] [-n] [-p] [-r] [-sFILE] [-xNUM] [-z] command-string"
#define APPCMD(p) {(void) strcat(cmd, p); (void) strcat(cmd, " ");}
static char _Sfile[MAXFULLNAME];
static int _Statop;
char _Grade = 'N';
/*
* uux
*/
main(argc, argv)
char *argv[];
{
FILE *fprx = NULL, *fpc = NULL, *fpd = NULL, *fp = NULL;
extern onintr();
int cfileUsed = 0; /* >0 if commands put in C. file flag */
int rflag = 0; /* C. files for receiving flag */
int cflag = 0; /* if > 0 make local copy of files to be sent */
int nflag = 0; /* if != 0, do not request error notification */
int zflag = 0; /* if != 0, request success notification */
int pipein = 0;
int startjob = 1;
short jflag = 0; /* -j flag output Jobid */
int bringback = 0; /* return stdin to invoker on error */
int ret, i;
char *getprm();
char redir = '\0';
char command = TRUE;
char cfile[NAMESIZE]; /* send commands for files from here */
char dfile[NAMESIZE]; /* used for all data files from here */
char rxfile[NAMESIZE]; /* file for X_ commands */
char tfile[NAMESIZE]; /* temporary file name */
char t2file[NAMESIZE]; /* temporary file name */
char buf[BUFSIZ];
char inargs[BUFSIZ];
char cmd[BUFSIZ];
char *ap;
char prm[BUFSIZ];
char syspart[NAMEBUF], rest[BUFSIZ];
char xsys[NAMEBUF];
char *fopt = NULL;
char *retaddr = NULL;
struct stat stbuf;
/* we want this to run as uucp, even if the kernel doesn't */
Uid = getuid();
Euid = geteuid(); /* this should be UUCPUID */
if (Uid == 0)
setuid(UUCPUID);
/* choose LOGFILE */
(void) strcpy(Logfile, LOGUUX);
/*
* determine local system name
*/
(void) strcpy(Progname, "uux");
Pchar = 'X';
(void) signal(SIGILL, onintr);
(void) signal(SIGTRAP, onintr);
(void) signal(SIGIOT, onintr);
(void) signal(SIGEMT, onintr);
(void) signal(SIGFPE, onintr);
(void) signal(SIGBUS, onintr);
(void) signal(SIGSEGV, onintr);
(void) signal(SIGSYS, onintr);
(void) signal(SIGTERM, SIG_IGN);
uucpname(Myname);
Ofn = 1;
Ifn = 0;
*_Sfile = '\0';
/*
* since getopt() can't handle the pipe input option '-'
* I'll change it to "-p"
*/
for (i=0; i<argc; i++)
if (EQUALS(argv[i], "-"))
argv[i] = "-p";
while ((i = getopt(argc, argv, "a:bcCjg:nprs:x:z")) != EOF) {
switch(i){
/*
* use this name in the U line
*/
case 'a':
retaddr = optarg;
break;
/*
* if return code non-zero, return command's input
*/
case 'b':
bringback = 1;
break;
/* do not make local copies of files to be sent (default) */
case 'c':
cflag = 0;
break;
/* make local copies of files to be sent */
case 'C':
cflag = 1;
break;
/*
* set priority of request
*/
case 'g':
_Grade = *optarg;
break;
case 'j': /* job id */
jflag = 1;
break;
/*
* do not send failure notification to user
*/
case 'n':
nflag++;
break;
/*
* send success notification to user
*/
case 'z':
zflag++;
break;
/*
* -p or - option specifies input from pipe
*/
case 'p':
pipein = 1;
break;
/*
* do not start transfer
*/
case 'r':
startjob = 0;
break;
case 's':
fopt = optarg;
_Statop++;
break;
/*
* debugging level
*/
case 'x':
Debug = atoi(optarg);
if (Debug <= 0)
Debug = 1;
break;
default:
(void) fprintf(stderr, "\tusage: %s %s\n", Progname, USAGE);
exit(2);
}
}
DEBUG(4, "\n\n** %s **\n", "START");
/*
* copy arguments into a buffer for later
* processing
*/
inargs[0] = '\0';
for (; optind < argc; optind++) {
DEBUG(4, "arg - %s:", argv[optind]);
(void) strcat(inargs, " ");
(void) strcat(inargs, argv[optind]);
}
/*
* get working directory and change
* to spool directory
*/
DEBUG(4, "arg - %s\n", inargs);
if(fopt){
gwd(Wrkdir);
if(*fopt != '/')
(void) sprintf(_Sfile, "%s/%s", Wrkdir, fopt);
else
(void) sprintf(_Sfile, "%s", fopt);
}
if (chdir(WORKSPACE) != 0) {
(void) fprintf(stderr, "No spool directory - %s - get help\n", WORKSPACE);
cleanup(12);
}
/*
* find remote system name
* remote name is first to know that
* is not > or <
*/
ap = inargs;
xsys[0] = '\0';
while ((ap = getprm(ap, prm)) != NULL) {
if (prm[0] == '>' || prm[0] == '<') {
ap = getprm(ap, prm);
continue;
}
/*
* split name into system name
* and command name
*/
split(prm, xsys, rest);
break;
}
if (xsys[0] == '\0')
(void) strcpy(xsys, Myname);
strncpy(Rmtname, xsys, MAXBASENAME);
Rmtname[MAXBASENAME] = '\0';
DEBUG(4, "xsys %s\n", xsys);
/*
* check to see if system name is valid
*/
if (versys(xsys, 0) != 0) {
/*
* bad system name
*/
fprintf(stderr, "bad system name: %s\n", xsys);
if (fprx != NULL)
(void) fclose(fprx);
if (fpc != NULL)
(void) fclose(fpc);
cleanup(11);
}
/*
* determine id of user starting remote
* execution
*/
guinfo(Uid, User);
(void) strcpy(Loginuser,User);
DEBUG(6, "User %s\n", User);
if (retaddr == NULL)
retaddr = User;
/*
* initialize command buffer
*/
*cmd = '\0';
/*
* generate JCL files to work from
*/
/*
* fpc is the C. file for the local site.
* collect commands into cfile.
* commit if not empty (at end).
*
* the appropriate C. file.
*/
gename(CMDPRE, xsys, _Grade, cfile);
DEBUG(9, "cfile = %s\n", cfile);
ASSERT(access(cfile, 0) != 0, Fl_EXISTS, cfile, errno);
fpc = fdopen(ret = creat(cfile, CFILEMODE), "w");
ASSERT(ret >= 0 && fpc != NULL, Ct_OPEN, cfile, errno);
/* set Jobid -- C.jobid */
(void) strncpy(Jobid, BASENAME(cfile, '.'), NAMESIZE);
Jobid[NAMESIZE-1] = '\0';
/*
* rxfile is the X. file for the job, fprx is its stream ptr.
* if the command is to be executed locally, rxfile becomes
* a local X. file, otherwise we send it as a D. file to the
* remote site.
*/
gename(DATAPRE, xsys, 'X', rxfile);
DEBUG(9, "rxfile = %s\n", rxfile);
ASSERT(access(rxfile, 0) != 0, Fl_EXISTS, rxfile, errno);
fprx = fdopen(ret = creat(rxfile, DFILEMODE), "w");
ASSERT(ret >= 0 && fprx != NULL, Ct_WRITE, rxfile, errno);
clearerr(fprx);
(void) fprintf(fprx,"%c %s %s\n", X_USER, User, Myname);
if (zflag) {
(void) fprintf(fprx, "%c return status on success\n",
X_COMMENT);
(void) fprintf(fprx,"%c\n", X_SENDZERO);
}
if (nflag) {
(void) fprintf(fprx, "%c don't return status on failure\n",
X_COMMENT);
(void) fprintf(fprx,"%c\n", X_SENDNOTHING);
} else {
(void) fprintf(fprx, "%c return status on failure\n",
X_COMMENT);
fprintf(fprx,"%c\n", X_NONZERO);
}
if (bringback) {
(void) fprintf(fprx, "%c return input on abnormal exit\n",
X_COMMENT);
(void) fprintf(fprx,"%c\n", X_BRINGBACK);
}
if (_Statop)
(void) fprintf(fprx,"%c %s\n", X_MAILF, _Sfile);
if (retaddr != NULL) {
(void) fprintf(fprx, "%c return address for status or input return\n",
X_COMMENT);
(void) fprintf(fprx,"%c %s\n", X_RETADDR, retaddr);
}
/*
* create a JCL file to spool pipe input into
*/
if (pipein) {
/*
* fpd is the D. file into which we now read
* input from stdin
*/
gename(DATAPRE, Myname, 'B', dfile);
ASSERT(access(dfile, 0) != 0, Fl_EXISTS, dfile, errno);
i = creat(dfile, DFILEMODE);
ASSERT(i >= 0, Ct_OPEN, dfile, errno);
while ((ret = read(0, buf, BUFSIZ)) > 0)
ASSERT(write(i, buf, ret) == ret, Ct_WRITE, dfile, errno);
(void) close(i);
/*
* if command is to be executed on remote
* create extra JCL
*/
if (!EQUALSN(Myname, xsys, SYSNSIZE)) {
GENSEND(fpc, dfile, dfile, dfile);
}
/*
* create file for X_ commands
*/
(void) fprintf(fprx, "%c %s\n", X_RQDFILE, dfile);
(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
wfcommit(dfile, dfile, xsys);
}
/*
* parse command
*/
ap = inargs;
while ((ap = getprm(ap, prm)) != NULL) {
DEBUG(4, "prm - %s\n", prm);
/*
* redirection of I/O
*/
if (prm[0] == '>' || prm[0] == '<') {
redir = prm[0];
continue;
}
/*
* some terminator
*/
if ( prm[0] == '|' || prm[0] == '^'
|| prm[0] == '&' || prm[0] == ';') {
if (*cmd != '\0') /* not 1st thing on line */
APPCMD(prm);
command = TRUE;
continue;
}
/*
* process command or file or option
* break out system and file name and
* use default if necessary
*/
ret = split(prm, syspart, rest);
DEBUG(4, "syspart -> %s, ", syspart);
DEBUG(4, "rest -> %s, ", rest);
DEBUG(4, "ret -> %d\n", ret);
if (command && redir == '\0') {
/*
* command
*/
APPCMD(rest);
command = FALSE;
continue;
}
if (syspart[0] == '\0') {
(void) strcpy(syspart, Myname);
DEBUG(6, "syspart -> %s\n", syspart);
} else if (versys(syspart, 0) != 0) {
/*
* bad system name
*/
fprintf(stderr, "bad system name: %s\n", syspart);
if (fprx != NULL)
(void) fclose(fprx);
if (fpc != NULL)
(void) fclose(fpc);
cleanup(11);
}
/*
* process file or option
*/
/*
* process file argument
* expand filename and create JCL card for
* redirected output
* e.g., X file sys
*/
if (redir == '>') {
if (rest[0] != '~')
if (ckexpf(rest))
cleanup(6);
ASSERT(fprintf(fprx, "%c %s %s\n", X_STDOUT, rest,
syspart) >= 0, Ct_WRITE, rxfile, errno);
redir = '\0';
continue;
}
/*
* if no system specified, then being
* processed locally
*/
if (ret == NOSYSPART && redir == '\0') {
/*
* option
*/
APPCMD(rest);
continue;
}
/* local xeqn + local file (!x !f) */
if ((EQUALSN(xsys, Myname, SYSNSIZE))
&& (EQUALSN(xsys, syspart, SYSNSIZE))) {
/*
* create JCL card
*/
if (ckexpf(rest))
cleanup(7);
/*
* JCL card for local input
* e.g., I file
*/
if (redir == '<') {
(void) fprintf(fprx, "%c %s\n", X_STDIN, rest);
} else
APPCMD(rest);
ASSERT(fprx != NULL, Ct_WRITE, rxfile, errno);
redir = '\0';
continue;
}
/* remote xeqn + local file (sys!x !f) */
if (EQUALSN(syspart, Myname, SYSNSIZE)) {
/*
* check access to local file
* if cflag is set, copy to spool directory
* otherwise, just mention it in the X. file
*/
if (ckexpf(rest))
cleanup(6);
DEBUG(4, "rest %s\n", rest);
/* see if I can read this file as read uid, gid */
if (uidstat(rest, &stbuf) != 0) {
(void) fprintf(stderr,
"can't get file status %s\n", rest);
cleanup(8);
}
if ( !(stbuf.st_mode & ANYREAD)
&& !(stbuf.st_uid == Uid && stbuf.st_mode & 0400)
&& !(stbuf.st_gid ==getgid() && stbuf.st_mode & 0040)
) {
fprintf(stderr,"permission denied %s\n", rest);
cleanup(1);
}
/* D. file for sending local file */
gename(DATAPRE, xsys, 'A', dfile);
if (cflag || !(stbuf.st_mode & ANYREAD)) {
/* make local copy */
if (uidxcp(rest, dfile) != 0) {
fprintf(stderr,"can't copy %s\n", rest);
cleanup(5);
}
(void) chmod(dfile, DFILEMODE);
/* generate 'send' entry in command file */
GENSEND(fpc, rest, dfile, dfile);
wfcommit(dfile, dfile, xsys); /* commit input */
} else /* don't make local copy */
GENSEND(fpc, rest, dfile, "D.0");
/*
* JCL cards for redirected input in X. file,
* e.g.
* I D.xxx
* F D.xxx
*/
if (redir == '<') {
/*
* don't bother making a X_RQDFILE line that
* renames stdin on the remote side, since the
* remote command can't know its name anyway
*/
(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
(void) fprintf(fprx, "%c %s\n", X_RQDFILE, dfile);
} else {
APPCMD(BASENAME(rest, '/'));;
/*
* generate X. JCL card that specifies
* F file
*/
(void) fprintf(fprx, "%c %s %s\n", X_RQDFILE,
dfile, BASENAME(rest, '/'));
}
redir = '\0';
continue;
}
/* local xeqn + remote file (!x sys!f ) */
if (EQUALS(Myname, xsys)) {
/*
* expand receive file name
*/
if (ckexpf(rest))
cleanup(6);
/*
* tfile is command file for receive from remote.
* we defer commiting until later so
* that only one C. file is created per site.
*
* dfile is name of data file to receive into;
* we don't use it, just name it.
*
* the name of the remote is appended to the
* X_RQDFILE line to help uuxqt in finding the
* D. file when it arrives.
*/
if (gtcfile(tfile, syspart) != SUCCESS) {
gename(CMDPRE, syspart, 'R', tfile);
ASSERT(access(tfile, 0) != 0,
Fl_EXISTS, tfile, errno);
svcfile(tfile, syspart);
(void) close(creat(tfile, CFILEMODE));
}
fp = fopen(tfile, "a");
ASSERT(fp != NULL, Ct_OPEN, tfile, errno);
setbuf(fp, CNULL);
gename(DATAPRE, syspart, 'R', dfile);
/* prepare JCL card to receive file */
GENRCV(fp, rest, dfile);
ASSERT(ferror(fp) == 0, Ct_WRITE, dfile, errno);
(void) fclose(fp);
rflag++;
if (rest[0] != '~')
if (ckexpf(rest))
cleanup(7);
/*
* generate receive entries
*/
if (redir == '<') {
(void) fprintf(fprx,
"%c %s/%s/%s\n", X_RQDFILE, Spool,
syspart, dfile);
(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
} else {
(void) fprintf(fprx, "%c %s/%s/%s %s\n",
X_RQDFILE, Spool, syspart, dfile,
BASENAME(rest, '/'));
APPCMD(BASENAME(rest, '/'));
}
redir = '\0';
continue;
}
/* remote xeqn/file, different remotes (xsys!cmd syspart!rest) */
if (!EQUALS(syspart, xsys)) {
/*
* strategy:
* request rest from syspart.
*
* set up a local X. file that will send rest to xsys,
* once it arrives from syspart.
*
* arrange so that the xsys D. file (fated to become
* an X. file on xsys), rest is required and named.
*
* pictorially:
*
* ===== syspart/C.syspartR.... ===== (tfile)
* R rest D.syspart... (dfile)
*
*
* ===== local/X.local... ===== (t2file)
* F Spool/syspart/D.syspart... rest (dfile)
* C uucp -C rest D.syspart... (dfile)
*
* ===== xsys/D.xsysG.... (fprx)
* F D.syspart... rest (dfile)
* or, in the case of redir == '<'
* F D.syspart... (dfile)
* I D.syspart... (dfile)
*
* while we do push rest around a bunch,
* we use the protection scheme to good effect.
*
* we must rely on uucp's treatment of requests
* form XQTDIR to get the data file to the right
* place ultimately.
*/
/* build (or append to) C.syspartR... */
if (gtcfile(tfile, syspart) != SUCCESS) {
gename(CMDPRE, syspart, 'R', tfile);
ASSERT(access(tfile, 0) != 0,
Fl_EXISTS, tfile, errno);
svcfile(tfile, syspart);
(void) close(creat(tfile, CFILEMODE));
}
fp = fopen(tfile, "a");
ASSERT(fp != NULL, Ct_OPEN, tfile, errno);
setbuf(fp, CNULL);
gename(DATAPRE, syspart, 'R', dfile);
GENRCV(fp, rest, dfile);
ASSERT(ferror(fp) == 0, Ct_WRITE, dfile, errno);
(void) fclose(fp);
/* build local/X.localG... */
gename(XQTPRE, Myname, _Grade, t2file);
ASSERT(access(t2file, 0)!=0, Fl_EXISTS, t2file, errno);
(void) close(creat(t2file, CFILEMODE));
fp = fopen(t2file, "w");
ASSERT(fp != NULL, Ct_OPEN, t2file, errno);
setbuf(fp, CNULL);
(void) fprintf(fp, "%c %s/%s/%s %s\n", X_RQDFILE,
Spool, syspart, dfile, BASENAME(rest, '/'));
(void) fprintf(fp, "%c uucp -C %s %s!%s\n",
X_CMD, BASENAME(rest, '/'), xsys, dfile);
ASSERT(ferror(fp) == 0, Ct_WRITE, t2file, errno);
(void) fclose(fp);
/* put t2file where uuxqt can get at it */
wfcommit(t2file, t2file, Myname);
/* generate xsys/X.sysG... cards */
if (redir == '<') {
(void) fprintf(fprx, "%c %s\n",
X_RQDFILE, dfile);
(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
} else {
(void) fprintf(fprx, "%c %s %s\n", X_RQDFILE,
dfile, BASENAME(rest, '/'));
APPCMD(BASENAME(rest, '/'));
}
redir = '\0';
continue;
}
/* remote xeqn + remote file, same remote (sys!x sys!f) */
if (rest[0] != '~') /* expand '~' on remote */
if (ckexpf(rest))
cleanup(7);
if (redir == '<') {
(void) fprintf(fprx, "%c %s\n", X_STDIN, rest);
}
else
APPCMD(rest);
redir = '\0';
continue;
}
/*
* place command to be executed in JCL file
*/
(void) fprintf(fprx, "%c %s\n", X_CMD, cmd);
ASSERT(ferror(fprx) == 0, Ct_WRITE, rxfile, errno);
(void) fclose(fprx); /* rxfile is ready for commit */
logent(cmd, "QUEUED");
/* the X. name must have the X.Jobid so status reporting is by jobid */
(void) sprintf(tfile, "X.%s", Jobid);
if (EQUALS(xsys, Myname)) {
/* local xeqn -- use X_ file here */
wfcommit(rxfile, tfile, xsys);
/*
* see if -r option requested JCL to be queued only
*/
if (startjob)
xuuxqt(Myname);
} else {
/* remote xeqn -- send rxfile to remote */
/* put it in a place where cico can get at it */
wfcommit(rxfile, rxfile, xsys);
GENSEND(fpc, rxfile, tfile, rxfile);
}
cfileUsed = (ftell(fpc) != 0L); /* was cfile used? */
ASSERT(ferror(fpc) == 0, Ct_WRITE, cfile, errno);
(void) fclose(fpc);
/*
* has any command been placed in command JCL file
*/
if (cfileUsed) {
wfcommit(cfile, cfile, xsys);
/*
* see if -r option requested JCL to be queued only
*/
if (startjob)
xuucico(xsys);
} else
unlink(cfile);
/* commit any leftover C. files for remote receive */
commitall();
if (jflag) /* print Jobid */
printf("%s\n", Jobid);
cleanup(0);
}
/*
* cleanup and unlink if error
* code -> exit code
* return:
* none
*/
cleanup(code)
register int code;
{
rmlock(CNULL);
if (code) {
wfabort();
fprintf(stderr, "uux failed ( %d )\n", code);
fflush(stderr);
if (code < 0) {
setuid(getuid());
signal(SIGIOT, SIG_DFL);
abort();
}
}
DEBUG(1, "exit code %d\n", code);
if (code < 0)
exit(-code);
else
exit(code);
}
/*
* catch signal then cleanup and exit
*/
onintr(inter)
register int inter;
{
char str[30];
(void) signal(inter, SIG_IGN);
(void) sprintf(str, "XSIGNAL %d", inter);
logent(str, "XCAUGHT");
cleanup(-inter);
}