Merge branch 'less_closed'
[unleashed.git] / usr / src / cmd / bnu / uuxqt.c
blob70083f685e5b29c8a8c754fae4f1c14043a1ac90
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include "uucp.h"
33 #include "log.h"
36 * execute commands set up by a uux command,
37 * usually from a remote machine - set by uucp.
40 #ifndef V7
41 #define LOGNAME "LOGNAME=uucp"
42 #else
43 #define LOGNAME "USER=uucp"
44 #endif
46 #define C_COMMAND 1
47 #define C_FILE 2
48 #define BAD_COMMAND 1
49 #define BAD_FILE 2
50 #define USAGEPREFIX "Usage:"
51 #define USAGE "[-x DEBUG] [-s SYSTEM]"
53 char _Xfile[MAXFULLNAME];
54 char _Cmd[2 * BUFSIZ]; /* build up command buffer */
55 int _CargType; /* argument type of next C argument */
57 static void retosndr(), uucpst();
58 static int chkFile();
59 static int doFileChk();
60 void cleanup(), xprocess();
62 int
63 main(argc, argv, envp)
64 int argc;
65 char *argv[];
66 char *envp[];
68 DIR *fp1;
69 struct limits limitval;
70 int ret, maxnumb;
71 char dirname[MAXFULLNAME], lockname[MAXFULLNAME];
72 void onintr();
74 /* Set locale environment variables local definitions */
75 (void) setlocale(LC_ALL, "");
76 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
77 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
78 #endif
79 (void) textdomain(TEXT_DOMAIN);
81 (void) signal(SIGILL, onintr);
82 (void) signal(SIGTRAP, onintr);
83 (void) signal(SIGIOT, onintr);
84 (void) signal(SIGEMT, onintr);
85 (void) signal(SIGFPE, onintr);
86 (void) signal(SIGBUS, onintr);
87 (void) signal(SIGSEGV, onintr);
88 (void) signal(SIGSYS, onintr);
89 (void) signal(SIGPIPE, onintr);
90 (void) signal(SIGTERM, SIG_IGN);
92 /* choose LOGFILE */
93 (void) strcpy(Logfile, LOGUUXQT);
96 * get local system name
98 Env = envp;
99 Nstat.t_qtime = time((time_t *)0);
100 (void) strcpy(Progname, "uuxqt");
101 Pchar = 'Q';
102 uucpname(Myname);
103 Ofn = 1;
104 Ifn = 0;
105 dirname[0] = dirname[MAXFULLNAME-1] = NULLCHAR;
106 while ((ret = getopt(argc, argv, "s:x:")) != EOF) {
107 switch (ret) {
110 * debugging level
112 case 'x':
113 Debug = atoi(optarg);
114 if (Debug <= 0)
115 Debug = 1;
116 break;
118 case 's':
120 * fake out uuxqt and use the argument as if
121 * it were the spool directory for the purpose
122 * of determining what subdirectories to search
123 * EX: mkdir /tmp/foo; touch /tmp/foo/[baz, gorp]
124 * uuxqt -s/tmp/foo
125 * this will cause uuxqt to only run on the sub
126 * baz and gorp in the Spool directory. Trust me.
128 (void) strlcpy(dirname, optarg,
129 (MAXFULLNAME - sizeof (SEQLOCK)));
130 break;
132 default:
133 (void) fprintf(stderr, "%s %s %s\n",
134 gettext(USAGEPREFIX), Progname, gettext(USAGE));
135 exit(1);
138 if (argc != optind) {
139 (void) fprintf(stderr, "%s %s %s\n",
140 gettext(USAGEPREFIX), Progname, gettext(USAGE));
141 exit(1);
144 DEBUG(4, "\n\n** START **\n%s", "");
145 acInit("rexe");
146 scInit("rexe");
147 if (scanlimit("uuxqt", &limitval) == FAIL) {
148 DEBUG(1, "No limits for uuxqt in %s\n", LIMITS);
149 } else {
150 maxnumb = limitval.totalmax;
151 if (maxnumb < 0) {
152 DEBUG(4, "Non-positive limit for uuxqt in %s\n", LIMITS);
153 DEBUG(1, "No limits for uuxqt\n%s", "");
154 } else {
155 DEBUG(4, "Uuxqt limit %d -- ", maxnumb);
156 ret = cuantos(X_LOCKPRE, X_LOCKDIR);
157 DEBUG(4, "found %d -- ", ret);
158 if (maxnumb >= 0 && ret >= maxnumb) {
159 DEBUG(4, "exiting.%s\n", "");
160 exit(0);
162 DEBUG(4, "continuing.%s\n", "");
167 * determine user who started uuxqt (in principle)
169 strcpy(User, "uucp"); /* in case all else fails (can't happen) */
170 Uid = getuid();
171 Euid = geteuid(); /* this should be UUCPUID */
172 guinfo(Euid, User);
174 if (Uid == 0)
175 (void) setuid(UUCPUID);
177 setuucp(User);
178 DEBUG(4, "User - %s\n", User);
179 guinfo(Uid, Loginuser);
183 DEBUG(4, "process\n%s", "");
185 fp1 = opendir(Spool);
186 ASSERT(fp1 != NULL, Ct_OPEN, Spool, errno);
187 if (dirname[0] != NULLCHAR) {
188 /* look for special characters in remote name */
189 if (strpbrk(dirname, Shchar) != NULL) {
190 /* ignore naughty name */
191 DEBUG(4, "Bad remote name '%s'", dirname);
192 errent("BAD REMOTE NAME", dirname, 0, __FILE__, __LINE__);
193 closedir(fp1);
194 cleanup(101);
198 (void) snprintf(lockname, sizeof (lockname), "%s.%s",
199 X_LOCK, dirname);
200 if (mklock(lockname) == SUCCESS) {
201 xprocess(dirname);
202 rmlock(CNULL);
204 } else {
205 while (gdirf(fp1, dirname, Spool) == TRUE) {
206 if (strpbrk(dirname, Shchar) != NULL) {
207 /* skip naughty names */
208 errent("BAD REMOTE NAME", dirname, 0,
209 __FILE__, __LINE__);
210 continue;
212 (void) snprintf(lockname, sizeof (lockname), "%s.%s",
213 X_LOCK, dirname);
214 if (mklock(lockname) != SUCCESS)
215 continue;
216 xprocess(dirname);
217 rmlock(CNULL);
221 closedir(fp1);
222 cleanup(0);
223 /* NOTREACHED */
224 return (0);
227 void
228 cleanup(code)
229 int code;
231 rmlock(CNULL);
232 exit(code);
236 * catch signal then cleanup and exit
238 void
239 onintr(inter)
240 int inter;
242 char str[30];
243 (void) signal(inter, SIG_IGN);
244 (void) sprintf(str, "QSIGNAL %d", inter);
245 logent(str, "QCAUGHT");
246 acEndexe(cpucycle(), PARTIAL); /* stop collecting accounting log */
247 cleanup(-inter);
250 #define XCACHESIZE (4096 / (MAXBASENAME + 1))
251 static char xcache[XCACHESIZE][MAXBASENAME + 1]; /* cache for X. files */
252 static int xcachesize = 0; /* how many left? */
255 * stash an X. file so we can process them sorted first by grade, then by
256 * sequence number
258 static void
259 xstash(file)
260 char *file;
262 if (xcachesize < XCACHESIZE) {
263 DEBUG(4, "stashing %s\n", file);
264 (void) strlcpy(xcache[xcachesize++], file, (MAXBASENAME + 1));
269 * xcompare
270 * comparison routine for for qsort()
272 static int
273 xcompare(f1, f2)
274 const void *f1, *f2;
276 /* assumes file name is X.siteG1234 */
277 /* use -strcmp() so that xstash is sorted largest first */
278 /* pull files out of the stash from largest index to smallest */
280 return (-strcmp((char *)f1 + strlen((char *)f1) - 5,
281 (char *)f2 + strlen((char *)f2) - 5));
285 * xsort
286 * sort the cached X. files,
287 * largest (last) to smallest (next to be processed)
289 static void
290 xsort()
292 DEBUG(4, "xsort: first was %s\n", xcache[0]);
293 qsort(xcache, xcachesize, MAXBASENAME + 1, xcompare);
294 DEBUG(4, "xsort: first is %s\n", xcache[0]);
298 * xget
299 * return smallest X. file in cache
300 * (hint: it's the last one in the array)
302 static int
303 xget(file)
304 char *file;
306 if (xcachesize > 0) {
307 strlcpy(file, xcache[--xcachesize], (MAXBASENAME + 1));
308 DEBUG(4, "xget: returning %s\n", file);
309 return (1);
310 } else {
311 /* avoid horror of xcachesize < 0 (impossible, you say?)! */
312 xcachesize = 0;
313 return (0);
319 * get a file to execute
320 * file -> a read to return filename in
321 * returns:
322 * 0 -> no file
323 * 1 -> file to execute
326 gt_Xfile(file, dir)
327 char *file, *dir;
329 DIR *pdir;
331 if (xcachesize == 0) {
332 /* open spool directory */
333 pdir = opendir(dir);
334 /* this was an ASSERT, but it's not so bad as all that */
335 if (pdir == NULL)
336 return (0);
338 /* scan spool directory looking for X. files to stash */
339 while (gnamef(pdir, file) == TRUE) {
340 DEBUG(4, "gt_Xfile got %s\n", file);
341 /* look for x prefix */
342 if (file[0] != XQTPRE)
343 continue;
345 /* check to see if required files have arrived */
346 if (gotfiles(file))
347 xstash(file);
348 if (xcachesize >= XCACHESIZE)
349 break;
351 closedir(pdir);
352 xsort();
355 return (xget(file));
359 * check for needed files
360 * file -> name of file to check
361 * return:
362 * 0 -> not ready
363 * 1 -> all files ready
366 gotfiles(file)
367 char *file;
369 FILE *fp;
370 struct stat stbuf;
371 char buf[BUFSIZ], rqfile[MAXNAMESIZE];
373 fp = fopen(file, "r");
374 if (fp == NULL)
375 return (FALSE);
377 while (fgets(buf, BUFSIZ, fp) != NULL) {
378 DEBUG(4, "%s\n", buf);
381 * look at required files
383 if (buf[0] != X_RQDFILE)
384 continue;
385 (void) sscanf(&buf[1], "%63s", rqfile);
388 * expand file name
390 expfile(rqfile);
393 * see if file exists
395 if (stat(rqfile, &stbuf) == -1) {
396 fclose(fp);
397 return (FALSE);
401 fclose(fp);
402 return (TRUE);
406 * remove execute files to x-directory
408 * _Xfile is a global
409 * return:
410 * none
412 void
413 rm_Xfiles()
415 FILE *fp;
416 char buf[BUFSIZ], file[MAXNAMESIZE], tfile[MAXNAMESIZE];
417 char tfull[MAXFULLNAME];
419 if ((fp = fopen(_Xfile, "r")) == NULL) {
420 DEBUG(4, "rm_Xfiles: can't read %s\n", _Xfile);
421 return;
425 * (void) unlink each file belonging to job
427 while (fgets(buf, BUFSIZ, fp) != NULL) {
428 if (buf[0] != X_RQDFILE)
429 continue;
430 if (sscanf(&buf[1], "%63s%63s", file, tfile) < 2)
431 continue;
432 (void) snprintf(tfull, sizeof (tfull), "%s/%s", XQTDIR, tfile);
433 (void) unlink(tfull);
435 fclose(fp);
439 * move execute files to x-directory
440 * _Xfile is a global
441 * return:
442 * none
444 void
445 mv_Xfiles()
447 FILE *fp;
448 char buf[BUFSIZ], ffile[MAXFULLNAME], tfile[MAXNAMESIZE];
449 char tfull[MAXFULLNAME];
451 if ((fp = fopen(_Xfile, "r")) == NULL) {
452 DEBUG(4, "mv_Xfiles: can't read %s\n", _Xfile);
453 return;
456 while (fgets(buf, BUFSIZ, fp) != NULL) {
457 if (buf[0] != X_RQDFILE)
458 continue;
459 if (sscanf(&buf[1], "%63s%63s", ffile, tfile) < 2)
460 continue;
463 * expand file names and move to
464 * execute directory
465 * Make files readable by anyone
467 expfile(ffile);
468 (void) snprintf(tfull, sizeof (tfull), "%s/%s", XQTDIR, tfile);
470 if (chkpth(ffile, CK_READ) == FAIL)
471 continue; /* execution will fail later */
472 if (chkpth(tfull, CK_WRITE) == FAIL) {
474 * tfull will have been canonicalized. If
475 * it still points to XQTDIR, allow us to
476 * write there.
478 if (!PREFIX(XQTDIR, tfull))
479 continue; /* execution will fail later */
480 /* otherwise, keep going */
483 ASSERT(xmv(ffile, tfull) == 0, "XMV ERROR", tfull, errno);
484 chmod(tfull, PUB_FILEMODE);
486 fclose(fp);
490 * undo what mv_Xfiles did
491 * _Xfile is a global
492 * return:
493 * none
495 void
496 unmv_Xfiles()
498 FILE *fp;
499 char buf[BUFSIZ], ffile[MAXNAMESIZE], tfile[MAXNAMESIZE];
500 char tfull[MAXFULLNAME], ffull[MAXFULLNAME], xfull[MAXFULLNAME];
502 (void) snprintf(xfull, MAXFULLNAME, "%s/%s", RemSpool, _Xfile);
503 if ((fp = fopen(xfull, "r")) == NULL) {
504 DEBUG(4, "unmv_Xfiles: can't read %s\n", xfull);
505 return;
508 while (fgets(buf, BUFSIZ, fp) != NULL) {
509 if (buf[0] != X_RQDFILE)
510 continue;
511 if (sscanf(&buf[1], "%63s%63s", ffile, tfile) < 2)
512 continue;
515 * expand file names and move back to
516 * spool directory
517 * Make files readable by uucp
519 (void) snprintf(ffull, MAXFULLNAME, "%s/%s", RemSpool, ffile);
520 /* i know we're in .Xqtdir, but ... */
521 (void) snprintf(tfull, MAXFULLNAME, "%s/%s", XQTDIR, tfile);
523 if (chkpth(ffull, CK_WRITE) == FAIL ||
524 chkpth(tfull, CK_READ) == FAIL)
525 continue;
527 ASSERT(xmv(tfull, ffull) == 0, "XMV ERROR", ffull, errno);
528 (void) chmod(ffull, (mode_t)0600);
530 fclose(fp);
534 * chkpart - checks the string (ptr points to it) for illegal command or
535 * file permission restriction - called recursively
536 * to check lines that have `string` or (string) form.
537 * _Cmd is the buffer where the command is built up.
538 * _CargType is the type of the next C line argument
540 * Return:
541 * BAD_FILE if a non permitted file is found
542 * BAD_COMMAND if non permitted command is found
543 * 0 - ok
546 static int
547 chkpart(char *ptr)
549 char prm[BUFSIZ], whitesp[BUFSIZ], rqtcmd[BUFSIZ], xcmd[BUFSIZ];
550 char savechar[2]; /* one character string with NULL */
551 int ret;
553 /* _CargType is the arg type for this iteration (cmd or file) */
554 while ((ptr = getprm(ptr, whitesp, prm)) != NULL) {
555 DEBUG(4, "prm='%s'\n", prm);
556 switch (*prm) {
558 /* End of command delimiter */
559 case ';':
560 case '^':
561 case '&':
562 case '|':
563 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
564 (void) strlcat(_Cmd, prm, sizeof (_Cmd));
565 _CargType = C_COMMAND;
566 continue;
568 /* Other delimiter */
569 case '>':
570 case '<':
571 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
572 (void) strlcat(_Cmd, prm, sizeof (_Cmd));
573 continue;
575 case '`': /* don't allow any ` commands */
576 case '\\':
577 return (BAD_COMMAND);
579 /* Some allowable quoted string */
580 case '(':
581 case '"':
582 case '\'':
583 /* must recurse */
584 savechar[0] = *prm;
585 savechar[1] = NULLCHAR;
586 /* put leading white space & first char into command */
587 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
588 (void) strlcat(_Cmd, savechar, sizeof (_Cmd));
589 savechar[0] = prm[strlen(prm)-1];
590 prm[strlen(prm)-1] = NULLCHAR; /* delete last character */
592 /* recurse */
593 if (ret = chkpart(prm+1)) { /* failed */
594 return (ret);
596 /* put last char into command */
597 (void) strlcat(_Cmd, savechar, sizeof (_Cmd));
598 continue;
600 case '2':
601 if (*(prm+1) == '>') {
602 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
603 (void) strlcat(_Cmd, prm, sizeof (_Cmd));
604 continue;
606 /* fall through if not "2>" */
608 default: /* check for command or file */
609 break;
612 if (_CargType == C_COMMAND) {
613 (void) strlcpy(rqtcmd, prm, sizeof (rqtcmd));
614 if (*rqtcmd == '~')
615 expfile(rqtcmd);
616 if ((cmdOK(rqtcmd, xcmd)) == FALSE)
617 return (BAD_COMMAND);
618 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
619 (void) strlcat(_Cmd, xcmd, sizeof (_Cmd));
620 _CargType = C_FILE;
621 continue;
624 (void) strlcpy(rqtcmd, prm, sizeof (rqtcmd));
625 if (*rqtcmd == '~')
626 expfile(rqtcmd);
627 if (chkFile(rqtcmd)) {
628 return (BAD_FILE);
629 } else {
630 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
631 (void) strlcat(_Cmd, rqtcmd, sizeof (_Cmd));
634 if (whitesp[0] != '\0')
635 /* restore any trailing white space */
636 (void) strlcat(_Cmd, whitesp, sizeof (_Cmd));
637 return (0); /* all ok */
641 * chkFile - try to find a path name in the prm.
642 * if found, check it for access permission.
644 * check file access permissions
645 * if ! in name assume that access on local machine is required
647 * Return:
648 * BAD_FILE - not permitted
649 * 0 - ok
652 static int
653 chkFile(char *prm)
655 char *p, buf[BUFSIZ];
657 (void) strlcpy(buf, prm, sizeof (buf));
658 switch (*prm) {
659 case '~':
660 case '/':
661 if (doFileChk(buf))
662 return (BAD_FILE);
663 else
664 return (0);
665 /*NOTREACHED*/
667 case '!':
668 return (chkFile(buf+1));
669 /*NOTREACHED*/
671 default:
672 break;
675 if ((p = strchr(buf, '!')) == NULL) { /* no "!", look for "/" */
676 if ((p = strchr(buf, '/')) == NULL) { /* ok */
677 return (0);
679 if (doFileChk(p))
680 return (BAD_FILE);
681 else
682 return (0);
685 /* there is at least one '!' - see if it refers to my system */
686 if (PREFIX(Myname, buf)) /* my system so far, check further */
687 return (chkFile(p+1)); /* recurse with thing after '!' */
688 else /* not my system - not my worry */
689 return (0);
693 * doFileChk - check file path permission
694 * NOTE: file is assumed to be a buffer that expfile an
695 * write into.
696 * Return
697 * BAD_FILE - not allowed
698 * 0 - ok
701 static int
702 doFileChk(char *file)
704 expfile(file);
705 DEBUG(7, "fullname: %s\n", file);
706 if (chkpth(file, CK_READ) == FAIL ||
707 chkpth(file, CK_WRITE) == FAIL)
708 return (BAD_FILE);
709 else
710 return (0);
715 * return stuff to user
716 * user -> user to notify
717 * rmt -> system name where user resides
718 * file -> file to return (generally contains input)
719 * cmd -> command that was to be executed
720 * buf -> user friendly face saving uplifting edifying missive
721 * errfile -> stderr output from cmd xeqn
722 * return:
723 * none
725 static void
726 retosndr(user, rmt, file, cmd, buf, errfile)
727 char *user, *rmt, *file, *cmd, *buf, *errfile;
729 char ruser[BUFSIZ], msg[BUFSIZ], subj[BUFSIZ];
731 (void) snprintf(msg, sizeof (msg), "%s\t[%s %s (%s)]\n\t%s\n%s\n",
732 gettext("remote execution"), gettext("uucp job"),
733 *Jobid ? Jobid : &_Xfile[2], timeStamp(), cmd, buf);
735 DEBUG(5, "retosndr %s, ", msg);
737 if (EQUALS(rmt, Myname))
738 (void) strlcpy(ruser, user, sizeof (ruser));
739 else
740 (void) snprintf(ruser, sizeof (ruser), "%s!%s", rmt, user);
742 (void) strlcpy(subj, gettext("remote execution status"), sizeof (subj));
743 mailst(ruser, subj, msg, file, errfile);
748 * uucpst - send the status message back using a uucp command
749 * NOTE - this would be better if the file could be appended.
750 * - suggestion for the future - if rmail would take a file name
751 * instead of just person, then that facility would be correct,
752 * and this routine would not be needed.
755 static void
756 uucpst(rmt, tofile, errfile, cmd, buf)
757 char *rmt, *tofile, *errfile, *cmd, *buf;
759 char arg[MAXFULLNAME], tmp[NAMESIZE], msg[BUFSIZ];
760 pid_t pid, ret;
761 int status;
762 FILE *fp, *fi;
764 (void) snprintf(msg, sizeof (msg), "%s %s (%s) %s\n\t%s\n%s\n",
765 gettext("uucp job"), *Jobid ? Jobid : &_Xfile[2], timeStamp(),
766 gettext("remote execution"), cmd, buf);
768 (void) snprintf(tmp, sizeof (tmp), "%s.%ld", rmt, (long)getpid());
769 if ((fp = fopen(tmp, "w")) == NULL)
770 return;
771 (void) fprintf(fp, "%s\n", msg);
773 /* copy back stderr */
774 if (*errfile != '\0' && NOTEMPTY(errfile) &&
775 (fi = fopen(errfile, "r")) != NULL) {
776 fputs("\n\t===== stderr was =====\n", fp);
777 if (xfappend(fi, fp) != SUCCESS)
778 fputs("\n\t===== well, i tried =====\n", fp);
779 (void) fclose(fi);
780 fputc('\n', fp);
784 (void) fclose(fp);
785 (void) snprintf(arg, sizeof (arg), "%s!%s", rmt, tofile);
787 /* start uucp */
789 if ((pid = vfork()) == 0) {
790 (void) close(0);
791 (void) close(1);
792 (void) close(2);
793 (void) open("/dev/null", 2);
794 (void) open("/dev/null", 2);
795 (void) open("/dev/null", 2);
796 (void) signal(SIGINT, SIG_IGN);
797 (void) signal(SIGHUP, SIG_IGN);
798 (void) signal(SIGQUIT, SIG_IGN);
799 ucloselog();
801 (void) execle("/usr/bin/uucp", "UUCP",
802 "-C", tmp, arg, (char *)0, Env);
803 _exit(100);
806 if (pid == -1)
807 return;
809 while ((ret = wait(&status)) != pid)
810 if (ret == -1 && errno != EINTR)
811 break;
813 (void) unlink(tmp);
816 void
817 xprocess(dirname)
818 char *dirname;
820 char fdgrade(); /* returns default service grade on system */
821 int return_stdin; /* return stdin for failed commands */
822 int cmdok, ret, badfiles;
823 mode_t mask;
824 int send_zero; /* return successful completion status */
825 int send_nonzero; /* return unsuccessful completion status */
826 int send_nothing; /* request for no exit status */
827 int store_status; /* store status of command in local file */
828 char lbuf[BUFSIZ];
829 char dqueue; /* var to hold the default service grade */
830 char *errname = ""; /* name of local stderr output file */
831 char *p;
832 char sendsys[MAXNAMESIZE];
833 char dfile[MAXFULLNAME], cfile[MAXFULLNAME], incmd[BUFSIZ];
834 char errDfile[BUFSIZ];
835 char fin[MAXFULLNAME];
836 char fout[MAXFULLNAME], sysout[NAMESIZE];
837 char ferr[MAXFULLNAME], syserr[NAMESIZE];
838 char file[MAXFULLNAME], tempname[NAMESIZE];
839 char _Sfile[MAXFULLNAME]; /* name of local file for status */
840 FILE *xfp, *fp;
841 struct stat sb;
842 char buf[BUFSIZ], user[BUFSIZ], retaddr[BUFSIZ], retuser[BUFSIZ],
843 msgbuf[BUFSIZ];
844 char origsys[MAXFULLNAME], origuser[MAXFULLNAME];
846 (void) strlcpy(Rmtname, dirname, sizeof (Rmtname));
847 chremdir(Rmtname);
848 (void) mchFind(Rmtname);
849 while (gt_Xfile(_Xfile, RemSpool) > 0) {
850 DEBUG(4, "_Xfile - %s\n", _Xfile);
852 if ((xfp = fopen(_Xfile, "r")) == NULL) {
853 toCorrupt(_Xfile);
854 continue;
856 ASSERT(xfp != NULL, Ct_OPEN, _Xfile, errno);
858 if (stat(_Xfile, &sb) != -1)
859 Nstat.t_qtime = sb.st_mtime;
861 * initialize to defaults
863 (void) strlcpy(user, User, sizeof (user));
864 (void) strcpy(fin, "/dev/null");
865 (void) strcpy(fout, "/dev/null");
866 (void) strcpy(ferr, "/dev/null");
867 (void) sprintf(sysout, "%.*s", MAXBASENAME, Myname);
868 (void) sprintf(syserr, "%.*s", MAXBASENAME, Myname);
869 badfiles = 0;
870 *incmd = *retaddr = *retuser = *Jobid = NULLCHAR;
871 initSeq();
872 send_zero = send_nonzero = send_nothing = 0;
873 store_status = 0;
874 return_stdin = 0;
876 while (fgets(buf, BUFSIZ, xfp) != NULL) {
878 * interpret JCL card
880 switch (buf[0]) {
881 case X_USER:
883 * user name
884 * (ignore Rmtname)
885 * The utmpx username field is 32 characters long;
886 * UUCP usage truncates system name to 14 bytes.
888 (void) sscanf(&buf[1], "%32s%14s", user, origsys);
889 (void) strlcpy(origuser, user, sizeof (origuser));
890 break;
892 case X_STDIN:
894 * standard input
896 (void) sscanf(&buf[1], "%256s", fin);
897 expfile(fin);
898 if (chkpth(fin, CK_READ)) {
899 DEBUG(4, "badfile - in: %s\n", fin);
900 badfiles = 1;
902 break;
904 case X_STDOUT:
906 * standard output
908 (void) sscanf(&buf[1], "%256s%14s", fout, sysout);
909 if ((p = strpbrk(sysout, "!/")) != NULL)
910 *p = NULLCHAR; /* these are dangerous */
911 if (*sysout != NULLCHAR && !EQUALS(sysout, Myname))
912 break;
914 expfile(fout);
915 if (chkpth(fout, CK_WRITE)) {
916 badfiles = 1;
917 DEBUG(4, "badfile - out: %s\n", fout);
919 break;
921 case X_STDERR: /* standard error */
922 (void) sscanf(&buf[1], "%256s%14s", ferr, syserr);
923 if ((p = strpbrk(syserr, "!/")) != NULL)
924 *p = NULLCHAR; /* these are dangerous */
925 if (*syserr != NULLCHAR && !EQUALS(syserr, Myname))
926 break;
928 expfile(ferr);
929 if (chkpth(ferr, CK_WRITE)) {
930 badfiles = 1;
931 DEBUG(4, "badfile - error: %s\n", ferr);
933 break;
936 case X_CMD: /* command to execute */
937 (void) strlcpy(incmd, &buf[2], sizeof (incmd));
938 if (*(incmd + strlen(incmd) - 1) == '\n')
939 *(incmd + strlen(incmd) - 1) = NULLCHAR;
940 break;
942 case X_MAILF: /* put status in _Sfile */
943 store_status = 1;
944 (void) sscanf(&buf[1], "%256s", _Sfile);
945 break;
947 case X_SENDNOTHING: /* no failure notification */
948 send_nothing++;
949 break;
951 case X_SENDZERO: /* success notification */
952 send_zero++;
953 break;
955 case X_NONZERO: /* failure notification */
956 send_nonzero++;
957 break;
959 case X_BRINGBACK: /* return stdin on command failure */
960 return_stdin = 1;
961 break;
964 case X_RETADDR:
966 * return address -- is user's name
967 * put "Rmtname!" in front of it so mail
968 * will always get back to remote system.
970 (void) sscanf(&buf[1], "%s", retuser);
973 * Creates string of Rmtname!Rmtname!user which
974 * confuses rmail.
975 * (void) strcat(strcat(strcpy(retaddr, Rmtname), "!"),
976 * retuser);
978 break;
980 case X_JOBID:
982 * job id for notification
983 * (should be MAXBASENAME, not 14, but no can do)
985 (void) sscanf(&buf[1], "%14s", Jobid);
986 break;
988 default:
989 break;
993 fclose(xfp);
994 DEBUG(4, "fin - %s, ", fin);
995 DEBUG(4, "fout - %s, ", fout);
996 DEBUG(4, "ferr - %s, ", ferr);
997 DEBUG(4, "sysout - %s, ", sysout);
998 DEBUG(4, "syserr - %s, ", syserr);
999 DEBUG(4, "user - %s\n", user);
1000 DEBUG(4, "incmd - %s\n", incmd);
1002 scRexe(origsys, origuser, Loginuser, incmd);
1004 if (retuser[0] != NULLCHAR)
1005 (void) strlcpy(user, retuser, sizeof (user)); /* pick on this guy */
1007 /* get rid of stuff that can be dangerous */
1008 if ((p = strpbrk(user, Shchar)) != NULL) {
1009 *p = NULLCHAR;
1012 if (incmd[0] == NULLCHAR) {
1013 /* this is a bad X. file - just get rid of it */
1014 toCorrupt(_Xfile);
1015 continue;
1019 * send_nothing must be explicitly requested to avert failure status
1020 * send_zero must be explicitly requested for success notification
1022 if (!send_nothing)
1023 send_nonzero++;
1026 * command execution
1030 * generate a temporary file (if necessary)
1031 * to hold output to be shipped back
1033 if (EQUALS(fout, "/dev/null"))
1034 (void) strcpy(dfile, "/dev/null");
1035 else {
1036 gename(DATAPRE, sysout, 'O', tempname);
1037 (void) snprintf(dfile, sizeof (dfile), "%s/%s", WORKSPACE,
1038 tempname);
1042 * generate a temporary file (if necessary)
1043 * to hold errors to be shipped back
1046 * This is what really should be done. However for compatibility
1047 * for the interim at least, we will always create temp file
1048 * so we can return error output. If this temp file IS conditionally
1049 * created, we must remove the unlink() of errDfile at the end
1050 * because it may REALLY be /dev/null.
1051 * if (EQUALS(ferr, "/dev/null"))
1052 * (void) strcpy(errDfile, "/dev/null");
1053 * else {
1055 gename(DATAPRE, syserr, 'E', tempname);
1056 (void) snprintf(errDfile, sizeof (errDfile), "%s/%s",
1057 WORKSPACE, tempname);
1062 /* initialize command line */
1063 /* set up two environment variables, remote machine name */
1064 /* and remote user name if available from R line */
1066 * xcu4 requires that uucp *does* expand wildcards and uux *does not*
1067 * expand wild cards... Further restrictions are that uux must work
1068 * with every other uucp / uux that initiated a request, so nothing
1069 * strange can been done to communicate that it was uucp that sent
1070 * the request and not uux, What we settle on here is looking for
1071 * the command name uucp and expanding wildcards in only that case.
1072 * It is true that a user can spoof this using uux, but in reality
1073 * this would be identical to using the uucp command to start with.
1075 if (strncmp(incmd, "uucp ", 5) == 0) {
1076 (void) snprintf(_Cmd, sizeof (_Cmd),
1077 "%s %s UU_MACHINE=%s UU_USER=%s "
1078 " export UU_MACHINE UU_USER PATH; ",
1079 PATH, LOGNAME, Rmtname, user);
1080 } else {
1081 (void) snprintf(_Cmd, sizeof (_Cmd),
1082 "%s %s UU_MACHINE=%s UU_USER=%s "
1083 " export UU_MACHINE UU_USER PATH; set -f; ",
1084 PATH, LOGNAME, Rmtname, user);
1088 * check to see if command can be executed
1090 _CargType = C_COMMAND; /* the first thing is a command */
1091 cmdok = chkpart(incmd);
1093 if (badfiles || (cmdok == BAD_COMMAND) || cmdok == BAD_FILE) {
1094 if (cmdok == BAD_COMMAND) {
1095 (void) snprintf(lbuf, sizeof (lbuf), "%s!%s XQT DENIED",
1096 Rmtname, user);
1097 (void) snprintf(msgbuf, sizeof (msgbuf),
1098 "execution permission denied to %s!%s", Rmtname, user);
1099 } else {
1100 (void) snprintf(lbuf, sizeof (lbuf),
1101 "%s!%s XQT - STDIN/STDOUT/FILE ACCESS DENIED",
1102 Rmtname, user);
1103 (void) snprintf(msgbuf, sizeof (msgbuf),
1104 "file access denied to %s!%s", Rmtname, user);
1106 logent(incmd, lbuf);
1107 DEBUG(4, "bad command %s\n", incmd);
1109 scWlog(); /* log security vialotion */
1111 if (send_nonzero)
1112 retosndr(user, Rmtname, return_stdin ? fin : "",
1113 incmd, msgbuf, "");
1114 if (store_status)
1115 uucpst(Rmtname, _Sfile, "", incmd, msgbuf);
1116 goto rmfiles;
1119 (void) snprintf(lbuf, sizeof (lbuf), "%s!%s XQT", Rmtname, user);
1120 logent(_Cmd, lbuf);
1121 DEBUG(4, "cmd %s\n", _Cmd);
1123 /* move files to execute directory and change to that directory */
1125 mv_Xfiles();
1127 ASSERT(chdir(XQTDIR) == 0, Ct_CHDIR, XQTDIR, errno);
1128 acRexe(&_Xfile[2], origsys, origuser, Myname, Loginuser, incmd);
1130 /* invoke shell to execute command */
1132 mask = umask(0);
1133 DEBUG(7, "full cmd: %s\n", _Cmd);
1135 cpucycle();
1136 ret = shio(_Cmd, fin, dfile, errDfile);
1137 if (ret == 0)
1138 acEndexe(cpucycle(), COMPLETE);
1139 else
1140 acEndexe(cpucycle(), PARTIAL);
1142 umask(mask);
1143 if (ret == -1) { /* -1 means the fork() failed */
1144 unmv_Xfiles(); /* put things back */
1145 errent(Ct_FORK, buf, errno, __FILE__, __LINE__);
1146 cleanup(1);
1149 if (ret == 0) { /* exit == signal == 0 */
1150 (void) strcpy(msgbuf, "exited normally");
1151 } else { /* exit != 0 */
1152 int exitcode = (ret >> 8) & 0377;
1154 if (exitcode) {
1155 /* exit != 0 */
1156 (void) snprintf(msgbuf, sizeof (msgbuf),
1157 "exited with status %d", exitcode);
1158 } else {
1159 /* signal != 0 */
1160 (void) snprintf(msgbuf, sizeof (msgbuf),
1161 "terminated by signal %d", ret & 0177);
1163 DEBUG(5, "%s\n", msgbuf);
1164 (void) snprintf(lbuf, sizeof (lbuf), "%s - %s", incmd, msgbuf);
1165 logent(lbuf, "COMMAND FAIL");
1168 /* change back to spool directory */
1170 chremdir(Rmtname);
1172 /* remove file */
1174 rm_Xfiles();
1177 * We used to append stderr to stdout. Since stderr can
1178 * now be specified separately, never append it to stdout.
1179 * It can still be gotten via -s status file option.
1182 if (!EQUALS(fout, "/dev/null")) {
1184 * if output is on this machine copy output
1185 * there, otherwise spawn job to send to send
1186 * output elsewhere.
1189 if (EQUALS(sysout, Myname)) {
1190 if ((xmv(dfile, fout)) != 0) {
1191 logent("FAILED", "COPY");
1192 scWrite();
1193 (void) snprintf(msgbuf + strlen(msgbuf),
1194 (sizeof (msgbuf) - strlen(msgbuf)),
1195 "\nCould not move stdout to %s,", fout);
1196 if (putinpub(fout, dfile, origuser) == 0)
1197 (void) snprintf(msgbuf + strlen(msgbuf),
1198 (sizeof (msgbuf) - strlen(msgbuf)),
1199 "\n\tstdout left in %s.", fout);
1200 else
1201 (void) strlcat(msgbuf, " stdout lost.",
1202 sizeof (msgbuf));
1204 } else {
1205 char *bname;
1207 if (eaccess(GRADES, 04) != -1)
1208 dqueue = fdgrade();
1209 else
1210 dqueue = Grade;
1211 gename(CMDPRE, sysout, dqueue, tempname);
1212 (void) snprintf(cfile, sizeof (cfile), "%s/%s",
1213 WORKSPACE, tempname);
1214 fp = fdopen(ret = creat(cfile, CFILEMODE), "w");
1215 ASSERT(ret >= 0 && fp != NULL, Ct_OPEN, cfile, errno);
1216 bname = BASENAME(dfile, '/');
1217 (void) fprintf(fp, "S %s %s %s -d %s 0666\n",
1218 bname, fout, user, bname);
1219 fclose(fp);
1220 (void) snprintf(sendsys, sizeof (sendsys), "%s/%c", sysout,
1221 dqueue);
1222 sendsys[MAXNAMESIZE-1] = '\0';
1223 wfcommit(dfile, BASENAME(dfile, '/'), sendsys);
1224 wfcommit(cfile, BASENAME(cfile, '/'), sendsys);
1227 if (!EQUALS(ferr, "/dev/null")) {
1229 * if stderr is on this machine copy output
1230 * there, otherwise spawn job to send to send
1231 * it elsewhere.
1233 if (EQUALS(syserr, Myname)) {
1234 errname = ferr;
1235 if ((xmv(errDfile, ferr)) != 0) {
1236 logent("FAILED", "COPY");
1237 scWrite();
1238 (void) snprintf(msgbuf + strlen(msgbuf),
1239 (sizeof (msgbuf) - strlen(msgbuf)),
1240 "\nCould not move stderr to %s,", ferr);
1241 if (putinpub(ferr, errDfile, origuser) == 0) {
1242 (void) snprintf(msgbuf+strlen(msgbuf),
1243 (sizeof (msgbuf) - strlen(msgbuf)),
1244 "\n\tstderr left in %s", ferr);
1245 } else {
1246 errname = errDfile;
1247 (void) strlcat(msgbuf, " stderr lost.",
1248 sizeof (msgbuf));
1251 } else {
1252 char *bname;
1254 if (eaccess(GRADES, 04) != -1)
1255 dqueue = fdgrade();
1256 else
1257 dqueue = Grade;
1258 gename(CMDPRE, syserr, dqueue, tempname);
1259 (void) snprintf(cfile, sizeof (cfile), "%s/%s",
1260 WORKSPACE, tempname);
1261 fp = fdopen(ret = creat(cfile, CFILEMODE), "w");
1262 ASSERT(ret >= 0 && fp != NULL, Ct_OPEN, cfile, errno);
1263 bname = BASENAME(errDfile, '/');
1264 (void) fprintf(fp, "S %s %s %s -d %s 0666\n",
1265 bname, ferr, user, bname);
1266 fclose(fp);
1267 (void) snprintf(sendsys, sizeof (sendsys), "%s/%c",
1268 syserr, dqueue);
1269 sendsys[MAXNAMESIZE-1] = '\0';
1270 wfcommit(errDfile, BASENAME(errDfile, '/'), sendsys);
1271 wfcommit(cfile, BASENAME(cfile, '/'), sendsys);
1273 } else {
1275 * If we conditionally create stderr tempfile, we must
1276 * remove this unlink() since errDfile may REALLY be /dev/null
1278 unlink(errDfile);
1281 if (ret == 0) {
1282 if (send_zero)
1283 retosndr(user, Rmtname, "", incmd, msgbuf, "");
1284 if (store_status)
1285 uucpst(Rmtname, _Sfile, "", incmd, msgbuf);
1286 } else {
1287 if (send_nonzero)
1288 retosndr(user, Rmtname, return_stdin ? fin : "",
1289 incmd, msgbuf, errname);
1290 if (store_status)
1291 uucpst(Rmtname, _Sfile, errname, incmd, msgbuf);
1294 rmfiles:
1296 /* delete job files in spool directory */
1297 xfp = fopen(_Xfile, "r");
1298 ASSERT(xfp != NULL, Ct_OPEN, _Xfile, errno);
1299 while (fgets(buf, BUFSIZ, xfp) != NULL) {
1300 if (buf[0] != X_RQDFILE)
1301 continue;
1302 (void) sscanf(&buf[1], "%63s", file);
1303 expfile(file);
1304 if (chkpth(file, CK_WRITE) != FAIL)
1305 (void) unlink(file);
1307 (void) unlink(_Xfile);
1308 fclose(xfp);