xref: /titanic_51/usr/src/cmd/bnu/uux.c (revision 45818ee124adeaaf947698996b4f4c722afc6d1f)
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) {\
37 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);\
38 }
39 #define GENRCV(f, a, b) {\
40 char tbuf[SHORTBUF]; \
41 gename (DATAPRE, xsys, 'Z', tbuf); \
42 ASSERT(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 
56 static char	_Sfile[MAXFULLNAME];
57 static int	_Statop;
58 char Sgrade[NAMESIZE];
59 void cleanup();
60 static void usage();
61 static void onintr();
62 
63 /*
64  *	uux
65  */
66 int
67 main(argc, argv, envp)
68 int argc;
69 char *argv[];
70 char *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  */
952 void
953 cleanup(code)
954 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 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