1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * sm_statd.c consists of routines used for the intermediate 44 * statd implementation(3.2 rpc.statd); 45 * it creates an entry in "current" directory for each site that it monitors; 46 * after crash and recovery, it moves all entries in "current" 47 * to "backup" directory, and notifies the corresponding statd of its recovery. 48 */ 49 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <unistd.h> 53 #include <string.h> 54 #include <syslog.h> 55 #include <netdb.h> 56 #include <sys/types.h> 57 #include <sys/stat.h> 58 #include <sys/file.h> 59 #include <sys/param.h> 60 #include <arpa/inet.h> 61 #include <dirent.h> 62 #include <rpc/rpc.h> 63 #include <rpcsvc/sm_inter.h> 64 #include <rpcsvc/nsm_addr.h> 65 #include <errno.h> 66 #include <memory.h> 67 #include <signal.h> 68 #include <synch.h> 69 #include <thread.h> 70 #include <limits.h> 71 #include <strings.h> 72 #include "sm_statd.h" 73 74 75 int LOCAL_STATE; 76 77 sm_hash_t mon_table[MAX_HASHSIZE]; 78 static sm_hash_t record_table[MAX_HASHSIZE]; 79 static sm_hash_t recov_q; 80 81 static name_entry *find_name(name_entry **namepp, char *name); 82 static name_entry *insert_name(name_entry **namepp, char *name, 83 int need_alloc); 84 static void delete_name(name_entry **namepp, char *name); 85 static void remove_name(char *name, int op, int startup); 86 static int statd_call_statd(char *name); 87 static void pr_name(char *name, int flag); 88 static void *thr_statd_init(); 89 static void *sm_try(); 90 static void *thr_call_statd(void *); 91 static void remove_single_name(char *name, char *dir1, char *dir2); 92 static int move_file(char *fromdir, char *file, char *todir); 93 static int count_symlinks(char *dir, char *name, int *count); 94 static char *family2string(sa_family_t family); 95 96 /* 97 * called when statd first comes up; it searches /etc/sm to gather 98 * all entries to notify its own failure 99 */ 100 void 101 statd_init() 102 { 103 struct dirent *dirp, *entp; 104 DIR *dp; 105 FILE *fp, *fp_tmp; 106 int i, tmp_state; 107 char state_file[MAXPATHLEN+SM_MAXPATHLEN]; 108 109 if (debug) 110 (void) printf("enter statd_init\n"); 111 112 /* 113 * First try to open the file. If that fails, try to create it. 114 * If that fails, give up. 115 */ 116 if ((fp = fopen(STATE, "r+")) == (FILE *)NULL) 117 if ((fp = fopen(STATE, "w+")) == (FILE *)NULL) { 118 syslog(LOG_ERR, "can't open %s: %m", STATE); 119 exit(1); 120 } else 121 (void) chmod(STATE, 0644); 122 if ((fscanf(fp, "%d", &LOCAL_STATE)) == EOF) { 123 if (debug >= 2) 124 (void) printf("empty file\n"); 125 LOCAL_STATE = 0; 126 } 127 128 /* 129 * Scan alternate paths for largest "state" number 130 */ 131 for (i = 0; i < pathix; i++) { 132 (void) sprintf(state_file, "%s/statmon/state", path_name[i]); 133 if ((fp_tmp = fopen(state_file, "r+")) == (FILE *)NULL) { 134 if ((fp_tmp = fopen(state_file, "w+")) 135 == (FILE *)NULL) { 136 if (debug) 137 syslog(LOG_ERR, 138 "can't open %s: %m", 139 state_file); 140 continue; 141 } else 142 (void) chmod(state_file, 0644); 143 } 144 if ((fscanf(fp_tmp, "%d", &tmp_state)) == EOF) { 145 if (debug) 146 syslog(LOG_ERR, 147 "statd: %s: file empty\n", state_file); 148 (void) fclose(fp_tmp); 149 continue; 150 } 151 if (tmp_state > LOCAL_STATE) { 152 LOCAL_STATE = tmp_state; 153 if (debug) 154 (void) printf("Update LOCAL STATE: %d\n", 155 tmp_state); 156 } 157 (void) fclose(fp_tmp); 158 } 159 160 LOCAL_STATE = ((LOCAL_STATE%2) == 0) ? LOCAL_STATE+1 : LOCAL_STATE+2; 161 162 /* IF local state overflows, reset to value 1 */ 163 if (LOCAL_STATE < 0) { 164 LOCAL_STATE = 1; 165 } 166 167 /* Copy the LOCAL_STATE value back to all stat files */ 168 if (fseek(fp, 0, 0) == -1) { 169 syslog(LOG_ERR, "statd: fseek failed\n"); 170 exit(1); 171 } 172 173 (void) fprintf(fp, "%-10d", LOCAL_STATE); 174 (void) fflush(fp); 175 if (fsync(fileno(fp)) == -1) { 176 syslog(LOG_ERR, "statd: fsync failed\n"); 177 exit(1); 178 } 179 (void) fclose(fp); 180 181 for (i = 0; i < pathix; i++) { 182 (void) sprintf(state_file, "%s/statmon/state", path_name[i]); 183 if ((fp_tmp = fopen(state_file, "r+")) == (FILE *)NULL) { 184 if ((fp_tmp = fopen(state_file, "w+")) 185 == (FILE *)NULL) { 186 syslog(LOG_ERR, 187 "can't open %s: %m", state_file); 188 continue; 189 } else 190 (void) chmod(state_file, 0644); 191 } 192 (void) fprintf(fp_tmp, "%-10d", LOCAL_STATE); 193 (void) fflush(fp_tmp); 194 if (fsync(fileno(fp_tmp)) == -1) { 195 syslog(LOG_ERR, 196 "statd: %s: fsync failed\n", state_file); 197 (void) fclose(fp_tmp); 198 exit(1); 199 } 200 (void) fclose(fp_tmp); 201 } 202 203 if (debug) 204 (void) printf("local state = %d\n", LOCAL_STATE); 205 206 if ((mkdir(CURRENT, SM_DIRECTORY_MODE)) == -1) { 207 if (errno != EEXIST) { 208 syslog(LOG_ERR, "statd: mkdir current, error %m\n"); 209 exit(1); 210 } 211 } 212 if ((mkdir(BACKUP, SM_DIRECTORY_MODE)) == -1) { 213 if (errno != EEXIST) { 214 syslog(LOG_ERR, "statd: mkdir backup, error %m\n"); 215 exit(1); 216 } 217 } 218 219 /* get all entries in CURRENT into BACKUP */ 220 if ((dp = opendir(CURRENT)) == (DIR *)NULL) { 221 syslog(LOG_ERR, "statd: open current directory, error %m\n"); 222 exit(1); 223 } 224 225 entp = (struct dirent *)xmalloc(MAXDIRENT); 226 if (entp == NULL) { 227 exit(1); 228 } 229 230 while ((dirp = readdir_r(dp, entp)) != (struct dirent *)NULL) { 231 if (strcmp(dirp->d_name, ".") != 0 && 232 strcmp(dirp->d_name, "..") != 0) { 233 /* rename all entries from CURRENT to BACKUP */ 234 (void) move_file(CURRENT, dirp->d_name, BACKUP); 235 } 236 } 237 238 free(entp); 239 (void) closedir(dp); 240 241 /* Contact hosts' statd */ 242 if (thr_create(NULL, NULL, thr_statd_init, NULL, THR_DETACHED, 0)) { 243 syslog(LOG_ERR, 244 "statd: unable to create thread for thr_statd_init\n"); 245 exit(1); 246 } 247 } 248 249 /* 250 * Work thread which contacts hosts' statd. 251 */ 252 void * 253 thr_statd_init() 254 { 255 struct dirent *dirp, *entp; 256 DIR *dp; 257 int num_threads; 258 int num_join; 259 int i; 260 char *name; 261 char buf[MAXPATHLEN+SM_MAXPATHLEN]; 262 263 /* Go thru backup directory and contact hosts */ 264 if ((dp = opendir(BACKUP)) == (DIR *)NULL) { 265 syslog(LOG_ERR, "statd: open backup directory, error %m\n"); 266 exit(1); 267 } 268 269 entp = (struct dirent *)xmalloc(MAXDIRENT); 270 if (entp == NULL) { 271 exit(1); 272 } 273 274 /* 275 * Create "UNDETACHED" threads for each symlink and (unlinked) 276 * regular file in backup directory to initiate statd_call_statd. 277 * NOTE: These threads are the only undetached threads in this 278 * program and thus, the thread id is not needed to join the threads. 279 */ 280 num_threads = 0; 281 while ((dirp = readdir_r(dp, entp)) != (struct dirent *)NULL) { 282 /* 283 * If host file is not a symlink, don't bother to 284 * spawn a thread for it. If any link(s) refer to 285 * it, the host will be contacted using the link(s). 286 * If not, we'll deal with it during the legacy pass. 287 */ 288 (void) sprintf(buf, "%s/%s", BACKUP, dirp->d_name); 289 if (is_symlink(buf) == 0) { 290 continue; 291 } 292 293 /* 294 * If the num_threads has exceeded, wait until 295 * a certain amount of threads have finished. 296 * Currently, 10% of threads created should be joined. 297 */ 298 if (num_threads > MAX_THR) { 299 num_join = num_threads/PERCENT_MINJOIN; 300 for (i = 0; i < num_join; i++) 301 thr_join(0, 0, 0); 302 num_threads -= num_join; 303 } 304 305 /* 306 * If can't alloc name then print error msg and 307 * continue to next item on list. 308 */ 309 name = strdup(dirp->d_name); 310 if (name == (char *)NULL) { 311 syslog(LOG_ERR, 312 "statd: unable to allocate space for name %s\n", 313 dirp->d_name); 314 continue; 315 } 316 317 /* Create a thread to do a statd_call_statd for name */ 318 if (thr_create(NULL, NULL, thr_call_statd, 319 (void *) name, 0, 0)) { 320 syslog(LOG_ERR, 321 "statd: unable to create thr_call_statd() for name %s.\n", 322 dirp->d_name); 323 free(name); 324 continue; 325 } 326 num_threads++; 327 } 328 329 /* 330 * Join the other threads created above before processing the 331 * legacies. This allows all symlinks and the regular files 332 * to which they correspond to be processed and deleted. 333 */ 334 for (i = 0; i < num_threads; i++) { 335 thr_join(0, 0, 0); 336 } 337 338 /* Reuse the buffer for readdir_r use */ 339 (void) memset(entp, 0, MAXDIRENT); 340 341 /* 342 * The second pass checks for `legacies': regular files which 343 * never had symlinks pointing to them at all, just like in the 344 * good old (pre-1184192 fix) days. Once a machine has cleaned 345 * up its legacies they should only reoccur due to catastrophes 346 * (e.g., severed symlinks). 347 */ 348 rewinddir(dp); 349 num_threads = 0; 350 while ((dirp = readdir_r(dp, entp)) != (struct dirent *)NULL) { 351 if (strcmp(dirp->d_name, ".") == 0 || 352 strcmp(dirp->d_name, "..") == 0) { 353 continue; 354 } 355 356 (void) sprintf(buf, "%s/%s", BACKUP, dirp->d_name); 357 if (is_symlink(buf)) { 358 /* 359 * We probably couldn't reach this host and it's 360 * been put on the recovery queue for retry. 361 * Skip it and keep looking for regular files. 362 */ 363 continue; 364 } 365 366 if (debug) { 367 (void) printf("thr_statd_init: legacy %s\n", 368 dirp->d_name); 369 } 370 371 /* 372 * If the number of threads exceeds the maximum, wait 373 * for some fraction of them to finish before 374 * continuing. 375 */ 376 if (num_threads > MAX_THR) { 377 num_join = num_threads/PERCENT_MINJOIN; 378 for (i = 0; i < num_join; i++) 379 thr_join(0, 0, 0); 380 num_threads -= num_join; 381 } 382 383 /* 384 * If can't alloc name then print error msg and 385 * continue to next item on list. 386 */ 387 name = strdup(dirp->d_name); 388 if (name == (char *)NULL) { 389 syslog(LOG_ERR, 390 "statd: unable to allocate space for name %s\n", 391 dirp->d_name); 392 continue; 393 } 394 395 /* Create a thread to do a statd_call_statd for name */ 396 if (thr_create(NULL, NULL, thr_call_statd, 397 (void *) name, 0, 0)) { 398 syslog(LOG_ERR, 399 "statd: unable to create thr_call_statd() for name %s.\n", 400 dirp->d_name); 401 free(name); 402 continue; 403 } 404 num_threads++; 405 } 406 407 free(entp); 408 (void) closedir(dp); 409 410 /* 411 * Join the other threads created above before creating thread 412 * to process items in recovery table. 413 */ 414 for (i = 0; i < num_threads; i++) { 415 thr_join(0, 0, 0); 416 } 417 418 /* 419 * Need to only copy /var/statmon/sm.bak to alternate paths, since 420 * the only hosts in /var/statmon/sm should be the ones currently 421 * being monitored and already should be in alternate paths as part 422 * of insert_mon(). 423 */ 424 for (i = 0; i < pathix; i++) { 425 (void) sprintf(buf, "%s/statmon/sm.bak", path_name[i]); 426 if ((mkdir(buf, SM_DIRECTORY_MODE)) == -1) { 427 if (errno != EEXIST) 428 syslog(LOG_ERR, "statd: mkdir %s error %m\n", 429 buf); 430 else 431 copydir_from_to(BACKUP, buf); 432 } else 433 copydir_from_to(BACKUP, buf); 434 } 435 436 437 /* 438 * Reset the die and in_crash variable and signal other threads 439 * that have issued an sm_crash and are waiting. 440 */ 441 mutex_lock(&crash_lock); 442 die = 0; 443 in_crash = 0; 444 mutex_unlock(&crash_lock); 445 cond_broadcast(&crash_finish); 446 447 if (debug) 448 (void) printf("Creating thread for sm_try\n"); 449 450 /* Continue to notify statd on hosts that were unreachable. */ 451 if (thr_create(NULL, NULL, sm_try, NULL, THR_DETACHED, 0)) 452 syslog(LOG_ERR, 453 "statd: unable to create thread for sm_try().\n"); 454 thr_exit((void *) 0); 455 #ifdef lint 456 return (0); 457 #endif 458 } 459 460 /* 461 * Work thread to make call to statd_call_statd. 462 */ 463 void * 464 thr_call_statd(void *namep) 465 { 466 char *name = (char *)namep; 467 468 /* 469 * If statd of name is unreachable, add name to recovery table 470 * otherwise if statd_call_statd was successful, remove from backup. 471 */ 472 if (statd_call_statd(name) != 0) { 473 int n; 474 char *tail; 475 char path[MAXPATHLEN]; 476 /* 477 * since we are constructing this pathname below we add 478 * another space for the terminating NULL so we don't 479 * overflow our buffer when we do the readlink 480 */ 481 char rname[MAXNAMELEN + 1]; 482 483 if (debug) { 484 (void) printf( 485 "statd call failed, inserting %s in recov_q\n", name); 486 } 487 mutex_lock(&recov_q.lock); 488 (void) insert_name(&recov_q.sm_recovhdp, name, 0); 489 mutex_unlock(&recov_q.lock); 490 491 /* 492 * If we queued a symlink name in the recovery queue, 493 * we now clean up the regular file to which it referred. 494 * This may leave a severed symlink if multiple links 495 * referred to one regular file; this is unaesthetic but 496 * it works. The big benefit is that it prevents us 497 * from recovering the same host twice (as symlink and 498 * as regular file) needlessly, usually on separate reboots. 499 */ 500 (void) strcpy(path, BACKUP); 501 (void) strcat(path, "/"); 502 (void) strcat(path, name); 503 if (is_symlink(path)) { 504 n = readlink(path, rname, MAXNAMELEN); 505 if (n <= 0) { 506 if (debug >= 2) { 507 (void) printf( 508 "thr_call_statd: can't read link %s\n", 509 path); 510 } 511 } else { 512 rname[n] = '\0'; 513 514 tail = strrchr(path, '/') + 1; 515 516 if ((strlen(BACKUP) + strlen(rname) + 2) <= 517 MAXPATHLEN) { 518 (void) strcpy(tail, rname); 519 delete_file(path); 520 } else if (debug) { 521 printf("thr_call_statd: path over" 522 "maxpathlen!\n"); 523 } 524 } 525 526 } 527 528 if (debug) 529 pr_name(name, 0); 530 531 } else { 532 /* 533 * If `name' is an IP address symlink to a name file, 534 * remove it now. If it is the last such symlink, 535 * remove the name file as well. Regular files with 536 * no symlinks to them are assumed to be legacies and 537 * are removed as well. 538 */ 539 remove_name(name, 1, 1); 540 free(name); 541 } 542 thr_exit((void *) 0); 543 #ifdef lint 544 return (0); 545 #endif 546 } 547 548 /* 549 * Notifies the statd of host specified by name to indicate that 550 * state has changed for this server. 551 */ 552 static int 553 statd_call_statd(name) 554 char *name; 555 { 556 enum clnt_stat clnt_stat; 557 struct timeval tottimeout; 558 CLIENT *clnt; 559 char *name_or_addr; 560 stat_chge ntf; 561 int i; 562 int rc; 563 int dummy1, dummy2, dummy3, dummy4; 564 char ascii_addr[MAXNAMELEN]; 565 size_t unq_len; 566 567 ntf.mon_name = hostname; 568 ntf.state = LOCAL_STATE; 569 if (debug) 570 (void) printf("statd_call_statd at %s\n", name); 571 572 /* 573 * If it looks like an ASCII <address family>.<address> specifier, 574 * strip off the family - we just want the address when obtaining 575 * a client handle. 576 * If it's anything else, just pass it on to create_client(). 577 */ 578 unq_len = strcspn(name, "."); 579 580 if ((strncmp(name, SM_ADDR_IPV4, unq_len) == 0) || 581 (strncmp(name, SM_ADDR_IPV6, unq_len) == 0)) { 582 name_or_addr = strchr(name, '.') + 1; 583 } else { 584 name_or_addr = name; 585 } 586 587 /* 588 * NOTE: We depend here upon the fact that the RPC client code 589 * allows us to use ASCII dotted quad `names', i.e. "192.9.200.1". 590 * This may change in a future release. 591 */ 592 if (debug) { 593 (void) printf("statd_call_statd: calling create_client(%s)\n", 594 name_or_addr); 595 } 596 597 tottimeout.tv_sec = SM_RPC_TIMEOUT; 598 tottimeout.tv_usec = 0; 599 600 if ((clnt = create_client(name_or_addr, SM_PROG, SM_VERS, 601 &tottimeout)) == (CLIENT *) NULL) { 602 return (-1); 603 } 604 605 /* Perform notification to client */ 606 rc = 0; 607 clnt_stat = clnt_call(clnt, SM_NOTIFY, xdr_stat_chge, (char *)&ntf, 608 xdr_void, NULL, tottimeout); 609 if (debug) { 610 (void) printf("clnt_stat=%s(%d)\n", 611 clnt_sperrno(clnt_stat), clnt_stat); 612 } 613 if (clnt_stat != (int)RPC_SUCCESS) { 614 syslog(LOG_WARNING, 615 "statd: cannot talk to statd at %s, %s(%d)\n", 616 name_or_addr, clnt_sperrno(clnt_stat), clnt_stat); 617 rc = -1; 618 } 619 620 /* For HA systems and multi-homed hosts */ 621 ntf.state = LOCAL_STATE; 622 for (i = 0; i < addrix; i++) { 623 ntf.mon_name = host_name[i]; 624 if (debug) 625 (void) printf("statd_call_statd at %s\n", name_or_addr); 626 clnt_stat = clnt_call(clnt, SM_NOTIFY, xdr_stat_chge, 627 (char *)&ntf, xdr_void, NULL, 628 tottimeout); 629 if (clnt_stat != (int)RPC_SUCCESS) { 630 syslog(LOG_WARNING, 631 "statd: cannot talk to statd at %s, %s(%d)\n", 632 name_or_addr, clnt_sperrno(clnt_stat), clnt_stat); 633 rc = -1; 634 } 635 } 636 clnt_destroy(clnt); 637 return (rc); 638 } 639 640 /* 641 * Continues to contact hosts in recovery table that were unreachable. 642 * NOTE: There should only be one sm_try thread executing and 643 * thus locks are not needed for recovery table. Die is only cleared 644 * after all the hosts has at least been contacted once. The reader/writer 645 * lock ensures to finish this code before an sm_crash is started. Die 646 * variable will signal it. 647 */ 648 void * 649 sm_try() 650 { 651 name_entry *nl, *next; 652 timestruc_t wtime; 653 int delay = 0; 654 655 rw_rdlock(&thr_rwlock); 656 if (mutex_trylock(&sm_trylock)) 657 goto out; 658 mutex_lock(&crash_lock); 659 660 while (!die) { 661 wtime.tv_sec = delay; 662 wtime.tv_nsec = 0; 663 /* 664 * Wait until signalled to wakeup or time expired. 665 * If signalled to be awoken, then a crash has occurred 666 * or otherwise time expired. 667 */ 668 if (cond_reltimedwait(&retrywait, &crash_lock, &wtime) == 0) { 669 break; 670 } 671 672 /* Exit loop if queue is empty */ 673 if ((next = recov_q.sm_recovhdp) == NULL) 674 break; 675 676 mutex_unlock(&crash_lock); 677 678 while (((nl = next) != (name_entry *)NULL) && (!die)) { 679 next = next->nxt; 680 if (statd_call_statd(nl->name) == 0) { 681 /* remove name from BACKUP */ 682 remove_name(nl->name, 1, 0); 683 mutex_lock(&recov_q.lock); 684 /* remove entry from recovery_q */ 685 delete_name(&recov_q.sm_recovhdp, nl->name); 686 mutex_unlock(&recov_q.lock); 687 } else { 688 /* 689 * Print message only once since unreachable 690 * host can be contacted forever. 691 */ 692 if (delay == 0) 693 syslog(LOG_WARNING, 694 "statd: host %s is not responding\n", 695 nl->name); 696 } 697 } 698 /* 699 * Increment the amount of delay before restarting again. 700 * The amount of delay should not exceed the MAX_DELAYTIME. 701 */ 702 if (delay <= MAX_DELAYTIME) 703 delay += INC_DELAYTIME; 704 mutex_lock(&crash_lock); 705 } 706 707 mutex_unlock(&crash_lock); 708 mutex_unlock(&sm_trylock); 709 out: 710 rw_unlock(&thr_rwlock); 711 if (debug) 712 (void) printf("EXITING sm_try\n"); 713 thr_exit((void *) 0); 714 #ifdef lint 715 return (0); 716 #endif 717 } 718 719 /* 720 * Malloc's space and returns the ptr to malloc'ed space. NULL if unsuccessful. 721 */ 722 char * 723 xmalloc(len) 724 unsigned len; 725 { 726 char *new; 727 728 if ((new = malloc(len)) == 0) { 729 syslog(LOG_ERR, "statd: malloc, error %m\n"); 730 return ((char *)NULL); 731 } else { 732 (void) memset(new, 0, len); 733 return (new); 734 } 735 } 736 737 /* 738 * the following two routines are very similar to 739 * insert_mon and delete_mon in sm_proc.c, except the structture 740 * is different 741 */ 742 static name_entry * 743 insert_name(namepp, name, need_alloc) 744 name_entry **namepp; 745 char *name; 746 int need_alloc; 747 { 748 name_entry *new; 749 750 new = (name_entry *)xmalloc(sizeof (name_entry)); 751 if (new == (name_entry *) NULL) 752 return (NULL); 753 754 /* Allocate name when needed which is only when adding to record_t */ 755 if (need_alloc) { 756 if ((new->name = strdup(name)) == (char *)NULL) { 757 syslog(LOG_ERR, "statd: strdup, error %m\n"); 758 free(new); 759 return (NULL); 760 } 761 } else 762 new->name = name; 763 764 new->nxt = *namepp; 765 if (new->nxt != (name_entry *)NULL) 766 new->nxt->prev = new; 767 768 new->prev = (name_entry *) NULL; 769 770 *namepp = new; 771 if (debug) { 772 (void) printf("insert_name: inserted %s at %p\n", 773 name, (void *)namepp); 774 } 775 776 return (new); 777 } 778 779 /* 780 * Deletes name from specified list (namepp). 781 */ 782 static void 783 delete_name(namepp, name) 784 name_entry **namepp; 785 char *name; 786 { 787 name_entry *nl; 788 789 nl = *namepp; 790 while (nl != (name_entry *)NULL) { 791 if (str_cmp_address_specifier(nl->name, name) == 0 || 792 str_cmp_unqual_hostname(nl->name, name) == 0) { 793 if (nl->prev != (name_entry *)NULL) 794 nl->prev->nxt = nl->nxt; 795 else 796 *namepp = nl->nxt; 797 if (nl->nxt != (name_entry *)NULL) 798 nl->nxt->prev = nl->prev; 799 free(nl->name); 800 free(nl); 801 return; 802 } 803 nl = nl->nxt; 804 } 805 } 806 807 /* 808 * Finds name from specified list (namep). 809 */ 810 static name_entry * 811 find_name(namep, name) 812 name_entry **namep; 813 char *name; 814 { 815 name_entry *nl; 816 817 nl = *namep; 818 819 while (nl != (name_entry *)NULL) { 820 if (str_cmp_unqual_hostname(nl->name, name) == 0) { 821 return (nl); 822 } 823 nl = nl->nxt; 824 } 825 return ((name_entry *)NULL); 826 } 827 828 /* 829 * Creates a file. 830 */ 831 832 int 833 create_file(name) 834 char *name; 835 { 836 int fd; 837 838 /* 839 * The file might already exist. If it does, we ask for only write 840 * permission, since that's all the file was created with. 841 */ 842 if ((fd = open(name, O_CREAT | O_WRONLY, S_IWUSR)) == -1) { 843 if (errno != EEXIST) { 844 syslog(LOG_ERR, "can't open %s: %m", name); 845 return (1); 846 } 847 } 848 849 if (debug >= 2) 850 (void) printf("%s is created\n", name); 851 if (close(fd)) { 852 syslog(LOG_ERR, "statd: close, error %m\n"); 853 return (1); 854 } 855 856 return (0); 857 } 858 859 /* 860 * Deletes the file specified by name. 861 */ 862 void 863 delete_file(name) 864 char *name; 865 { 866 if (debug >= 2) 867 (void) printf("Remove monitor entry %s\n", name); 868 if (unlink(name) == -1) { 869 if (errno != ENOENT) 870 syslog(LOG_ERR, "statd: unlink of %s, error %m", name); 871 } 872 } 873 874 /* 875 * Return 1 if file is a symlink, else 0. 876 */ 877 int 878 is_symlink(file) 879 char *file; 880 { 881 int error; 882 struct stat lbuf; 883 884 do { 885 bzero((caddr_t)&lbuf, sizeof (lbuf)); 886 error = lstat(file, &lbuf); 887 } while (error == EINTR); 888 889 if (error == 0) { 890 return ((lbuf.st_mode & S_IFMT) == S_IFLNK); 891 } 892 893 return (0); 894 } 895 896 /* 897 * Moves the file specified by `from' to `to' only if the 898 * new file is guaranteed to be created (which is presumably 899 * why we don't just do a rename(2)). If `from' is a 900 * symlink, the destination file will be a similar symlink 901 * in the directory of `to'. 902 * 903 * Returns 0 for success, 1 for failure. 904 */ 905 static int 906 move_file(fromdir, file, todir) 907 char *fromdir; 908 char *file; 909 char *todir; 910 { 911 int n; 912 char rname[MAXNAMELEN + 1]; /* +1 for the terminating NULL */ 913 char from[MAXPATHLEN]; 914 char to[MAXPATHLEN]; 915 916 (void) strcpy(from, fromdir); 917 (void) strcat(from, "/"); 918 (void) strcat(from, file); 919 if (is_symlink(from)) { 920 /* 921 * Dig out the name of the regular file the link points to. 922 */ 923 n = readlink(from, rname, MAXNAMELEN); 924 if (n <= 0) { 925 if (debug >= 2) { 926 (void) printf("move_file: can't read link %s\n", 927 from); 928 } 929 return (1); 930 } 931 rname[n] = '\0'; 932 933 /* 934 * Create the link. 935 */ 936 if (create_symlink(todir, rname, file) != 0) { 937 return (1); 938 } 939 } else { 940 /* 941 * Do what we've always done to move regular files. 942 */ 943 (void) strcpy(to, todir); 944 (void) strcat(to, "/"); 945 (void) strcat(to, file); 946 if (create_file(to) != 0) { 947 return (1); 948 } 949 } 950 951 /* 952 * Remove the old file if we've created the new one. 953 */ 954 if (unlink(from) < 0) { 955 syslog(LOG_ERR, "move_file: unlink of %s, error %m", from); 956 return (1); 957 } 958 959 return (0); 960 } 961 962 /* 963 * Create a symbolic link named `lname' to regular file `rname'. 964 * Both files should be in directory `todir'. 965 */ 966 int 967 create_symlink(todir, rname, lname) 968 char *todir; 969 char *rname; 970 char *lname; 971 { 972 int error; 973 char lpath[MAXPATHLEN]; 974 975 /* 976 * Form the full pathname of the link. 977 */ 978 (void) strcpy(lpath, todir); 979 (void) strcat(lpath, "/"); 980 (void) strcat(lpath, lname); 981 982 /* 983 * Now make the new symlink ... 984 */ 985 if (symlink(rname, lpath) < 0) { 986 error = errno; 987 if (error != 0 && error != EEXIST) { 988 if (debug >= 2) { 989 (void) printf( 990 "create_symlink: can't link %s/%s -> %s\n", 991 todir, lname, rname); 992 } 993 return (1); 994 } 995 } 996 997 if (debug) { 998 if (error == EEXIST) { 999 (void) printf("link %s/%s -> %s already exists\n", 1000 todir, lname, rname); 1001 } else { 1002 (void) printf("created link %s/%s -> %s\n", 1003 todir, lname, rname); 1004 } 1005 } 1006 1007 return (0); 1008 } 1009 1010 /* 1011 * remove the name from the specified directory 1012 * op = 0: CURRENT 1013 * op = 1: BACKUP 1014 */ 1015 static void 1016 remove_name(char *name, int op, int startup) 1017 { 1018 int i; 1019 char *alt_dir; 1020 char *queue; 1021 1022 if (op == 0) { 1023 alt_dir = "statmon/sm"; 1024 queue = CURRENT; 1025 } else { 1026 alt_dir = "statmon/sm.bak"; 1027 queue = BACKUP; 1028 } 1029 1030 remove_single_name(name, queue, NULL); 1031 /* 1032 * At startup, entries have not yet been copied to alternate 1033 * directories and thus do not need to be removed. 1034 */ 1035 if (startup == 0) { 1036 for (i = 0; i < pathix; i++) { 1037 remove_single_name(name, path_name[i], alt_dir); 1038 } 1039 } 1040 } 1041 1042 /* 1043 * Remove the name from the specified directory, which is dir1/dir2 or 1044 * dir1, depending on whether dir2 is NULL. 1045 */ 1046 static void 1047 remove_single_name(char *name, char *dir1, char *dir2) 1048 { 1049 int n, error; 1050 char path[MAXPATHLEN+MAXNAMELEN+SM_MAXPATHLEN]; /* why > MAXPATHLEN? */ 1051 char dirpath[MAXPATHLEN]; 1052 char rname[MAXNAMELEN + 1]; /* +1 for NULL term */ 1053 1054 if (strlen(name) + strlen(dir1) + (dir2 != NULL ? strlen(dir2) : 0) 1055 + 3 > MAXPATHLEN) { 1056 if (dir2 != NULL) 1057 syslog(LOG_ERR, 1058 "statd: pathname too long: %s/%s/%s\n", 1059 dir1, dir2, name); 1060 else 1061 syslog(LOG_ERR, 1062 "statd: pathname too long: %s/%s\n", 1063 dir1, name); 1064 1065 return; 1066 } 1067 1068 (void) strcpy(path, dir1); 1069 (void) strcat(path, "/"); 1070 if (dir2 != NULL) { 1071 (void) strcat(path, dir2); 1072 (void) strcat(path, "/"); 1073 } 1074 (void) strcpy(dirpath, path); /* save here - we may need it shortly */ 1075 (void) strcat(path, name); 1076 1077 /* 1078 * Despite the name of this routine :-@), `path' may be a symlink 1079 * to a regular file. If it is, and if that file has no other 1080 * links to it, we must remove it now as well. 1081 */ 1082 if (is_symlink(path)) { 1083 n = readlink(path, rname, MAXNAMELEN); 1084 if (n > 0) { 1085 rname[n] = '\0'; 1086 1087 if (count_symlinks(dirpath, rname, &n) < 0) { 1088 return; 1089 } 1090 1091 if (n == 1) { 1092 (void) strcat(dirpath, rname); 1093 error = unlink(dirpath); 1094 if (debug >= 2) { 1095 if (error < 0) { 1096 (void) printf( 1097 "remove_name: can't unlink %s\n", 1098 dirpath); 1099 } else { 1100 (void) printf( 1101 "remove_name: unlinked %s\n", 1102 dirpath); 1103 } 1104 } 1105 } 1106 } else { 1107 /* 1108 * Policy: if we can't read the symlink, leave it 1109 * here for analysis by the system administrator. 1110 */ 1111 syslog(LOG_ERR, 1112 "statd: can't read link %s: %m\n", path); 1113 } 1114 } 1115 1116 /* 1117 * If it's a regular file, we can assume all symlinks and the 1118 * files to which they refer have been processed already - just 1119 * fall through to here to remove it. 1120 */ 1121 delete_file(path); 1122 } 1123 1124 /* 1125 * Count the number of symlinks in `dir' which point to `name' (also in dir). 1126 * Passes back symlink count in `count'. 1127 * Returns 0 for success, < 0 for failure. 1128 */ 1129 static int 1130 count_symlinks(char *dir, char *name, int *count) 1131 { 1132 int cnt = 0; 1133 int n; 1134 DIR *dp; 1135 struct dirent *dirp, *entp; 1136 char lpath[MAXPATHLEN]; 1137 char rname[MAXNAMELEN + 1]; /* +1 for term NULL */ 1138 1139 if ((dp = opendir(dir)) == (DIR *)NULL) { 1140 syslog(LOG_ERR, "count_symlinks: open %s dir, error %m\n", 1141 dir); 1142 return (-1); 1143 } 1144 1145 entp = (struct dirent *)xmalloc(MAXDIRENT); 1146 if (entp == NULL) { 1147 (void) closedir(dp); 1148 return (-1); 1149 } 1150 1151 while ((dirp = readdir_r(dp, entp)) != (struct dirent *)NULL) { 1152 if (strcmp(dirp->d_name, ".") == 0 || 1153 strcmp(dirp->d_name, "..") == 0) { 1154 continue; 1155 } 1156 1157 (void) sprintf(lpath, "%s%s", dir, dirp->d_name); 1158 if (is_symlink(lpath)) { 1159 /* 1160 * Fetch the name of the file the symlink refers to. 1161 */ 1162 n = readlink(lpath, rname, MAXNAMELEN); 1163 if (n <= 0) { 1164 if (debug >= 2) { 1165 (void) printf( 1166 "count_symlinks: can't read link %s\n", 1167 lpath); 1168 } 1169 continue; 1170 } 1171 rname[n] = '\0'; 1172 1173 /* 1174 * If `rname' matches `name', bump the count. There 1175 * may well be multiple symlinks to the same name, so 1176 * we must continue to process the entire directory. 1177 */ 1178 if (strcmp(rname, name) == 0) { 1179 cnt++; 1180 } 1181 } 1182 } 1183 1184 free(entp); 1185 (void) closedir(dp); 1186 1187 if (debug) { 1188 (void) printf("count_symlinks: found %d symlinks\n", cnt); 1189 } 1190 *count = cnt; 1191 return (0); 1192 } 1193 1194 /* 1195 * Manage the cache of hostnames. An entry for each host that has recently 1196 * locked a file is kept. There is an in-ram table (rec_table) and an empty 1197 * file in the file system name space (/var/statmon/sm/<name>). This 1198 * routine adds (deletes) the name to (from) the in-ram table and the entry 1199 * to (from) the file system name space. 1200 * 1201 * If op == 1 then the name is added to the queue otherwise the name is 1202 * deleted. 1203 */ 1204 void 1205 record_name(name, op) 1206 char *name; 1207 int op; 1208 { 1209 name_entry *nl; 1210 int i; 1211 char path[MAXPATHLEN+MAXNAMELEN+SM_MAXPATHLEN]; 1212 name_entry **record_q; 1213 unsigned int hash; 1214 1215 /* 1216 * These names are supposed to be just host names, not paths or 1217 * other arbitrary files. 1218 * manipulating the empty pathname unlinks CURRENT, 1219 * manipulating files with '/' would allow you to create and unlink 1220 * files all over the system; LOG_AUTH, it's a security thing. 1221 * Don't remove the directories . and .. 1222 */ 1223 if (name == NULL) 1224 return; 1225 1226 if (name[0] == '\0' || strchr(name, '/') != NULL || 1227 strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { 1228 syslog(LOG_ERR|LOG_AUTH, "statd: attempt to %s \"%s/%s\"", 1229 op == 1 ? "create" : "remove", CURRENT, name); 1230 return; 1231 } 1232 1233 SMHASH(name, hash); 1234 if (debug) { 1235 if (op == 1) 1236 (void) printf("inserting %s at hash %d,\n", 1237 name, hash); 1238 else 1239 (void) printf("deleting %s at hash %d\n", name, hash); 1240 pr_name(name, 1); 1241 } 1242 1243 1244 if (op == 1) { /* insert */ 1245 mutex_lock(&record_table[hash].lock); 1246 record_q = &record_table[hash].sm_rechdp; 1247 if ((nl = find_name(record_q, name)) == (name_entry *)NULL) { 1248 1249 int path_len; 1250 1251 if ((nl = insert_name(record_q, name, 1)) != 1252 (name_entry *) NULL) 1253 nl->count++; 1254 mutex_unlock(&record_table[hash].lock); 1255 /* make an entry in current directory */ 1256 1257 path_len = strlen(CURRENT) + strlen(name) + 2; 1258 if (path_len > MAXPATHLEN) { 1259 syslog(LOG_ERR, 1260 "statd: pathname too long: %s/%s\n", 1261 CURRENT, name); 1262 return; 1263 } 1264 (void) strcpy(path, CURRENT); 1265 (void) strcat(path, "/"); 1266 (void) strcat(path, name); 1267 (void) create_file(path); 1268 if (debug) { 1269 (void) printf("After insert_name\n"); 1270 pr_name(name, 1); 1271 } 1272 /* make an entry in alternate paths */ 1273 for (i = 0; i < pathix; i++) { 1274 path_len = strlen(path_name[i]) + 1275 strlen("/statmon/sm/") + 1276 strlen(name) + 1; 1277 1278 if (path_len > MAXPATHLEN) { 1279 syslog(LOG_ERR, 1280 "statd: pathname too long: %s/statmon/sm/%s\n", 1281 path_name[i], name); 1282 continue; 1283 } 1284 (void) strcpy(path, path_name[i]); 1285 (void) strcat(path, "/statmon/sm/"); 1286 (void) strcat(path, name); 1287 (void) create_file(path); 1288 } 1289 return; 1290 } 1291 nl->count++; 1292 mutex_unlock(&record_table[hash].lock); 1293 1294 } else { /* delete */ 1295 mutex_lock(&record_table[hash].lock); 1296 record_q = &record_table[hash].sm_rechdp; 1297 if ((nl = find_name(record_q, name)) == (name_entry *)NULL) { 1298 mutex_unlock(&record_table[hash].lock); 1299 return; 1300 } 1301 nl->count--; 1302 if (nl->count == 0) { 1303 delete_name(record_q, name); 1304 mutex_unlock(&record_table[hash].lock); 1305 /* remove this entry from current directory */ 1306 remove_name(name, 0, 0); 1307 } else 1308 mutex_unlock(&record_table[hash].lock); 1309 if (debug) { 1310 (void) printf("After delete_name \n"); 1311 pr_name(name, 1); 1312 } 1313 } 1314 } 1315 1316 /* 1317 * This routine adds a symlink in the form of an ASCII dotted quad 1318 * IP address that is linked to the name already recorded in the 1319 * filesystem name space by record_name(). Enough information is 1320 * (hopefully) provided to support other address types in the future. 1321 * The purpose of this is to cache enough information to contact 1322 * hosts in other domains during server crash recovery (see bugid 1323 * 1184192). 1324 * 1325 * The worst failure mode here is that the symlink is not made, and 1326 * statd falls back to the old buggy behavior. 1327 */ 1328 void 1329 record_addr(char *name, sa_family_t family, struct netobj *ah) 1330 { 1331 int i; 1332 int path_len; 1333 char *famstr; 1334 struct in_addr addr; 1335 char *addr6; 1336 char ascii_addr[MAXNAMELEN]; 1337 char path[MAXPATHLEN]; 1338 1339 if (family == AF_INET) { 1340 if (ah->n_len != sizeof (struct in_addr)) 1341 return; 1342 addr = *(struct in_addr *)ah->n_bytes; 1343 } else if (family == AF_INET6) { 1344 if (ah->n_len != sizeof (struct in6_addr)) 1345 return; 1346 addr6 = (char *)ah->n_bytes; 1347 } else 1348 return; 1349 1350 if (debug) { 1351 if (family == AF_INET) 1352 (void) printf("record_addr: addr= %x\n", addr.s_addr); 1353 else if (family == AF_INET6) 1354 (void) printf("record_addr: addr= %x\n", \ 1355 ((struct in6_addr *)addr6)->s6_addr); 1356 } 1357 1358 if (family == AF_INET) { 1359 if (addr.s_addr == INADDR_ANY || 1360 ((addr.s_addr && 0xff000000) == 0) || 1361 IN_BADCLASS(addr.s_addr)) { 1362 syslog(LOG_DEBUG, 1363 "record_addr: illegal IP address %x\n", 1364 addr.s_addr); 1365 return; 1366 } 1367 } 1368 1369 /* convert address to ASCII */ 1370 famstr = family2string(family); 1371 if (famstr == NULL) { 1372 syslog(LOG_DEBUG, 1373 "record_addr: unsupported address family %d\n", 1374 family); 1375 return; 1376 } 1377 1378 switch (family) { 1379 char abuf[INET6_ADDRSTRLEN]; 1380 case AF_INET: 1381 (void) sprintf(ascii_addr, "%s.%s", famstr, inet_ntoa(addr)); 1382 break; 1383 1384 case AF_INET6: 1385 (void) sprintf(ascii_addr, "%s.%s", famstr,\ 1386 inet_ntop(family, addr6, abuf, sizeof (abuf))); 1387 break; 1388 1389 default: 1390 if (debug) { 1391 (void) printf( 1392 "record_addr: family2string supports unknown family %d (%s)\n", 1393 family, 1394 famstr); 1395 } 1396 free(famstr); 1397 return; 1398 } 1399 1400 if (debug) { 1401 (void) printf("record_addr: ascii_addr= %s\n", ascii_addr); 1402 } 1403 free(famstr); 1404 1405 /* 1406 * Make the symlink in CURRENT. The `name' file should have 1407 * been created previously by record_name(). 1408 */ 1409 (void) create_symlink(CURRENT, name, ascii_addr); 1410 1411 /* 1412 * Similarly for alternate paths. 1413 */ 1414 for (i = 0; i < pathix; i++) { 1415 path_len = strlen(path_name[i]) + 1416 strlen("/statmon/sm/") + 1417 strlen(name) + 1; 1418 1419 if (path_len > MAXPATHLEN) { 1420 syslog(LOG_ERR, 1421 "statd: pathname too long: %s/statmon/sm/%s\n", 1422 path_name[i], name); 1423 continue; 1424 } 1425 (void) strcpy(path, path_name[i]); 1426 (void) strcat(path, "/statmon/sm"); 1427 (void) create_symlink(path, name, ascii_addr); 1428 } 1429 } 1430 1431 /* 1432 * SM_CRASH - simulate a crash of statd. 1433 */ 1434 void 1435 sm_crash() 1436 { 1437 name_entry *nl, *next; 1438 mon_entry *nl_monp, *mon_next; 1439 int k; 1440 my_id *nl_idp; 1441 1442 for (k = 0; k < MAX_HASHSIZE; k++) { 1443 mutex_lock(&mon_table[k].lock); 1444 if ((mon_next = mon_table[k].sm_monhdp) == 1445 (mon_entry *) NULL) { 1446 mutex_unlock(&mon_table[k].lock); 1447 continue; 1448 } else { 1449 while ((nl_monp = mon_next) != (mon_entry *)NULL) { 1450 mon_next = mon_next->nxt; 1451 nl_idp = &nl_monp->id.mon_id.my_id; 1452 free(nl_monp->id.mon_id.mon_name); 1453 free(nl_idp->my_name); 1454 free(nl_monp); 1455 } 1456 mon_table[k].sm_monhdp = (mon_entry *)NULL; 1457 } 1458 mutex_unlock(&mon_table[k].lock); 1459 } 1460 1461 /* Clean up entries in record table */ 1462 for (k = 0; k < MAX_HASHSIZE; k++) { 1463 mutex_lock(&record_table[k].lock); 1464 if ((next = record_table[k].sm_rechdp) == 1465 (name_entry *) NULL) { 1466 mutex_unlock(&record_table[k].lock); 1467 continue; 1468 } else { 1469 while ((nl = next) != (name_entry *)NULL) { 1470 next = next->nxt; 1471 free(nl->name); 1472 free(nl); 1473 } 1474 record_table[k].sm_rechdp = (name_entry *)NULL; 1475 } 1476 mutex_unlock(&record_table[k].lock); 1477 } 1478 1479 /* Clean up entries in recovery table */ 1480 mutex_lock(&recov_q.lock); 1481 if ((next = recov_q.sm_recovhdp) != (name_entry *)NULL) { 1482 while ((nl = next) != (name_entry *)NULL) { 1483 next = next->nxt; 1484 free(nl->name); 1485 free(nl); 1486 } 1487 recov_q.sm_recovhdp = (name_entry *)NULL; 1488 } 1489 mutex_unlock(&recov_q.lock); 1490 statd_init(); 1491 } 1492 1493 /* 1494 * Initialize the hash tables: mon_table, record_table, recov_q and 1495 * locks. 1496 */ 1497 void 1498 sm_inithash() 1499 { 1500 int k; 1501 1502 if (debug) 1503 (void) printf("Initializing hash tables\n"); 1504 for (k = 0; k < MAX_HASHSIZE; k++) { 1505 mon_table[k].sm_monhdp = (mon_entry *)NULL; 1506 record_table[k].sm_rechdp = (name_entry *)NULL; 1507 mutex_init(&mon_table[k].lock, USYNC_THREAD, NULL); 1508 mutex_init(&record_table[k].lock, USYNC_THREAD, NULL); 1509 } 1510 mutex_init(&recov_q.lock, USYNC_THREAD, NULL); 1511 recov_q.sm_recovhdp = (name_entry *)NULL; 1512 1513 } 1514 1515 /* 1516 * Maps a socket address family to a name string, or NULL if the family 1517 * is not supported by statd. 1518 * Caller is responsible for freeing storage used by result string, if any. 1519 */ 1520 static char * 1521 family2string(sa_family_t family) 1522 { 1523 char *rc; 1524 1525 switch (family) { 1526 case AF_INET: 1527 rc = strdup(SM_ADDR_IPV4); 1528 break; 1529 1530 case AF_INET6: 1531 rc = strdup(SM_ADDR_IPV6); 1532 break; 1533 1534 default: 1535 rc = NULL; 1536 break; 1537 } 1538 1539 return (rc); 1540 } 1541 1542 /* 1543 * Prints out list in record_table if flag is 1 otherwise 1544 * prints out each list in recov_q specified by name. 1545 */ 1546 static void 1547 pr_name(name, flag) 1548 char *name; 1549 int flag; 1550 { 1551 name_entry *nl; 1552 unsigned int hash; 1553 1554 if (!debug) 1555 return; 1556 if (flag) { 1557 SMHASH(name, hash); 1558 (void) printf("*****record_q: "); 1559 mutex_lock(&record_table[hash].lock); 1560 nl = record_table[hash].sm_rechdp; 1561 while (nl != (name_entry *)NULL) { 1562 (void) printf("(%x), ", (int)nl); 1563 nl = nl->nxt; 1564 } 1565 mutex_unlock(&record_table[hash].lock); 1566 } else { 1567 (void) printf("*****recovery_q: "); 1568 mutex_lock(&recov_q.lock); 1569 nl = recov_q.sm_recovhdp; 1570 while (nl != (name_entry *)NULL) { 1571 (void) printf("(%x), ", (int)nl); 1572 nl = nl->nxt; 1573 } 1574 mutex_unlock(&recov_q.lock); 1575 1576 } 1577 (void) printf("\n"); 1578 } 1579