/* uux.c Prepare to execute a command on a remote system. Copyright (C) 1991, 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author of the program may be contacted at ian@airs.com or c/o AIRS, P.O. Box 520, Waltham, MA 02254. $Log: uux.c,v $ Revision 1.1.1.1 1993/03/21 09:45:37 cgd initial import of 386bsd-0.1 sources Revision 1.32 1992/03/15 04:51:17 ian Keep an array of signals we've received rather than a single variable Revision 1.31 1992/03/12 19:54:43 ian Debugging based on types rather than number Revision 1.30 1992/03/02 15:20:43 ian Check iSignal before entering fread Revision 1.29 1992/02/29 04:07:08 ian Added -j option to uucp and uux Revision 1.28 1992/02/29 01:06:59 ian Chip Salzenberg: recheck file permissions before sending Revision 1.27 1992/02/28 05:06:15 ian T. William Wells: fsysdep_catch must be a macro Revision 1.26 1992/02/27 05:40:54 ian T. William Wells: detach from controlling terminal, handle signals safely Revision 1.25 1992/02/23 03:26:51 ian Overhaul to use automatic configure shell script Revision 1.24 1992/02/08 22:33:32 ian Only get the current working directory if it's going to be needed Revision 1.23 1992/02/08 20:33:57 ian Handle all possible signals raised by abort Revision 1.22 1992/02/08 03:54:18 ian Include only in , added 1992 copyright Revision 1.21 1992/02/02 20:34:36 ian Niels Baggesen: must check user permissions on access to local files Revision 1.20 1992/01/21 19:39:12 ian Chip Salzenberg: uucp and uux start uucico for right system, not any Revision 1.19 1992/01/15 07:06:29 ian Set configuration directory in Makefile rather than sysdep.h Revision 1.18 1992/01/05 03:09:17 ian Changed abProgram and abVersion to non const to avoid compiler bug Revision 1.17 1992/01/05 02:51:38 ian Allocate enough space for log message Revision 1.16 1991/12/29 04:04:18 ian Added a bunch of extern definitions Revision 1.15 1991/12/21 21:16:05 ian Franc,ois Pinard: remove parentheses from ZSHELLSEPS Revision 1.14 1991/12/20 03:07:54 ian Added space and tab to ZSHELLSEPS to stop command at whitespace Revision 1.13 1991/12/18 03:54:14 ian Made error messages to terminal appear more normal Revision 1.12 1991/12/14 16:09:07 ian Added -l option to uux to link files into the spool directory Revision 1.11 1991/12/11 03:59:19 ian Create directories when necessary; don't just assume they exist Revision 1.10 1991/12/07 03:03:12 ian Split arguments like sh; request sh execution if any metachars appear Revision 1.9 1991/11/21 22:17:06 ian Add version string, print version when printing usage Revision 1.8 1991/11/15 19:17:32 ian Hannu Strang: copy stdin using fread/fwrite, not fgets/fputs Revision 1.7 1991/11/13 23:08:40 ian Expand remote pathnames in uucp and uux; fix up uux special cases Revision 1.6 1991/11/08 21:53:17 ian Brian Campbell: fix argument handling when looking for '-' Revision 1.5 1991/11/07 22:52:49 ian Chip Salzenberg: avoid recursive strtok, handle redirection better Revision 1.4 1991/09/19 03:23:34 ian Chip Salzenberg: append to private debugging file, don't overwrite it Revision 1.3 1991/09/19 02:30:37 ian From Chip Salzenberg: check whether signal is ignored differently Revision 1.2 1991/09/11 02:33:14 ian Added ffork argument to fsysdep_run Revision 1.1 1991/09/10 19:40:31 ian Initial revision */ #include "uucp.h" #if USE_RCS_ID char uux_rcsid[] = "$Id: uux.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $"; #endif #include #include #include "getopt.h" #include "system.h" #include "sysdep.h" /* External functions. */ extern int fclose (); /* These character lists should, perhaps, be in sysdep.h. */ /* This is the list of shell metacharacters that we check for. If one of these is present, we request uuxqt to execute the command with /bin/sh. Otherwise we let it execute using execve. */ #define ZSHELLCHARS "\"'`*?[;&()|<>\\$" /* This is the list of word separators. We break filename arguments at these characters. */ #define ZSHELLSEPS ";&*|<> \t" /* This is the list of word separators without the redirection operators. */ #define ZSHELLNONREDIRSEPS ";&*| \t" /* The program name. */ char abProgram[] = "uux"; /* Long getopt options. */ static const struct option asXlongopts[] = { { NULL, 0, NULL, 0 } }; const struct option *_getopt_long_options = asXlongopts; /* The execute file we are creating. */ static FILE *eXxqt_file; /* A list of commands to be spooled. */ static struct scmd *pasXcmds; static int cXcmds; /* A file to close if we're forced to exit. */ static FILE *eXclose; /* Local functions. */ static void uxusage P((void)); static void uxadd_xqt_line P((int bchar, const char *z1, const char *z2)); static void uxadd_send_file P((const char *zfrom, const char *zto, const char *zoptions, const char *ztemp)); static void uxcopy_stdin P((FILE *e)); static void uxrecord_file P((const char *zfile)); static void uxabort P((void)); int main (argc, argv) int argc; char **argv; { int iopt; /* -a: requestor address for status reports. */ const char *zrequestor = NULL; /* -b: if true, return standard input on error. */ boolean fretstdin = FALSE; /* -c,-C: if true, copy to spool directory. */ boolean fcopy = FALSE; /* -c: set if -c appears explicitly; if it and -l appear, then if the link fails we don't copy the file. */ boolean fdontcopy = FALSE; /* -I: configuration file name. */ const char *zconfig = NULL; /* -j: output job id. */ boolean fjobid = FALSE; /* -g: job grade. */ char bgrade = BDEFAULT_UUX_GRADE; /* -l: link file to spool directory. */ boolean flink = FALSE; /* -n: do not notify upon command completion. */ boolean fno_ack = FALSE; /* -p: read standard input for command standard input. */ boolean fread_stdin = FALSE; /* -r: do not start uucico when finished. */ boolean fuucico = TRUE; /* -s: report status to named file. */ const char *zstatus_file = NULL; /* -W: only expand local file names. */ boolean fexpand = TRUE; /* -z: report status only on error. */ boolean ferror_ack = FALSE; int i; int clen; char *zargs; char *zarg; char *zcmd; char *zexclam; boolean fgetcwd; const char *zuser; struct ssysteminfo sxqtsys; const struct ssysteminfo *qxqtsys; boolean fxqtlocal; char **pzargs; int calloc_args; int cargs; const char *zxqtname; char abxqt_tname[CFILE_NAME_LEN]; char abxqt_xname[CFILE_NAME_LEN]; boolean fneedshell; char *zprint; const char *zcall_system; boolean fcall_any; boolean fexit; /* We need to be able to read a single - as an option, which getopt won't do. So that we can still use getopt, we run through the options looking for an option "-"; if we find one we change it to "-p", which is an equivalent option. */ for (i = 1; i < argc; i++) { if (argv[i][0] != '-') break; if (argv[i][1] == '\0') argv[i] = xstrdup ("-p"); else { const char *z; for (z = argv[i] + 1; *z != '\0'; z++) { /* If the option takes an argument, and the argument is not appended, then skip the next argument. */ if (*z == 'a' || *z == 'g' || *z == 'I' || *z == 's' || *z == 'x') { if (z[1] == '\0') i++; break; } } } } /* The leading + in the getopt string means to stop processing options as soon as a non-option argument is seen. */ while ((iopt = getopt (argc, argv, "+a:bcCg:I:jlnprs:Wx:z")) != EOF) { switch (iopt) { case 'a': /* Set requestor name: mail address to which status reports should be sent. */ zrequestor = optarg; break; case 'b': /* Return standard input on error. */ fretstdin = TRUE; break; case 'c': /* Do not copy local files to spool directory. */ fcopy = FALSE; fdontcopy = TRUE; break; case 'C': /* Copy local files to spool directory. */ fcopy = TRUE; break; case 'I': /* Configuration file name. */ zconfig = optarg; break; case 'j': /* Output jobid. */ fjobid = TRUE; break; case 'g': /* Set job grade. */ bgrade = optarg[0]; break; case 'l': /* Link file to spool directory. */ flink = TRUE; break; case 'n': /* Do not notify upon command completion. */ fno_ack = TRUE; break; case 'p': /* Read standard input for command standard input. */ fread_stdin = TRUE; break; case 'r': /* Do not start uucico when finished. */ fuucico = FALSE; break; case 's': /* Report status to named file. */ zstatus_file = optarg; break; case 'W': /* Only expand local file names. */ fexpand = FALSE; break; case 'x': #if DEBUG > 1 /* Set debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'z': /* Report status only on error. */ ferror_ack = TRUE; break; case 0: /* Long option found and flag set. */ break; default: uxusage (); break; } } if (! FGRADE_LEGAL (bgrade)) { ulog (LOG_ERROR, "Ignoring illegal grade"); bgrade = BDEFAULT_UUX_GRADE; } if (optind == argc) uxusage (); uread_config (zconfig); /* The command and files arguments could be quoted in any number of ways, so we split them apart ourselves. We do this before calling usysdep_initialize because we want to set fgetcwd correctly. */ clen = 1; for (i = optind; i < argc; i++) clen += strlen (argv[i]) + 1; zargs = (char *) alloca (clen); *zargs = '\0'; for (i = optind; i < argc; i++) { strcat (zargs, argv[i]); strcat (zargs, " "); } /* The first argument is the command to execute. */ clen = strcspn (zargs, ZSHELLSEPS); zcmd = (char *) alloca (clen + 1); strncpy (zcmd, zargs, clen); zcmd[clen] = '\0'; zargs += clen; /* Split the arguments out into an array. We break the arguments into alternating sequences of characters not in ZSHELLSEPS and characters in ZSHELLSEPS. We remove whitespace. We separate the redirection characters '>' and '<' into their own arguments to make them easier to process below. */ calloc_args = 10; pzargs = (char **) xmalloc (calloc_args * sizeof (char *)); cargs = 0; for (zarg = strtok (zargs, " \t"); zarg != NULL; zarg = strtok ((char *) NULL, " \t")) { while (*zarg != '\0') { if (cargs >= calloc_args + 1) { calloc_args += 10; pzargs = (char **) xrealloc ((pointer) pzargs, calloc_args * sizeof (char *)); } clen = strcspn (zarg, ZSHELLSEPS); if (clen > 0) { pzargs[cargs] = (char *) xmalloc (clen + 1); strncpy (pzargs[cargs], zarg, clen); pzargs[cargs][clen] = '\0'; ++cargs; zarg += clen; } /* We deliberately separate '>' and '<' out. */ if (*zarg != '\0') { clen = strspn (zarg, ZSHELLNONREDIRSEPS); if (clen == 0) clen = 1; pzargs[cargs] = (char *) xmalloc (clen + 1); strncpy (pzargs[cargs], zarg, clen); pzargs[cargs][clen] = '\0'; ++cargs; zarg += clen; } } } /* Now look through the arguments to see if we are going to need the current working directory. We don't try to make a precise determination, just a conservative one. The basic idea is that we don't want to get the cwd for 'rmail - foo!user' (note that we don't examine the command itself). */ fgetcwd = FALSE; for (i = 0; i < cargs; i++) { zexclam = strrchr (pzargs[i], '!'); if (zexclam != NULL && fsysdep_needs_cwd (zexclam + 1)) { fgetcwd = TRUE; break; } if ((pzargs[i][0] == '<' || pzargs[i][0] == '>') && i + 1 < cargs && strchr (pzargs[i + 1], '!') == NULL && fsysdep_needs_cwd (pzargs[i + 1])) { fgetcwd = TRUE; break; } } #ifdef SIGINT usysdep_signal (SIGINT); #endif #ifdef SIGHUP usysdep_signal (SIGHUP); #endif #ifdef SIGQUIT usysdep_signal (SIGQUIT); #endif #ifdef SIGTERM usysdep_signal (SIGTERM); #endif #ifdef SIGPIPE usysdep_signal (SIGPIPE); #endif usysdep_initialize (FALSE, fgetcwd); ulog_fatal_fn (uxabort); zuser = zsysdep_login_name (); /* Figure out which system the command is to be executed on. */ zexclam = strchr (zcmd, '!'); if (zexclam == NULL) { qxqtsys = &sLocalsys; fxqtlocal = TRUE; } else { *zexclam = '\0'; if (*zcmd == '\0' || strcmp (zcmd, zLocalname) == 0) { qxqtsys = &sLocalsys; fxqtlocal = TRUE; } else { if (fread_system_info (zcmd, &sxqtsys)) qxqtsys = &sxqtsys; else { if (! fUnknown_ok) ulog (LOG_FATAL, "System %s unknown", zcmd); qxqtsys = &sUnknown; sUnknown.zname = zcmd; } fxqtlocal = FALSE; } zcmd = zexclam + 1; } /* Make sure we have a spool directory. */ if (! fsysdep_make_spool_dir (qxqtsys)) uxabort (); /* Name and open the execute file. If the execution is to occur on a remote system, we must create a data file and copy it over. */ if (fxqtlocal) zxqtname = zsysdep_xqt_file_name (); else zxqtname = zsysdep_data_file_name (qxqtsys, 'X', abxqt_tname, (char *) NULL, abxqt_xname); if (zxqtname == NULL) uxabort (); eXxqt_file = esysdep_fopen (zxqtname, FALSE, FALSE, TRUE); if (eXxqt_file == NULL) uxabort (); uxrecord_file (xstrdup (zxqtname)); /* Specify the user. */ uxadd_xqt_line ('U', zuser, zLocalname); /* Look through the arguments. Any argument containing an exclamation point character is interpreted as a file name, and is sent to the appropriate system. */ zcall_system = NULL; fcall_any = FALSE; for (i = 0; i < cargs; i++) { const char *zsystem, *zconst; char *zfile; boolean finput, foutput; boolean flocal; /* Check for a parenthesized argument; remove the parentheses and otherwise ignore it (this is how an exclamation point is quoted). */ if (pzargs[i][0] == '(') { clen = strlen (pzargs[i]); if (pzargs[i][clen - 1] != ')') ulog (LOG_ERROR, "Mismatched parentheses"); else pzargs[i][clen - 1] = '\0'; ++pzargs[i]; continue; } /* Check whether we are doing a redirection. */ finput = FALSE; foutput = FALSE; if (i + 1 < cargs) { if (pzargs[i][0] == '<') finput = TRUE; else if (pzargs[i][0] == '>') foutput = TRUE; if (finput || foutput) { pzargs[i] = NULL; i++; } } zexclam = strchr (pzargs[i], '!'); /* If there is no exclamation point and no redirection, this argument is left untouched. */ if (zexclam == NULL && ! finput && ! foutput) continue; /* Get the system name and file name for this file. */ if (zexclam == NULL) { zsystem = zLocalname; zfile = pzargs[i]; flocal = TRUE; } else { *zexclam = '\0'; zsystem = pzargs[i]; if (zsystem[0] != '\0') flocal = strcmp (zsystem, zLocalname) == 0; else { zsystem = zLocalname; flocal = TRUE; } zfile = zexclam + 1; } /* Add the current working directory to the file name if it's not an absolute path. */ if (fexpand || flocal) { zconst = zsysdep_add_cwd (zfile, flocal); if (zconst == NULL) uxabort (); zfile = xstrdup (zconst); } /* Check for output redirection. We strip this argument out, and create an O command which tells uuxqt where to send the output. */ if (foutput) { if (flocal) { if (! fin_directory_list (qxqtsys, zfile, qxqtsys->zremote_receive, TRUE, FALSE, (const char *) NULL)) ulog (LOG_FATAL, "Not permitted to create %s", zfile); } if (strcmp (zsystem, qxqtsys->zname) == 0) uxadd_xqt_line ('O', zfile, (const char *) NULL); else uxadd_xqt_line ('O', zfile, zsystem); pzargs[i] = NULL; continue; } if (finput) { if (fread_stdin) ulog (LOG_FATAL, "Standard input specified twice"); pzargs[i] = NULL; } if (flocal) { char *zuse; const char *zdata; char abtname[CFILE_NAME_LEN]; char abdname[CFILE_NAME_LEN]; /* It's a local file. If requested by -C, copy the file to the spool directory. If requested by -l, link the file to the spool directory; if the link fails, we copy the file, unless -c was explictly used. If the execution is occurring on the local system, we force the copy as well, because otherwise we would have to have some way to tell uuxqt not to move the file. If the file is being shipped to another system, we must set up a transfer request. First make sure the user has legitimate access, since we are running setuid. */ if (! fsysdep_access (zfile)) uxabort (); if (fcopy || flink || fxqtlocal) { char *zdup; boolean fdid; zdata = zsysdep_data_file_name (qxqtsys, bgrade, abtname, abdname, (char *) NULL); if (zdata == NULL) uxabort (); zdup = xstrdup (zdata); uxrecord_file (zdup); fdid = FALSE; if (flink) { boolean fworked; if (! fsysdep_link (zfile, zdup, &fworked)) uxabort (); if (fworked) fdid = TRUE; else if (fdontcopy) ulog (LOG_FATAL, "%s: Can't link to spool directory", zfile); } if (! fdid) { if (! fcopy_file (zfile, zdup, FALSE, TRUE)) uxabort (); } xfree ((pointer) zdup); zuse = abtname; } else { /* Make sure the daemon can access the file. */ if (! fsysdep_daemon_access (zfile)) uxabort (); if (! fin_directory_list (&sLocalsys, zfile, sLocalsys.zlocal_send, TRUE, TRUE, zuser)) ulog (LOG_FATAL, "Not permitted to send from %s", zfile); zuse = zfile; zdata = zsysdep_data_file_name (qxqtsys, bgrade, (char *) NULL, abdname, (char *) NULL); if (zdata == NULL) uxabort (); strcpy (abtname, "D.0"); } if (fxqtlocal) { if (finput) uxadd_xqt_line ('I', zuse, (char *) NULL); else pzargs[i] = zuse; } else { uxadd_send_file (zuse, abdname, fcopy || flink || fxqtlocal ? "C" : "c", abtname); if (finput) { uxadd_xqt_line ('F', abdname, (char *) NULL); uxadd_xqt_line ('I', abdname, (char *) NULL); } else { const char *zbase; zbase = zsysdep_base_name (zfile); if (zbase == NULL) uxabort (); uxadd_xqt_line ('F', abdname, zbase); pzargs[i] = xstrdup (zbase); } } } else if (strcmp (qxqtsys->zname, zsystem) == 0) { /* The file is already on the system where the command is to be executed. */ if (finput) uxadd_xqt_line ('I', zfile, (const char *) NULL); else pzargs[i] = zfile; } else { struct ssysteminfo sfromsys; const struct ssysteminfo *qfromsys; char abtname[CFILE_NAME_LEN]; char abdname[CFILE_NAME_LEN]; char *ztemp; struct scmd s; const char *zjobid; /* We need to request a remote file. Make sure we have a spool directory for the remote system. */ if (! fread_system_info (zsystem, &sfromsys)) { if (! fUnknown_ok) ulog (LOG_FATAL, "System %s unknown", zsystem); sfromsys = sUnknown; sfromsys.zname = zsystem; } qfromsys = &sfromsys; if (! fsysdep_make_spool_dir (qfromsys)) uxabort (); /* We want the file to wind up in the spool directory of the local system (whether the execution is occurring locally or not); we have to use an absolute file name here, because otherwise the file would wind up in the spool directory of the system it is coming from. */ if (! fxqtlocal) { if (! fsysdep_make_spool_dir (&sLocalsys)) uxabort (); } zconst = zsysdep_data_file_name (&sLocalsys, bgrade, abtname, (char *) NULL, (char *) NULL); if (zconst == NULL) uxabort (); /* Request the file. The special option '9' is a signal to uucico that it's OK to receive a file into the spool directory; normally such requests are rejected. */ s.bcmd = 'R'; s.pseq = NULL; s.zfrom = zfile; s.zto = abtname; s.zuser = zuser; s.zoptions = "9"; s.ztemp = ""; s.imode = 0600; s.znotify = ""; s.cbytes = -1; zjobid = zsysdep_spool_commands (qfromsys, bgrade, 1, &s); if (zjobid == NULL) uxabort (); if (fjobid) printf ("%s\n", zjobid); if (fcall_any) zcall_system = NULL; else { fcall_any = TRUE; zcall_system = xstrdup (qfromsys->zname); } /* Now if the execution is to occur on another system, we must create an execute file to send the file there. The name of the file on the execution system is put into abdname. */ if (fxqtlocal) ztemp = abtname; else { const char *zxqt_file; FILE *e; /* Get a file name to use on the execution system. */ if (zsysdep_data_file_name (qxqtsys, bgrade, (char *) NULL, abdname, (char *) NULL) == NULL) uxabort (); ztemp = abdname; /* The local spool directory was created above, if it didn't already exist. */ zxqt_file = zsysdep_xqt_file_name (); if (zxqt_file == NULL) uxabort (); /* Queue up a uucp command to be executed locally once the file arrives. We take advantage of the file renaming and moving that uuxqt does to remove the file and avoid the hassles of adding the current directory. The -W switch to uucp prevents from adding the current directory to the remote file. */ e = esysdep_fopen (zxqt_file, FALSE, FALSE, TRUE); if (e == NULL) uxabort (); eXclose = e; uxrecord_file (xstrdup (zxqt_file)); fprintf (e, "U %s %s\n", zuser, zLocalname); fprintf (e, "F %s foo\n", abtname); fprintf (e, "C uucp -CW foo %s!%s\n", qxqtsys->zname, abdname); eXclose = NULL; if (fclose (e) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); } /* Tell the command execution to wait until the file has been received, and tell it the real file name to use. */ if (finput) { uxadd_xqt_line ('F', ztemp, (char *) NULL); uxadd_xqt_line ('I', ztemp, (char *) NULL); } else { const char *zbase; zbase = zsysdep_base_name (zfile); if (zbase == NULL) uxabort (); uxadd_xqt_line ('F', ztemp, zbase); pzargs[i] = xstrdup (zbase); } } } /* If standard input is to be read from the stdin of uux, we read it here into a temporary file and send it to the execute system. */ if (fread_stdin) { const char *zdata; char abtname[CFILE_NAME_LEN]; char abdname[CFILE_NAME_LEN]; FILE *e; zdata = zsysdep_data_file_name (qxqtsys, bgrade, abtname, abdname, (char *) NULL); if (zdata == NULL) uxabort (); e = esysdep_fopen (zdata, FALSE, FALSE, TRUE); if (e == NULL) uxabort (); eXclose = e; uxrecord_file (xstrdup (zdata)); uxcopy_stdin (e); eXclose = NULL; if (fclose (e) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); if (fxqtlocal) uxadd_xqt_line ('I', abtname, (const char *) NULL); else { uxadd_xqt_line ('F', abdname, (const char *) NULL); uxadd_xqt_line ('I', abdname, (const char *) NULL); uxadd_send_file (abtname, abdname, "C", abtname); } } /* Here all the arguments have been determined, so the command can be written out. If any of the arguments contain shell metacharacters, we request remote execution with /bin/sh (this is the 'e' command in the execute file). The default is assumed to be remote execution with execve. */ fprintf (eXxqt_file, "C %s", zcmd); fneedshell = FALSE; if (zcmd[strcspn (zcmd, ZSHELLCHARS)] != '\0') fneedshell = TRUE; for (i = 0; i < cargs; i++) { if (pzargs[i] != NULL) { fprintf (eXxqt_file, " %s", pzargs[i]); if (pzargs[i][strcspn (pzargs[i], ZSHELLCHARS)] != '\0') fneedshell = TRUE; } } fprintf (eXxqt_file, "\n"); /* Write out all the other miscellaneous junk. */ if (fno_ack) uxadd_xqt_line ('N', (const char *) NULL, (const char *) NULL); if (ferror_ack) uxadd_xqt_line ('Z', (const char *) NULL, (const char *) NULL); if (zrequestor != NULL) uxadd_xqt_line ('R', zrequestor, (const char *) NULL); if (fretstdin) uxadd_xqt_line ('B', (const char *) NULL, (const char *) NULL); if (zstatus_file != NULL) uxadd_xqt_line ('M', zstatus_file, (const char *) NULL); if (fneedshell) uxadd_xqt_line ('e', (const char *) NULL, (const char *) NULL); if (fclose (eXxqt_file) != 0) ulog (LOG_FATAL, "fclose: %s", strerror (errno)); eXxqt_file = NULL; /* If the execution is to occur on another system, we must now arrange to copy the execute file to this system. */ if (! fxqtlocal) uxadd_send_file (abxqt_tname, abxqt_xname, "C", abxqt_tname); /* If we got a signal, get out before spooling anything. */ if (FGOT_SIGNAL ()) uxabort (); /* From here on in, it's too late. We don't call uxabort. */ if (cXcmds > 0) { const char *zjobid; zjobid = zsysdep_spool_commands (qxqtsys, bgrade, cXcmds, pasXcmds); if (zjobid == NULL) { ulog_close (); usysdep_exit (FALSE); } if (fjobid) printf ("%s\n", zjobid); if (fcall_any) zcall_system = NULL; else { fcall_any = TRUE; zcall_system = qxqtsys->zname; } } /* If all that worked, make a log file entry. All log file reports up to this point went to stderr. */ ulog_to_file (TRUE); ulog_system (qxqtsys->zname); ulog_user (zuser); clen = strlen (zcmd) + 2; for (i = 0; i < cargs; i++) if (pzargs[i] != NULL) clen += strlen (pzargs[i]) + 1; zprint = (char *) alloca (clen); strcpy (zprint, zcmd); strcat (zprint, " "); for (i = 0; i < cargs; i++) { if (pzargs[i] != NULL) { strcat (zprint, pzargs[i]); strcat (zprint, " "); } } zprint[strlen (zprint) - 1] = '\0'; ulog (LOG_NORMAL, "Queuing %s", zprint); ulog_close (); if (! fuucico) fexit = TRUE; else { if (zcall_system != NULL) fexit = fsysdep_run (TRUE, "uucico", "-s", zcall_system); else if (fcall_any) fexit = fsysdep_run (TRUE, "uucico", "-r1", (const char *) NULL); else fexit = TRUE; } usysdep_exit (fexit); /* Avoid error about not returning a value. */ return 0; } /* Report command usage. */ static void uxusage () { fprintf (stderr, "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", abVersion); fprintf (stderr, "Usage: uux [options] [-] command\n"); fprintf (stderr, " -,-p: Read standard input for standard input of command\n"); fprintf (stderr, " -c: Do not copy local files to spool directory (default)\n"); fprintf (stderr, " -C: Copy local files to spool directory\n"); fprintf (stderr, " -l: link local files to spool directory\n"); fprintf (stderr, " -g grade: Set job grade (must be alphabetic)\n"); fprintf (stderr, " -n: Do not report completion status\n"); fprintf (stderr, " -z: Report completion status only on error\n"); fprintf (stderr, " -r: Do not start uucico daemon\n"); fprintf (stderr, " -a address: Address to mail status report to\n"); fprintf (stderr, " -b: Return standard input with status report\n"); fprintf (stderr, " -s file: Report completion status to file\n"); fprintf (stderr, " -j: Report job id\n"); fprintf (stderr, " -x debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG fprintf (stderr, " -I file: Set configuration file to use (default %s%s)\n", NEWCONFIGLIB, CONFIGFILE); #endif /* HAVE_TAYLOR_CONFIG */ exit (EXIT_FAILURE); } /* Add a line to the execute file. */ static void uxadd_xqt_line (bchar, z1, z2) int bchar; const char *z1; const char *z2; { if (z1 == NULL) fprintf (eXxqt_file, "%c\n", bchar); else if (z2 == NULL) fprintf (eXxqt_file, "%c %s\n", bchar, z1); else fprintf (eXxqt_file, "%c %s %s\n", bchar, z1, z2); } /* Add a file to be sent to the execute system. */ static void uxadd_send_file (zfrom, zto, zoptions, ztemp) const char *zfrom; const char *zto; const char *zoptions; const char *ztemp; { struct scmd s; s.bcmd = 'S'; s.pseq = NULL; s.zfrom = xstrdup (zfrom); s.zto = xstrdup (zto); s.zuser = zsysdep_login_name (); s.zoptions = xstrdup (zoptions); s.ztemp = xstrdup (ztemp); s.imode = 0666; s.znotify = ""; s.cbytes = -1; ++cXcmds; pasXcmds = (struct scmd *) xrealloc ((pointer) pasXcmds, cXcmds * sizeof (struct scmd)); pasXcmds[cXcmds - 1] = s; } /* Copy stdin to a file. This is a separate function because it may call setjump. */ static void uxcopy_stdin (e) FILE *e; { CATCH_PROTECT int cread; char ab[1024]; do { int cwrite; if (fsysdep_catch ()) { usysdep_start_catch (); if (FGOT_SIGNAL ()) uxabort (); /* There's an unimportant race here. If the user hits ^C between the FGOT_SIGNAL we just did and the time we enter fread, we won't know about the signal (unless we're doing a longjmp, but we normally aren't). It's not a big problem, because the user can just hit ^C again. */ cread = fread (ab, sizeof (char), sizeof ab, stdin); } usysdep_end_catch (); if (FGOT_SIGNAL ()) uxabort (); if (cread > 0) { cwrite = fwrite (ab, sizeof (char), cread, e); if (cwrite != cread) { if (cwrite == EOF) ulog (LOG_FATAL, "fwrite: %s", strerror (errno)); else ulog (LOG_FATAL, "fwrite: Wrote %d when attempted %d", cwrite, cread); } } } while (cread == sizeof ab); } /* Keep track of all files we have created so that we can delete them if we get a signal. The argument will be on the heap. */ static int cxfiles; static const char **pxaz; static void uxrecord_file (zfile) const char *zfile; { pxaz = (const char **) xrealloc ((pointer) pxaz, (cxfiles + 1) * sizeof (const char *)); pxaz[cxfiles] = zfile; ++cxfiles; } /* Delete all the files we have recorded and exit. */ static void uxabort () { int i; if (eXxqt_file != NULL) (void) fclose (eXxqt_file); if (eXclose != NULL) (void) fclose (eXclose); for (i = 0; i < cxfiles; i++) (void) remove (pxaz[i]); ulog_close (); usysdep_exit (FALSE); }