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