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