This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / gnu / libexec / uucp / uux / uux.c
CommitLineData
78ed81a3 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 Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
24 */
25
26#include "uucp.h"
27
28#if USE_RCS_ID
29const char uux_rcsid[] = "$Id: uux.c,v 1.1 1993/08/04 19:37:14 jtc Exp $";
30#endif
31
32#include "uudefs.h"
33#include "uuconf.h"
34#include "system.h"
35#include "sysdep.h"
36#include "getopt.h"
37
38#include <ctype.h>
39#include <errno.h>
40\f
41/* These character lists should, perhaps, be in sysdep.h. */
42
43/* This is the list of shell metacharacters that we check for. If one
44 of these is present, we request uuxqt to execute the command with
45 /bin/sh. Otherwise we let it execute using execve. */
46
47#define ZSHELLCHARS "\"'`*?[;&()|<>\\$"
48
49/* This is the list of word separators. We break filename arguments
50 at these characters. */
51#define ZSHELLSEPS ";&*|<> \t"
52
53/* This is the list of word separators without the redirection
54 operators. */
55#define ZSHELLNONREDIRSEPS ";&*| \t"
56\f
57/* The program name. */
58char abProgram[] = "uux";
59
60/* The name of the execute file. */
61const char *zXxqt_name;
62
63/* The execute file we are creating. */
64static FILE *eXxqt_file;
65
66/* A list of commands to be spooled. */
67static struct scmd *pasXcmds;
68static int cXcmds;
69
70/* A file to close if we're forced to exit. */
71static FILE *eXclose;
72\f
73/* Local functions. */
74static void uxusage P((void));
75static void uxadd_xqt_line P((int bchar, const char *z1, const char *z2));
76static void uxadd_send_file P((const char *zfrom, const char *zto,
77 const char *zoptions, const char *ztemp,
78 const char *zforward,
79 const struct uuconf_system *qxqtsys,
80 const char *zxqtloc,
81 int bgrade));
82static void uxcopy_stdin P((FILE *e));
83static void uxrecord_file P((const char *zfile));
84static void uxabort P((void));
85\f
86/* Long getopt options. */
87static const struct option asXlongopts[] = { { NULL, 0, NULL, 0 } };
88
89/* The main routine. */
90
91int
92main (argc, argv)
93 int argc;
94 char **argv;
95{
96 /* -a: requestor address for status reports. */
97 const char *zrequestor = NULL;
98 /* -b: if true, return standard input on error. */
99 boolean fretstdin = FALSE;
100 /* -c,-C: if true, copy to spool directory. */
101 boolean fcopy = FALSE;
102 /* -c: set if -c appears explicitly; if it and -l appear, then if the
103 link fails we don't copy the file. */
104 boolean fdontcopy = FALSE;
105 /* -I: configuration file name. */
106 const char *zconfig = NULL;
107 /* -j: output job id. */
108 boolean fjobid = FALSE;
109 /* -g: job grade. */
110 char bgrade = BDEFAULT_UUX_GRADE;
111 /* -l: link file to spool directory. */
112 boolean flink = FALSE;
113 /* -n: do not notify upon command completion. */
114 boolean fno_ack = FALSE;
115 /* -p: read standard input for command standard input. */
116 boolean fread_stdin = FALSE;
117 /* -r: do not start uucico when finished. */
118 boolean fuucico = TRUE;
119 /* -s: report status to named file. */
120 const char *zstatus_file = NULL;
121 /* -W: only expand local file names. */
122 boolean fexpand = TRUE;
123 /* -z: report status only on error. */
124 boolean ferror_ack = FALSE;
125 int iopt;
126 pointer puuconf;
127 int iuuconf;
128 const char *zlocalname;
129 const char *zxqtloc;
130 int i;
131 size_t clen;
132 char *zargs;
133 char *zarg;
134 char *zcmd;
135 const char *zsys;
136 char *zexclam;
137 boolean fgetcwd;
138 const char *zuser;
139 struct uuconf_system sxqtsys;
140 boolean fxqtlocal;
141 char *zforward;
142 char **pzargs;
143 int calloc_args;
144 int cargs;
145 char abxqt_tname[CFILE_NAME_LEN];
146 char abxqt_xname[CFILE_NAME_LEN];
147 const char *zinput_from;
148 const char *zinput_to;
149 const char *zinput_temp;
150 boolean finputcopied;
151 char *zcall_system;
152 boolean fcall_any;
153 struct uuconf_system slocalsys;
154 boolean fneedshell;
155 char *zfullcmd;
156 boolean fexit;
157
158 /* We need to be able to read a single - as an option, which getopt
159 won't do. So that we can still use getopt, we run through the
160 options looking for an option "-"; if we find one we change it to
161 "-p", which is equivalent to "-". */
162 for (i = 1; i < argc; i++)
163 {
164 if (argv[i][0] != '-')
165 break;
166 if (argv[i][1] == '\0')
167 argv[i] = zbufcpy ("-p");
168 else
169 {
170 const char *z;
171
172 for (z = argv[i] + 1; *z != '\0'; z++)
173 {
174 /* If the option takes an argument, and the argument is
175 not appended, then skip the next argument. */
176 if (*z == 'a' || *z == 'g' || *z == 'I'
177 || *z == 's' || *z == 'x')
178 {
179 if (z[1] == '\0')
180 i++;
181 break;
182 }
183 }
184 }
185 }
186
187 /* The leading + in the getopt string means to stop processing
188 options as soon as a non-option argument is seen. */
189 while ((iopt = getopt_long (argc, argv, "+a:bcCg:I:jlnprs:Wx:z",
190 asXlongopts, (int *) NULL)) != EOF)
191 {
192 switch (iopt)
193 {
194 case 'a':
195 /* Set requestor name: mail address to which status reports
196 should be sent. */
197 zrequestor = optarg;
198 break;
199
200 case 'b':
201 /* Return standard input on error. */
202 fretstdin = TRUE;
203 break;
204
205 case 'c':
206 /* Do not copy local files to spool directory. */
207 fcopy = FALSE;
208 fdontcopy = TRUE;
209 break;
210
211 case 'C':
212 /* Copy local files to spool directory. */
213 fcopy = TRUE;
214 break;
215
216 case 'I':
217 /* Configuration file name. */
218 if (fsysdep_other_config (optarg))
219 zconfig = optarg;
220 break;
221
222 case 'j':
223 /* Output jobid. */
224 fjobid = TRUE;
225 break;
226
227 case 'g':
228 /* Set job grade. */
229 bgrade = optarg[0];
230 break;
231
232 case 'l':
233 /* Link file to spool directory. */
234 flink = TRUE;
235 break;
236
237 case 'n':
238 /* Do not notify upon command completion. */
239 fno_ack = TRUE;
240 break;
241
242 case 'p':
243 /* Read standard input for command standard input. */
244 fread_stdin = TRUE;
245 break;
246
247 case 'r':
248 /* Do not start uucico when finished. */
249 fuucico = FALSE;
250 break;
251
252 case 's':
253 /* Report status to named file. */
254 zstatus_file = optarg;
255 break;
256
257 case 'W':
258 /* Only expand local file names. */
259 fexpand = FALSE;
260 break;
261
262 case 'x':
263#if DEBUG > 1
264 /* Set debugging level. */
265 iDebug |= idebug_parse (optarg);
266#endif
267 break;
268
269 case 'z':
270 /* Report status only on error. */
271 ferror_ack = TRUE;
272 break;
273
274 case 0:
275 /* Long option found and flag set. */
276 break;
277
278 default:
279 uxusage ();
280 break;
281 }
282 }
283
284 if (! UUCONF_GRADE_LEGAL (bgrade))
285 {
286 ulog (LOG_ERROR, "Ignoring illegal grade");
287 bgrade = BDEFAULT_UUX_GRADE;
288 }
289
290 if (optind == argc)
291 uxusage ();
292
293 iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig);
294 if (iuuconf != UUCONF_SUCCESS)
295 ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
296
297#if DEBUG > 1
298 {
299 const char *zdebug;
300
301 iuuconf = uuconf_debuglevel (puuconf, &zdebug);
302 if (iuuconf != UUCONF_SUCCESS)
303 ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
304 if (zdebug != NULL)
305 iDebug |= idebug_parse (zdebug);
306 }
307#endif
308
309 /* The command and files arguments could be quoted in any number of
310 ways, so we split them apart ourselves. We do this before
311 calling usysdep_initialize because we want to set fgetcwd
312 correctly. */
313 clen = 1;
314 for (i = optind; i < argc; i++)
315 clen += strlen (argv[i]) + 1;
316
317 zargs = zbufalc (clen);
318 *zargs = '\0';
319 for (i = optind; i < argc; i++)
320 {
321 strcat (zargs, argv[i]);
322 strcat (zargs, " ");
323 }
324
325 /* The first argument is the command to execute. */
326 clen = strcspn (zargs, ZSHELLSEPS);
327 zcmd = zbufalc (clen + 1);
328 strncpy (zcmd, zargs, clen);
329 zcmd[clen] = '\0';
330 zargs += clen;
331
332 /* Split the arguments out into an array. We break the arguments
333 into alternating sequences of characters not in ZSHELLSEPS
334 and characters in ZSHELLSEPS. We remove whitespace. We
335 separate the redirection characters '>' and '<' into their
336 own arguments to make them easier to process below. */
337 calloc_args = 10;
338 pzargs = (char **) xmalloc (calloc_args * sizeof (char *));
339 cargs = 0;
340
341 for (zarg = strtok (zargs, " \t");
342 zarg != NULL;
343 zarg = strtok ((char *) NULL, " \t"))
344 {
345 while (*zarg != '\0')
346 {
347 if (cargs + 1 >= calloc_args)
348 {
349 calloc_args += 10;
350 pzargs = (char **) xrealloc ((pointer) pzargs,
351 calloc_args * sizeof (char *));
352 }
353
354 clen = strcspn (zarg, ZSHELLSEPS);
355 if (clen > 0)
356 {
357 pzargs[cargs] = zbufalc (clen + 1);
358 memcpy (pzargs[cargs], zarg, clen);
359 pzargs[cargs][clen] = '\0';
360 ++cargs;
361 zarg += clen;
362 }
363
364 /* We deliberately separate '>' and '<' out. */
365 if (*zarg != '\0')
366 {
367 clen = strspn (zarg, ZSHELLNONREDIRSEPS);
368 if (clen == 0)
369 clen = 1;
370 pzargs[cargs] = zbufalc (clen + 1);
371 memcpy (pzargs[cargs], zarg, clen);
372 pzargs[cargs][clen] = '\0';
373 ++cargs;
374 zarg += clen;
375 }
376 }
377 }
378
379 /* Now look through the arguments to see if we are going to need the
380 current working directory. We don't try to make a precise
381 determination, just a conservative one. The basic idea is that
382 we don't want to get the cwd for 'foo!rmail - user' (note that we
383 don't examine the command itself). */
384 fgetcwd = FALSE;
385 for (i = 0; i < cargs; i++)
386 {
387 if (pzargs[i][0] == '(')
388 continue;
389 zexclam = strrchr (pzargs[i], '!');
390 if (zexclam != NULL && fsysdep_needs_cwd (zexclam + 1))
391 {
392 fgetcwd = TRUE;
393 break;
394 }
395 if ((pzargs[i][0] == '<' || pzargs[i][0] == '>')
396 && i + 1 < cargs
397 && strchr (pzargs[i + 1], '!') == NULL
398 && fsysdep_needs_cwd (pzargs[i + 1]))
399 {
400 fgetcwd = TRUE;
401 break;
402 }
403 }
404
405#ifdef SIGINT
406 usysdep_signal (SIGINT);
407#endif
408#ifdef SIGHUP
409 usysdep_signal (SIGHUP);
410#endif
411#ifdef SIGQUIT
412 usysdep_signal (SIGQUIT);
413#endif
414#ifdef SIGTERM
415 usysdep_signal (SIGTERM);
416#endif
417#ifdef SIGPIPE
418 usysdep_signal (SIGPIPE);
419#endif
420
421 usysdep_initialize (puuconf, INIT_SUID | (fgetcwd ? INIT_GETCWD : 0));
422
423 ulog_fatal_fn (uxabort);
424
425 zuser = zsysdep_login_name ();
426
427 /* Get the local system name. */
428 iuuconf = uuconf_localname (puuconf, &zlocalname);
429 if (iuuconf == UUCONF_NOT_FOUND)
430 {
431 zlocalname = zsysdep_localname ();
432 if (zlocalname == NULL)
433 exit (EXIT_FAILURE);
434 }
435 else if (iuuconf != UUCONF_SUCCESS)
436 ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
437
438 /* Get the local system information. */
439 iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys);
440 if (iuuconf != UUCONF_SUCCESS)
441 {
442 if (iuuconf != UUCONF_NOT_FOUND)
443 ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
444 iuuconf = uuconf_system_local (puuconf, &slocalsys);
445 if (iuuconf != UUCONF_SUCCESS)
446 ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
447 }
448
449 /* Figure out which system the command is to be executed on. Some
450 mailers apparently pass local!rmail, so we must explicitly check
451 for that. */
452 zexclam = strchr (zcmd, '!');
453 while (zexclam != NULL)
454 {
455 *zexclam = '\0';
456 if (strcmp (zcmd, zlocalname) == 0)
457 ;
458 else if (slocalsys.uuconf_pzalias == NULL)
459 break;
460 else
461 {
462 char **pzal;
463
464 for (pzal = slocalsys.uuconf_pzalias; *pzal != NULL; pzal++)
465 if (strcmp (zcmd, *pzal) == 0)
466 break;
467 if (*pzal == NULL)
468 break;
469 }
470 zcmd = zexclam + 1;
471 zexclam = strchr (zcmd, '!');
472 }
473 if (zexclam == NULL)
474 {
475 zsys = zlocalname;
476 fxqtlocal = TRUE;
477 zforward = NULL;
478 }
479 else
480 {
481 zsys = zcmd;
482 zcmd = zexclam + 1;
483 fxqtlocal = FALSE;
484
485 /* See if we must forward this command through other systems
486 (e.g. uux a!b!cmd). */
487 zexclam = strrchr (zcmd, '!');
488 if (zexclam == NULL)
489 zforward = NULL;
490 else
491 {
492 clen = zexclam - zcmd;
493 zforward = zbufalc (clen);
494 memcpy (zforward, zcmd, clen);
495 zforward[clen] = '\0';
496 zcmd = zexclam + 1;
497 }
498 }
499
500 if (fxqtlocal)
501 sxqtsys = slocalsys;
502 else
503 {
504 iuuconf = uuconf_system_info (puuconf, zsys, &sxqtsys);
505 if (iuuconf != UUCONF_SUCCESS)
506 {
507 if (iuuconf != UUCONF_NOT_FOUND)
508 ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
509 if (! funknown_system (puuconf, zsys, &sxqtsys))
510 ulog (LOG_FATAL, "%s: System not found", zsys);
511 }
512 }
513
514 /* Get the local name the remote system know us as. */
515 zxqtloc = sxqtsys.uuconf_zlocalname;
516 if (zxqtloc == NULL)
517 zxqtloc = zlocalname;
518
519 /* We can send this as an E command if the execution is on a
520 different, directly connected, system and the only file used is
521 the standard input and comes from this system. This is true of
522 the common cases of rmail and rnews. We get an execute file name
523 here in case we need it. */
524 if (fxqtlocal)
525 zXxqt_name = zsysdep_xqt_file_name ();
526 else
527 zXxqt_name = zsysdep_data_file_name (&sxqtsys, zxqtloc, bgrade, TRUE,
528 abxqt_tname, (char *) NULL,
529 abxqt_xname);
530 if (zXxqt_name == NULL)
531 uxabort ();
532
533 uxrecord_file (zXxqt_name);
534
535 /* Look through the arguments. Any argument containing an
536 exclamation point character is interpreted as a file name, and is
537 sent to the appropriate system. */
538 zinput_from = NULL;
539 zinput_to = NULL;
540 zinput_temp = NULL;
541 finputcopied = FALSE;
542 zcall_system = NULL;
543 fcall_any = FALSE;
544
545 for (i = 0; i < cargs; i++)
546 {
547 const char *zsystem;
548 char *zfile;
549 char *zforw;
550 boolean finput, foutput;
551 boolean flocal, fonxqt;
552
553 /* Check for a parenthesized argument; remove the parentheses
554 and otherwise ignore it (this is how an exclamation point is
555 quoted). */
556 if (pzargs[i][0] == '(')
557 {
558 clen = strlen (pzargs[i]);
559 if (pzargs[i][clen - 1] != ')')
560 ulog (LOG_ERROR, "Mismatched parentheses");
561 else
562 pzargs[i][clen - 1] = '\0';
563 ++pzargs[i];
564 continue;
565 }
566
567 /* Check whether we are doing a redirection. */
568 finput = FALSE;
569 foutput = FALSE;
570 if (i + 1 < cargs)
571 {
572 if (pzargs[i][0] == '<')
573 finput = TRUE;
574 else if (pzargs[i][0] == '>')
575 foutput = TRUE;
576 if (finput || foutput)
577 {
578 pzargs[i] = NULL;
579 i++;
580 }
581 }
582
583 zexclam = strchr (pzargs[i], '!');
584
585 /* If there is no exclamation point and no redirection, this
586 argument is left untouched. */
587 if (zexclam == NULL && ! finput && ! foutput)
588 continue;
589
590 /* Get the system name and file name for this file. */
591 if (zexclam == NULL)
592 {
593 zsystem = zlocalname;
594 zfile = pzargs[i];
595 flocal = TRUE;
596 zforw = NULL;
597 }
598 else
599 {
600 *zexclam = '\0';
601 zsystem = pzargs[i];
602 if (*zsystem != '\0')
603 flocal = FALSE;
604 else
605 {
606 zsystem = zlocalname;
607 flocal = TRUE;
608 }
609 zfile = zexclam + 1;
610 zexclam = strrchr (zfile, '!');
611 if (zexclam == NULL)
612 zforw = NULL;
613 else
614 {
615 if (flocal)
616 ulog (LOG_FATAL, "!%s: Can't figure out where to get file",
617 zfile);
618 *zexclam = '\0';
619 zforw = zfile;
620 zfile = zexclam + 1;
621 }
622 }
623
624 /* Check if the file is already on the execution system. */
625 if (flocal)
626 fonxqt = fxqtlocal;
627 else if (fxqtlocal)
628 fonxqt = FALSE;
629 else if (zforward == NULL ? zforw != NULL : zforw == NULL)
630 fonxqt = FALSE;
631 else if (zforward != NULL
632 && zforw != NULL
633 && strcmp (zforward, zforw) != 0)
634 fonxqt = FALSE;
635 else if (strcmp (zsystem, sxqtsys.uuconf_zname) == 0)
636 fonxqt = TRUE;
637 else if (sxqtsys.uuconf_pzalias == NULL)
638 fonxqt = FALSE;
639 else
640 {
641 char **pzal;
642
643 fonxqt = FALSE;
644 for (pzal = sxqtsys.uuconf_pzalias; *pzal != NULL; pzal++)
645 {
646 if (strcmp (zsystem, *pzal) == 0)
647 {
648 fonxqt = TRUE;
649 break;
650 }
651 }
652 }
653
654 /* Turn the file into an absolute path. */
655 if (flocal)
656 zfile = zsysdep_local_file_cwd (zfile, sxqtsys.uuconf_zpubdir);
657 else if (fexpand)
658 zfile = zsysdep_add_cwd (zfile);
659 if (zfile == NULL)
660 uxabort ();
661
662 /* Check for output redirection. */
663 if (foutput)
664 {
665 if (flocal)
666 {
667 if (! fin_directory_list (zfile,
668 sxqtsys.uuconf_pzremote_receive,
669 sxqtsys.uuconf_zpubdir, TRUE,
670 FALSE, (const char *) NULL))
671 ulog (LOG_FATAL, "Not permitted to create %s", zfile);
672 }
673
674 /* There are various cases of output redirection.
675
676 uux cmd >out: The command is executed on the local
677 system, and the output file is placed on the local
678 system (fonxqt is TRUE).
679
680 uux cmd >a!out: The command is executed on the local
681 system, and the output file is sent to a.
682
683 uux a!cmd >out: The command is executed on a, and the
684 output file is returned to the local system (flocal
685 is TRUE).
686
687 uux a!cmd >a!out: The command is executed on a, and the
688 output file is left on a (fonxqt is TRUE).
689
690 uux a!cmd >b!out: The command is executed on a, and the
691 output file is sent to b; traditionally, I believe
692 that b is relative to a, rather than to the local
693 system. However, this essentially contradicts the
694 previous two cases, in which the output file is
695 relative to the local system.
696
697 Now, the cases that we don't handle.
698
699 uux cmd >a!b!out: The command is executed on the local
700 system, and the output file is sent to b via a. This
701 requires the local uuxqt to support forwarding of the
702 output file.
703
704 uux a!b!cmd >out: The command is executed on b, which is
705 reached via a. Probably the output file is intended
706 for the local system, in which case the uuxqt on b
707 must support forwarding of the output file.
708
709 uux a!b!cmd >c!out: Is c relative to b or to the local
710 system? If it's relative to b this is easy to
711 handle. Otherwise, we must arrange for the file to
712 be sent back to the local system and for the local
713 system to send it on to c.
714
715 There are many variations of the last case. It's not at
716 all clear to me how they should be handled. */
717 if (zforward != NULL || zforw != NULL)
718 ulog (LOG_FATAL, "May not forward standard output");
719
720 if (fonxqt)
721 uxadd_xqt_line ('O', zfile, (const char *) NULL);
722 else if (flocal)
723 uxadd_xqt_line ('O', zfile, zxqtloc);
724 else
725 uxadd_xqt_line ('O', zfile, zsystem);
726 pzargs[i] = NULL;
727 continue;
728 }
729
730 if (finput)
731 {
732 if (fread_stdin)
733 ulog (LOG_FATAL, "Standard input specified twice");
734 pzargs[i] = NULL;
735 }
736
737 if (flocal)
738 {
739 char *zuse;
740 char *zdata;
741 char abtname[CFILE_NAME_LEN];
742 char abdname[CFILE_NAME_LEN];
743
744 /* It's a local file. If requested by -C, copy the file to
745 the spool directory. If requested by -l, link the file
746 to the spool directory; if the link fails, we copy the
747 file, unless -c was explictly used. If the execution is
748 occurring on the local system, we force the copy as well,
749 because otherwise we would have to have some way to tell
750 uuxqt not to move the file. If the file is being shipped
751 to another system, we must set up a transfer request.
752 First make sure the user has legitimate access, since we
753 are running setuid. */
754 if (! fsysdep_access (zfile))
755 uxabort ();
756
757 zdata = zsysdep_data_file_name (&sxqtsys, zxqtloc, bgrade, FALSE,
758 abtname, abdname, (char *) NULL);
759 if (zdata == NULL)
760 uxabort ();
761
762 if (fcopy || flink || fxqtlocal)
763 {
764 boolean fdid;
765
766 uxrecord_file (zdata);
767
768 fdid = FALSE;
769 if (flink)
770 {
771 boolean fworked;
772
773 if (! fsysdep_link (zfile, zdata, &fworked))
774 uxabort ();
775
776 if (fworked)
777 fdid = TRUE;
778 else if (fdontcopy)
779 ulog (LOG_FATAL, "%s: Can't link to spool directory",
780 zfile);
781 }
782
783 if (! fdid)
784 {
785 openfile_t efile;
786
787 efile = esysdep_user_fopen (zfile, TRUE, TRUE);
788 if (! ffileisopen (efile))
789 uxabort ();
790 if (! fcopy_open_file (efile, zdata, FALSE, TRUE))
791 uxabort ();
792 (void) ffileclose (efile);
793 }
794
795 zuse = abtname;
796 }
797 else
798 {
799 /* We don't actually use the spool file name, but we
800 need a name to use as the destination. */
801 ubuffree (zdata);
802 /* Make sure the daemon can access the file. */
803 if (! fsysdep_daemon_access (zfile))
804 uxabort ();
805 if (! fin_directory_list (zfile, sxqtsys.uuconf_pzlocal_send,
806 sxqtsys.uuconf_zpubdir, TRUE,
807 TRUE, zuser))
808 ulog (LOG_FATAL, "Not permitted to send from %s",
809 zfile);
810
811 zuse = zfile;
812 }
813
814 if (fxqtlocal)
815 {
816 if (finput)
817 uxadd_xqt_line ('I', zuse, (char *) NULL);
818 else
819 pzargs[i] = zuse;
820 }
821 else
822 {
823 finputcopied = fcopy || flink;
824
825 if (finput)
826 {
827 zinput_from = zuse;
828 zinput_to = zbufcpy (abdname);
829 zinput_temp = zbufcpy (abtname);
830 }
831 else
832 {
833 char *zbase;
834
835 uxadd_send_file (zuse, abdname,
836 finputcopied ? "C" : "c",
837 abtname, zforward, &sxqtsys,
838 zxqtloc, bgrade);
839 zbase = zsysdep_base_name (zfile);
840 if (zbase == NULL)
841 uxabort ();
842 uxadd_xqt_line ('F', abdname, zbase);
843 pzargs[i] = zbase;
844 }
845 }
846 }
847 else if (fonxqt)
848 {
849 /* The file is already on the system where the command is to
850 be executed. */
851 if (finput)
852 uxadd_xqt_line ('I', zfile, (const char *) NULL);
853 else
854 pzargs[i] = zfile;
855 }
856 else
857 {
858 struct uuconf_system sfromsys;
859 char abtname[CFILE_NAME_LEN];
860 struct scmd s;
861 char *zjobid;
862
863 /* We need to request a remote file. */
864 iuuconf = uuconf_system_info (puuconf, zsystem, &sfromsys);
865 if (iuuconf != UUCONF_SUCCESS)
866 {
867 if (iuuconf != UUCONF_NOT_FOUND)
868 ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
869 if (! funknown_system (puuconf, zsystem, &sfromsys))
870 ulog (LOG_FATAL, "%s: System not found", zsystem);
871 }
872
873 if (fonxqt)
874 {
875 /* The file is already on the system where the command is to
876 be executed. */
877 if (finput)
878 uxadd_xqt_line ('I', zfile, (const char *) NULL);
879 else
880 pzargs[i] = zfile;
881 }
882 else
883 {
884 char *zdata;
885
886 if (! sfromsys.uuconf_fcall_transfer
887 && ! sfromsys.uuconf_fcalled_transfer)
888 ulog (LOG_FATAL,
889 "Not permitted to transfer files to or from %s",
890 sfromsys.uuconf_zname);
891
892 if (zforw != NULL)
893 {
894 /* This is ``uux cmd a!b!file''. To make this work,
895 we would have to be able to set up a request to a
896 to fetch file from b and send it to us. But it
897 turns out that that will not work, because when a
898 sends us the file we will put it in a's spool
899 directory, not the local system spool directory.
900 So we won't have any way to find it. This is not
901 a conceptual problem, and it could doubtless be
902 solved. Please feel free to solve it and send me
903 the solution. */
904 ulog (LOG_FATAL, "File forwarding not supported");
905 }
906
907 /* We must request the file from the remote system to
908 this one. */
909 zdata = zsysdep_data_file_name (&slocalsys, zxqtloc, bgrade,
910 FALSE, abtname, (char *) NULL,
911 (char *) NULL);
912 if (zdata == NULL)
913 uxabort ();
914 ubuffree (zdata);
915
916 /* Request the file. The special option '9' is a signal
917 to uucico that it's OK to receive a file into the
918 spool directory; normally such requests are rejected.
919 This privilege is easy to abuse. */
920 s.bcmd = 'R';
921 s.pseq = NULL;
922 s.zfrom = zfile;
923 s.zto = zbufcpy (abtname);
924 s.zuser = zuser;
925 s.zoptions = "9";
926 s.ztemp = "";
927 s.imode = 0600;
928 s.znotify = "";
929 s.cbytes = -1;
930 s.zcmd = NULL;
931 s.ipos = 0;
932
933 zjobid = zsysdep_spool_commands (&sfromsys, bgrade, 1, &s);
934 if (zjobid == NULL)
935 uxabort ();
936
937 if (fjobid)
938 printf ("%s\n", zjobid);
939
940 ubuffree (zjobid);
941
942 if (fcall_any)
943 {
944 ubuffree (zcall_system);
945 zcall_system = NULL;
946 }
947 else
948 {
949 fcall_any = TRUE;
950 zcall_system = zbufcpy (sfromsys.uuconf_zname);
951 }
952
953 if (fxqtlocal)
954 {
955 /* Tell the command execution to wait until the file
956 has been received, and tell it the real file
957 name. */
958 if (finput)
959 {
960 uxadd_xqt_line ('F', abtname, (char *) NULL);
961 uxadd_xqt_line ('I', abtname, (char *) NULL);
962 }
963 else
964 {
965 char *zbase;
966
967 zbase = zsysdep_base_name (zfile);
968 if (zbase == NULL)
969 uxabort ();
970 uxadd_xqt_line ('F', abtname, zbase);
971 pzargs[i] = zbase;
972 }
973 }
974 else
975 {
976 char abxtname[CFILE_NAME_LEN];
977 char *zbase;
978 char *zxqt;
979 FILE *e;
980
981 /* Now we must arrange to forward the file on to the
982 execution system. We need to get a name to give
983 the file on the execution system (abxtname). */
984 zdata = zsysdep_data_file_name (&sxqtsys, zxqtloc,
985 bgrade, TRUE, abxtname,
986 (char *) NULL,
987 (char *) NULL);
988 if (zdata == NULL)
989 uxabort ();
990 ubuffree (zdata);
991
992 zbase = zsysdep_base_name (zfile);
993 if (zbase == NULL)
994 uxabort ();
995
996 zxqt = zsysdep_xqt_file_name ();
997 if (zxqt == NULL)
998 uxabort ();
999 e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE);
1000 if (e == NULL)
1001 uxabort ();
1002 uxrecord_file (zxqt);
1003
1004 fprintf (e, "U %s %s\n", zsysdep_login_name (),
1005 zlocalname);
1006 fprintf (e, "F %s %s\n", abtname, zbase);
1007 fprintf (e, "C uucp -C -W -d -g %c %s %s!", bgrade,
1008 zbase, sxqtsys.uuconf_zname);
1009 if (zforward != NULL)
1010 fprintf (e, "%s!", zforward);
1011 fprintf (e, "%s\n", abxtname);
1012
1013 if (fclose (e) != 0)
1014 ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1015
1016 if (finput)
1017 {
1018 uxadd_xqt_line ('F', abxtname, (char *) NULL);
1019 uxadd_xqt_line ('I', abxtname, (char *) NULL);
1020 ubuffree (zbase);
1021 }
1022 else
1023 {
1024 uxadd_xqt_line ('F', abxtname, zbase);
1025 pzargs[i] = zbase;
1026 }
1027 }
1028 }
1029
1030 (void) uuconf_system_free (puuconf, &sfromsys);
1031 }
1032 }
1033
1034 /* If standard input is to be read from the stdin of uux, we read it
1035 here into a temporary file and send it to the execute system. */
1036 if (fread_stdin)
1037 {
1038 char *zdata;
1039 char abtname[CFILE_NAME_LEN];
1040 char abdname[CFILE_NAME_LEN];
1041 FILE *e;
1042
1043 zdata = zsysdep_data_file_name (&sxqtsys, zxqtloc, bgrade, FALSE,
1044 abtname, abdname, (char *) NULL);
1045 if (zdata == NULL)
1046 uxabort ();
1047
1048 e = esysdep_fopen (zdata, FALSE, FALSE, TRUE);
1049 if (e == NULL)
1050 uxabort ();
1051
1052 eXclose = e;
1053 uxrecord_file (zdata);
1054
1055 uxcopy_stdin (e);
1056
1057 eXclose = NULL;
1058 if (fclose (e) != 0)
1059 ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1060
1061 if (fxqtlocal)
1062 uxadd_xqt_line ('I', abtname, (const char *) NULL);
1063 else
1064 {
1065 zinput_from = zbufcpy (abtname);
1066 zinput_to = zbufcpy (abdname);
1067 zinput_temp = zinput_from;
1068 finputcopied = TRUE;
1069 }
1070 }
1071
1072 /* If we are returning standard input, or we're putting the status
1073 in a file, we can't use an E command. */
1074 if (fretstdin)
1075 uxadd_xqt_line ('B', (const char *) NULL, (const char *) NULL);
1076
1077 if (zstatus_file != NULL)
1078 uxadd_xqt_line ('M', zstatus_file, (const char *) NULL);
1079
1080 /* Get the complete command line, and decide whether the command
1081 needs to be executed by the shell. */
1082 fneedshell = FALSE;
1083
1084 if (zcmd[strcspn (zcmd, ZSHELLCHARS)] != '\0')
1085 fneedshell = TRUE;
1086
1087 clen = strlen (zcmd) + 1;
1088 for (i = 0; i < cargs; i++)
1089 {
1090 if (pzargs[i] != NULL)
1091 {
1092 clen += strlen (pzargs[i]) + 1;
1093 if (pzargs[i][strcspn (pzargs[i], ZSHELLCHARS)] != '\0')
1094 fneedshell = TRUE;
1095 }
1096 }
1097
1098 zfullcmd = zbufalc (clen);
1099
1100 strcpy (zfullcmd, zcmd);
1101 for (i = 0; i < cargs; i++)
1102 {
1103 if (pzargs[i] != NULL)
1104 {
1105 strcat (zfullcmd, " ");
1106 strcat (zfullcmd, pzargs[i]);
1107 }
1108 }
1109
1110 /* If we haven't written anything to the execution file yet, and we
1111 have a standard input file, and we're not forwarding, then every
1112 other option can be handled in an E command. */
1113 if (eXxqt_file == NULL && zinput_from != NULL && zforward == NULL)
1114 {
1115 struct scmd s;
1116 char aboptions[10];
1117 char *zoptions;
1118
1119 /* Set up an E command. */
1120 s.bcmd = 'E';
1121 s.pseq = NULL;
1122 s.zuser = zuser;
1123 s.zfrom = zinput_from;
1124 s.zto = zinput_to;
1125 s.zoptions = aboptions;
1126 zoptions = aboptions;
1127 *zoptions++ = finputcopied ? 'C' : 'c';
1128 if (fno_ack)
1129 *zoptions++ = 'N';
1130 if (ferror_ack)
1131 *zoptions++ = 'Z';
1132 if (zrequestor != NULL)
1133 *zoptions++ = 'R';
1134 if (fneedshell)
1135 *zoptions++ = 'e';
1136 *zoptions = '\0';
1137 s.ztemp = zinput_temp;
1138 s.imode = 0666;
1139 if (zrequestor == NULL)
1140 zrequestor = "\"\"";
1141 s.znotify = zrequestor;
1142 s.cbytes = -1;
1143 s.zcmd = zfullcmd;
1144 s.ipos = 0;
1145
1146 ++cXcmds;
1147 pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds,
1148 cXcmds * sizeof (struct scmd));
1149 pasXcmds[cXcmds - 1] = s;
1150 }
1151 else
1152 {
1153 /* Finish up the execute file. */
1154 uxadd_xqt_line ('U', zuser, zxqtloc);
1155 if (zinput_from != NULL)
1156 {
1157 uxadd_xqt_line ('F', zinput_to, (char *) NULL);
1158 uxadd_xqt_line ('I', zinput_to, (char *) NULL);
1159 uxadd_send_file (zinput_from, zinput_to,
1160 finputcopied ? "C" : "c",
1161 zinput_temp, zforward, &sxqtsys, zxqtloc,
1162 bgrade);
1163 }
1164 if (fno_ack)
1165 uxadd_xqt_line ('N', (const char *) NULL, (const char *) NULL);
1166 if (ferror_ack)
1167 uxadd_xqt_line ('Z', (const char *) NULL, (const char *) NULL);
1168 if (zrequestor != NULL)
1169 uxadd_xqt_line ('R', zrequestor, (const char *) NULL);
1170 if (fneedshell)
1171 uxadd_xqt_line ('e', (const char *) NULL, (const char *) NULL);
1172 uxadd_xqt_line ('C', zfullcmd, (const char *) NULL);
1173 if (fclose (eXxqt_file) != 0)
1174 ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1175 eXxqt_file = NULL;
1176
1177 /* If the execution is to occur on another system, we must now
1178 arrange to copy the execute file to this system. */
1179 if (! fxqtlocal)
1180 uxadd_send_file (abxqt_tname, abxqt_xname, "C", abxqt_tname,
1181 zforward, &sxqtsys, zxqtloc, bgrade);
1182 }
1183
1184 /* If we got a signal, get out before spooling anything. */
1185 if (FGOT_SIGNAL ())
1186 uxabort ();
1187
1188 /* From here on in, it's too late. We don't call uxabort. */
1189 if (cXcmds > 0)
1190 {
1191 char *zjobid;
1192
1193 if (! sxqtsys.uuconf_fcall_transfer
1194 && ! sxqtsys.uuconf_fcalled_transfer)
1195 ulog (LOG_FATAL, "Not permitted to transfer files to or from %s",
1196 sxqtsys.uuconf_zname);
1197
1198 zjobid = zsysdep_spool_commands (&sxqtsys, bgrade, cXcmds, pasXcmds);
1199 if (zjobid == NULL)
1200 {
1201 ulog_close ();
1202 usysdep_exit (FALSE);
1203 }
1204
1205 if (fjobid)
1206 printf ("%s\n", zjobid);
1207
1208 ubuffree (zjobid);
1209
1210 if (fcall_any)
1211 {
1212 ubuffree (zcall_system);
1213 zcall_system = NULL;
1214 }
1215 else
1216 {
1217 fcall_any = TRUE;
1218 zcall_system = zbufcpy (sxqtsys.uuconf_zname);
1219 }
1220 }
1221
1222 /* If all that worked, make a log file entry. All log file reports
1223 up to this point went to stderr. */
1224 ulog_to_file (puuconf, TRUE);
1225 ulog_system (sxqtsys.uuconf_zname);
1226 ulog_user (zuser);
1227
1228 ulog (LOG_NORMAL, "Queuing %s", zfullcmd);
1229
1230 ulog_close ();
1231
1232 if (! fuucico)
1233 fexit = TRUE;
1234 else
1235 {
1236 if (zcall_system != NULL)
1237 fexit = fsysdep_run ("uucico", "-s", zcall_system);
1238 else if (fcall_any)
1239 fexit = fsysdep_run ("uucico", "-r1", (const char *) NULL);
1240 else
1241 fexit = TRUE;
1242 }
1243
1244 usysdep_exit (fexit);
1245
1246 /* Avoid error about not returning a value. */
1247 return 0;
1248}
1249
1250/* Report command usage. */
1251
1252static void
1253uxusage ()
1254{
1255 fprintf (stderr,
1256 "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n",
1257 VERSION);
1258 fprintf (stderr,
1259 "Usage: uux [options] [-] command\n");
1260 fprintf (stderr,
1261 " -,-p: Read standard input for standard input of command\n");
1262 fprintf (stderr,
1263 " -c: Do not copy local files to spool directory (default)\n");
1264 fprintf (stderr,
1265 " -C: Copy local files to spool directory\n");
1266 fprintf (stderr,
1267 " -l: link local files to spool directory\n");
1268 fprintf (stderr,
1269 " -g grade: Set job grade (must be alphabetic)\n");
1270 fprintf (stderr,
1271 " -n: Do not report completion status\n");
1272 fprintf (stderr,
1273 " -z: Report completion status only on error\n");
1274 fprintf (stderr,
1275 " -r: Do not start uucico daemon\n");
1276 fprintf (stderr,
1277 " -a address: Address to mail status report to\n");
1278 fprintf (stderr,
1279 " -b: Return standard input with status report\n");
1280 fprintf (stderr,
1281 " -s file: Report completion status to file\n");
1282 fprintf (stderr,
1283 " -j: Report job id\n");
1284 fprintf (stderr,
1285 " -x debug: Set debugging level\n");
1286#if HAVE_TAYLOR_CONFIG
1287 fprintf (stderr,
1288 " -I file: Set configuration file to use\n");
1289#endif /* HAVE_TAYLOR_CONFIG */
1290 exit (EXIT_FAILURE);
1291}
1292\f
1293/* Add a line to the execute file. */
1294
1295static void
1296uxadd_xqt_line (bchar, z1, z2)
1297 int bchar;
1298 const char *z1;
1299 const char *z2;
1300{
1301 if (eXxqt_file == NULL)
1302 {
1303 eXxqt_file = esysdep_fopen (zXxqt_name, FALSE, FALSE, TRUE);
1304 if (eXxqt_file == NULL)
1305 uxabort ();
1306 }
1307
1308 if (z1 == NULL)
1309 fprintf (eXxqt_file, "%c\n", bchar);
1310 else if (z2 == NULL)
1311 fprintf (eXxqt_file, "%c %s\n", bchar, z1);
1312 else
1313 fprintf (eXxqt_file, "%c %s %s\n", bchar, z1, z2);
1314}
1315
1316/* Add a file to be sent to the execute system. */
1317
1318static void
1319uxadd_send_file (zfrom, zto, zoptions, ztemp, zforward, qxqtsys, zxqtloc,
1320 bgrade)
1321 const char *zfrom;
1322 const char *zto;
1323 const char *zoptions;
1324 const char *ztemp;
1325 const char *zforward;
1326 const struct uuconf_system *qxqtsys;
1327 const char *zxqtloc;
1328 int bgrade;
1329{
1330 struct scmd s;
1331
1332 if (zforward != NULL)
1333 {
1334 char *zbase;
1335 char *zxqt;
1336 char abtname[CFILE_NAME_LEN];
1337 char abdname[CFILE_NAME_LEN];
1338 char abxname[CFILE_NAME_LEN];
1339 FILE *e;
1340
1341 /* We want to forward this file through the first execution
1342 system to other systems. We set up a remote execution of
1343 uucp to forward the file. */
1344 zbase = zsysdep_base_name (zfrom);
1345 if (zbase == NULL)
1346 uxabort ();
1347
1348 zxqt = zsysdep_data_file_name (qxqtsys, zxqtloc, bgrade, TRUE, abtname,
1349 abdname, abxname);
1350 if (zxqt == NULL)
1351 uxabort ();
1352 e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE);
1353 if (e == NULL)
1354 uxabort ();
1355 uxrecord_file (zxqt);
1356
1357 fprintf (e, "U %s %s\n", zsysdep_login_name (), zxqtloc);
1358 fprintf (e, "F %s %s\n", abdname, zbase);
1359 fprintf (e, "C uucp -C -W -d -g %c %s %s!%s\n",
1360 bgrade, zbase, zforward, zto);
1361
1362 ubuffree (zbase);
1363
1364 if (fclose (e) != 0)
1365 ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1366
1367 /* Send the execution file. */
1368 s.bcmd = 'S';
1369 s.pseq = NULL;
1370 s.zfrom = zbufcpy (abtname);
1371 s.zto = zbufcpy (abxname);
1372 s.zuser = zsysdep_login_name ();
1373 s.zoptions = "C";
1374 s.ztemp = s.zfrom;
1375 s.imode = 0666;
1376 s.znotify = NULL;
1377 s.cbytes = -1;
1378 s.zcmd = NULL;
1379 s.ipos = 0;
1380
1381 ++cXcmds;
1382 pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds,
1383 cXcmds * sizeof (struct scmd));
1384 pasXcmds[cXcmds - 1] = s;
1385
1386 /* Send the data file to abdname where the execution file will
1387 expect it. */
1388 zto = abdname;
1389 }
1390
1391 s.bcmd = 'S';
1392 s.pseq = NULL;
1393 s.zfrom = zbufcpy (zfrom);
1394 s.zto = zbufcpy (zto);
1395 s.zuser = zsysdep_login_name ();
1396 s.zoptions = zbufcpy (zoptions);
1397 s.ztemp = zbufcpy (ztemp);
1398 s.imode = 0666;
1399 s.znotify = "";
1400 s.cbytes = -1;
1401 s.zcmd = NULL;
1402 s.ipos = 0;
1403
1404 ++cXcmds;
1405 pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds,
1406 cXcmds * sizeof (struct scmd));
1407 pasXcmds[cXcmds - 1] = s;
1408}
1409\f
1410/* Copy stdin to a file. This is a separate function because it may
1411 call setjmp. */
1412
1413static void
1414uxcopy_stdin (e)
1415 FILE *e;
1416{
1417 CATCH_PROTECT size_t cread;
1418 char ab[1024];
1419
1420 do
1421 {
1422 size_t cwrite;
1423
1424 /* I want to use fread here, but there is a bug in some versions
1425 of SVR4 which causes fread to return less than a complete
1426 buffer even if EOF has not been reached. This is not online
1427 time, so speed is not critical, but it's still quite annoying
1428 to have to use an inefficient algorithm. */
1429 cread = 0;
1430 if (fsysdep_catch ())
1431 {
1432 usysdep_start_catch ();
1433
1434 while (cread < sizeof (ab))
1435 {
1436 int b;
1437
1438 if (FGOT_SIGNAL ())
1439 uxabort ();
1440
1441 /* There's an unimportant race here. If the user hits
1442 ^C between the FGOT_SIGNAL we just did and the time
1443 we enter getchar, we won't know about the signal
1444 (unless we're doing a longjmp, but we normally
1445 aren't). It's not a big problem, because the user
1446 can just hit ^C again. */
1447 b = getchar ();
1448 if (b == EOF)
1449 break;
1450 ab[cread] = b;
1451 ++cread;
1452 }
1453 }
1454
1455 usysdep_end_catch ();
1456
1457 if (FGOT_SIGNAL ())
1458 uxabort ();
1459
1460 if (cread > 0)
1461 {
1462 cwrite = fwrite (ab, sizeof (char), cread, e);
1463 if (cwrite != cread)
1464 ulog (LOG_FATAL, "fwrite: Wrote %d when attempted %d",
1465 (int) cwrite, (int) cread);
1466 }
1467 }
1468 while (cread == sizeof ab);
1469}
1470\f
1471/* Keep track of all files we have created so that we can delete them
1472 if we get a signal. The argument will be on the heap. */
1473
1474static int cXfiles;
1475static const char **pXaz;
1476
1477static void
1478uxrecord_file (zfile)
1479 const char *zfile;
1480{
1481 pXaz = (const char **) xrealloc ((pointer) pXaz,
1482 (cXfiles + 1) * sizeof (const char *));
1483 pXaz[cXfiles] = zfile;
1484 ++cXfiles;
1485}
1486
1487/* Delete all the files we have recorded and exit. */
1488
1489static void
1490uxabort ()
1491{
1492 int i;
1493
1494 if (eXxqt_file != NULL)
1495 (void) fclose (eXxqt_file);
1496 if (eXclose != NULL)
1497 (void) fclose (eXclose);
1498 for (i = 0; i < cXfiles; i++)
1499 (void) remove (pXaz[i]);
1500 ulog_close ();
1501 usysdep_exit (FALSE);
1502}