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