1 /* 2 * $Id: util.c,v 5.2 90/06/23 22:20:06 jsp Rel $ 3 * 4 * Copyright (c) 1990 Jan-Simon Pendry 5 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 6 * Copyright (c) 1990 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Jan-Simon Pendry at Imperial College, London. 11 * 12 * %sccs.include.redist.c% 13 * 14 * @(#)util.c 5.1 (Berkeley) 06/29/90 15 */ 16 17 /* 18 * Utils 19 */ 20 21 #include "am.h" 22 #ifdef HAS_SYSLOG 23 #include <syslog.h> 24 #endif /* HAS_SYSLOG */ 25 #include <ctype.h> 26 #include <sys/stat.h> 27 28 #include <netdb.h> 29 30 31 INLINE 32 char *strnsave(str, len) 33 const char *str; 34 int len; 35 { 36 char *sp = (char *) xmalloc(len+1); 37 38 bcopy(str, sp, len); 39 sp[len] = 0; 40 41 return sp; 42 } 43 44 char *strdup(s) 45 const char *s; 46 { 47 return strnsave(s, strlen(s)); 48 } 49 50 /* 51 * Concatenate three strings and store in buffer pointed to 52 * by p, making p large enough to hold the strings 53 */ 54 char *str3cat(p, s1, s2, s3) 55 char *p; 56 char *s1; 57 char *s2; 58 char *s3; 59 { 60 int l1 = strlen(s1); 61 int l2 = strlen(s2); 62 int l3 = strlen(s3); 63 p = (char *) xrealloc(p, l1 + l2 + l3 + 1); 64 bcopy(s1, p, l1); 65 bcopy(s2, p + l1, l2); 66 bcopy(s3, p + l1 + l2, l3 + 1); 67 return p; 68 } 69 70 char *strealloc(p, s) 71 char *p; 72 char *s; 73 { 74 int len = strlen(s) + 1; 75 76 p = (char *) xrealloc((voidp) p, len); 77 78 strcpy(p, s); 79 #ifdef DEBUG_MEM 80 malloc_verify(); 81 #endif /* DEBUG_MEM */ 82 return p; 83 } 84 85 voidp xrealloc(ptr, len) 86 voidp ptr; 87 int len; 88 { 89 #if defined(DEBUG) && defined(DEBUG_MEM) 90 Debug(D_MEM) plog(XLOG_DEBUG, "Reallocated size %d; block %#x", len, ptr); 91 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */ 92 93 if (ptr) 94 ptr = (voidp) realloc(ptr, (unsigned) len); 95 else 96 ptr = (voidp) xmalloc((unsigned) len); 97 98 if (!ptr) { 99 plog(XLOG_FATAL, "Out of memory in realloc"); 100 going_down(1); 101 abort(); 102 } 103 return ptr; 104 } 105 106 char **strsplit(s, qc) 107 char *s; 108 int qc; 109 { 110 char **ivec; 111 int ic = 0; 112 int done = 0; 113 114 ivec = (char **) xmalloc((ic+1)*sizeof(char *)); 115 116 while (!done) { 117 char *v; 118 /* 119 * skip white space 120 */ 121 while (*s && isascii(*s) && isspace(*s)) 122 s++; 123 124 /* 125 * End of string? 126 */ 127 if (!*s) 128 break; 129 130 /* 131 * remember start of string 132 */ 133 v = s; 134 135 /* 136 * skip to white space 137 */ 138 while (*s && (!isascii(*s) || !isspace(*s))) { 139 if (*s++ == qc) { 140 /* 141 * Skip past string. 142 */ 143 s++; 144 while (*s && *s != qc) 145 s++; 146 if (*s == qc) 147 s++; 148 } 149 } 150 151 if (!*s) 152 done = 1; 153 *s++ = '\0'; 154 155 /* 156 * save string in new ivec slot 157 */ 158 ivec[ic++] = v; 159 ivec = (char **) xrealloc(ivec, (ic+1)*sizeof(char *)); 160 #ifdef DEBUG 161 Debug(D_STR) 162 plog(XLOG_DEBUG, "strsplit saved \"%s\"", v); 163 #endif /* DEBUG */ 164 } 165 166 #ifdef DEBUG 167 Debug(D_STR) 168 plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic); 169 #endif /* DEBUG */ 170 171 ivec[ic] = 0; 172 173 return ivec; 174 } 175 176 /* 177 * Strip off the trailing part of a domain 178 * to produce a short-form domain relative 179 * to the local host domain. 180 * Note that this has no effect if the domain 181 * names do not have the same number of 182 * components. If that restriction proves 183 * to be a problem then the loop needs recoding 184 * to skip from right to left and do partial 185 * matches along the way -- ie more expensive. 186 */ 187 static void domain_strip P((char *otherdom, char *localdom)); 188 static void domain_strip(otherdom, localdom) 189 char *otherdom, *localdom; 190 { 191 char *p1 = otherdom-1; 192 char *p2 = localdom-1; 193 194 do { 195 if (p1 = strchr(p1+1, '.')) 196 if (p2 = strchr(p2+1, '.')) 197 if (strcmp(p1+1, p2+1) == 0) { 198 *p1 = '\0'; 199 break; 200 } 201 } while (p1 && p2); 202 } 203 204 /* 205 * Normalize a host name 206 */ 207 void host_normalize P((char **chp)); 208 void host_normalize(chp) 209 char **chp; 210 { 211 /* 212 * Normalize hosts is used to resolve host name aliases 213 * and replace them with the standard-form name. 214 * Invoked with "-n" command line option. 215 */ 216 if (normalize_hosts) { 217 struct hostent *hp; 218 clock_valid = 0; 219 hp = gethostbyname(*chp); 220 if (hp && hp->h_addrtype == AF_INET) { 221 #ifdef DEBUG 222 dlog("Hostname %s normalized to %s", *chp, hp->h_name); 223 #endif /* DEBUG */ 224 *chp = strealloc(*chp, hp->h_name); 225 } 226 } 227 domain_strip(*chp, hostd); 228 } 229 230 /* 231 * Keys are not allowed to contain " ' ! or ; to avoid 232 * problems with macro expansions. 233 */ 234 static char invalid_keys[] = "\"'!;@ \t\n"; 235 int valid_key P((char *key)); 236 int valid_key(key) 237 char *key; 238 { 239 while (*key) 240 if (strchr(invalid_keys, *key++)) 241 return FALSE; 242 return TRUE; 243 } 244 245 void going_down P((int rc)); 246 void going_down(rc) 247 int rc; 248 { 249 if (foreground) { 250 if (amd_state != Start) { 251 if (amd_state != Done) 252 return; 253 unregister_amq(); 254 } 255 } 256 if (foreground) { 257 plog(XLOG_INFO, "Finishing with status %d", rc); 258 } else { 259 #ifdef DEBUG 260 dlog("background process exiting with status %d", rc); 261 #endif /* DEBUG */ 262 } 263 264 exit(rc); 265 } 266 267 #ifdef DEBUG_MEM 268 static int mem_bytes; 269 static int orig_mem_bytes; 270 static void checkup_mem(P_void) 271 { 272 extern struct mallinfo __mallinfo; 273 if (mem_bytes != __mallinfo.uordbytes) { 274 if (orig_mem_bytes == 0) 275 mem_bytes = orig_mem_bytes = __mallinfo.uordbytes; 276 else { 277 fprintf(logfp, "%s[%d]: ", progname, mypid); 278 if (mem_bytes < __mallinfo.uordbytes) { 279 fprintf(logfp, "ALLOC: %d bytes", 280 __mallinfo.uordbytes - mem_bytes); 281 } else { 282 fprintf(logfp, "FREE: %d bytes", 283 mem_bytes - __mallinfo.uordbytes); 284 } 285 mem_bytes = __mallinfo.uordbytes; 286 fprintf(logfp, ", making %d missing\n", 287 mem_bytes - orig_mem_bytes); 288 } 289 } 290 malloc_verify(); 291 } 292 #endif /* DEBUG_MEM */ 293 294 /* 295 * Take a log format string and expand occurences of %m 296 * with the current error code take from errno. 297 */ 298 INLINE 299 static void expand_error(f, e) 300 char *f; 301 char *e; 302 { 303 extern int sys_nerr; 304 extern char *sys_errlist[]; 305 char *p; 306 int error = errno; 307 308 for (p = f; *e = *p; e++, p++) { 309 if (p[0] == '%' && p[1] == 'm') { 310 char *errstr; 311 if (error < 0 || error >= sys_nerr) 312 errstr = 0; 313 else 314 errstr = sys_errlist[error]; 315 if (errstr) 316 strcpy(e, errstr); 317 else 318 sprintf(e, "Error %d", error); 319 e += strlen(e) - 1; 320 p++; 321 } 322 } 323 } 324 325 /* 326 * Output the time of day and hostname to the logfile 327 */ 328 static void show_time_host_and_name(lvl) 329 int lvl; 330 { 331 static time_t last_t = 0; 332 static char *last_ctime = 0; 333 time_t t = clocktime(); 334 char *sev; 335 extern char *ctime(); 336 337 #if defined(DEBUG) && defined(PARANOID) 338 extern char **gargv; 339 #endif /* defined(DEBUG) && defined(PARANOID) */ 340 341 if (t != last_t) { 342 last_ctime = ctime(&t); 343 last_t = t; 344 } 345 346 switch (lvl) { 347 case XLOG_FATAL: sev = "fatal:"; break; 348 case XLOG_ERROR: sev = "error:"; break; 349 case XLOG_USER: sev = "user: "; break; 350 case XLOG_WARNING: sev = "warn: "; break; 351 case XLOG_INFO: sev = "info: "; break; 352 case XLOG_DEBUG: sev = "debug:"; break; 353 case XLOG_MAP: sev = "map: "; break; 354 case XLOG_STATS: sev = "stats:"; break; 355 default: sev = "hmm: "; break; 356 } 357 fprintf(logfp, "%15.15s %s %s[%d]/%s ", 358 last_ctime+4, hostname, 359 #if defined(DEBUG) && defined(PARANOID) 360 gargv[0], 361 #else 362 progname, 363 #endif /* defined(DEBUG) && defined(PARANOID) */ 364 mypid, 365 sev); 366 } 367 368 #ifdef DEBUG 369 /*VARARGS1*/ 370 void dplog(fmt, j,s,_,p,e,n,d,r,y) 371 char *fmt; 372 char *j, *s, *_, *p, *e, *n, *d, *r, *y; 373 { 374 plog(XLOG_DEBUG, fmt, j,s,_,p,e,n,d,r,y); 375 } 376 377 #endif /* DEBUG */ 378 /*VARARGS1*/ 379 void plog(lvl, fmt, j,s,_,p,e,n,d,r,y) 380 int lvl; 381 char *fmt; 382 char *j, *s, *_, *p, *e, *n, *d, *r, *y; 383 { 384 char msg[1024]; 385 char efmt[1024]; 386 char *ptr = msg; 387 388 if (!(xlog_level & lvl)) 389 return; 390 391 #ifdef DEBUG_MEM 392 checkup_mem(); 393 #endif /* DEBUG_MEM */ 394 395 expand_error(fmt, efmt); 396 sprintf(ptr, efmt, j,s,_,p,e,n,d,r,y); 397 ptr += strlen(ptr); 398 if (ptr[-1] == '\n') 399 *--ptr = '\0'; 400 #ifdef HAS_SYSLOG 401 if (syslogging) { 402 switch(lvl) { /* from mike <mcooper@usc.edu> */ 403 case XLOG_FATAL: lvl = LOG_CRIT; break; 404 case XLOG_ERROR: lvl = LOG_ERR; break; 405 case XLOG_USER: lvl = LOG_WARNING; break; 406 case XLOG_WARNING: lvl = LOG_WARNING; break; 407 case XLOG_INFO: lvl = LOG_INFO; break; 408 case XLOG_DEBUG: lvl = LOG_DEBUG; break; 409 case XLOG_MAP: lvl = LOG_DEBUG; break; 410 case XLOG_STATS: lvl = LOG_INFO; break; 411 default: lvl = LOG_ERR; break; 412 } 413 syslog(lvl, "%s", msg); 414 return; 415 } 416 #endif /* HAS_SYSLOG */ 417 418 *ptr++ = '\n'; 419 *ptr = '\0'; 420 421 /* 422 * Mimic syslog header 423 */ 424 show_time_host_and_name(lvl); 425 fwrite(msg, ptr - msg, 1, logfp); 426 fflush(logfp); 427 } 428 429 int bind_resv_port P((int so, u_short *pp)); 430 int bind_resv_port(so, pp) 431 int so; 432 u_short *pp; 433 { 434 struct sockaddr_in sin; 435 int rc; 436 unsigned short port; 437 438 bzero((voidp) &sin, sizeof(sin)); 439 sin.sin_family = AF_INET; 440 441 port = IPPORT_RESERVED; 442 443 do { 444 --port; 445 sin.sin_port = htons(port); 446 rc = bind(so, (struct sockaddr *) &sin, sizeof(sin)); 447 } while (rc < 0 && port > IPPORT_RESERVED/2); 448 449 if (pp && rc == 0) 450 *pp = port; 451 return rc; 452 } 453 454 void forcibly_timeout_mp P((am_node *mp)); 455 void forcibly_timeout_mp(mp) 456 am_node *mp; 457 { 458 mntfs *mf = mp->am_mnt; 459 /* 460 * Arrange to timeout this node 461 */ 462 if (mf && ((mp->am_flags & AMF_ROOT) || 463 (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)))) { 464 if (!(mf->mf_flags & MFF_UNMOUNTING)) 465 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path); 466 } else { 467 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path); 468 mp->am_flags &= ~AMF_NOTIMEOUT; 469 mp->am_ttl = clocktime(); 470 reschedule_timeout_mp(); 471 } 472 } 473 474 void am_mounted P((am_node *mp)); 475 void am_mounted(mp) 476 am_node *mp; 477 { 478 mntfs *mf = mp->am_mnt; 479 int quoted; 480 mf->mf_flags |= MFF_MOUNTED; 481 mf->mf_error = 0; 482 483 /* 484 * Patch up path for direct mounts 485 */ 486 if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &dfs_ops) 487 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); 488 489 /* 490 * Check whether this mount should be cached permanently 491 */ 492 if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) { 493 mp->am_flags |= AMF_NOTIMEOUT; 494 } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') { 495 mp->am_flags |= AMF_NOTIMEOUT; 496 } else { 497 struct mntent mnt; 498 mnt.mnt_opts = mf->mf_fo->opt_opts; 499 if (hasmntopt(&mnt, "nounmount")) 500 mp->am_flags |= AMF_NOTIMEOUT; 501 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) 502 mp->am_timeo = am_timeo; 503 /* if ((mf->mf_server->fs_pinger = hasmntval(&mnt, "ping")) == 0) 504 mf->mf_server->fs_pinger = AM_PINGER; 505 */ 506 } 507 508 /* 509 * Do mounted callback 510 */ 511 if (mf->mf_ops->mounted) 512 (*mf->mf_ops->mounted)(mf); 513 514 /* 515 * If this node is a symlink then 516 * compute the length of the returned string. 517 */ 518 if (mf->mf_fattr.type == NFLNK) 519 mf->mf_fattr.size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount); 520 521 /* 522 * Record mount time 523 */ 524 mf->mf_fattr.mtime.seconds = mp->am_stats.s_mtime = clocktime(); 525 new_ttl(mp); 526 /* 527 * Update mtime of parent node 528 */ 529 if (mp->am_parent && mp->am_parent->am_mnt) 530 mp->am_parent->am_mnt->mf_fattr.mtime.seconds = mp->am_stats.s_mtime; 531 532 /* 533 * Log message 534 */ 535 quoted = strchr(mf->mf_info, ' ') != 0; 536 plog(XLOG_INFO, "%s%s%s mounted fstype %s on %s", 537 quoted ? "\"" : "", 538 mf->mf_info, 539 quoted ? "\"" : "", 540 mf->mf_ops->fs_type, mf->mf_mount); 541 542 /* 543 * Update stats 544 */ 545 amd_stats.d_mok++; 546 } 547 548 int mount_node P((am_node *mp)); 549 int mount_node(mp) 550 am_node *mp; 551 { 552 mntfs *mf = mp->am_mnt; 553 int error; 554 555 mf->mf_flags |= MFF_MOUNTING; 556 error = (*mf->mf_ops->mount_fs)(mp); 557 mf = mp->am_mnt; 558 mf->mf_flags &= ~MFF_MOUNTING; 559 if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) { 560 /* ...but see ifs_mount */ 561 am_mounted(mp); 562 } 563 564 return error; 565 } 566 567 void am_unmounted P((am_node *mp)); 568 void am_unmounted(mp) 569 am_node *mp; 570 { 571 mntfs *mf = mp->am_mnt; 572 573 if (!foreground) /* firewall - should never happen */ 574 return; 575 576 #ifdef DEBUG 577 /*dlog("in am_unmounted(), foreground = %d", foreground);*/ 578 #endif /* DEBUG */ 579 580 /* 581 * Do unmounted callback 582 */ 583 if (mf->mf_ops->umounted) 584 (*mf->mf_ops->umounted)(mp); 585 586 /* 587 * Update mtime of parent node 588 */ 589 if (mp->am_parent && mp->am_parent->am_mnt) 590 mp->am_parent->am_mnt->mf_fattr.mtime.seconds = clocktime(); 591 592 free_map(mp); 593 } 594 595 596 /* 597 * Fork the automounter 598 * 599 * TODO: Need a better strategy for handling errors 600 */ 601 static int dofork(P_void); 602 INLINE 603 static int dofork() 604 { 605 int pid; 606 top: 607 pid = fork(); 608 609 if (pid < 0) { 610 sleep(1); 611 goto top; 612 } 613 614 if (pid == 0) { 615 mypid = getpid(); 616 foreground = 0; 617 } 618 619 return pid; 620 } 621 622 int background(P_void); 623 int background() 624 { 625 int pid = dofork(); 626 if (pid == 0) { 627 #ifdef DEBUG 628 dlog("backgrounded"); 629 #endif /* DEBUG */ 630 foreground = 0; 631 } 632 633 return pid; 634 } 635 636 int mkdirs P((char *path, int mode)); 637 int mkdirs(path, mode) 638 char *path; 639 int mode; 640 { 641 /* 642 * take a copy in case path is in readonly store 643 */ 644 char *p2 = strdup(path); 645 char *sp = p2; 646 struct stat stb; 647 int error_so_far = 0; 648 649 /* 650 * Skip through the string make the directories. 651 * Mostly ignore errors - the result is tested at the end. 652 * 653 * This assumes we are root so that we can do mkdir in a 654 * mode 555 directory... 655 */ 656 while (sp = strchr(sp+1, '/')) { 657 *sp = '\0'; 658 if (mkdir(p2, mode) < 0) { 659 error_so_far = errno; 660 } else { 661 #ifdef DEBUG 662 dlog("mkdir(%s)", p2); 663 #endif /* DEBUG */ 664 } 665 *sp = '/'; 666 } 667 668 if (mkdir(p2, mode) < 0) { 669 error_so_far = errno; 670 } else { 671 #ifdef DEBUG 672 dlog("mkdir(%s)", p2); 673 #endif /* DEBUG */ 674 } 675 676 #ifdef SUNOS4_WORKAROUND 677 /* 678 * Do a sync - if we do rmdirs() immediately 679 * and then the system crashes it leaves 680 * the filesystem in a state that fsck -p 681 * can't fix. (Observed more than once on 682 * SunOS 4 ...) 683 * 684 * The problem was caused by a bug somewhere 685 * in the UFS code which has since been fixed 686 * (at least at Berkeley). 687 * 688 * Attempted workaround - XXX. 689 */ 690 sync(); 691 #endif /* SUNOS4_WORKAROUND */ 692 693 free(p2); 694 695 return stat(path, &stb) == 0 && 696 (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far; 697 } 698 699 void rmdirs P((char *dir)); 700 void rmdirs(dir) 701 char *dir; 702 { 703 char *xdp = strdup(dir); 704 char *dp; 705 706 do { 707 struct stat stb; 708 /* 709 * Try to find out whether this was 710 * created by amd. Do this by checking 711 * for owner write permission. 712 */ 713 if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) { 714 if (rmdir(xdp) < 0) { 715 if (errno != ENOTEMPTY && errno != EBUSY && errno != EEXIST) 716 plog(XLOG_ERROR, "rmdir(%s): %m", xdp); 717 break; 718 } else { 719 #ifdef DEBUG 720 dlog("rmdir(%s)", xdp); 721 #endif /* DEBUG */ 722 } 723 } else { 724 break; 725 } 726 dp = strrchr(xdp, '/'); 727 if (dp) 728 *dp = '\0'; 729 } while (dp && dp > xdp); 730 free(xdp); 731 } 732 733 /* 734 * Because the internal clock is only used for 735 * timing out mounts, it does not have to be 736 * particularly accurate, so long as it does not run 737 * ahead of the real time. So, to reduce the system 738 * call overhead, repeated calls to gettimeofday() 739 * are replaced by calls to the macro clocktime(). 740 * If the global time (clock_valid) is zero then 741 * update_clocktime() is called to obtain the real time. 742 * Before any system calls that are likely to block for a 743 * significant time, the clock_valid value is set 744 * so that the clock is recomputed next time it is 745 * needed. 746 */ 747 748 time_t clock_valid = 0; 749 #ifndef clocktime 750 time_t clocktime(P_void) 751 { 752 return time(&clock_valid); 753 } 754 #endif /* clocktime */ 755 756 voidp xmalloc(len) 757 int len; 758 { 759 voidp p; 760 int retries = 600; 761 762 do { 763 p = (voidp) malloc((unsigned) len); 764 if (p) { 765 #if defined(DEBUG) && defined(DEBUG_MEM) 766 Debug(D_MEM) plog(XLOG_DEBUG, "Allocated size %d; block %#x", len, p); 767 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */ 768 return p; 769 } 770 if (retries > 0) { 771 plog(XLOG_ERROR, "Retrying memory allocation"); 772 sleep(1); 773 } 774 } while (--retries); 775 776 plog(XLOG_FATAL, "Out of memory"); 777 going_down(1); 778 779 abort(); 780 781 return 0; 782 } 783 784 #if defined(DEBUG) && defined(DEBUG_MEM) 785 xfree(f, l, p) 786 char *f; 787 int l; 788 voidp p; 789 { 790 Debug(D_MEM) plog(XLOG_DEBUG, "Free in %s:%d: block %#x", f, l, p); 791 #undef free 792 free(p); 793 } 794 #endif /* defined(DEBUG) && defined(DEBUG_MEM) */ 795