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