1/* filnam.c
2   Get names to use for UUCP files.
3
4   Copyright (C) 1991, 1992, 1993, 1995 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21
22   The author of the program may be contacted at ian@airs.com.
23   */
24
25#include "uucp.h"
26
27#include "uudefs.h"
28#include "uuconf.h"
29#include "sysdep.h"
30#include "system.h"
31
32#include <errno.h>
33
34#if HAVE_FCNTL_H
35#include <fcntl.h>
36#else
37#if HAVE_SYS_FILE_H
38#include <sys/file.h>
39#endif
40#endif
41
42#ifndef O_RDONLY
43#define O_RDONLY 0
44#define O_WRONLY 1
45#define O_RDWR 2
46#endif
47
48#ifndef O_NOCTTY
49#define O_NOCTTY 0
50#endif
51
52/* We need a definition for SEEK_SET.  */
53
54#ifndef SEEK_SET
55#define SEEK_SET 0
56#endif
57
58/* We use POSIX style fcntl locks if they are available, unless
59   O_CREAT is not defined.  We could use them in the latter case, but
60   the code would have to become more complex to avoid races
61   concerning the use of creat.  It is very unlikely that there is any
62   system which does have POSIX style locking but does not have
63   O_CREAT.  */
64#if ! HAVE_BROKEN_SETLKW
65#ifdef F_SETLKW
66#ifdef O_CREAT
67#define USE_POSIX_LOCKS 1
68#endif
69#endif
70#endif
71#ifndef USE_POSIX_LOCKS
72#define USE_POSIX_LOCKS 0
73#endif
74
75/* External functions.  */
76#ifndef lseek
77extern off_t lseek ();
78#endif
79
80#define ZCHARS \
81  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
82
83/* Local functions.  */
84
85static boolean fscmd_seq P((const char *zsystem, char *zseq));
86static char *zsfile_name P((int btype, const char *zsystem,
87			    const char *zlocalname, int bgrade,
88			    boolean fxqt, char *ztname, char *zdname,
89			    char *zxname));
90
91/* Get a new command sequence number (this is not a sequence number to
92   be used for communicating with another system, but a sequence
93   number to be used when generating the name of a command file).
94   The sequence number is placed into zseq, which should be five
95   characters long.  */
96
97static boolean
98fscmd_seq (zsystem, zseq)
99     const char *zsystem;
100     char *zseq;
101{
102  int cdelay;
103  char *zfree;
104  const char *zfile;
105  int o;
106  boolean flockfile;
107  int i;
108  boolean fret;
109
110  cdelay = 5;
111
112#if ! USE_POSIX_LOCKS
113  {
114    boolean ferr;
115
116    /* Lock the sequence file.  */
117    while (! fsdo_lock ("LCK..SEQ", TRUE, &ferr))
118      {
119	if (ferr || FGOT_SIGNAL ())
120	  return FALSE;
121	sleep (cdelay);
122	if (cdelay < 60)
123	  ++cdelay;
124      }
125  }
126#endif
127
128  zfree = NULL;
129
130#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43
131  zfile = "SEQF";
132#endif
133#if SPOOLDIR_HDB || SPOOLDIR_SVR4
134  zfree = zsysdep_in_dir (".Sequence", zsystem);
135  zfile = zfree;
136#endif
137#if SPOOLDIR_ULTRIX
138  if (! fsultrix_has_spool (zsystem))
139    zfile = "sys/DEFAULT/.SEQF";
140  else
141    {
142      zfree = zsappend3 ("sys", zsystem, ".SEQF");
143      zfile = zfree;
144    }
145#endif /* SPOOLDIR_ULTRIX */
146#if SPOOLDIR_TAYLOR
147  zfree = zsysdep_in_dir (zsystem, "SEQF");
148  zfile = zfree;
149#endif /* SPOOLDIR_TAYLOR */
150
151#ifdef O_CREAT
152  o = open ((char *) zfile, O_RDWR | O_CREAT | O_NOCTTY, IPRIVATE_FILE_MODE);
153#else
154  o = open ((char *) zfile, O_RDWR | O_NOCTTY);
155  if (o < 0 && errno == ENOENT)
156    {
157      o = creat ((char *) zfile, IPRIVATE_FILE_MODE);
158      if (o >= 0)
159	{
160	  (void) close (o);
161	  o = open ((char *) zfile, O_RDWR | O_NOCTTY);
162	}
163    }
164#endif
165
166  if (o < 0)
167    {
168      if (errno == ENOENT)
169	{
170	  if (! fsysdep_make_dirs (zfile, FALSE))
171	    {
172#if ! USE_POSIX_LOCKS
173	      (void) fsdo_unlock ("LCK..SEQ", TRUE);
174#endif
175	      return FALSE;
176	    }
177#ifdef O_CREAT
178	  o = open ((char *) zfile,
179		    O_RDWR | O_CREAT | O_NOCTTY,
180		    IPRIVATE_FILE_MODE);
181#else
182	  o = creat ((char *) zfile, IPRIVATE_FILE_MODE);
183	  if (o >= 0)
184	    {
185	      (void) close (o);
186	      o = open ((char *) zfile, O_RDWR | O_NOCTTY);
187	    }
188#endif
189	}
190      if (o < 0)
191	{
192	  ulog (LOG_ERROR, "open (%s): %s", zfile, strerror (errno));
193#if ! USE_POSIX_LOCKS
194	  (void) fsdo_unlock ("LCK..SEQ", TRUE);
195#endif
196	  return FALSE;
197	}
198    }
199
200#if ! USE_POSIX_LOCKS
201  flockfile = TRUE;
202#else
203  {
204    struct flock slock;
205
206    flockfile = FALSE;
207
208    slock.l_type = F_WRLCK;
209    slock.l_whence = SEEK_SET;
210    slock.l_start = 0;
211    slock.l_len = 0;
212    while (fcntl (o, F_SETLKW, &slock) == -1)
213      {
214	boolean fagain;
215
216	/* Some systems define F_SETLKW, but it does not work.  We try
217           to catch those systems at runtime, and revert to using a
218           lock file.  */
219	if (errno == EINVAL)
220	  {
221	    boolean ferr;
222
223	    /* Lock the sequence file.  */
224	    while (! fsdo_lock ("LCK..SEQ", TRUE, &ferr))
225	      {
226		if (ferr || FGOT_SIGNAL ())
227		  {
228		    (void) close (o);
229		    return FALSE;
230		  }
231		sleep (cdelay);
232		if (cdelay < 60)
233		  ++cdelay;
234	      }
235
236	    flockfile = TRUE;
237
238	    break;
239	  }
240
241	fagain = FALSE;
242	if (errno == ENOMEM)
243	  fagain = TRUE;
244#ifdef ENOLCK
245	if (errno == ENOLCK)
246	  fagain = TRUE;
247#endif
248#ifdef ENOSPC
249	if (errno == ENOSPC)
250	  fagain = TRUE;
251#endif
252	if (fagain)
253	  {
254	    sleep (cdelay);
255	    if (cdelay < 60)
256	      ++cdelay;
257	    continue;
258	  }
259	ulog (LOG_ERROR, "Locking %s: %s", zfile, strerror (errno));
260	(void) close (o);
261	return FALSE;
262      }
263  }
264#endif
265
266  if (read (o, zseq, CSEQLEN) != CSEQLEN)
267    strcpy (zseq, "0000");
268  zseq[CSEQLEN] = '\0';
269
270  /* We must add one to the sequence number and return the new value.
271     On Ultrix, arbitrary characters are allowed in the sequence
272     number.  On other systems, the sequence number apparently must be
273     in hex.  */
274#if SPOOLDIR_V2 || SPOOLDIR_BSD42 || SPOOLDIR_BSD43 || SPOOLDIR_HDB || SPOOLDIR_SVR4
275  i = (int) strtol (zseq, (char **) NULL, 16);
276  ++i;
277  if (i > 0xffff)
278    i = 0;
279  /* The sprintf argument has CSEQLEN built into it.  */
280  sprintf (zseq, "%04x", (unsigned int) i);
281#endif
282#if SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR
283  for (i = CSEQLEN - 1; i >= 0; i--)
284    {
285      const char *zdig;
286
287      zdig = strchr (ZCHARS, zseq[i]);
288      if (zdig == NULL || zdig[0] == '\0' || zdig[1] == '\0')
289	zseq[i] = '0';
290      else
291	{
292	  zseq[i] = zdig[1];
293	  break;
294	}
295    }
296#endif /* SPOOLDIR_ULTRIX || SPOOLDIR_TAYLOR */
297
298  fret = TRUE;
299
300  if (lseek (o, (off_t) 0, SEEK_SET) < 0
301      || write (o, zseq, CSEQLEN) != CSEQLEN
302      || close (o) < 0)
303    {
304      ulog (LOG_ERROR, "lseek or write or close %s: %s",
305	    zfile, strerror (errno));
306      (void) close (o);
307      fret = FALSE;
308    }
309
310  if (flockfile)
311    (void) fsdo_unlock ("LCK..SEQ", TRUE);
312
313  ubuffree (zfree);
314
315  return fret;
316}
317
318/* Get the name of a command or data file for a remote system.  The
319   btype argument should be C for a command file or D for a data file.
320   If the grade of a data file is X, it is assumed that this is going
321   to become an execute file on some other system.  The zsystem
322   argument is the system that the file will be transferred to.  The
323   ztname argument will be set to a file name that could be passed to
324   zsysdep_spool_file_name.  The zdname argument, if not NULL, will be
325   set to a data file name appropriate for the remote system.  The
326   zxname argument, if not NULL, will be set to the name of an execute
327   file on the remote system.  None of the names will be more than 14
328   characters long.  */
329
330/*ARGSUSED*/
331static char *
332zsfile_name (btype, zsystem, zlocalname, bgrade, fxqt, ztname, zdname, zxname)
333     int btype;
334     const char *zsystem;
335     const char *zlocalname;
336     int bgrade;
337     boolean fxqt;
338     char *ztname;
339     char *zdname;
340     char *zxname;
341{
342  char abseq[CSEQLEN + 1];
343  char absimple[11 + CSEQLEN];
344  char *zname;
345
346  if (zlocalname == NULL)
347    zlocalname = zSlocalname;
348
349  while (TRUE)
350    {
351      if (! fscmd_seq (zsystem, abseq))
352	return NULL;
353
354      if (btype == 'C')
355	{
356#if ! SPOOLDIR_TAYLOR
357	  sprintf (absimple, "C.%.7s%c%s", zsystem, bgrade, abseq);
358#else
359	  sprintf (absimple, "C.%c%s", bgrade, abseq);
360#endif
361	}
362      else if (btype == 'D')
363	{
364	  /* This name doesn't really matter that much; it's just the
365	     name we use on the local system.  The name we use on the
366	     remote system, which we return in zdname, should contain
367	     our system name so that remote UUCP's running SPOOLDIR_V2
368	     and the like can distinguish while files come from which
369	     systems.  */
370#if SPOOLDIR_SVR4
371	  sprintf (absimple, "D.%.7s%c%s", zsystem, bgrade, abseq);
372#else /* ! SPOOLDIR_SVR4 */
373#if ! SPOOLDIR_TAYLOR
374	  sprintf (absimple, "D.%.7s%c%s", zlocalname, bgrade, abseq);
375#else /* SPOOLDIR_TAYLOR */
376	  if (fxqt)
377	    sprintf (absimple, "D.X%s", abseq);
378	  else
379	    sprintf (absimple, "D.%s", abseq);
380#endif /* SPOOLDIR_TAYLOR */
381#endif /* ! SPOOLDIR_HDB && ! SPOOLDIR_SVR4 */
382	}
383#if DEBUG > 0
384      else
385	ulog (LOG_FATAL, "zsfile_name: Can't happen");
386#endif
387
388      zname = zsfind_file (absimple, zsystem, bgrade);
389      if (zname == NULL)
390	return NULL;
391
392      if (! fsysdep_file_exists (zname))
393	break;
394
395      ubuffree (zname);
396    }
397
398  if (ztname != NULL)
399    strcpy (ztname, absimple);
400
401  if (zdname != NULL)
402    sprintf (zdname, "D.%.7s%c%s", zlocalname, bgrade, abseq);
403
404  if (zxname != NULL)
405    sprintf (zxname, "X.%.7s%c%s", zlocalname, bgrade, abseq);
406
407  return zname;
408}
409
410/* Return a name to use for a data file to be copied to another
411   system.  The name returned will be for a real file.  The zlocalname
412   argument is the local name as seen by the remote system, the bgrade
413   argument is the file grade, and the fxqt argument is TRUE if this
414   file will become an execution file.  The ztname argument, if not
415   NULL, will be set to a name that could be passed to
416   zsysdep_spool_file_name to get back the return value of this
417   function.  The zdname argument, if not NULL, will be set to a name
418   that the file could be given on another system.  The zxname
419   argument, if not NULL, will be set to a name for an execute file on
420   another system.  */
421
422char *
423zsysdep_data_file_name (qsys, zlocalname, bgrade, fxqt, ztname, zdname,
424			zxname)
425     const struct uuconf_system *qsys;
426     const char *zlocalname;
427     int bgrade;
428     boolean fxqt;
429     char *ztname;
430     char *zdname;
431     char *zxname;
432{
433  return zsfile_name ('D', qsys->uuconf_zname, zlocalname, bgrade, fxqt,
434		      ztname, zdname, zxname);
435}
436
437#if SPOOLDIR_TAYLOR
438
439/* Write out a number in base 62 into a given number of characters,
440   right justified with zero fill.  This is used by zscmd_file if
441   SPOOLDIR_TAYLOR.  */
442
443static void usput62 P((long i, char *, int c));
444
445static void
446usput62 (i, z, c)
447     long i;
448     char *z;
449     int c;
450{
451  for (--c; c >= 0; --c)
452    {
453      int d;
454
455      d = i % 62;
456      i /= 62;
457      if (d < 26)
458	z[c] = 'A' + d;
459      else if (d < 52)
460	z[c] = 'a' + d - 26;
461      else
462	z[c] = '0' + d - 52;
463    }
464}
465
466#endif /* SPOOLDIR_TAYLOR */
467
468/* Get a command file name.  */
469
470char *
471zscmd_file (qsys, bgrade)
472     const struct uuconf_system *qsys;
473     int bgrade;
474{
475#if ! SPOOLDIR_TAYLOR
476  return zsfile_name ('C', qsys->uuconf_zname, (const char *) NULL,
477		      bgrade, FALSE, (char *) NULL, (char *) NULL,
478		      (char *) NULL);
479#else
480  char *zname;
481  long isecs, imicros;
482  pid_t ipid;
483
484  /* This file name is never seen by the remote system, so we don't
485     actually need to get a sequence number for it.  We just need to
486     get a file name which is unique for this system.  We don't try
487     this optimization for other spool directory formats, mainly due
488     to compatibility concerns.  It would be possible for HDB and SVR4
489     spool directory formats.
490
491     We get a unique name by combining the process ID and the current
492     time.  The file name must start with C.g, where g is the grade.
493     Note that although it is likely that this name will be unique, it
494     is not guaranteed, so the caller must be careful.  */
495
496  isecs = ixsysdep_time (&imicros);
497  ipid = getpid ();
498
499  /* We are going to represent the file name as a series of numbers in
500     base 62 (using the alphanumeric characters).  The maximum file
501     name length is 14 characters, so we may use 11.  We use 3 for the
502     seconds within the day, 3 for the microseconds, and 5 for the
503     process ID.  */
504
505  /* Cut the seconds down to a number within a day (maximum value
506     86399 < 62 ** 3 == 238328).  */
507  isecs %= (long) 24 * (long) 60 * (long) 60;
508  /* Divide the microseconds (max 999999) by 5 to make sure they are
509     less than 62 ** 3.  */
510  imicros %= 1000000;
511  imicros /= 5;
512
513  while (TRUE)
514    {
515      char ab[15];
516
517      ab[0] = 'C';
518      ab[1] = '.';
519      ab[2] = bgrade;
520      usput62 (isecs, ab + 3, 3);
521      usput62 (imicros, ab + 6, 3);
522      usput62 ((long) ipid, ab + 9, 5);
523      ab[14] = '\0';
524
525      zname = zsfind_file (ab, qsys->uuconf_zname, bgrade);
526      if (zname == NULL)
527	return NULL;
528
529      if (! fsysdep_file_exists (zname))
530	break;
531
532      ubuffree (zname);
533
534      /* We hit a duplicate.  Move backward in time until we find an
535         available name.  Note that there is still a theoretical race
536         condition, since 5 base 62 digits might not be enough for the
537         process ID, and some other process might be running these
538         checks at the same time as we are.  The caller must deal with
539         this.  */
540      if (imicros == 0)
541	{
542	  imicros = (long) 62 * (long) 62 * (long) 62;
543	  if (isecs == 0)
544	    isecs = (long) 62 * (long) 62 * (long) 62;
545	  --isecs;
546	}
547      --imicros;
548    }
549
550  return zname;
551
552#endif
553}
554
555/* Return a name for an execute file to be created locally.  This is
556   used by uux to execute a command locally with remote files.  */
557
558char *
559zsysdep_xqt_file_name ()
560{
561  char abseq[CSEQLEN + 1];
562  char absx[11 + CSEQLEN];
563  char *zname;
564
565  while (TRUE)
566    {
567      if (! fscmd_seq (zSlocalname, abseq))
568	return NULL;
569
570      sprintf (absx, "X.%.7sX%s", zSlocalname, abseq);
571
572      zname = zsfind_file (absx, zSlocalname, -1);
573      if (zname == NULL)
574	return NULL;
575
576      if (! fsysdep_file_exists (zname))
577	break;
578
579      ubuffree (zname);
580    }
581
582  return zname;
583}
584