/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include "uucp.h" #define SHORTBUF 64 #define NOSYSPART 0 #define GENSEND(f, a, b, c) {\ ASSERT(fprintf(f, "S %s %s %s -%s %s 0666 %s %s\n", a, b, User, _Statop?"o":"", c, User, _Sfile) >= 0, Ct_WRITE, "", errno);\ } #define GENRCV(f, a, b) {\ char tbuf[SHORTBUF]; \ gename (DATAPRE, xsys, 'Z', tbuf); \ ASSERT(fprintf(f, "R %s %s %s - %s 0666 %s %s\n", a, b, User, _Sfile, User, tbuf) \ >= 0, Ct_WRITE, "", errno);\ } #define STRNCPY(str1, str2) { \ (void) strncpy(str1, str2, (sizeof(str1) - 1)); \ str1[sizeof(str1) - 1] = '\0'; \ } #define STRNCAT(str1, str2) { \ (void) strncat(str1, str2, \ (sizeof(str1) - 1 - strlen(str1))); \ } #define APPCMD(p) {STRNCAT(cmd, p); STRNCAT(cmd, " ");} static char _Sfile[MAXFULLNAME]; static int _Statop; char Sgrade[NAMESIZE]; void cleanup(); static void usage(); static void onintr(); /* * uux */ int main(argc, argv, envp) int argc; char *argv[]; char *envp[]; { char *jid(); FILE *fprx = NULL, *fpc = NULL, *fpd = NULL, *fp = NULL; int cfileUsed = 0; /* >0 if commands put in C. file 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'; /* X_STDIN, X_STDOUT, X_STDERR as approprite */ 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[MAXFULLNAME], rest[BUFSIZ]; char xsys[MAXFULLNAME]; char *fopt = NULL; char *retaddr = NULL; struct stat stbuf; /* Set locale environment variables local definitions */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif (void) textdomain(TEXT_DOMAIN); /* 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); /* init environment for fork-exec */ Env = envp; /* choose LOGFILE */ STRNCPY(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'; /* * determine id of user starting remote * execution */ guinfo(Uid, User); STRNCPY(Loginuser,User); *Sgrade = NULLCHAR; /* * this is a check to see if we are using the administrator * defined service grade. The GRADES file will determine if * we are. If so then setup the default grade variables. */ if (eaccess(GRADES, 04) != -1) { Grade = 'A'; Sgrades = TRUE; STRNCPY(Sgrade, "default"); } /* * create/append command log */ commandlog(argc,argv); /* * since getopt() can't handle the pipe input option '-'; * change it to "-p" */ for (i=1; i= argc ) usage(); /* * copy arguments into a buffer for later * processing */ inargs[0] = '\0'; for (; optind < argc; optind++) { DEBUG(4, "arg - %s:", argv[optind]); STRNCAT(inargs, " "); STRNCAT(inargs, argv[optind]); } /* * get working directory and change * to spool directory */ DEBUG(4, "arg - %s\n", inargs); gwd(Wrkdir); if(fopt){ if(*fopt != '/') { (void) snprintf(_Sfile, (sizeof(_Sfile) - 1), "%s/%s", Wrkdir, fopt); _Sfile[sizeof(_Sfile) - 1] = '\0'; } else { (void) snprintf(_Sfile, (sizeof(_Sfile) - 1), "%s", fopt); _Sfile[sizeof(_Sfile) - 1] = '\0'; } } else strcpy(_Sfile, "dummy"); if (chdir(WORKSPACE) != 0) { (void) fprintf(stderr, gettext("No spool directory - %s - get help\n"), WORKSPACE); cleanup(EX_OSERR); } /* * find remote system name * remote name is first to know that * is not > or < */ ap = inargs; xsys[0] = '\0'; while ((ap = getprm(ap, (char *)NULL, prm)) != NULL) { if (prm[0] == '>' || prm[0] == '<') { ap = getprm(ap, (char *)NULL, prm); continue; } /* * split name into system name * and command name */ (void) split(prm, xsys, CNULL, rest); break; } if (xsys[0] == '\0') STRNCPY(xsys, Myname); STRNCPY(Rmtname, xsys); DEBUG(4, "xsys %s\n", xsys); /* get real Myname - it depends on who I'm calling--Rmtname */ (void) mchFind(Rmtname); myName(Myname); /* * check to see if system name is valid */ if (versys(xsys) != 0) { /* * bad system name */ fprintf(stderr, gettext("bad system name: %s\n"), xsys); if (fprx != NULL) (void) fclose(fprx); if (fpc != NULL) (void) fclose(fpc); cleanup(EX_NOHOST); } 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); setbuf(fpc, CNULL); /* set Jobid -- C.jobid */ STRNCPY(Jobid, BASENAME(cfile, '.')); /* * 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); setbuf(fprx, CNULL); 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); } (void) fprintf(fprx, "%c job id for status reporting\n", X_COMMENT); (void) fprintf(fprx,"%c %s\n", X_JOBID, Jobid); /* * 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); fpd = fdopen(ret = creat(dfile, DFILEMODE), "w"); ASSERT(ret >= 0 && fpd != NULL, Ct_OPEN, dfile, errno); /* * read pipe to EOF */ while (!feof(stdin)) { ret = fread(buf, 1, BUFSIZ, stdin); ASSERT(fwrite(buf, 1, ret, fpd) == ret, Ct_WRITE, dfile, errno); } ASSERT(fflush(fpd) != EOF && ferror(fpd) == 0, Ct_WRITE, dfile, errno); (void) fclose(fpd); /* * if command is to be executed on remote * create extra JCL */ if (!EQUALSN(Myname, xsys, MAXBASENAME)) { 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); if (EQUALS(Myname, xsys)) wfcommit(dfile, dfile, xsys); } /* * parse command */ ap = inargs; while ((ap = getprm(ap, (char *)NULL, prm)) != NULL) { DEBUG(4, "prm - %s\n", prm); /* * redirection of I/O */ if ( prm[0] == '<' ) { if (prm[1] == '<') { fprintf(stderr, gettext("'<<' may not be used in command\n")); cleanup(EX_USAGE); } redir = X_STDIN; continue; } if ( prm[0] == '>' ) { if (prm[1] == '>') { fprintf(stderr, gettext("'>>' may not be used in command\n")); cleanup(EX_USAGE); } if (prm[1] == '|') { fprintf(stderr, gettext("'>|' may not be used in command\n")); cleanup(EX_USAGE); } if (prm[1] == '&') { fprintf(stderr, gettext("'>&' may not be used in command\n")); cleanup(EX_USAGE); } redir = X_STDOUT; continue; } if ( EQUALS(prm, "2>") ) { redir = X_STDERR; 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, CNULL, 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') { STRNCPY(syspart, Myname); DEBUG(6, "syspart -> %s\n", syspart); } else if (versys(syspart) != 0) { /* * bad system name */ fprintf(stderr, gettext("bad system name: %s\n"), syspart); if (fprx != NULL) (void) fclose(fprx); if (fpc != NULL) (void) fclose(fpc); cleanup(EX_NOHOST); } /* * process file or option */ /* * process file argument * expand filename and create JCL card for * redirected output * e.g., X file sys */ if ((redir == X_STDOUT) || (redir == X_STDERR)) { if (rest[0] != '~') if (ckexpf(rest)) cleanup(EX_OSERR); ASSERT(fprintf(fprx, "%c %s %s\n", redir, 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, MAXBASENAME)) && (EQUALSN(xsys, syspart, MAXBASENAME))) { /* * create JCL card */ if (ckexpf(rest)) cleanup(EX_OSERR); /* * JCL card for local input * e.g., I file */ if (redir == X_STDIN) { (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, MAXBASENAME)) { /* * 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(EX_OSERR); 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, gettext("can't get file status %s\n"), rest); cleanup(EX_OSERR); } /* XXX - doesn't check group list */ if ( !(stbuf.st_mode & ANYREAD) && !(stbuf.st_uid == Uid && stbuf.st_mode & 0400) && !(stbuf.st_gid ==getgid() && stbuf.st_mode & 0040) ) { fprintf(stderr, gettext("permission denied %s\n"), rest); cleanup(EX_CANTCREAT); } /* 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, gettext("can't copy %s\n"), rest); cleanup(EX_CANTCREAT); } (void) chmod(dfile, DFILEMODE); /* generate 'send' entry in command file */ GENSEND(fpc, rest, dfile, dfile); } else /* don't make local copy */ GENSEND(fpc, rest, dfile, dfile); /* * JCL cards for redirected input in X. file, * e.g. * I D.xxx * F D.xxx */ if (redir == X_STDIN) { /* * 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(EX_OSERR); /* * strategy: * request rest from syspart. when it arrives, * we can run the command. * * 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. * * once the data file arrives from syspart. * arrange so that in the X. file (fprx), rest is * required and named appropriately. this * necessitates local access to SPOOL/syspart, which * is handled by a hook in uuxqt that allows files * in SPOOL/syspart to be renamed on the F line. * * pictorially: * * ===== syspart/C.syspart.... ===== (tfile) * R rest D.syspart... (dfile) * * * ===== local/X.local... ===== (fprx) * F SPOOL/syspart/D.syspart... rest (dfile) * * */ if (gtcfile(tfile, syspart) != SUCCESS) { gename(CMDPRE, syspart, 'R', tfile); ASSERT(access(tfile, 0) != 0, Fl_EXISTS, tfile, errno); svcfile(tfile, syspart, Sgrade); (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(fflush(fp) != EOF && ferror(fp) == 0, Ct_WRITE, dfile, errno); (void) fclose(fp); if (rest[0] != '~') if (ckexpf(rest)) cleanup(EX_OSERR); /* * generate receive entries */ if (redir == X_STDIN) { (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 in the xsys D. file (fated to become * an X. file on xsys), rest is required and named. * * pictorially: * * ===== syspart/C.syspart.... ===== (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 * from XQTDIR to get the data file to the right * place ultimately. */ /* build (or append to) C.syspart... */ if (gtcfile(tfile, syspart) != SUCCESS) { gename(CMDPRE, syspart, 'R', tfile); ASSERT(access(tfile, 0) != 0, Fl_EXISTS, tfile, errno); svcfile(tfile, syspart, Sgrade); (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(fflush(fp) != EOF && ferror(fp) == 0, Ct_WRITE, dfile, errno); (void) fclose(fp); /* build local/X.localG... */ /* name might collide with rxfile; no real danger */ 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 third party request, job id\n", X_COMMENT); (void) fprintf(fp, "%c %s\n", X_JOBID, Jobid); (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(fflush(fp) != EOF && 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 == X_STDIN) { (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(EX_OSERR); if (redir == X_STDIN) { (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(fflush(fprx) != EOF && ferror(fprx) == 0, Ct_WRITE, rxfile, errno); (void) fclose(fprx); /* rxfile is ready for commit */ logent(cmd, "QUEUED"); gename(XQTPRE, Myname, Grade, tfile); if (EQUALS(xsys, Myname)) { /* local xeqn -- use X_ file here */ /* this use of the X_ file can not collide with the earlier one */ 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 */ /* X_ file name might collide with an earlier use, */ /* but that one lives locally, while this one gets shipped */ GENSEND(fpc, rxfile, tfile, rxfile); } cfileUsed = (ftell(fpc) != 0L); /* was cfile used? */ ASSERT(fflush(fpc) != EOF && ferror(fpc) == 0, Ct_WRITE, cfile, errno); (void) fclose(fpc); /* commit C. files for remote receive */ commitall(); /* * has any command been placed in command JCL file */ if (cfileUsed) { svcfile(cfile, xsys, Sgrade); commitall(); /* * see if -r option requested JCL to be queued only */ if (startjob) xuucico(xsys); } else unlink(cfile); if (jflag) { /* print Jobid */ STRNCPY(Jobid, jid()); printf("%s\n", Jobid); } cleanup(0); /* NOTREACHED */ return (0); } /* * cleanup and unlink if error * code -> exit code * return: * none */ void cleanup(code) int code; { static int first = 1; /* prevent recursion on errors */ if (first) { first = 0; rmlock(CNULL); if (code) { fprintf(stderr, gettext("uux failed ( %d )\n"), code); wfabort(); } } DEBUG(1, "exit code %d\n", code); if (code < 0) exit(-code); else exit(code); } /* * catch signal then cleanup and exit */ static void onintr(inter) int inter; { char str[30]; (void) signal(inter, SIG_IGN); (void) sprintf(str, "XSIGNAL %d", inter); logent(str, "XCAUGHT"); cleanup(EX_TEMPFAIL); } static void usage() { (void) fprintf(stderr, gettext("Usage: %s [-bcCjnprz] [-a NAME]" " [-g GRADE] [-s FILE] [-x NUM] command-string\n"), Progname); exit(EX_USAGE); }