/* xqtsub.c System dependent functions used only by uuxqt. Copyright (C) 1991, 1992, 1993, 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 xqtsub_rcsid[] = "$Id$"; #endif #include "uudefs.h" #include "uuconf.h" #include "system.h" #include "sysdep.h" #include #include #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #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 #else /* ! HAVE_DIRENT_H */ #include #define dirent direct #endif /* ! HAVE_DIRENT_H */ #endif /* HAVE_OPENDIR */ /* Get a value for EX_TEMPFAIL. */ #if HAVE_SYSEXITS_H #include #endif #ifndef EX_TEMPFAIL #define EX_TEMPFAIL 75 #endif /* Get the full pathname of the command to execute, given the list of permitted commands and the allowed path. */ 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 we already have an absolute path, we can get out immediately. */ if (**pz == '/') { /* Quick error check. */ if (stat (*pz, &s) != 0) { ulog (LOG_ERROR, "%s: %s", *pz, strerror (errno)); *pferr = TRUE; return NULL; } return zbufcpy (*pz); } break; } } /* If we didn't find this command, get out. */ if (*pz == NULL) return NULL; /* We didn't find an absolute pathname, so we must look through the path. */ for (pz = pzpath; *pz != NULL; pz++) { char *zname; zname = zsysdep_in_dir (*pz, zcmd); if (stat (zname, &s) == 0) return zname; } return NULL; } /* Expand a local filename for uuxqt. This is special because uuxqt only wants to expand filenames that start with ~ (it does not want to prepend the current directory to other names) and if the ~ is double, it is turned into a single ~. This returns NULL to indicate that no change was required; it has no way to return error. */ 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 /* Check to see whether an argument specifies a file name; if it does, make sure that the file may legally be sent and/or received. For Unix, we do not permit any occurrence of "/../" in the name, nor may it start with "../". Otherwise, if it starts with "/" we check against the list of permitted files. */ boolean fsysdep_xqt_check_file (qsys, zfile) const struct uuconf_system *qsys; const char *zfile; { size_t clen; /* Disallow exact "..", prefix "../", suffix "/..", internal "/../", and restricted absolute paths. */ 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 /* ! ALLOW_FILENAME_ARGUMENTS */ /* Invoke the command specified by an execute file. */ /*ARGSUSED*/ boolean fsysdep_execute (qsys, zuser, pazargs, zfullcmd, zinput, zoutput, fshell, iseq, pzerror, pftemp) const struct uuconf_system *qsys; const char *zuser; const char **pazargs; const char *zfullcmd; const char *zinput; const char *zoutput; 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) { ulog (LOG_ERROR, "creat (%s): %s", zoutput, strerror (errno)); *pftemp = TRUE; ferr = TRUE; } else if (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 (%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, ":"); } } /* Pass zchdir as zxqtdir, fnosigs as TRUE, fshell as TRUE if we aren't already using the shell. */ ipid = ixsspawn (pazargs, aidescs, TRUE, FALSE, 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; } /* Lock a uuxqt process. */ 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; } /* Unlock a uuxqt process. */ boolean fsysdep_unlock_uuxqt (iseq, zcmd, cmaxuuxqts) int iseq; const char *zcmd; int cmaxuuxqts; { 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; } /* See whether a particular uuxqt command is locked (this depends on the implementation of fsdo_lock). */ 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; } /* Lock a particular execute file. */ 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; } /* Unlock a particular execute file. */ 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; } /* Lock the execute directory. Since we use a different directory depending on which LCK.XQT.dddd file we got, there is actually no need to create a lock file. We do make sure that the directory exists, though. */ 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 TRUE; } /* Unlock the execute directory and clear it out. The lock is actually the LCK.XQT.dddd file, so we don't unlock it, but we do remove all the files. */ boolean fsysdep_unlock_uuxqt_dir (iseq) int iseq; { const char *zxqtdir; char abxqtdir[sizeof XQTDIR + 4]; DIR *qdir; if (iseq == 0) zxqtdir = XQTDIR; else { sprintf (abxqtdir, "%s%04d", XQTDIR, iseq); zxqtdir = abxqtdir; } 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; } /* Move files into the execution directory. */ boolean fsysdep_move_uuxqt_files (cfiles, pzfrom, pzto, fto, iseq, pzinput) int cfiles; const char *const *pzfrom; const char *const *pzto; boolean fto; 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 (! fto) { const char *ztemp; ztemp = zfrom; zfrom = zto; zto = ztemp; (void) chmod (zfrom, IPRIVATE_FILE_MODE); } if (rename (zfrom, zto) < 0) { #if HAVE_RENAME /* On some systems the system call rename seems to fail for arbitrary reasons. To get around this, we always try to copy the file by hand if the rename failed. */ errno = EXDEV; #endif if (errno != EXDEV) { ulog (LOG_ERROR, "rename (%s, %s): %s", zfrom, zto, strerror (errno)); ubuffree (zfree); break; } if (! fcopy_file (zfrom, zto, FALSE, FALSE, FALSE)) { ubuffree (zfree); break; } if (remove (zfrom) < 0) ulog (LOG_ERROR, "remove (%s): %s", zfrom, strerror (errno)); } if (fto) (void) chmod (zto, IPUBLIC_FILE_MODE); ubuffree (zfree); } if (i < cfiles) { if (fto) (void) fsysdep_move_uuxqt_files (i, pzfrom, pzto, FALSE, iseq, (char **) NULL); return FALSE; } return TRUE; }