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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
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);\
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);\
46 #define STRNCPY(str1, str2) { \
47 (void) strncpy(str1, str2, (sizeof(str1) - 1)); \
48 str1[sizeof(str1) - 1] = '\0'; \
50 #define STRNCAT(str1, str2) { \
51 (void) strncat(str1, str2, \
52 (sizeof(str1) - 1 - strlen(str1))); \
54 #define APPCMD(p) {STRNCAT(cmd, p); STRNCAT(cmd, " ");}
56 static char _Sfile
[MAXFULLNAME
];
58 char Sgrade
[NAMESIZE
];
67 main(argc
, argv
, envp
)
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 */
80 short jflag
= 0; /* -j flag output Jobid */
81 int bringback
= 0; /* return stdin to invoker on error */
84 char redir
= '\0'; /* X_STDIN, X_STDOUT, X_STDERR as approprite */
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 */
96 char syspart
[MAXFULLNAME
], rest
[BUFSIZ
];
97 char xsys
[MAXFULLNAME
];
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 */
107 (void) textdomain(TEXT_DOMAIN
);
109 /* we want this to run as uucp, even if the kernel doesn't */
111 Euid
= geteuid(); /* this should be UUCPUID */
115 /* init environment for fork-exec */
119 STRNCPY(Logfile
, LOGUUX
);
122 * determine local system name
124 (void) strcpy(Progname
, "uux");
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
);
141 * determine id of user starting remote
145 STRNCPY(Loginuser
,User
);
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.
155 if (eaccess(GRADES
, 04) != -1) {
158 STRNCPY(Sgrade
, "default");
162 * create/append command log
164 commandlog(argc
,argv
);
167 * since getopt() can't handle the pipe input option '-';
170 for (i
=1; i
<argc
&& *argv
[i
] == '-'; i
++)
171 if (EQUALS(argv
[i
], "-"))
174 while ((i
= getopt(argc
, argv
, "a:bcCjg:nprs:x:z")) != EOF
) {
178 * use this name in the U line
185 * if return code non-zero, return command's input
191 /* do not make local copies of files to be sent (default) */
196 /* make local copies of files to be sent */
202 * set priority of request
206 if (strlen(optarg
) < (size_t) 2 && isalnum(*optarg
))
209 (void) fprintf(stderr
, gettext("No"
210 " administrator defined service"
211 " grades available on this"
213 (void) fprintf(stderr
, gettext("UUCP"
214 " service grades range from"
215 " [A-Z][a-z] only.\n"));
220 STRNCPY(Sgrade
, optarg
);
221 if (vergrd(Sgrade
) != SUCCESS
)
227 case 'j': /* job id */
233 * do not send failure notification to user
240 * send success notification to user
247 * -p or - option specifies input from pipe
254 * do not start transfer
269 Debug
= atoi(optarg
);
279 DEBUG(4, "\n\n** %s **\n", "START");
285 * copy arguments into a buffer for later
289 for (; optind
< argc
; optind
++) {
290 DEBUG(4, "arg - %s:", argv
[optind
]);
291 STRNCAT(inargs
, " ");
292 STRNCAT(inargs
, argv
[optind
]);
296 * get working directory and change
299 DEBUG(4, "arg - %s\n", inargs
);
303 (void) snprintf(_Sfile
, (sizeof(_Sfile
) - 1),
304 "%s/%s", Wrkdir
, fopt
);
305 _Sfile
[sizeof(_Sfile
) - 1] = '\0';
308 (void) snprintf(_Sfile
, (sizeof(_Sfile
) - 1),
310 _Sfile
[sizeof(_Sfile
) - 1] = '\0';
314 strcpy(_Sfile
, "dummy");
316 if (chdir(WORKSPACE
) != 0) {
317 (void) fprintf(stderr
,
318 gettext("No spool directory - %s - get help\n"), WORKSPACE
);
322 * find remote system name
323 * remote name is first to know that
328 while ((ap
= getprm(ap
, (char *)NULL
, prm
)) != NULL
) {
329 if (prm
[0] == '>' || prm
[0] == '<') {
330 ap
= getprm(ap
, (char *)NULL
, prm
);
335 * split name into system name
338 (void) split(prm
, xsys
, CNULL
, rest
);
342 STRNCPY(xsys
, Myname
);
343 STRNCPY(Rmtname
, xsys
);
344 DEBUG(4, "xsys %s\n", xsys
);
346 /* get real Myname - it depends on who I'm calling--Rmtname */
347 (void) mchFind(Rmtname
);
351 * check to see if system name is valid
353 if (versys(xsys
) != 0) {
357 fprintf(stderr
, gettext("bad system name: %s\n"), xsys
);
365 DEBUG(6, "User %s\n", User
);
370 * initialize command buffer
375 * generate JCL files to work from
379 * fpc is the C. file for the local site.
380 * collect commands into cfile.
381 * commit if not empty (at end).
383 * the appropriate C. file.
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
);
392 /* set Jobid -- C.jobid */
393 STRNCPY(Jobid
, BASENAME(cfile
, '.'));
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
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
);
410 (void) fprintf(fprx
,"%c %s %s\n", X_USER
, User
, Myname
);
412 (void) fprintf(fprx
, "%c return status on success\n",
414 (void) fprintf(fprx
,"%c\n", X_SENDZERO
);
418 (void) fprintf(fprx
, "%c don't return status on failure\n",
420 (void) fprintf(fprx
,"%c\n", X_SENDNOTHING
);
422 (void) fprintf(fprx
, "%c return status on failure\n",
424 fprintf(fprx
,"%c\n", X_NONZERO
);
428 (void) fprintf(fprx
, "%c return input on abnormal exit\n",
430 (void) fprintf(fprx
,"%c\n", X_BRINGBACK
);
434 (void) fprintf(fprx
,"%c %s\n", X_MAILF
, _Sfile
);
436 if (retaddr
!= NULL
) {
437 (void) fprintf(fprx
, "%c return address for status or input return\n",
439 (void) fprintf(fprx
,"%c %s\n", X_RETADDR
, retaddr
);
442 (void) fprintf(fprx
, "%c job id for status reporting\n", X_COMMENT
);
443 (void) fprintf(fprx
,"%c %s\n", X_JOBID
, Jobid
);
446 * create a JCL file to spool pipe input into
450 * fpd is the D. file into which we now read
454 gename(DATAPRE
, Myname
, 'B', dfile
);
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
);
463 while (!feof(stdin
)) {
464 ret
= fread(buf
, 1, BUFSIZ
, stdin
);
465 ASSERT(fwrite(buf
, 1, ret
, fpd
) == ret
, Ct_WRITE
,
468 ASSERT(fflush(fpd
) != EOF
&& ferror(fpd
) == 0, Ct_WRITE
, dfile
, errno
);
472 * if command is to be executed on remote
475 if (!EQUALSN(Myname
, xsys
, MAXBASENAME
)) {
476 GENSEND(fpc
, dfile
, dfile
, dfile
);
480 * create file for X_ commands
482 (void) fprintf(fprx
, "%c %s\n", X_RQDFILE
, dfile
);
483 (void) fprintf(fprx
, "%c %s\n", X_STDIN
, dfile
);
485 if (EQUALS(Myname
, xsys
))
486 wfcommit(dfile
, dfile
, xsys
);
493 while ((ap
= getprm(ap
, (char *)NULL
, prm
)) != NULL
) {
494 DEBUG(4, "prm - %s\n", prm
);
499 if ( prm
[0] == '<' ) {
502 gettext("'<<' may not be used in command\n"));
508 if ( prm
[0] == '>' ) {
511 gettext("'>>' may not be used in command\n"));
516 gettext("'>|' may not be used in command\n"));
521 gettext("'>&' may not be used in command\n"));
527 if ( EQUALS(prm
, "2>") ) {
535 if ( prm
[0] == '|' || prm
[0] == '^'
536 || prm
[0] == '&' || prm
[0] == ';') {
537 if (*cmd
!= '\0') /* not 1st thing on line */
544 * process command or file or option
545 * break out system and file name and
546 * use default if necessary
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
);
553 if (command
&& redir
== '\0') {
562 if (syspart
[0] == '\0') {
563 STRNCPY(syspart
, Myname
);
564 DEBUG(6, "syspart -> %s\n", syspart
);
565 } else if (versys(syspart
) != 0) {
570 gettext("bad system name: %s\n"), syspart
);
579 * process file or option
583 * process file argument
584 * expand filename and create JCL card for
588 if ((redir
== X_STDOUT
) || (redir
== X_STDERR
)) {
592 ASSERT(fprintf(fprx
, "%c %s %s\n", redir
, rest
,
593 syspart
) >= 0, Ct_WRITE
, rxfile
, errno
);
599 * if no system specified, then being
602 if (ret
== NOSYSPART
&& redir
== '\0') {
612 /* local xeqn + local file (!x !f) */
613 if ((EQUALSN(xsys
, Myname
, MAXBASENAME
))
614 && (EQUALSN(xsys
, syspart
, MAXBASENAME
))) {
621 * JCL card for local input
624 if (redir
== X_STDIN
) {
625 (void) fprintf(fprx
, "%c %s\n", X_STDIN
, rest
);
628 ASSERT(fprx
!= NULL
, Ct_WRITE
, rxfile
, errno
);
633 /* remote xeqn + local file (sys!x !f) */
634 if (EQUALSN(syspart
, Myname
, MAXBASENAME
)) {
636 * check access to local file
637 * if cflag is set, copy to spool directory
638 * otherwise, just mention it in the X. file
642 DEBUG(4, "rest %s\n", rest
);
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
);
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)
656 gettext("permission denied %s\n"), rest
);
657 cleanup(EX_CANTCREAT
);
660 /* D. file for sending local file */
661 gename(DATAPRE
, xsys
, 'A', dfile
);
663 if (cflag
|| !(stbuf
.st_mode
& ANYREAD
)) {
664 /* make local copy */
665 if (uidxcp(rest
, dfile
) != 0) {
667 gettext("can't copy %s\n"), rest
);
668 cleanup(EX_CANTCREAT
);
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
);
677 * JCL cards for redirected input in X. file,
682 if (redir
== X_STDIN
) {
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
688 (void) fprintf(fprx
, "%c %s\n", X_STDIN
, dfile
);
689 (void) fprintf(fprx
, "%c %s\n", X_RQDFILE
, dfile
);
691 APPCMD(BASENAME(rest
, '/'));;
693 * generate X. JCL card that specifies
696 (void) fprintf(fprx
, "%c %s %s\n", X_RQDFILE
,
697 dfile
, BASENAME(rest
, '/'));
704 /* local xeqn + remote file (!x sys!f ) */
705 if (EQUALS(Myname
, xsys
)) {
707 * expand receive file name
713 * request rest from syspart. when it arrives,
714 * we can run the command.
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.
720 * dfile is name of data file to receive into;
721 * we don't use it, just name it.
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.
732 * ===== syspart/C.syspart.... ===== (tfile)
733 * R rest D.syspart... (dfile)
736 * ===== local/X.local... ===== (fprx)
737 * F SPOOL/syspart/D.syspart... rest (dfile)
741 if (gtcfile(tfile
, syspart
) != SUCCESS
) {
742 gename(CMDPRE
, syspart
, 'R', tfile
);
744 ASSERT(access(tfile
, 0) != 0,
745 Fl_EXISTS
, tfile
, errno
);
746 svcfile(tfile
, syspart
, Sgrade
);
747 (void) close(creat(tfile
, CFILEMODE
));
749 fp
= fopen(tfile
, "a");
750 ASSERT(fp
!= NULL
, Ct_OPEN
, tfile
, errno
);
752 gename(DATAPRE
, syspart
, 'R', dfile
);
754 /* prepare JCL card to receive file */
755 GENRCV(fp
, rest
, dfile
);
756 ASSERT(fflush(fp
) != EOF
&& ferror(fp
) == 0, Ct_WRITE
, dfile
, errno
);
763 * generate receive entries
765 if (redir
== X_STDIN
) {
767 "%c %s/%s/%s\n", X_RQDFILE
, Spool
,
769 (void) fprintf(fprx
, "%c %s\n", X_STDIN
, dfile
);
771 (void) fprintf(fprx
, "%c %s/%s/%s %s\n",
772 X_RQDFILE
, Spool
, syspart
, dfile
,
773 BASENAME(rest
, '/'));
774 APPCMD(BASENAME(rest
, '/'));
781 /* remote xeqn/file, different remotes (xsys!cmd syspart!rest) */
782 if (!EQUALS(syspart
, xsys
)) {
785 * request rest from syspart.
787 * set up a local X. file that will send rest to xsys,
788 * once it arrives from syspart.
790 * arrange so that in the xsys D. file (fated to become
791 * an X. file on xsys), rest is required and named.
795 * ===== syspart/C.syspart.... ===== (tfile)
796 * R rest D.syspart... (dfile)
799 * ===== local/X.local... ===== (t2file)
800 * F Spool/syspart/D.syspart... rest (dfile)
801 * C uucp -C rest D.syspart... (dfile)
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)
809 * while we do push rest around a bunch,
810 * we use the protection scheme to good effect.
812 * we must rely on uucp's treatment of requests
813 * from XQTDIR to get the data file to the right
817 /* build (or append to) C.syspart... */
818 if (gtcfile(tfile
, syspart
) != SUCCESS
) {
819 gename(CMDPRE
, syspart
, 'R', tfile
);
821 ASSERT(access(tfile
, 0) != 0,
822 Fl_EXISTS
, tfile
, errno
);
823 svcfile(tfile
, syspart
, Sgrade
);
824 (void) close(creat(tfile
, CFILEMODE
));
826 fp
= fopen(tfile
, "a");
827 ASSERT(fp
!= NULL
, Ct_OPEN
, tfile
, errno
);
829 gename(DATAPRE
, syspart
, 'R', dfile
);
830 GENRCV(fp
, rest
, dfile
);
831 ASSERT(fflush(fp
) != EOF
&& ferror(fp
) == 0, Ct_WRITE
, dfile
, errno
);
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
);
842 (void) fprintf(fp
, "%c third party request, job id\n",
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
);
852 /* put t2file where uuxqt can get at it */
853 wfcommit(t2file
, t2file
, Myname
);
855 /* generate xsys/X.sysG... cards */
856 if (redir
== X_STDIN
) {
857 (void) fprintf(fprx
, "%c %s\n",
859 (void) fprintf(fprx
, "%c %s\n", X_STDIN
, dfile
);
861 (void) fprintf(fprx
, "%c %s %s\n", X_RQDFILE
,
862 dfile
, BASENAME(rest
, '/'));
863 APPCMD(BASENAME(rest
, '/'));
869 /* remote xeqn + remote file, same remote (sys!x sys!f) */
870 if (rest
[0] != '~') /* expand '~' on remote */
873 if (redir
== X_STDIN
) {
874 (void) fprintf(fprx
, "%c %s\n", X_STDIN
, rest
);
884 * place command to be executed in JCL file
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");
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
);
898 * see if -r option requested JCL to be queued only
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 */
908 GENSEND(fpc
, rxfile
, tfile
, rxfile
);
911 cfileUsed
= (ftell(fpc
) != 0L); /* was cfile used? */
912 ASSERT(fflush(fpc
) != EOF
&& ferror(fpc
) == 0, Ct_WRITE
, cfile
, errno
);
915 /* commit C. files for remote receive */
920 * has any command been placed in command JCL file
924 svcfile(cfile
, xsys
, Sgrade
);
928 * see if -r option requested JCL to be queued only
935 if (jflag
) { /* print Jobid */
936 STRNCPY(Jobid
, jid());
937 printf("%s\n", Jobid
);
947 * cleanup and unlink if error
956 static int first
= 1;
958 /* prevent recursion on errors */
963 fprintf(stderr
, gettext("uux failed ( %d )\n"), code
);
967 DEBUG(1, "exit code %d\n", code
);
975 * catch signal then cleanup and exit
982 (void) signal(inter
, SIG_IGN
);
983 (void) sprintf(str
, "XSIGNAL %d", inter
);
984 logent(str
, "XCAUGHT");
985 cleanup(EX_TEMPFAIL
);
992 (void) fprintf(stderr
, gettext("Usage: %s [-bcCjnprz] [-a NAME]"
993 " [-g GRADE] [-s FILE] [-x NUM] command-string\n"), Progname
);