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