xref: /386bsd/usr/src/libexec/uucp/sys3.c (revision a2142627)
1 /* sys3.unx
2    The system dependent spool directory subroutines for Unix.
3 
4    Copyright (C) 1991, 1992 Ian Lance Taylor
5 
6    This file is part of the Taylor UUCP package.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22    The author of the program may be contacted at ian@airs.com or
23    c/o AIRS, P.O. Box 520, Waltham, MA 02254.
24 
25    $Log:	sys3.unx,v $
26 # Revision 1.2  92/05/13  05:42:07  rich
27 # ported to 386bsd
28 #
29 # Revision 1.1  1992/05/10  18:00:16  rich
30 # Initial revision
31 #
32    Revision 1.48  1992/04/01  22:36:48  ian
33    David J. MacKenzie: some USG_STATFS systems use 512 despite f_bsize
34 
35    Revision 1.47  1992/03/30  15:03:07  ian
36    Niels Baggesen: USG statfs has an f_bsize field
37 
38    Revision 1.46  1992/03/28  22:06:38  ian
39    Michael I Bushnell: renamed enum tstatus to avoid header file conflict
40 
41    Revision 1.45  1992/03/26  20:20:28  ian
42    Reduce race condition in fsdo_lock
43 
44    Revision 1.44  1992/03/15  01:54:46  ian
45    All execs are now done in isspawn, all waits are done in iswait
46 
47    Revision 1.43  1992/03/12  19:54:43  ian
48    Debugging based on types rather than number
49 
50    Revision 1.42  1992/03/10  20:45:58  ian
51    Check size of destination file system as well as temporary system
52 
53    Revision 1.41  1992/03/09  19:42:43  ian
54    Ted Lindgreen: don't send mail for nonexistent file
55 
56    Revision 1.40  1992/03/04  01:40:51  ian
57    Thomas Fischer: tweaked a bit for the NeXT
58 
59    Revision 1.39  1992/02/29  04:07:08  ian
60    Added -j option to uucp and uux
61 
62    Revision 1.38  1992/02/29  01:06:59  ian
63    Chip Salzenberg: recheck file permissions before sending
64 
65    Revision 1.37  1992/02/28  15:57:58  ian
66    Give error if esysdep_open_send is given a directory
67 
68    Revision 1.36  1992/02/24  22:05:30  ian
69    Roberto Biancardi: support F_CHSIZE and F_FREESP in esysdep_truncate
70 
71    Revision 1.35  1992/02/24  20:07:43  ian
72    John Theus: some systems don't have <fcntl.h>
73 
74    Revision 1.34  1992/02/23  03:26:51  ian
75    Overhaul to use automatic configure shell script
76 
77    Revision 1.33  1992/02/20  04:18:59  ian
78    Added uustat
79 
80    Revision 1.32  1992/02/08  03:54:18  ian
81    Include <string.h> only in <uucp.h>, added 1992 copyright
82 
83    Revision 1.31  1992/02/02  20:42:40  ian
84    Niels Baggesen: case enum to int before comparison
85 
86    Revision 1.30  1992/02/01  00:54:31  ian
87    Michael Nolan: cast alloca return value
88 
89    Revision 1.29  1992/01/29  04:27:11  ian
90    Jay Vassos-Libove: removed some conflicting declarations
91 
92    Revision 1.28  1992/01/28  04:34:10  ian
93    Marty Shannon: handle trailing '/' to indicate directory
94 
95    Revision 1.27  1992/01/14  04:51:48  ian
96    David Nugent: don't declare chmod
97 
98    Revision 1.26  1992/01/14  04:25:20  ian
99    Chip Salzenberg: avoid use before set warning
100 
101    Revision 1.25  1992/01/14  03:46:55  ian
102    Chip Salzenberg: handle invalid status values in status files
103 
104    Revision 1.24  1992/01/13  06:11:39  ian
105    David Nugent: can't declare open or fcntl
106 
107    Revision 1.23  1992/01/05  03:18:54  ian
108    Avoid redefining SEEK_SET
109 
110    Revision 1.22  1992/01/04  22:56:22  ian
111    Added extern definition
112 
113    Revision 1.21  1992/01/03  05:44:35  ian
114    Remove temporary file if link fails in fsdo_lock
115 
116    Revision 1.20  1991/12/29  04:04:18  ian
117    Added a bunch of extern definitions
118 
119    Revision 1.19  1991/12/22  22:14:19  ian
120    Monty Solomon: added HAVE_UNISTD_H configuration parameter
121 
122    Revision 1.18  1991/12/22  20:50:47  ian
123    Franc,ois Pinard: fixed bug in fsysdep_get_status
124 
125    Revision 1.17  1991/12/12  18:35:47  ian
126    Do locking with link to avoid races and to permit running as root
127 
128    Revision 1.16  1991/12/12  17:45:34  ian
129    fcopy_file now creates the file with IPRIVATE_MODE
130 
131    Revision 1.15  1991/12/11  03:59:19  ian
132    Create directories when necessary; don't just assume they exist
133 
134    Revision 1.14  1991/12/09  19:07:07  ian
135    Richard Todd: add HAVE_V2_LOCKFILES--binary number in lock file
136 
137    Revision 1.13  1991/12/03  02:59:46  ian
138    Using LOCKDIR clobbered a byte on the stack
139 
140    Revision 1.12  1991/12/01  02:23:12  ian
141    Niels Baggesen: don't multiply include <unistd.h>
142 
143    Revision 1.11  1991/12/01  01:12:40  ian
144    Marty Shannon: accept truncated status file; also eliminated scanf calls
145 
146    Revision 1.10  1991/11/30  23:28:26  ian
147    Marty Shannon: some systems need a fake version of the rename system call
148 
149    Revision 1.9  1991/11/21  21:43:42  ian
150    Eliminate unused MIN_FREE_BYTES
151 
152    Revision 1.8  1991/11/21  21:07:46  ian
153    Brian Campbell: offer ltrunc as an alternative to ftruncate
154 
155    Revision 1.7  1991/11/10  21:32:16  ian
156    Fixed ftruncate call
157 
158    Revision 1.6  1991/11/10  19:24:22  ian
159    Added pffile protocol entry point for file level control
160 
161    Revision 1.5  1991/11/07  19:32:28  ian
162    Chip Salzenberg: allow LOCKDIR, and check that locking process exists
163 
164    Revision 1.4  1991/09/19  17:28:01  ian
165    Chip Salzenberg: make sure spool directory files are not world readable
166 
167    Revision 1.3  1991/09/19  03:23:34  ian
168    Chip Salzenberg: append to private debugging file, don't overwrite it
169 
170    Revision 1.2  1991/09/19  03:06:04  ian
171    Chip Salzenberg: put BNU temporary files in system's directory
172 
173    Revision 1.1  1991/09/10  19:45:50  ian
174    Initial revision
175 
176    */
177 
178 #include "uucp.h"
179 
180 #if USE_RCS_ID
181 char sys3_unx_rcsid[] = "$Id: sys3.unx,v 1.2 92/05/13 05:42:07 rich Exp Locker: root $";
182 #endif
183 
184 #include <ctype.h>
185 #include <errno.h>
186 
187 #if USE_STDIO && HAVE_UNISTD_H
188 #include <unistd.h>
189 #endif
190 
191 #include "system.h"
192 #include "sysdep.h"
193 
194 #include <pwd.h>
195 
196 #if HAVE_FCNTL_H
197 #include <fcntl.h>
198 #else
199 #if HAVE_SYS_FILE_H
200 #include <sys/file.h>
201 #endif
202 #endif
203 
204 #ifndef O_RDONLY
205 #define O_RDONLY 0
206 #define O_WRONLY 1
207 #define O_RDWR 2
208 #endif
209 
210 /* Get the right header files for statfs and friends.  This stuff is
211    from David MacKenzie's df program.  */
212 
213 #ifdef FS_STATVFS
214 #include <sys/statvfs.h>
215 extern int statvfs ();
216 #endif
217 
218 #ifdef FS_USG_STATFS
219 #include <sys/statfs.h>
220 extern int statfs ();
221 #endif
222 
223 #ifdef FS_MNTENT
224 #include <sys/vfs.h>
225 extern int statfs ();
226 #endif
227 
228 #ifdef FS_GETMNT
229 #include <sys/param.h>
230 #include <sys/mount.h>
231 extern int statfs ();
232 #endif
233 
234 #ifdef FS_STATFS
235 #include <sys/mount.h>
236 extern int statfs ();
237 #endif
238 
239 #ifdef FS_USTAT
240 #include <ustat.h>
241 extern int ustat ();
242 #endif
243 
244 /* We need a definition for SEEK_SET.  */
245 
246 #ifndef SEEK_SET
247 #define SEEK_SET 0
248 #endif
249 
250 /* External functions.  */
251 extern int close (), link (), read (), write ();
252 #ifndef __386BSD__
253 extern int kill ();
254 #endif __386BSD__
255 extern int fstat (), stat ();
256 extern int fclose (), fseek (), pclose ();
257 extern pid_t getpid ();
258 extern off_t lseek ();
259 extern char *strrchr ();
260 
261 #if HAVE_FTRUNCATE
262 extern int ftruncate ();
263 #endif
264 
265 #if HAVE_RENAME
266 extern int rename ();
267 #else
268 static int rename P((const char *zfrom, const char *zto));
269 #endif
270 
271 /* There are several types of files that go in the spool directory,
272    and they go into various different subdirectories.  When using
273    SPOOLDIR_TAYLOR, there is a subdirectory for each system for which
274    communication occurs; these system names have been made canonical
275    via fread_system_info or ztranslate_name, so they will fit any
276    name length restrictions (namely 14 characters on System V).
277    Whenever the system name LOCAL appears below, it means whatever
278    the local system name is.
279 
280    Command files
281    These contain instructions for uucico indicating what files to transfer
282    to and from what systems.  Each line of a work file is a command
283    beginning with S, R or X.
284    #if ! SPOOLDIR_TAYLOR
285    They are named C.ssssssgqqqq, where ssssss is the system name to
286    transfer to or from, g is the grade and qqqq is the sequence number.
287    #if SPOOLDIR_V2
288    They are put in the spool directory.
289    #elif SPOOLDIR_BSD42 | SPOOLDIR_BSD43
290    They are put in the directory C.
291    #elif SPOOLDIR_BNU
292    They are put in a directory named for the system for which they were
293    created.
294    #elif SPOOLDIR_ULTRIX
295    If the directory sys/ssssss exists, they are put in the directory
296    sys/ssssss/C; otherwise, they are put in the directory sys/DEFAULT/C.
297    #endif
298    #else SPOOLDIR_TAYLOR
299    They are named C.gqqqq, where g is the grade and qqqq is the sequence
300    number, and are placed in the directory ssssss/C. where ssssss is
301    the system name to transfer to or from.
302    #endif
303 
304    Data files
305    There are files to be transferred to other systems.  Some files to
306    be transferred may not be in the spool directory, depending on how
307    uucp was invoked.  Data files are named in work files, so it is
308    never necessary to look at them directly (except to remove old ones);
309    it is only necessary to create them.  These means that the many
310    variations in naming are inconsequential.
311    #if ! SPOOLDIR_TAYLOR
312    They are named D.ssssssgqqqq where ssssss is a system name (which
313    may be LOCAL for locally initiated transfers or a remote system for
314    remotely initiated transfers, except that BNU appears to use the
315    system the file is being transferred to), g is the grade and qqqq
316    is the sequence number.  Some systems use a trailing subjob ID
317    number, but we currently do not.  The grade is not important, and
318    some systems do not use it.  If the data file is to become an
319    execution file on another system the grade (if present) will be
320    'X'.  Otherwise Ultrix appears to use 'b'; the uux included with
321    gnuucp 1.0 appears to use 'S'; SCO does not appear to use a grade,
322    although it does use a subjob ID number.
323    #if SPOOLDIR_V2
324    They are put in the spool directory.
325    #elif SPOOLDIR_BSD42
326    If the name begins with D.LOCAL, the file is put in the directory
327    D.LOCAL.  Otherwise the file is put in the directory D..
328    #elif SPOOLDIR_BSD43
329    If the name begins with D.LOCALX, the file is put in the directory
330    D.LOCALX.  Otherwise if the name begins with D.LOCAL, the file is
331    put in the directory D.LOCAL Otherwise the file is put in the
332    directory D..
333    #elif SPOOLDIR_BNU
334    They are put in a directory named for the system for which they
335    were created.
336    #elif SPOOLDIR_ULTRIX
337    Say the file is being transferred to system REMOTE.  If the
338    directory sys/REMOTE exists, then if the file begins with D.LOCALX
339    it is put in sys/REMOTE/D.LOCALX, if the file begins with D.LOCAL
340    it is put in sys/REMOTE/D.LOCAL, and otherwise it is put in
341    sys/REMOTE/D..  If the directory sys/REMOTE does not exist, the
342    same applies except that DEFAULT is used instead of REMOTE.
343    #endif
344    #else SPOOLDIR_TAYLOR
345    If the file is to become an executable file on another system it is
346    named D.Xqqqq, otherwise it is named D.qqqq where in both cases
347    qqqq is a sequence number.  If the corresponding C. file is in
348    directory ssssss/C., a D.X file is placed in ssssss/D.X and a D.
349    file is placed in ssssss/D..
350    #endif
351 
352    Execute files
353    These are files that specify programs to be executed.  They are
354    created by uux, perhaps as run on another system.  These names are
355    important, because a file transfer done to an execute file name
356    causes an execution to occur.  The name is X.ssssssgqqqq, where
357    ssssss is the requesting system, g is the grade, and qqqq is a
358    sequence number.
359    #if SPOOLDIR_V2 | SPOOLDIR_BSD42
360    These files are placed in the spool directory.
361    #elif SPOOLDIR_BSD43
362    These files are placed in the directory X..
363    #elif SPOOLDIR_BNU
364    These files are put in a directory named for the system for which
365    the files were created.
366    #elif SPOOLDIR_ULTRIX
367    If there is a spool directory (sys/ssssss) for the requesting
368    system, the files are placed in sys/ssssss/X.; otherwise, the files
369    are placed in sys/DEFAULT/X..
370    #elif SPOOLDIR_TAYLOR
371    The system name is automatically truncated to seven characters when
372    a file is created.  The files are placed in the subdirectory X. of
373    a directory named for the system for which the files were created.
374    #endif
375 
376    Temporary receive files
377    These are used when receiving files from another system.  They are
378    later renamed to the final name.  The actual name is unimportant,
379    although it generally begins with TM..
380    #if SPOOLDIR_V2 | SPOOLDIR_BSD42
381    These files are placed in the spool directory.
382    #elif SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR
383    These files are placed in the directory .Temp.
384    #elif SPOOLDIR_BNU
385    These files are placed in a directory named for the system for
386    which they were created.
387    #endif
388 
389    Lock files
390    These files are used to lock systems, devices and special files.
391    The file LCK..ssssss is used to lock a system, where ssssss is the
392    system name.  The file LCK..dev is used to lock a device, where dev
393    is the device name.  The file LCK.file is used to lock a file,
394    where file is LOG for the log file, SQ for the system sequence
395    number file, or SEQF for the work queue sequence number file.  At
396    least under Ultrix, the file LCK.XQT is used to lock uuxqt
397    execution.  Some systems supposedly use LCK.SEQL for something.  On
398    some systems, the contents of the lock file is the ASCII process
399    id; on others, it is the process id as four data bytes.  As far as
400    I can tell, the only lock file I really have to get right is the
401    one locking a device, so that cu won't use it; if somebody tries to
402    run old UUCP and this at the same time, they will probably have
403    trouble unless they make sure the locking is correct for their
404    system.  Not that there is any easy way to make that check,
405    unfortunately.  Supposedly all normal systems put the LCK files in
406    the spool directory, and this package will do that also.
407 
408    System status files
409    These are used to record when the last call was made to the system
410    and what the status is.  They are used to prevent frequent recalls
411    to a system which is not responding.  I will not attempt to
412    recreate the format of these exactly, since they are not all that
413    important.  They will be put in the directory .Status, as in BNU,
414    and they use the system name as the name of the file.
415 
416    Log files
417    These are used to record what UUCP has done.  I will not attempt to
418    recreate the format of these at all.  They will be stored in the
419    directory .Log/uucico (or .Log/uucp, .Log/uux, .Log/uuxqt) and
420    named for the relevant system.  This is the format used by BNU.
421 
422    Statistics files
423    I don't really know the format of these.  They are apparently used
424    to keep track of what jobs have been run by UUCP, but at least on
425    Ultrix they don't seem to be used very consistently.
426 
427    Sequence file
428    This is used to generate a unique sequence number.  It contains an
429    ASCII number.
430    #if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43
431    The file is named SEQF and is kept in the spool directory.
432    #elif SPOOLDIR_BNU
433    A separate sequence file is kept for each system in the directory
434    .Sequence with the name of the system.
435    #elif SPOOLDIR_ULTRIX
436    Each system with a file sys/ssssss has a sequence file in
437    sys/ssssss/.SEQF.  Other systems use sys/DEFAULT/.SEQF.
438    #else SPOOLDIR_TAYLOR
439    A sequence file named SEQF is kept in the directory ssssss for each
440    system.
441    #endif
442 
443    Audit files
444    Debugging messages are stored in these when running as a slave.  We
445    use the file AUDIT in the spool directory.  */
446 
447 /* Local functions.  */
448 
449 static char *zsstatic_size P((int c));
450 #if SPOOLDIR_TAYLOR
451 static const char *zsappend3 P((const char *zdir1, const char *zdir2,
452 				const char *zfile));
453 #endif
454 #if SPOOLDIR_ULTRIX
455 static const char *zsappend4 P((const char *zdir1, const char *zdir2,
456 				const char *zdir3, const char *zfile));
457 #endif
458 static const char *zsfind_file P((const char *zsimple,
459 				  const char *zsystem));
460 static boolean fscmd_seq P((const char *zsystem, char *zseq));
461 static const char *zsfile_name P((int btype, const char *zsystem,
462 				  int bgrade, char *ztname,
463 				  char *zdname, char *zxname));
464 
465 /* A few routines to manipulate strings with directories.  */
466 
467 #define CSTATICLEN (50)
468 static char abSstatic[CSTATICLEN];
469 static char *zSstatic_alloc;
470 static int cSstatic_alloc;
471 
472 /* Return a pointer to a static buffer of a certain size.  */
473 
474 static char *
475 zsstatic_size (c)
476      int c;
477 {
478   if (c <= CSTATICLEN)
479     return abSstatic;
480   if (cSstatic_alloc < c)
481     {
482       xfree ((pointer) zSstatic_alloc);
483       zSstatic_alloc = (char *) xmalloc (c);
484       cSstatic_alloc = c;
485    }
486   return zSstatic_alloc;
487 }
488 
489 /* Copy a string into a static buffer.  */
490 
491 const char *
492 zscopy (z)
493      const char *z;
494 {
495   char *zret;
496 
497   zret = zsstatic_size (strlen (z) + 1);
498   strcpy (zret, z);
499   return (const char *) zret;
500 }
501 
502 /* Stick a directory and file name together.  Return a static buffer
503    holding the combined name.  This is called by Unix system dependent
504    routines outside this file.  */
505 
506 const char *
507 zsappend (zdir, zfile)
508      const char *zdir;
509      const char *zfile;
510 {
511   char *zret;
512 
513   zret = zsstatic_size (strlen (zdir) + strlen (zfile) + sizeof "/");
514   sprintf (zret, "%s/%s", zdir, zfile);
515   return (const char *) zret;
516 }
517 
518 #if SPOOLDIR_TAYLOR
519 
520 /* Stick two directories and a file name together.  Return a static
521    buffer holding the combined name.  */
522 
523 static const char *
524 zsappend3 (zdir1, zdir2, zfile)
525      const char *zdir1;
526      const char *zdir2;
527      const char *zfile;
528 {
529   char *zret;
530 
531   zret = zsstatic_size (strlen (zdir1) + strlen (zdir2)
532 			+ strlen (zfile) + sizeof "//");
533   sprintf (zret, "%s/%s/%s", zdir1, zdir2, zfile);
534   return (const char *) zret;
535 }
536 
537 #endif /* SPOOLDIR_TAYLOR */
538 
539 #if SPOOLDIR_ULTRIX
540 
541 /* Stick three directories and a file name together.  Return a static
542    buffer holding the combined name.  */
543 
544 static const char *
545 zsappend4 (zdir1, zdir2, zdir3, zfile)
546      const char *zdir1;
547      const char *zdir2;
548      const char *zdir3;
549      const char *zfile;
550 {
551   char *zret;
552 
553   zret = zsstatic_size (strlen (zdir1) + strlen (zdir2) + strlen (zdir3)
554 			+ strlen (zfile) + sizeof "///");
555   sprintf (zret, "%s/%s/%s/%s", zdir1, zdir2, zdir3, zfile);
556   return (const char *) zret;
557 }
558 
559 /* See whether an ULTRIX spool directory exists for a system.  For system
560    ssssss, the spool directory is called sys/ssssss.  */
561 
562 boolean
563 fsultrix_has_spool (zsystem)
564      const char *zsystem;
565 {
566   char *z;
567 
568   z = (char *) alloca (sizeof "sys/" + strlen (zsystem));
569   sprintf (z, "sys/%s", zsystem);
570   return fsdirectory_exists (z);
571 }
572 
573 #endif /* SPOOLDIR_ULTRIX */
574 
575 /* Create a spool directory for a system.  This is only relevant for
576    SPOOLDIR_BNU or SPOOLDIR_TAYLOR.  The system specific directories
577    for Ultrix are meant to be created by hand.  */
578 
579 #if SPOOLDIR_TAYLOR | SPOOLDIR_BNU
580 
581 static boolean fsmkdir P((const char *zdir));
582 
583 static boolean
584 fsmkdir (zdir)
585      const char *zdir;
586 {
587   if (mkdir ((char *) zdir, IDIRECTORY_MODE) < 0)
588     {
589       ulog (LOG_ERROR, "mkdir (%s): %s", zdir, strerror (errno));
590       return FALSE;
591     }
592   return TRUE;
593 }
594 
595 #endif /* SPOOLDIR_TAYLOR | SPOOLDIR_BNU */
596 
597 boolean
598 fsysdep_make_spool_dir (qsys)
599      const struct ssysteminfo *qsys;
600 {
601   const char *zsystem;
602 
603   zsystem = qsys->zname;
604 
605 #if SPOOLDIR_BNU
606   if (fsdirectory_exists (zsystem))
607     return TRUE;
608   if (! fsmkdir (zsystem))
609     return FALSE;
610 #endif /* SPOOLDIR_BNU */
611 
612 #if SPOOLDIR_TAYLOR
613   if (fsdirectory_exists (zsystem))
614     return TRUE;
615   if (! fsmkdir (zsystem)
616       || ! fsmkdir (zsappend (zsystem, "C."))
617       || ! fsmkdir (zsappend (zsystem, "D."))
618       || ! fsmkdir (zsappend (zsystem, "D.X"))
619       || ! fsmkdir (zsappend (zsystem, "X.")))
620     return FALSE;
621 #endif /* SPOOLDIR_TAYLOR */
622 
623   return TRUE;
624 }
625 
626 /* Given the name of a file as specified in a UUCP command, and the
627    system for which this file has been created, return where to find
628    it in the spool directory.  The file will begin with C. (a command
629    file), D. (a data file) or X. (an execution file).  The return
630    value of this function will point to a static buffer.  */
631 
632 static const char *
633 zsfind_file (zsimple, zsystem)
634      const char *zsimple;
635      const char *zsystem;
636 {
637   if (zsimple[1] != '.'
638       || (*zsimple != 'C'
639 	  && *zsimple != 'D'
640 	  && *zsimple != 'X'))
641     {
642       ulog (LOG_ERROR, "Unrecognized file name %s", zsimple);
643       return NULL;
644     }
645 
646   switch (*zsimple)
647     {
648     case 'C':
649 #if SPOOLDIR_V2
650       return zscopy (zsimple);
651 #endif /* SPOOLDIR_V2 */
652 #if SPOOLDIR_BSD42 | SPOOLDIR_BSD43
653       return zsappend ("C.", zsimple);
654 #endif /* SPOOLDIR_BSD42 | SPOOLDIR_BSD43 */
655 #if SPOOLDIR_BNU
656       return zsappend (zsystem, zsimple);
657 #endif /* SPOOLDIR_BNU */
658 #if SPOOLDIR_ULTRIX
659       if (fsultrix_has_spool (zsystem))
660 	return zsappend4 ("sys", zsystem, "C.", zsimple);
661       else
662 	return zsappend4 ("sys", "DEFAULT", "C.", zsimple);
663 #endif
664 #if SPOOLDIR_TAYLOR
665       return zsappend3 (zsystem, "C.", zsimple);
666 #endif
667 
668     case 'D':
669 #if SPOOLDIR_V2
670       return zscopy (zsimple);
671 #endif /* SPOOLDIR_V2 */
672 #if SPOOLDIR_BSD42 | SPOOLDIR_BSD43
673       {
674 	int c;
675 	boolean ftruncated;
676 	char *zalloc;
677 
678 	/* D.LOCAL in D.LOCAL/, others in D./.  If BSD43, D.LOCALX in
679 	   D.LOCALX/.  */
680 	ftruncated = TRUE;
681 	if (strncmp (zsimple + 2, zLocalname, strlen (zLocalname)) == 0)
682 	  {
683 	    c = strlen (zLocalname);
684 	    ftruncated = FALSE;
685 	  }
686 	else if (strncmp (zsimple + 2, zLocalname, 7) == 0)
687 	  c = 7;
688 	else if (strncmp (zsimple + 2, zLocalname, 6) == 0)
689 	  c = 6;
690 	else
691 	  c = 0;
692 #if SPOOLDIR_BSD43
693 	if (c > 0 && zsimple[c + 2] == 'X')
694 	  c++;
695 #endif /* SPOOLDIR_BSD43 */
696 	if (c > 0)
697 	  {
698 	    zalloc = (char *) alloca (c + 3);
699 	    strncpy (zalloc, zsimple, c + 2);
700 	    zalloc[c + 2] = '\0';
701 
702 	    /* If we truncated the system name, and there is no existing
703 	       directory with the truncated name, then just use D..  */
704 	    if (ftruncated && ! fsdirectory_exists (zalloc))
705 	      return zsappend ("D.", zsimple);
706 
707 	    return zsappend (zalloc, zsimple);
708 	  }
709 	else
710 	  return zsappend ("D.", zsimple);
711       }
712 #endif /* SPOOLDIR_BSD42 | SPOOLDIR_BSD43 */
713 #if SPOOLDIR_BNU
714       return zsappend (zsystem, zsimple);
715 #endif /* SPOOLDIR_BNU */
716 #if SPOOLDIR_ULTRIX
717       {
718 	int c;
719 	boolean ftruncated;
720 	char *zalloc;
721 	const char *zdir;
722 
723 	/* D.LOCALX in D.LOCALX/, D.LOCAL in D.LOCAL/, others in D./.  */
724 
725 	ftruncated = TRUE;
726 	if (strncmp (zsimple + 2, zLocalname, strlen (zLocalname)) == 0)
727 	  {
728 	    c = strlen (zLocalname);
729 	    ftruncated = FALSE;
730 	  }
731 	else if (strncmp (zsimple + 2, zLocalname, 7) == 0)
732 	  c = 7;
733 	else if (strncmp (zsimple + 2, zLocalname, 6) == 0)
734 	  c = 6;
735 	else
736 	  c = 0;
737 	if (c > 0 && zsimple[c + 2] == 'X')
738 	  c++;
739 	if (c > 0)
740 	  {
741 	    zalloc = (char *) alloca (c + 3);
742 	    strncpy (zalloc, zsimple, c + 2);
743 	    zalloc[c + 2] = '\0';
744 	    zdir = zalloc;
745 
746 	    /* If we truncated the name, and there is no directory for
747 	       the truncated name, then don't use it.  */
748 	    if (ftruncated)
749 	      {
750 		char *zlook;
751 
752 		zlook = (char *) alloca (c + 20 + strlen (zsystem));
753 		if (fsultrix_has_spool (zsystem))
754 		  sprintf (zlook, "sys/%s/%s", zsystem, zdir);
755 		else
756 		  sprintf (zlook, "sys/DEFAULT/%s", zdir);
757 		if (! fsdirectory_exists (zlook))
758 		  zdir = "D.";
759 	      }
760 	  }
761 	else
762 	  zdir = "D.";
763 
764 	if (fsultrix_has_spool (zsystem))
765 	  return zsappend4 ("sys", zsystem, zdir, zsimple);
766 	else
767 	  return zsappend4 ("sys", "DEFAULT", zdir, zsimple);
768       }
769 #endif /* SPOOLDIR_ULTRIX */
770 #if SPOOLDIR_TAYLOR
771       if (zsimple[2] == 'X')
772 	return zsappend3 (zsystem, "D.X", zsimple);
773       else
774 	return zsappend3 (zsystem, "D.", zsimple);
775 #endif /* SPOOLDIR_TAYLOR */
776 
777       /* Files beginning with X. are execute files.  It is important
778 	 for security reasons that we know the system which created
779 	 the X. file.  This is easy under SPOOLDIR_BNU or
780 	 SPOOLDIR_TAYLOR, because the file will be in a directory
781 	 named for the system.  Under other schemes, we must get the
782 	 system name from the X. file name.  To prevent security
783 	 violations, we set the system name directly here; this will
784 	 cause problems if the maximum file name length is too short,
785 	 but hopefully no problem will occur since any System V
786 	 systems will be using either BNU or TAYLOR.  */
787 
788     case 'X':
789 #if ! SPOOLDIR_BNU && ! SPOOLDIR_TAYLOR
790       if (strncmp (zsimple + 2, zsystem, strlen (zsimple) - 7) != 0)
791 	{
792 	  char *zcopy;
793 
794 	  zcopy = (char *) alloca (strlen (zsystem) + 8);
795 	  sprintf (zcopy, "X.%s%s", zsystem,
796 		   zsimple + strlen (zsimple) - 5);
797 	  zsimple = zcopy;
798 	}
799 #endif /* ! SPOOLDIR_BNU && ! SPOOLDIR_TAYLOR */
800 
801 #if SPOOLDIR_V2 | SPOOLDIR_BSD42
802       return zscopy (zsimple);
803 #endif
804 #if SPOOLDIR_BSD43
805       return zsappend ("X.", zsimple);
806 #endif
807 #if SPOOLDIR_BNU
808       return zsappend (zsystem, zsimple);
809 #endif
810 #if SPOOLDIR_ULTRIX
811       if (fsultrix_has_spool (zsystem))
812 	return zsappend4 ("sys", zsystem, "X.", zsimple);
813       else
814 	return zsappend4 ("sys", "DEFAULT", "X.", zsimple);
815 #endif
816 #if SPOOLDIR_TAYLOR
817       return zsappend3 (zsystem, "X.", zsimple);
818 #endif
819 
820     default:
821 #if DEBUG > 0
822       ulog (LOG_FATAL, "zsfind_file: Can't happen");
823 #endif /* DEBUG */
824       return NULL;
825     }
826   /*NOTREACHED*/
827 }
828 
829 /* Get the status of a system.  */
830 
831 /*ARGSUSED*/
832 boolean
833 fsysdep_get_status (qsys, qret)
834      const struct ssysteminfo *qsys;
835      struct sstatus *qret;
836 {
837   const char *zname;
838   FILE *e;
839   char *zline;
840   char *zend, *znext;
841   boolean fbad;
842   int istat;
843 
844   zname = zsappend (".Status", qsys->zname);
845   e = fopen (zname, "r");
846   if (e == NULL)
847     {
848       if (errno != ENOENT)
849 	{
850 	  ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno));
851 	  return FALSE;
852 	}
853       zline = NULL;
854     }
855   else
856     {
857       zline = zfgets (e, FALSE);
858       (void) fclose (e);
859     }
860 
861   if (zline == NULL)
862     {
863       /* There is either no status file for this system, or it's been
864 	 truncated, so fake a good status.  */
865       qret->ttype = STATUS_COMPLETE;
866       qret->cretries = 0;
867       qret->ilast = 0;
868       qret->cwait = 0;
869       return TRUE;
870     }
871 
872   /* It turns out that scanf is not used much in this program, so for
873      the benefit of small computers we avoid linking it in.  This is
874      basically
875 
876      sscanf (zline, "%d %d %ld %d", &qret->ttype, &qret->cretries,
877              &qret->ilast, &qret->cwait);
878 
879      except that it's done with strtol.  */
880 
881   fbad = FALSE;
882   istat = (int) strtol (zline, &zend, 10);
883   if (zend == zline)
884     fbad = TRUE;
885 
886   /* On some systems it may be appropriate to map system dependent status
887      values on to our status values.  Perhaps someday.  */
888 
889   if (istat < 0 || istat >= (int) STATUS_VALUES)
890     istat = (int) STATUS_COMPLETE;
891   qret->ttype = (enum tstatus_type) istat;
892   znext = zend;
893   qret->cretries = (int) strtol (znext, &zend, 10);
894   if (zend == znext)
895     fbad = TRUE;
896   znext = zend;
897   qret->ilast = strtol (znext, &zend, 10);
898   if (zend == znext)
899     fbad = TRUE;
900   znext = zend;
901   qret->cwait = (int) strtol (znext, &zend, 10);
902   if (zend == znext)
903     fbad = TRUE;
904 
905   xfree ((pointer) zline);
906 
907   if (fbad)
908     {
909       ulog (LOG_ERROR, "Bad format of status file for %s", qsys->zname);
910       return FALSE;
911     }
912 
913   return TRUE;
914 }
915 
916 /* Set the status of a remote system.  We assume the system is locked
917    when this is called.  */
918 
919 /*ARGSUSED*/
920 boolean
921 fsysdep_set_status (qsys, qset)
922      const struct ssysteminfo *qsys;
923      const struct sstatus *qset;
924 {
925   const char *zname;
926   FILE *e;
927   int istat;
928 
929   zname = zsappend (".Status", qsys->zname);
930 
931   e = esysdep_fopen (zname, TRUE, FALSE, TRUE);
932   if (e == NULL)
933     return FALSE;
934   istat = (int) qset->ttype;
935 
936   /* On some systems it may be appropriate to map istat onto a system
937      dependent number.  Perhaps someday.  */
938 
939   fprintf (e, "%d %d %ld %d %s %s\n", istat, qset->cretries,
940 	   qset->ilast, qset->cwait, azStatus[(int) qset->ttype],
941 	   qsys->zname);
942   if (fclose (e) != 0)
943     {
944       ulog (LOG_ERROR, "fclose: %s", strerror (errno));
945       return FALSE;
946     }
947 
948   return TRUE;
949 }
950 
951 /* Get the real name of a spool file.  */
952 
953 const char *
954 zsysdep_spool_file_name (qsys, zfile)
955      const struct ssysteminfo *qsys;
956      const char *zfile;
957 {
958   return zsfind_file (zfile, qsys->zname);
959 }
960 
961 /* Expand a file name on the local system.  The qsys argument is only
962    used to determine which public directory to use.  */
963 
964 const char *
965 zsysdep_real_file_name (qsys, zfile, zname)
966      const struct ssysteminfo *qsys;
967      const char *zfile;
968      const char *zname;
969 {
970   const char *ztry;
971   char *zlook;
972 
973   if (zfile[0] == '/')
974     ztry = zfile;
975   else if (zfile[0] == '~')
976     {
977       const char *z;
978       char *zcopy;
979 
980       z = zstilde_expand (qsys, zfile);
981       zcopy = (char *) alloca (strlen (z) + 1);
982       strcpy (zcopy, z);
983       ztry = zcopy;
984     }
985   else
986     {
987       const char *zpub, *z;
988       char *zcopy;
989 
990       /* Put the file in the public directory.  */
991       if (qsys == NULL || qsys->zpubdir == NULL)
992 	zpub = zPubdir;
993       else
994 	zpub = qsys->zpubdir;
995       z = zsappend (zpub, zfile);
996       zcopy = (char *) alloca (strlen (z) + 1);
997       strcpy (zcopy, z);
998       ztry = zcopy;
999     }
1000 
1001   /* If we don't have a file name to use within a directory, or we
1002      haven't named a directory, we use what we've got so far.  If the
1003      name ends in a '/', it is assumed to name a directory.  */
1004 
1005   if (zname == NULL)
1006     return zscopy (ztry);
1007 
1008   if (ztry[strlen (ztry) - 1] != '/')
1009     {
1010       if (! fsdirectory_exists (ztry))
1011 	return zscopy (ztry);
1012     }
1013   else
1014     {
1015       char *zcopy;
1016       int clen;
1017 
1018       clen = strlen (ztry);
1019       zcopy = (char *) alloca (clen + 1);
1020       strcpy (zcopy, ztry);
1021       zcopy[clen - 1] = '\0';
1022       ztry = zcopy;
1023     }
1024 
1025   /* Get a name out of zname and tag it on.  */
1026 
1027   zlook = strrchr (zname, '/');
1028   if (zlook != NULL)
1029     zname = zlook + 1;
1030 
1031   return zsappend (ztry, zname);
1032 }
1033 
1034 /* Return a file name within a directory.  */
1035 
1036 const char *
1037 zsysdep_in_dir (zdir, zfile)
1038      const char *zdir;
1039      const char *zfile;
1040 {
1041   if (fsdirectory_exists (zdir))
1042     return zsappend (zdir, zfile);
1043   else
1044     return zdir;
1045 }
1046 
1047 /* Open a file to send to another system, and return the mode and
1048    the size.  */
1049 
1050 /*ARGSUSED*/
1051 openfile_t
1052 esysdep_open_send (qsys, zfile, fcheck, zuser, pimode, pcbytes, pfgone)
1053      const struct ssysteminfo *qsys;
1054      const char *zfile;
1055      boolean fcheck;
1056      const char *zuser;
1057      unsigned int *pimode;
1058      long *pcbytes;
1059      boolean *pfgone;
1060 {
1061   struct stat s;
1062   openfile_t e;
1063   int o;
1064 
1065   if (pfgone != NULL)
1066     *pfgone = FALSE;
1067 
1068   if (fsdirectory_exists (zfile))
1069     {
1070       ulog (LOG_ERROR, "%s: is a directory", zfile);
1071       return EFILECLOSED;
1072     }
1073 #if USE_STDIO
1074   e = fopen (zfile, BINREAD);
1075   if (e == NULL)
1076     {
1077       ulog (LOG_ERROR, "fopen (%s): %s", zfile, strerror (errno));
1078       if (pfgone != NULL && errno == ENOENT)
1079 	*pfgone = TRUE;
1080       return NULL;
1081     }
1082   o = fileno (e);
1083 #else
1084   e = open (zfile, O_RDONLY, 0);
1085   if (e == -1)
1086     {
1087       ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno));
1088       if (pfgone != NULL && errno == ENOENT)
1089 	*pfgone = TRUE;
1090       return -1;
1091     }
1092   o = e;
1093 #endif
1094 
1095   if (fstat (o, &s) == -1)
1096     {
1097       ulog (LOG_ERROR, "fstat: %s", strerror (errno));
1098       s.st_mode = 0666;
1099     }
1100 
1101   /* We have to recheck the file permission, although we probably
1102      checked it already, because otherwise there would be a window in
1103      which somebody could change the contents of a symbolic link to
1104      point to some file which was only readable by uucp.  */
1105   if (fcheck)
1106     {
1107       if (! fsuser_access (&s, R_OK, zuser))
1108 	{
1109 	  ulog (LOG_ERROR, "%s: %s", zfile, strerror (EACCES));
1110 	  (void) ffileclose (e);
1111 	  return EFILECLOSED;
1112 	}
1113     }
1114 
1115   *pimode = s.st_mode & 0777;
1116   *pcbytes = s.st_size;
1117   return e;
1118 }
1119 
1120 /* Get a temporary file name.  */
1121 
1122 /*ARGSUSED*/
1123 const char *
1124 zstemp_file (qsys)
1125      const struct ssysteminfo *qsys;
1126 {
1127   static int icount;
1128   char *zret;
1129 
1130 #if SPOOLDIR_V2 | SPOOLDIR_BSD42
1131   {
1132     static char ab[sizeof "TM.12345.123"];
1133 
1134     sprintf (ab, "TM.%05d.%03d", getpid (), icount);
1135     zret = ab;
1136   }
1137 #endif
1138 #if SPOOLDIR_BSD43 | SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR
1139   {
1140     static char ab[sizeof ".Temp/TM.12345.123"];
1141 
1142     sprintf (ab, ".Temp/TM.%05d.%03d", getpid (), icount);
1143     zret = ab;
1144   }
1145 #endif
1146 #if SPOOLDIR_BNU
1147   {
1148     static char *z;
1149     static int calc;
1150     int cneed;
1151 
1152     cneed = strlen (qsys->zname) + sizeof "/TM.12345.123";
1153     if (cneed > calc)
1154       {
1155 	xfree ((pointer) z);
1156 	z = (char *) xmalloc (cneed);
1157 	calc = cneed;
1158       }
1159     sprintf (z, "%s/TM.%05d.%03d", qsys->zname, getpid (), icount);
1160     zret = z;
1161   }
1162 #endif
1163 
1164   ++icount;
1165 
1166   return zret;
1167 }
1168 
1169 /* Open a temporary file to receive into.  This should, perhaps, check
1170    that we have write permission on the receiving directory, but it
1171    doesn't.  It is supposed to set *pcbytes to the size of the largest
1172    file that can be accepted.  */
1173 
1174 /*ARGSUSED*/
1175 openfile_t
1176 esysdep_open_receive (qsys, zto, pztemp, pcbytes)
1177      const struct ssysteminfo *qsys;
1178      const char *zto;
1179      const char **pztemp;
1180      long *pcbytes;
1181 {
1182   const char *z;
1183   int o;
1184   openfile_t e;
1185   long c1, c2;
1186   char *zcopy, *zslash;
1187 
1188   z = zstemp_file (qsys);
1189 
1190   o = creat (z, IPRIVATE_FILE_MODE);
1191 
1192   if (o == -1)
1193     {
1194       if (errno == ENOENT)
1195 	{
1196 	  if (! fsysdep_make_dirs (z, FALSE))
1197 	    return EFILECLOSED;
1198 	  o = creat (z, IPRIVATE_FILE_MODE);
1199 	}
1200       if (o == -1)
1201 	{
1202 	  ulog (LOG_ERROR, "creat (%s): %s", z, strerror (errno));
1203 	  return EFILECLOSED;
1204 	}
1205     }
1206 
1207 #if USE_STDIO
1208   e = fdopen (o, (char *) BINWRITE);
1209 
1210   if (e == NULL)
1211     {
1212       ulog (LOG_ERROR, "fdopen (%s): %s", z, strerror (errno));
1213       (void) close (o);
1214       (void) remove (z);
1215       return NULL;
1216     }
1217 #else
1218   e = o;
1219 #endif
1220 
1221   *pztemp = z;
1222 
1223   /* Try to determine the amount of free space available for the
1224      temporary file and for the final destination.  This code is
1225      mostly from David MacKenzie's df program.  */
1226 
1227   c1 = (long) -1;
1228   c2 = (long) -1;
1229 
1230   zcopy = (char *) alloca (strlen (zto) + 1);
1231   strcpy (zcopy, zto);
1232   zslash = strrchr (zcopy, '/');
1233   if (zslash != NULL)
1234     *zslash = '\0';
1235   else
1236     {
1237       zcopy[0] = '.';
1238       zcopy[1] = '\0';
1239     }
1240 
1241   {
1242 #ifdef FS_STATVFS
1243     struct statvfs s;
1244 
1245     if (statvfs (z, &s) >= 0)
1246       c1 = (long) s.f_bavail * (long) s.f_frsize;
1247     if (statvfs (zcopy, &s) >= 0)
1248       c2 = (long) s.f_bavail * (long) s.f_frsize;
1249 #endif
1250 #ifdef FS_USG_STATFS
1251     struct statfs s;
1252 
1253     /* This structure has an f_bsize field, but on many systems
1254        f_bfree is measured in 512 byte blocks.  On some systems,
1255        f_bfree is measured in f_bsize byte blocks.  Rather than
1256        overestimate the amount of free space, this code assumes that
1257        f_bfree is measuring 512 byte blocks.  */
1258 
1259     if (statfs (z, &s, sizeof s, 0) >= 0)
1260       c1 = (long) s.f_bfree * (long) 512;
1261     if (statfs (zcopy, &s, sizeof s, 0) >= 0)
1262       c2 = (long) s.f_bfree * (long) 512;
1263 #endif
1264 #ifdef FS_MNTENT
1265     struct statfs s;
1266 
1267     if (statfs (z, &s) == 0)
1268       c1 = (long) s.f_bavail * (long) s.f_bsize;
1269     if (statfs (zcopy, &s) == 0)
1270       c2 = (long) s.f_bavail * (long) s.f_bsize;
1271 #endif
1272 #ifdef FS_GETMNT
1273     struct fs_data s;
1274 
1275     if (statfs (z, &s) == 1)
1276       c1 = (long) s.fd_req.bfreen * (long) 1024;
1277     if (statfs (zcopy, &s) == 1)
1278       c2 = (long) s.fd_req.bfreen * (long) 1024;
1279 #endif
1280 #ifdef FS_STATFS
1281     struct statfs s;
1282 
1283     if (statfs (z, &s) >= 0)
1284       c1 = (long) s.f_bavail * (long) s.f_fsize;
1285     if (statfs (zcopy, &s) >= 0)
1286       c2 = (long) s.f_bavail * (long) s.f_fsize;
1287 #endif
1288 #ifdef FS_USTAT
1289     struct stat sstat;
1290     struct ustat s;
1291 
1292     if (fstat (o, &sstat) == 0
1293 	&& ustat (sstat.st_dev, &s) == 0)
1294       c1 = (long) s.f_tfree * (long) 512;
1295     if (stat (zcopy, &sstat) == 0
1296 	&& ustat (sstat.st_dev, &s) == 0)
1297       c2 = (long) s.f_tfree * (long) 512;
1298 #endif
1299   }
1300 
1301   if (c1 == (long) -1)
1302     *pcbytes = c2;
1303   else if (c2 == (long) -1)
1304     *pcbytes = c1;
1305   else if (c1 < c2)
1306     *pcbytes = c1;
1307   else
1308     *pcbytes = c2;
1309 
1310   return e;
1311 }
1312 
1313 /* After the temporary file has been completely written out, the file
1314    is closed and this routine is called to move it into its final
1315    location.  If we fail, we must remove the temporary file.  */
1316 
1317 boolean
1318 fsysdep_move_file (zorig, zto, imode, fcheck, zuser)
1319      const char *zorig;
1320      const char *zto;
1321      unsigned int imode;
1322      boolean fcheck;
1323      const char *zuser;
1324 {
1325   DEBUG_MESSAGE2 (DEBUG_SPOOLDIR,
1326 		  "fsysdep_move_file: Moving %s to %s", zorig, zto);
1327 
1328   /* Unless and until we add an option to change the ownership of the
1329      file, the only information we want from the mode is whether the
1330      file is executable or not.  It would be dumb to create a file
1331      with mode 0600, for example, since the owner will be uucp and the
1332      recipient will not be able to read it.  If we do not have an
1333      absolute path to the file, which means that it is being moved
1334      somewhere in the spool directory, we don't change the mode; in
1335      general, the files in the spool directory should not be
1336      publically readable.  */
1337 
1338   if (*zto != '/')
1339     imode = 0;
1340 
1341   if (imode != 0)
1342     {
1343       if ((imode & 0111) != 0)
1344 	imode = S_IRWXU | S_IRWXG | S_IRWXO;
1345       else
1346 	imode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1347     }
1348 
1349   /* Optionally make sure that zuser has write access on the
1350      directory.  We only check files that are not in the spool
1351      directory.  */
1352   if (fcheck && *zto == '/')
1353     {
1354       char *zcopy;
1355       char *zslash;
1356       struct stat s;
1357 
1358       zcopy = (char *) alloca (strlen (zto) + 1);
1359       strcpy (zcopy, zto);
1360       zslash = strrchr (zcopy, '/');
1361       if (zslash == zcopy)
1362 	zslash[1] = '\0';
1363       else
1364 	*zslash = '\0';
1365 
1366       if (stat (zcopy, &s) != 0)
1367 	{
1368 	  ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno));
1369 	  (void) remove (zorig);
1370 	  return FALSE;
1371 	}
1372       if (! fsuser_access (&s, W_OK, zuser))
1373 	{
1374 	  ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES));
1375 	  (void) remove (zorig);
1376 	  return FALSE;
1377 	}
1378 
1379       /* A malicious user now has a few milliseconds to change a
1380 	 symbolic link to a directory uucp has write permission on but
1381 	 the user does not (the obvious choice being /usr/lib/uucp).
1382 	 The only certain method I can come up with to close this race
1383 	 is to fork an suid process which takes on the users identity
1384 	 and does the actual copy.  This is sufficiently high overhead
1385 	 that I'm not going to do it.  */
1386     }
1387 
1388   /* We try to use rename to move the file.  */
1389 
1390   if (rename (zorig, zto) == 0)
1391     {
1392       /* We must set the correct file mode, but don't worry if it doesn't
1393 	 work.  There should be an option for setting the owner, as
1394 	 well.  */
1395       if (imode != 0)
1396 	(void) chmod (zto, imode);
1397       return TRUE;
1398     }
1399 
1400   /* If this file is in the spool directory, make sure all directories
1401      exist.  */
1402   if (*zto != '/' && errno == ENOENT)
1403     {
1404       if (! fsysdep_make_dirs (zto, FALSE))
1405 	{
1406 	  (void) remove (zorig);
1407 	  return FALSE;
1408 	}
1409       if (rename (zorig, zto) == 0)
1410 	{
1411 	  if (imode != 0)
1412 	    (void) chmod (zto, imode);
1413 	  return TRUE;
1414 	}
1415     }
1416 
1417   /* If we can't link across devices, we must copy the file by hand.  */
1418   if (errno != EXDEV)
1419     {
1420       ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto,
1421 	    strerror (errno));
1422       (void) remove (zorig);
1423       return FALSE;
1424     }
1425 
1426   /* If the destination file is not in the spool directory, any
1427      necessary directories should already have been made.  */
1428   if (! fcopy_file (zorig, zto, FALSE, *zto != '/'))
1429     {
1430       (void) remove (zorig);
1431       return FALSE;
1432     }
1433 
1434   if (remove (zorig) < 0)
1435     ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno));
1436 
1437   if (imode != 0)
1438     (void) chmod (zto, imode);
1439 
1440   return TRUE;
1441 }
1442 
1443 /* Truncate a file to zero length.  If this fails, it closes and
1444    removes the file.  We support a number of different means of
1445    truncation, which is probably a waste of time since this function
1446    is currently only called when the 'f' protocol resends a file.  */
1447 
1448 #if HAVE_FTRUNCATE
1449 #undef HAVE_LTRUNC
1450 #define HAVE_LTRUNC 0
1451 #endif
1452 
1453 #if ! HAVE_FTRUNCATE && ! HAVE_LTRUNC
1454 #ifdef F_CHSIZE
1455 #define HAVE_F_CHSIZE 1
1456 #else /* ! defined (F_CHSIZE) */
1457 #ifdef F_FREESP
1458 #define HAVE_F_FREESP 1
1459 #endif /* defined (F_FREESP) */
1460 #endif /* ! defined (F_CHSIZE) */
1461 #endif /* ! HAVE_FTRUNCATE && ! HAVE_LTRUNC */
1462 
1463 openfile_t
1464 esysdep_truncate (e, zname)
1465      openfile_t e;
1466      const char *zname;
1467 {
1468   int o;
1469 
1470 #if HAVE_FTRUNCATE || HAVE_LTRUNC || HAVE_F_CHSIZE || HAVE_F_FREESP
1471   int itrunc;
1472 
1473   if (! ffilerewind (e))
1474     {
1475       ulog (LOG_ERROR, "rewind: %s", strerror (errno));
1476       (void) ffileclose (e);
1477       (void) remove (zname);
1478       return EFILECLOSED;
1479     }
1480 
1481 #if USE_STDIO
1482   o = fileno (e);
1483 #else
1484   o = e;
1485 #endif
1486 
1487 #if HAVE_FTRUNCATE
1488   itrunc = ftruncate (o, 0);
1489 #endif
1490 #if HAVE_LTRUNC
1491   itrunc = ltrunc (o, (long) 0, SEEK_SET);
1492 #endif
1493 #if HAVE_F_CHSIZE
1494   itrunc = fcntl (o, F_CHSIZE, (off_t) 0);
1495 #endif
1496 #if HAVE_F_FREESP
1497   /* This selection is based on an implementation of ftruncate by
1498      kucharsk@Solbourne.com (William Kucharski).  */
1499   {
1500     struct flock fl;
1501 
1502     fl.l_whence = 0;
1503     fl.l_len = 0;
1504     fl.l_start = 0;
1505     fl.l_type = F_WRLCK;
1506 
1507     itrunc = fcntl (o, F_FREESP, &fl);
1508   }
1509 #endif
1510 
1511   if (itrunc != 0)
1512     {
1513 #if HAVE_FTRUNCATE
1514       ulog (LOG_ERROR, "ftruncate: %s", strerror (errno));
1515 #endif
1516 #ifdef HAVE_LTRUNC
1517       ulog (LOG_ERROR, "ltrunc: %s", strerror (errno));
1518 #endif
1519 #ifdef HAVE_F_CHSIZE
1520       ulog (LOG_ERROR, "fcntl (F_CHSIZE): %s", strerror (errno));
1521 #endif
1522 #ifdef HAVE_F_FREESP
1523       ulog (LOG_ERROR, "fcntl (F_FREESP): %s", strerror (errno));
1524 #endif
1525 
1526       (void) ffileclose (e);
1527       (void) remove (zname);
1528       return EFILECLOSED;
1529     }
1530 
1531   return e;
1532 #else /* ! (HAVE_FTRUNCATE || HAVE_LTRUNC) */
1533   (void) ffileclose (e);
1534   (void) remove (zname);
1535 
1536   o = creat (zname, IPRIVATE_FILE_MODE);
1537 
1538   if (o == -1)
1539     {
1540       ulog (LOG_ERROR, "open (%s): %s", zname, strerror (errno));
1541       return EFILECLOSED;
1542     }
1543 
1544 #if USE_STDIO
1545   e = fdopen (o, (char *) BINWRITE);
1546 
1547   if (e == NULL)
1548     {
1549       ulog (LOG_ERROR, "fdopen (%s): %s", zname, strerror (errno));
1550       (void) close (o);
1551       (void) remove (zname);
1552       return NULL;
1553     }
1554 #else /* ! USE_STDIO */
1555   e = o;
1556 #endif /* ! USE_STDIO */
1557 
1558   return e;
1559 #endif /* ! HAVE_FTRUNCATE */
1560 }
1561 
1562 /* Lock something.  If the fspooldir argument is TRUE, the argument is
1563    a file name relative to the spool directory; otherwise the argument
1564    is a simple file name which should be created in the system lock
1565    directory (under BNU this is /etc/locks).  */
1566 
1567 /*ARGSUSED*/
1568 boolean
1569 fsdo_lock (zlock, fspooldir)
1570      const char *zlock;
1571      boolean fspooldir;
1572 {
1573   const char *zpath, *zslash;
1574   int cslash;
1575   char *ztempfile;
1576   char abtempfile[20];
1577   int o;
1578   pid_t ime;
1579 #if HAVE_V2_LOCKFILES
1580   int i;
1581 #else
1582   char ab[12];
1583 #endif
1584   int cwrote;
1585   const char *zerr;
1586   boolean fret;
1587 
1588 #ifdef LOCKDIR
1589   if (fspooldir)
1590     zpath = zlock;
1591   else
1592     {
1593       char *zalc;
1594 
1595       zalc = (char *) alloca (sizeof LOCKDIR + strlen (zlock) + 1);
1596       sprintf (zalc, "%s/%s", LOCKDIR, zlock);
1597       zpath = zalc;
1598     }
1599 #else /* ! defined (LOCKDIR) */
1600   zpath = zlock;
1601 #endif
1602 
1603   ime = getpid ();
1604 
1605   /* We do the actual lock by creating a file and then linking it to
1606      the final file name we want.  This avoids race conditions due to
1607      one process checking the file before we have finished writing it,
1608      and also works even if we are somehow running as root.
1609 
1610      First, create the file in the right directory (we must create the
1611      file in the same directory since otherwise we might attempt a
1612      cross-device link).  */
1613   zslash = strrchr (zpath, '/');
1614   if (zslash == NULL)
1615     cslash = 0;
1616   else
1617     cslash = zslash - zpath + 1;
1618 
1619   ztempfile = (char *) alloca (cslash + sizeof "TMP1234567890");
1620   strncpy (ztempfile, zpath, cslash);
1621   sprintf (abtempfile, "TMP%010d", (int) ime);
1622   ztempfile[cslash] = '\0';
1623   strcat (ztempfile, abtempfile);
1624 
1625   o = creat (ztempfile, IPUBLIC_FILE_MODE);
1626   if (o < 0)
1627     {
1628       if (errno == ENOENT)
1629 	{
1630 	  if (! fsysdep_make_dirs (ztempfile, FALSE))
1631 	    return FALSE;
1632 	  o = creat (ztempfile, IPUBLIC_FILE_MODE);
1633 	}
1634       if (o < 0)
1635 	{
1636 	  ulog (LOG_ERROR, "open (%s): %s", ztempfile, strerror (errno));
1637 	  return FALSE;
1638 	}
1639     }
1640 
1641 #if HAVE_V2_LOCKFILES
1642   i = ime;
1643   cwrote = write (o, &i, sizeof i);
1644 #else
1645   sprintf (ab, "%10d\n", (int) ime);
1646   cwrote = write (o, ab, strlen (ab));
1647 #endif
1648 
1649   zerr = NULL;
1650   if (cwrote < 0)
1651     zerr = "write";
1652   if (close (o) < 0)
1653     zerr = "close";
1654   if (zerr != NULL)
1655     {
1656       ulog (LOG_ERROR, "%s (%s): %s", zerr, ztempfile, strerror (errno));
1657       (void) remove (ztempfile);
1658       return FALSE;
1659     }
1660 
1661   /* Now try to link the file we just created to the lock file that we
1662      want.  If it fails, try reading the existing file to make sure
1663      the process that created it still exists.  We do this in a loop
1664      to make it easy to retry if the old locking process no longer
1665      exists.  */
1666 
1667   fret = TRUE;
1668   o = -1;
1669   zerr = NULL;
1670 
1671   while (link (ztempfile, zpath) != 0)
1672     {
1673       int cgot;
1674       int ipid;
1675 
1676       fret = FALSE;
1677 
1678       if (errno != EEXIST)
1679 	{
1680 	  ulog (LOG_ERROR, "link (%s, %s): %s", ztempfile, zpath,
1681 		strerror (errno));
1682 	  break;
1683 	}
1684 
1685       o = open (zpath, O_RDWR, 0);
1686       if (o < 0)
1687 	{
1688 	  zerr = "open";
1689 	  break;
1690 	}
1691 
1692       /* The race starts here.  See below for a discussion.  */
1693 
1694 #if HAVE_V2_LOCKFILES
1695       cgot = read (o, &i, sizeof i);
1696 #else
1697       cgot = read (o, ab, sizeof ab - 1);
1698 #endif
1699 
1700       if (cgot < 0)
1701 	{
1702 	  zerr = "read";
1703 	  break;
1704 	}
1705 
1706 #if HAVE_V2_LOCKFILES
1707       ipid = i;
1708 #else
1709       ab[cgot] = '\0';
1710       ipid = atoi (ab);
1711 #endif
1712 
1713       /* If the process still exists, we will get EPERM rather than
1714 	 ESRCH.  We then return FALSE to indicate that we cannot make
1715 	 the lock.  */
1716       if (kill (ipid, 0) == 0 || errno == EPERM)
1717 	break;
1718 
1719       /* On NFS, the link might have actually succeeded even though we
1720 	 got a failure return.  This can happen if the original
1721 	 acknowledgement was lost or delayed and the operation was
1722 	 retried.  In this case the pid will be our own.  This
1723 	 introduces a rather improbable race condition: if a stale
1724 	 lock was left with our process ID in it, and another process
1725 	 just did the above kill but has not yet changed the lock file
1726 	 to hold its own process ID, we could start up and make it all
1727 	 the way to here and think we have the lock.  I'm not going to
1728 	 worry about this possibility.  */
1729       if (ipid == ime)
1730 	{
1731 	  fret = TRUE;
1732 	  break;
1733 	}
1734 
1735       ulog (LOG_ERROR, "Found stale lock %s held by process %d",
1736 	    zpath, ipid);
1737 
1738       /* This is a stale lock, created by a process that no longer
1739 	 exists.
1740 
1741 	 Now we could remove the file, but that would be a race
1742 	 condition.  If we were interrupted any time after we did the
1743 	 read until we did the remove, another process could get in,
1744 	 open the file, find that it was a stale lock, remove the file
1745 	 and create a new one.  When we woke up we would remove the
1746 	 file the other process just created.
1747 
1748 	 These files are being generated partially for the benefit of
1749 	 cu, and it would be nice to avoid the race however cu avoids
1750 	 it, so that the programs remain compatible.  Unfortunately,
1751 	 nobody seems to know how cu avoids the race, or even if it
1752 	 tries to avoid it at all.
1753 
1754 	 There are a few ways to avoid the race.  We could use kernel
1755 	 locking primitives, but they may not be available.  We could
1756 	 link to a special file name, but if that file were left lying
1757 	 around then no stale lock could ever be broken (Henry Spencer
1758 	 would think this was a good thing).
1759 
1760 	 Instead I've implemented the following procedure: seek to the
1761 	 start of the file, write our pid into it, sleep for five
1762 	 seconds, and then make sure our pid is still there.  Anybody
1763 	 who checks the file while we're asleep will find our pid
1764 	 there and fail the lock.  The only race will come from
1765 	 another process which has done the read by the time we do our
1766 	 write.  That process will then have five seconds to do its
1767 	 own write.  When we wake up, we'll notice that our pid is no
1768 	 longer in the file, and retry the lock from the beginning.
1769 
1770 	 This relies on the atomicity of write(2).  If it possible for
1771 	 the writes of two processes to be interleaved, the two
1772 	 processes could livelock.  POSIX unfortunately leaves this
1773 	 case explicitly undefined; however, given that the write is
1774 	 of less than a disk block, it's difficult to imagine an
1775 	 interleave occurring.
1776 
1777 	 Note that this is still a race.  If it takes the second
1778 	 process more than five seconds to do the kill, the lseek, and
1779 	 the write, both processes will think they have the lock.
1780 	 Perhaps the length of time to sleep should be configurable.
1781 	 Even better, perhaps I should add a configuration option to
1782 	 use a permanent lock file, which eliminates any race and
1783 	 forces the installer to be aware of the existence of the
1784 	 permanent lock file.
1785 
1786 	 For the benefit of cu, we stat the file after the sleep, to
1787 	 make sure some cu program hasn't deleted it for us.  */
1788 
1789       if (lseek (o, (off_t) 0, SEEK_SET) != 0)
1790 	{
1791 	  zerr = "lseek";
1792 	  break;
1793 	}
1794 
1795 #if HAVE_V2_LOCKFILES
1796       i = ime;
1797       cwrote = write (o, &i, sizeof i);
1798 #else
1799       sprintf (ab, "%10d\n", (int) ime);
1800       cwrote = write (o, ab, strlen (ab));
1801 #endif
1802 
1803       if (cwrote < 0)
1804 	{
1805 	  zerr = "write";
1806 	  break;
1807 	}
1808 
1809       (void) sleep (5);
1810 
1811       if (lseek (o, (off_t) 0, SEEK_SET) != 0)
1812 	{
1813 	  zerr = "lseek";
1814 	  break;
1815 	}
1816 
1817 #if HAVE_V2_LOCKFILES
1818       cgot = read (o, &i, sizeof i);
1819 #else
1820       cgot = read (o, ab, sizeof ab - 1);
1821 #endif
1822 
1823       if (cgot < 0)
1824 	{
1825 	  zerr = "read";
1826 	  break;
1827 	}
1828 
1829 #if HAVE_V2_LOCKFILES
1830       ipid = i;
1831 #else
1832       ab[cgot] = '\0';
1833       ipid = atoi (ab);
1834 #endif
1835 
1836       if (ipid == ime)
1837 	{
1838 	  struct stat sfile, sdescriptor;
1839 
1840 	  /* It looks like we have the lock.  Do the final stat
1841 	     check.  */
1842 
1843 	  if (stat (zpath, &sfile) != 0)
1844 	    {
1845 	      if (errno != ENOENT)
1846 		{
1847 		  zerr = "stat";
1848 		  break;
1849 		}
1850 	      /* Loop around and try again.  */
1851 	    }
1852 	  else
1853 	    {
1854 	      if (fstat (o, &sdescriptor) < 0)
1855 		{
1856 		  zerr = "fstat";
1857 		  break;
1858 		}
1859 
1860 	      if (sfile.st_ino == sdescriptor.st_ino
1861 		  && sfile.st_dev == sdescriptor.st_dev)
1862 		{
1863 		  /* Close the file before assuming we've succeeded to
1864 		     pick up any trailing errors.  */
1865 		  if (close (o) < 0)
1866 		    {
1867 		      zerr = "close";
1868 		      break;
1869 		    }
1870 
1871 		  o = -1;
1872 
1873 		  /* We have the lock.  */
1874 		  fret = TRUE;
1875 		  break;
1876 		}
1877 	    }
1878 	}
1879 
1880       /* Loop around and try the lock again.  We keep doing this until
1881 	 the lock file holds a pid that exists.  */
1882 
1883       (void) close (o);
1884       o = -1;
1885       fret = TRUE;
1886     }
1887 
1888   if (zerr != NULL)
1889     ulog (LOG_ERROR, "%s (%s): %s", zerr, zpath, strerror (errno));
1890 
1891   if (o >= 0)
1892     (void) close (o);
1893 
1894   /* It would be nice if we could leave the temporary file around for
1895      future calls, but considering that we create lock files in
1896      various different directories it's probably more trouble than
1897      it's worth.  */
1898   if (remove (ztempfile) != 0)
1899     ulog (LOG_ERROR, "remove (%s): %s", ztempfile, strerror (errno));
1900 
1901   return fret;
1902 }
1903 
1904 /* Unlock something.  The fspooldir argument is as in fsdo_lock.  */
1905 
1906 boolean
1907 fsdo_unlock (zlock, fspooldir)
1908      const char *zlock;
1909      boolean fspooldir;
1910 {
1911   const char *zpath;
1912 
1913 #ifdef LOCKDIR
1914   if (fspooldir)
1915     zpath = zlock;
1916   else
1917     {
1918       char *zalc;
1919 
1920       zalc = (char *) alloca (sizeof LOCKDIR + strlen (zlock) + 1);
1921       sprintf (zalc, "%s/%s", LOCKDIR, zlock);
1922       zpath = zalc;
1923     }
1924 #else /* ! defined (LOCKDIR) */
1925   zpath = zlock;
1926 #endif
1927 
1928   if (remove (zpath) == 0
1929       || errno == ENOENT)
1930     return TRUE;
1931   else
1932     {
1933       ulog (LOG_ERROR, "remove (%s): %s", zpath, strerror (errno));
1934       return FALSE;
1935     }
1936 }
1937 
1938 /* Lock a remote system.  */
1939 
1940 /*ARGSUSED*/
1941 boolean
1942 fsysdep_lock_system (qsys)
1943      const struct ssysteminfo *qsys;
1944 {
1945   char *z;
1946 
1947   z = (char *) alloca (strlen (qsys->zname) + sizeof "LCK..");
1948   sprintf (z, "LCK..%.8s", qsys->zname);
1949   return fsdo_lock (z, FALSE);
1950 }
1951 
1952 /* Unlock a remote system.  */
1953 
1954 /*ARGSUSED*/
1955 boolean
1956 fsysdep_unlock_system (qsys)
1957      const struct ssysteminfo *qsys;
1958 {
1959   char *z;
1960 
1961   z = (char *) alloca (strlen (qsys->zname) + sizeof "LCK..");
1962   sprintf (z, "LCK..%.8s", qsys->zname);
1963   return fsdo_unlock (z, FALSE);
1964 }
1965 
1966 /* Get a new command sequence number (this is not a sequence number to
1967    be used for communicating with another system, but a sequence
1968    number to be used when generating the name of a command file).
1969    The sequence number is placed into zseq, which should be five
1970    characters long.  */
1971 
1972 #define CSEQLEN (4)
1973 
1974 static boolean
1975 fscmd_seq (zsystem, zseq)
1976      const char *zsystem;
1977      char *zseq;
1978 {
1979   int ctries;
1980   const char *zfile;
1981   int o;
1982   int i;
1983 
1984   /* Lock the sequence file.  This may not be correct for all systems,
1985      but it only matters if the system UUCP and this UUCP are running
1986      at the same time.  */
1987 
1988   ctries = 0;
1989   while (! fsdo_lock ("LCK..SEQ", TRUE))
1990     {
1991       ++ctries;
1992       if (ctries > 10)
1993 	{
1994 	  ulog (LOG_ERROR, "Can't lock sequence file");
1995 	  return FALSE;
1996 	}
1997       sleep (1);
1998     }
1999 
2000 #if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43
2001   zfile = "SEQF";
2002 #endif
2003 #if SPOOLDIR_BNU
2004   {
2005     char *zalc;
2006 
2007     zalc = (char *) alloca (strlen (zsystem) + sizeof ".Sequence/");
2008     sprintf (zalc, ".Sequence/%s", zsystem);
2009     zfile = zalc;
2010   }
2011 #endif
2012 #if SPOOLDIR_ULTRIX
2013   if (fsultrix_has_spool (zsystem))
2014     {
2015       char *zalc;
2016 
2017       zalc = (char *) alloca (strlen (zsystem) + sizeof "sys//.SEQF");
2018       sprintf (zalc, "sys/%s/.SEQF", zsystem);
2019       zfile = zalc;
2020     }
2021   else
2022     zfile = "sys/DEFAULT/.SEQF";
2023 #endif /* SPOOLDIR_ULTRIX */
2024 #if SPOOLDIR_TAYLOR
2025   {
2026     char *zalc;
2027 
2028     zalc = (char *) alloca (strlen (zsystem) + sizeof "/SEQF");
2029     sprintf (zalc, "%s/SEQF", zsystem);
2030     zfile = zalc;
2031   }
2032 #endif /* SPOOLDIR_TAYLOR */
2033 
2034 #ifdef O_CREAT
2035   o = open (zfile, O_RDWR | O_CREAT, IPUBLIC_FILE_MODE);
2036 #else
2037   o = open (zfile, O_RDWR);
2038   if (o < 0 && errno == ENOENT)
2039     {
2040       o = creat (zfile, IPUBLIC_FILE_MODE);
2041       if (o >= 0)
2042 	{
2043 	  (void) close (o);
2044 	  o = open (zfile, O_RDWR);
2045 	}
2046     }
2047 #endif
2048 
2049   if (o < 0)
2050     {
2051       if (errno == ENOENT)
2052 	{
2053 	  if (! fsysdep_make_dirs (zfile, FALSE))
2054 	    {
2055 	      (void) fsdo_unlock ("LCK..SEQ", TRUE);
2056 	      return FALSE;
2057 	    }
2058 #ifdef O_CREAT
2059 	  o = open (zfile, O_RDWR | O_CREAT, IPUBLIC_FILE_MODE);
2060 #else
2061 	  o = creat (zfile, IPUBLIC_FILE_MODE);
2062 	  if (o >= 0)
2063 	    {
2064 	      (void) close (o);
2065 	      o = open (zfile, O_RDWR);
2066 	    }
2067 #endif
2068 	}
2069       if (o < 0)
2070 	{
2071 	  ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno));
2072 	  (void) fsdo_unlock ("LCK..SEQ", TRUE);
2073 	  return FALSE;
2074 	}
2075     }
2076 
2077   if (read (o, zseq, CSEQLEN) != CSEQLEN)
2078     strcpy (zseq, "0000");
2079   zseq[CSEQLEN] = '\0';
2080 
2081   /* We must add one to the sequence number and return the new value.
2082      On Ultrix, arbitrary characters are allowed in the sequence number.
2083      On other systems, the sequence number apparently must be in hex.  */
2084 
2085 #if SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_BNU
2086   i = strtol (zseq, (char **) NULL, 16);
2087   ++i;
2088   if (i > 0xffff)
2089     i = 0;
2090   /* The sprintf argument has CSEQLEN built into it.  */
2091   sprintf (zseq, "%04x", (unsigned int) i);
2092 #endif /* SPOOLDIR_V2 | SPOOLDIR_BSD42 | SPOOLDIR_BSD43 | SPOOLDIR_BNU */
2093 #if SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR
2094   for (i = CSEQLEN - 1; i >= 0; i--)
2095     {
2096       if (zseq[i] == 'z')
2097 	{
2098 	  zseq[i] = '0';
2099 	  continue;
2100 	}
2101 
2102       if (zseq[i] == '9')
2103 	zseq[i] = 'A';
2104       else if (zseq[i] == 'Z')
2105 	zseq[i] = 'a';
2106       else if ((zseq[i] >= '0' && zseq[i] < '9')
2107 	       || (zseq[i] >= 'A' && zseq[i] < 'Z')
2108 	       || (zseq[i] >= 'a' && zseq[i] < 'z'))
2109 	++zseq[i];
2110       else
2111 	{
2112 	  /* A bad character was found; reset the entire sequence
2113 	     number.  */
2114 	  ulog (LOG_ERROR,
2115 		"Bad sequence number %s for system %s",
2116 		zseq, zsystem);
2117 	  strcpy (zseq, "0000");
2118 	}
2119 
2120       break;
2121     }
2122 #endif /* SPOOLDIR_ULTRIX | SPOOLDIR_TAYLOR */
2123 
2124   if (lseek (o, (off_t) 0, SEEK_SET) < 0
2125       || write (o, zseq, CSEQLEN) != CSEQLEN
2126       || close (o) < 0)
2127     {
2128       ulog (LOG_ERROR, "lseek or write or close: %s", strerror (errno));
2129       (void) close (o);
2130       (void) fsdo_unlock ("LCK..SEQ", TRUE);
2131       return FALSE;
2132     }
2133 
2134   (void) fsdo_unlock ("LCK..SEQ", TRUE);
2135 
2136   return TRUE;
2137 }
2138 
2139 /* Get the name of a command or data file for a remote system.  The
2140    btype argument should be C for a command file or D for a data file.
2141    If the grade of a data file is X, it is assumed that this is going
2142    to become an execute file on some other system.  The zsystem
2143    argument is the system that the file will be transferred to.  The
2144    ztname argument will be set to a file name that could be passed to
2145    zsysdep_spool_file_name.  The zdname argument, if not NULL, will be
2146    set to a data file name appropriate for the remote system.  The
2147    zxname argument, if not NULL, will be set to the name of an execute
2148    file on the remote system.  None of the names will be more than 14
2149    characters long.  */
2150 
2151 static const char *
2152 zsfile_name (btype, zsystem, bgrade, ztname, zdname, zxname)
2153      int btype;
2154      const char *zsystem;
2155      int bgrade;
2156      char *ztname;
2157      char *zdname;
2158      char *zxname;
2159 {
2160   char abseq[CSEQLEN + 1];
2161   char absimple[11 + CSEQLEN];
2162   const char *zname;
2163   struct stat s;
2164 
2165   do
2166     {
2167       if (! fscmd_seq (zsystem, abseq))
2168 	return NULL;
2169 
2170       if (btype == 'C')
2171 	{
2172 #if ! SPOOLDIR_TAYLOR
2173 	  sprintf (absimple, "C.%.7s%c%s", zsystem, bgrade, abseq);
2174 #else
2175 	  sprintf (absimple, "C.%c%s", bgrade, abseq);
2176 #endif
2177 	}
2178       else if (btype == 'D')
2179 	{
2180 #if ! SPOOLDIR_TAYLOR
2181 	  /* Note that a data file uses the local system's name.  */
2182 	  sprintf (absimple, "D.%.7s%c%s", zLocalname, bgrade, abseq);
2183 #else
2184 	  if (bgrade == 'X')
2185 	    sprintf (absimple, "D.X%s", abseq);
2186 	  else
2187 	    sprintf (absimple, "D.%s", abseq);
2188 #endif
2189 	}
2190 #if DEBUG > 0
2191       else
2192 	ulog (LOG_FATAL, "zsfile_name: Can't happen (%d)", btype);
2193 #endif
2194 
2195       zname = zsfind_file (absimple, zsystem);
2196       if (zname == NULL)
2197 	return NULL;
2198     }
2199   while (stat (zname, &s) == 0);
2200 
2201   if (ztname != NULL)
2202     strcpy (ztname, absimple);
2203 
2204   if (zdname != NULL)
2205     sprintf (zdname, "D.%.7s%c%s", zLocalname, bgrade, abseq);
2206 
2207   if (zxname != NULL)
2208     sprintf (zxname, "X.%.7s%c%s", zLocalname, bgrade, abseq);
2209 
2210   return zname;
2211 }
2212 
2213 /* Given a set of commands to execute for a remote system, create a
2214    command file holding them.  This creates a single command file
2215    holding all the commands passed in.  It returns a jobid.  */
2216 
2217 const char *
2218 zsysdep_spool_commands (qsys, bgrade, ccmds, pascmds)
2219      const struct ssysteminfo *qsys;
2220      int bgrade;
2221      int ccmds;
2222      const struct scmd *pascmds;
2223 {
2224   const char *z;
2225   FILE *e;
2226   int i;
2227   const struct scmd *q;
2228   const char *zjobid;
2229 
2230 #if DEBUG > 0
2231   if (! FGRADE_LEGAL (bgrade))
2232     ulog (LOG_FATAL, "Bad grade %d", bgrade);
2233 #endif
2234 
2235   z = zsfile_name ('C', qsys->zname, bgrade, (char *) NULL, (char *) NULL,
2236 		   (char *) NULL);
2237   if (z == NULL)
2238     return NULL;
2239 
2240   e = esysdep_fopen (z, FALSE, FALSE, TRUE);
2241   if (e == NULL)
2242     return NULL;
2243 
2244   for (i = 0, q = pascmds; i < ccmds; i++, q++)
2245     {
2246       switch (q->bcmd)
2247 	{
2248 	case 'S':
2249 	  fprintf (e, "S %s %s %s -%s %s 0%o %s\n", q->zfrom, q->zto,
2250 		   q->zuser, q->zoptions, q->ztemp, q->imode,
2251 		   q->znotify);
2252 	  break;
2253 	case 'R':
2254 	  fprintf (e, "R %s %s %s -%s\n", q->zfrom, q->zto, q->zuser,
2255 		   q->zoptions);
2256 	  break;
2257 	case 'X':
2258 	  fprintf (e, "X %s %s %s -%s\n", q->zfrom, q->zto, q->zuser,
2259 		   q->zoptions);
2260 	  break;
2261 	default:
2262 	  ulog (LOG_ERROR,
2263 		"zsysdep_spool_commands: Unrecognized type %d",
2264 		q->bcmd);
2265 	  (void) fclose (e);
2266 	  (void) remove (z);
2267 	  return NULL;
2268 	}
2269     }
2270 
2271   if (fclose (e) != 0)
2272     {
2273       ulog (LOG_ERROR, "fclose: %s", strerror (errno));
2274       (void) remove (z);
2275       return NULL;
2276     }
2277 
2278   zjobid = zsfile_to_jobid (qsys, z);
2279   if (zjobid == NULL)
2280     (void) remove (z);
2281   return zjobid;
2282 }
2283 
2284 /* Return a name to use for a data file to be copied to another
2285    system.  The name returned will be for a real file.  The ztname
2286    argument, if not NULL, will be set to a name that could be passed
2287    to zsysdep_spool_file_name to get back the return value of this
2288    function.  The zdname argument, if not NULL, will be set to a name
2289    that the file could be given on another system.  The zxname
2290    argument, if not NULL, will be set to a name for an execute file on
2291    another system.  */
2292 
2293 const char *
2294 zsysdep_data_file_name (qsys, bgrade, ztname, zdname, zxname)
2295      const struct ssysteminfo *qsys;
2296      int bgrade;
2297      char *ztname;
2298      char *zdname;
2299      char *zxname;
2300 {
2301   return zsfile_name ('D', qsys->zname, bgrade, ztname, zdname, zxname);
2302 }
2303 
2304 /* Return a name for an execute file to be created locally.  This is
2305    used by uux to execute a command locally with remote files.  */
2306 
2307 const char *
2308 zsysdep_xqt_file_name ()
2309 {
2310   char abseq[CSEQLEN + 1];
2311   char absx[11 + CSEQLEN];
2312   const char *zname;
2313   struct stat s;
2314 
2315   while (TRUE)
2316     {
2317       if (! fscmd_seq (zLocalname, abseq))
2318 	return NULL;
2319 
2320       sprintf (absx, "X.%.7sX%s", zLocalname, abseq);
2321 
2322       zname = zsfind_file (absx, zLocalname);
2323       if (zname == NULL)
2324 	return NULL;
2325       if (stat (zname, &s) != 0)
2326 	break;
2327     }
2328 
2329   return zname;
2330 }
2331 
2332 /* Start getting a wildcarded file spec.  We use the shell to expand
2333    the wildcard.  */
2334 
2335 static char *zSwildcard_alloc;
2336 static char *zSwildcard;
2337 
2338 boolean
2339 fsysdep_wildcard_start (qsys, zfile)
2340      const struct ssysteminfo *qsys;
2341      const char *zfile;
2342 {
2343   char *zcmd;
2344   const char *azargs[4];
2345   FILE *e;
2346   pid_t ipid;
2347 
2348   zSwildcard_alloc = NULL;
2349   zSwildcard = NULL;
2350 
2351   if (*zfile == '~')
2352     {
2353       zfile = zstilde_expand (qsys, zfile);
2354       if (zfile == NULL)
2355 	return FALSE;
2356     }
2357 
2358   if (*zfile != '/')
2359     {
2360       ulog (LOG_ERROR, "Relative path not permitted in wildcard");
2361       return FALSE;
2362     }
2363 
2364   zcmd = (char *) alloca (sizeof ECHO_PROGRAM + sizeof " " + strlen (zfile));
2365   sprintf (zcmd, "%s %s", ECHO_PROGRAM, zfile);
2366 
2367   azargs[0] = "/bin/sh";
2368   azargs[1] = "-c";
2369   azargs[2] = zcmd;
2370   azargs[3] = NULL;
2371 
2372   e = espopen (azargs, TRUE, &ipid);
2373   if (e == NULL)
2374     {
2375       ulog (LOG_ERROR, "espopen: %s", strerror (errno));
2376       return FALSE;
2377     }
2378 
2379   zSwildcard_alloc = zfgets (e, FALSE);
2380 
2381   if (iswait ((unsigned long) ipid, ECHO_PROGRAM) != 0)
2382     {
2383       xfree ((pointer) zSwildcard_alloc);
2384       return FALSE;
2385     }
2386 
2387   if (zSwildcard_alloc == NULL)
2388     return FALSE;
2389 
2390   DEBUG_MESSAGE1 (DEBUG_EXECUTE,
2391 		  "fsysdep_wildcard_start: got \"%s\"",
2392 		  zSwildcard_alloc);
2393 
2394   zSwildcard = zSwildcard_alloc;
2395 
2396   return TRUE;
2397 }
2398 
2399 /* Get the next wildcard spec.  */
2400 
2401 /*ARGSUSED*/
2402 const char *
2403 zsysdep_wildcard (qsys, zfile)
2404      const struct ssysteminfo *qsys;
2405      const char *zfile;
2406 {
2407   char *zret;
2408 
2409   if (zSwildcard_alloc == NULL || zSwildcard == NULL)
2410     return NULL;
2411 
2412   zret = zSwildcard;
2413 
2414   while (*zSwildcard != '\0' && ! isspace (BUCHAR (*zSwildcard)))
2415     ++zSwildcard;
2416 
2417   if (*zSwildcard == '\0')
2418     zSwildcard = NULL;
2419   else
2420     {
2421       *zSwildcard = '\0';
2422       ++zSwildcard;
2423       while (*zSwildcard != '\0' && isspace (BUCHAR (*zSwildcard)))
2424 	++zSwildcard;
2425       if (*zSwildcard == '\0')
2426 	zSwildcard = NULL;
2427     }
2428 
2429   return zret;
2430 }
2431 
2432 /* Finish up getting wildcard specs.  */
2433 
2434 boolean
2435 fsysdep_wildcard_end ()
2436 {
2437   xfree ((pointer) zSwildcard_alloc);
2438   zSwildcard_alloc = NULL;
2439   zSwildcard = NULL;
2440   return TRUE;
2441 }
2442 
2443 /* Get the current conversation sequence number for a remote system,
2444    and increment it for next time.  The conversation sequence number
2445    is kept in a file named .SQ in the spool directory for that system.
2446    This is not compatible with other versions of UUCP, but it makes
2447    more sense to me.  The sequence file is only used if specified in
2448    the information for that system.  In V2, the file
2449    /usr/lib/uucp/SQFILE is searched for each system to get a
2450    conversation sequence number.  */
2451 
2452 long
2453 isysdep_get_sequence (qsys)
2454      const struct ssysteminfo *qsys;
2455 {
2456   FILE *e;
2457   const char *zname;
2458   struct stat s;
2459   long iseq;
2460 
2461   /* This will only be called when the system is locked anyhow, so there
2462      is no need to use a separate lock for the conversation sequence
2463      file.  */
2464 
2465   zname = zsappend (".Sequence", qsys->zname);
2466 
2467   iseq = 0;
2468   if (stat (zname, &s) == 0)
2469     {
2470       boolean fok;
2471       char *zline;
2472 
2473       /* The file should only be readable and writable by uucp.  */
2474 
2475       if ((s.st_mode & (S_IRWXG | S_IRWXO)) != 0)
2476 	{
2477 	  ulog (LOG_ERROR,
2478 		"Bad file protection for conversation sequence file");
2479 	  return -1;
2480 	}
2481 
2482       /* The file already exists, so we don't have to worry about
2483 	 its protection.  */
2484       e = fopen (zname, "r+");
2485       if (e == NULL)
2486 	{
2487 	  ulog (LOG_ERROR, "fopen (%s): %s", zname, strerror (errno));
2488 	  return -1;
2489 	}
2490 
2491       zline = zfgets (e, FALSE);
2492 
2493       fok = TRUE;
2494       if (zline == NULL)
2495 	fok = FALSE;
2496       else
2497 	{
2498 	  char *zend;
2499 
2500 	  iseq = strtol (zline, &zend, 10);
2501 	  if (zend == zline)
2502 	    fok = FALSE;
2503 	  xfree ((pointer) zline);
2504 	}
2505 
2506       if (! fok)
2507 	{
2508 	  ulog (LOG_ERROR, "Bad format for conversation sequence file");
2509 	  return -1;
2510 	}
2511 
2512       rewind (e);
2513     }
2514   else
2515     {
2516       e = esysdep_fopen (zname, FALSE, FALSE, TRUE);
2517       if (e == NULL)
2518 	return -1;
2519     }
2520 
2521   ++iseq;
2522 
2523   fprintf (e, "%ld", iseq);
2524 
2525   if (fclose (e) != 0)
2526     {
2527       ulog (LOG_ERROR, "fclose: %s", strerror (errno));
2528       return -1;
2529     }
2530 
2531   return iseq;
2532 }
2533 
2534 /* Get the Unix file mode of a file.  */
2535 
2536 unsigned int
2537 isysdep_file_mode (zfile)
2538      const char *zfile;
2539 {
2540   struct stat s;
2541 
2542   if (stat (zfile, &s) != 0)
2543     {
2544       ulog (LOG_ERROR, "stat (%s): %s", zfile, strerror (errno));
2545       return 0;
2546     }
2547 
2548 #if S_IRWXU != 0700
2549  #error Files modes need to be translated
2550 #endif
2551 
2552   /* We can't return 0, since that indicate an error.  */
2553   if ((s.st_mode & 0777) == 0)
2554     return 0400;
2555 
2556   return s.st_mode & 0777;
2557 }
2558 
2559 /* Translate a file name and an associated system into a job id.
2560    These job ids are used by uustat.  We use the system name attached
2561    to the grade and sequence number.  */
2562 
2563 const char *
2564 zsfile_to_jobid (qsys, zfile)
2565      const struct ssysteminfo *qsys;
2566      const char *zfile;
2567 {
2568   char *zid;
2569 
2570   zid = (char *) alloca (strlen (qsys->zname) + CSEQLEN + 2);
2571   sprintf (zid, "%s%s", qsys->zname,
2572 	   zfile + strlen (zfile) - CSEQLEN - 1);
2573 
2574   return zscopy (zid);
2575 }
2576 
2577 /* Turn a job id back into a file name.  */
2578 
2579 const char *
2580 zsjobid_to_file (zid, pzsystem)
2581      const char *zid;
2582      const char **pzsystem;
2583 {
2584   int clen;
2585   char abend[CSEQLEN + 2];
2586   static char *zsys;
2587   static int csyslen;
2588   char abname[CSEQLEN + 11];
2589 
2590   clen = strlen (zid);
2591   strcpy (abend, zid + clen - CSEQLEN - 1);
2592 
2593   if (clen - CSEQLEN > csyslen)
2594     {
2595       zsys = (char *) xrealloc ((pointer) zsys, clen - CSEQLEN);
2596       csyslen = clen - CSEQLEN;
2597     }
2598 
2599   strncpy (zsys, zid, clen - CSEQLEN - 1);
2600   zsys[clen - CSEQLEN - 1] = '\0';
2601   if (pzsystem != NULL)
2602     *pzsystem = zsys;
2603 
2604   /* This must correspond to zsfile_name.  */
2605 #if ! SPOOLDIR_TAYLOR
2606   sprintf (abname, "C.%.7s%s", zsys, abend);
2607 #else
2608   sprintf (abname, "C.%s", abend);
2609 #endif
2610 
2611   return zsfind_file (abname, zsys);
2612 }
2613 
2614 #if ! HAVE_RENAME
2615 
2616 /* This is currently the only file which calls rename, so I've put my
2617    fake rename function in here.  */
2618 
2619 static int
2620 rename (zfrom, zto)
2621      const char *zfrom;
2622      const char *zto;
2623 {
2624   /* Try to make the link without removing the old file first.  */
2625   if (link (zfrom, zto) == -1)
2626     {
2627       if (errno != EEXIST)
2628 	return -1;
2629 
2630       /* Assume that this will never be called with zfrom the same as
2631 	 zto.  If it could be, this is wrong.  */
2632       (void) remove (zto);
2633 
2634       if (link (zfrom, zto) == -1)
2635 	return -1;
2636     }
2637 
2638   return remove (zfrom);
2639 }
2640 
2641 #endif /* ! HAVE_RENAME */
2642 
2643 /*
2644   Local variables:
2645   mode:c
2646   End:
2647   */
2648