1 /* 2 * Copyright (c) 1990 Jan-Simon Pendry 3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 4 * Copyright (c) 1990 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jan-Simon Pendry at Imperial College, London. 9 * 10 * %sccs.include.redist.c% 11 * 12 * @(#)util.c 5.5 (Berkeley) 05/31/92 13 * 14 * $Id: util.c,v 5.2.2.2 1992/03/07 17:52:06 jsp Exp $ 15 * 16 */ 17 18 /* 19 * Utils 20 */ 21 22 #include "am.h" 23 #include <ctype.h> 24 #include <sys/stat.h> 25 #include <netdb.h> 26 27 28 char *strnsave(str, len) 29 Const char *str; 30 int len; 31 { 32 char *sp = (char *) xmalloc(len+1); 33 34 bcopy(str, sp, len); 35 sp[len] = 0; 36 37 return sp; 38 } 39 40 char *strdup(s) 41 Const char *s; 42 { 43 return strnsave(s, strlen(s)); 44 } 45 46 /* 47 * Concatenate three strings and store in buffer pointed to 48 * by p, making p large enough to hold the strings 49 */ 50 char *str3cat(p, s1, s2, s3) 51 char *p; 52 char *s1; 53 char *s2; 54 char *s3; 55 { 56 int l1 = strlen(s1); 57 int l2 = strlen(s2); 58 int l3 = strlen(s3); 59 p = (char *) xrealloc(p, l1 + l2 + l3 + 1); 60 bcopy(s1, p, l1); 61 bcopy(s2, p + l1, l2); 62 bcopy(s3, p + l1 + l2, l3 + 1); 63 return p; 64 } 65 66 char *strealloc(p, s) 67 char *p; 68 char *s; 69 { 70 int len = strlen(s) + 1; 71 72 p = (char *) xrealloc((voidp) p, len); 73 74 strcpy(p, s); 75 #ifdef DEBUG_MEM 76 malloc_verify(); 77 #endif /* DEBUG_MEM */ 78 return p; 79 } 80 81 char **strsplit P((char *s, int ch, int qc)); 82 char **strsplit(s, ch, qc) 83 char *s; 84 int ch; 85 int qc; 86 { 87 char **ivec; 88 int ic = 0; 89 int done = 0; 90 91 ivec = (char **) xmalloc((ic+1)*sizeof(char *)); 92 93 while (!done) { 94 char *v; 95 /* 96 * skip to split char 97 */ 98 while (*s && (ch == ' ' ? (isascii(*s) && isspace(*s)) : *s == ch)) 99 *s++ = '\0'; 100 101 /* 102 * End of string? 103 */ 104 if (!*s) 105 break; 106 107 /* 108 * remember start of string 109 */ 110 v = s; 111 112 /* 113 * skip to split char 114 */ 115 while (*s && !(ch == ' ' ? (isascii(*s) && isspace(*s)) : *s == ch)) { 116 if (*s++ == qc) { 117 /* 118 * Skip past string. 119 */ 120 s++; 121 while (*s && *s != qc) 122 s++; 123 if (*s == qc) 124 s++; 125 } 126 } 127 128 if (!*s) 129 done = 1; 130 *s++ = '\0'; 131 132 /* 133 * save string in new ivec slot 134 */ 135 ivec[ic++] = v; 136 ivec = (char **) xrealloc((voidp) ivec, (ic+1)*sizeof(char *)); 137 #ifdef DEBUG 138 Debug(D_STR) 139 plog(XLOG_DEBUG, "strsplit saved \"%s\"", v); 140 #endif /* DEBUG */ 141 } 142 143 #ifdef DEBUG 144 Debug(D_STR) 145 plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic); 146 #endif /* DEBUG */ 147 148 ivec[ic] = 0; 149 150 return ivec; 151 } 152 153 /* 154 * Strip off the trailing part of a domain 155 * to produce a short-form domain relative 156 * to the local host domain. 157 * Note that this has no effect if the domain 158 * names do not have the same number of 159 * components. If that restriction proves 160 * to be a problem then the loop needs recoding 161 * to skip from right to left and do partial 162 * matches along the way -- ie more expensive. 163 */ 164 static void domain_strip P((char *otherdom, char *localdom)); 165 static void domain_strip(otherdom, localdom) 166 char *otherdom, *localdom; 167 { 168 #ifdef PARTIAL_DOMAINS 169 char *p1 = otherdom-1; 170 char *p2 = localdom-1; 171 172 do { 173 if (p1 = strchr(p1+1, '.')) 174 if (p2 = strchr(p2+1, '.')) 175 if (strcmp(p1+1, p2+1) == 0) { 176 *p1 = '\0'; 177 break; 178 } 179 } while (p1 && p2); 180 #else 181 char *p1, *p2; 182 183 if ((p1 = strchr(otherdom, '.')) && 184 (p2 = strchr(localdom, '.')) && 185 (strcmp(p1+1, p2+1) == 0)) 186 *p1 = '\0'; 187 #endif /* PARTIAL_DOMAINS */ 188 } 189 190 /* 191 * Normalize a host name 192 */ 193 void host_normalize P((char **chp)); 194 void host_normalize(chp) 195 char **chp; 196 { 197 /* 198 * Normalize hosts is used to resolve host name aliases 199 * and replace them with the standard-form name. 200 * Invoked with "-n" command line option. 201 */ 202 if (normalize_hosts) { 203 struct hostent *hp; 204 clock_valid = 0; 205 hp = gethostbyname(*chp); 206 if (hp && hp->h_addrtype == AF_INET) { 207 #ifdef DEBUG 208 dlog("Hostname %s normalized to %s", *chp, hp->h_name); 209 #endif /* DEBUG */ 210 *chp = strealloc(*chp, hp->h_name); 211 } 212 } 213 domain_strip(*chp, hostd); 214 } 215 216 /* 217 * Make a dotted quad from a 32bit IP address 218 * addr is in network byte order. 219 * sizeof(buf) needs to be at least 16. 220 */ 221 char *inet_dquad P((char *buf, unsigned long addr)); 222 char *inet_dquad(buf, addr) 223 char *buf; 224 unsigned long addr; 225 { 226 addr = ntohl(addr); 227 sprintf(buf, "%d.%d.%d.%d", 228 ((addr >> 24) & 0xff), 229 ((addr >> 16) & 0xff), 230 ((addr >> 8) & 0xff), 231 ((addr >> 0) & 0xff)); 232 return buf; 233 } 234 235 /* 236 * Keys are not allowed to contain " ' ! or ; to avoid 237 * problems with macro expansions. 238 */ 239 static char invalid_keys[] = "\"'!;@ \t\n"; 240 int valid_key P((char *key)); 241 int valid_key(key) 242 char *key; 243 { 244 while (*key) 245 if (strchr(invalid_keys, *key++)) 246 return FALSE; 247 return TRUE; 248 } 249 250 void going_down P((int rc)); 251 void going_down(rc) 252 int rc; 253 { 254 if (foreground) { 255 if (amd_state != Start) { 256 if (amd_state != Done) 257 return; 258 unregister_amq(); 259 } 260 } 261 if (foreground) { 262 plog(XLOG_INFO, "Finishing with status %d", rc); 263 } else { 264 #ifdef DEBUG 265 dlog("background process exiting with status %d", rc); 266 #endif /* DEBUG */ 267 } 268 269 exit(rc); 270 } 271 272 273 int bind_resv_port P((int so, u_short *pp)); 274 int bind_resv_port(so, pp) 275 int so; 276 u_short *pp; 277 { 278 struct sockaddr_in sin; 279 int rc; 280 unsigned short port; 281 282 bzero((voidp) &sin, sizeof(sin)); 283 sin.sin_family = AF_INET; 284 285 port = IPPORT_RESERVED; 286 287 do { 288 --port; 289 sin.sin_port = htons(port); 290 rc = bind(so, (struct sockaddr *) &sin, sizeof(sin)); 291 } while (rc < 0 && port > IPPORT_RESERVED/2); 292 293 if (pp && rc == 0) 294 *pp = port; 295 return rc; 296 } 297 298 void forcibly_timeout_mp P((am_node *mp)); 299 void forcibly_timeout_mp(mp) 300 am_node *mp; 301 { 302 mntfs *mf = mp->am_mnt; 303 /* 304 * Arrange to timeout this node 305 */ 306 if (mf && ((mp->am_flags & AMF_ROOT) || 307 (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)))) { 308 if (!(mf->mf_flags & MFF_UNMOUNTING)) 309 plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path); 310 } else { 311 plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path); 312 mp->am_flags &= ~AMF_NOTIMEOUT; 313 mp->am_ttl = clocktime(); 314 reschedule_timeout_mp(); 315 } 316 } 317 318 void mf_mounted P((mntfs *mf)); 319 void mf_mounted(mf) 320 mntfs *mf; 321 { 322 int quoted; 323 int wasmounted = mf->mf_flags & MFF_MOUNTED; 324 325 if (!wasmounted) { 326 /* 327 * If this is a freshly mounted 328 * filesystem then update the 329 * mntfs structure... 330 */ 331 mf->mf_flags |= MFF_MOUNTED; 332 mf->mf_error = 0; 333 334 /* 335 * Do mounted callback 336 */ 337 if (mf->mf_ops->mounted) 338 (*mf->mf_ops->mounted)(mf); 339 340 mf->mf_fo = 0; 341 } 342 343 /* 344 * Log message 345 */ 346 quoted = strchr(mf->mf_info, ' ') != 0; 347 plog(XLOG_INFO, "%s%s%s %s fstype %s on %s", 348 quoted ? "\"" : "", 349 mf->mf_info, 350 quoted ? "\"" : "", 351 wasmounted ? "referenced" : "mounted", 352 mf->mf_ops->fs_type, mf->mf_mount); 353 } 354 355 void am_mounted P((am_node *mp)); 356 void am_mounted(mp) 357 am_node *mp; 358 { 359 mntfs *mf = mp->am_mnt; 360 361 mf_mounted(mf); 362 363 /* 364 * Patch up path for direct mounts 365 */ 366 if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &dfs_ops) 367 mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); 368 369 /* 370 * Check whether this mount should be cached permanently 371 */ 372 if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) { 373 mp->am_flags |= AMF_NOTIMEOUT; 374 } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') { 375 mp->am_flags |= AMF_NOTIMEOUT; 376 } else { 377 struct mntent mnt; 378 if (mf->mf_mopts) { 379 mnt.mnt_opts = mf->mf_mopts; 380 if (hasmntopt(&mnt, "nounmount")) 381 mp->am_flags |= AMF_NOTIMEOUT; 382 if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) 383 mp->am_timeo = am_timeo; 384 } 385 } 386 387 /* 388 * If this node is a symlink then 389 * compute the length of the returned string. 390 */ 391 if (mp->am_fattr.type == NFLNK) 392 mp->am_fattr.size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount); 393 394 /* 395 * Record mount time 396 */ 397 mp->am_fattr.mtime.seconds = mp->am_stats.s_mtime = clocktime(); 398 new_ttl(mp); 399 /* 400 * Update mtime of parent node 401 */ 402 if (mp->am_parent && mp->am_parent->am_mnt) 403 mp->am_parent->am_fattr.mtime.seconds = mp->am_stats.s_mtime; 404 405 406 /* 407 * Update stats 408 */ 409 amd_stats.d_mok++; 410 } 411 412 int mount_node P((am_node *mp)); 413 int mount_node(mp) 414 am_node *mp; 415 { 416 mntfs *mf = mp->am_mnt; 417 int error; 418 419 mf->mf_flags |= MFF_MOUNTING; 420 error = (*mf->mf_ops->mount_fs)(mp); 421 mf = mp->am_mnt; 422 if (error >= 0) 423 mf->mf_flags &= ~MFF_MOUNTING; 424 if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) { 425 /* ...but see ifs_mount */ 426 am_mounted(mp); 427 } 428 429 return error; 430 } 431 432 void am_unmounted P((am_node *mp)); 433 void am_unmounted(mp) 434 am_node *mp; 435 { 436 mntfs *mf = mp->am_mnt; 437 438 if (!foreground) /* firewall - should never happen */ 439 return; 440 441 #ifdef DEBUG 442 /*dlog("in am_unmounted(), foreground = %d", foreground);*/ 443 #endif /* DEBUG */ 444 445 /* 446 * Do unmounted callback 447 */ 448 if (mf->mf_ops->umounted) 449 (*mf->mf_ops->umounted)(mp); 450 451 /* 452 * Update mtime of parent node 453 */ 454 if (mp->am_parent && mp->am_parent->am_mnt) 455 mp->am_parent->am_fattr.mtime.seconds = clocktime(); 456 457 free_map(mp); 458 } 459 460 int auto_fmount P((am_node *mp)); 461 int auto_fmount(mp) 462 am_node *mp; 463 { 464 mntfs *mf = mp->am_mnt; 465 return (*mf->mf_ops->fmount_fs)(mf); 466 } 467 468 int auto_fumount P((am_node *mp)); 469 int auto_fumount(mp) 470 am_node *mp; 471 { 472 mntfs *mf = mp->am_mnt; 473 return (*mf->mf_ops->fumount_fs)(mf); 474 } 475 476 /* 477 * Fork the automounter 478 * 479 * TODO: Need a better strategy for handling errors 480 */ 481 static int dofork(P_void); 482 static int dofork() 483 { 484 int pid; 485 top: 486 pid = fork(); 487 488 if (pid < 0) { 489 sleep(1); 490 goto top; 491 } 492 493 if (pid == 0) { 494 mypid = getpid(); 495 foreground = 0; 496 } 497 498 return pid; 499 } 500 501 int background(P_void); 502 int background() 503 { 504 int pid = dofork(); 505 if (pid == 0) { 506 #ifdef DEBUG 507 dlog("backgrounded"); 508 #endif 509 foreground = 0; 510 } 511 512 return pid; 513 } 514 515 /* 516 * Make all the directories in the path. 517 */ 518 int mkdirs P((char *path, int mode)); 519 int mkdirs(path, mode) 520 char *path; 521 int mode; 522 { 523 /* 524 * take a copy in case path is in readonly store 525 */ 526 char *p2 = strdup(path); 527 char *sp = p2; 528 struct stat stb; 529 int error_so_far = 0; 530 531 /* 532 * Skip through the string make the directories. 533 * Mostly ignore errors - the result is tested at the end. 534 * 535 * This assumes we are root so that we can do mkdir in a 536 * mode 555 directory... 537 */ 538 while (sp = strchr(sp+1, '/')) { 539 *sp = '\0'; 540 if (mkdir(p2, mode) < 0) { 541 error_so_far = errno; 542 } else { 543 #ifdef DEBUG 544 dlog("mkdir(%s)", p2); 545 #endif 546 } 547 *sp = '/'; 548 } 549 550 if (mkdir(p2, mode) < 0) { 551 error_so_far = errno; 552 } else { 553 #ifdef DEBUG 554 dlog("mkdir(%s)", p2); 555 #endif 556 } 557 558 #ifdef SUNOS4_WORKAROUND 559 /* 560 * Do a sync - if we do rmdirs() immediately 561 * and then the system crashes it leaves 562 * the filesystem in a state that fsck -p 563 * can't fix. (Observed more than once on 564 * SunOS 4 ...) 565 * 566 * The problem was caused by a bug somewhere 567 * in the UFS code which has since been fixed 568 * (at least at Berkeley). 569 * 570 * Attempted workaround - XXX. 571 */ 572 sync(); 573 #endif /* SUNOS4_WORKAROUND */ 574 575 free(p2); 576 577 return stat(path, &stb) == 0 && 578 (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far; 579 } 580 581 /* 582 * Remove as many directories in the path as possible. 583 * Give up if the directory doesn't appear to have 584 * been created by Amd (not mode dr-x) or an rmdir 585 * fails for any reason. 586 */ 587 void rmdirs P((char *dir)); 588 void rmdirs(dir) 589 char *dir; 590 { 591 char *xdp = strdup(dir); 592 char *dp; 593 594 do { 595 struct stat stb; 596 /* 597 * Try to find out whether this was 598 * created by amd. Do this by checking 599 * for owner write permission. 600 */ 601 if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) { 602 if (rmdir(xdp) < 0) { 603 if (errno != ENOTEMPTY && 604 errno != EBUSY && 605 errno != EEXIST && 606 errno != EINVAL) 607 plog(XLOG_ERROR, "rmdir(%s): %m", xdp); 608 break; 609 } else { 610 #ifdef DEBUG 611 dlog("rmdir(%s)", xdp); 612 #endif 613 } 614 } else { 615 break; 616 } 617 dp = strrchr(xdp, '/'); 618 if (dp) 619 *dp = '\0'; 620 } while (dp && dp > xdp); 621 free(xdp); 622 } 623