OpenSolaris_b135/cmd/bnu/uux.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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  &&  *argv[i] == '-'; 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':
			if (!Sgrades) {
				if (strlen(optarg) < (size_t) 2 && isalnum(*optarg))
					Grade = *optarg;
				else {
					(void) fprintf(stderr, gettext("No"
					    " administrator defined service"
					    " grades available on this"
					    " machine.\n"));
					(void) fprintf(stderr, gettext("UUCP"
					    " service grades range from"
					    " [A-Z][a-z] only.\n"));
					cleanup(-1);
				}
			}
			else {
				STRNCPY(Sgrade, optarg);
				if (vergrd(Sgrade) != SUCCESS)
					cleanup(FAIL);
			}
			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:
			usage();
		}
	}

	DEBUG(4, "\n\n** %s **\n", "START");

	if( optind >= 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);
}