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