NetBSD/gnu/libexec/uucp/uuxqt.c

1371 lines
33 KiB
C
Raw Normal View History

1993-03-21 12:45:37 +03:00
/* uuxqt.c
Run uux commands.
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: uuxqt.c,v $
Revision 1.1.1.1 1993/03/21 09:45:37 cgd
initial import of 386bsd-0.1 sources
Revision 1.43 1992/03/17 04:42:41 ian
Correct bug in previous patch
Revision 1.42 1992/03/17 03:15:40 ian
Pass command to fsysdep_execute as first element of argument array
Revision 1.41 1992/03/17 01:07:15 ian
Miscellaneous cleanup
Revision 1.40 1992/03/16 19:44:45 ian
Cast result of alloca
Revision 1.39 1992/03/15 04:51:17 ian
Keep an array of signals we've received rather than a single variable
Revision 1.38 1992/03/15 01:54:46 ian
All execs are now done in isspawn, all waits are done in iswait
Revision 1.37 1992/03/12 19:54:43 ian
Debugging based on types rather than number
Revision 1.36 1992/03/11 22:34:25 ian
Chip Salzenberg: support Internet mail addresses in uuxqt replies
Revision 1.35 1992/03/11 22:06:37 ian
Marty Shannon: added max-uuxqts command
Revision 1.34 1992/03/11 17:04:53 ian
Jon Zeeff: retry execution later if temporary failure
Revision 1.33 1992/03/11 02:09:19 ian
Correct bug in previous change
Revision 1.32 1992/03/04 02:32:26 ian
Handle executions on local system
Revision 1.31 1992/02/29 04:07:08 ian
Added -j option to uucp and uux
Revision 1.30 1992/02/29 01:06:59 ian
Chip Salzenberg: recheck file permissions before sending
Revision 1.29 1992/02/27 05:40:54 ian
T. William Wells: detach from controlling terminal, handle signals safely
Revision 1.28 1992/02/24 04:58:47 ian
Only permit files to be received into directories that are world-writeable
Revision 1.27 1992/02/23 19:50:50 ian
Handle READ and WRITE in Permissions correctly
Revision 1.26 1992/02/23 03:26:51 ian
Overhaul to use automatic configure shell script
Revision 1.25 1992/02/18 19:03:02 ian
Pass fdaemon argument correctly to usysdep_initialize
Revision 1.24 1992/02/18 04:53:26 ian
T. William Wells: make sure sh execution uses absolute path
Revision 1.23 1992/02/14 21:32:50 ian
Niels Baggesen: under HAVE_BNU_LOGGING, don't lost system name when dieing
Revision 1.22 1992/02/08 22:33:32 ian
Only get the current working directory if it's going to be needed
Revision 1.21 1992/02/08 20:33:57 ian
Handle all possible signals raised by abort
Revision 1.20 1992/02/08 03:54:18 ian
Include <string.h> only in <uucp.h>, added 1992 copyright
Revision 1.19 1992/01/16 17:48:41 ian
Niels Baggesen: was checking strcmp return incorrectly
Revision 1.18 1992/01/15 07:06:29 ian
Set configuration directory in Makefile rather than sysdep.h
Revision 1.17 1992/01/05 03:09:17 ian
Changed abProgram and abVersion to non const to avoid compiler bug
Revision 1.16 1992/01/04 21:43:24 ian
Chip Salzenberg: added ALLOW_FILENAME_ARGUMENTS to permit them
Revision 1.15 1992/01/04 04:12:54 ian
David J. Fiander: make sure execution arguments are not bad file names
Revision 1.14 1991/12/29 04:22:41 ian
The mailing address was not getting initialized
Revision 1.13 1991/12/29 04:04:18 ian
Added a bunch of extern definitions
Revision 1.12 1991/12/20 00:17:38 ian
Don't process execute files for unknown systems
Revision 1.11 1991/12/19 03:52:07 ian
David Nugent: rescan the list of execute files until nothing can be done
Revision 1.10 1991/12/18 03:54:14 ian
Made error messages to terminal appear more normal
Revision 1.9 1991/12/17 04:55:01 ian
David Nugent: ignore SIGHUP in uucico and uuxqt
Revision 1.8 1991/12/09 18:49:06 ian
Richard Todd: the requestor address is relative to the requesting system
Revision 1.7 1991/12/06 23:42:18 ian
Don't acknowledge success by default
Revision 1.6 1991/12/01 01:28:44 ian
Mitch Mitchell: fixed comment listing supported commands
Revision 1.5 1991/11/21 22:17:06 ian
Add version string, print version when printing usage
Revision 1.4 1991/11/07 20:52:33 ian
Chip Salzenberg: pass command as single argument to /bin/sh
Revision 1.3 1991/09/19 16:15:58 ian
Chip Salzenberg: configuration option for permitting execution via sh
Revision 1.2 1991/09/19 02:30:37 ian
From Chip Salzenberg: check whether signal is ignored differently
Revision 1.1 1991/09/10 19:40:31 ian
Initial revision
*/
#include "uucp.h"
#if USE_RCS_ID
char uuxqt_rcsid[] = "$Id: uuxqt.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
#endif
#include <errno.h>
#include <ctype.h>
#include "getopt.h"
#include "system.h"
#include "sysdep.h"
/* External functions. */
extern int fclose ();
/* The program name. */
char abProgram[] = "uuxqt";
/* Static variables used to unlock things if we get a fatal error. */
static int iQunlock_seq = -1;
static const char *zQunlock_cmd;
static const char *zQunlock_file;
static boolean fQunlock_directory;
/* Local functions. */
static void uqusage P((void));
static void uqabort P((void));
static void uqdo_xqt_file P((const char *zfile,
const struct ssysteminfo *qsys,
const char *zcmd, boolean *pfprocessed));
static void uqcleanup P((const char *zfile, int iflags));
/* Long getopt options. */
static const struct option asQlongopts[] = { { NULL, 0, NULL, 0 } };
const struct option *_getopt_long_options = asQlongopts;
int
main (argc, argv)
int argc;
char **argv;
{
int iopt;
/* 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;
boolean fany;
const char *z;
const char *zgetsys;
boolean ferr;
struct ssysteminfo sreadsys;
const struct ssysteminfo *qreadsys;
while ((iopt = getopt (argc, argv, "c:I:s:x:")) != EOF)
{
switch (iopt)
{
case 'c':
/* Set the type of command to execute. */
zcmd = optarg;
break;
case 'I':
/* Set the configuration file name. */
zconfig = optarg;
break;
case 's':
zdosys = optarg;
break;
case 'x':
#if DEBUG > 1
/* Set the debugging level. */
iDebug |= idebug_parse (optarg);
#endif
break;
case 0:
/* Long option found and flag set. */
break;
default:
uqusage ();
break;
}
}
if (optind != argc)
uqusage ();
uread_config (zconfig);
#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 (TRUE, FALSE);
ulog_to_file (TRUE);
ulog_fatal_fn (uqabort);
/* Limit the number of uuxqt processes, and make sure we're the only
uuxqt daemon running for this command. */
iQunlock_seq = isysdep_lock_uuxqt (zcmd);
if (iQunlock_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 ())
{
ulog_close ();
usysdep_exit (FALSE);
}
qreadsys = NULL;
while ((z = zsysdep_get_xqt (&zgetsys, &ferr)) != NULL)
{
char *zcopy;
boolean fprocessed;
const struct ssysteminfo *qusesys;
#if ! HAVE_ALLOCA
/* Clear out any accumulated alloca buffers. */
(void) alloca (0);
#endif
/* It would be more efficient to pass zdosys down to the
routines which retrieve execute files. */
if (zdosys != NULL && strcmp (zdosys, zgetsys) != 0)
continue;
if (strcmp (zgetsys, zLocalname) == 0)
qusesys = &sLocalsys;
else
{
if (qreadsys == NULL
|| strcmp (qreadsys->zname, zgetsys) != 0)
{
if (fread_system_info (zgetsys, &sreadsys))
qreadsys = &sreadsys;
else
{
if (! fUnknown_ok)
{
ulog (LOG_ERROR,
"%s: Execute file for unknown system %s",
z, zgetsys);
(void) remove (z);
continue;
}
qreadsys = &sUnknown;
sUnknown.zname = xstrdup (zgetsys);
}
if (! fsysdep_make_spool_dir (qreadsys))
continue;
}
qusesys = qreadsys;
}
/* If we've received a signal, get out of the loop. */
if (FGOT_SIGNAL ())
break;
zcopy = xstrdup (z);
ulog_system (qusesys->zname);
uqdo_xqt_file (zcopy, qusesys, zcmd, &fprocessed);
ulog_system ((const char *) NULL);
ulog_user ((const char *) NULL);
if (fprocessed)
fany = TRUE;
xfree ((pointer) zcopy);
}
usysdep_get_xqt_free ();
}
while (fany && ! FGOT_SIGNAL ());
(void) fsysdep_unlock_uuxqt (iQunlock_seq, zcmd);
iQunlock_seq = -1;
ulog_close ();
if (FGOT_SIGNAL ())
ferr = TRUE;
usysdep_exit (! ferr);
/* Avoid errors about not returning a value. */
return 0;
}
static void
uqusage ()
{
fprintf (stderr,
"Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n",
abVersion);
fprintf (stderr,
"Usage: uuxqt [-c cmd] [-I file] [-s system] [-x debug]\n");
fprintf (stderr,
" -c cmd: Set type of command to execute\n");
fprintf (stderr,
" -s system: Execute commands only for named system\n");
fprintf (stderr,
" -x debug: Set debugging level (0 for none, 9 is max)\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);
}
/* This is the abort function called when we get a fatal error. */
static void
uqabort ()
{
#if ! HAVE_BNU_LOGGING
/* When using BNU 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 ();
if (zQunlock_file != NULL)
(void) fsysdep_unlock_uuxqt_file (zQunlock_file);
if (iQunlock_seq >= 0)
(void) fsysdep_unlock_uuxqt (iQunlock_seq, zQunlock_cmd);
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 const char **azQargs;
/* Command as a complete string. */
static char *zQcmd;
/* Standard input file name. */
static const char *zQinput;
/* Standard output file name. */
static const char *zQoutfile;
/* Standard output system. */
static const 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 const 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 enum tcmdtabret tqcmd P((int argc, char **argv, pointer pvar,
const char *zerr));
static enum tcmdtabret tqout P((int argc, char **argv, pointer pvar,
const char *zerr));
static enum tcmdtabret tqfile P((int argc, char **argv, pointer pvar,
const char *zerr));
static enum tcmdtabret tquser P((int argc, char **argv, pointer pvar,
const char *zerr));
static enum tcmdtabret tqset P((int argc, char **argv, pointer pvar,
const char *zerr));
static struct scmdtab asQcmds[] =
{
{ "C", CMDTABTYPE_FN | 0, NULL, tqcmd },
{ "I", CMDTABTYPE_STRING, (pointer) &zQinput, NULL },
{ "O", CMDTABTYPE_FN | 0, NULL, tqout },
{ "F", CMDTABTYPE_FN | 0, NULL, tqfile },
{ "R", CMDTABTYPE_STRING, (pointer) &zQrequestor, NULL },
{ "U", CMDTABTYPE_FN | 3, NULL, tquser },
{ "N", CMDTABTYPE_FN | 1, (pointer) &fQno_ack, tqset },
{ "n", CMDTABTYPE_FN | 1, (pointer) &fQsuccess_ack, tqset },
{ "B", CMDTABTYPE_FN | 1, (pointer) &fQsend_input, tqset },
#if ALLOW_SH_EXECUTION
{ "e", CMDTABTYPE_FN | 1, (pointer) &fQuse_sh, tqset },
#endif
{ "E", CMDTABTYPE_FN | 1, (pointer) &fQuse_exec, tqset },
{ "M", CMDTABTYPE_STRING, (pointer) &zQstatus_file, NULL },
{ NULL, 0, NULL, NULL }
};
/* Handle the C command: store off the arguments. */
/*ARGSUSED*/
static enum tcmdtabret
tqcmd (argc, argv, pvar, zerr)
int argc;
char **argv;
pointer pvar;
const char *zerr;
{
int i;
int clen;
if (argc <= 1)
return CMDTABRET_FREE;
azQargs = (const char **) xmalloc (argc * sizeof (char *));
clen = 0;
for (i = 1; i < argc; i++)
{
azQargs[i - 1] = 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 CMDTABRET_CONTINUE;
}
/* Handle the O command, which may have one or two arguments. */
/*ARGSUSED*/
static enum tcmdtabret
tqout (argc, argv, pvar, zerr)
int argc;
char **argv;
pointer pvar;
const char *zerr;
{
if (argc != 2 && argc != 3)
{
ulog (LOG_ERROR, "%s: %s: Wrong number of arguments",
zerr, argv[0]);
return CMDTABRET_FREE;
}
zQoutfile = argv[1];
if (argc == 3)
zQoutsys = argv[2];
return CMDTABRET_CONTINUE;
}
/* Handle the F command, which may have one or two arguments. */
/*ARGSUSED*/
static enum tcmdtabret
tqfile (argc, argv, pvar, zerr)
int argc;
char **argv;
pointer pvar;
const char *zerr;
{
if (argc != 2 && argc != 3)
{
ulog (LOG_ERROR, "%s: %s: Wrong number of arguments",
zerr, argv[0]);
return CMDTABRET_FREE;
}
/* If this file is not in the spool directory, just ignore it. */
if (! fspool_file (argv[1]))
return CMDTABRET_FREE;
++cQfiles;
azQfiles = (char **) xrealloc ((pointer) azQfiles,
cQfiles * sizeof (char *));
azQfiles_to = (char **) xrealloc ((pointer) azQfiles_to,
cQfiles * sizeof (char *));
azQfiles[cQfiles - 1] = xstrdup (argv[1]);
if (argc == 3)
azQfiles_to[cQfiles - 1] = xstrdup (argv[2]);
else
azQfiles_to[cQfiles - 1] = NULL;
return CMDTABRET_FREE;
}
/* Handle the U command, which takes two arguments. */
/*ARGSUSED*/
static enum tcmdtabret
tquser (argc, argv, pvar, zerr)
int argc;
char **argv;
pointer pvar;
const char *zerr;
{
zQuser = argv[1];
zQsystem = argv[2];
return CMDTABRET_CONTINUE;
}
/* Handle various commands which just set boolean variables. */
/*ARGSUSED*/
static enum tcmdtabret
tqset (argc, argv, pvar, zerr)
int argc;
char **argv;
pointer pvar;
const char *zerr;
{
boolean *pf = (boolean *) pvar;
*pf = TRUE;
return CMDTABRET_FREE;
}
/* 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)
/* Process an execute file. The zfile argument is the name of the
execute file. 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 (zfile, qsys, zcmd, pfprocessed)
const char *zfile;
const struct ssysteminfo *qsys;
const char *zcmd;
boolean *pfprocessed;
{
const char *zcmds;
const char *zabsolute;
boolean ferr;
FILE *e;
int i;
int iclean;
const char *zmail;
const char *zoutput;
char abtemp[CFILE_NAME_LEN];
char abdata[CFILE_NAME_LEN];
const char *zerror;
struct ssysteminfo soutsys;
const struct ssysteminfo *qoutsys;
boolean fshell;
char *zfullcmd;
boolean ftemp;
*pfprocessed = FALSE;
/* If we're not permitted to execute anything for this system,
we can just clobber the file without even looking at it. */
zcmds = qsys->zcmds;
if (*zcmds == '\0')
{
ulog (LOG_ERROR, "%s: No commands permitted for system %s",
zfile, qsys->zname);
(void) remove (zfile);
return;
}
/* If we are only willing to execute a particular command, and it
is not one of those accepted by this system, quit now. */
if (zcmd != NULL
&& strcmp (zcmds, "ALL") != 0
&& strstr (zcmds, zcmd) == NULL)
return;
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
uprocesscmds (e, (struct smulti_file *) NULL, asQcmds, zfile, 0);
(void) fclose (e);
iclean = 0;
if (azQargs == NULL)
{
ulog (LOG_ERROR, "%s: No command given", zfile);
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;
if (zQuser != NULL)
ulog_user (zQuser);
else if (zQrequestor != NULL)
ulog_user (zQrequestor);
else
ulog_user ("unknown");
/* Make sure that all the required files exist, and get their
full names in the spool directory. */
for (i = 0; i < cQfiles; i++)
{
const char *zreal;
zreal = zsysdep_spool_file_name (qsys, azQfiles[i]);
if (zreal == NULL)
{
uqcleanup (zfile, iclean);
return;
}
if (! fsysdep_file_exists (zreal))
{
uqcleanup (zfile, iclean);
return;
}
xfree ((pointer) azQfiles[i]);
azQfiles[i] = xstrdup (zreal);
}
/* See if we need the execute directory, and lock it if we do. */
for (i = 0; i < cQfiles; i++)
{
int itries;
if (azQfiles_to[i] == NULL)
continue;
for (itries = 0; itries < 5; itries++)
{
if (fsysdep_lock_uuxqt_dir ())
break;
usysdep_sleep (30);
}
if (itries >= 5)
{
ulog (LOG_ERROR, "Could not lock execute directory");
uqcleanup (zfile, iclean);
return;
}
fQunlock_directory = TRUE;
break;
}
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
&& zQsystem != NULL
#if HAVE_INTERNET_MAIL
&& strchr (zmail, '@') == NULL
#endif
&& strcmp (zQsystem, zLocalname) != 0)
{
char *zset;
zset = (char *) alloca (strlen (zQsystem) + strlen (zmail) + 2);
sprintf (zset, "%s!%s", zQsystem, zmail);
zmail = zset;
}
/* Get the pathname to execute. */
zabsolute = zsysdep_find_command (azQargs[0], zcmds, qsys->zpath,
&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);
}
uqcleanup (zfile, iclean);
return;
}
{
char *zcopy;
zcopy = (char *) alloca (strlen (zabsolute) + 1);
strcpy (zcopy, zabsolute);
zabsolute = zcopy;
}
azQargs[0] = zabsolute;
#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);
}
uqcleanup (zfile, iclean);
return;
}
}
#endif /* ! ALLOW_FILENAME_ARGUMENTS */
ulog (LOG_NORMAL, "Executing %s (%s)", zfile, zQcmd);
if (zQinput != NULL)
{
boolean fspool;
fspool = fspool_file (zQinput);
if (fspool)
zQinput = zsysdep_spool_file_name (qsys, zQinput);
else
zQinput = zsysdep_real_file_name (qsys, zQinput,
(const char *) NULL);
if (zQinput == NULL)
{
/* If we get an error, try again later. */
uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
*pfprocessed = FALSE;
return;
}
zQinput = xstrdup (zQinput);
iclean |= FREE_QINPUT;
if (! fspool
&& ! fin_directory_list (qsys, zQinput,
qsys->zremote_send, 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;
}
}
if (zQoutfile == NULL)
{
zoutput = NULL;
qoutsys = NULL;
}
else if (zQoutsys != NULL
&& strcmp (zQoutsys, zLocalname) != 0)
{
const char *zdata;
char *zcopy;
/* The output file is destined for some other system, so we must
use a temporary file to catch standard output. */
if (strcmp (zQoutsys, qsys->zname) == 0)
qoutsys = qsys;
else
{
if (! fread_system_info (zQoutsys, &soutsys))
{
if (! fUnknown_ok)
{
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;
}
soutsys = sUnknown;
soutsys.zname = zQoutsys;
}
qoutsys = &soutsys;
if (! fsysdep_make_spool_dir (qoutsys))
{
/* If we get an error, try again later. */
uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
*pfprocessed = FALSE;
return;
}
}
zdata = zsysdep_data_file_name (qoutsys, BDEFAULT_UUX_GRADE, 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;
}
zcopy = (char *) alloca (strlen (zdata) + 1);
strcpy (zcopy, zdata);
zoutput = zcopy;
}
else
{
boolean fok;
char *zcopy;
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
{
zQoutfile = zsysdep_real_file_name (&sLocalsys, zQoutfile,
(const char *) NULL);
if (zQoutfile == NULL)
{
/* If we get an error, try again later. */
uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
*pfprocessed = FALSE;
return;
}
/* Make sure it's OK to receive this file. Note that this
means that a locally executed uux (which presumably
requires remote files) will only be able to create files
in standard directories. If we don't it this way, users
could clobber files which uucp has access to; still, it
would be nice to allow them to direct the output to their
home directory. */
fok = fin_directory_list (qsys, zQoutfile, qsys->zremote_receive,
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;
}
zcopy = (char *) alloca (strlen (zQoutfile) + 1);
strcpy (zcopy, zQoutfile);
zQoutfile = zcopy;
zoutput = zcopy;
}
/* Move the required files to the execution directory if necessary. */
for (i = 0; i < cQfiles; i++)
{
if (azQfiles_to[i] != NULL)
{
const char *zname;
/* Move the file to the execute directory. */
zname = zsysdep_in_dir (XQTDIR, azQfiles_to[i]);
if (zname == NULL)
{
/* If we get an error, try again later. */
uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
*pfprocessed = FALSE;
return;
}
if (! fsysdep_move_file (azQfiles[i], zname, 0, FALSE,
(const char *) NULL))
{
/* If we get an error, try again later. This may not be
correct, depending on what kind of error we get. */
uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
*pfprocessed = FALSE;
return;
}
/* If we just moved the standard input file, adjust the
file name. */
if (zQinput != NULL && strcmp (azQfiles[i], zQinput) == 0)
{
xfree ((pointer) zQinput);
zQinput = xstrdup (zname);
}
}
}
#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. */
zfullcmd = (char *) alloca (strlen (zQcmd) + strlen (azQargs[0]) + 2);
*zfullcmd = '\0';
for (i = 0; azQargs[i] != NULL; i++)
{
strcat (zfullcmd, azQargs[i]);
strcat (zfullcmd, " ");
}
zfullcmd[strlen (zfullcmd) - 1] = '\0';
if (! fsysdep_execute (qsys,
zQuser == NULL ? (const char *) "uucp" : zQuser,
azQargs, zfullcmd, zQinput, zoutput, fshell,
&zerror, &ftemp))
{
if (ftemp)
{
ulog (LOG_NORMAL, "Will retry later (%s)", zfile);
if (zoutput != NULL)
(void) remove (zoutput);
if (zerror != NULL)
(void) remove (zerror);
uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
*pfprocessed = FALSE;
return;
}
ulog (LOG_NORMAL, "Execution failed (%s)", zfile);
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;
pz[i++] = "Standard error output was:\n";
istart = i;
while ((zline = zfgets (eerr, FALSE)) != NULL)
{
if (i >= cgot)
{
cgot += 20;
pz = ((const char **)
xrealloc ((pointer) pz,
cgot * sizeof (const char *)));
}
pz[i++] = zline;
}
(void) fclose (eerr);
}
(void) fsysdep_mail (zmail, "Execution failed", i, pz);
for (; istart < i; istart++)
xfree ((pointer) pz[istart]);
xfree ((pointer) pz);
}
if (qoutsys != NULL)
(void) remove (zoutput);
}
else
{
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.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 = "";
/* The number of bytes will be filled in when the file is
actually sent. */
s.cbytes = -1;
(void) zsysdep_spool_commands (qoutsys, BDEFAULT_UUX_GRADE,
1, &s);
}
}
if (zerror != NULL)
(void) remove (zerror);
uqcleanup (zfile, iclean);
}
/* 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 & FREE_QINPUT) != 0)
xfree ((pointer) zQinput);
if (fQunlock_directory)
{
(void) fsysdep_unlock_uuxqt_dir ();
fQunlock_directory = FALSE;
}
for (i = 0; i < cQfiles; i++)
{
xfree ((pointer) azQfiles[i]);
xfree ((pointer) azQfiles_to[i]);
}
xfree ((pointer) azQargs);
azQargs = NULL;
xfree ((pointer) zQcmd);
zQcmd = NULL;
xfree ((pointer) azQfiles);
azQfiles = NULL;
xfree ((pointer) azQfiles_to);
azQfiles_to = NULL;
}