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