xref: /386bsd/usr/src/libexec/uucp/uuxqt.c (revision a2142627)
1 /* uuxqt.c
2    Run uux commands.
3 
4    Copyright (C) 1991, 1992 Ian Lance Taylor
5 
6    This file is part of the Taylor UUCP package.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22    The author of the program may be contacted at ian@airs.com or
23    c/o AIRS, P.O. Box 520, Waltham, MA 02254.
24 
25    $Log: uuxqt.c,v $
26    Revision 1.43  1992/03/17  04:42:41  ian
27    Correct bug in previous patch
28 
29    Revision 1.42  1992/03/17  03:15:40  ian
30    Pass command to fsysdep_execute as first element of argument array
31 
32    Revision 1.41  1992/03/17  01:07:15  ian
33    Miscellaneous cleanup
34 
35    Revision 1.40  1992/03/16  19:44:45  ian
36    Cast result of alloca
37 
38    Revision 1.39  1992/03/15  04:51:17  ian
39    Keep an array of signals we've received rather than a single variable
40 
41    Revision 1.38  1992/03/15  01:54:46  ian
42    All execs are now done in isspawn, all waits are done in iswait
43 
44    Revision 1.37  1992/03/12  19:54:43  ian
45    Debugging based on types rather than number
46 
47    Revision 1.36  1992/03/11  22:34:25  ian
48    Chip Salzenberg: support Internet mail addresses in uuxqt replies
49 
50    Revision 1.35  1992/03/11  22:06:37  ian
51    Marty Shannon: added max-uuxqts command
52 
53    Revision 1.34  1992/03/11  17:04:53  ian
54    Jon Zeeff: retry execution later if temporary failure
55 
56    Revision 1.33  1992/03/11  02:09:19  ian
57    Correct bug in previous change
58 
59    Revision 1.32  1992/03/04  02:32:26  ian
60    Handle executions on local system
61 
62    Revision 1.31  1992/02/29  04:07:08  ian
63    Added -j option to uucp and uux
64 
65    Revision 1.30  1992/02/29  01:06:59  ian
66    Chip Salzenberg: recheck file permissions before sending
67 
68    Revision 1.29  1992/02/27  05:40:54  ian
69    T. William Wells: detach from controlling terminal, handle signals safely
70 
71    Revision 1.28  1992/02/24  04:58:47  ian
72    Only permit files to be received into directories that are world-writeable
73 
74    Revision 1.27  1992/02/23  19:50:50  ian
75    Handle READ and WRITE in Permissions correctly
76 
77    Revision 1.26  1992/02/23  03:26:51  ian
78    Overhaul to use automatic configure shell script
79 
80    Revision 1.25  1992/02/18  19:03:02  ian
81    Pass fdaemon argument correctly to usysdep_initialize
82 
83    Revision 1.24  1992/02/18  04:53:26  ian
84    T. William Wells: make sure sh execution uses absolute path
85 
86    Revision 1.23  1992/02/14  21:32:50  ian
87    Niels Baggesen: under HAVE_BNU_LOGGING, don't lost system name when dieing
88 
89    Revision 1.22  1992/02/08  22:33:32  ian
90    Only get the current working directory if it's going to be needed
91 
92    Revision 1.21  1992/02/08  20:33:57  ian
93    Handle all possible signals raised by abort
94 
95    Revision 1.20  1992/02/08  03:54:18  ian
96    Include <string.h> only in <uucp.h>, added 1992 copyright
97 
98    Revision 1.19  1992/01/16  17:48:41  ian
99    Niels Baggesen: was checking strcmp return incorrectly
100 
101    Revision 1.18  1992/01/15  07:06:29  ian
102    Set configuration directory in Makefile rather than sysdep.h
103 
104    Revision 1.17  1992/01/05  03:09:17  ian
105    Changed abProgram and abVersion to non const to avoid compiler bug
106 
107    Revision 1.16  1992/01/04  21:43:24  ian
108    Chip Salzenberg: added ALLOW_FILENAME_ARGUMENTS to permit them
109 
110    Revision 1.15  1992/01/04  04:12:54  ian
111    David J. Fiander: make sure execution arguments are not bad file names
112 
113    Revision 1.14  1991/12/29  04:22:41  ian
114    The mailing address was not getting initialized
115 
116    Revision 1.13  1991/12/29  04:04:18  ian
117    Added a bunch of extern definitions
118 
119    Revision 1.12  1991/12/20  00:17:38  ian
120    Don't process execute files for unknown systems
121 
122    Revision 1.11  1991/12/19  03:52:07  ian
123    David Nugent: rescan the list of execute files until nothing can be done
124 
125    Revision 1.10  1991/12/18  03:54:14  ian
126    Made error messages to terminal appear more normal
127 
128    Revision 1.9  1991/12/17  04:55:01  ian
129    David Nugent: ignore SIGHUP in uucico and uuxqt
130 
131    Revision 1.8  1991/12/09  18:49:06  ian
132    Richard Todd: the requestor address is relative to the requesting system
133 
134    Revision 1.7  1991/12/06  23:42:18  ian
135    Don't acknowledge success by default
136 
137    Revision 1.6  1991/12/01  01:28:44  ian
138    Mitch Mitchell: fixed comment listing supported commands
139 
140    Revision 1.5  1991/11/21  22:17:06  ian
141    Add version string, print version when printing usage
142 
143    Revision 1.4  1991/11/07  20:52:33  ian
144    Chip Salzenberg: pass command as single argument to /bin/sh
145 
146    Revision 1.3  1991/09/19  16:15:58  ian
147    Chip Salzenberg: configuration option for permitting execution via sh
148 
149    Revision 1.2  1991/09/19  02:30:37  ian
150    From Chip Salzenberg: check whether signal is ignored differently
151 
152    Revision 1.1  1991/09/10  19:40:31  ian
153    Initial revision
154 
155    */
156 
157 #include "uucp.h"
158 
159 #if USE_RCS_ID
160 char uuxqt_rcsid[] = "$Id: uuxqt.c,v 1.43 1992/03/17 04:42:41 ian Rel $";
161 #endif
162 
163 #include <errno.h>
164 #include <ctype.h>
165 
166 #include "getopt.h"
167 
168 #include "system.h"
169 #include "sysdep.h"
170 
171 /* External functions.  */
172 extern int fclose ();
173 
174 /* The program name.  */
175 char abProgram[] = "uuxqt";
176 
177 /* Static variables used to unlock things if we get a fatal error.  */
178 
179 static int iQunlock_seq = -1;
180 static const char *zQunlock_cmd;
181 static const char *zQunlock_file;
182 static boolean fQunlock_directory;
183 
184 /* Local functions.  */
185 
186 static void uqusage P((void));
187 static void uqabort P((void));
188 static void uqdo_xqt_file P((const char *zfile,
189 			     const struct ssysteminfo *qsys,
190 			     const char *zcmd, boolean *pfprocessed));
191 static void uqcleanup P((const char *zfile, int iflags));
192 
193 /* Long getopt options.  */
194 
195 static const struct option asQlongopts[] = { { NULL, 0, NULL, 0 } };
196 
197 const struct option *_getopt_long_options = asQlongopts;
198 
199 int
main(argc,argv)200 main (argc, argv)
201      int argc;
202      char **argv;
203 {
204   int iopt;
205   /* The type of command to execute (NULL for any type).  */
206   const char *zcmd = NULL;
207   /* The configuration file name.  */
208   const char *zconfig = NULL;
209   /* The system to execute commands for.  */
210   const char *zdosys = NULL;
211   boolean fany;
212   const char *z;
213   const char *zgetsys;
214   boolean ferr;
215   struct ssysteminfo sreadsys;
216   const struct ssysteminfo *qreadsys;
217 
218   while ((iopt = getopt (argc, argv, "c:I:s:x:")) != EOF)
219     {
220       switch (iopt)
221 	{
222 	case 'c':
223 	  /* Set the type of command to execute.  */
224 	  zcmd = optarg;
225 	  break;
226 
227 	case 'I':
228 	  /* Set the configuration file name.  */
229 	  zconfig = optarg;
230 	  break;
231 
232 	case 's':
233 	  zdosys = optarg;
234 	  break;
235 
236 	case 'x':
237 #if DEBUG > 1
238 	  /* Set the debugging level.  */
239 	  iDebug |= idebug_parse (optarg);
240 #endif
241 	  break;
242 
243 	case 0:
244 	  /* Long option found and flag set.  */
245 	  break;
246 
247 	default:
248 	  uqusage ();
249 	  break;
250 	}
251     }
252 
253   if (optind != argc)
254     uqusage ();
255 
256   uread_config (zconfig);
257 
258 #ifdef SIGINT
259   usysdep_signal (SIGINT);
260 #endif
261 #ifdef SIGHUP
262   usysdep_signal (SIGHUP);
263 #endif
264 #ifdef SIGQUIT
265   usysdep_signal (SIGQUIT);
266 #endif
267 #ifdef SIGTERM
268   usysdep_signal (SIGTERM);
269 #endif
270 #ifdef SIGPIPE
271   usysdep_signal (SIGPIPE);
272 #endif
273 
274   usysdep_initialize (TRUE, FALSE);
275 
276   ulog_to_file (TRUE);
277   ulog_fatal_fn (uqabort);
278 
279   /* Limit the number of uuxqt processes, and make sure we're the only
280      uuxqt daemon running for this command.  */
281   iQunlock_seq = isysdep_lock_uuxqt (zcmd);
282   if (iQunlock_seq < 0)
283     {
284       ulog_close ();
285       usysdep_exit (TRUE);
286     }
287   zQunlock_cmd = zcmd;
288 
289   /* Keep scanning the execute files until we don't process any of
290      them.  */
291 
292   do
293     {
294       fany = FALSE;
295 
296       /* Look for each execute file, and run it.  */
297 
298       if (! fsysdep_get_xqt_init ())
299 	{
300 	  ulog_close ();
301 	  usysdep_exit (FALSE);
302 	}
303 
304       qreadsys = NULL;
305 
306       while ((z = zsysdep_get_xqt (&zgetsys, &ferr)) != NULL)
307 	{
308 	  char *zcopy;
309 	  boolean fprocessed;
310 	  const struct ssysteminfo *qusesys;
311 
312 #if ! HAVE_ALLOCA
313 	  /* Clear out any accumulated alloca buffers.  */
314 	  (void) alloca (0);
315 #endif
316 
317 	  /* It would be more efficient to pass zdosys down to the
318 	     routines which retrieve execute files.  */
319 	  if (zdosys != NULL && strcmp (zdosys, zgetsys) != 0)
320 	    continue;
321 
322 	  if (strcmp (zgetsys, zLocalname) == 0)
323 	    qusesys = &sLocalsys;
324 	  else
325 	    {
326 	      if (qreadsys == NULL
327 		  || strcmp (qreadsys->zname, zgetsys) != 0)
328 		{
329 		  if (fread_system_info (zgetsys, &sreadsys))
330 		    qreadsys = &sreadsys;
331 		  else
332 		    {
333 		      if (! fUnknown_ok)
334 			{
335 			  ulog (LOG_ERROR,
336 				"%s: Execute file for unknown system %s",
337 				z, zgetsys);
338 			  (void) remove (z);
339 			  continue;
340 			}
341 		      qreadsys = &sUnknown;
342 		      sUnknown.zname = xstrdup (zgetsys);
343 		    }
344 
345 		  if (! fsysdep_make_spool_dir (qreadsys))
346 		    continue;
347 		}
348 
349 	      qusesys = qreadsys;
350 	    }
351 
352 	  /* If we've received a signal, get out of the loop.  */
353 	  if (FGOT_SIGNAL ())
354 	    break;
355 
356 	  zcopy = xstrdup (z);
357 
358 	  ulog_system (qusesys->zname);
359 	  uqdo_xqt_file (zcopy, qusesys, zcmd, &fprocessed);
360 	  ulog_system ((const char *) NULL);
361 	  ulog_user ((const char *) NULL);
362 
363 	  if (fprocessed)
364 	    fany = TRUE;
365 	  xfree ((pointer) zcopy);
366 	}
367 
368       usysdep_get_xqt_free ();
369     }
370   while (fany && ! FGOT_SIGNAL ());
371 
372   (void) fsysdep_unlock_uuxqt (iQunlock_seq, zcmd);
373   iQunlock_seq = -1;
374 
375   ulog_close ();
376 
377   if (FGOT_SIGNAL ())
378     ferr = TRUE;
379 
380   usysdep_exit (! ferr);
381 
382   /* Avoid errors about not returning a value.  */
383   return 0;
384 }
385 
386 static void
uqusage()387 uqusage ()
388 {
389   fprintf (stderr,
390 	   "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n",
391 	   abVersion);
392   fprintf (stderr,
393 	   "Usage: uuxqt [-c cmd] [-I file] [-s system] [-x debug]\n");
394   fprintf (stderr,
395 	   " -c cmd: Set type of command to execute\n");
396   fprintf (stderr,
397 	   " -s system: Execute commands only for named system\n");
398   fprintf (stderr,
399 	   " -x debug: Set debugging level (0 for none, 9 is max)\n");
400 #if HAVE_TAYLOR_CONFIG
401   fprintf (stderr,
402 	   " -I file: Set configuration file to use (default %s%s)\n",
403 	   NEWCONFIGLIB, CONFIGFILE);
404 #endif /* HAVE_TAYLOR_CONFIG */
405   exit (EXIT_FAILURE);
406 }
407 
408 /* This is the abort function called when we get a fatal error.  */
409 
410 static void
uqabort()411 uqabort ()
412 {
413 #if ! HAVE_BNU_LOGGING
414   /* When using BNU logging, it's a pain to have no system name.  */
415   ulog_system ((const char *) NULL);
416 #endif
417 
418   ulog_user ((const char *) NULL);
419 
420   if (fQunlock_directory)
421     (void) fsysdep_unlock_uuxqt_dir ();
422 
423   if (zQunlock_file != NULL)
424     (void) fsysdep_unlock_uuxqt_file (zQunlock_file);
425 
426   if (iQunlock_seq >= 0)
427     (void) fsysdep_unlock_uuxqt (iQunlock_seq, zQunlock_cmd);
428 
429   ulog_close ();
430 
431   usysdep_exit (FALSE);
432 }
433 
434 /* An execute file is a series of lines.  The first character of each
435    line is a command.  The following commands are defined:
436 
437    C command-line
438    I standard-input
439    O standard-output [ system ]
440    F required-file filename-to-use
441    R requestor-address
442    U user system
443    Z (acknowledge if command failed; default)
444    N (no acknowledgement on failure)
445    n (acknowledge if command succeeded)
446    B (return command input on error)
447    e (process with sh)
448    E (process with exec)
449    M status-file
450    # comment
451 
452    Unrecognized commands are ignored.  We actually do not recognize
453    the Z command, since it requests default behaviour.  We always send
454    mail on failure, unless the N command appears.  We never send mail
455    on success, unless the n command appears.
456 
457    This code does not currently support the B or M commands.  */
458 
459 /* Command arguments.  */
460 static const char **azQargs;
461 /* Command as a complete string.  */
462 static char *zQcmd;
463 /* Standard input file name.  */
464 static const char *zQinput;
465 /* Standard output file name.  */
466 static const char *zQoutfile;
467 /* Standard output system.  */
468 static const char *zQoutsys;
469 /* Number of required files.  */
470 static int cQfiles;
471 /* Names of required files.  */
472 static char **azQfiles;
473 /* Names required files should be renamed to (NULL if original is OK).  */
474 static char **azQfiles_to;
475 /* Requestor address (this is where mail should be sent).  */
476 static const char *zQrequestor;
477 /* User name.  */
478 static const char *zQuser;
479 /* System name.  */
480 static const char *zQsystem;
481 /* This is set by the N flag, meaning that no acknowledgement should
482    be mailed on failure.  */
483 static boolean fQno_ack;
484 /* This is set by the n flag, meaning that acknowledgement should be
485    mailed if the command succeeded.  */
486 static boolean fQsuccess_ack;
487 /* This is set by the B flag, meaning that command input should be
488    mailed to the requestor if an error occurred.  */
489 static boolean fQsend_input;
490 /* This is set by the E flag, meaning that exec should be used to
491    execute the command.  */
492 static boolean fQuse_exec;
493 /* The status should be copied to this file on the requesting host.  */
494 static const char *zQstatus_file;
495 #if ALLOW_SH_EXECUTION
496 /* This is set by the e flag, meaning that sh should be used to
497    execute the command.  */
498 static boolean fQuse_sh;
499 #endif /* ALLOW_SH_EXECUTION */
500 
501 static enum tcmdtabret tqcmd P((int argc, char **argv, pointer pvar,
502 				const char *zerr));
503 static enum tcmdtabret tqout P((int argc, char **argv, pointer pvar,
504 				const char *zerr));
505 static enum tcmdtabret tqfile P((int argc, char **argv, pointer pvar,
506 				 const char *zerr));
507 static enum tcmdtabret tquser P((int argc, char **argv, pointer pvar,
508 				 const char *zerr));
509 static enum tcmdtabret tqset P((int argc, char **argv, pointer pvar,
510 				const char *zerr));
511 
512 static struct scmdtab asQcmds[] =
513 {
514   { "C", CMDTABTYPE_FN | 0, NULL, tqcmd },
515   { "I", CMDTABTYPE_STRING, (pointer) &zQinput, NULL },
516   { "O", CMDTABTYPE_FN | 0, NULL, tqout },
517   { "F", CMDTABTYPE_FN | 0, NULL, tqfile },
518   { "R", CMDTABTYPE_STRING, (pointer) &zQrequestor, NULL },
519   { "U", CMDTABTYPE_FN | 3, NULL, tquser },
520   { "N", CMDTABTYPE_FN | 1, (pointer) &fQno_ack, tqset },
521   { "n", CMDTABTYPE_FN | 1, (pointer) &fQsuccess_ack, tqset },
522   { "B", CMDTABTYPE_FN | 1, (pointer) &fQsend_input, tqset },
523 #if ALLOW_SH_EXECUTION
524   { "e", CMDTABTYPE_FN | 1, (pointer) &fQuse_sh, tqset },
525 #endif
526   { "E", CMDTABTYPE_FN | 1, (pointer) &fQuse_exec, tqset },
527   { "M", CMDTABTYPE_STRING, (pointer) &zQstatus_file, NULL },
528   { NULL, 0, NULL, NULL }
529 };
530 
531 /* Handle the C command: store off the arguments.  */
532 
533 /*ARGSUSED*/
534 static enum tcmdtabret
tqcmd(argc,argv,pvar,zerr)535 tqcmd (argc, argv, pvar, zerr)
536      int argc;
537      char **argv;
538      pointer pvar;
539      const char *zerr;
540 {
541   int i;
542   int clen;
543 
544   if (argc <= 1)
545     return CMDTABRET_FREE;
546 
547   azQargs = (const char **) xmalloc (argc * sizeof (char *));
548   clen = 0;
549   for (i = 1; i < argc; i++)
550     {
551       azQargs[i - 1] = argv[i];
552       clen += strlen (argv[i]) + 1;
553     }
554   azQargs[i - 1] = NULL;
555 
556   zQcmd = (char *) xmalloc (clen);
557   zQcmd[0] = '\0';
558   for (i = 1; i < argc - 1; i++)
559     {
560       strcat (zQcmd, argv[i]);
561       strcat (zQcmd, " ");
562     }
563   strcat (zQcmd, argv[i]);
564 
565   return CMDTABRET_CONTINUE;
566 }
567 
568 /* Handle the O command, which may have one or two arguments.  */
569 
570 /*ARGSUSED*/
571 static enum tcmdtabret
tqout(argc,argv,pvar,zerr)572 tqout (argc, argv, pvar, zerr)
573      int argc;
574      char **argv;
575      pointer pvar;
576      const char *zerr;
577 {
578   if (argc != 2 && argc != 3)
579     {
580       ulog (LOG_ERROR, "%s: %s: Wrong number of arguments",
581 	    zerr, argv[0]);
582       return CMDTABRET_FREE;
583     }
584 
585   zQoutfile = argv[1];
586   if (argc == 3)
587     zQoutsys = argv[2];
588 
589   return CMDTABRET_CONTINUE;
590 }
591 
592 /* Handle the F command, which may have one or two arguments.  */
593 
594 /*ARGSUSED*/
595 static enum tcmdtabret
tqfile(argc,argv,pvar,zerr)596 tqfile (argc, argv, pvar, zerr)
597      int argc;
598      char **argv;
599      pointer pvar;
600      const char *zerr;
601 {
602   if (argc != 2 && argc != 3)
603     {
604       ulog (LOG_ERROR, "%s: %s: Wrong number of arguments",
605 	    zerr, argv[0]);
606       return CMDTABRET_FREE;
607     }
608 
609   /* If this file is not in the spool directory, just ignore it.  */
610   if (! fspool_file (argv[1]))
611     return CMDTABRET_FREE;
612 
613   ++cQfiles;
614   azQfiles = (char **) xrealloc ((pointer) azQfiles,
615 				 cQfiles * sizeof (char *));
616   azQfiles_to = (char **) xrealloc ((pointer) azQfiles_to,
617 				    cQfiles * sizeof (char *));
618 
619   azQfiles[cQfiles - 1] = xstrdup (argv[1]);
620   if (argc == 3)
621     azQfiles_to[cQfiles - 1] = xstrdup (argv[2]);
622   else
623     azQfiles_to[cQfiles - 1] = NULL;
624 
625   return CMDTABRET_FREE;
626 }
627 
628 /* Handle the U command, which takes two arguments.  */
629 
630 /*ARGSUSED*/
631 static enum tcmdtabret
tquser(argc,argv,pvar,zerr)632 tquser (argc, argv, pvar, zerr)
633      int argc;
634      char **argv;
635      pointer pvar;
636      const char *zerr;
637 {
638   zQuser = argv[1];
639   zQsystem = argv[2];
640   return CMDTABRET_CONTINUE;
641 }
642 
643 /* Handle various commands which just set boolean variables.  */
644 
645 /*ARGSUSED*/
646 static enum tcmdtabret
tqset(argc,argv,pvar,zerr)647 tqset (argc, argv, pvar, zerr)
648      int argc;
649      char **argv;
650      pointer pvar;
651      const char *zerr;
652 {
653   boolean *pf = (boolean *) pvar;
654 
655   *pf = TRUE;
656   return CMDTABRET_FREE;
657 }
658 
659 /* The execution processing does a lot of things that have to be
660    cleaned up.  Rather than try to add the appropriate statements
661    to each return point, we keep a set of flags indicating what
662    has to be cleaned up.  The actual clean up is done by the
663    function uqcleanup.  */
664 
665 #define REMOVE_FILE (01)
666 #define REMOVE_NEEDED (02)
667 #define FREE_QINPUT (04)
668 
669 /* Process an execute file.  The zfile argument is the name of the
670    execute file.  The qsys argument describes the system it came from.
671    The zcmd argument is the name of the command we are executing (from
672    the -c option) or NULL if any command is OK.  This sets
673    *pfprocessed to TRUE if the file is ready to be executed.  */
674 
675 static void
uqdo_xqt_file(zfile,qsys,zcmd,pfprocessed)676 uqdo_xqt_file (zfile, qsys, zcmd, pfprocessed)
677      const char *zfile;
678      const struct ssysteminfo *qsys;
679      const char *zcmd;
680      boolean *pfprocessed;
681 {
682   const char *zcmds;
683   const char *zabsolute;
684   boolean ferr;
685   FILE *e;
686   int i;
687   int iclean;
688   const char *zmail;
689   const char *zoutput;
690   char abtemp[CFILE_NAME_LEN];
691   char abdata[CFILE_NAME_LEN];
692   const char *zerror;
693   struct ssysteminfo soutsys;
694   const struct ssysteminfo *qoutsys;
695   boolean fshell;
696   char *zfullcmd;
697   boolean ftemp;
698 
699   *pfprocessed = FALSE;
700 
701   /* If we're not permitted to execute anything for this system,
702      we can just clobber the file without even looking at it.  */
703   zcmds = qsys->zcmds;
704 
705   if (*zcmds == '\0')
706     {
707       ulog (LOG_ERROR, "%s: No commands permitted for system %s",
708 	    zfile, qsys->zname);
709       (void) remove (zfile);
710       return;
711     }
712 
713   /* If we are only willing to execute a particular command, and it
714      is not one of those accepted by this system, quit now.  */
715   if (zcmd != NULL
716       && strcmp (zcmds, "ALL") != 0
717       && strstr (zcmds, zcmd) == NULL)
718     return;
719 
720   e = fopen (zfile, "r");
721   if (e == NULL)
722     return;
723 
724   azQargs = NULL;
725   zQcmd = NULL;
726   zQinput = NULL;
727   zQoutfile = NULL;
728   zQoutsys = NULL;
729   cQfiles = 0;
730   azQfiles = NULL;
731   azQfiles_to = NULL;
732   zQrequestor = NULL;
733   zQuser = NULL;
734   zQsystem = NULL;
735   fQno_ack = FALSE;
736   fQsuccess_ack = FALSE;
737   fQsend_input = FALSE;
738   fQuse_exec = FALSE;
739   zQstatus_file = NULL;
740 #if ALLOW_SH_EXECUTION
741   fQuse_sh = FALSE;
742 #endif
743 
744   uprocesscmds (e, (struct smulti_file *) NULL, asQcmds, zfile, 0);
745 
746   (void) fclose (e);
747 
748   iclean = 0;
749 
750   if (azQargs == NULL)
751     {
752       ulog (LOG_ERROR, "%s: No command given", zfile);
753       uqcleanup (zfile, iclean | REMOVE_FILE);
754       return;
755     }
756 
757   if (zcmd != NULL)
758     {
759       if (strcmp (zcmd, azQargs[0]) != 0)
760 	{
761 	  uqcleanup (zfile, iclean);
762 	  return;
763 	}
764     }
765   else
766     {
767       /* If there is a lock file for this particular command already,
768 	 it means that some other uuxqt is supposed to handle it.  */
769       if (fsysdep_uuxqt_locked (azQargs[0]))
770 	{
771 	  uqcleanup (zfile, iclean);
772 	  return;
773 	}
774     }
775 
776   /* Lock this particular file.  */
777 
778   if (! fsysdep_lock_uuxqt_file (zfile))
779     {
780       uqcleanup (zfile, iclean);
781       return;
782     }
783 
784   zQunlock_file = zfile;
785 
786   if (zQuser != NULL)
787     ulog_user (zQuser);
788   else if (zQrequestor != NULL)
789     ulog_user (zQrequestor);
790   else
791     ulog_user ("unknown");
792 
793   /* Make sure that all the required files exist, and get their
794      full names in the spool directory.  */
795 
796   for (i = 0; i < cQfiles; i++)
797     {
798       const char *zreal;
799 
800       zreal = zsysdep_spool_file_name (qsys, azQfiles[i]);
801       if (zreal == NULL)
802 	{
803 	  uqcleanup (zfile, iclean);
804 	  return;
805 	}
806       if (! fsysdep_file_exists (zreal))
807 	{
808 	  uqcleanup (zfile, iclean);
809 	  return;
810 	}
811       xfree ((pointer) azQfiles[i]);
812       azQfiles[i] = xstrdup (zreal);
813     }
814 
815   /* See if we need the execute directory, and lock it if we do.  */
816 
817   for (i = 0; i < cQfiles; i++)
818     {
819       int itries;
820 
821       if (azQfiles_to[i] == NULL)
822 	continue;
823 
824       for (itries = 0; itries < 5; itries++)
825 	{
826 	  if (fsysdep_lock_uuxqt_dir ())
827 	    break;
828 	  usysdep_sleep (30);
829 	}
830       if (itries >= 5)
831 	{
832 	  ulog (LOG_ERROR, "Could not lock execute directory");
833 	  uqcleanup (zfile, iclean);
834 	  return;
835 	}
836 
837       fQunlock_directory = TRUE;
838       break;
839     }
840 
841   iclean |= REMOVE_FILE | REMOVE_NEEDED;
842   *pfprocessed = TRUE;
843 
844   /* Get the address to mail results to.  Prepend the system from
845      which the execute file originated, since mail addresses are
846      relative to it.  */
847 
848   zmail = NULL;
849   if (zQrequestor != NULL)
850     zmail = zQrequestor;
851   else if (zQuser != NULL)
852     zmail = zQuser;
853   if (zmail != NULL
854       && zQsystem != NULL
855 #if HAVE_INTERNET_MAIL
856       && strchr (zmail, '@') == NULL
857 #endif
858       && strcmp (zQsystem, zLocalname) != 0)
859     {
860       char *zset;
861 
862       zset = (char *) alloca (strlen (zQsystem) + strlen (zmail) + 2);
863 
864       sprintf (zset, "%s!%s", zQsystem, zmail);
865       zmail = zset;
866     }
867 
868   /* Get the pathname to execute.  */
869 
870   zabsolute = zsysdep_find_command (azQargs[0], zcmds, qsys->zpath,
871 				    &ferr);
872   if (zabsolute == NULL)
873     {
874       if (ferr)
875 	{
876 	  /* If we get an error, try again later.  */
877 	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
878 	  *pfprocessed = FALSE;
879 	  return;
880 	}
881 
882       /* Not permitted.  Send mail to requestor.  */
883 
884       ulog (LOG_ERROR, "Not permitted to execute %s",
885 	    azQargs[0]);
886 
887       if (zmail != NULL && ! fQno_ack)
888 	{
889 	  const char *az[20];
890 
891 	  i = 0;
892 	  az[i++] = "Your execution request failed because you are not";
893 	  az[i++] = " permitted to execute\n\t";
894 	  az[i++] = azQargs[0];
895 	  az[i++] = "\non this system.\n";
896 	  az[i++] = "Execution requested was:\n\t";
897 	  az[i++] = zQcmd;
898 	  az[i++] = "\n";
899 
900 	  (void) fsysdep_mail (zmail, "Execution failed", i, az);
901 	}
902 
903       uqcleanup (zfile, iclean);
904       return;
905     }
906 
907   {
908     char *zcopy;
909 
910     zcopy = (char *) alloca (strlen (zabsolute) + 1);
911     strcpy (zcopy, zabsolute);
912     zabsolute = zcopy;
913   }
914 
915   azQargs[0] = zabsolute;
916 
917 #if ! ALLOW_FILENAME_ARGUMENTS
918 
919   /* Check all the arguments to make sure they don't try to specify
920      files they are not permitted to access.  */
921 
922   for (i = 1; azQargs[i] != NULL; i++)
923     {
924       if (! fsysdep_xqt_check_file (qsys, azQargs[i]))
925 	{
926 	  if (zmail != NULL && ! fQno_ack)
927 	    {
928 	      const char *az[20];
929 	      const char *zfailed;
930 
931 	      zfailed = azQargs[i];
932 	      i = 0;
933 	      az[i++] = "Your execution request failed because you are not";
934 	      az[i++] = " permitted to refer to file\n\t";
935 	      az[i++] = zfailed;
936 	      az[i++] = "\non this system.\n";
937 	      az[i++] = "Execution requested was:\n\t";
938 	      az[i++] = zQcmd;
939 	      az[i++] = "\n";
940 
941 	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
942 	    }
943 
944 	  uqcleanup (zfile, iclean);
945 	  return;
946 	}
947     }
948 
949 #endif /* ! ALLOW_FILENAME_ARGUMENTS */
950 
951   ulog (LOG_NORMAL, "Executing %s (%s)", zfile, zQcmd);
952 
953   if (zQinput != NULL)
954     {
955       boolean fspool;
956 
957       fspool = fspool_file (zQinput);
958       if (fspool)
959 	zQinput = zsysdep_spool_file_name (qsys, zQinput);
960       else
961 	zQinput = zsysdep_real_file_name (qsys, zQinput,
962 					  (const char *) NULL);
963       if (zQinput == NULL)
964 	{
965 	  /* If we get an error, try again later.  */
966 	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
967 	  *pfprocessed = FALSE;
968 	  return;
969 	}
970 
971       zQinput = xstrdup (zQinput);
972       iclean |= FREE_QINPUT;
973 
974       if (! fspool
975 	  && ! fin_directory_list (qsys, zQinput,
976 				   qsys->zremote_send, TRUE, TRUE,
977 				   (const char *) NULL))
978 	{
979 	  ulog (LOG_ERROR, "Not permitted to read %s", zQinput);
980 
981 	  if (zmail != NULL && ! fQno_ack)
982 	    {
983 	      const char *az[20];
984 
985 	      i = 0;
986 	      az[i++] = "Your execution request failed because you are";
987 	      az[i++] = " not permitted to read\n\t";
988 	      az[i++] = zQinput;
989 	      az[i++] = "\non this system.\n";
990 	      az[i++] = "Execution requested was:\n\t";
991 	      az[i++] = zQcmd;
992 	      az[i++] = "\n";
993 
994 	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
995 	    }
996 
997 	  uqcleanup (zfile, iclean);
998 	  return;
999 	}
1000     }
1001 
1002   if (zQoutfile == NULL)
1003     {
1004       zoutput = NULL;
1005       qoutsys = NULL;
1006     }
1007   else if (zQoutsys != NULL
1008 	   && strcmp (zQoutsys, zLocalname) != 0)
1009     {
1010       const char *zdata;
1011       char *zcopy;
1012 
1013       /* The output file is destined for some other system, so we must
1014 	 use a temporary file to catch standard output.  */
1015 
1016       if (strcmp (zQoutsys, qsys->zname) == 0)
1017 	qoutsys = qsys;
1018       else
1019 	{
1020 	  if (! fread_system_info (zQoutsys, &soutsys))
1021 	    {
1022 	      if (! fUnknown_ok)
1023 		{
1024 		  ulog (LOG_ERROR,
1025 			"Can't send standard output to unknown system %s",
1026 			zQoutsys);
1027 		  /* We don't send mail to unknown systems, either.
1028 		     Maybe we should.  */
1029 		  uqcleanup (zfile, iclean);
1030 		  return;
1031 		}
1032 	      soutsys = sUnknown;
1033 	      soutsys.zname = zQoutsys;
1034 	    }
1035 
1036 	  qoutsys = &soutsys;
1037 
1038 	  if (! fsysdep_make_spool_dir (qoutsys))
1039 	    {
1040 	      /* If we get an error, try again later.  */
1041 	      uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1042 	      *pfprocessed = FALSE;
1043 	      return;
1044 	    }
1045 	}
1046 
1047       zdata = zsysdep_data_file_name (qoutsys, BDEFAULT_UUX_GRADE, abtemp,
1048 				      abdata, (char *) NULL);
1049       if (zdata == NULL)
1050 	{
1051 	  /* If we get an error, try again later.  */
1052 	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1053 	  *pfprocessed = FALSE;
1054 	  return;
1055 	}
1056 
1057       zcopy = (char *) alloca (strlen (zdata) + 1);
1058       strcpy (zcopy, zdata);
1059       zoutput = zcopy;
1060     }
1061   else
1062     {
1063       boolean fok;
1064       char *zcopy;
1065 
1066       qoutsys = NULL;
1067 
1068       /* If we permitted the standard output to be redirected into
1069 	 the spool directory, people could set up phony commands.  */
1070 
1071       if (fspool_file (zQoutfile))
1072 	fok = FALSE;
1073       else
1074 	{
1075 	  zQoutfile = zsysdep_real_file_name (&sLocalsys, zQoutfile,
1076 					      (const char *) NULL);
1077 	  if (zQoutfile == NULL)
1078 	    {
1079 	      /* If we get an error, try again later.  */
1080 	      uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1081 	      *pfprocessed = FALSE;
1082 	      return;
1083 	    }
1084 
1085 	  /* Make sure it's OK to receive this file.  Note that this
1086 	     means that a locally executed uux (which presumably
1087 	     requires remote files) will only be able to create files
1088 	     in standard directories.  If we don't it this way, users
1089 	     could clobber files which uucp has access to; still, it
1090 	     would be nice to allow them to direct the output to their
1091 	     home directory.  */
1092 
1093 	  fok = fin_directory_list (qsys, zQoutfile, qsys->zremote_receive,
1094 				    TRUE, FALSE, (const char *) NULL);
1095 	}
1096 
1097       if (! fok)
1098 	{
1099 	  ulog (LOG_ERROR, "Not permitted to write to %s", zQoutfile);
1100 
1101 	  if (zmail != NULL && ! fQno_ack)
1102 	    {
1103 	      const char *az[20];
1104 
1105 	      i = 0;
1106 	      az[i++] = "Your execution request failed because you are";
1107 	      az[i++] = " not permitted to write to\n\t";
1108 	      az[i++] = zQoutfile;
1109 	      az[i++] = "\non this system.\n";
1110 	      az[i++] = "Execution requested was:\n\t";
1111 	      az[i++] = zQcmd;
1112 	      az[i++] = "\n";
1113 
1114 	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
1115 	    }
1116 
1117 	  uqcleanup (zfile, iclean);
1118 	  return;
1119 	}
1120 
1121       zcopy = (char *) alloca (strlen (zQoutfile) + 1);
1122       strcpy (zcopy, zQoutfile);
1123       zQoutfile = zcopy;
1124       zoutput = zcopy;
1125     }
1126 
1127   /* Move the required files to the execution directory if necessary.  */
1128 
1129   for (i = 0; i < cQfiles; i++)
1130     {
1131       if (azQfiles_to[i] != NULL)
1132 	{
1133 	  const char *zname;
1134 
1135 	  /* Move the file to the execute directory.  */
1136 
1137 	  zname = zsysdep_in_dir (XQTDIR, azQfiles_to[i]);
1138 	  if (zname == NULL)
1139 	    {
1140 	      /* If we get an error, try again later.  */
1141 	      uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1142 	      *pfprocessed = FALSE;
1143 	      return;
1144 	    }
1145 	  if (! fsysdep_move_file (azQfiles[i], zname, 0, FALSE,
1146 				   (const char *) NULL))
1147 	    {
1148 	      /* If we get an error, try again later.  This may not be
1149 		 correct, depending on what kind of error we get.  */
1150 	      uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1151 	      *pfprocessed = FALSE;
1152 	      return;
1153 	    }
1154 
1155 	  /* If we just moved the standard input file, adjust the
1156 	     file name.  */
1157 	  if (zQinput != NULL && strcmp (azQfiles[i], zQinput) == 0)
1158 	    {
1159 	      xfree ((pointer) zQinput);
1160 	      zQinput = xstrdup (zname);
1161 	    }
1162 	}
1163     }
1164 
1165 #if ALLOW_SH_EXECUTION
1166   fshell = fQuse_sh;
1167 #else
1168   fshell = FALSE;
1169 #endif
1170 
1171   /* Get a shell command which uses the full path of the command to
1172      execute.  */
1173   zfullcmd = (char *) alloca (strlen (zQcmd) + strlen (azQargs[0]) + 2);
1174   *zfullcmd = '\0';
1175   for (i = 0; azQargs[i] != NULL; i++)
1176     {
1177       strcat (zfullcmd, azQargs[i]);
1178       strcat (zfullcmd, " ");
1179     }
1180   zfullcmd[strlen (zfullcmd) - 1] = '\0';
1181 
1182   if (! fsysdep_execute (qsys,
1183 			 zQuser == NULL ? (const char *) "uucp" : zQuser,
1184 			 azQargs, zfullcmd, zQinput, zoutput, fshell,
1185 			 &zerror, &ftemp))
1186     {
1187       if (ftemp)
1188 	{
1189 	  ulog (LOG_NORMAL, "Will retry later (%s)", zfile);
1190 	  if (zoutput != NULL)
1191 	    (void) remove (zoutput);
1192 	  if (zerror != NULL)
1193 	    (void) remove (zerror);
1194 	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1195 	  *pfprocessed = FALSE;
1196 	  return;
1197 	}
1198 
1199       ulog (LOG_NORMAL, "Execution failed (%s)", zfile);
1200 
1201       if (zmail != NULL && ! fQno_ack)
1202 	{
1203 	  const char **pz;
1204 	  int cgot;
1205 	  FILE *eerr;
1206 	  int istart;
1207 
1208 	  cgot = 20;
1209 	  pz = (const char **) xmalloc (cgot * sizeof (const char *));
1210 	  i = 0;
1211 	  pz[i++] = "Execution request failed:\n\t";
1212 	  pz[i++] = zQcmd;
1213 	  pz[i++] = "\n";
1214 
1215 	  if (zerror == NULL)
1216 	    eerr = NULL;
1217 	  else
1218 	    eerr = fopen (zerror, "r");
1219 	  if (eerr == NULL)
1220 	    {
1221 	      pz[i++] = "There was no output on standard error\n";
1222 	      istart = i;
1223 	    }
1224 	  else
1225 	    {
1226 	      char *zline;
1227 
1228 	      pz[i++] = "Standard error output was:\n";
1229 	      istart = i;
1230 
1231 	      while ((zline = zfgets (eerr, FALSE)) != NULL)
1232 		{
1233 		  if (i >= cgot)
1234 		    {
1235 		      cgot += 20;
1236 		      pz = ((const char **)
1237 			    xrealloc ((pointer) pz,
1238 				      cgot * sizeof (const char *)));
1239 		    }
1240 		  pz[i++] = zline;
1241 		}
1242 
1243 	      (void) fclose (eerr);
1244 	    }
1245 
1246 	  (void) fsysdep_mail (zmail, "Execution failed", i, pz);
1247 
1248 	  for (; istart < i; istart++)
1249 	    xfree ((pointer) pz[istart]);
1250 	  xfree ((pointer) pz);
1251 	}
1252 
1253       if (qoutsys != NULL)
1254 	(void) remove (zoutput);
1255     }
1256   else
1257     {
1258       if (zmail != NULL && fQsuccess_ack)
1259 	{
1260 	  const char *az[20];
1261 
1262 	  i = 0;
1263 	  az[i++] = "\nExecution request succeeded:\n\t";
1264 	  az[i++] = zQcmd;
1265 	  az[i++] = "\n";
1266 
1267 	  (void) fsysdep_mail (zmail, "Execution succeded", i, az);
1268 	}
1269 
1270       /* Now we may have to uucp the output to some other machine.  */
1271 
1272       if (qoutsys != NULL)
1273 	{
1274 	  struct scmd s;
1275 
1276 	  /* Fill in the command structure.  */
1277 
1278 	  s.bcmd = 'S';
1279 	  s.pseq = NULL;
1280 	  s.zfrom = abtemp;
1281 	  s.zto = zQoutfile;
1282 	  if (zQuser != NULL)
1283 	    s.zuser = zQuser;
1284 	  else
1285 	    s.zuser = "uucp";
1286 	  if (zmail != NULL && fQsuccess_ack)
1287 	    s.zoptions = "Cn";
1288 	  else
1289 	    s.zoptions = "C";
1290 	  s.ztemp = abtemp;
1291 	  s.imode = 0666;
1292 	  if (zmail != NULL && fQsuccess_ack)
1293 	    s.znotify = zmail;
1294 	  else
1295 	    s.znotify = "";
1296 	  /* The number of bytes will be filled in when the file is
1297 	     actually sent.  */
1298 	  s.cbytes = -1;
1299 
1300 	  (void) zsysdep_spool_commands (qoutsys, BDEFAULT_UUX_GRADE,
1301 					 1, &s);
1302 	}
1303     }
1304 
1305   if (zerror != NULL)
1306     (void) remove (zerror);
1307 
1308   uqcleanup (zfile, iclean);
1309 }
1310 
1311 /* Clean up the results of uqdo_xqt_file.  */
1312 
1313 static void
uqcleanup(zfile,iflags)1314 uqcleanup (zfile, iflags)
1315      const char *zfile;
1316      int iflags;
1317 {
1318   int i;
1319 
1320   DEBUG_MESSAGE2 (DEBUG_SPOOLDIR,
1321 		  "uqcleanup: %s, %d", zfile, iflags);
1322 
1323   if (zQunlock_file != NULL)
1324     {
1325       (void) fsysdep_unlock_uuxqt_file (zQunlock_file);
1326       zQunlock_file = NULL;
1327     }
1328 
1329   if ((iflags & REMOVE_FILE) != 0)
1330     (void) remove (zfile);
1331 
1332   if ((iflags & REMOVE_NEEDED) != 0)
1333     {
1334       for (i = 0; i < cQfiles; i++)
1335 	{
1336 	  if (azQfiles[i] != NULL)
1337 	    (void) remove (azQfiles[i]);
1338 	}
1339     }
1340 
1341   if ((iflags & FREE_QINPUT) != 0)
1342     xfree ((pointer) zQinput);
1343 
1344   if (fQunlock_directory)
1345     {
1346       (void) fsysdep_unlock_uuxqt_dir ();
1347       fQunlock_directory = FALSE;
1348     }
1349 
1350   for (i = 0; i < cQfiles; i++)
1351     {
1352       xfree ((pointer) azQfiles[i]);
1353       xfree ((pointer) azQfiles_to[i]);
1354     }
1355 
1356   xfree ((pointer) azQargs);
1357   azQargs = NULL;
1358 
1359   xfree ((pointer) zQcmd);
1360   zQcmd = NULL;
1361 
1362   xfree ((pointer) azQfiles);
1363   azQfiles = NULL;
1364 
1365   xfree ((pointer) azQfiles_to);
1366   azQfiles_to = NULL;
1367 }
1368