2651 lines
66 KiB
Plaintext
2651 lines
66 KiB
Plaintext
/* 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.1.1.1 1993/03/21 09:45:37 cgd
|
||
initial import of 386bsd-0.1 sources
|
||
|
||
# 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.1.1.1 1993/03/21 09:45:37 cgd Exp $";
|
||
#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
|
||
|
||
/* 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. */
|
||
|
||
/* 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));
|
||
|
||
/* 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 */
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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*/
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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 */
|
||
}
|
||
|
||
/* 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);
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* 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);
|
||
}
|
||
|
||
#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 */
|
||
|
||
/*
|
||
Local variables:
|
||
mode:c
|
||
End:
|
||
*/
|