#include "uucp.h"
#if USE_RCS_ID
const char xqtsub_rcsid[] = "$Id: xqtsub.c,v 1.24 2002/03/05 19:10:42 ian Rel $";
#endif
#include "uudefs.h"
#include "uuconf.h"
#include "system.h"
#include "sysdep.h"
#include <ctype.h>
#include <errno.h>
#if HAVE_FCNTL_H
#include <fcntl.h>
#else
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#endif
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
#ifndef FD_CLOEXEC
#define FD_CLOEXEC 1
#endif
#if HAVE_OPENDIR
#if HAVE_DIRENT_H
#include <dirent.h>
#else
#include <sys/dir.h>
#define dirent direct
#endif
#endif
#if HAVE_SYSEXITS_H
#include <sysexits.h>
#endif
#ifndef EX_TEMPFAIL
#define EX_TEMPFAIL 75
#endif
static boolean fclean_uuxqt_dir P((const char *zxqtdir));
char *
zsysdep_find_command (zcmd, pzcmds, pzpath, pferr)
const char *zcmd;
char **pzcmds;
char **pzpath;
boolean *pferr;
{
char **pz;
struct stat s;
*pferr = FALSE;
for (pz = pzcmds; *pz != NULL; pz++)
{
char *zslash;
if (strcmp (*pz, "ALL") == 0)
break;
zslash = strrchr (*pz, '/');
if (zslash != NULL)
++zslash;
else
zslash = *pz;
if (strcmp (zslash, zcmd) == 0
|| strcmp (*pz, zcmd) == 0)
{
if (**pz == '/')
{
if (stat (*pz, &s) != 0)
{
ulog (LOG_ERROR, "%s: %s", *pz, strerror (errno));
*pferr = TRUE;
return NULL;
}
return zbufcpy (*pz);
}
break;
}
}
if (*pz == NULL)
return NULL;
for (pz = pzpath; *pz != NULL; pz++)
{
char *zname;
zname = zsysdep_in_dir (*pz, zcmd);
if (stat (zname, &s) == 0)
return zname;
}
return NULL;
}
char *
zsysdep_xqt_local_file (qsys, zfile)
const struct uuconf_system *qsys;
const char *zfile;
{
if (*zfile != '~')
return NULL;
if (zfile[1] == '~')
{
size_t clen;
char *zret;
clen = strlen (zfile);
zret = zbufalc (clen);
memcpy (zret, zfile + 1, clen);
return zret;
}
return zsysdep_local_file (zfile, qsys->uuconf_zpubdir,
(boolean *) NULL);
}
#if ! ALLOW_FILENAME_ARGUMENTS
boolean
fsysdep_xqt_check_file (qsys, zfile)
const struct uuconf_system *qsys;
const char *zfile;
{
size_t clen;
clen = strlen (zfile);
if ((clen == sizeof ".." - 1
&& strcmp (zfile, "..") == 0)
|| strncmp (zfile, "../", sizeof "../" - 1) == 0
|| (clen >= sizeof "/.." - 1
&& strcmp (zfile + clen - (sizeof "/.." - 1), "/..") == 0)
|| strstr (zfile, "/../") != NULL
|| (*zfile == '/'
&& (! fin_directory_list (zfile, qsys->uuconf_pzremote_send,
qsys->uuconf_zpubdir, TRUE, FALSE,
(const char *) NULL)
|| ! fin_directory_list (zfile, qsys->uuconf_pzremote_receive,
qsys->uuconf_zpubdir, TRUE, FALSE,
(const char *) NULL))))
{
ulog (LOG_ERROR, "Not permitted to refer to file \"%s\"", zfile);
return FALSE;
}
return TRUE;
}
#endif
boolean
fsysdep_execute (qsys, zuser, pazargs, zfullcmd, zinput, zoutput,
zchdir, fshell, iseq, pzerror, pftemp)
const struct uuconf_system *qsys;
const char *zuser;
const char **pazargs;
const char *zfullcmd ATTRIBUTE_UNUSED;
const char *zinput;
const char *zoutput;
const char *zchdir;
boolean fshell;
int iseq;
char **pzerror;
boolean *pftemp;
{
int aidescs[3];
boolean ferr;
pid_t ipid;
int ierr;
char abxqtdir[sizeof XQTDIR + 4];
const char *zxqtdir;
int istat;
char *zpath;
#if ALLOW_SH_EXECUTION
const char *azshargs[4];
#endif
*pzerror = NULL;
*pftemp = FALSE;
aidescs[0] = SPAWN_NULL;
aidescs[1] = SPAWN_NULL;
aidescs[2] = SPAWN_NULL;
ferr = FALSE;
if (zinput != NULL)
{
aidescs[0] = open ((char *) zinput, O_RDONLY | O_NOCTTY, 0);
if (aidescs[0] < 0)
{
ulog (LOG_ERROR, "open (%s): %s", zinput, strerror (errno));
ferr = TRUE;
}
else if (fcntl (aidescs[0], F_SETFD,
fcntl (aidescs[0], F_GETFD, 0) | FD_CLOEXEC) < 0)
{
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
ferr = TRUE;
}
}
if (! ferr && zoutput != NULL)
{
aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE);
if (aidescs[1] < 0)
{
if (errno == ENOENT && zoutput[0] != '/')
{
if (! fsysdep_make_dirs (zoutput, FALSE))
{
*pftemp = TRUE;
ferr = TRUE;
}
else
aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE);
}
if (! ferr && aidescs[1] < 0)
{
ulog (LOG_ERROR, "creat during fsysdep_execute part A (%s) in %s: %s", zoutput, getwd(NULL),
strerror (errno));
*pftemp = TRUE;
ferr = TRUE;
}
}
if (! ferr
&& fcntl (aidescs[1], F_SETFD,
fcntl (aidescs[1], F_GETFD, 0) | FD_CLOEXEC) < 0)
{
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
ferr = TRUE;
}
}
if (! ferr)
{
*pzerror = zstemp_file (qsys);
aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE);
if (aidescs[2] < 0)
{
if (errno == ENOENT)
{
if (! fsysdep_make_dirs (*pzerror, FALSE))
{
*pftemp = TRUE;
ferr = TRUE;
}
else
aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE);
}
if (! ferr && aidescs[2] < 0)
{
ulog (LOG_ERROR, "creat during fsysdep_execute part B (%s): %s", *pzerror, strerror (errno));
*pftemp = TRUE;
ferr = TRUE;
}
}
if (! ferr
&& fcntl (aidescs[2], F_SETFD,
fcntl (aidescs[2], F_GETFD, 0) | FD_CLOEXEC) < 0)
{
ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
ferr = TRUE;
}
}
if (iseq == 0)
zxqtdir = XQTDIR;
else
{
sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
zxqtdir = abxqtdir;
}
if (ferr)
{
if (aidescs[0] != SPAWN_NULL)
(void) close (aidescs[0]);
if (aidescs[1] != SPAWN_NULL)
(void) close (aidescs[1]);
if (aidescs[2] != SPAWN_NULL)
(void) close (aidescs[2]);
ubuffree (*pzerror);
return FALSE;
}
#if ALLOW_SH_EXECUTION
if (fshell)
{
azshargs[0] = "/bin/sh";
azshargs[1] = "-c";
azshargs[2] = zfullcmd;
azshargs[3] = NULL;
pazargs = azshargs;
}
#else
fshell = FALSE;
#endif
if (qsys->uuconf_pzpath == NULL)
zpath = NULL;
else
{
size_t c;
char **pz;
c = 0;
for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++)
c += strlen (*pz) + 1;
zpath = zbufalc (c);
*zpath = '\0';
for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++)
{
strcat (zpath, *pz);
if (pz[1] != NULL)
strcat (zpath, ":");
}
}
ipid = ixsspawn (pazargs, aidescs, TRUE, FALSE, zchdir ? zchdir : zxqtdir,
TRUE, !fshell, zpath, qsys->uuconf_zname, zuser);
ierr = errno;
ubuffree (zpath);
if (aidescs[0] != SPAWN_NULL)
(void) close (aidescs[0]);
if (aidescs[1] != SPAWN_NULL)
(void) close (aidescs[1]);
if (aidescs[2] != SPAWN_NULL)
(void) close (aidescs[2]);
if (ipid < 0)
{
ulog (LOG_ERROR, "ixsspawn: %s", strerror (ierr));
*pftemp = TRUE;
return FALSE;
}
istat = ixswait ((unsigned long) ipid, "Execution");
if (istat == EX_TEMPFAIL)
*pftemp = TRUE;
return istat == 0;
}
int
ixsysdep_lock_uuxqt (zcmd, cmaxuuxqts)
const char *zcmd;
int cmaxuuxqts;
{
char ab[sizeof "LCK.XQT.9999"];
int i;
if (cmaxuuxqts <= 0 || cmaxuuxqts >= 10000)
cmaxuuxqts = 9999;
for (i = 0; i < cmaxuuxqts; i++)
{
sprintf (ab, "LCK.XQT.%d", i);
if (fsdo_lock (ab, TRUE, (boolean *) NULL))
break;
}
if (i >= cmaxuuxqts)
return -1;
if (zcmd != NULL)
{
char abcmd[sizeof "LXQ.123456789"];
sprintf (abcmd, "LXQ.%.9s", zcmd);
abcmd[strcspn (abcmd, " \t/")] = '\0';
if (! fsdo_lock (abcmd, TRUE, (boolean *) NULL))
{
(void) fsdo_unlock (ab, TRUE);
return -1;
}
}
return i;
}
boolean
fsysdep_unlock_uuxqt (iseq, zcmd, cmaxuuxqts)
int iseq;
const char *zcmd;
int cmaxuuxqts ATTRIBUTE_UNUSED;
{
char ab[sizeof "LCK.XQT.9999"];
boolean fret;
fret = TRUE;
sprintf (ab, "LCK.XQT.%d", iseq);
if (! fsdo_unlock (ab, TRUE))
fret = FALSE;
if (zcmd != NULL)
{
char abcmd[sizeof "LXQ.123456789"];
sprintf (abcmd, "LXQ.%.9s", zcmd);
abcmd[strcspn (abcmd, " \t/")] = '\0';
if (! fsdo_unlock (abcmd, TRUE))
fret = FALSE;
}
return fret;
}
boolean
fsysdep_uuxqt_locked (zcmd)
const char *zcmd;
{
char ab[sizeof "LXQ.123456789"];
struct stat s;
sprintf (ab, "LXQ.%.9s", zcmd);
return stat (ab, &s) == 0;
}
boolean
fsysdep_lock_uuxqt_file (zfile)
const char *zfile;
{
char *zcopy, *z;
boolean fret;
zcopy = zbufcpy (zfile);
z = strrchr (zcopy, '/');
if (z == NULL)
*zcopy = 'L';
else
*(z + 1) = 'L';
fret = fsdo_lock (zcopy, TRUE, (boolean *) NULL);
ubuffree (zcopy);
return fret;
}
boolean
fsysdep_unlock_uuxqt_file (zfile)
const char *zfile;
{
char *zcopy, *z;
boolean fret;
zcopy = zbufcpy (zfile);
z = strrchr (zcopy, '/');
if (z == NULL)
*zcopy = 'L';
else
*(z + 1) = 'L';
fret = fsdo_unlock (zcopy, TRUE);
ubuffree (zcopy);
return fret;
}
boolean
fsysdep_lock_uuxqt_dir (iseq)
int iseq;
{
const char *zxqtdir;
char abxqtdir[sizeof XQTDIR + 4];
if (iseq == 0)
zxqtdir = XQTDIR;
else
{
sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
zxqtdir = abxqtdir;
}
if (mkdir (zxqtdir, S_IRWXU) < 0
&& errno != EEXIST
&& errno != EISDIR)
{
ulog (LOG_ERROR, "mkdir (%s): %s", zxqtdir, strerror (errno));
return FALSE;
}
return fclean_uuxqt_dir (zxqtdir);
}
boolean
fsysdep_unlock_uuxqt_dir (iseq)
int iseq;
{
const char *zxqtdir;
char abxqtdir[sizeof XQTDIR + 4];
if (iseq == 0)
zxqtdir = XQTDIR;
else
{
sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
zxqtdir = abxqtdir;
}
return fclean_uuxqt_dir (zxqtdir);
}
static boolean
fclean_uuxqt_dir (zxqtdir)
const char *zxqtdir;
{
DIR *qdir;
qdir = opendir ((char *) zxqtdir);
if (qdir != NULL)
{
struct dirent *qentry;
while ((qentry = readdir (qdir)) != NULL)
{
char *z;
if (strcmp (qentry->d_name, ".") == 0
|| strcmp (qentry->d_name, "..") == 0)
continue;
z = zsysdep_in_dir (zxqtdir, qentry->d_name);
if (remove (z) < 0)
{
int ierr;
ierr = errno;
if (! fsysdep_directory (z))
ulog (LOG_ERROR, "remove (%s): %s", z,
strerror (ierr));
else
(void) fsysdep_rmdir (z);
}
ubuffree (z);
}
closedir (qdir);
}
return TRUE;
}
boolean
fsysdep_copy_uuxqt_files (cfiles, pzfrom, pzto, iseq, pzinput)
int cfiles;
const char *const *pzfrom;
const char *const *pzto;
int iseq;
char **pzinput;
{
char *zinput;
const char *zxqtdir;
char abxqtdir[sizeof XQTDIR + 4];
int i;
if (pzinput == NULL)
zinput = NULL;
else
zinput = *pzinput;
if (iseq == 0)
zxqtdir = XQTDIR;
else
{
sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
zxqtdir = abxqtdir;
}
for (i = 0; i < cfiles; i++)
{
const char *zfrom, *zto;
char *zfree;
if (pzto[i] == NULL)
continue;
zfree = zsysdep_in_dir (zxqtdir, pzto[i]);
zfrom = pzfrom[i];
zto = zfree;
if (zinput != NULL && strcmp (zinput, zfrom) == 0)
{
*pzinput = zbufcpy (zto);
zinput = NULL;
}
if (link (zfrom, zto) < 0)
{
if (errno != EXDEV && errno != EEXIST && errno != EMLINK)
{
ulog (LOG_ERROR, "link (%s, %s): %s", zfrom, zto,
strerror (errno));
ubuffree (zfree);
return FALSE;
}
if (! fcopy_file (zfrom, zto, FALSE, FALSE, FALSE))
{
ubuffree (zfree);
return FALSE;
}
}
(void) chmod (zto, IPUBLIC_FILE_MODE);
#ifdef WORLD_WRITABLE_FILE_IN
char rfile[PATH_MAX];
realpath(zto, rfile);
ulog(LOG_ERROR, "open of %s WORLD_WRITABLE_FILE_IN is %s", rfile, WORLD_WRITABLE_FILE_IN);
if (rfile == strstr(rfile, WORLD_WRITABLE_FILE_IN)) {
chmod(zto, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
ulog(LOG_ERROR, "xqtsub chmod'ing %s", zto);
}
#endif
ubuffree (zfree);
}
return TRUE;
}