/* uuxqt.c Run uux commands. Copyright (C) 1991, 1992, 1993, 1994, 1995 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com or c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144. */ #include "uucp.h" #if USE_RCS_ID const char uuxqt_rcsid[] = "$Id: uuxqt.c,v 1.1 1995/10/18 08:38:45 deraadt Exp $"; #endif #include #include #include "getopt.h" #include "uudefs.h" #include "uuconf.h" #include "system.h" /* Static variables used to unlock things if we get a fatal error. */ static int iQlock_seq = -1; static const char *zQunlock_cmd; static const char *zQunlock_file; static boolean fQunlock_directory; int cQmaxuuxqts; /* Static variables to free in uqcleanup. */ static char *zQoutput; static char *zQmail; /* Local functions. */ static void uqusage P((void)); static void uqhelp P((void)); static void uqabort P((void)); static void uqdo_xqt_file P((pointer puuconf, const char *zfile, const char *zbase, const struct uuconf_system *qsys, const char *zlocalname, const char *zcmd, boolean *pfprocessed)); static void uqcleanup P((const char *zfile, int iflags)); static int isave_files P((const struct uuconf_system *, const char *zmail, const char *zfile, int iclean)); static boolean fqforward P((const char *zfile, char **pzallowed, const char *zlog, const char *zmail)); /* Long getopt options. */ static const struct option asQlongopts[] = { { "command", required_argument, 0, 'c' }, { "system", required_argument, 0, 's' }, { "config", required_argument, NULL, 'I' }, { "debug", required_argument, NULL, 'x' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; int main (argc, argv) int argc; char **argv; { /* The type of command to execute (NULL for any type). */ const char *zcmd = NULL; /* The configuration file name. */ const char *zconfig = NULL; /* The system to execute commands for. */ const char *zdosys = NULL; int iopt; pointer puuconf; int iuuconf; const char *zlocalname; boolean fany; char *z, *zgetsys; boolean ferr; boolean fsys; struct uuconf_system ssys; zProgram = argv[0]; while ((iopt = getopt_long (argc, argv, "c:I:s:vx:", asQlongopts, (int *) NULL)) != EOF) { switch (iopt) { case 'c': /* Set the type of command to execute. */ zcmd = optarg; break; case 'I': /* Set the configuration file name. */ if (fsysdep_other_config (optarg)) zconfig = optarg; break; case 's': zdosys = optarg; break; case 'x': #if DEBUG > 1 /* Set the debugging level. */ iDebug |= idebug_parse (optarg); #endif break; case 'v': /* Print version and exit. */ printf ("%s: Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n", zProgram, VERSION); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 1: /* --help. */ uqhelp (); exit (EXIT_SUCCESS); /*NOTREACHED*/ case 0: /* Long option found and flag set. */ break; default: uqusage (); break; } } if (optind != argc) uqusage (); iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #if DEBUG > 1 { const char *zdebug; iuuconf = uuconf_debuglevel (puuconf, &zdebug); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (zdebug != NULL) iDebug |= idebug_parse (zdebug); } #endif iuuconf = uuconf_maxuuxqts (puuconf, &cQmaxuuxqts); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); #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 (puuconf, INIT_SUID); ulog_to_file (puuconf, TRUE); ulog_fatal_fn (uqabort); iuuconf = uuconf_localname (puuconf, &zlocalname); if (iuuconf == UUCONF_NOT_FOUND) { zlocalname = zsysdep_localname (); if (zlocalname == NULL) exit (EXIT_FAILURE); } else if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); fsys = FALSE; /* If we were given a system name, canonicalize it. */ if (zdosys != NULL) { iuuconf = uuconf_system_info (puuconf, zdosys, &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); if (strcmp (zdosys, zlocalname) == 0) { iuuconf = uuconf_system_local (puuconf, &ssys); if (iuuconf != UUCONF_SUCCESS) ulog_uuconf (LOG_FATAL, puuconf, iuuconf); ssys.uuconf_zname = (char *) zlocalname; } else { if (! funknown_system (puuconf, zdosys, &ssys)) ulog (LOG_FATAL, "%s: system not found", zdosys); } } zdosys = zbufcpy (ssys.uuconf_zname); fsys = TRUE; } /* Limit the number of uuxqt processes, and make sure we're the only uuxqt daemon running for this command. */ iQlock_seq = ixsysdep_lock_uuxqt (zcmd, cQmaxuuxqts); if (iQlock_seq < 0) { ulog_close (); usysdep_exit (TRUE); } zQunlock_cmd = zcmd; /* Keep scanning the execute files until we don't process any of them. */ do { fany = FALSE; /* Look for each execute file, and run it. */ if (! fsysdep_get_xqt_init (zdosys)) { ulog_close (); usysdep_exit (FALSE); } while ((z = zsysdep_get_xqt (zdosys, &zgetsys, &ferr)) != NULL) { const char *zloc; boolean fprocessed; char *zbase; /* Get the system information for the system returned by zsysdep_get_xqt. */ if (! fsys || strcmp (ssys.uuconf_zname, zgetsys) != 0) { if (fsys) (void) uuconf_system_free (puuconf, &ssys); iuuconf = uuconf_system_info (puuconf, zgetsys, &ssys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (z); ubuffree (zgetsys); continue; } else if (strcmp (zgetsys, zlocalname) == 0) { iuuconf = uuconf_system_local (puuconf, &ssys); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); ubuffree (z); ubuffree (zgetsys); continue; } ssys.uuconf_zname = (char *) zlocalname; } else { if (! funknown_system (puuconf, zgetsys, &ssys)) { ulog (LOG_ERROR, "%s: Execute file for unknown system %s", z, zgetsys); (void) remove (z); ubuffree (z); ubuffree (zgetsys); continue; } } } fsys = TRUE; } /* If we've received a signal, get out of the loop. */ if (FGOT_SIGNAL ()) { ubuffree (z); ubuffree (zgetsys); break; } /* Make sure we are supposed to be executing jobs for this system. */ if (zdosys != NULL && strcmp (zdosys, ssys.uuconf_zname) != 0) { ubuffree (z); ubuffree (zgetsys); continue; } zloc = ssys.uuconf_zlocalname; if (zloc == NULL) zloc = zlocalname; ulog_system (ssys.uuconf_zname); zbase = zsysdep_base_name (z); uqdo_xqt_file (puuconf, z, zbase, &ssys, zloc, zcmd, &fprocessed); ubuffree (zbase); ulog_system ((const char *) NULL); ulog_user ((const char *) NULL); if (fprocessed) fany = TRUE; ubuffree (z); ubuffree (zgetsys); } usysdep_get_xqt_free (zdosys); } while (fany && ! FGOT_SIGNAL ()); (void) fsysdep_unlock_uuxqt (iQlock_seq, zcmd, cQmaxuuxqts); iQlock_seq = -1; ulog_close (); if (FGOT_SIGNAL ()) ferr = TRUE; usysdep_exit (! ferr); /* Avoid errors about not returning a value. */ return 0; } static void uqhelp () { printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n", VERSION); printf ("Usage: %s [-c,--command cmd] [-s,--system system]\n", zProgram); printf (" -c,--command cmd: Set type of command to execute\n"); printf (" -s,--system system: Execute commands only for named system\n"); printf (" -x,--debug debug: Set debugging level\n"); #if HAVE_TAYLOR_CONFIG printf (" -I,--config file: Set configuration file to use\n"); #endif /* HAVE_TAYLOR_CONFIG */ printf (" -v,--version: Print version and exit\n"); printf (" --help: Print help and exit\n"); } static void uqusage () { fprintf (stderr, "Usage: %s [-c,--command cmd] [-s,--system system]\n", zProgram); fprintf (stderr, "Use %s --help for help\n", zProgram); exit (EXIT_FAILURE); } /* This is the abort function called when we get a fatal error. */ static void uqabort () { #if ! HAVE_HDB_LOGGING /* When using HDB logging, it's a pain to have no system name. */ ulog_system ((const char *) NULL); #endif ulog_user ((const char *) NULL); if (fQunlock_directory) (void) fsysdep_unlock_uuxqt_dir (iQlock_seq); if (zQunlock_file != NULL) (void) fsysdep_unlock_uuxqt_file (zQunlock_file); if (iQlock_seq >= 0) (void) fsysdep_unlock_uuxqt (iQlock_seq, zQunlock_cmd, cQmaxuuxqts); ulog_close (); usysdep_exit (FALSE); } /* An execute file is a series of lines. The first character of each line is a command. The following commands are defined: C command-line I standard-input O standard-output [ system ] F required-file filename-to-use R requestor-address U user system Z (acknowledge if command failed; default) N (no acknowledgement on failure) n (acknowledge if command succeeded) B (return command input on error) e (process with sh) E (process with exec) M status-file # comment Unrecognized commands are ignored. We actually do not recognize the Z command, since it requests default behaviour. We always send mail on failure, unless the N command appears. We never send mail on success, unless the n command appears. This code does not currently support the B or M commands. */ /* Command arguments. */ static char **azQargs; /* Command as a complete string. */ static char *zQcmd; /* Standard input file name. */ static char *zQinput; /* Standard output file name. */ static char *zQoutfile; /* Standard output system. */ static char *zQoutsys; /* Number of required files. */ static int cQfiles; /* Names of required files. */ static char **azQfiles; /* Names required files should be renamed to (NULL if original is OK). */ static char **azQfiles_to; /* Requestor address (this is where mail should be sent). */ static char *zQrequestor; /* User name. */ static const char *zQuser; /* System name. */ static const char *zQsystem; /* This is set by the N flag, meaning that no acknowledgement should be mailed on failure. */ static boolean fQno_ack; /* This is set by the n flag, meaning that acknowledgement should be mailed if the command succeeded. */ static boolean fQsuccess_ack; /* This is set by the B flag, meaning that command input should be mailed to the requestor if an error occurred. */ static boolean fQsend_input; /* This is set by the E flag, meaning that exec should be used to execute the command. */ static boolean fQuse_exec; /* The status should be copied to this file on the requesting host. */ static const char *zQstatus_file; #if ALLOW_SH_EXECUTION /* This is set by the e flag, meaning that sh should be used to execute the command. */ static boolean fQuse_sh; #endif /* ALLOW_SH_EXECUTION */ static int iqcmd P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqout P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqfile P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqrequestor P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iquser P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); static int iqset P((pointer puuconf, int argc, char **argv, pointer pvar, pointer pinfo)); /* We are lax about the number of arguments the functions accept, because there is a lot of variation in what other (buggy) UUCP packages generate. Unused arguments are ignored. */ static const struct uuconf_cmdtab asQcmds[] = { { "C", UUCONF_CMDTABTYPE_FN | 0, NULL, iqcmd }, { "I", UUCONF_CMDTABTYPE_STRING, (pointer) &zQinput, NULL }, { "O", UUCONF_CMDTABTYPE_FN | 0, NULL, iqout }, { "F", UUCONF_CMDTABTYPE_FN | 0, NULL, iqfile }, { "R", UUCONF_CMDTABTYPE_FN | 0, NULL, iqrequestor }, { "U", UUCONF_CMDTABTYPE_FN | 0, NULL, iquser }, { "N", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQno_ack, iqset }, { "n", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQsuccess_ack, iqset }, { "B", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQsend_input, iqset }, #if ALLOW_SH_EXECUTION { "e", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQuse_sh, iqset }, #endif { "E", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQuse_exec, iqset }, { "M", UUCONF_CMDTABTYPE_STRING, (pointer) &zQstatus_file, NULL }, { NULL, 0, NULL, NULL } }; /* Handle the C command: store off the arguments. */ /*ARGSUSED*/ static int iqcmd (puuconf, argc, argv, pvar, pinfo) pointer puuconf; int argc; char **argv; pointer pvar; pointer pinfo; { int i; size_t clen; if (argc <= 1) return UUCONF_CMDTABRET_CONTINUE; azQargs = (char **) xmalloc (argc * sizeof (char *)); clen = 0; for (i = 1; i < argc; i++) { azQargs[i - 1] = zbufcpy (argv[i]); clen += strlen (argv[i]) + 1; } azQargs[i - 1] = NULL; zQcmd = (char *) xmalloc (clen); zQcmd[0] = '\0'; for (i = 1; i < argc - 1; i++) { strcat (zQcmd, argv[i]); strcat (zQcmd, " "); } strcat (zQcmd, argv[i]); return UUCONF_CMDTABRET_CONTINUE; } /* Handle the O command, which may have one or two arguments. */ /*ARGSUSED*/ static int iqout (puuconf, argc, argv, pvar, pinfo) pointer puuconf; int argc; char **argv; pointer pvar; pointer pinfo; { if (argc > 1) zQoutfile = zbufcpy (argv[1]); if (argc > 2) zQoutsys = zbufcpy (argv[2]); return UUCONF_CMDTABRET_CONTINUE; } /* Handle the F command, which may have one or two arguments. */ /*ARGSUSED*/ static int iqfile (puuconf, argc, argv, pvar, pinfo) pointer puuconf; int argc; char **argv; pointer pvar; pointer pinfo; { if (argc < 2) return UUCONF_CMDTABRET_CONTINUE; /* If this file is not in the spool directory, just ignore it. */ if (! fspool_file (argv[1])) return UUCONF_CMDTABRET_CONTINUE; ++cQfiles; azQfiles = (char **) xrealloc ((pointer) azQfiles, cQfiles * sizeof (char *)); azQfiles_to = (char **) xrealloc ((pointer) azQfiles_to, cQfiles * sizeof (char *)); azQfiles[cQfiles - 1] = zbufcpy (argv[1]); if (argc > 2) azQfiles_to[cQfiles - 1] = zbufcpy (argv[2]); else azQfiles_to[cQfiles - 1] = NULL; return UUCONF_CMDTABRET_CONTINUE; } /* Handle the R command, which may have one or two arguments. */ /*ARGSUSED*/ static int iqrequestor (puuconf, argc, argv, pvar, pinfo) pointer puuconf; int argc; char **argv; pointer pvar; pointer pinfo; { /* We normally have a single argument, which is the ``requestor'' address, to which we should send any success or error messages. Apparently the DOS program UUPC sends two arguments, which are the username and the host. */ if (argc == 2) zQrequestor = zbufcpy (argv[1]); else if (argc > 2) { zQrequestor = zbufalc (strlen (argv[1]) + strlen (argv[2]) + sizeof "!"); sprintf (zQrequestor, "%s!%s", argv[2], argv[1]); } return UUCONF_CMDTABRET_CONTINUE; } /* Handle the U command, which takes two arguments. */ /*ARGSUSED*/ static int iquser (puuconf, argc, argv, pvar, pinfo) pointer puuconf; int argc; char **argv; pointer pvar; pointer pinfo; { if (argc > 1) zQuser = argv[1]; if (argc > 2) zQsystem = argv[2]; return UUCONF_CMDTABRET_KEEP; } /* Handle various commands which just set boolean variables. */ /*ARGSUSED*/ static int iqset (puuconf, argc, argv, pvar, pinfo) pointer puuconf; int argc; char **argv; pointer pvar; pointer pinfo; { boolean *pf = (boolean *) pvar; *pf = TRUE; return UUCONF_CMDTABRET_CONTINUE; } /* The execution processing does a lot of things that have to be cleaned up. Rather than try to add the appropriate statements to each return point, we keep a set of flags indicating what has to be cleaned up. The actual clean up is done by the function uqcleanup. */ #define REMOVE_FILE (01) #define REMOVE_NEEDED (02) #define FREE_QINPUT (04) #define REMOVE_QINPUT (010) #define FREE_OUTPUT (020) #define FREE_MAIL (040) /* Process an execute file. The zfile argument is the name of the execute file. The zbase argument is the base name of zfile. The qsys argument describes the system it came from. The zcmd argument is the name of the command we are executing (from the -c option) or NULL if any command is OK. This sets *pfprocessed to TRUE if the file is ready to be executed. */ static void uqdo_xqt_file (puuconf, zfile, zbase, qsys, zlocalname, zcmd, pfprocessed) pointer puuconf; const char *zfile; const char *zbase; const struct uuconf_system *qsys; const char *zlocalname; const char *zcmd; boolean *pfprocessed; { char *zabsolute; boolean ferr; FILE *e; int iuuconf; int i; int iclean; const char *zmail; char *zoutput; char *zinput; boolean fbadname; char abtemp[CFILE_NAME_LEN]; char abdata[CFILE_NAME_LEN]; char *zerror; struct uuconf_system soutsys; const struct uuconf_system *qoutsys; boolean fshell; size_t clen; char *zfullcmd; boolean ftemp; *pfprocessed = FALSE; e = fopen (zfile, "r"); if (e == NULL) return; azQargs = NULL; zQcmd = NULL; zQinput = NULL; zQoutfile = NULL; zQoutsys = NULL; cQfiles = 0; azQfiles = NULL; azQfiles_to = NULL; zQrequestor = NULL; zQuser = NULL; zQsystem = NULL; fQno_ack = FALSE; fQsuccess_ack = FALSE; fQsend_input = FALSE; fQuse_exec = FALSE; zQstatus_file = NULL; #if ALLOW_SH_EXECUTION fQuse_sh = FALSE; #endif iuuconf = uuconf_cmd_file (puuconf, e, asQcmds, (pointer) zbase, (uuconf_cmdtabfn) NULL, (UUCONF_CMDTABFLAG_CASE | UUCONF_CMDTABFLAG_NOCOMMENTS), (pointer) NULL); (void) fclose (e); if (iuuconf != UUCONF_SUCCESS) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); /* If we got a non-transient error, we notify the administrator. We can't bounce it back to the original requestor, because we don't know how to read the file to figure out who it is (it would probably be possible to read the file and work it out, but it doesn't seem worth it for such an unlikely error). */ if (UUCONF_ERROR_VALUE (iuuconf) == UUCONF_SYNTAX_ERROR || UUCONF_ERROR_VALUE (iuuconf) == UUCONF_UNKNOWN_COMMAND) { const char *az[20]; char *znew; i = 0; az[i++] = "The execution file\n\t"; az[i++] = zfile; az[i++] = "\nfor system\n\t"; az[i++] = qsys->uuconf_zname; az[i++] = "\nwas corrupt. "; znew = zsysdep_save_corrupt_file (zfile); if (znew == NULL) { az[i++] = "The file could not be preserved.\n"; (void) remove (zfile); } else { az[i++] = "It has been moved to\n\t"; az[i++] = znew; az[i++] = "\n"; } (void) fsysdep_mail (OWNER, "Corrupt execution file", i, az); ubuffree (znew); } return; } iclean = 0; if (azQargs == NULL) { ulog (LOG_ERROR, "%s: No command given", zbase); uqcleanup (zfile, iclean | REMOVE_FILE); return; } if (zcmd != NULL) { if (strcmp (zcmd, azQargs[0]) != 0) { uqcleanup (zfile, iclean); return; } } else { /* If there is a lock file for this particular command already, it means that some other uuxqt is supposed to handle it. */ if (fsysdep_uuxqt_locked (azQargs[0])) { uqcleanup (zfile, iclean); return; } } /* Lock this particular file. */ if (! fsysdep_lock_uuxqt_file (zfile)) { uqcleanup (zfile, iclean); return; } zQunlock_file = zfile; /* Now that we have the file locked, make sure it still exists. Otherwise another uuxqt could have just finished processing it and removed the lock file. */ if (! fsysdep_file_exists (zfile)) { uqcleanup (zfile, iclean); return; } if (zQuser != NULL) ulog_user (zQuser); else if (zQrequestor != NULL) ulog_user (zQrequestor); else ulog_user ("unknown"); /* zQsystem, if it is set, comes from the execution file, which means that we do not trust it. We only retain it if qsys->uuconf_zname is a prefix of it, since that can happen with a job from an anonymous system on certain spool directory types, and is unlikely to cause any trouble anyhow. */ if (zQsystem == NULL || strncmp (zQsystem, qsys->uuconf_zname, strlen (qsys->uuconf_zname)) != 0) zQsystem = qsys->uuconf_zname; /* Make sure that all the required files exist, and get their full names in the spool directory. */ for (i = 0; i < cQfiles; i++) { char *zreal; zreal = zsysdep_spool_file_name (qsys, azQfiles[i], (pointer) NULL); if (zreal == NULL) { uqcleanup (zfile, iclean); return; } if (! fsysdep_file_exists (zreal)) { uqcleanup (zfile, iclean); return; } ubuffree (azQfiles[i]); azQfiles[i] = zbufcpy (zreal); ubuffree (zreal); } /* Lock the execution directory. */ if (! fsysdep_lock_uuxqt_dir (iQlock_seq)) { ulog (LOG_ERROR, "Could not lock execute directory"); uqcleanup (zfile, iclean); return; } fQunlock_directory = TRUE; iclean |= REMOVE_FILE | REMOVE_NEEDED; *pfprocessed = TRUE; /* Get the address to mail results to. Prepend the system from which the execute file originated, since mail addresses are relative to it. */ zmail = NULL; if (zQrequestor != NULL) zmail = zQrequestor; else if (zQuser != NULL) zmail = zQuser; if (zmail != NULL #if HAVE_INTERNET_MAIL && strchr (zmail, '@') == NULL #endif && strcmp (zQsystem, zlocalname) != 0) { char *zset; zset = zbufalc (strlen (zQsystem) + strlen (zmail) + 2); sprintf (zset, "%s!%s", zQsystem, zmail); zmail = zset; zQmail = zset; iclean |= FREE_MAIL; } /* The command "uucp" is handled specially. We make sure that the appropriate forwarding is permitted, and we add a -u argument to specify the user. */ if (strcmp (azQargs[0], "uucp") == 0) { char *zfrom, *zto; boolean fmany; char **azargs; const char *zuser; zfrom = NULL; zto = NULL; fmany = FALSE; /* Skip all the options, and get the from and to specs. We don't permit multiple arguments. */ for (i = 1; azQargs[i] != NULL; i++) { if (azQargs[i][0] == '-') { char *zopts; for (zopts = azQargs[i] + 1; *zopts != '\0'; zopts++) { /* The -g, -n, and -s options take an argument. */ if (*zopts == 'g' || *zopts == 'n' || *zopts == 's') { if (zopts[1] == '\0') ++i; break; } /* The -I, -u and -x options are not permitted. */ if (*zopts == 'I' || *zopts == 'u' || *zopts == 'x') { *zopts = 'r'; if (zopts[1] != '\0') zopts[1] = '\0'; else { ++i; azQargs[i] = zbufcpy ("-r"); } break; } } } else if (zfrom == NULL) zfrom = azQargs[i]; else if (zto == NULL) zto = azQargs[i]; else { fmany = TRUE; break; } } /* Add the -u argument. This is required to let uucp do the correct permissions checking on the file transfer. */ for (i = 0; azQargs[i] != NULL; i++) ; azargs = (char **) xmalloc ((i + 2) * sizeof (char *)); azargs[0] = azQargs[0]; zuser = zQuser; if (zuser == NULL) zuser = "uucp"; azargs[1] = zbufalc (strlen (zQsystem) + strlen (zuser) + sizeof "-u!"); sprintf (azargs[1], "-u%s!%s", zQsystem, zuser); memcpy (azargs + 2, azQargs + 1, i * sizeof (char *)); xfree ((pointer) azQargs); azQargs = azargs; /* Find the uucp binary. */ zabsolute = zsysdep_find_command ("uucp", qsys->uuconf_pzcmds, qsys->uuconf_pzpath, &ferr); if (zabsolute == NULL && ! ferr) { const char *azcmds[2]; /* If "uucp" is not a permitted command, then the forwarding entries must be set. */ if (! fqforward (zfrom, qsys->uuconf_pzforward_from, "from", zmail) || ! fqforward (zto, qsys->uuconf_pzforward_to, "to", zmail)) { uqcleanup (zfile, iclean); return; } /* If "uucp" is not a permitted command, then only uucp requests with a single source are permitted, since that is all that will be generated by uucp or uux. */ if (fmany) { ulog (LOG_ERROR, "Bad uucp request %s", zQcmd); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because it was an"; az[i++] = " unsupported uucp request.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } uqcleanup (zfile, iclean); return; } azcmds[0] = "uucp"; azcmds[1] = NULL; zabsolute = zsysdep_find_command ("uucp", (char **) azcmds, qsys->uuconf_pzpath, &ferr); } if (zabsolute == NULL) { if (! ferr) ulog (LOG_ERROR, "Can't find uucp executable"); uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } } else { /* Get the pathname to execute. */ zabsolute = zsysdep_find_command (azQargs[0], qsys->uuconf_pzcmds, qsys->uuconf_pzpath, &ferr); if (zabsolute == NULL) { if (ferr) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } /* Not permitted. Send mail to requestor. */ ulog (LOG_ERROR, "Not permitted to execute %s", azQargs[0]); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are not"; az[i++] = " permitted to execute\n\t"; az[i++] = azQargs[0]; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } iclean = isave_files (qsys, zmail, zfile, iclean); uqcleanup (zfile, iclean); return; } } ubuffree (azQargs[0]); azQargs[0] = zabsolute; for (i = 1; azQargs[i] != NULL; i++) { char *zlocal; zlocal = zsysdep_xqt_local_file (qsys, azQargs[i]); if (zlocal != NULL) { ubuffree (azQargs[i]); azQargs[i] = zlocal; } } #if ! ALLOW_FILENAME_ARGUMENTS /* Check all the arguments to make sure they don't try to specify files they are not permitted to access. */ for (i = 1; azQargs[i] != NULL; i++) { if (! fsysdep_xqt_check_file (qsys, azQargs[i])) { if (zmail != NULL && ! fQno_ack) { const char *az[20]; const char *zfailed; zfailed = azQargs[i]; i = 0; az[i++] = "Your execution request failed because you are not"; az[i++] = " permitted to refer to file\n\t"; az[i++] = zfailed; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } iclean = isave_files (qsys, zmail, zfile, iclean); uqcleanup (zfile, iclean); return; } } #endif /* ! ALLOW_FILENAME_ARGUMENTS */ ulog (LOG_NORMAL, "Executing %s (%s)", zbase, zQcmd); if (zQinput != NULL) { boolean fspool; char *zreal; fspool = fspool_file (zQinput); if (! fspool) zreal = zsysdep_local_file (zQinput, qsys->uuconf_zpubdir, &fbadname); else { zreal = zsysdep_spool_file_name (qsys, zQinput, (pointer) NULL); fbadname = FALSE; } if (zreal == NULL && ! fbadname) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } if (zreal != NULL) { zQinput = zreal; iclean |= FREE_QINPUT; if (fspool) iclean |= REMOVE_QINPUT; } if (zreal == NULL || (! fspool && ! fin_directory_list (zQinput, qsys->uuconf_pzremote_send, qsys->uuconf_zpubdir, TRUE, TRUE, (const char *) NULL))) { ulog (LOG_ERROR, "Not permitted to read %s", zQinput); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are"; az[i++] = " not permitted to read\n\t"; az[i++] = zQinput; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } uqcleanup (zfile, iclean); return; } } zoutput = NULL; if (zQoutfile == NULL) qoutsys = NULL; else if (zQoutsys != NULL && strcmp (zQoutsys, zlocalname) != 0) { char *zdata; /* The output file is destined for some other system, so we must use a temporary file to catch standard output. */ if (strcmp (zQoutsys, qsys->uuconf_zname) == 0) qoutsys = qsys; else { iuuconf = uuconf_system_info (puuconf, zQoutsys, &soutsys); if (iuuconf != UUCONF_SUCCESS) { if (iuuconf != UUCONF_NOT_FOUND) { ulog_uuconf (LOG_ERROR, puuconf, iuuconf); uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } if (! funknown_system (puuconf, zQoutsys, &soutsys)) { ulog (LOG_ERROR, "Can't send standard output to unknown system %s", zQoutsys); /* We don't send mail to unknown systems, either. Maybe we should. */ uqcleanup (zfile, iclean); return; } } qoutsys = &soutsys; } zdata = zsysdep_data_file_name (qoutsys, zlocalname, BDEFAULT_UUX_GRADE, FALSE, abtemp, abdata, (char *) NULL); if (zdata == NULL) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } zoutput = zdata; zQoutput = zoutput; iclean |= FREE_OUTPUT; } else { boolean fok; qoutsys = NULL; /* If we permitted the standard output to be redirected into the spool directory, people could set up phony commands. */ if (fspool_file (zQoutfile)) fok = FALSE; else { zoutput = zsysdep_local_file (zQoutfile, qsys->uuconf_zpubdir, &fbadname); if (zoutput == NULL) { if (! fbadname) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } fok = FALSE; } else { ubuffree (zQoutfile); zQoutfile = zoutput; /* Make sure it's OK to receive this file. */ fok = fin_directory_list (zQoutfile, qsys->uuconf_pzremote_receive, qsys->uuconf_zpubdir, TRUE, FALSE, (const char *) NULL); } } if (! fok) { ulog (LOG_ERROR, "Not permitted to write to %s", zQoutfile); if (zmail != NULL && ! fQno_ack) { const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are"; az[i++] = " not permitted to write to\n\t"; az[i++] = zQoutfile; az[i++] = "\non this system.\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } uqcleanup (zfile, iclean); return; } } /* Move the required files to the execution directory if necessary. */ zinput = zQinput; if (! fsysdep_move_uuxqt_files (cQfiles, (const char **) azQfiles, (const char **) azQfiles_to, TRUE, iQlock_seq, &zinput)) { /* If we get an error, try again later. */ uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } if (zQinput != NULL && strcmp (zQinput, zinput) != 0) { if ((iclean & FREE_QINPUT) != 0) ubuffree (zQinput); zQinput = zinput; iclean |= FREE_QINPUT; } #if ALLOW_SH_EXECUTION fshell = fQuse_sh; #else fshell = FALSE; #endif /* Get a shell command which uses the full path of the command to execute. */ clen = 0; for (i = 0; azQargs[i] != NULL; i++) clen += strlen (azQargs[i]) + 1; zfullcmd = zbufalc (clen); strcpy (zfullcmd, azQargs[0]); for (i = 1; azQargs[i] != NULL; i++) { strcat (zfullcmd, " "); strcat (zfullcmd, azQargs[i]); } if (! fsysdep_execute (qsys, zQuser == NULL ? (const char *) "uucp" : zQuser, (const char **) azQargs, zfullcmd, zQinput, zoutput, fshell, iQlock_seq, &zerror, &ftemp)) { ubuffree (zfullcmd); (void) fsysdep_move_uuxqt_files (cQfiles, (const char **) azQfiles, (const char **) azQfiles_to, FALSE, iQlock_seq, (char **) NULL); if (ftemp) { ulog (LOG_NORMAL, "Will retry later (%s)", zbase); if (zoutput != NULL) (void) remove (zoutput); if (zerror != NULL) { (void) remove (zerror); ubuffree (zerror); } uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED)); *pfprocessed = FALSE; return; } ulog (LOG_NORMAL, "Execution failed (%s)", zbase); if (zmail != NULL && ! fQno_ack) { const char **pz; int cgot; FILE *eerr; int istart; cgot = 20; pz = (const char **) xmalloc (cgot * sizeof (const char *)); i = 0; pz[i++] = "Execution request failed:\n\t"; pz[i++] = zQcmd; pz[i++] = "\n"; if (zerror == NULL) eerr = NULL; else eerr = fopen (zerror, "r"); if (eerr == NULL) { pz[i++] = "There was no output on standard error\n"; istart = i; } else { char *zline; size_t cline; pz[i++] = "Standard error output was:\n"; istart = i; zline = NULL; cline = 0; while (getline (&zline, &cline, eerr) > 0) { if (i >= cgot) { cgot += 20; pz = ((const char **) xrealloc ((pointer) pz, cgot * sizeof (const char *))); } pz[i++] = zbufcpy (zline); } (void) fclose (eerr); xfree ((pointer) zline); } (void) fsysdep_mail (zmail, "Execution failed", i, pz); for (; istart < i; istart++) ubuffree ((char *) pz[istart]); xfree ((pointer) pz); } if (qoutsys != NULL) (void) remove (zoutput); iclean = isave_files (qsys, zmail, zfile, iclean); } else { ubuffree (zfullcmd); if (zmail != NULL && fQsuccess_ack) { const char *az[20]; i = 0; az[i++] = "\nExecution request succeeded:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution succeded", i, az); } /* Now we may have to uucp the output to some other machine. */ if (qoutsys != NULL) { struct scmd s; /* Fill in the command structure. */ s.bcmd = 'S'; s.bgrade = BDEFAULT_UUX_GRADE; s.pseq = NULL; s.zfrom = abtemp; s.zto = zQoutfile; if (zQuser != NULL) s.zuser = zQuser; else s.zuser = "uucp"; if (zmail != NULL && fQsuccess_ack) s.zoptions = "Cn"; else s.zoptions = "C"; s.ztemp = abtemp; s.imode = 0666; if (zmail != NULL && fQsuccess_ack) s.znotify = zmail; else s.znotify = ""; s.cbytes = -1; s.zcmd = NULL; s.ipos = 0; ubuffree (zsysdep_spool_commands (qoutsys, BDEFAULT_UUX_GRADE, 1, &s)); } } if (zerror != NULL) { (void) remove (zerror); ubuffree (zerror); } uqcleanup (zfile, iclean); } /* If we have enough disk space, save the data files so that the UUCP administrator can examine them. Send a mail message listing the saved files. */ static int isave_files (qsys, zmail, zfile, iclean) const struct uuconf_system *qsys; const char *zmail; const char *zfile; int iclean; { long cspace; char *zsavecmd; char **pzsave; int c; int ifile; char *zsaveinput; const char **pz; int i; /* Save the files if there is 1.5 times the amount of required free space. */ cspace = csysdep_bytes_free (zfile); if (cspace == -1) cspace = FREE_SPACE_DELTA; cspace -= qsys->uuconf_cfree_space + qsys->uuconf_cfree_space / 2; if (cspace < 0) return iclean; zsavecmd = zsysdep_save_failed_file (zfile); if (zsavecmd == NULL) return iclean; c = 1; pzsave = (char **) xmalloc (cQfiles * sizeof (char *)); for (ifile = 0; ifile < cQfiles; ifile++) { if (azQfiles[ifile] != NULL) { ++c; pzsave[ifile] = zsysdep_save_failed_file (azQfiles[ifile]); if (pzsave[ifile] == NULL) { ubuffree (zsavecmd); for (i = 0; i < ifile; i++) if (azQfiles[i] != NULL) ubuffree (pzsave[i]); xfree ((pointer) pzsave); return iclean; } } } zsaveinput = NULL; if ((iclean & REMOVE_QINPUT) != 0 && fsysdep_file_exists (zQinput)) { zsaveinput = zsysdep_save_failed_file (zQinput); if (zsaveinput == NULL) { ubuffree (zsavecmd); for (i = 0; i < cQfiles; i++) if (azQfiles[i] != NULL) ubuffree (pzsave[i]); xfree ((pointer) pzsave); return iclean; } } pz = (const char **) xmalloc ((20 + 2 * cQfiles) * sizeof (char *)); i = 0; pz[i++] = "A UUCP execution request failed:\n\t"; pz[i++] = zQcmd; if (zmail != NULL) { pz[i++] = "\nThe request was made by\n\t"; pz[i++] = zmail; } else { pz[i++] = "\nThe request came from system\n\t"; pz[i++] = qsys->uuconf_zname; } if (c == 1 && zsaveinput == NULL) pz[i++] = "\nThe following file has been saved:\n\t"; else pz[i++] = "\nThe following files have been saved:\n\t"; pz[i++] = zsavecmd; for (ifile = 0; ifile < cQfiles; ifile++) { if (azQfiles[ifile] != NULL) { pz[i++] = "\n\t"; pz[i++] = pzsave[ifile]; } } if (zsaveinput != NULL) { pz[i++] = "\n\t"; pz[i++] = zsaveinput; } pz[i++] = "\n"; (void) fsysdep_mail (OWNER, "UUCP execution files saved after failure", i, pz); xfree ((pointer) pz); ubuffree (zsavecmd); for (ifile = 0; ifile < cQfiles; ifile++) if (azQfiles[ifile] != NULL) ubuffree (pzsave[ifile]); xfree ((pointer) pzsave); ubuffree (zsaveinput); return iclean &~ (REMOVE_FILE | REMOVE_NEEDED); } /* Clean up the results of uqdo_xqt_file. */ static void uqcleanup (zfile, iflags) const char *zfile; int iflags; { int i; DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, "uqcleanup: %s, %d", zfile, iflags); if (zQunlock_file != NULL) { (void) fsysdep_unlock_uuxqt_file (zQunlock_file); zQunlock_file = NULL; } if ((iflags & REMOVE_FILE) != 0) (void) remove (zfile); if ((iflags & REMOVE_NEEDED) != 0) { for (i = 0; i < cQfiles; i++) { if (azQfiles[i] != NULL) (void) remove (azQfiles[i]); } if ((iflags & REMOVE_QINPUT) != 0) (void) remove (zQinput); } if ((iflags & FREE_QINPUT) != 0) ubuffree (zQinput); if ((iflags & FREE_OUTPUT) != 0) ubuffree (zQoutput); if ((iflags & FREE_MAIL) != 0) ubuffree (zQmail); if (fQunlock_directory) { (void) fsysdep_unlock_uuxqt_dir (iQlock_seq); fQunlock_directory = FALSE; } for (i = 0; i < cQfiles; i++) { ubuffree (azQfiles[i]); ubuffree (azQfiles_to[i]); } ubuffree (zQoutfile); ubuffree (zQoutsys); ubuffree (zQrequestor); if (azQargs != NULL) { for (i = 0; azQargs[i] != NULL; i++) ubuffree (azQargs[i]); xfree ((pointer) azQargs); azQargs = NULL; } xfree ((pointer) zQcmd); zQcmd = NULL; xfree ((pointer) azQfiles); azQfiles = NULL; xfree ((pointer) azQfiles_to); azQfiles_to = NULL; } /* Check whether forwarding is permitted. */ static boolean fqforward (zfile, pzallowed, zlog, zmail) const char *zfile; char **pzallowed; const char *zlog; const char *zmail; { const char *zexclam; zexclam = strchr (zfile, '!'); if (zexclam != NULL) { size_t clen; char *zsys; boolean fret; clen = zexclam - zfile; zsys = zbufalc (clen + 1); memcpy (zsys, zfile, clen); zsys[clen] = '\0'; fret = FALSE; if (pzallowed != NULL) { char **pz; for (pz = pzallowed; *pz != NULL; pz++) { if (strcmp (*pz, "ANY") == 0 || strcmp (*pz, zsys) == 0) { fret = TRUE; break; } } } if (! fret) { ulog (LOG_ERROR, "Not permitted to forward %s %s (%s)", zlog, zsys, zQcmd); if (zmail != NULL && ! fQno_ack) { int i; const char *az[20]; i = 0; az[i++] = "Your execution request failed because you are"; az[i++] = " not permitted to forward files\n"; az[i++] = zlog; az[i++] = " the system\n\t"; az[i++] = zsys; az[i++] = "\n"; az[i++] = "Execution requested was:\n\t"; az[i++] = zQcmd; az[i++] = "\n"; (void) fsysdep_mail (zmail, "Execution failed", i, az); } } ubuffree (zsys); return fret; } return TRUE; }