1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29
30#include "uucp.h"
31
32#define SHORTBUF 64
33
34#define NOSYSPART 0
35
36#define GENSEND(f, a, b, c) {\
37ASSERT(fprintf(f, "S %s %s %s -%s %s 0666 %s %s\n", a, b, User, _Statop?"o":"", c, User, _Sfile) >= 0, Ct_WRITE, "", errno);\
38}
39#define GENRCV(f, a, b) {\
40char tbuf[SHORTBUF]; \
41gename (DATAPRE, xsys, 'Z', tbuf); \
42ASSERT(fprintf(f, "R %s %s %s - %s 0666 %s %s\n", a, b, User, _Sfile, User, tbuf) \
43 >= 0, Ct_WRITE, "", errno);\
44}
45
46#define	STRNCPY(str1, str2)	{ \
47			(void) strncpy(str1, str2, (sizeof(str1) - 1)); \
48			str1[sizeof(str1) - 1] = '\0'; \
49		}
50#define	STRNCAT(str1, str2)	{ \
51			(void) strncat(str1, str2, \
52				(sizeof(str1) - 1 - strlen(str1))); \
53		}
54#define APPCMD(p)	{STRNCAT(cmd, p); STRNCAT(cmd, " ");}
55
56static char	_Sfile[MAXFULLNAME];
57static int	_Statop;
58char Sgrade[NAMESIZE];
59void cleanup();
60static void usage();
61static void onintr();
62
63/*
64 *	uux
65 */
66int
67main(argc, argv, envp)
68int argc;
69char *argv[];
70char *envp[];
71{
72	char *jid();
73	FILE *fprx = NULL, *fpc = NULL, *fpd = NULL, *fp = NULL;
74	int cfileUsed = 0;	/*  >0 if commands put in C. file flag  */
75	int cflag = 0;		/* if > 0 make local copy of files to be sent */
76	int nflag = 0;		/* if != 0, do not request error notification */
77	int zflag = 0;		/* if != 0, request success notification */
78	int pipein = 0;
79	int startjob = 1;
80	short jflag = 0;	/* -j flag  output Jobid */
81	int bringback = 0;	/* return stdin to invoker on error */
82	int ret, i;
83	char *getprm();
84	char redir = '\0';	/* X_STDIN, X_STDOUT, X_STDERR as approprite */
85	char command = TRUE;
86	char cfile[NAMESIZE];	/* send commands for files from here */
87	char dfile[NAMESIZE];	/* used for all data files from here */
88	char rxfile[NAMESIZE];	/* file for X_ commands */
89	char tfile[NAMESIZE];	/* temporary file name */
90	char t2file[NAMESIZE];	/* temporary file name */
91	char buf[BUFSIZ];
92	char inargs[BUFSIZ];
93	char cmd[BUFSIZ];
94	char *ap;
95	char prm[BUFSIZ];
96	char syspart[MAXFULLNAME], rest[BUFSIZ];
97	char xsys[MAXFULLNAME];
98	char	*fopt = NULL;
99	char	*retaddr = NULL;
100	struct stat stbuf;
101
102	/* Set locale environment variables local definitions */
103	(void) setlocale(LC_ALL, "");
104#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
105#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
106#endif
107	(void) textdomain(TEXT_DOMAIN);
108
109 /* we want this to run as uucp, even if the kernel doesn't */
110	Uid = getuid();
111	Euid = geteuid();	/* this should be UUCPUID */
112	if (Uid == 0)
113	    setuid(UUCPUID);
114
115/* init environment for fork-exec */
116	Env = envp;
117
118	/* choose LOGFILE */
119	STRNCPY(Logfile, LOGUUX);
120
121	/*
122	 * determine local system name
123	 */
124	(void) strcpy(Progname, "uux");
125	Pchar = 'X';
126	(void) signal(SIGILL, onintr);
127	(void) signal(SIGTRAP, onintr);
128	(void) signal(SIGIOT, onintr);
129	(void) signal(SIGEMT, onintr);
130	(void) signal(SIGFPE, onintr);
131	(void) signal(SIGBUS, onintr);
132	(void) signal(SIGSEGV, onintr);
133	(void) signal(SIGSYS, onintr);
134	(void) signal(SIGTERM, SIG_IGN);
135	uucpname(Myname);
136	Ofn = 1;
137	Ifn = 0;
138	*_Sfile = '\0';
139
140	/*
141	 * determine id of user starting remote
142	 * execution
143	 */
144	guinfo(Uid, User);
145	STRNCPY(Loginuser,User);
146
147	*Sgrade = NULLCHAR;
148
149	/*
150	 * this is a check to see if we are using the administrator
151	 * defined service grade. The GRADES file will determine if
152	 * we are. If so then setup the default grade variables.
153	 */
154
155	if (eaccess(GRADES, 04) != -1) {
156		Grade = 'A';
157		Sgrades = TRUE;
158		STRNCPY(Sgrade, "default");
159	}
160
161	/*
162	 * create/append command log
163	 */
164	commandlog(argc,argv);
165
166	/*
167	 * since getopt() can't handle the pipe input option '-';
168	 * change it to "-p"
169	 */
170	for (i=1; i<argc  &&  *argv[i] == '-'; i++)
171	    if (EQUALS(argv[i], "-"))
172		argv[i] = "-p";
173
174	while ((i = getopt(argc, argv, "a:bcCjg:nprs:x:z")) != EOF) {
175		switch(i){
176
177		/*
178		 * use this name in the U line
179		 */
180		case 'a':
181			retaddr = optarg;
182			break;
183
184		/*
185		 * if return code non-zero, return command's input
186		 */
187		case 'b':
188			bringback = 1;
189			break;
190
191		/* do not make local copies of files to be sent (default) */
192		case 'c':
193			cflag = 0;
194			break;
195
196		/* make local copies of files to be sent */
197		case 'C':
198			cflag = 1;
199			break;
200
201		/*
202		 * set priority of request
203		 */
204		case 'g':
205			if (!Sgrades) {
206				if (strlen(optarg) < (size_t) 2 && isalnum(*optarg))
207					Grade = *optarg;
208				else {
209					(void) fprintf(stderr, gettext("No"
210					    " administrator defined service"
211					    " grades available on this"
212					    " machine.\n"));
213					(void) fprintf(stderr, gettext("UUCP"
214					    " service grades range from"
215					    " [A-Z][a-z] only.\n"));
216					cleanup(-1);
217				}
218			}
219			else {
220				STRNCPY(Sgrade, optarg);
221				if (vergrd(Sgrade) != SUCCESS)
222					cleanup(FAIL);
223			}
224			break;
225
226
227		case 'j':	/* job id */
228			jflag = 1;
229			break;
230
231
232		/*
233		 * do not send failure notification to user
234		 */
235		case 'n':
236			nflag++;
237			break;
238
239		/*
240		 * send success notification to user
241		 */
242		case 'z':
243			zflag++;
244			break;
245
246		/*
247		 * -p or - option specifies input from pipe
248		 */
249		case 'p':
250			pipein = 1;
251			break;
252
253		/*
254		 * do not start transfer
255		 */
256		case 'r':
257			startjob = 0;
258			break;
259
260		case 's':
261			fopt = optarg;
262			_Statop++;
263			break;
264
265		/*
266		 * debugging level
267		 */
268		case 'x':
269			Debug = atoi(optarg);
270			if (Debug <= 0)
271				Debug = 1;
272			break;
273
274		default:
275			usage();
276		}
277	}
278
279	DEBUG(4, "\n\n** %s **\n", "START");
280
281	if( optind >= argc )
282		usage();
283
284	/*
285	 * copy arguments into a buffer for later
286	 * processing
287	 */
288	inargs[0] = '\0';
289	for (; optind < argc; optind++) {
290		DEBUG(4, "arg - %s:", argv[optind]);
291		STRNCAT(inargs, " ");
292		STRNCAT(inargs, argv[optind]);
293	}
294
295	/*
296	 * get working directory and change
297	 * to spool directory
298	 */
299	DEBUG(4, "arg - %s\n", inargs);
300	gwd(Wrkdir);
301	if(fopt){
302		if(*fopt != '/') {
303			(void) snprintf(_Sfile, (sizeof(_Sfile) - 1),
304					"%s/%s", Wrkdir, fopt);
305			_Sfile[sizeof(_Sfile) - 1] = '\0';
306		}
307		else {
308			(void) snprintf(_Sfile, (sizeof(_Sfile) - 1),
309					"%s", fopt);
310			_Sfile[sizeof(_Sfile) - 1] = '\0';
311		}
312	}
313	else
314		strcpy(_Sfile, "dummy");
315
316	if (chdir(WORKSPACE) != 0) {
317	    (void) fprintf(stderr,
318		gettext("No spool directory - %s - get help\n"), WORKSPACE);
319	    cleanup(EX_OSERR);
320	}
321	/*
322	 * find remote system name
323	 * remote name is first to know that
324	 * is not > or <
325	 */
326	ap = inargs;
327	xsys[0] = '\0';
328	while ((ap = getprm(ap, (char *)NULL, prm)) != NULL) {
329		if (prm[0] == '>' || prm[0] == '<') {
330			ap = getprm(ap, (char *)NULL, prm);
331			continue;
332		}
333
334		/*
335		 * split name into system name
336		 * and command name
337		 */
338		(void) split(prm, xsys, CNULL, rest);
339		break;
340	}
341	if (xsys[0] == '\0')
342		STRNCPY(xsys, Myname);
343	STRNCPY(Rmtname, xsys);
344	DEBUG(4, "xsys %s\n", xsys);
345
346	/* get real Myname - it depends on who I'm calling--Rmtname */
347	(void) mchFind(Rmtname);
348	myName(Myname);
349
350	/*
351	 * check to see if system name is valid
352	 */
353	if (versys(xsys) != 0) {
354		/*
355		 * bad system name
356		 */
357		fprintf(stderr, gettext("bad system name: %s\n"), xsys);
358		if (fprx != NULL)
359			(void) fclose(fprx);
360		if (fpc != NULL)
361			(void) fclose(fpc);
362		cleanup(EX_NOHOST);
363	}
364
365	DEBUG(6, "User %s\n", User);
366	if (retaddr == NULL)
367		retaddr = User;
368
369	/*
370	 * initialize command buffer
371	 */
372	*cmd = '\0';
373
374	/*
375	 * generate JCL files to work from
376	 */
377
378	/*
379	 * fpc is the C. file for the local site.
380	 * collect commands into cfile.
381	 * commit if not empty (at end).
382	 *
383	 * the appropriate C. file.
384	 */
385	gename(CMDPRE, xsys, Grade, cfile);
386	DEBUG(9, "cfile = %s\n", cfile);
387	ASSERT(access(cfile, 0) != 0, Fl_EXISTS, cfile, errno);
388	fpc = fdopen(ret = creat(cfile, CFILEMODE), "w");
389	ASSERT(ret >= 0 && fpc != NULL, Ct_OPEN, cfile, errno);
390	setbuf(fpc, CNULL);
391
392	/*  set Jobid -- C.jobid */
393	STRNCPY(Jobid, BASENAME(cfile, '.'));
394
395	/*
396	 * rxfile is the X. file for the job, fprx is its stream ptr.
397	 * if the command is to be executed locally, rxfile becomes
398	 * a local X. file, otherwise we send it as a D. file to the
399	 * remote site.
400	 */
401
402	gename(DATAPRE, xsys, 'X', rxfile);
403	DEBUG(9, "rxfile = %s\n", rxfile);
404	ASSERT(access(rxfile, 0) != 0, Fl_EXISTS, rxfile, errno);
405	fprx = fdopen(ret = creat(rxfile, DFILEMODE), "w");
406	ASSERT(ret >= 0 && fprx != NULL, Ct_WRITE, rxfile, errno);
407	setbuf(fprx, CNULL);
408	clearerr(fprx);
409
410	(void) fprintf(fprx,"%c %s %s\n", X_USER, User, Myname);
411	if (zflag) {
412		(void) fprintf(fprx, "%c return status on success\n",
413			X_COMMENT);
414		(void) fprintf(fprx,"%c\n", X_SENDZERO);
415	}
416
417	if (nflag) {
418		(void) fprintf(fprx, "%c don't return status on failure\n",
419			X_COMMENT);
420		(void) fprintf(fprx,"%c\n", X_SENDNOTHING);
421	} else {
422		(void) fprintf(fprx, "%c return status on failure\n",
423			X_COMMENT);
424		fprintf(fprx,"%c\n", X_NONZERO);
425	}
426
427	if (bringback) {
428		(void) fprintf(fprx, "%c return input on abnormal exit\n",
429			X_COMMENT);
430		(void) fprintf(fprx,"%c\n", X_BRINGBACK);
431	}
432
433	if (_Statop)
434		(void) fprintf(fprx,"%c %s\n", X_MAILF, _Sfile);
435
436	if (retaddr != NULL) {
437		(void) fprintf(fprx, "%c return address for status or input return\n",
438			X_COMMENT);
439		(void) fprintf(fprx,"%c %s\n", X_RETADDR, retaddr);
440	}
441
442	(void) fprintf(fprx, "%c job id for status reporting\n", X_COMMENT);
443	(void) fprintf(fprx,"%c %s\n", X_JOBID, Jobid);
444
445	/*
446	 * create a JCL file to spool pipe input into
447	 */
448	if (pipein) {
449		/*
450		 * fpd is the D. file into which we now read
451		 * input from stdin
452		 */
453
454		gename(DATAPRE, Myname, 'B', dfile);
455
456		ASSERT(access(dfile, 0) != 0, Fl_EXISTS, dfile, errno);
457		fpd = fdopen(ret = creat(dfile, DFILEMODE), "w");
458		ASSERT(ret >= 0 && fpd != NULL, Ct_OPEN, dfile, errno);
459
460		/*
461		 * read pipe to EOF
462		 */
463		while (!feof(stdin)) {
464			ret = fread(buf, 1, BUFSIZ, stdin);
465			ASSERT(fwrite(buf, 1, ret, fpd) == ret, Ct_WRITE,
466				dfile, errno);
467		}
468		ASSERT(fflush(fpd) != EOF && ferror(fpd) == 0, Ct_WRITE, dfile, errno);
469		(void) fclose(fpd);
470
471		/*
472		 * if command is to be executed on remote
473		 * create extra JCL
474		 */
475		if (!EQUALSN(Myname, xsys, MAXBASENAME)) {
476			GENSEND(fpc, dfile, dfile, dfile);
477		}
478
479		/*
480		 * create file for X_ commands
481		 */
482		(void) fprintf(fprx, "%c %s\n", X_RQDFILE, dfile);
483		(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
484
485		if (EQUALS(Myname, xsys))
486			wfcommit(dfile, dfile, xsys);
487
488	}
489	/*
490	 * parse command
491	 */
492	ap = inargs;
493	while ((ap = getprm(ap, (char *)NULL, prm)) != NULL) {
494		DEBUG(4, "prm - %s\n", prm);
495
496		/*
497		 * redirection of I/O
498		 */
499		if ( prm[0] == '<' ) {
500		    if (prm[1] == '<') {
501			fprintf(stderr,
502			  gettext("'<<' may not be used in command\n"));
503			cleanup(EX_USAGE);
504		    }
505		    redir = X_STDIN;
506		    continue;
507		}
508		if ( prm[0] == '>' ) {
509		    if (prm[1] == '>') {
510			fprintf(stderr,
511			  gettext("'>>' may not be used in command\n"));
512			cleanup(EX_USAGE);
513		    }
514		    if (prm[1] == '|') {
515			fprintf(stderr,
516			  gettext("'>|' may not be used in command\n"));
517			cleanup(EX_USAGE);
518		    }
519		    if (prm[1] == '&') {
520			fprintf(stderr,
521			  gettext("'>&' may not be used in command\n"));
522			cleanup(EX_USAGE);
523		    }
524		    redir = X_STDOUT;
525		    continue;
526		}
527		if ( EQUALS(prm, "2>") ) {
528		    redir = X_STDERR;
529		    continue;
530		}
531
532		/*
533		 * some terminator
534		 */
535		if ( prm[0] == '|' || prm[0] == '^'
536		  || prm[0] == '&' || prm[0] == ';') {
537			if (*cmd != '\0')	/* not 1st thing on line */
538				APPCMD(prm);
539			command = TRUE;
540			continue;
541		}
542
543		/*
544		 * process command or file or option
545		 * break out system and file name and
546		 * use default if necessary
547		 */
548		ret = split(prm, syspart, CNULL, rest);
549		DEBUG(4, "syspart -> %s, ", syspart);
550		DEBUG(4, "rest -> %s, ", rest);
551		DEBUG(4, "ret -> %d\n", ret);
552
553		if (command  && redir == '\0') {
554			/*
555			 * command
556			 */
557			APPCMD(rest);
558			command = FALSE;
559			continue;
560		}
561
562		if (syspart[0] == '\0') {
563			STRNCPY(syspart, Myname);
564			DEBUG(6, "syspart -> %s\n", syspart);
565		} else if (versys(syspart) != 0) {
566			/*
567			 * bad system name
568			 */
569			fprintf(stderr,
570			    gettext("bad system name: %s\n"), syspart);
571			if (fprx != NULL)
572				(void) fclose(fprx);
573			if (fpc != NULL)
574				(void) fclose(fpc);
575			cleanup(EX_NOHOST);
576		}
577
578		/*
579		 * process file or option
580		 */
581
582		/*
583		 * process file argument
584		 * expand filename and create JCL card for
585		 * redirected output
586		 * e.g., X file sys
587		 */
588		if ((redir == X_STDOUT) || (redir == X_STDERR)) {
589			if (rest[0] != '~')
590				if (ckexpf(rest))
591					cleanup(EX_OSERR);
592			ASSERT(fprintf(fprx, "%c %s %s\n", redir, rest,
593				syspart) >= 0, Ct_WRITE, rxfile, errno);
594			redir = '\0';
595			continue;
596		}
597
598		/*
599		 * if no system specified, then being
600		 * processed locally
601		 */
602		if (ret == NOSYSPART && redir == '\0') {
603
604			/*
605			 * option
606			 */
607			APPCMD(rest);
608			continue;
609		}
610
611
612		/* local xeqn + local file  (!x !f) */
613		if ((EQUALSN(xsys, Myname, MAXBASENAME))
614		 && (EQUALSN(xsys, syspart, MAXBASENAME))) {
615			/*
616			 * create JCL card
617			 */
618			if (ckexpf(rest))
619				cleanup(EX_OSERR);
620			/*
621			 * JCL card for local input
622			 * e.g., I file
623			 */
624			if (redir == X_STDIN) {
625				(void) fprintf(fprx, "%c %s\n", X_STDIN, rest);
626			} else
627				APPCMD(rest);
628			ASSERT(fprx != NULL, Ct_WRITE, rxfile, errno);
629			redir = '\0';
630			continue;
631		}
632
633		/* remote xeqn + local file (sys!x !f) */
634		if (EQUALSN(syspart, Myname, MAXBASENAME)) {
635			/*
636			 * check access to local file
637			 * if cflag is set, copy to spool directory
638			 * otherwise, just mention it in the X. file
639			 */
640			if (ckexpf(rest))
641				cleanup(EX_OSERR);
642			DEBUG(4, "rest %s\n", rest);
643
644			/* see if I can read this file as read uid, gid */
645			if (uidstat(rest, &stbuf) != 0) {
646			    (void) fprintf(stderr,
647			      gettext("can't get file status %s\n"), rest);
648			    cleanup(EX_OSERR);
649			}
650			/* XXX - doesn't check group list */
651			if ( !(stbuf.st_mode & ANYREAD)
652		  	  && !(stbuf.st_uid == Uid && stbuf.st_mode & 0400)
653		  	  && !(stbuf.st_gid ==getgid() && stbuf.st_mode & 0040)
654		  	  ) {
655				fprintf(stderr,
656				    gettext("permission denied %s\n"), rest);
657				cleanup(EX_CANTCREAT);
658			}
659
660			/* D. file for sending local file */
661			gename(DATAPRE, xsys, 'A', dfile);
662
663			if (cflag || !(stbuf.st_mode & ANYREAD)) {
664				/* make local copy */
665				if (uidxcp(rest, dfile) != 0) {
666				    fprintf(stderr,
667					gettext("can't copy %s\n"), rest);
668				    cleanup(EX_CANTCREAT);
669				}
670				(void) chmod(dfile, DFILEMODE);
671				/* generate 'send' entry in command file */
672				GENSEND(fpc, rest, dfile, dfile);
673			} else		/* don't make local copy */
674				GENSEND(fpc, rest, dfile, dfile);
675
676			/*
677			 * JCL cards for redirected input in X. file,
678			 * e.g.
679			 * I D.xxx
680			 * F D.xxx
681			 */
682			if (redir == X_STDIN) {
683				/*
684				 * don't bother making a X_RQDFILE line that
685				 * renames stdin on the remote side, since the
686				 * remote command can't know its name anyway
687				 */
688				(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
689				(void) fprintf(fprx, "%c %s\n", X_RQDFILE, dfile);
690			} else {
691				APPCMD(BASENAME(rest, '/'));;
692				/*
693				 * generate X. JCL card that specifies
694				 * F file
695				 */
696				(void) fprintf(fprx, "%c %s %s\n", X_RQDFILE,
697				 dfile, BASENAME(rest, '/'));
698			}
699			redir = '\0';
700
701			continue;
702		}
703
704		/* local xeqn + remote file (!x sys!f ) */
705		if (EQUALS(Myname, xsys)) {
706			/*
707			 * expand receive file name
708			 */
709			if (ckexpf(rest))
710				cleanup(EX_OSERR);
711			/*
712			 * strategy:
713			 * request rest from syspart.  when it arrives,
714			 * we can run the command.
715			 *
716			 * tfile is command file for receive from remote.
717			 * we defer commiting until later so
718			 * that only one C. file is created per site.
719			 *
720			 * dfile is name of data file to receive into;
721			 * we don't use it, just name it.
722			 *
723			 * once the data file arrives from syspart.
724			 * arrange so that in the X. file (fprx), rest is
725			 * required and named appropriately.  this
726			 * necessitates local access to SPOOL/syspart, which
727			 * is handled by a hook in uuxqt that allows files
728			 * in SPOOL/syspart to be renamed on the F line.
729			 *
730			 * pictorially:
731			 *
732			 * ===== syspart/C.syspart.... =====	(tfile)
733			 * R rest D.syspart...			(dfile)
734			 *
735			 *
736			 * ===== local/X.local... =====		(fprx)
737			 * F SPOOL/syspart/D.syspart... rest	(dfile)
738			 *
739			 *
740			 */
741			if (gtcfile(tfile, syspart) != SUCCESS) {
742				gename(CMDPRE, syspart, 'R', tfile);
743
744				ASSERT(access(tfile, 0) != 0,
745				    Fl_EXISTS, tfile, errno);
746				svcfile(tfile, syspart, Sgrade);
747				(void) close(creat(tfile, CFILEMODE));
748			}
749			fp = fopen(tfile, "a");
750			ASSERT(fp != NULL, Ct_OPEN, tfile, errno);
751			setbuf(fp, CNULL);
752			gename(DATAPRE, syspart, 'R', dfile);
753
754			/* prepare JCL card to receive file */
755			GENRCV(fp, rest, dfile);
756			ASSERT(fflush(fp) != EOF && ferror(fp) == 0, Ct_WRITE, dfile, errno);
757			(void) fclose(fp);
758			if (rest[0] != '~')
759				if (ckexpf(rest))
760					cleanup(EX_OSERR);
761
762			/*
763			 * generate receive entries
764			 */
765			if (redir == X_STDIN) {
766				(void) fprintf(fprx,
767					"%c %s/%s/%s\n", X_RQDFILE, Spool,
768					syspart, dfile);
769				(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
770			} else {
771				(void) fprintf(fprx, "%c %s/%s/%s %s\n",
772				X_RQDFILE, Spool, syspart, dfile,
773				BASENAME(rest, '/'));
774				APPCMD(BASENAME(rest, '/'));
775			}
776
777			redir = '\0';
778			continue;
779		}
780
781		/* remote xeqn/file, different remotes (xsys!cmd syspart!rest) */
782		if (!EQUALS(syspart, xsys)) {
783			/*
784			 * strategy:
785			 * request rest from syspart.
786			 *
787			 * set up a local X. file that will send rest to xsys,
788			 * once it arrives from syspart.
789			 *
790			 * arrange so that in the xsys D. file (fated to become
791			 * an X. file on xsys), rest is required and named.
792			 *
793			 * pictorially:
794			 *
795			 * ===== syspart/C.syspart.... =====	(tfile)
796			 * R rest D.syspart...			(dfile)
797			 *
798			 *
799			 * ===== local/X.local... =====		(t2file)
800			 * F Spool/syspart/D.syspart... rest	(dfile)
801			 * C uucp -C rest D.syspart...		(dfile)
802			 *
803			 * ===== xsys/D.xsysG....		(fprx)
804			 * F D.syspart... rest			(dfile)
805			 * or, in the case of redir == '<'
806			 * F D.syspart...			(dfile)
807			 * I D.syspart...			(dfile)
808			 *
809			 * while we do push rest around a bunch,
810			 * we use the protection scheme to good effect.
811			 *
812			 * we must rely on uucp's treatment of requests
813			 * from XQTDIR to get the data file to the right
814			 * place ultimately.
815			 */
816
817			/* build (or append to) C.syspart... */
818			if (gtcfile(tfile, syspart) != SUCCESS) {
819				gename(CMDPRE, syspart, 'R', tfile);
820
821				ASSERT(access(tfile, 0) != 0,
822				    Fl_EXISTS, tfile, errno);
823				svcfile(tfile, syspart, Sgrade);
824				(void) close(creat(tfile, CFILEMODE));
825			}
826			fp = fopen(tfile, "a");
827			ASSERT(fp != NULL, Ct_OPEN, tfile, errno);
828			setbuf(fp, CNULL);
829			gename(DATAPRE, syspart, 'R', dfile);
830			GENRCV(fp, rest, dfile);
831			ASSERT(fflush(fp) != EOF && ferror(fp) == 0, Ct_WRITE, dfile, errno);
832			(void) fclose(fp);
833
834			/* build local/X.localG... */
835			/* name might collide with rxfile; no real danger */
836			gename(XQTPRE, Myname, Grade, t2file);
837			ASSERT(access(t2file, 0)!=0, Fl_EXISTS, t2file, errno);
838			(void) close(creat(t2file, CFILEMODE));
839			fp = fopen(t2file, "w");
840			ASSERT(fp != NULL, Ct_OPEN, t2file, errno);
841			setbuf(fp, CNULL);
842			(void) fprintf(fp, "%c third party request, job id\n",
843									X_COMMENT);
844			(void) fprintf(fp, "%c %s\n", X_JOBID, Jobid);
845			(void) fprintf(fp, "%c %s/%s/%s %s\n", X_RQDFILE,
846				Spool, syspart, dfile, BASENAME(rest, '/'));
847			(void) fprintf(fp, "%c uucp -C %s %s!%s\n",
848				X_CMD, BASENAME(rest, '/'), xsys, dfile);
849			ASSERT(fflush(fp) != EOF && ferror(fp) == 0, Ct_WRITE, t2file, errno);
850			(void) fclose(fp);
851
852			/* put t2file where uuxqt can get at it */
853			wfcommit(t2file, t2file, Myname);
854
855			/* generate xsys/X.sysG... cards */
856			if (redir == X_STDIN) {
857				(void) fprintf(fprx, "%c %s\n",
858					X_RQDFILE, dfile);
859				(void) fprintf(fprx, "%c %s\n", X_STDIN, dfile);
860			} else {
861				(void) fprintf(fprx, "%c %s %s\n", X_RQDFILE,
862				 dfile, BASENAME(rest, '/'));
863				APPCMD(BASENAME(rest, '/'));
864			}
865			redir = '\0';
866			continue;
867		}
868
869		/* remote xeqn + remote file, same remote (sys!x sys!f) */
870		if (rest[0] != '~')	/* expand '~' on remote */
871			if (ckexpf(rest))
872				cleanup(EX_OSERR);
873		if (redir == X_STDIN) {
874			(void) fprintf(fprx, "%c %s\n", X_STDIN, rest);
875		}
876		else
877			APPCMD(rest);
878		redir = '\0';
879		continue;
880
881	}
882
883	/*
884	 * place command to be executed in JCL file
885	 */
886	(void) fprintf(fprx, "%c %s\n", X_CMD, cmd);
887	ASSERT(fflush(fprx) != EOF && ferror(fprx) == 0, Ct_WRITE, rxfile, errno);
888	(void) fclose(fprx);		/* rxfile is ready for commit */
889	logent(cmd, "QUEUED");
890
891	gename(XQTPRE, Myname, Grade, tfile);
892	if (EQUALS(xsys, Myname)) {
893		/* local xeqn -- use X_ file here */
894		/* this use of the X_ file can not collide with the earlier one */
895		wfcommit(rxfile, tfile, xsys);
896
897		/*
898		 * see if -r option requested JCL to be queued only
899		 */
900		if (startjob)
901			xuuxqt(Myname);
902	} else {
903		/* remote xeqn -- send rxfile to remote */
904		/* put it in a place where cico can get at it */
905		/* X_ file name might collide with an earlier use, */
906		/* but that one lives locally, while this one gets shipped */
907
908		GENSEND(fpc, rxfile, tfile, rxfile);
909	}
910
911	cfileUsed = (ftell(fpc) != 0L);	/* was cfile used? */
912	ASSERT(fflush(fpc) != EOF && ferror(fpc) == 0, Ct_WRITE, cfile, errno);
913	(void) fclose(fpc);
914
915	/* commit C. files for remote receive */
916
917	commitall();
918
919	/*
920	 * has any command been placed in command JCL file
921	 */
922	if (cfileUsed) {
923
924		svcfile(cfile, xsys, Sgrade);
925		commitall();
926
927		/*
928		 * see if -r option requested JCL to be queued only
929		 */
930		if (startjob)
931			xuucico(xsys);
932	} else
933		unlink(cfile);
934
935	if (jflag) {	/* print Jobid */
936		STRNCPY(Jobid, jid());
937		printf("%s\n", Jobid);
938	}
939
940	cleanup(0);
941	/* NOTREACHED */
942	return (0);
943}
944
945
946/*
947 * cleanup and unlink if error
948 *	code	-> exit code
949 * return:
950 *	none
951 */
952void
953cleanup(code)
954int code;
955{
956	static int first = 1;
957
958	/* prevent recursion on errors */
959	if (first) {
960		first = 0;
961		rmlock(CNULL);
962		if (code) {
963			fprintf(stderr, gettext("uux failed ( %d )\n"), code);
964			wfabort();
965		}
966	}
967	DEBUG(1, "exit code %d\n", code);
968	if (code < 0)
969		exit(-code);
970	else
971		exit(code);
972}
973
974/*
975 * catch signal then cleanup and exit
976 */
977static void
978onintr(inter)
979int inter;
980{
981	char str[30];
982	(void) signal(inter, SIG_IGN);
983	(void) sprintf(str, "XSIGNAL %d", inter);
984	logent(str, "XCAUGHT");
985	cleanup(EX_TEMPFAIL);
986}
987
988
989static void
990usage()
991{
992	(void) fprintf(stderr, gettext("Usage: %s [-bcCjnprz] [-a NAME]"
993	    " [-g GRADE] [-s FILE] [-x NUM] command-string\n"), Progname);
994	exit(EX_USAGE);
995}
996