+/* sys3.unx
+ The system dependent spool directory subroutines for Unix.
+
+ 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: sys3.unx,v $
+# Revision 1.2 92/05/13 05:42:07 rich
+# ported to 386bsd
+#
+# Revision 1.1 1992/05/10 18:00:16 rich
+# Initial revision
+#
+ Revision 1.48 1992/04/01 22:36:48 ian
+ David J. MacKenzie: some USG_STATFS systems use 512 despite f_bsize
+
+ Revision 1.47 1992/03/30 15:03:07 ian
+ Niels Baggesen: USG statfs has an f_bsize field
+
+ Revision 1.46 1992/03/28 22:06:38 ian
+ Michael I Bushnell: renamed enum tstatus to avoid header file conflict
+
+ Revision 1.45 1992/03/26 20:20:28 ian
+ Reduce race condition in fsdo_lock
+
+ Revision 1.44 1992/03/15 01:54:46 ian
+ All execs are now done in isspawn, all waits are done in iswait
+
+ Revision 1.43 1992/03/12 19:54:43 ian
+ Debugging based on types rather than number
+
+ Revision 1.42 1992/03/10 20:45:58 ian
+ Check size of destination file system as well as temporary system
+
+ Revision 1.41 1992/03/09 19:42:43 ian
+ Ted Lindgreen: don't send mail for nonexistent file
+
+ Revision 1.40 1992/03/04 01:40:51 ian
+ Thomas Fischer: tweaked a bit for the NeXT
+
+ Revision 1.39 1992/02/29 04:07:08 ian
+ Added -j option to uucp and uux
+
+ Revision 1.38 1992/02/29 01:06:59 ian
+ Chip Salzenberg: recheck file permissions before sending
+
+ Revision 1.37 1992/02/28 15:57:58 ian
+ Give error if esysdep_open_send is given a directory
+
+ Revision 1.36 1992/02/24 22:05:30 ian
+ Roberto Biancardi: support F_CHSIZE and F_FREESP in esysdep_truncate
+
+ Revision 1.35 1992/02/24 20:07:43 ian
+ John Theus: some systems don't have <fcntl.h>
+
+ Revision 1.34 1992/02/23 03:26:51 ian
+ Overhaul to use automatic configure shell script
+
+ Revision 1.33 1992/02/20 04:18:59 ian
+ Added uustat
+
+ Revision 1.32 1992/02/08 03:54:18 ian
+ Include <string.h> only in <uucp.h>, added 1992 copyright
+
+ Revision 1.31 1992/02/02 20:42:40 ian
+ Niels Baggesen: case enum to int before comparison
+
+ Revision 1.30 1992/02/01 00:54:31 ian
+ Michael Nolan: cast alloca return value
+
+ Revision 1.29 1992/01/29 04:27:11 ian
+ Jay Vassos-Libove: removed some conflicting declarations
+
+ Revision 1.28 1992/01/28 04:34:10 ian
+ Marty Shannon: handle trailing '/' to indicate directory
+
+ Revision 1.27 1992/01/14 04:51:48 ian
+ David Nugent: don't declare chmod
+
+ Revision 1.26 1992/01/14 04:25:20 ian
+ Chip Salzenberg: avoid use before set warning
+
+ Revision 1.25 1992/01/14 03:46:55 ian
+ Chip Salzenberg: handle invalid status values in status files
+
+ Revision 1.24 1992/01/13 06:11:39 ian
+ David Nugent: can't declare open or fcntl
+
+ Revision 1.23 1992/01/05 03:18:54 ian
+ Avoid redefining SEEK_SET
+
+ Revision 1.22 1992/01/04 22:56:22 ian
+ Added extern definition
+
+ Revision 1.21 1992/01/03 05:44:35 ian
+ Remove temporary file if link fails in fsdo_lock
+
+ Revision 1.20 1991/12/29 04:04:18 ian
+ Added a bunch of extern definitions
+
+ Revision 1.19 1991/12/22 22:14:19 ian
+ Monty Solomon: added HAVE_UNISTD_H configuration parameter
+
+ Revision 1.18 1991/12/22 20:50:47 ian
+ Franc,ois Pinard: fixed bug in fsysdep_get_status
+
+ Revision 1.17 1991/12/12 18:35:47 ian
+ Do locking with link to avoid races and to permit running as root
+
+ Revision 1.16 1991/12/12 17:45:34 ian
+ fcopy_file now creates the file with IPRIVATE_MODE
+
+ Revision 1.15 1991/12/11 03:59:19 ian
+ Create directories when necessary; don't just assume they exist
+
+ Revision 1.14 1991/12/09 19:07:07 ian
+ Richard Todd: add HAVE_V2_LOCKFILES--binary number in lock file
+
+ Revision 1.13 1991/12/03 02:59:46 ian
+ Using LOCKDIR clobbered a byte on the stack
+
+ Revision 1.12 1991/12/01 02:23:12 ian
+ Niels Baggesen: don't multiply include <unistd.h>
+
+ Revision 1.11 1991/12/01 01:12:40 ian
+ Marty Shannon: accept truncated status file; also eliminated scanf calls
+
+ Revision 1.10 1991/11/30 23:28:26 ian
+ Marty Shannon: some systems need a fake version of the rename system call
+
+ Revision 1.9 1991/11/21 21:43:42 ian
+ Eliminate unused MIN_FREE_BYTES
+
+ Revision 1.8 1991/11/21 21:07:46 ian
+ Brian Campbell: offer ltrunc as an alternative to ftruncate
+
+ Revision 1.7 1991/11/10 21:32:16 ian
+ Fixed ftruncate call
+
+ Revision 1.6 1991/11/10 19:24:22 ian
+ Added pffile protocol entry point for file level control
+
+ Revision 1.5 1991/11/07 19:32:28 ian
+ Chip Salzenberg: allow LOCKDIR, and check that locking process exists
+
+ Revision 1.4 1991/09/19 17:28:01 ian
+ Chip Salzenberg: make sure spool directory files are not world readable
+
+ Revision 1.3 1991/09/19 03:23:34 ian
+ Chip Salzenberg: append to private debugging file, don't overwrite it
+
+ Revision 1.2 1991/09/19 03:06:04 ian
+ Chip Salzenberg: put BNU temporary files in system's directory
+
+ Revision 1.1 1991/09/10 19:45:50 ian
+ Initial revision
+
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+char sys3_unx_rcsid[] = "$Id: sys3.unx,v 1.2 92/05/13 05:42:07 rich Exp Locker: root $";
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+
+#if USE_STDIO && HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "system.h"
+#include "sysdep.h"
+
+#include <pwd.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
+
+/* Get the right header files for statfs and friends. This stuff is
+ from David MacKenzie's df program. */
+
+#ifdef FS_STATVFS
+#include <sys/statvfs.h>
+extern int statvfs ();
+#endif
+
+#ifdef FS_USG_STATFS
+#include <sys/statfs.h>
+extern int statfs ();
+#endif
+
+#ifdef FS_MNTENT
+#include <sys/vfs.h>
+extern int statfs ();
+#endif
+
+#ifdef FS_GETMNT
+#include <sys/param.h>
+#include <sys/mount.h>
+extern int statfs ();
+#endif
+
+#ifdef FS_STATFS
+#include <sys/mount.h>
+extern int statfs ();
+#endif
+
+#ifdef FS_USTAT
+#include <ustat.h>
+extern int ustat ();
+#endif
+
+/* We need a definition for SEEK_SET. */
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+/* External functions. */
+extern int close (), link (), read (), write ();
+#ifndef __386BSD__
+extern int kill ();
+#endif __386BSD__
+extern int fstat (), stat ();
+extern int fclose (), fseek (), pclose ();
+extern pid_t getpid ();
+extern off_t lseek ();
+extern char *strrchr ();
+
+#if HAVE_FTRUNCATE
+extern int ftruncate ();
+#endif
+
+#if HAVE_RENAME
+extern int rename ();
+#else
+static int rename P((const char *zfrom, const char *zto));
+#endif
+\f
+/* There are several types of files that go in the spool directory,
+ and they go into various different subdirectories. When using
+ SPOOLDIR_TAYLOR, there is a subdirectory for each system for which
+ communication occurs; these system names have been made canonical
+ via fread_system_info or ztranslate_name, so they will fit any
+ name length restrictions (namely 14 characters on System V).
+ Whenever the system name LOCAL appears below, it means whatever
+ the local system name is.
+
+ Command files
+ These contain instructions for uucico indicating what files to transfer
+ to and from what systems. Each line of a work file is a command
+ beginning with S, R or X.
+ #if ! SPOOLDIR_TAYLOR
+ They are named C.ssssssgqqqq, where ssssss is the system name to
+ transfer to or from, g is the grade and qqqq is the sequence number.
+ #if SPOOLDIR_V2
+ They are put in the spool directory.
+ #elif SPOOLDIR_BSD42 | SPOOLDIR_BSD43
+ They are put in the directory C.
+ #elif SPOOLDIR_BNU
+ They are put in a directory named for the system for which they were
+ created.
+ #elif SPOOLDIR_ULTRIX
+ If the directory sys/ssssss exists, they are put in the directory
+ sys/ssssss/C; otherwise, they are put in the directory sys/DEFAULT/C.
+ #endif
+ #else SPOOLDIR_TAYLOR
+ They are named C.gqqqq, where g is the grade and qqqq is the sequence
+ number, and are placed in the directory ssssss/C. where ssssss is
+ the system name to transfer to or from.
+ #endif
+
+ Data files
+ There are files to be transferred to other systems. Some files to
+ be transferred may not be in the spool directory, depending on how
+ uucp was invoked. Data files are named in work files, so it is
+ never necessary to look at them directly (except to remove old ones);
+ it is only necessary to create them. These means that the many
+ variations in naming are inconsequential.
+ #if ! SPOOLDIR_TAYLOR
+ They are named D.ssssssgqqqq where ssssss is a system name (which
+ may be LOCAL for locally initiated transfers or a remote system for
+ remotely initiated transfers, except that BNU appears to use the
+ system the file is being transferred to), g is the grade and qqqq
+ is the sequence number. Some systems use a trailing subjob ID
+ number, but we currently do not. The grade is not important, and
+ some systems do not use it. If the data file is to become an
+ execution file on another system the grade (if present) will be
+ 'X'. Otherwise Ultrix appears to use 'b'; the uux included with
+ gnuucp 1.0 appears to use 'S'; SCO does not appear to use a grade,
+ although it does use a subjob ID number.
+ #if SPOOLDIR_V2
+ They are put in the spool directory.
+ #elif SPOOLDIR_BSD42
+ If the name begins with D.LOCAL, the file is put in the directory
+ D.LOCAL. Otherwise the file is put in the directory D..
+ #elif SPOOLDIR_BSD43
+ If the name begins with D.LOCALX, the file is put in the directory
+ D.LOCALX. Otherwise if the name begins with D.LOCAL, the file is
+ put in the directory D.LOCAL Otherwise the file is put in the
+ directory D..
+ #elif SPOOLDIR_BNU
+ They are put in a directory named for the system for which they
+ were created.
+ #elif SPOOLDIR_ULTRIX
+ Say the file is being transferred to system REMOTE. If the
+ directory sys/REMOTE exists, then if the file begins with D.LOCALX
+ it is put in sys/REMOTE/D.LOCALX, if the file begins with D.LOCAL
+ it is put in sys/REMOTE/D.LOCAL, and otherwise it is put in
+ sys/REMOTE/D.. If the directory sys/REMOTE does not exist, the
+ same applies except that DEFAULT is used instead of REMOTE.
+ #endif
+ #else SPOOLDIR_TAYLOR
+ If the file is to become an executable file on another system it is
+ named D.Xqqqq, otherwise it is named D.qqqq where in both cases
+ qqqq is a sequence number. If the corresponding C. file is in
+ directory ssssss/C., a D.X file is placed in ssssss/D.X and a D.
+ file is placed in ssssss/D..
+ #endif
+
+ Execute files
+ These are files that specify programs to be executed. They are
+ created by uux, perhaps as run on another system. These names are
+ important, because a file transfer done to an execute file name
+ causes an execution to occur. The name is X.ssssssgqqqq, where
+ ssssss is the requesting system, g is the grade, and qqqq is a
+ sequence number.
+ #if SPOOLDIR_V2 | SPOOLDIR_BSD42
+ These files are placed in the spool directory.
+ #elif SPOOLDIR_BSD43
+ These files are placed in the directory X..
+ #elif SPOOLDIR_BNU
+ These files are put in a directory named for the system for which
+ the files were created.
+ #elif SPOOLDIR_ULTRIX
+ If there is a spool directory (sys/ssssss) for the requesting
+ system, the files are placed in sys/ssssss/X.; otherwise, the files
+ are placed in sys/DEFAULT/X..
+ #elif SPOOLDIR_TAYLOR
+ The system name is automatically truncated to seven characters when
+ a file is created. The files are placed in the subdirectory X. of
+ a directory named for the system for which the files were created.
+ #endif
+
+ Temporary receive files
+ These are used when receiving files from another system. They are
+ later renamed to the final name. The actual name is unimportant,
+ although it generally begins with TM..
+ #if SPOOLDIR_V2 | SPOOLDIR_BSD42
+ These files are placed in the spool directory.
+ #elif SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR
+ These files are placed in the directory .Temp.
+ #elif SPOOLDIR_BNU
+ These files are placed in a directory named for the system for
+ which they were created.
+ #endif
+
+ Lock files
+ These files are used to lock systems, devices and special files.
+ The file LCK..ssssss is used to lock a system, where ssssss is the
+ system name. The file LCK..dev is used to lock a device, where dev
+ is the device name. The file LCK.file is used to lock a file,
+ where file is LOG for the log file, SQ for the system sequence
+ number file, or SEQF for the work queue sequence number file. At
+ least under Ultrix, the file LCK.XQT is used to lock uuxqt
+ execution. Some systems supposedly use LCK.SEQL for something. On
+ some systems, the contents of the lock file is the ASCII process
+ id; on others, it is the process id as four data bytes. As far as
+ I can tell, the only lock file I really have to get right is the
+ one locking a device, so that cu won't use it; if somebody tries to
+ run old UUCP and this at the same time, they will probably have
+ trouble unless they make sure the locking is correct for their
+ system. Not that there is any easy way to make that check,
+ unfortunately. Supposedly all normal systems put the LCK files in
+ the spool directory, and this package will do that also.
+
+ System status files
+ These are used to record when the last call was made to the system
+ and what the status is. They are used to prevent frequent recalls
+ to a system which is not responding. I will not attempt to
+ recreate the format of these exactly, since they are not all that
+ important. They will be put in the directory .Status, as in BNU,
+ and they use the system name as the name of the file.
+
+ Log files
+ These are used to record what UUCP has done. I will not attempt to
+ recreate the format of these at all. They will be stored in the
+ directory .Log/uucico (or .Log/uucp, .Log/uux, .Log/uuxqt) and
+ named for the relevant system. This is the format used by BNU.
+
+ Statistics files
+ I don't really know the format of these. They are apparently used
+ to keep track of what jobs have been run by UUCP, but at least on
+ Ultrix they don't seem to be used very consistently.
+
+ Sequence file
+ This is used to generate a unique sequence number. It contains an
+ ASCII number.
+ #if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43
+ The file is named SEQF and is kept in the spool directory.
+ #elif SPOOLDIR_BNU
+ A separate sequence file is kept for each system in the directory
+ .Sequence with the name of the system.
+ #elif SPOOLDIR_ULTRIX
+ Each system with a file sys/ssssss has a sequence file in
+ sys/ssssss/.SEQF. Other systems use sys/DEFAULT/.SEQF.
+ #else SPOOLDIR_TAYLOR
+ A sequence file named SEQF is kept in the directory ssssss for each
+ system.
+ #endif
+
+ Audit files
+ Debugging messages are stored in these when running as a slave. We
+ use the file AUDIT in the spool directory. */
+\f
+/* Local functions. */
+
+static char *zsstatic_size P((int c));
+#if SPOOLDIR_TAYLOR
+static const char *zsappend3 P((const char *zdir1, const char *zdir2,
+ const char *zfile));
+#endif
+#if SPOOLDIR_ULTRIX
+static const char *zsappend4 P((const char *zdir1, const char *zdir2,
+ const char *zdir3, const char *zfile));
+#endif
+static const char *zsfind_file P((const char *zsimple,
+ const char *zsystem));
+static boolean fscmd_seq P((const char *zsystem, char *zseq));
+static const char *zsfile_name P((int btype, const char *zsystem,
+ int bgrade, char *ztname,
+ char *zdname, char *zxname));
+\f
+/* A few routines to manipulate strings with directories. */
+
+#define CSTATICLEN (50)
+static char abSstatic[CSTATICLEN];
+static char *zSstatic_alloc;
+static int cSstatic_alloc;
+
+/* Return a pointer to a static buffer of a certain size. */
+
+static char *
+zsstatic_size (c)
+ int c;
+{
+ if (c <= CSTATICLEN)
+ return abSstatic;
+ if (cSstatic_alloc < c)
+ {
+ xfree ((pointer) zSstatic_alloc);
+ zSstatic_alloc = (char *) xmalloc (c);
+ cSstatic_alloc = c;
+ }
+ return zSstatic_alloc;
+}
+
+/* Copy a string into a static buffer. */
+
+const char *
+zscopy (z)
+ const char *z;
+{
+ char *zret;
+
+ zret = zsstatic_size (strlen (z) + 1);
+ strcpy (zret, z);
+ return (const char *) zret;
+}
+
+/* Stick a directory and file name together. Return a static buffer
+ holding the combined name. This is called by Unix system dependent
+ routines outside this file. */
+
+const char *
+zsappend (zdir, zfile)
+ const char *zdir;
+ const char *zfile;
+{
+ char *zret;
+
+ zret = zsstatic_size (strlen (zdir) + strlen (zfile) + sizeof "/");
+ sprintf (zret, "%s/%s", zdir, zfile);
+ return (const char *) zret;
+}
+
+#if SPOOLDIR_TAYLOR
+
+/* Stick two directories and a file name together. Return a static
+ buffer holding the combined name. */
+
+static const char *
+zsappend3 (zdir1, zdir2, zfile)
+ const char *zdir1;
+ const char *zdir2;
+ const char *zfile;
+{
+ char *zret;
+
+ zret = zsstatic_size (strlen (zdir1) + strlen (zdir2)
+ + strlen (zfile) + sizeof "//");
+ sprintf (zret, "%s/%s/%s", zdir1, zdir2, zfile);
+ return (const char *) zret;
+}
+
+#endif /* SPOOLDIR_TAYLOR */
+
+#if SPOOLDIR_ULTRIX
+
+/* Stick three directories and a file name together. Return a static
+ buffer holding the combined name. */
+
+static const char *
+zsappend4 (zdir1, zdir2, zdir3, zfile)
+ const char *zdir1;
+ const char *zdir2;
+ const char *zdir3;
+ const char *zfile;
+{
+ char *zret;
+
+ zret = zsstatic_size (strlen (zdir1) + strlen (zdir2) + strlen (zdir3)
+ + strlen (zfile) + sizeof "///");
+ sprintf (zret, "%s/%s/%s/%s", zdir1, zdir2, zdir3, zfile);
+ return (const char *) zret;
+}
+
+/* See whether an ULTRIX spool directory exists for a system. For system
+ ssssss, the spool directory is called sys/ssssss. */
+
+boolean
+fsultrix_has_spool (zsystem)
+ const char *zsystem;
+{
+ char *z;
+
+ z = (char *) alloca (sizeof "sys/" + strlen (zsystem));
+ sprintf (z, "sys/%s", zsystem);
+ return fsdirectory_exists (z);
+}
+
+#endif /* SPOOLDIR_ULTRIX */
+\f
+/* Create a spool directory for a system. This is only relevant for
+ SPOOLDIR_BNU or SPOOLDIR_TAYLOR. The system specific directories
+ for Ultrix are meant to be created by hand. */
+
+#if SPOOLDIR_TAYLOR | SPOOLDIR_BNU
+
+static boolean fsmkdir P((const char *zdir));
+
+static boolean
+fsmkdir (zdir)
+ const char *zdir;
+{
+ if (mkdir ((char *) zdir, IDIRECTORY_MODE) < 0)
+ {
+ ulog (LOG_ERROR, "mkdir (%s): %s", zdir, strerror (errno));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#endif /* SPOOLDIR_TAYLOR | SPOOLDIR_BNU */
+
+boolean
+fsysdep_make_spool_dir (qsys)
+ const struct ssysteminfo *qsys;
+{
+ const char *zsystem;
+
+ zsystem = qsys->zname;
+
+#if SPOOLDIR_BNU
+ if (fsdirectory_exists (zsystem))
+ return TRUE;
+ if (! fsmkdir (zsystem))
+ return FALSE;
+#endif /* SPOOLDIR_BNU */
+
+#if SPOOLDIR_TAYLOR
+ if (fsdirectory_exists (zsystem))
+ return TRUE;
+ if (! fsmkdir (zsystem)
+ || ! fsmkdir (zsappend (zsystem, "C."))
+ || ! fsmkdir (zsappend (zsystem, "D."))
+ || ! fsmkdir (zsappend (zsystem, "D.X"))
+ || ! fsmkdir (zsappend (zsystem, "X.")))
+ return FALSE;
+#endif /* SPOOLDIR_TAYLOR */
+
+ return TRUE;
+}
+\f
+/* Given the name of a file as specified in a UUCP command, and the
+ system for which this file has been created, return where to find
+ it in the spool directory. The file will begin with C. (a command
+ file), D. (a data file) or X. (an execution file). The return
+ value of this function will point to a static buffer. */
+
+static const char *
+zsfind_file (zsimple, zsystem)
+ const char *zsimple;
+ const char *zsystem;
+{
+ if (zsimple[1] != '.'
+ || (*zsimple != 'C'
+ && *zsimple != 'D'
+ && *zsimple != 'X'))
+ {
+ ulog (LOG_ERROR, "Unrecognized file name %s", zsimple);
+ return NULL;
+ }
+
+ switch (*zsimple)
+ {
+ case 'C':
+#if SPOOLDIR_V2
+ return zscopy (zsimple);
+#endif /* SPOOLDIR_V2 */
+#if SPOOLDIR_BSD42 | SPOOLDIR_BSD43
+ return zsappend ("C.", zsimple);
+#endif /* SPOOLDIR_BSD42 | SPOOLDIR_BSD43 */
+#if SPOOLDIR_BNU
+ return zsappend (zsystem, zsimple);
+#endif /* SPOOLDIR_BNU */
+#if SPOOLDIR_ULTRIX
+ if (fsultrix_has_spool (zsystem))
+ return zsappend4 ("sys", zsystem, "C.", zsimple);
+ else
+ return zsappend4 ("sys", "DEFAULT", "C.", zsimple);
+#endif
+#if SPOOLDIR_TAYLOR
+ return zsappend3 (zsystem, "C.", zsimple);
+#endif
+
+ case 'D':
+#if SPOOLDIR_V2
+ return zscopy (zsimple);
+#endif /* SPOOLDIR_V2 */
+#if SPOOLDIR_BSD42 | SPOOLDIR_BSD43
+ {
+ int c;
+ boolean ftruncated;
+ char *zalloc;
+
+ /* D.LOCAL in D.LOCAL/, others in D./. If BSD43, D.LOCALX in
+ D.LOCALX/. */
+ ftruncated = TRUE;
+ if (strncmp (zsimple + 2, zLocalname, strlen (zLocalname)) == 0)
+ {
+ c = strlen (zLocalname);
+ ftruncated = FALSE;
+ }
+ else if (strncmp (zsimple + 2, zLocalname, 7) == 0)
+ c = 7;
+ else if (strncmp (zsimple + 2, zLocalname, 6) == 0)
+ c = 6;
+ else
+ c = 0;
+#if SPOOLDIR_BSD43
+ if (c > 0 && zsimple[c + 2] == 'X')
+ c++;
+#endif /* SPOOLDIR_BSD43 */
+ if (c > 0)
+ {
+ zalloc = (char *) alloca (c + 3);
+ strncpy (zalloc, zsimple, c + 2);
+ zalloc[c + 2] = '\0';
+
+ /* If we truncated the system name, and there is no existing
+ directory with the truncated name, then just use D.. */
+ if (ftruncated && ! fsdirectory_exists (zalloc))
+ return zsappend ("D.", zsimple);
+
+ return zsappend (zalloc, zsimple);
+ }
+ else
+ return zsappend ("D.", zsimple);
+ }
+#endif /* SPOOLDIR_BSD42 | SPOOLDIR_BSD43 */
+#if SPOOLDIR_BNU
+ return zsappend (zsystem, zsimple);
+#endif /* SPOOLDIR_BNU */
+#if SPOOLDIR_ULTRIX
+ {
+ int c;
+ boolean ftruncated;
+ char *zalloc;
+ const char *zdir;
+
+ /* D.LOCALX in D.LOCALX/, D.LOCAL in D.LOCAL/, others in D./. */
+
+ ftruncated = TRUE;
+ if (strncmp (zsimple + 2, zLocalname, strlen (zLocalname)) == 0)
+ {
+ c = strlen (zLocalname);
+ ftruncated = FALSE;
+ }
+ else if (strncmp (zsimple + 2, zLocalname, 7) == 0)
+ c = 7;
+ else if (strncmp (zsimple + 2, zLocalname, 6) == 0)
+ c = 6;
+ else
+ c = 0;
+ if (c > 0 && zsimple[c + 2] == 'X')
+ c++;
+ if (c > 0)
+ {
+ zalloc = (char *) alloca (c + 3);
+ strncpy (zalloc, zsimple, c + 2);
+ zalloc[c + 2] = '\0';
+ zdir = zalloc;
+
+ /* If we truncated the name, and there is no directory for
+ the truncated name, then don't use it. */
+ if (ftruncated)
+ {
+ char *zlook;
+
+ zlook = (char *) alloca (c + 20 + strlen (zsystem));
+ if (fsultrix_has_spool (zsystem))
+ sprintf (zlook, "sys/%s/%s", zsystem, zdir);
+ else
+ sprintf (zlook, "sys/DEFAULT/%s", zdir);
+ if (! fsdirectory_exists (zlook))
+ zdir = "D.";
+ }
+ }
+ else
+ zdir = "D.";
+
+ if (fsultrix_has_spool (zsystem))
+ return zsappend4 ("sys", zsystem, zdir, zsimple);
+ else
+ return zsappend4 ("sys", "DEFAULT", zdir, zsimple);
+ }
+#endif /* SPOOLDIR_ULTRIX */
+#if SPOOLDIR_TAYLOR
+ if (zsimple[2] == 'X')
+ return zsappend3 (zsystem, "D.X", zsimple);
+ else
+ return zsappend3 (zsystem, "D.", zsimple);
+#endif /* SPOOLDIR_TAYLOR */
+
+ /* Files beginning with X. are execute files. It is important
+ for security reasons that we know the system which created
+ the X. file. This is easy under SPOOLDIR_BNU or
+ SPOOLDIR_TAYLOR, because the file will be in a directory
+ named for the system. Under other schemes, we must get the
+ system name from the X. file name. To prevent security
+ violations, we set the system name directly here; this will
+ cause problems if the maximum file name length is too short,
+ but hopefully no problem will occur since any System V
+ systems will be using either BNU or TAYLOR. */
+
+ case 'X':
+#if ! SPOOLDIR_BNU && ! SPOOLDIR_TAYLOR
+ if (strncmp (zsimple + 2, zsystem, strlen (zsimple) - 7) != 0)
+ {
+ char *zcopy;
+
+ zcopy = (char *) alloca (strlen (zsystem) + 8);
+ sprintf (zcopy, "X.%s%s", zsystem,
+ zsimple + strlen (zsimple) - 5);
+ zsimple = zcopy;
+ }
+#endif /* ! SPOOLDIR_BNU && ! SPOOLDIR_TAYLOR */
+
+#if SPOOLDIR_V2 | SPOOLDIR_BSD42
+ return zscopy (zsimple);
+#endif
+#if SPOOLDIR_BSD43
+ return zsappend ("X.", zsimple);
+#endif
+#if SPOOLDIR_BNU
+ return zsappend (zsystem, zsimple);
+#endif
+#if SPOOLDIR_ULTRIX
+ if (fsultrix_has_spool (zsystem))
+ return zsappend4 ("sys", zsystem, "X.", zsimple);
+ else
+ return zsappend4 ("sys", "DEFAULT", "X.", zsimple);
+#endif
+#if SPOOLDIR_TAYLOR
+ return zsappend3 (zsystem, "X.", zsimple);
+#endif
+
+ default:
+#if DEBUG > 0
+ ulog (LOG_FATAL, "zsfind_file: Can't happen");
+#endif /* DEBUG */
+ return NULL;
+ }
+ /*NOTREACHED*/
+}
+\f
+/* Get the status of a system. */
+
+/*ARGSUSED*/
+boolean
+fsysdep_get_status (qsys, qret)
+ const struct ssysteminfo *qsys;
+ struct sstatus *qret;
+{
+ const char *zname;
+ FILE *e;
+ char *zline;
+ char *zend, *znext;
+ boolean fbad;
+ int istat;
+
+ zname = zsappend (".Status", qsys->zname);
+ e = fopen (zname, "r");
+ if (e == NULL)
+ {
+ if (errno != ENOENT)
+ {
+ ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno));
+ return FALSE;
+ }
+ zline = NULL;
+ }
+ else
+ {
+ zline = zfgets (e, FALSE);
+ (void) fclose (e);
+ }
+
+ if (zline == NULL)
+ {
+ /* There is either no status file for this system, or it's been
+ truncated, so fake a good status. */
+ qret->ttype = STATUS_COMPLETE;
+ qret->cretries = 0;
+ qret->ilast = 0;
+ qret->cwait = 0;
+ return TRUE;
+ }
+
+ /* It turns out that scanf is not used much in this program, so for
+ the benefit of small computers we avoid linking it in. This is
+ basically
+
+ sscanf (zline, "%d %d %ld %d", &qret->ttype, &qret->cretries,
+ &qret->ilast, &qret->cwait);
+
+ except that it's done with strtol. */
+
+ fbad = FALSE;
+ istat = (int) strtol (zline, &zend, 10);
+ if (zend == zline)
+ fbad = TRUE;
+
+ /* On some systems it may be appropriate to map system dependent status
+ values on to our status values. Perhaps someday. */
+
+ if (istat < 0 || istat >= (int) STATUS_VALUES)
+ istat = (int) STATUS_COMPLETE;
+ qret->ttype = (enum tstatus_type) istat;
+ znext = zend;
+ qret->cretries = (int) strtol (znext, &zend, 10);
+ if (zend == znext)
+ fbad = TRUE;
+ znext = zend;
+ qret->ilast = strtol (znext, &zend, 10);
+ if (zend == znext)
+ fbad = TRUE;
+ znext = zend;
+ qret->cwait = (int) strtol (znext, &zend, 10);
+ if (zend == znext)
+ fbad = TRUE;
+
+ xfree ((pointer) zline);
+
+ if (fbad)
+ {
+ ulog (LOG_ERROR, "Bad format of status file for %s", qsys->zname);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Set the status of a remote system. We assume the system is locked
+ when this is called. */
+
+/*ARGSUSED*/
+boolean
+fsysdep_set_status (qsys, qset)
+ const struct ssysteminfo *qsys;
+ const struct sstatus *qset;
+{
+ const char *zname;
+ FILE *e;
+ int istat;
+
+ zname = zsappend (".Status", qsys->zname);
+
+ e = esysdep_fopen (zname, TRUE, FALSE, TRUE);
+ if (e == NULL)
+ return FALSE;
+ istat = (int) qset->ttype;
+
+ /* On some systems it may be appropriate to map istat onto a system
+ dependent number. Perhaps someday. */
+
+ fprintf (e, "%d %d %ld %d %s %s\n", istat, qset->cretries,
+ qset->ilast, qset->cwait, azStatus[(int) qset->ttype],
+ qsys->zname);
+ if (fclose (e) != 0)
+ {
+ ulog (LOG_ERROR, "fclose: %s", strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+\f
+/* Get the real name of a spool file. */
+
+const char *
+zsysdep_spool_file_name (qsys, zfile)
+ const struct ssysteminfo *qsys;
+ const char *zfile;
+{
+ return zsfind_file (zfile, qsys->zname);
+}
+
+/* Expand a file name on the local system. The qsys argument is only
+ used to determine which public directory to use. */
+
+const char *
+zsysdep_real_file_name (qsys, zfile, zname)
+ const struct ssysteminfo *qsys;
+ const char *zfile;
+ const char *zname;
+{
+ const char *ztry;
+ char *zlook;
+
+ if (zfile[0] == '/')
+ ztry = zfile;
+ else if (zfile[0] == '~')
+ {
+ const char *z;
+ char *zcopy;
+
+ z = zstilde_expand (qsys, zfile);
+ zcopy = (char *) alloca (strlen (z) + 1);
+ strcpy (zcopy, z);
+ ztry = zcopy;
+ }
+ else
+ {
+ const char *zpub, *z;
+ char *zcopy;
+
+ /* Put the file in the public directory. */
+ if (qsys == NULL || qsys->zpubdir == NULL)
+ zpub = zPubdir;
+ else
+ zpub = qsys->zpubdir;
+ z = zsappend (zpub, zfile);
+ zcopy = (char *) alloca (strlen (z) + 1);
+ strcpy (zcopy, z);
+ ztry = zcopy;
+ }
+
+ /* If we don't have a file name to use within a directory, or we
+ haven't named a directory, we use what we've got so far. If the
+ name ends in a '/', it is assumed to name a directory. */
+
+ if (zname == NULL)
+ return zscopy (ztry);
+
+ if (ztry[strlen (ztry) - 1] != '/')
+ {
+ if (! fsdirectory_exists (ztry))
+ return zscopy (ztry);
+ }
+ else
+ {
+ char *zcopy;
+ int clen;
+
+ clen = strlen (ztry);
+ zcopy = (char *) alloca (clen + 1);
+ strcpy (zcopy, ztry);
+ zcopy[clen - 1] = '\0';
+ ztry = zcopy;
+ }
+
+ /* Get a name out of zname and tag it on. */
+
+ zlook = strrchr (zname, '/');
+ if (zlook != NULL)
+ zname = zlook + 1;
+
+ return zsappend (ztry, zname);
+}
+
+/* Return a file name within a directory. */
+
+const char *
+zsysdep_in_dir (zdir, zfile)
+ const char *zdir;
+ const char *zfile;
+{
+ if (fsdirectory_exists (zdir))
+ return zsappend (zdir, zfile);
+ else
+ return zdir;
+}
+\f
+/* Open a file to send to another system, and return the mode and
+ the size. */
+
+/*ARGSUSED*/
+openfile_t
+esysdep_open_send (qsys, zfile, fcheck, zuser, pimode, pcbytes, pfgone)
+ const struct ssysteminfo *qsys;
+ const char *zfile;
+ boolean fcheck;
+ const char *zuser;
+ unsigned int *pimode;
+ long *pcbytes;
+ boolean *pfgone;
+{
+ struct stat s;
+ openfile_t e;
+ int o;
+
+ if (pfgone != NULL)
+ *pfgone = FALSE;
+
+ if (fsdirectory_exists (zfile))
+ {
+ ulog (LOG_ERROR, "%s: is a directory", zfile);
+ return EFILECLOSED;
+ }
+#if USE_STDIO
+ e = fopen (zfile, BINREAD);
+ if (e == NULL)
+ {
+ ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno));
+ if (pfgone != NULL && errno == ENOENT)
+ *pfgone = TRUE;
+ return NULL;
+ }
+ o = fileno (e);
+#else
+ e = open (zfile, O_RDONLY, 0);
+ if (e == -1)
+ {
+ ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno));
+ if (pfgone != NULL && errno == ENOENT)
+ *pfgone = TRUE;
+ return -1;
+ }
+ o = e;
+#endif
+
+ if (fstat (o, &s) == -1)
+ {
+ ulog (LOG_ERROR, "fstat: %s", strerror (errno));
+ s.st_mode = 0666;
+ }
+
+ /* We have to recheck the file permission, although we probably
+ checked it already, because otherwise there would be a window in
+ which somebody could change the contents of a symbolic link to
+ point to some file which was only readable by uucp. */
+ if (fcheck)
+ {
+ if (! fsuser_access (&s, R_OK, zuser))
+ {
+ ulog (LOG_ERROR, "%s: %s", zfile, strerror (EACCES));
+ (void) ffileclose (e);
+ return EFILECLOSED;
+ }
+ }
+
+ *pimode = s.st_mode & 0777;
+ *pcbytes = s.st_size;
+ return e;
+}
+\f
+/* Get a temporary file name. */
+
+/*ARGSUSED*/
+const char *
+zstemp_file (qsys)
+ const struct ssysteminfo *qsys;
+{
+ static int icount;
+ char *zret;
+
+#if SPOOLDIR_V2 | SPOOLDIR_BSD42
+ {
+ static char ab[sizeof "TM.12345.123"];
+
+ sprintf (ab, "TM.%05d.%03d", getpid (), icount);
+ zret = ab;
+ }
+#endif
+#if SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR
+ {
+ static char ab[sizeof ".Temp/TM.12345.123"];
+
+ sprintf (ab, ".Temp/TM.%05d.%03d", getpid (), icount);
+ zret = ab;
+ }
+#endif
+#if SPOOLDIR_BNU
+ {
+ static char *z;
+ static int calc;
+ int cneed;
+
+ cneed = strlen (qsys->zname) + sizeof "/TM.12345.123";
+ if (cneed > calc)
+ {
+ xfree ((pointer) z);
+ z = (char *) xmalloc (cneed);
+ calc = cneed;
+ }
+ sprintf (z, "%s/TM.%05d.%03d", qsys->zname, getpid (), icount);
+ zret = z;
+ }
+#endif
+
+ ++icount;
+
+ return zret;
+}
+
+/* Open a temporary file to receive into. This should, perhaps, check
+ that we have write permission on the receiving directory, but it
+ doesn't. It is supposed to set *pcbytes to the size of the largest
+ file that can be accepted. */
+
+/*ARGSUSED*/
+openfile_t
+esysdep_open_receive (qsys, zto, pztemp, pcbytes)
+ const struct ssysteminfo *qsys;
+ const char *zto;
+ const char **pztemp;
+ long *pcbytes;
+{
+ const char *z;
+ int o;
+ openfile_t e;
+ long c1, c2;
+ char *zcopy, *zslash;
+
+ z = zstemp_file (qsys);
+
+ o = creat (z, IPRIVATE_FILE_MODE);
+
+ if (o == -1)
+ {
+ if (errno == ENOENT)
+ {
+ if (! fsysdep_make_dirs (z, FALSE))
+ return EFILECLOSED;
+ o = creat (z, IPRIVATE_FILE_MODE);
+ }
+ if (o == -1)
+ {
+ ulog (LOG_ERROR, "creat (%s): %s", z, strerror (errno));
+ return EFILECLOSED;
+ }
+ }
+
+#if USE_STDIO
+ e = fdopen (o, (char *) BINWRITE);
+
+ if (e == NULL)
+ {
+ ulog (LOG_ERROR, "fdopen (%s): %s", z, strerror (errno));
+ (void) close (o);
+ (void) remove (z);
+ return NULL;
+ }
+#else
+ e = o;
+#endif
+
+ *pztemp = z;
+
+ /* Try to determine the amount of free space available for the
+ temporary file and for the final destination. This code is
+ mostly from David MacKenzie's df program. */
+
+ c1 = (long) -1;
+ c2 = (long) -1;
+
+ zcopy = (char *) alloca (strlen (zto) + 1);
+ strcpy (zcopy, zto);
+ zslash = strrchr (zcopy, '/');
+ if (zslash != NULL)
+ *zslash = '\0';
+ else
+ {
+ zcopy[0] = '.';
+ zcopy[1] = '\0';
+ }
+
+ {
+#ifdef FS_STATVFS
+ struct statvfs s;
+
+ if (statvfs (z, &s) >= 0)
+ c1 = (long) s.f_bavail * (long) s.f_frsize;
+ if (statvfs (zcopy, &s) >= 0)
+ c2 = (long) s.f_bavail * (long) s.f_frsize;
+#endif
+#ifdef FS_USG_STATFS
+ struct statfs s;
+
+ /* This structure has an f_bsize field, but on many systems
+ f_bfree is measured in 512 byte blocks. On some systems,
+ f_bfree is measured in f_bsize byte blocks. Rather than
+ overestimate the amount of free space, this code assumes that
+ f_bfree is measuring 512 byte blocks. */
+
+ if (statfs (z, &s, sizeof s, 0) >= 0)
+ c1 = (long) s.f_bfree * (long) 512;
+ if (statfs (zcopy, &s, sizeof s, 0) >= 0)
+ c2 = (long) s.f_bfree * (long) 512;
+#endif
+#ifdef FS_MNTENT
+ struct statfs s;
+
+ if (statfs (z, &s) == 0)
+ c1 = (long) s.f_bavail * (long) s.f_bsize;
+ if (statfs (zcopy, &s) == 0)
+ c2 = (long) s.f_bavail * (long) s.f_bsize;
+#endif
+#ifdef FS_GETMNT
+ struct fs_data s;
+
+ if (statfs (z, &s) == 1)
+ c1 = (long) s.fd_req.bfreen * (long) 1024;
+ if (statfs (zcopy, &s) == 1)
+ c2 = (long) s.fd_req.bfreen * (long) 1024;
+#endif
+#ifdef FS_STATFS
+ struct statfs s;
+
+ if (statfs (z, &s) >= 0)
+ c1 = (long) s.f_bavail * (long) s.f_fsize;
+ if (statfs (zcopy, &s) >= 0)
+ c2 = (long) s.f_bavail * (long) s.f_fsize;
+#endif
+#ifdef FS_USTAT
+ struct stat sstat;
+ struct ustat s;
+
+ if (fstat (o, &sstat) == 0
+ && ustat (sstat.st_dev, &s) == 0)
+ c1 = (long) s.f_tfree * (long) 512;
+ if (stat (zcopy, &sstat) == 0
+ && ustat (sstat.st_dev, &s) == 0)
+ c2 = (long) s.f_tfree * (long) 512;
+#endif
+ }
+
+ if (c1 == (long) -1)
+ *pcbytes = c2;
+ else if (c2 == (long) -1)
+ *pcbytes = c1;
+ else if (c1 < c2)
+ *pcbytes = c1;
+ else
+ *pcbytes = c2;
+
+ return e;
+}
+
+/* After the temporary file has been completely written out, the file
+ is closed and this routine is called to move it into its final
+ location. If we fail, we must remove the temporary file. */
+
+boolean
+fsysdep_move_file (zorig, zto, imode, fcheck, zuser)
+ const char *zorig;
+ const char *zto;
+ unsigned int imode;
+ boolean fcheck;
+ const char *zuser;
+{
+ DEBUG_MESSAGE2 (DEBUG_SPOOLDIR,
+ "fsysdep_move_file: Moving %s to %s", zorig, zto);
+
+ /* Unless and until we add an option to change the ownership of the
+ file, the only information we want from the mode is whether the
+ file is executable or not. It would be dumb to create a file
+ with mode 0600, for example, since the owner will be uucp and the
+ recipient will not be able to read it. If we do not have an
+ absolute path to the file, which means that it is being moved
+ somewhere in the spool directory, we don't change the mode; in
+ general, the files in the spool directory should not be
+ publically readable. */
+
+ if (*zto != '/')
+ imode = 0;
+
+ if (imode != 0)
+ {
+ if ((imode & 0111) != 0)
+ imode = S_IRWXU | S_IRWXG | S_IRWXO;
+ else
+ imode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ }
+
+ /* Optionally make sure that zuser has write access on the
+ directory. We only check files that are not in the spool
+ directory. */
+ if (fcheck && *zto == '/')
+ {
+ char *zcopy;
+ char *zslash;
+ struct stat s;
+
+ zcopy = (char *) alloca (strlen (zto) + 1);
+ strcpy (zcopy, zto);
+ zslash = strrchr (zcopy, '/');
+ if (zslash == zcopy)
+ zslash[1] = '\0';
+ else
+ *zslash = '\0';
+
+ if (stat (zcopy, &s) != 0)
+ {
+ ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno));
+ (void) remove (zorig);
+ return FALSE;
+ }
+ if (! fsuser_access (&s, W_OK, zuser))
+ {
+ ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES));
+ (void) remove (zorig);
+ return FALSE;
+ }
+
+ /* A malicious user now has a few milliseconds to change a
+ symbolic link to a directory uucp has write permission on but
+ the user does not (the obvious choice being /usr/lib/uucp).
+ The only certain method I can come up with to close this race
+ is to fork an suid process which takes on the users identity
+ and does the actual copy. This is sufficiently high overhead
+ that I'm not going to do it. */
+ }
+
+ /* We try to use rename to move the file. */
+
+ if (rename (zorig, zto) == 0)
+ {
+ /* We must set the correct file mode, but don't worry if it doesn't
+ work. There should be an option for setting the owner, as
+ well. */
+ if (imode != 0)
+ (void) chmod (zto, imode);
+ return TRUE;
+ }
+
+ /* If this file is in the spool directory, make sure all directories
+ exist. */
+ if (*zto != '/' && errno == ENOENT)
+ {
+ if (! fsysdep_make_dirs (zto, FALSE))
+ {
+ (void) remove (zorig);
+ return FALSE;
+ }
+ if (rename (zorig, zto) == 0)
+ {
+ if (imode != 0)
+ (void) chmod (zto, imode);
+ return TRUE;
+ }
+ }
+
+ /* If we can't link across devices, we must copy the file by hand. */
+ if (errno != EXDEV)
+ {
+ ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto,
+ strerror (errno));
+ (void) remove (zorig);
+ return FALSE;
+ }
+
+ /* If the destination file is not in the spool directory, any
+ necessary directories should already have been made. */
+ if (! fcopy_file (zorig, zto, FALSE, *zto != '/'))
+ {
+ (void) remove (zorig);
+ return FALSE;
+ }
+
+ if (remove (zorig) < 0)
+ ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno));
+
+ if (imode != 0)
+ (void) chmod (zto, imode);
+
+ return TRUE;
+}
+\f
+/* Truncate a file to zero length. If this fails, it closes and
+ removes the file. We support a number of different means of
+ truncation, which is probably a waste of time since this function
+ is currently only called when the 'f' protocol resends a file. */
+
+#if HAVE_FTRUNCATE
+#undef HAVE_LTRUNC
+#define HAVE_LTRUNC 0
+#endif
+
+#if ! HAVE_FTRUNCATE && ! HAVE_LTRUNC
+#ifdef F_CHSIZE
+#define HAVE_F_CHSIZE 1
+#else /* ! defined (F_CHSIZE) */
+#ifdef F_FREESP
+#define HAVE_F_FREESP 1
+#endif /* defined (F_FREESP) */
+#endif /* ! defined (F_CHSIZE) */
+#endif /* ! HAVE_FTRUNCATE && ! HAVE_LTRUNC */
+
+openfile_t
+esysdep_truncate (e, zname)
+ openfile_t e;
+ const char *zname;
+{
+ int o;
+
+#if HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP
+ int itrunc;
+
+ if (! ffilerewind (e))
+ {
+ ulog (LOG_ERROR, "rewind: %s", strerror (errno));
+ (void) ffileclose (e);
+ (void) remove (zname);
+ return EFILECLOSED;
+ }
+
+#if USE_STDIO
+ o = fileno (e);
+#else
+ o = e;
+#endif
+
+#if HAVE_FTRUNCATE
+ itrunc = ftruncate (o, 0);
+#endif
+#if HAVE_LTRUNC
+ itrunc = ltrunc (o, (long) 0, SEEK_SET);
+#endif
+#if HAVE_F_CHSIZE
+ itrunc = fcntl (o, F_CHSIZE, (off_t) 0);
+#endif
+#if HAVE_F_FREESP
+ /* This selection is based on an implementation of ftruncate by
+ kucharsk@Solbourne.com (William Kucharski). */
+ {
+ struct flock fl;
+
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = 0;
+ fl.l_type = F_WRLCK;
+
+ itrunc = fcntl (o, F_FREESP, &fl);
+ }
+#endif
+
+ if (itrunc != 0)
+ {
+#if HAVE_FTRUNCATE
+ ulog (LOG_ERROR, "ftruncate: %s", strerror (errno));
+#endif
+#ifdef HAVE_LTRUNC
+ ulog (LOG_ERROR, "ltrunc: %s", strerror (errno));
+#endif
+#ifdef HAVE_F_CHSIZE
+ ulog (LOG_ERROR, "fcntl (F_CHSIZE): %s", strerror (errno));
+#endif
+#ifdef HAVE_F_FREESP
+ ulog (LOG_ERROR, "fcntl (F_FREESP): %s", strerror (errno));
+#endif
+
+ (void) ffileclose (e);
+ (void) remove (zname);
+ return EFILECLOSED;
+ }
+
+ return e;
+#else /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC) */
+ (void) ffileclose (e);
+ (void) remove (zname);
+
+ o = creat (zname, IPRIVATE_FILE_MODE);
+
+ if (o == -1)
+ {
+ ulog (LOG_ERROR, "open (%s): %s", zname, strerror (errno));
+ return EFILECLOSED;
+ }
+
+#if USE_STDIO
+ e = fdopen (o, (char *) BINWRITE);
+
+ if (e == NULL)
+ {
+ ulog (LOG_ERROR, "fdopen (%s): %s", zname, strerror (errno));
+ (void) close (o);
+ (void) remove (zname);
+ return NULL;
+ }
+#else /* ! USE_STDIO */
+ e = o;
+#endif /* ! USE_STDIO */
+
+ return e;
+#endif /* ! HAVE_FTRUNCATE */
+}
+\f
+/* Lock something. If the fspooldir argument is TRUE, the argument is
+ a file name relative to the spool directory; otherwise the argument
+ is a simple file name which should be created in the system lock
+ directory (under BNU this is /etc/locks). */
+
+/*ARGSUSED*/
+boolean
+fsdo_lock (zlock, fspooldir)
+ const char *zlock;
+ boolean fspooldir;
+{
+ const char *zpath, *zslash;
+ int cslash;
+ char *ztempfile;
+ char abtempfile[20];
+ int o;
+ pid_t ime;
+#if HAVE_V2_LOCKFILES
+ int i;
+#else
+ char ab[12];
+#endif
+ int cwrote;
+ const char *zerr;
+ boolean fret;
+
+#ifdef LOCKDIR
+ if (fspooldir)
+ zpath = zlock;
+ else
+ {
+ char *zalc;
+
+ zalc = (char *) alloca (sizeof LOCKDIR + strlen (zlock) + 1);
+ sprintf (zalc, "%s/%s", LOCKDIR, zlock);
+ zpath = zalc;
+ }
+#else /* ! defined (LOCKDIR) */
+ zpath = zlock;
+#endif
+
+ ime = getpid ();
+
+ /* We do the actual lock by creating a file and then linking it to
+ the final file name we want. This avoids race conditions due to
+ one process checking the file before we have finished writing it,
+ and also works even if we are somehow running as root.
+
+ First, create the file in the right directory (we must create the
+ file in the same directory since otherwise we might attempt a
+ cross-device link). */
+ zslash = strrchr (zpath, '/');
+ if (zslash == NULL)
+ cslash = 0;
+ else
+ cslash = zslash - zpath + 1;
+
+ ztempfile = (char *) alloca (cslash + sizeof "TMP1234567890");
+ strncpy (ztempfile, zpath, cslash);
+ sprintf (abtempfile, "TMP%010d", (int) ime);
+ ztempfile[cslash] = '\0';
+ strcat (ztempfile, abtempfile);
+
+ o = creat (ztempfile, IPUBLIC_FILE_MODE);
+ if (o < 0)
+ {
+ if (errno == ENOENT)
+ {
+ if (! fsysdep_make_dirs (ztempfile, FALSE))
+ return FALSE;
+ o = creat (ztempfile, IPUBLIC_FILE_MODE);
+ }
+ if (o < 0)
+ {
+ ulog (LOG_ERROR, "open (%s): %s", ztempfile, strerror (errno));
+ return FALSE;
+ }
+ }
+
+#if HAVE_V2_LOCKFILES
+ i = ime;
+ cwrote = write (o, &i, sizeof i);
+#else
+ sprintf (ab, "%10d\n", (int) ime);
+ cwrote = write (o, ab, strlen (ab));
+#endif
+
+ zerr = NULL;
+ if (cwrote < 0)
+ zerr = "write";
+ if (close (o) < 0)
+ zerr = "close";
+ if (zerr != NULL)
+ {
+ ulog (LOG_ERROR, "%s (%s): %s", zerr, ztempfile, strerror (errno));
+ (void) remove (ztempfile);
+ return FALSE;
+ }
+
+ /* Now try to link the file we just created to the lock file that we
+ want. If it fails, try reading the existing file to make sure
+ the process that created it still exists. We do this in a loop
+ to make it easy to retry if the old locking process no longer
+ exists. */
+
+ fret = TRUE;
+ o = -1;
+ zerr = NULL;
+
+ while (link (ztempfile, zpath) != 0)
+ {
+ int cgot;
+ int ipid;
+
+ fret = FALSE;
+
+ if (errno != EEXIST)
+ {
+ ulog (LOG_ERROR, "link (%s, %s): %s", ztempfile, zpath,
+ strerror (errno));
+ break;
+ }
+
+ o = open (zpath, O_RDWR, 0);
+ if (o < 0)
+ {
+ zerr = "open";
+ break;
+ }
+
+ /* The race starts here. See below for a discussion. */
+
+#if HAVE_V2_LOCKFILES
+ cgot = read (o, &i, sizeof i);
+#else
+ cgot = read (o, ab, sizeof ab - 1);
+#endif
+
+ if (cgot < 0)
+ {
+ zerr = "read";
+ break;
+ }
+
+#if HAVE_V2_LOCKFILES
+ ipid = i;
+#else
+ ab[cgot] = '\0';
+ ipid = atoi (ab);
+#endif
+
+ /* If the process still exists, we will get EPERM rather than
+ ESRCH. We then return FALSE to indicate that we cannot make
+ the lock. */
+ if (kill (ipid, 0) == 0 || errno == EPERM)
+ break;
+
+ /* On NFS, the link might have actually succeeded even though we
+ got a failure return. This can happen if the original
+ acknowledgement was lost or delayed and the operation was
+ retried. In this case the pid will be our own. This
+ introduces a rather improbable race condition: if a stale
+ lock was left with our process ID in it, and another process
+ just did the above kill but has not yet changed the lock file
+ to hold its own process ID, we could start up and make it all
+ the way to here and think we have the lock. I'm not going to
+ worry about this possibility. */
+ if (ipid == ime)
+ {
+ fret = TRUE;
+ break;
+ }
+
+ ulog (LOG_ERROR, "Found stale lock %s held by process %d",
+ zpath, ipid);
+
+ /* This is a stale lock, created by a process that no longer
+ exists.
+
+ Now we could remove the file, but that would be a race
+ condition. If we were interrupted any time after we did the
+ read until we did the remove, another process could get in,
+ open the file, find that it was a stale lock, remove the file
+ and create a new one. When we woke up we would remove the
+ file the other process just created.
+
+ These files are being generated partially for the benefit of
+ cu, and it would be nice to avoid the race however cu avoids
+ it, so that the programs remain compatible. Unfortunately,
+ nobody seems to know how cu avoids the race, or even if it
+ tries to avoid it at all.
+
+ There are a few ways to avoid the race. We could use kernel
+ locking primitives, but they may not be available. We could
+ link to a special file name, but if that file were left lying
+ around then no stale lock could ever be broken (Henry Spencer
+ would think this was a good thing).
+
+ Instead I've implemented the following procedure: seek to the
+ start of the file, write our pid into it, sleep for five
+ seconds, and then make sure our pid is still there. Anybody
+ who checks the file while we're asleep will find our pid
+ there and fail the lock. The only race will come from
+ another process which has done the read by the time we do our
+ write. That process will then have five seconds to do its
+ own write. When we wake up, we'll notice that our pid is no
+ longer in the file, and retry the lock from the beginning.
+
+ This relies on the atomicity of write(2). If it possible for
+ the writes of two processes to be interleaved, the two
+ processes could livelock. POSIX unfortunately leaves this
+ case explicitly undefined; however, given that the write is
+ of less than a disk block, it's difficult to imagine an
+ interleave occurring.
+
+ Note that this is still a race. If it takes the second
+ process more than five seconds to do the kill, the lseek, and
+ the write, both processes will think they have the lock.
+ Perhaps the length of time to sleep should be configurable.
+ Even better, perhaps I should add a configuration option to
+ use a permanent lock file, which eliminates any race and
+ forces the installer to be aware of the existence of the
+ permanent lock file.
+
+ For the benefit of cu, we stat the file after the sleep, to
+ make sure some cu program hasn't deleted it for us. */
+
+ if (lseek (o, (off_t) 0, SEEK_SET) != 0)
+ {
+ zerr = "lseek";
+ break;
+ }
+
+#if HAVE_V2_LOCKFILES
+ i = ime;
+ cwrote = write (o, &i, sizeof i);
+#else
+ sprintf (ab, "%10d\n", (int) ime);
+ cwrote = write (o, ab, strlen (ab));
+#endif
+
+ if (cwrote < 0)
+ {
+ zerr = "write";
+ break;
+ }
+
+ (void) sleep (5);
+
+ if (lseek (o, (off_t) 0, SEEK_SET) != 0)
+ {
+ zerr = "lseek";
+ break;
+ }
+
+#if HAVE_V2_LOCKFILES
+ cgot = read (o, &i, sizeof i);
+#else
+ cgot = read (o, ab, sizeof ab - 1);
+#endif
+
+ if (cgot < 0)
+ {
+ zerr = "read";
+ break;
+ }
+
+#if HAVE_V2_LOCKFILES
+ ipid = i;
+#else
+ ab[cgot] = '\0';
+ ipid = atoi (ab);
+#endif
+
+ if (ipid == ime)
+ {
+ struct stat sfile, sdescriptor;
+
+ /* It looks like we have the lock. Do the final stat
+ check. */
+
+ if (stat (zpath, &sfile) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ zerr = "stat";
+ break;
+ }
+ /* Loop around and try again. */
+ }
+ else
+ {
+ if (fstat (o, &sdescriptor) < 0)
+ {
+ zerr = "fstat";
+ break;
+ }
+
+ if (sfile.st_ino == sdescriptor.st_ino
+ && sfile.st_dev == sdescriptor.st_dev)
+ {
+ /* Close the file before assuming we've succeeded to
+ pick up any trailing errors. */
+ if (close (o) < 0)
+ {
+ zerr = "close";
+ break;
+ }
+
+ o = -1;
+
+ /* We have the lock. */
+ fret = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Loop around and try the lock again. We keep doing this until
+ the lock file holds a pid that exists. */
+
+ (void) close (o);
+ o = -1;
+ fret = TRUE;
+ }
+
+ if (zerr != NULL)
+ ulog (LOG_ERROR, "%s (%s): %s", zerr, zpath, strerror (errno));
+
+ if (o >= 0)
+ (void) close (o);
+
+ /* It would be nice if we could leave the temporary file around for
+ future calls, but considering that we create lock files in
+ various different directories it's probably more trouble than
+ it's worth. */
+ if (remove (ztempfile) != 0)
+ ulog (LOG_ERROR, "remove (%s): %s", ztempfile, strerror (errno));
+
+ return fret;
+}
+
+/* Unlock something. The fspooldir argument is as in fsdo_lock. */
+
+boolean
+fsdo_unlock (zlock, fspooldir)
+ const char *zlock;
+ boolean fspooldir;
+{
+ const char *zpath;
+
+#ifdef LOCKDIR
+ if (fspooldir)
+ zpath = zlock;
+ else
+ {
+ char *zalc;
+
+ zalc = (char *) alloca (sizeof LOCKDIR + strlen (zlock) + 1);
+ sprintf (zalc, "%s/%s", LOCKDIR, zlock);
+ zpath = zalc;
+ }
+#else /* ! defined (LOCKDIR) */
+ zpath = zlock;
+#endif
+
+ if (remove (zpath) == 0
+ || errno == ENOENT)
+ return TRUE;
+ else
+ {
+ ulog (LOG_ERROR, "remove (%s): %s", zpath, strerror (errno));
+ return FALSE;
+ }
+}
+
+/* Lock a remote system. */
+
+/*ARGSUSED*/
+boolean
+fsysdep_lock_system (qsys)
+ const struct ssysteminfo *qsys;
+{
+ char *z;
+
+ z = (char *) alloca (strlen (qsys->zname) + sizeof "LCK..");
+ sprintf (z, "LCK..%.8s", qsys->zname);
+ return fsdo_lock (z, FALSE);
+}
+
+/* Unlock a remote system. */
+
+/*ARGSUSED*/
+boolean
+fsysdep_unlock_system (qsys)
+ const struct ssysteminfo *qsys;
+{
+ char *z;
+
+ z = (char *) alloca (strlen (qsys->zname) + sizeof "LCK..");
+ sprintf (z, "LCK..%.8s", qsys->zname);
+ return fsdo_unlock (z, FALSE);
+}
+\f
+/* Get a new command sequence number (this is not a sequence number to
+ be used for communicating with another system, but a sequence
+ number to be used when generating the name of a command file).
+ The sequence number is placed into zseq, which should be five
+ characters long. */
+
+#define CSEQLEN (4)
+
+static boolean
+fscmd_seq (zsystem, zseq)
+ const char *zsystem;
+ char *zseq;
+{
+ int ctries;
+ const char *zfile;
+ int o;
+ int i;
+
+ /* Lock the sequence file. This may not be correct for all systems,
+ but it only matters if the system UUCP and this UUCP are running
+ at the same time. */
+
+ ctries = 0;
+ while (! fsdo_lock ("LCK..SEQ", TRUE))
+ {
+ ++ctries;
+ if (ctries > 10)
+ {
+ ulog (LOG_ERROR, "Can't lock sequence file");
+ return FALSE;
+ }
+ sleep (1);
+ }
+
+#if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43
+ zfile = "SEQF";
+#endif
+#if SPOOLDIR_BNU
+ {
+ char *zalc;
+
+ zalc = (char *) alloca (strlen (zsystem) + sizeof ".Sequence/");
+ sprintf (zalc, ".Sequence/%s", zsystem);
+ zfile = zalc;
+ }
+#endif
+#if SPOOLDIR_ULTRIX
+ if (fsultrix_has_spool (zsystem))
+ {
+ char *zalc;
+
+ zalc = (char *) alloca (strlen (zsystem) + sizeof "sys//.SEQF");
+ sprintf (zalc, "sys/%s/.SEQF", zsystem);
+ zfile = zalc;
+ }
+ else
+ zfile = "sys/DEFAULT/.SEQF";
+#endif /* SPOOLDIR_ULTRIX */
+#if SPOOLDIR_TAYLOR
+ {
+ char *zalc;
+
+ zalc = (char *) alloca (strlen (zsystem) + sizeof "/SEQF");
+ sprintf (zalc, "%s/SEQF", zsystem);
+ zfile = zalc;
+ }
+#endif /* SPOOLDIR_TAYLOR */
+
+#ifdef O_CREAT
+ o = open (zfile, O_RDWR | O_CREAT, IPUBLIC_FILE_MODE);
+#else
+ o = open (zfile, O_RDWR);
+ if (o < 0 && errno == ENOENT)
+ {
+ o = creat (zfile, IPUBLIC_FILE_MODE);
+ if (o >= 0)
+ {
+ (void) close (o);
+ o = open (zfile, O_RDWR);
+ }
+ }
+#endif
+
+ if (o < 0)
+ {
+ if (errno == ENOENT)
+ {
+ if (! fsysdep_make_dirs (zfile, FALSE))
+ {
+ (void) fsdo_unlock ("LCK..SEQ", TRUE);
+ return FALSE;
+ }
+#ifdef O_CREAT
+ o = open (zfile, O_RDWR | O_CREAT, IPUBLIC_FILE_MODE);
+#else
+ o = creat (zfile, IPUBLIC_FILE_MODE);
+ if (o >= 0)
+ {
+ (void) close (o);
+ o = open (zfile, O_RDWR);
+ }
+#endif
+ }
+ if (o < 0)
+ {
+ ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno));
+ (void) fsdo_unlock ("LCK..SEQ", TRUE);
+ return FALSE;
+ }
+ }
+
+ if (read (o, zseq, CSEQLEN) != CSEQLEN)
+ strcpy (zseq, "0000");
+ zseq[CSEQLEN] = '\0';
+
+ /* We must add one to the sequence number and return the new value.
+ On Ultrix, arbitrary characters are allowed in the sequence number.
+ On other systems, the sequence number apparently must be in hex. */
+
+#if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_BNU
+ i = strtol (zseq, (char **) NULL, 16);
+ ++i;
+ if (i > 0xffff)
+ i = 0;
+ /* The sprintf argument has CSEQLEN built into it. */
+ sprintf (zseq, "%04x", (unsigned int) i);
+#endif /* SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_BNU */
+#if SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR
+ for (i = CSEQLEN - 1; i >= 0; i--)
+ {
+ if (zseq[i] == 'z')
+ {
+ zseq[i] = '0';
+ continue;
+ }
+
+ if (zseq[i] == '9')
+ zseq[i] = 'A';
+ else if (zseq[i] == 'Z')
+ zseq[i] = 'a';
+ else if ((zseq[i] >= '0' && zseq[i] < '9')
+ || (zseq[i] >= 'A' && zseq[i] < 'Z')
+ || (zseq[i] >= 'a' && zseq[i] < 'z'))
+ ++zseq[i];
+ else
+ {
+ /* A bad character was found; reset the entire sequence
+ number. */
+ ulog (LOG_ERROR,
+ "Bad sequence number %s for system %s",
+ zseq, zsystem);
+ strcpy (zseq, "0000");
+ }
+
+ break;
+ }
+#endif /* SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR */
+
+ if (lseek (o, (off_t) 0, SEEK_SET) < 0
+ || write (o, zseq, CSEQLEN) != CSEQLEN
+ || close (o) < 0)
+ {
+ ulog (LOG_ERROR, "lseek or write or close: %s", strerror (errno));
+ (void) close (o);
+ (void) fsdo_unlock ("LCK..SEQ", TRUE);
+ return FALSE;
+ }
+
+ (void) fsdo_unlock ("LCK..SEQ", TRUE);
+
+ return TRUE;
+}
+\f
+/* Get the name of a command or data file for a remote system. The
+ btype argument should be C for a command file or D for a data file.
+ If the grade of a data file is X, it is assumed that this is going
+ to become an execute file on some other system. The zsystem
+ argument is the system that the file will be transferred to. The
+ ztname argument will be set to a file name that could be passed to
+ zsysdep_spool_file_name. The zdname argument, if not NULL, will be
+ set to a data file name appropriate for the remote system. The
+ zxname argument, if not NULL, will be set to the name of an execute
+ file on the remote system. None of the names will be more than 14
+ characters long. */
+
+static const char *
+zsfile_name (btype, zsystem, bgrade, ztname, zdname, zxname)
+ int btype;
+ const char *zsystem;
+ int bgrade;
+ char *ztname;
+ char *zdname;
+ char *zxname;
+{
+ char abseq[CSEQLEN + 1];
+ char absimple[11 + CSEQLEN];
+ const char *zname;
+ struct stat s;
+
+ do
+ {
+ if (! fscmd_seq (zsystem, abseq))
+ return NULL;
+
+ if (btype == 'C')
+ {
+#if ! SPOOLDIR_TAYLOR
+ sprintf (absimple, "C.%.7s%c%s", zsystem, bgrade, abseq);
+#else
+ sprintf (absimple, "C.%c%s", bgrade, abseq);
+#endif
+ }
+ else if (btype == 'D')
+ {
+#if ! SPOOLDIR_TAYLOR
+ /* Note that a data file uses the local system's name. */
+ sprintf (absimple, "D.%.7s%c%s", zLocalname, bgrade, abseq);
+#else
+ if (bgrade == 'X')
+ sprintf (absimple, "D.X%s", abseq);
+ else
+ sprintf (absimple, "D.%s", abseq);
+#endif
+ }
+#if DEBUG > 0
+ else
+ ulog (LOG_FATAL, "zsfile_name: Can't happen (%d)", btype);
+#endif
+
+ zname = zsfind_file (absimple, zsystem);
+ if (zname == NULL)
+ return NULL;
+ }
+ while (stat (zname, &s) == 0);
+
+ if (ztname != NULL)
+ strcpy (ztname, absimple);
+
+ if (zdname != NULL)
+ sprintf (zdname, "D.%.7s%c%s", zLocalname, bgrade, abseq);
+
+ if (zxname != NULL)
+ sprintf (zxname, "X.%.7s%c%s", zLocalname, bgrade, abseq);
+
+ return zname;
+}
+
+/* Given a set of commands to execute for a remote system, create a
+ command file holding them. This creates a single command file
+ holding all the commands passed in. It returns a jobid. */
+
+const char *
+zsysdep_spool_commands (qsys, bgrade, ccmds, pascmds)
+ const struct ssysteminfo *qsys;
+ int bgrade;
+ int ccmds;
+ const struct scmd *pascmds;
+{
+ const char *z;
+ FILE *e;
+ int i;
+ const struct scmd *q;
+ const char *zjobid;
+
+#if DEBUG > 0
+ if (! FGRADE_LEGAL (bgrade))
+ ulog (LOG_FATAL, "Bad grade %d", bgrade);
+#endif
+
+ z = zsfile_name ('C', qsys->zname, bgrade, (char *) NULL, (char *) NULL,
+ (char *) NULL);
+ if (z == NULL)
+ return NULL;
+
+ e = esysdep_fopen (z, FALSE, FALSE, TRUE);
+ if (e == NULL)
+ return NULL;
+
+ for (i = 0, q = pascmds; i < ccmds; i++, q++)
+ {
+ switch (q->bcmd)
+ {
+ case 'S':
+ fprintf (e, "S %s %s %s -%s %s 0%o %s\n", q->zfrom, q->zto,
+ q->zuser, q->zoptions, q->ztemp, q->imode,
+ q->znotify);
+ break;
+ case 'R':
+ fprintf (e, "R %s %s %s -%s\n", q->zfrom, q->zto, q->zuser,
+ q->zoptions);
+ break;
+ case 'X':
+ fprintf (e, "X %s %s %s -%s\n", q->zfrom, q->zto, q->zuser,
+ q->zoptions);
+ break;
+ default:
+ ulog (LOG_ERROR,
+ "zsysdep_spool_commands: Unrecognized type %d",
+ q->bcmd);
+ (void) fclose (e);
+ (void) remove (z);
+ return NULL;
+ }
+ }
+
+ if (fclose (e) != 0)
+ {
+ ulog (LOG_ERROR, "fclose: %s", strerror (errno));
+ (void) remove (z);
+ return NULL;
+ }
+
+ zjobid = zsfile_to_jobid (qsys, z);
+ if (zjobid == NULL)
+ (void) remove (z);
+ return zjobid;
+}
+
+/* Return a name to use for a data file to be copied to another
+ system. The name returned will be for a real file. The ztname
+ argument, if not NULL, will be set to a name that could be passed
+ to zsysdep_spool_file_name to get back the return value of this
+ function. The zdname argument, if not NULL, will be set to a name
+ that the file could be given on another system. The zxname
+ argument, if not NULL, will be set to a name for an execute file on
+ another system. */
+
+const char *
+zsysdep_data_file_name (qsys, bgrade, ztname, zdname, zxname)
+ const struct ssysteminfo *qsys;
+ int bgrade;
+ char *ztname;
+ char *zdname;
+ char *zxname;
+{
+ return zsfile_name ('D', qsys->zname, bgrade, ztname, zdname, zxname);
+}
+
+/* Return a name for an execute file to be created locally. This is
+ used by uux to execute a command locally with remote files. */
+
+const char *
+zsysdep_xqt_file_name ()
+{
+ char abseq[CSEQLEN + 1];
+ char absx[11 + CSEQLEN];
+ const char *zname;
+ struct stat s;
+
+ while (TRUE)
+ {
+ if (! fscmd_seq (zLocalname, abseq))
+ return NULL;
+
+ sprintf (absx, "X.%.7sX%s", zLocalname, abseq);
+
+ zname = zsfind_file (absx, zLocalname);
+ if (zname == NULL)
+ return NULL;
+ if (stat (zname, &s) != 0)
+ break;
+ }
+
+ return zname;
+}
+\f
+/* Start getting a wildcarded file spec. We use the shell to expand
+ the wildcard. */
+
+static char *zSwildcard_alloc;
+static char *zSwildcard;
+
+boolean
+fsysdep_wildcard_start (qsys, zfile)
+ const struct ssysteminfo *qsys;
+ const char *zfile;
+{
+ char *zcmd;
+ const char *azargs[4];
+ FILE *e;
+ pid_t ipid;
+
+ zSwildcard_alloc = NULL;
+ zSwildcard = NULL;
+
+ if (*zfile == '~')
+ {
+ zfile = zstilde_expand (qsys, zfile);
+ if (zfile == NULL)
+ return FALSE;
+ }
+
+ if (*zfile != '/')
+ {
+ ulog (LOG_ERROR, "Relative path not permitted in wildcard");
+ return FALSE;
+ }
+
+ zcmd = (char *) alloca (sizeof ECHO_PROGRAM + sizeof " " + strlen (zfile));
+ sprintf (zcmd, "%s %s", ECHO_PROGRAM, zfile);
+
+ azargs[0] = "/bin/sh";
+ azargs[1] = "-c";
+ azargs[2] = zcmd;
+ azargs[3] = NULL;
+
+ e = espopen (azargs, TRUE, &ipid);
+ if (e == NULL)
+ {
+ ulog (LOG_ERROR, "espopen: %s", strerror (errno));
+ return FALSE;
+ }
+
+ zSwildcard_alloc = zfgets (e, FALSE);
+
+ if (iswait ((unsigned long) ipid, ECHO_PROGRAM) != 0)
+ {
+ xfree ((pointer) zSwildcard_alloc);
+ return FALSE;
+ }
+
+ if (zSwildcard_alloc == NULL)
+ return FALSE;
+
+ DEBUG_MESSAGE1 (DEBUG_EXECUTE,
+ "fsysdep_wildcard_start: got \"%s\"",
+ zSwildcard_alloc);
+
+ zSwildcard = zSwildcard_alloc;
+
+ return TRUE;
+}
+
+/* Get the next wildcard spec. */
+
+/*ARGSUSED*/
+const char *
+zsysdep_wildcard (qsys, zfile)
+ const struct ssysteminfo *qsys;
+ const char *zfile;
+{
+ char *zret;
+
+ if (zSwildcard_alloc == NULL || zSwildcard == NULL)
+ return NULL;
+
+ zret = zSwildcard;
+
+ while (*zSwildcard != '\0' && ! isspace (BUCHAR (*zSwildcard)))
+ ++zSwildcard;
+
+ if (*zSwildcard == '\0')
+ zSwildcard = NULL;
+ else
+ {
+ *zSwildcard = '\0';
+ ++zSwildcard;
+ while (*zSwildcard != '\0' && isspace (BUCHAR (*zSwildcard)))
+ ++zSwildcard;
+ if (*zSwildcard == '\0')
+ zSwildcard = NULL;
+ }
+
+ return zret;
+}
+
+/* Finish up getting wildcard specs. */
+
+boolean
+fsysdep_wildcard_end ()
+{
+ xfree ((pointer) zSwildcard_alloc);
+ zSwildcard_alloc = NULL;
+ zSwildcard = NULL;
+ return TRUE;
+}
+\f
+/* Get the current conversation sequence number for a remote system,
+ and increment it for next time. The conversation sequence number
+ is kept in a file named .SQ in the spool directory for that system.
+ This is not compatible with other versions of UUCP, but it makes
+ more sense to me. The sequence file is only used if specified in
+ the information for that system. In V2, the file
+ /usr/lib/uucp/SQFILE is searched for each system to get a
+ conversation sequence number. */
+
+long
+isysdep_get_sequence (qsys)
+ const struct ssysteminfo *qsys;
+{
+ FILE *e;
+ const char *zname;
+ struct stat s;
+ long iseq;
+
+ /* This will only be called when the system is locked anyhow, so there
+ is no need to use a separate lock for the conversation sequence
+ file. */
+
+ zname = zsappend (".Sequence", qsys->zname);
+
+ iseq = 0;
+ if (stat (zname, &s) == 0)
+ {
+ boolean fok;
+ char *zline;
+
+ /* The file should only be readable and writable by uucp. */
+
+ if ((s.st_mode & (S_IRWXG | S_IRWXO)) != 0)
+ {
+ ulog (LOG_ERROR,
+ "Bad file protection for conversation sequence file");
+ return -1;
+ }
+
+ /* The file already exists, so we don't have to worry about
+ its protection. */
+ e = fopen (zname, "r+");
+ if (e == NULL)
+ {
+ ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno));
+ return -1;
+ }
+
+ zline = zfgets (e, FALSE);
+
+ fok = TRUE;
+ if (zline == NULL)
+ fok = FALSE;
+ else
+ {
+ char *zend;
+
+ iseq = strtol (zline, &zend, 10);
+ if (zend == zline)
+ fok = FALSE;
+ xfree ((pointer) zline);
+ }
+
+ if (! fok)
+ {
+ ulog (LOG_ERROR, "Bad format for conversation sequence file");
+ return -1;
+ }
+
+ rewind (e);
+ }
+ else
+ {
+ e = esysdep_fopen (zname, FALSE, FALSE, TRUE);
+ if (e == NULL)
+ return -1;
+ }
+
+ ++iseq;
+
+ fprintf (e, "%ld", iseq);
+
+ if (fclose (e) != 0)
+ {
+ ulog (LOG_ERROR, "fclose: %s", strerror (errno));
+ return -1;
+ }
+
+ return iseq;
+}
+\f
+/* Get the Unix file mode of a file. */
+
+unsigned int
+isysdep_file_mode (zfile)
+ const char *zfile;
+{
+ struct stat s;
+
+ if (stat (zfile, &s) != 0)
+ {
+ ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno));
+ return 0;
+ }
+
+#if S_IRWXU != 0700
+ #error Files modes need to be translated
+#endif
+
+ /* We can't return 0, since that indicate an error. */
+ if ((s.st_mode & 0777) == 0)
+ return 0400;
+
+ return s.st_mode & 0777;
+}
+\f
+/* Translate a file name and an associated system into a job id.
+ These job ids are used by uustat. We use the system name attached
+ to the grade and sequence number. */
+
+const char *
+zsfile_to_jobid (qsys, zfile)
+ const struct ssysteminfo *qsys;
+ const char *zfile;
+{
+ char *zid;
+
+ zid = (char *) alloca (strlen (qsys->zname) + CSEQLEN + 2);
+ sprintf (zid, "%s%s", qsys->zname,
+ zfile + strlen (zfile) - CSEQLEN - 1);
+
+ return zscopy (zid);
+}
+
+/* Turn a job id back into a file name. */
+
+const char *
+zsjobid_to_file (zid, pzsystem)
+ const char *zid;
+ const char **pzsystem;
+{
+ int clen;
+ char abend[CSEQLEN + 2];
+ static char *zsys;
+ static int csyslen;
+ char abname[CSEQLEN + 11];
+
+ clen = strlen (zid);
+ strcpy (abend, zid + clen - CSEQLEN - 1);
+
+ if (clen - CSEQLEN > csyslen)
+ {
+ zsys = (char *) xrealloc ((pointer) zsys, clen - CSEQLEN);
+ csyslen = clen - CSEQLEN;
+ }
+
+ strncpy (zsys, zid, clen - CSEQLEN - 1);
+ zsys[clen - CSEQLEN - 1] = '\0';
+ if (pzsystem != NULL)
+ *pzsystem = zsys;
+
+ /* This must correspond to zsfile_name. */
+#if ! SPOOLDIR_TAYLOR
+ sprintf (abname, "C.%.7s%s", zsys, abend);
+#else
+ sprintf (abname, "C.%s", abend);
+#endif
+
+ return zsfind_file (abname, zsys);
+}
+\f
+#if ! HAVE_RENAME
+
+/* This is currently the only file which calls rename, so I've put my
+ fake rename function in here. */
+
+static int
+rename (zfrom, zto)
+ const char *zfrom;
+ const char *zto;
+{
+ /* Try to make the link without removing the old file first. */
+ if (link (zfrom, zto) == -1)
+ {
+ if (errno != EEXIST)
+ return -1;
+
+ /* Assume that this will never be called with zfrom the same as
+ zto. If it could be, this is wrong. */
+ (void) remove (zto);
+
+ if (link (zfrom, zto) == -1)
+ return -1;
+ }
+
+ return remove (zfrom);
+}
+
+#endif /* ! HAVE_RENAME */
+\f
+/*
+ Local variables:
+ mode:c
+ End:
+ */