xref: /386bsd/usr/src/libexec/uucp/uux.c (revision a2142627)
1 /* uux.c
2    Prepare to execute a command on a remote system.
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: uux.c,v $
26    Revision 1.32  1992/03/15  04:51:17  ian
27    Keep an array of signals we've received rather than a single variable
28 
29    Revision 1.31  1992/03/12  19:54:43  ian
30    Debugging based on types rather than number
31 
32    Revision 1.30  1992/03/02  15:20:43  ian
33    Check iSignal before entering fread
34 
35    Revision 1.29  1992/02/29  04:07:08  ian
36    Added -j option to uucp and uux
37 
38    Revision 1.28  1992/02/29  01:06:59  ian
39    Chip Salzenberg: recheck file permissions before sending
40 
41    Revision 1.27  1992/02/28  05:06:15  ian
42    T. William Wells: fsysdep_catch must be a macro
43 
44    Revision 1.26  1992/02/27  05:40:54  ian
45    T. William Wells: detach from controlling terminal, handle signals safely
46 
47    Revision 1.25  1992/02/23  03:26:51  ian
48    Overhaul to use automatic configure shell script
49 
50    Revision 1.24  1992/02/08  22:33:32  ian
51    Only get the current working directory if it's going to be needed
52 
53    Revision 1.23  1992/02/08  20:33:57  ian
54    Handle all possible signals raised by abort
55 
56    Revision 1.22  1992/02/08  03:54:18  ian
57    Include <string.h> only in <uucp.h>, added 1992 copyright
58 
59    Revision 1.21  1992/02/02  20:34:36  ian
60    Niels Baggesen: must check user permissions on access to local files
61 
62    Revision 1.20  1992/01/21  19:39:12  ian
63    Chip Salzenberg: uucp and uux start uucico for right system, not any
64 
65    Revision 1.19  1992/01/15  07:06:29  ian
66    Set configuration directory in Makefile rather than sysdep.h
67 
68    Revision 1.18  1992/01/05  03:09:17  ian
69    Changed abProgram and abVersion to non const to avoid compiler bug
70 
71    Revision 1.17  1992/01/05  02:51:38  ian
72    Allocate enough space for log message
73 
74    Revision 1.16  1991/12/29  04:04:18  ian
75    Added a bunch of extern definitions
76 
77    Revision 1.15  1991/12/21  21:16:05  ian
78    Franc,ois Pinard: remove parentheses from ZSHELLSEPS
79 
80    Revision 1.14  1991/12/20  03:07:54  ian
81    Added space and tab to ZSHELLSEPS to stop command at whitespace
82 
83    Revision 1.13  1991/12/18  03:54:14  ian
84    Made error messages to terminal appear more normal
85 
86    Revision 1.12  1991/12/14  16:09:07  ian
87    Added -l option to uux to link files into the spool directory
88 
89    Revision 1.11  1991/12/11  03:59:19  ian
90    Create directories when necessary; don't just assume they exist
91 
92    Revision 1.10  1991/12/07  03:03:12  ian
93    Split arguments like sh; request sh execution if any metachars appear
94 
95    Revision 1.9  1991/11/21  22:17:06  ian
96    Add version string, print version when printing usage
97 
98    Revision 1.8  1991/11/15  19:17:32  ian
99    Hannu Strang: copy stdin using fread/fwrite, not fgets/fputs
100 
101    Revision 1.7  1991/11/13  23:08:40  ian
102    Expand remote pathnames in uucp and uux; fix up uux special cases
103 
104    Revision 1.6  1991/11/08  21:53:17  ian
105    Brian Campbell: fix argument handling when looking for '-'
106 
107    Revision 1.5  1991/11/07  22:52:49  ian
108    Chip Salzenberg: avoid recursive strtok, handle redirection better
109 
110    Revision 1.4  1991/09/19  03:23:34  ian
111    Chip Salzenberg: append to private debugging file, don't overwrite it
112 
113    Revision 1.3  1991/09/19  02:30:37  ian
114    From Chip Salzenberg: check whether signal is ignored differently
115 
116    Revision 1.2  1991/09/11  02:33:14  ian
117    Added ffork argument to fsysdep_run
118 
119    Revision 1.1  1991/09/10  19:40:31  ian
120    Initial revision
121 
122    */
123 
124 #include "uucp.h"
125 
126 #if USE_RCS_ID
127 char uux_rcsid[] = "$Id: uux.c,v 1.32 1992/03/15 04:51:17 ian Rel $";
128 #endif
129 
130 #include <ctype.h>
131 #include <errno.h>
132 
133 #include "getopt.h"
134 
135 #include "system.h"
136 #include "sysdep.h"
137 
138 /* External functions.  */
139 extern int fclose ();
140 
141 /* These character lists should, perhaps, be in sysdep.h.  */
142 
143 /* This is the list of shell metacharacters that we check for.  If one
144    of these is present, we request uuxqt to execute the command with
145    /bin/sh.  Otherwise we let it execute using execve.  */
146 
147 #define ZSHELLCHARS "\"'`*?[;&()|<>\\$"
148 
149 /* This is the list of word separators.  We break filename arguments
150    at these characters.  */
151 #define ZSHELLSEPS ";&*|<> \t"
152 
153 /* This is the list of word separators without the redirection
154    operators.  */
155 #define ZSHELLNONREDIRSEPS ";&*| \t"
156 
157 /* The program name.  */
158 char abProgram[] = "uux";
159 
160 /* Long getopt options.  */
161 
162 static const struct option asXlongopts[] = { { NULL, 0, NULL, 0 } };
163 
164 const struct option *_getopt_long_options = asXlongopts;
165 
166 /* The execute file we are creating.  */
167 
168 static FILE *eXxqt_file;
169 
170 /* A list of commands to be spooled.  */
171 
172 static struct scmd *pasXcmds;
173 static int cXcmds;
174 
175 /* A file to close if we're forced to exit.  */
176 
177 static FILE *eXclose;
178 
179 /* Local functions.  */
180 
181 static void uxusage P((void));
182 static void uxadd_xqt_line P((int bchar, const char *z1, const char *z2));
183 static void uxadd_send_file P((const char *zfrom, const char *zto,
184 			       const char *zoptions, const char *ztemp));
185 static void uxcopy_stdin P((FILE *e));
186 static void uxrecord_file P((const char *zfile));
187 static void uxabort P((void));
188 
189 int
main(argc,argv)190 main (argc, argv)
191      int argc;
192      char **argv;
193 {
194   int iopt;
195   /* -a: requestor address for status reports.  */
196   const char *zrequestor = NULL;
197   /* -b: if true, return standard input on error.  */
198   boolean fretstdin = FALSE;
199   /* -c,-C: if true, copy to spool directory.  */
200   boolean fcopy = FALSE;
201   /* -c: set if -c appears explicitly; if it and -l appear, then if the
202      link fails we don't copy the file.  */
203   boolean fdontcopy = FALSE;
204   /* -I: configuration file name.  */
205   const char *zconfig = NULL;
206   /* -j: output job id.  */
207   boolean fjobid = FALSE;
208   /* -g: job grade.  */
209   char bgrade = BDEFAULT_UUX_GRADE;
210   /* -l: link file to spool directory.  */
211   boolean flink = FALSE;
212   /* -n: do not notify upon command completion.  */
213   boolean fno_ack = FALSE;
214   /* -p: read standard input for command standard input.  */
215   boolean fread_stdin = FALSE;
216   /* -r: do not start uucico when finished.  */
217   boolean fuucico = TRUE;
218   /* -s: report status to named file.  */
219   const char *zstatus_file = NULL;
220   /* -W: only expand local file names.  */
221   boolean fexpand = TRUE;
222   /* -z: report status only on error.  */
223   boolean ferror_ack = FALSE;
224   int i;
225   int clen;
226   char *zargs;
227   char *zarg;
228   char *zcmd;
229   char *zexclam;
230   boolean fgetcwd;
231   const char *zuser;
232   struct ssysteminfo sxqtsys;
233   const struct ssysteminfo *qxqtsys;
234   boolean fxqtlocal;
235   char **pzargs;
236   int calloc_args;
237   int cargs;
238   const char *zxqtname;
239   char abxqt_tname[CFILE_NAME_LEN];
240   char abxqt_xname[CFILE_NAME_LEN];
241   boolean fneedshell;
242   char *zprint;
243   const char *zcall_system;
244   boolean fcall_any;
245   boolean fexit;
246 
247   /* We need to be able to read a single - as an option, which getopt
248      won't do.  So that we can still use getopt, we run through the
249      options looking for an option "-"; if we find one we change it to
250      "-p", which is an equivalent option.  */
251 
252   for (i = 1; i < argc; i++)
253     {
254       if (argv[i][0] != '-')
255 	break;
256       if (argv[i][1] == '\0')
257 	argv[i] = xstrdup ("-p");
258       else
259 	{
260 	  const char *z;
261 
262 	  for (z = argv[i] + 1; *z != '\0'; z++)
263 	    {
264 	      /* If the option takes an argument, and the argument is
265 		 not appended, then skip the next argument.  */
266 	      if (*z == 'a' || *z == 'g' || *z == 'I'
267 		  || *z == 's' || *z == 'x')
268 		{
269 		  if (z[1] == '\0')
270 		    i++;
271 		  break;
272 		}
273 	    }
274 	}
275     }
276 
277   /* The leading + in the getopt string means to stop processing
278      options as soon as a non-option argument is seen.  */
279 
280   while ((iopt = getopt (argc, argv, "+a:bcCg:I:jlnprs:Wx:z")) != EOF)
281     {
282       switch (iopt)
283 	{
284 	case 'a':
285 	  /* Set requestor name: mail address to which status reports
286 	     should be sent.  */
287 	  zrequestor = optarg;
288 	  break;
289 
290 	case 'b':
291 	  /* Return standard input on error.  */
292 	  fretstdin = TRUE;
293 	  break;
294 
295 	case 'c':
296 	  /* Do not copy local files to spool directory.  */
297 	  fcopy = FALSE;
298 	  fdontcopy = TRUE;
299 	  break;
300 
301 	case 'C':
302 	  /* Copy local files to spool directory.  */
303 	  fcopy = TRUE;
304 	  break;
305 
306 	case 'I':
307 	  /* Configuration file name.  */
308 	  zconfig = optarg;
309 	  break;
310 
311 	case 'j':
312 	  /* Output jobid.  */
313 	  fjobid = TRUE;
314 	  break;
315 
316 	case 'g':
317 	  /* Set job grade.  */
318 	  bgrade = optarg[0];
319 	  break;
320 
321 	case 'l':
322 	  /* Link file to spool directory.  */
323 	  flink = TRUE;
324 	  break;
325 
326 	case 'n':
327 	  /* Do not notify upon command completion.  */
328 	  fno_ack = TRUE;
329 	  break;
330 
331 	case 'p':
332 	  /* Read standard input for command standard input.  */
333 	  fread_stdin = TRUE;
334 	  break;
335 
336 	case 'r':
337 	  /* Do not start uucico when finished.  */
338 	  fuucico = FALSE;
339 	  break;
340 
341 	case 's':
342 	  /* Report status to named file.  */
343 	  zstatus_file = optarg;
344 	  break;
345 
346 	case 'W':
347 	  /* Only expand local file names.  */
348 	  fexpand = FALSE;
349 	  break;
350 
351 	case 'x':
352 #if DEBUG > 1
353 	  /* Set debugging level.  */
354 	  iDebug |= idebug_parse (optarg);
355 #endif
356 	  break;
357 
358 	case 'z':
359 	  /* Report status only on error.  */
360 	  ferror_ack = TRUE;
361 	  break;
362 
363 	case 0:
364 	  /* Long option found and flag set.  */
365 	  break;
366 
367 	default:
368 	  uxusage ();
369 	  break;
370 	}
371     }
372 
373   if (! FGRADE_LEGAL (bgrade))
374     {
375       ulog (LOG_ERROR, "Ignoring illegal grade");
376       bgrade = BDEFAULT_UUX_GRADE;
377     }
378 
379   if (optind == argc)
380     uxusage ();
381 
382   uread_config (zconfig);
383 
384   /* The command and files arguments could be quoted in any number of
385      ways, so we split them apart ourselves.  We do this before
386      calling usysdep_initialize because we want to set fgetcwd
387      correctly.  */
388   clen = 1;
389   for (i = optind; i < argc; i++)
390     clen += strlen (argv[i]) + 1;
391 
392   zargs = (char *) alloca (clen);
393   *zargs = '\0';
394   for (i = optind; i < argc; i++)
395     {
396       strcat (zargs, argv[i]);
397       strcat (zargs, " ");
398     }
399 
400   /* The first argument is the command to execute.  */
401   clen = strcspn (zargs, ZSHELLSEPS);
402   zcmd = (char *) alloca (clen + 1);
403   strncpy (zcmd, zargs, clen);
404   zcmd[clen] = '\0';
405   zargs += clen;
406 
407   /* Split the arguments out into an array.  We break the arguments
408      into alternating sequences of characters not in ZSHELLSEPS
409      and characters in ZSHELLSEPS.  We remove whitespace.  We
410      separate the redirection characters '>' and '<' into their
411      own arguments to make them easier to process below.  */
412 
413   calloc_args = 10;
414   pzargs = (char **) xmalloc (calloc_args * sizeof (char *));
415   cargs = 0;
416 
417   for (zarg = strtok (zargs, " \t");
418        zarg != NULL;
419        zarg = strtok ((char *) NULL, " \t"))
420     {
421       while (*zarg != '\0')
422 	{
423 	  if (cargs >= calloc_args + 1)
424 	    {
425 	      calloc_args += 10;
426 	      pzargs = (char **) xrealloc ((pointer) pzargs,
427 					   calloc_args * sizeof (char *));
428 	    }
429 
430 	  clen = strcspn (zarg, ZSHELLSEPS);
431 	  if (clen > 0)
432 	    {
433 	      pzargs[cargs] = (char *) xmalloc (clen + 1);
434 	      strncpy (pzargs[cargs], zarg, clen);
435 	      pzargs[cargs][clen] = '\0';
436 	      ++cargs;
437 	      zarg += clen;
438 	    }
439 
440 	  /* We deliberately separate '>' and '<' out.  */
441 	  if (*zarg != '\0')
442 	    {
443 	      clen = strspn (zarg, ZSHELLNONREDIRSEPS);
444 	      if (clen == 0)
445 		clen = 1;
446 	      pzargs[cargs] = (char *) xmalloc (clen + 1);
447 	      strncpy (pzargs[cargs], zarg, clen);
448 	      pzargs[cargs][clen] = '\0';
449 	      ++cargs;
450 	      zarg += clen;
451 	    }
452 	}
453     }
454 
455   /* Now look through the arguments to see if we are going to need the
456      current working directory.  We don't try to make a precise
457      determination, just a conservative one.  The basic idea is that
458      we don't want to get the cwd for 'rmail - foo!user' (note that we
459      don't examine the command itself).  */
460   fgetcwd = FALSE;
461   for (i = 0; i < cargs; i++)
462     {
463       zexclam = strrchr (pzargs[i], '!');
464       if (zexclam != NULL && fsysdep_needs_cwd (zexclam + 1))
465 	{
466 	  fgetcwd = TRUE;
467 	  break;
468 	}
469       if ((pzargs[i][0] == '<' || pzargs[i][0] == '>')
470 	  && i + 1 < cargs
471 	  && strchr (pzargs[i + 1], '!') == NULL
472 	  && fsysdep_needs_cwd (pzargs[i + 1]))
473 	{
474 	  fgetcwd = TRUE;
475 	  break;
476 	}
477     }
478 
479 #ifdef SIGINT
480   usysdep_signal (SIGINT);
481 #endif
482 #ifdef SIGHUP
483   usysdep_signal (SIGHUP);
484 #endif
485 #ifdef SIGQUIT
486   usysdep_signal (SIGQUIT);
487 #endif
488 #ifdef SIGTERM
489   usysdep_signal (SIGTERM);
490 #endif
491 #ifdef SIGPIPE
492   usysdep_signal (SIGPIPE);
493 #endif
494 
495   usysdep_initialize (FALSE, fgetcwd);
496 
497   ulog_fatal_fn (uxabort);
498 
499   zuser = zsysdep_login_name ();
500 
501   /* Figure out which system the command is to be executed on.  */
502   zexclam = strchr (zcmd, '!');
503   if (zexclam == NULL)
504     {
505       qxqtsys = &sLocalsys;
506       fxqtlocal = TRUE;
507     }
508   else
509     {
510       *zexclam = '\0';
511 
512       if (*zcmd == '\0' || strcmp (zcmd, zLocalname) == 0)
513 	{
514 	  qxqtsys = &sLocalsys;
515 	  fxqtlocal = TRUE;
516 	}
517       else
518 	{
519 	  if (fread_system_info (zcmd, &sxqtsys))
520 	    qxqtsys = &sxqtsys;
521 	  else
522 	    {
523 	      if (! fUnknown_ok)
524 		ulog (LOG_FATAL, "System %s unknown", zcmd);
525 	      qxqtsys = &sUnknown;
526 	      sUnknown.zname = zcmd;
527 	    }
528 
529 	  fxqtlocal = FALSE;
530 	}
531 
532       zcmd = zexclam + 1;
533     }
534 
535   /* Make sure we have a spool directory.  */
536 
537   if (! fsysdep_make_spool_dir (qxqtsys))
538     uxabort ();
539 
540   /* Name and open the execute file.  If the execution is to occur on
541      a remote system, we must create a data file and copy it over.  */
542   if (fxqtlocal)
543     zxqtname = zsysdep_xqt_file_name ();
544   else
545     zxqtname = zsysdep_data_file_name (qxqtsys, 'X', abxqt_tname,
546 				       (char *) NULL, abxqt_xname);
547   if (zxqtname == NULL)
548     uxabort ();
549 
550   eXxqt_file = esysdep_fopen (zxqtname, FALSE, FALSE, TRUE);
551   if (eXxqt_file == NULL)
552     uxabort ();
553 
554   uxrecord_file (xstrdup (zxqtname));
555 
556   /* Specify the user.  */
557   uxadd_xqt_line ('U', zuser, zLocalname);
558 
559   /* Look through the arguments.  Any argument containing an
560      exclamation point character is interpreted as a file name, and is
561      sent to the appropriate system.  */
562 
563   zcall_system = NULL;
564   fcall_any = FALSE;
565 
566   for (i = 0; i < cargs; i++)
567     {
568       const char *zsystem, *zconst;
569       char *zfile;
570       boolean finput, foutput;
571       boolean flocal;
572 
573       /* Check for a parenthesized argument; remove the parentheses
574 	 and otherwise ignore it (this is how an exclamation point is
575 	 quoted).  */
576 
577       if (pzargs[i][0] == '(')
578 	{
579 	  clen = strlen (pzargs[i]);
580 	  if (pzargs[i][clen - 1] != ')')
581 	    ulog (LOG_ERROR, "Mismatched parentheses");
582 	  else
583 	    pzargs[i][clen - 1] = '\0';
584 	  ++pzargs[i];
585 	  continue;
586 	}
587 
588       /* Check whether we are doing a redirection.  */
589 
590       finput = FALSE;
591       foutput = FALSE;
592       if (i + 1 < cargs)
593 	{
594 	  if (pzargs[i][0] == '<')
595 	    finput = TRUE;
596 	  else if (pzargs[i][0] == '>')
597 	    foutput = TRUE;
598 	  if (finput || foutput)
599 	    {
600 	      pzargs[i] = NULL;
601 	      i++;
602 	    }
603 	}
604 
605       zexclam = strchr (pzargs[i], '!');
606 
607       /* If there is no exclamation point and no redirection, this
608 	 argument is left untouched.  */
609 
610       if (zexclam == NULL && ! finput && ! foutput)
611 	continue;
612 
613       /* Get the system name and file name for this file.  */
614 
615       if (zexclam == NULL)
616 	{
617 	  zsystem = zLocalname;
618 	  zfile = pzargs[i];
619 	  flocal = TRUE;
620 	}
621       else
622 	{
623 	  *zexclam = '\0';
624 	  zsystem = pzargs[i];
625 	  if (zsystem[0] != '\0')
626 	    flocal = strcmp (zsystem, zLocalname) == 0;
627 	  else
628 	    {
629 	      zsystem = zLocalname;
630 	      flocal = TRUE;
631 	    }
632 	  zfile = zexclam + 1;
633 	}
634 
635       /* Add the current working directory to the file name if it's
636 	 not an absolute path.  */
637       if (fexpand || flocal)
638 	{
639 	  zconst = zsysdep_add_cwd (zfile, flocal);
640 	  if (zconst == NULL)
641 	    uxabort ();
642 	  zfile = xstrdup (zconst);
643 	}
644 
645       /* Check for output redirection.  We strip this argument out,
646 	 and create an O command which tells uuxqt where to send the
647 	 output.  */
648 
649       if (foutput)
650 	{
651 	  if (flocal)
652 	    {
653 	      if (! fin_directory_list (qxqtsys, zfile,
654 					qxqtsys->zremote_receive, TRUE,
655 					FALSE, (const char *) NULL))
656 		ulog (LOG_FATAL, "Not permitted to create %s", zfile);
657 	    }
658 
659 	  if (strcmp (zsystem, qxqtsys->zname) == 0)
660 	    uxadd_xqt_line ('O', zfile, (const char *) NULL);
661 	  else
662 	    uxadd_xqt_line ('O', zfile, zsystem);
663 	  pzargs[i] = NULL;
664 	  continue;
665 	}
666 
667       if (finput)
668 	{
669 	  if (fread_stdin)
670 	    ulog (LOG_FATAL, "Standard input specified twice");
671 	  pzargs[i] = NULL;
672 	}
673 
674       if (flocal)
675 	{
676 	  char *zuse;
677 	  const char *zdata;
678 	  char abtname[CFILE_NAME_LEN];
679 	  char abdname[CFILE_NAME_LEN];
680 
681 	  /* It's a local file.  If requested by -C, copy the file to
682 	     the spool directory.  If requested by -l, link the file
683 	     to the spool directory; if the link fails, we copy the
684 	     file, unless -c was explictly used.  If the execution is
685 	     occurring on the local system, we force the copy as well,
686 	     because otherwise we would have to have some way to tell
687 	     uuxqt not to move the file.  If the file is being shipped
688 	     to another system, we must set up a transfer request.
689 	     First make sure the user has legitimate access, since we
690 	     are running setuid.  */
691 
692 	  if (! fsysdep_access (zfile))
693 	    uxabort ();
694 
695 	  if (fcopy || flink || fxqtlocal)
696 	    {
697 	      char *zdup;
698 	      boolean fdid;
699 
700 	      zdata = zsysdep_data_file_name (qxqtsys, bgrade, abtname,
701 					      abdname, (char *) NULL);
702 	      if (zdata == NULL)
703 		uxabort ();
704 
705 	      zdup = xstrdup (zdata);
706 	      uxrecord_file (zdup);
707 
708 	      fdid = FALSE;
709 	      if (flink)
710 		{
711 		  boolean fworked;
712 
713 		  if (! fsysdep_link (zfile, zdup, &fworked))
714 		    uxabort ();
715 
716 		  if (fworked)
717 		    fdid = TRUE;
718 		  else if (fdontcopy)
719 		    ulog (LOG_FATAL, "%s: Can't link to spool directory",
720 			  zfile);
721 		}
722 
723 	      if (! fdid)
724 		{
725 		  if (! fcopy_file (zfile, zdup, FALSE, TRUE))
726 		    uxabort ();
727 		}
728 
729 	      xfree ((pointer) zdup);
730 
731 	      zuse = abtname;
732 	    }
733 	  else
734 	    {
735 	      /* Make sure the daemon can access the file.  */
736 	      if (! fsysdep_daemon_access (zfile))
737 		uxabort ();
738 	      if (! fin_directory_list (&sLocalsys, zfile,
739 					sLocalsys.zlocal_send,
740 					TRUE, TRUE, zuser))
741 		ulog (LOG_FATAL, "Not permitted to send from %s",
742 		      zfile);
743 
744 	      zuse = zfile;
745 
746 	      zdata = zsysdep_data_file_name (qxqtsys, bgrade,
747 					      (char *) NULL, abdname,
748 					      (char *) NULL);
749 	      if (zdata == NULL)
750 		uxabort ();
751 	      strcpy (abtname, "D.0");
752 	    }
753 
754 	  if (fxqtlocal)
755 	    {
756 	      if (finput)
757 		uxadd_xqt_line ('I', zuse, (char *) NULL);
758 	      else
759 		pzargs[i] = zuse;
760 	    }
761 	  else
762 	    {
763 	      uxadd_send_file (zuse, abdname,
764 			       fcopy || flink || fxqtlocal ? "C" : "c",
765 			       abtname);
766 
767 	      if (finput)
768 		{
769 		  uxadd_xqt_line ('F', abdname, (char *) NULL);
770 		  uxadd_xqt_line ('I', abdname, (char *) NULL);
771 		}
772 	      else
773 		{
774 		  const char *zbase;
775 
776 		  zbase = zsysdep_base_name (zfile);
777 		  if (zbase == NULL)
778 		    uxabort ();
779 		  uxadd_xqt_line ('F', abdname, zbase);
780 		  pzargs[i] = xstrdup (zbase);
781 		}
782 	    }
783 	}
784       else if (strcmp (qxqtsys->zname, zsystem) == 0)
785 	{
786 	  /* The file is already on the system where the command is to
787 	     be executed.  */
788 	  if (finput)
789 	    uxadd_xqt_line ('I', zfile, (const char *) NULL);
790 	  else
791 	    pzargs[i] = zfile;
792 	}
793       else
794 	{
795 	  struct ssysteminfo sfromsys;
796 	  const struct ssysteminfo *qfromsys;
797 	  char abtname[CFILE_NAME_LEN];
798 	  char abdname[CFILE_NAME_LEN];
799 	  char *ztemp;
800 	  struct scmd s;
801 	  const char *zjobid;
802 
803 	  /* We need to request a remote file.  Make sure we have a
804 	     spool directory for the remote system.  */
805 
806 	  if (! fread_system_info (zsystem, &sfromsys))
807 	    {
808 	      if (! fUnknown_ok)
809 		ulog (LOG_FATAL, "System %s unknown", zsystem);
810 	      sfromsys = sUnknown;
811 	      sfromsys.zname = zsystem;
812 	    }
813 	  qfromsys = &sfromsys;
814 
815 	  if (! fsysdep_make_spool_dir (qfromsys))
816 	    uxabort ();
817 
818 	  /* We want the file to wind up in the spool directory of the
819 	     local system (whether the execution is occurring
820 	     locally or not); we have to use an absolute file name
821 	     here, because otherwise the file would wind up in the
822 	     spool directory of the system it is coming from.  */
823 
824 	  if (! fxqtlocal)
825 	    {
826 	      if (! fsysdep_make_spool_dir (&sLocalsys))
827 		uxabort ();
828 	    }
829 
830 	  zconst = zsysdep_data_file_name (&sLocalsys, bgrade,
831 					   abtname, (char *) NULL,
832 					   (char *) NULL);
833 	  if (zconst == NULL)
834 	    uxabort ();
835 
836 	  /* Request the file.  The special option '9' is a signal to
837 	     uucico that it's OK to receive a file into the spool
838 	     directory; normally such requests are rejected.  */
839 
840 	  s.bcmd = 'R';
841 	  s.pseq = NULL;
842 	  s.zfrom = zfile;
843 	  s.zto = abtname;
844 	  s.zuser = zuser;
845 	  s.zoptions = "9";
846 	  s.ztemp = "";
847 	  s.imode = 0600;
848 	  s.znotify = "";
849 	  s.cbytes = -1;
850 
851 	  zjobid = zsysdep_spool_commands (qfromsys, bgrade, 1, &s);
852 	  if (zjobid == NULL)
853 	    uxabort ();
854 
855 	  if (fjobid)
856 	    printf ("%s\n", zjobid);
857 
858 	  if (fcall_any)
859 	    zcall_system = NULL;
860 	  else
861 	    {
862 	      fcall_any = TRUE;
863 	      zcall_system = xstrdup (qfromsys->zname);
864 	    }
865 
866 	  /* Now if the execution is to occur on another system, we
867 	     must create an execute file to send the file there.  The
868 	     name of the file on the execution system is put into
869 	     abdname.  */
870 
871 	  if (fxqtlocal)
872 	    ztemp = abtname;
873 	  else
874 	    {
875 	      const char *zxqt_file;
876 	      FILE *e;
877 
878 	      /* Get a file name to use on the execution system.  */
879 
880 	      if (zsysdep_data_file_name (qxqtsys, bgrade,
881 					  (char *) NULL, abdname,
882 					  (char *) NULL) == NULL)
883 		uxabort ();
884 	      ztemp = abdname;
885 
886 	      /* The local spool directory was created above, if it
887 		 didn't already exist.  */
888 
889 	      zxqt_file = zsysdep_xqt_file_name ();
890 	      if (zxqt_file == NULL)
891 		uxabort ();
892 
893 	      /* Queue up a uucp command to be executed locally once
894 		 the file arrives.  We take advantage of the file
895 		 renaming and moving that uuxqt does to remove the
896 		 file and avoid the hassles of adding the current
897 		 directory.  The -W switch to uucp prevents from
898 		 adding the current directory to the remote file.  */
899 
900 	      e = esysdep_fopen (zxqt_file, FALSE, FALSE, TRUE);
901 	      if (e == NULL)
902 		uxabort ();
903 
904 	      eXclose = e;
905 	      uxrecord_file (xstrdup (zxqt_file));
906 
907 	      fprintf (e, "U %s %s\n", zuser, zLocalname);
908 	      fprintf (e, "F %s foo\n", abtname);
909 	      fprintf (e, "C uucp -CW foo %s!%s\n", qxqtsys->zname,
910 		       abdname);
911 
912 	      eXclose = NULL;
913 	      if (fclose (e) != 0)
914 		ulog (LOG_FATAL, "fclose: %s", strerror (errno));
915 	    }
916 
917 	  /* Tell the command execution to wait until the file has
918 	     been received, and tell it the real file name to use.  */
919 
920 	  if (finput)
921 	    {
922 	      uxadd_xqt_line ('F', ztemp, (char *) NULL);
923 	      uxadd_xqt_line ('I', ztemp, (char *) NULL);
924 	    }
925 	  else
926 	    {
927 	      const char *zbase;
928 
929 	      zbase = zsysdep_base_name (zfile);
930 	      if (zbase == NULL)
931 		uxabort ();
932 	      uxadd_xqt_line ('F', ztemp, zbase);
933 	      pzargs[i] = xstrdup (zbase);
934 	    }
935 	}
936     }
937 
938   /* If standard input is to be read from the stdin of uux, we read it
939      here into a temporary file and send it to the execute system.  */
940 
941   if (fread_stdin)
942     {
943       const char *zdata;
944       char abtname[CFILE_NAME_LEN];
945       char abdname[CFILE_NAME_LEN];
946       FILE *e;
947 
948       zdata = zsysdep_data_file_name (qxqtsys, bgrade, abtname, abdname,
949 				      (char *) NULL);
950       if (zdata == NULL)
951 	uxabort ();
952 
953       e = esysdep_fopen (zdata, FALSE, FALSE, TRUE);
954       if (e == NULL)
955 	uxabort ();
956 
957       eXclose = e;
958       uxrecord_file (xstrdup (zdata));
959 
960       uxcopy_stdin (e);
961 
962       eXclose = NULL;
963       if (fclose (e) != 0)
964 	ulog (LOG_FATAL, "fclose: %s", strerror (errno));
965 
966       if (fxqtlocal)
967 	uxadd_xqt_line ('I', abtname, (const char *) NULL);
968       else
969 	{
970 	  uxadd_xqt_line ('F', abdname, (const char *) NULL);
971 	  uxadd_xqt_line ('I', abdname, (const char *) NULL);
972 	  uxadd_send_file (abtname, abdname, "C", abtname);
973 	}
974     }
975 
976   /* Here all the arguments have been determined, so the command can
977      be written out.  If any of the arguments contain shell
978      metacharacters, we request remote execution with /bin/sh (this is
979      the 'e' command in the execute file).  The default is assumed to
980      be remote execution with execve.  */
981 
982   fprintf (eXxqt_file, "C %s", zcmd);
983 
984   fneedshell = FALSE;
985 
986   if (zcmd[strcspn (zcmd, ZSHELLCHARS)] != '\0')
987     fneedshell = TRUE;
988 
989   for (i = 0; i < cargs; i++)
990     {
991       if (pzargs[i] != NULL)
992 	{
993 	  fprintf (eXxqt_file, " %s", pzargs[i]);
994 	  if (pzargs[i][strcspn (pzargs[i], ZSHELLCHARS)] != '\0')
995 	    fneedshell = TRUE;
996 	}
997     }
998 
999   fprintf (eXxqt_file, "\n");
1000 
1001   /* Write out all the other miscellaneous junk.  */
1002 
1003   if (fno_ack)
1004     uxadd_xqt_line ('N', (const char *) NULL, (const char *) NULL);
1005 
1006   if (ferror_ack)
1007     uxadd_xqt_line ('Z', (const char *) NULL, (const char *) NULL);
1008 
1009   if (zrequestor != NULL)
1010     uxadd_xqt_line ('R', zrequestor, (const char *) NULL);
1011 
1012   if (fretstdin)
1013     uxadd_xqt_line ('B', (const char *) NULL, (const char *) NULL);
1014 
1015   if (zstatus_file != NULL)
1016     uxadd_xqt_line ('M', zstatus_file, (const char *) NULL);
1017 
1018   if (fneedshell)
1019     uxadd_xqt_line ('e', (const char *) NULL, (const char *) NULL);
1020 
1021   if (fclose (eXxqt_file) != 0)
1022     ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1023   eXxqt_file = NULL;
1024 
1025   /* If the execution is to occur on another system, we must now
1026      arrange to copy the execute file to this system.  */
1027 
1028   if (! fxqtlocal)
1029     uxadd_send_file (abxqt_tname, abxqt_xname, "C", abxqt_tname);
1030 
1031   /* If we got a signal, get out before spooling anything.  */
1032 
1033   if (FGOT_SIGNAL ())
1034     uxabort ();
1035 
1036   /* From here on in, it's too late.  We don't call uxabort.  */
1037 
1038   if (cXcmds > 0)
1039     {
1040       const char *zjobid;
1041 
1042       zjobid = zsysdep_spool_commands (qxqtsys, bgrade, cXcmds, pasXcmds);
1043       if (zjobid == NULL)
1044 	{
1045 	  ulog_close ();
1046 	  usysdep_exit (FALSE);
1047 	}
1048 
1049       if (fjobid)
1050 	printf ("%s\n", zjobid);
1051 
1052       if (fcall_any)
1053 	zcall_system = NULL;
1054       else
1055 	{
1056 	  fcall_any = TRUE;
1057 	  zcall_system = qxqtsys->zname;
1058 	}
1059     }
1060 
1061   /* If all that worked, make a log file entry.  All log file reports
1062      up to this point went to stderr.  */
1063 
1064   ulog_to_file (TRUE);
1065   ulog_system (qxqtsys->zname);
1066   ulog_user (zuser);
1067 
1068   clen = strlen (zcmd) + 2;
1069   for (i = 0; i < cargs; i++)
1070     if (pzargs[i] != NULL)
1071       clen += strlen (pzargs[i]) + 1;
1072 
1073   zprint = (char *) alloca (clen);
1074   strcpy (zprint, zcmd);
1075   strcat (zprint, " ");
1076   for (i = 0; i < cargs; i++)
1077     {
1078       if (pzargs[i] != NULL)
1079 	{
1080 	  strcat (zprint, pzargs[i]);
1081 	  strcat (zprint, " ");
1082 	}
1083     }
1084   zprint[strlen (zprint) - 1] = '\0';
1085 
1086   ulog (LOG_NORMAL, "Queuing %s", zprint);
1087 
1088   ulog_close ();
1089 
1090   if (! fuucico)
1091     fexit = TRUE;
1092   else
1093     {
1094       if (zcall_system != NULL)
1095 	fexit = fsysdep_run (TRUE, "uucico", "-s", zcall_system);
1096       else if (fcall_any)
1097 	fexit = fsysdep_run (TRUE, "uucico", "-r1", (const char *) NULL);
1098       else
1099 	fexit = TRUE;
1100     }
1101 
1102   usysdep_exit (fexit);
1103 
1104   /* Avoid error about not returning a value.  */
1105   return 0;
1106 }
1107 
1108 /* Report command usage.  */
1109 
1110 static void
uxusage()1111 uxusage ()
1112 {
1113   fprintf (stderr,
1114 	   "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n",
1115 	   abVersion);
1116   fprintf (stderr,
1117 	   "Usage: uux [options] [-] command\n");
1118   fprintf (stderr,
1119 	   " -,-p: Read standard input for standard input of command\n");
1120   fprintf (stderr,
1121 	   " -c: Do not copy local files to spool directory (default)\n");
1122   fprintf (stderr,
1123 	   " -C: Copy local files to spool directory\n");
1124   fprintf (stderr,
1125 	   " -l: link local files to spool directory\n");
1126   fprintf (stderr,
1127 	   " -g grade: Set job grade (must be alphabetic)\n");
1128   fprintf (stderr,
1129 	   " -n: Do not report completion status\n");
1130   fprintf (stderr,
1131 	   " -z: Report completion status only on error\n");
1132   fprintf (stderr,
1133 	   " -r: Do not start uucico daemon\n");
1134   fprintf (stderr,
1135 	   " -a address: Address to mail status report to\n");
1136   fprintf (stderr,
1137 	   " -b: Return standard input with status report\n");
1138   fprintf (stderr,
1139 	   " -s file: Report completion status to file\n");
1140   fprintf (stderr,
1141 	   " -j: Report job id\n");
1142   fprintf (stderr,
1143 	   " -x debug: Set debugging level\n");
1144 #if HAVE_TAYLOR_CONFIG
1145   fprintf (stderr,
1146 	   " -I file: Set configuration file to use (default %s%s)\n",
1147 	   NEWCONFIGLIB, CONFIGFILE);
1148 #endif /* HAVE_TAYLOR_CONFIG */
1149   exit (EXIT_FAILURE);
1150 }
1151 
1152 /* Add a line to the execute file.  */
1153 
1154 static void
uxadd_xqt_line(bchar,z1,z2)1155 uxadd_xqt_line (bchar, z1, z2)
1156      int bchar;
1157      const char *z1;
1158      const char *z2;
1159 {
1160   if (z1 == NULL)
1161     fprintf (eXxqt_file, "%c\n", bchar);
1162   else if (z2 == NULL)
1163     fprintf (eXxqt_file, "%c %s\n", bchar, z1);
1164   else
1165     fprintf (eXxqt_file, "%c %s %s\n", bchar, z1, z2);
1166 }
1167 
1168 /* Add a file to be sent to the execute system.  */
1169 
1170 static void
uxadd_send_file(zfrom,zto,zoptions,ztemp)1171 uxadd_send_file (zfrom, zto, zoptions, ztemp)
1172      const char *zfrom;
1173      const char *zto;
1174      const char *zoptions;
1175      const char *ztemp;
1176 {
1177   struct scmd s;
1178 
1179   s.bcmd = 'S';
1180   s.pseq = NULL;
1181   s.zfrom = xstrdup (zfrom);
1182   s.zto = xstrdup (zto);
1183   s.zuser = zsysdep_login_name ();
1184   s.zoptions = xstrdup (zoptions);
1185   s.ztemp = xstrdup (ztemp);
1186   s.imode = 0666;
1187   s.znotify = "";
1188   s.cbytes = -1;
1189 
1190   ++cXcmds;
1191   pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds,
1192 				       cXcmds * sizeof (struct scmd));
1193   pasXcmds[cXcmds - 1] = s;
1194 }
1195 
1196 /* Copy stdin to a file.  This is a separate function because it may
1197    call setjump.  */
1198 
1199 static void
uxcopy_stdin(e)1200 uxcopy_stdin (e)
1201      FILE *e;
1202 {
1203   CATCH_PROTECT int cread;
1204   char ab[1024];
1205 
1206   do
1207     {
1208       int cwrite;
1209 
1210       if (fsysdep_catch ())
1211 	{
1212 	  usysdep_start_catch ();
1213 	  if (FGOT_SIGNAL ())
1214 	    uxabort ();
1215 
1216 	  /* There's an unimportant race here.  If the user hits ^C
1217 	     between the FGOT_SIGNAL we just did and the time we enter
1218 	     fread, we won't know about the signal (unless we're doing
1219 	     a longjmp, but we normally aren't).  It's not a big
1220 	     problem, because the user can just hit ^C again.  */
1221 
1222 	  cread = fread (ab, sizeof (char), sizeof ab, stdin);
1223 	}
1224 
1225       usysdep_end_catch ();
1226 
1227       if (FGOT_SIGNAL ())
1228 	uxabort ();
1229 
1230       if (cread > 0)
1231 	{
1232 	  cwrite = fwrite (ab, sizeof (char), cread, e);
1233 	  if (cwrite != cread)
1234 	    {
1235 	      if (cwrite == EOF)
1236 		ulog (LOG_FATAL, "fwrite: %s", strerror (errno));
1237 	      else
1238 		ulog (LOG_FATAL, "fwrite: Wrote %d when attempted %d",
1239 		      cwrite, cread);
1240 	    }
1241 	}
1242     }
1243   while (cread == sizeof ab);
1244 }
1245 
1246 /* Keep track of all files we have created so that we can delete them
1247    if we get a signal.  The argument will be on the heap.  */
1248 
1249 static int cxfiles;
1250 static const char **pxaz;
1251 
1252 static void
uxrecord_file(zfile)1253 uxrecord_file (zfile)
1254      const char *zfile;
1255 {
1256   pxaz = (const char **) xrealloc ((pointer) pxaz,
1257 				   (cxfiles + 1) * sizeof (const char *));
1258   pxaz[cxfiles] = zfile;
1259   ++cxfiles;
1260 }
1261 
1262 /* Delete all the files we have recorded and exit.  */
1263 
1264 static void
uxabort()1265 uxabort ()
1266 {
1267   int i;
1268 
1269   if (eXxqt_file != NULL)
1270     (void) fclose (eXxqt_file);
1271   if (eXclose != NULL)
1272     (void) fclose (eXclose);
1273   for (i = 0; i < cxfiles; i++)
1274     (void) remove (pxaz[i]);
1275   ulog_close ();
1276   usysdep_exit (FALSE);
1277 }
1278