xref: /illumos-gate/usr/src/cmd/fs.d/nfs/statd/sm_proc.c (revision a6f0fae9)
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 #include <stdio.h>
43 #include <sys/types.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <rpc/rpc.h>
49 #include <rpcsvc/sm_inter.h>
50 #include <rpcsvc/nsm_addr.h>
51 #include <memory.h>
52 #include <net/if.h>
53 #include <sys/sockio.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <netdb.h>
58 #include <netdir.h>
59 #include <synch.h>
60 #include <thread.h>
61 #include <assert.h>
62 #include "sm_statd.h"
63 
64 static int local_state;		/* fake local sm state */
65 				/* client name-to-address translation table */
66 static name_addr_entry_t *name_addr = NULL;
67 
68 
69 #define	LOGHOST "loghost"
70 
71 static void delete_mon(char *mon_name, my_id *my_idp);
72 static void insert_mon(mon *monp);
73 static void pr_mon(char *);
74 static int statd_call_lockd(mon *monp, int state);
75 static int hostname_eq(char *host1, char *host2);
76 static char *get_system_id(char *hostname);
77 static void add_aliases(struct hostent *phost);
78 static void *thr_send_notice(void *);
79 static void delete_onemon(char *mon_name, my_id *my_idp,
80 				mon_entry **monitor_q);
81 static void send_notice(char *mon_name, int state);
82 static void add_to_host_array(char *host);
83 static int in_host_array(char *host);
84 static void pr_name_addr(name_addr_entry_t *name_addr);
85 
86 extern int self_check(char *hostname);
87 extern struct lifconf *getmyaddrs(void);
88 
89 /* ARGSUSED */
90 void
91 sm_status(namep, resp)
92 	sm_name *namep;
93 	sm_stat_res *resp;
94 {
95 
96 	if (debug)
97 		(void) printf("proc sm_stat: mon_name = %s\n",
98 				namep->mon_name);
99 
100 	resp->res_stat = stat_succ;
101 	resp->state = LOCAL_STATE;
102 }
103 
104 /* ARGSUSED */
105 void
106 sm_mon(monp, resp)
107 	mon *monp;
108 	sm_stat_res *resp;
109 {
110 	mon_id *monidp;
111 	monidp = &monp->mon_id;
112 
113 	rw_rdlock(&thr_rwlock);
114 	if (debug) {
115 		(void) printf("proc sm_mon: mon_name = %s, id = %d\n",
116 		monidp->mon_name, * ((int *)monp->priv));
117 		pr_mon(monp->mon_id.mon_name);
118 	}
119 
120 	/* only monitor other hosts */
121 	if (self_check(monp->mon_id.mon_name) == 0) {
122 		/* store monitor request into monitor_q */
123 		insert_mon(monp);
124 	}
125 
126 	pr_mon(monp->mon_id.mon_name);
127 	resp->res_stat = stat_succ;
128 	resp->state = local_state;
129 	rw_unlock(&thr_rwlock);
130 }
131 
132 /* ARGSUSED */
133 void
134 sm_unmon(monidp, resp)
135 	mon_id *monidp;
136 	sm_stat *resp;
137 {
138 	rw_rdlock(&thr_rwlock);
139 	if (debug) {
140 		(void) printf(
141 			"proc sm_unmon: mon_name = %s, [%s, %d, %d, %d]\n",
142 			monidp->mon_name, monidp->my_id.my_name,
143 			monidp->my_id.my_prog, monidp->my_id.my_vers,
144 			monidp->my_id.my_proc);
145 		pr_mon(monidp->mon_name);
146 	}
147 
148 	delete_mon(monidp->mon_name, &monidp->my_id);
149 	pr_mon(monidp->mon_name);
150 	resp->state = local_state;
151 	rw_unlock(&thr_rwlock);
152 }
153 
154 /* ARGSUSED */
155 void
156 sm_unmon_all(myidp, resp)
157 	my_id *myidp;
158 	sm_stat *resp;
159 {
160 	rw_rdlock(&thr_rwlock);
161 	if (debug)
162 		(void) printf("proc sm_unmon_all: [%s, %d, %d, %d]\n",
163 		myidp->my_name,
164 		myidp->my_prog, myidp->my_vers,
165 		myidp->my_proc);
166 	delete_mon((char *)NULL, myidp);
167 	pr_mon(NULL);
168 	resp->state = local_state;
169 	rw_unlock(&thr_rwlock);
170 }
171 
172 /*
173  * Notifies lockd specified by name that state has changed for this server.
174  */
175 void
176 sm_notify(ntfp)
177 	stat_chge *ntfp;
178 {
179 	rw_rdlock(&thr_rwlock);
180 	if (debug)
181 		(void) printf("sm_notify: %s state =%d\n",
182 			ntfp->mon_name, ntfp->state);
183 	send_notice(ntfp->mon_name, ntfp->state);
184 	rw_unlock(&thr_rwlock);
185 }
186 
187 /* ARGSUSED */
188 void
189 sm_simu_crash(myidp)
190 	void *myidp;
191 {
192 	int i;
193 	struct mon_entry *monitor_q;
194 	int found = 0;
195 
196 	/* Only one crash should be running at a time. */
197 	mutex_lock(&crash_lock);
198 	if (debug)
199 		(void) printf("proc sm_simu_crash\n");
200 	if (in_crash) {
201 		cond_wait(&crash_finish, &crash_lock);
202 		mutex_unlock(&crash_lock);
203 		return;
204 	} else {
205 		in_crash = 1;
206 	}
207 	mutex_unlock(&crash_lock);
208 
209 	for (i = 0; i < MAX_HASHSIZE; i++) {
210 		mutex_lock(&mon_table[i].lock);
211 		monitor_q = mon_table[i].sm_monhdp;
212 		if (monitor_q != (struct mon_entry *)NULL) {
213 			mutex_unlock(&mon_table[i].lock);
214 			found = 1;
215 			break;
216 		}
217 		mutex_unlock(&mon_table[i].lock);
218 	}
219 	/*
220 	 * If there are entries found in the monitor table,
221 	 * initiate a crash, else zero out the in_crash variable.
222 	 */
223 	if (found) {
224 		mutex_lock(&crash_lock);
225 		die = 1;
226 		/* Signal sm_retry() thread if sleeping. */
227 		cond_signal(&retrywait);
228 		mutex_unlock(&crash_lock);
229 		rw_wrlock(&thr_rwlock);
230 		sm_crash();
231 		rw_unlock(&thr_rwlock);
232 	} else {
233 		mutex_lock(&crash_lock);
234 		in_crash = 0;
235 		mutex_unlock(&crash_lock);
236 	}
237 }
238 
239 /* ARGSUSED */
240 void
241 nsmaddrproc1_reg(regargs, regresp)
242 	reg1args *regargs;
243 	reg1res  *regresp;
244 {
245 	nsm_addr_res status;
246 	name_addr_entry_t *entry;
247 	char *tmp_n_bytes;
248 	addr_entry_t *addr;
249 
250 	rw_rdlock(&thr_rwlock);
251 	if (debug) {
252 		int i;
253 
254 		(void) printf("nap1_reg: fam= %d, name= %s, len= %d\n",
255 				regargs->family,
256 				regargs->name,
257 				regargs->address.n_len);
258 		(void) printf("address is: ");
259 		for (i = 0; i < regargs->address.n_len; i++) {
260 			(void) printf("%d.",
261 				(unsigned char)regargs->address.n_bytes[i]);
262 		}
263 		(void) printf("\n");
264 	}
265 
266 	/*
267 	 * Locate the entry with the name in the NSM_ADDR_REG request if
268 	 * it exists.  If it doesn't, create a new entry to hold this name.
269 	 * The first time through this code, name_addr starts out as NULL.
270 	 */
271 	mutex_lock(&name_addrlock);
272 	for (entry = name_addr; entry; entry = entry->next) {
273 		if (strcmp(regargs->name, entry->name) == 0) {
274 			if (debug) {
275 				(void) printf("nap1_reg: matched name %s\n",
276 						entry->name);
277 			}
278 			break;
279 		}
280 	}
281 
282 	if (entry == NULL) {
283 		entry = (name_addr_entry_t *)malloc(sizeof (*entry));
284 		if (entry == NULL) {
285 			if (debug) {
286 				(void) printf(
287 				"nsmaddrproc1_reg: no memory for entry\n");
288 			}
289 			status = nsm_addr_fail;
290 			goto done;
291 		}
292 
293 		entry->name = strdup(regargs->name);
294 		if (entry->name == NULL) {
295 			if (debug) {
296 				(void) printf(
297 				"nsmaddrproc1_reg: no memory for name\n");
298 			}
299 			free(entry);
300 			status = nsm_addr_fail;
301 			goto done;
302 		}
303 		entry->addresses = NULL;
304 
305 		/*
306 		 * Link the new entry onto the *head* of the name_addr
307 		 * table.
308 		 *
309 		 * Note: there is code below in the address maintenance
310 		 * section that assumes this behavior.
311 		 */
312 		entry->next = name_addr;
313 		name_addr = entry;
314 	}
315 
316 	/*
317 	 * Try to match the address in the request; if it doesn't match,
318 	 * add it to the entry's address list.
319 	 */
320 	for (addr = entry->addresses; addr; addr = addr->next) {
321 		if (addr->family == (sa_family_t)regargs->family &&
322 		    addr->ah.n_len == regargs->address.n_len &&
323 		    memcmp(addr->ah.n_bytes, regargs->address.n_bytes,
324 			addr->ah.n_len) == 0) {
325 			if (debug) {
326 				int i;
327 
328 				(void) printf("nap1_reg: matched addr ");
329 				for (i = 0; i < addr->ah.n_len; i++) {
330 					(void) printf("%d.",
331 					(unsigned char)addr->ah.n_bytes[i]);
332 				}
333 				(void) printf(" family %d for name %s\n",
334 						addr->family,
335 						entry->name);
336 			}
337 			break;
338 		}
339 	}
340 
341 	if (addr == NULL) {
342 		addr = (addr_entry_t *)malloc(sizeof (*addr));
343 		tmp_n_bytes = (char *)malloc(regargs->address.n_len);
344 		if (addr == NULL || tmp_n_bytes == NULL) {
345 			if (debug) {
346 				(void) printf(
347 					"nap1_reg: no memory for addr\n");
348 			}
349 
350 			/*
351 			 * If this name entry was just newly made in the
352 			 * table, back it out now that we can't register
353 			 * an address with it anyway.
354 			 *
355 			 * Note: we are making an assumption about how
356 			 * names are added to (the head of) name_addr here.
357 			 */
358 			if (entry == name_addr && entry->addresses == NULL) {
359 				name_addr = name_addr->next;
360 				free(entry->name);
361 				free(entry);
362 				if (tmp_n_bytes)
363 					free(tmp_n_bytes);
364 				if (addr)
365 					free(addr);
366 				status = nsm_addr_fail;
367 				goto done;
368 			}
369 		}
370 
371 		/*
372 		 * Note:  this check for address family assumes that we
373 		 *	  will get something different here someday for
374 		 *	  other supported address types, such as IPv6.
375 		 */
376 		addr->ah.n_len = regargs->address.n_len;
377 		addr->ah.n_bytes = tmp_n_bytes;
378 		addr->family = regargs->family;
379 		if (debug) {
380 			if ((addr->family != AF_INET) && \
381 				(addr->family != AF_INET6)) {
382 				(void) printf(
383 					"nap1_reg: unknown addr family %d\n",
384 					addr->family);
385 			}
386 		}
387 		(void) memcpy(addr->ah.n_bytes, regargs->address.n_bytes,
388 				addr->ah.n_len);
389 
390 		addr->next = entry->addresses;
391 		entry->addresses = addr;
392 	}
393 
394 	status = nsm_addr_succ;
395 
396 done:
397 	regresp->status = status;
398 	if (debug) {
399 		pr_name_addr(name_addr);
400 	}
401 	mutex_unlock(&name_addrlock);
402 	rw_unlock(&thr_rwlock);
403 }
404 
405 /*
406  * Insert an entry into the monitor_q.  Space for the entry is allocated
407  * here.  It is then filled in from the information passed in.
408  */
409 static void
410 insert_mon(monp)
411 	mon *monp;
412 {
413 	mon_entry *new, *found;
414 	my_id *my_idp, *nl_idp;
415 	mon_entry *monitor_q;
416 	unsigned int hash;
417 	name_addr_entry_t *entry;
418 	addr_entry_t *addr;
419 
420 	/* Allocate entry for new */
421 	if ((new = (mon_entry *) malloc(sizeof (mon_entry))) == 0) {
422 		syslog(LOG_ERR,
423 			"statd: insert_mon: malloc error on mon %s (id=%d)\n",
424 			monp->mon_id.mon_name, * ((int *)monp->priv));
425 		return;
426 	}
427 
428 	/* Initialize and copy contents of monp to new */
429 	(void) memset(new, 0, sizeof (mon_entry));
430 	(void) memcpy(&new->id, monp, sizeof (mon));
431 
432 	/* Allocate entry for new mon_name */
433 	if ((new->id.mon_id.mon_name = strdup(monp->mon_id.mon_name)) == 0) {
434 		syslog(LOG_ERR,
435 			"statd: insert_mon: malloc error on mon %s (id=%d)\n",
436 			monp->mon_id.mon_name, * ((int *)monp->priv));
437 		free(new);
438 		return;
439 	}
440 
441 
442 	/* Allocate entry for new my_name */
443 	if ((new->id.mon_id.my_id.my_name =
444 		strdup(monp->mon_id.my_id.my_name)) == 0) {
445 		syslog(LOG_ERR,
446 			"statd: insert_mon: malloc error on mon %s (id=%d)\n",
447 			monp->mon_id.mon_name, * ((int *)monp->priv));
448 		free(new->id.mon_id.mon_name);
449 		free(new);
450 		return;
451 	}
452 
453 	if (debug)
454 		(void) printf("add_mon(%x) %s (id=%d)\n",
455 		(int)new, new->id.mon_id.mon_name, * ((int *)new->id.priv));
456 
457 	/*
458 	 * Record the name, and all addresses which have been registered
459 	 * for this name, in the filesystem name space.
460 	 */
461 	record_name(new->id.mon_id.mon_name, 1);
462 	if (regfiles_only == 0) {
463 		mutex_lock(&name_addrlock);
464 		for (entry = name_addr; entry; entry = entry->next) {
465 			if (strcmp(new->id.mon_id.mon_name, entry->name) != 0) {
466 				continue;
467 			}
468 
469 			for (addr = entry->addresses; addr; addr = addr->next) {
470 				record_addr(new->id.mon_id.mon_name,
471 						addr->family, &addr->ah);
472 			}
473 			break;
474 		}
475 		mutex_unlock(&name_addrlock);
476 	}
477 
478 	SMHASH(new->id.mon_id.mon_name, hash);
479 	mutex_lock(&mon_table[hash].lock);
480 	monitor_q = mon_table[hash].sm_monhdp;
481 
482 	/* If mon_table hash list is empty. */
483 	if (monitor_q == (struct mon_entry *)NULL) {
484 		if (debug)
485 			(void) printf("\nAdding to monitor_q hash %d\n", hash);
486 		new->nxt = new->prev = (mon_entry *)NULL;
487 		mon_table[hash].sm_monhdp = new;
488 		mutex_unlock(&mon_table[hash].lock);
489 		return;
490 	} else {
491 		found = 0;
492 		my_idp = &new->id.mon_id.my_id;
493 		while (monitor_q != (mon_entry *)NULL)  {
494 			/*
495 			 * This list is searched sequentially for the
496 			 * tuple (hostname, prog, vers, proc). The tuples
497 			 * are inserted in the beginning of the monitor_q,
498 			 * if the hostname is not already present in the list.
499 			 * If the hostname is found in the list, the incoming
500 			 * tuple is inserted just after all the tuples with the
501 			 * same hostname. However, if the tuple matches exactly
502 			 * with an entry in the list, space allocated for the
503 			 * new entry is released and nothing is inserted in the
504 			 * list.
505 			 */
506 
507 			if (str_cmp_unqual_hostname(
508 				monitor_q->id.mon_id.mon_name,
509 				new->id.mon_id.mon_name) == 0) {
510 				/* found */
511 				nl_idp = &monitor_q->id.mon_id.my_id;
512 				if ((str_cmp_unqual_hostname(my_idp->my_name,
513 					nl_idp->my_name) == 0) &&
514 					my_idp->my_prog == nl_idp->my_prog &&
515 					my_idp->my_vers == nl_idp->my_vers &&
516 					my_idp->my_proc == nl_idp->my_proc) {
517 					/*
518 					 * already exists an identical one,
519 					 * release the space allocated for the
520 					 * mon_entry
521 					 */
522 					free(new->id.mon_id.mon_name);
523 					free(new->id.mon_id.my_id.my_name);
524 					free(new);
525 					mutex_unlock(&mon_table[hash].lock);
526 					return;
527 				} else {
528 					/*
529 					 * mark the last callback that is
530 					 * not matching; new is inserted
531 					 * after this
532 					 */
533 					found = monitor_q;
534 				}
535 			} else if (found)
536 				break;
537 			monitor_q = monitor_q->nxt;
538 		}
539 		if (found) {
540 			/*
541 			 * insert just after the entry having matching tuple.
542 			 */
543 			new->nxt = found->nxt;
544 			new->prev = found;
545 			if (found->nxt != (mon_entry *)NULL)
546 				found->nxt->prev = new;
547 			found->nxt = new;
548 		} else {
549 			/*
550 			 * not found, insert in front of list.
551 			 */
552 			new->nxt = mon_table[hash].sm_monhdp;
553 			new->prev = (mon_entry *) NULL;
554 			if (new->nxt != (mon_entry *) NULL)
555 				new->nxt->prev = new;
556 			mon_table[hash].sm_monhdp = new;
557 		}
558 		mutex_unlock(&mon_table[hash].lock);
559 		return;
560 	}
561 }
562 
563 /*
564  * Deletes a specific monitor name or deletes all monitors with same id
565  * in hash table.
566  */
567 static void
568 delete_mon(mon_name, my_idp)
569 	char *mon_name;
570 	my_id *my_idp;
571 {
572 	unsigned int hash;
573 
574 	if (mon_name != (char *)NULL) {
575 		record_name(mon_name, 0);
576 		SMHASH(mon_name, hash);
577 		mutex_lock(&mon_table[hash].lock);
578 		delete_onemon(mon_name, my_idp, &mon_table[hash].sm_monhdp);
579 		mutex_unlock(&mon_table[hash].lock);
580 	} else {
581 		for (hash = 0; hash < MAX_HASHSIZE; hash++) {
582 			mutex_lock(&mon_table[hash].lock);
583 			delete_onemon(mon_name, my_idp,
584 					&mon_table[hash].sm_monhdp);
585 			mutex_unlock(&mon_table[hash].lock);
586 		}
587 	}
588 }
589 
590 /*
591  * Deletes a monitor in list.
592  * IF mon_name is NULL, delete all mon_names that have the same id,
593  * else delete specific monitor.
594  */
595 void
596 delete_onemon(mon_name, my_idp, monitor_q)
597 	char *mon_name;
598 	my_id *my_idp;
599 	mon_entry **monitor_q;
600 {
601 
602 	mon_entry *next, *nl;
603 	my_id *nl_idp;
604 
605 	next = *monitor_q;
606 	while ((nl = next) != (struct mon_entry *)NULL) {
607 		next = next->nxt;
608 		if (mon_name == (char *)NULL || (mon_name != (char *)NULL &&
609 			str_cmp_unqual_hostname(nl->id.mon_id.mon_name,
610 			mon_name) == 0)) {
611 			nl_idp = &nl->id.mon_id.my_id;
612 			if ((str_cmp_unqual_hostname(my_idp->my_name,
613 					nl_idp->my_name) == 0) &&
614 				my_idp->my_prog == nl_idp->my_prog &&
615 				my_idp->my_vers == nl_idp->my_vers &&
616 				my_idp->my_proc == nl_idp->my_proc) {
617 				/* found */
618 				if (debug)
619 					(void) printf("delete_mon(%x): %s\n",
620 							(int)nl, mon_name ?
621 							mon_name : "<NULL>");
622 				/*
623 				 * Remove the monitor name from the
624 				 * record_q, if id matches.
625 				 */
626 				record_name(nl->id.mon_id.mon_name, 0);
627 				/* if nl is not the first entry on list */
628 				if (nl->prev != (struct mon_entry *)NULL)
629 					nl->prev->nxt = nl->nxt;
630 				else {
631 					*monitor_q = nl->nxt;
632 				}
633 				if (nl->nxt != (struct mon_entry *)NULL)
634 					nl->nxt->prev = nl->prev;
635 				free(nl->id.mon_id.mon_name);
636 				free(nl_idp->my_name);
637 				free(nl);
638 			}
639 		} /* end of if mon */
640 	}
641 
642 }
643 /*
644  * Notify lockd of host specified by mon_name that the specified state
645  * has changed.
646  */
647 static void
648 send_notice(mon_name, state)
649 	char *mon_name;
650 	int state;
651 {
652 	struct mon_entry *next;
653 	mon_entry *monitor_q;
654 	unsigned int hash;
655 	moninfo_t *minfop;
656 	mon *monp;
657 
658 	SMHASH(mon_name, hash);
659 	mutex_lock(&mon_table[hash].lock);
660 	monitor_q = mon_table[hash].sm_monhdp;
661 
662 	next = monitor_q;
663 	while (next != (struct mon_entry *)NULL) {
664 		if (hostname_eq(next->id.mon_id.mon_name, mon_name)) {
665 			monp = &next->id;
666 			/*
667 			 * Prepare the minfop structure to pass to
668 			 * thr_create(). This structure is a copy of
669 			 * mon info and state.
670 			 */
671 			if ((minfop =
672 				(moninfo_t *)xmalloc(sizeof (moninfo_t))) !=
673 				(moninfo_t *)NULL) {
674 				(void) memcpy(&minfop->id, monp, sizeof (mon));
675 				/* Allocate entry for mon_name */
676 				if ((minfop->id.mon_id.mon_name =
677 					strdup(monp->mon_id.mon_name)) == 0) {
678 					syslog(LOG_ERR,
679 			"statd: send_notice: malloc error on mon %s (id=%d)\n",
680 						monp->mon_id.mon_name,
681 						* ((int *)monp->priv));
682 					free(minfop);
683 					continue;
684 				}
685 				/* Allocate entry for my_name */
686 				if ((minfop->id.mon_id.my_id.my_name =
687 				strdup(monp->mon_id.my_id.my_name)) == 0) {
688 					syslog(LOG_ERR,
689 			"statd: send_notice: malloc error on mon %s (id=%d)\n",
690 						monp->mon_id.mon_name,
691 						* ((int *)monp->priv));
692 					free(minfop->id.mon_id.mon_name);
693 					free(minfop);
694 					continue;
695 				}
696 				minfop->state = state;
697 				/*
698 				 * Create detached threads to process each host
699 				 * to notify.  If error, print out msg, free
700 				 * resources and continue.
701 				 */
702 				if (thr_create(NULL, NULL, thr_send_notice,
703 						(void *)minfop, THR_DETACHED,
704 						NULL)) {
705 				    syslog(LOG_ERR,
706 		"statd: unable to create thread to send_notice to %s.\n",
707 					mon_name);
708 				    free(minfop->id.mon_id.mon_name);
709 				    free(minfop->id.mon_id.my_id.my_name);
710 				    free(minfop);
711 				    continue;
712 				}
713 			}
714 		}
715 		next = next->nxt;
716 	}
717 	mutex_unlock(&mon_table[hash].lock);
718 }
719 
720 /*
721  * Work thread created to do the actual statd_call_lockd
722  */
723 static void *
724 thr_send_notice(void *arg)
725 {
726 	moninfo_t *minfop;
727 
728 	minfop = (moninfo_t *)arg;
729 
730 	if (statd_call_lockd(&minfop->id, minfop->state) == -1) {
731 		if (debug && minfop->id.mon_id.mon_name)
732 			(void) printf(
733 		"problem with notifying %s failure, give up\n",
734 			minfop->id.mon_id.mon_name);
735 	} else {
736 		if (debug)
737 			(void) printf(
738 		"send_notice: %s, %d notified.\n",
739 		minfop->id.mon_id.mon_name, minfop->state);
740 	}
741 
742 	free(minfop->id.mon_id.mon_name);
743 	free(minfop->id.mon_id.my_id.my_name);
744 	free(minfop);
745 
746 	thr_exit((void *) 0);
747 #ifdef lint
748 	/*NOTREACHED*/
749 	return ((void *)0);
750 #endif
751 }
752 
753 /*
754  * Contact lockd specified by monp.
755  */
756 static int
757 statd_call_lockd(monp, state)
758 	mon *monp;
759 	int state;
760 {
761 	enum clnt_stat clnt_stat;
762 	struct timeval tottimeout;
763 	struct status stat;
764 	my_id *my_idp;
765 	char *mon_name;
766 	int i;
767 	int rc = 0;
768 	CLIENT *clnt;
769 
770 	mon_name = monp->mon_id.mon_name;
771 	my_idp = &monp->mon_id.my_id;
772 	(void) memset(&stat, 0, sizeof (struct status));
773 	stat.mon_name = mon_name;
774 	stat.state = state;
775 	for (i = 0; i < 16; i++) {
776 		stat.priv[i] = monp->priv[i];
777 	}
778 	if (debug)
779 		(void) printf("statd_call_lockd: %s state = %d\n",
780 			stat.mon_name, stat.state);
781 
782 	tottimeout.tv_sec = SM_RPC_TIMEOUT;
783 	tottimeout.tv_usec = 0;
784 
785 	if ((clnt = create_client(my_idp->my_name, my_idp->my_prog,
786 		my_idp->my_vers, &tottimeout)) == (CLIENT *) NULL) {
787 			return (-1);
788 	}
789 
790 	clnt_stat = clnt_call(clnt, my_idp->my_proc, xdr_status, (char *)&stat,
791 				xdr_void, NULL, tottimeout);
792 	if (debug) {
793 		(void) printf("clnt_stat=%s(%d)\n",
794 			clnt_sperrno(clnt_stat), clnt_stat);
795 	}
796 	if (clnt_stat != (int)RPC_SUCCESS) {
797 		syslog(LOG_WARNING,
798 			"statd: cannot talk to lockd at %s, %s(%d)\n",
799 			my_idp->my_name, clnt_sperrno(clnt_stat), clnt_stat);
800 		rc = -1;
801 	}
802 
803 	clnt_destroy(clnt);
804 	return (rc);
805 
806 }
807 
808 /*
809  * Client handle created.
810  */
811 CLIENT *
812 create_client(host, prognum, versnum, utimeout)
813 	char	*host;
814 	int	prognum;
815 	int	versnum;
816 	struct timeval	*utimeout;
817 {
818 	int		fd;
819 	struct timeval	timeout;
820 	CLIENT		*client;
821 	struct t_info	tinfo;
822 
823 	if ((client = clnt_create_timed(host, prognum, versnum,
824 			"netpath", utimeout)) == NULL) {
825 		return (NULL);
826 	}
827 	(void) CLNT_CONTROL(client, CLGET_FD, (caddr_t)&fd);
828 	if (t_getinfo(fd, &tinfo) != -1) {
829 		if (tinfo.servtype == T_CLTS) {
830 			/*
831 			 * Set time outs for connectionless case
832 			 */
833 			timeout.tv_usec = 0;
834 			timeout.tv_sec = SM_CLTS_TIMEOUT;
835 			(void) CLNT_CONTROL(client,
836 				CLSET_RETRY_TIMEOUT, (caddr_t)&timeout);
837 		}
838 	} else
839 		return (NULL);
840 
841 	return (client);
842 }
843 
844 /*
845  * ONLY for debugging.
846  * Debug messages which prints out the monitor table information.
847  * If name is specified, just print out the hash list corresponding
848  * to name, otherwise print out the entire monitor table.
849  */
850 static void
851 pr_mon(name)
852 	char *name;
853 {
854 	mon_entry *nl;
855 	int hash;
856 
857 	if (!debug)
858 		return;
859 
860 	/* print all */
861 	if (name == NULL) {
862 		for (hash = 0; hash < MAX_HASHSIZE; hash++) {
863 			mutex_lock(&mon_table[hash].lock);
864 			nl = mon_table[hash].sm_monhdp;
865 			if (nl == (struct mon_entry *)NULL) {
866 				(void) printf(
867 					"*****monitor_q = NULL hash %d\n",
868 					hash);
869 				mutex_unlock(&mon_table[hash].lock);
870 				continue;
871 			}
872 			(void) printf("*****monitor_q:\n ");
873 			while (nl != (mon_entry *)NULL) {
874 				(void) printf("%s:(%x), ",
875 					nl->id.mon_id.mon_name, (int)nl);
876 				nl = nl->nxt;
877 			}
878 			mutex_unlock(&mon_table[hash].lock);
879 			(void) printf("\n");
880 		}
881 	} else { /* print one hash list */
882 		SMHASH(name, hash);
883 		mutex_lock(&mon_table[hash].lock);
884 		nl = mon_table[hash].sm_monhdp;
885 		if (nl == (struct mon_entry *)NULL) {
886 			(void) printf("*****monitor_q = NULL hash %d\n", hash);
887 		} else {
888 			(void) printf("*****monitor_q:\n ");
889 			while (nl != (mon_entry *)NULL) {
890 				(void) printf("%s:(%x), ",
891 					nl->id.mon_id.mon_name, (int)nl);
892 				nl = nl->nxt;
893 			}
894 			(void) printf("\n");
895 		}
896 		mutex_unlock(&mon_table[hash].lock);
897 	}
898 }
899 
900 /*
901  * Only for debugging.
902  * Dump the host name-to-address translation table passed in `name_addr'.
903  */
904 static void
905 pr_name_addr(name_addr_entry_t *name_addr)
906 {
907 	name_addr_entry_t *entry;
908 	addr_entry_t *addr;
909 	struct in_addr ipv4_addr;
910 	char *ipv6_addr;
911 	char abuf[INET6_ADDRSTRLEN];
912 
913 	assert(MUTEX_HELD(&name_addrlock));
914 	(void) printf("name-to-address translation table:\n");
915 	for (entry = name_addr; entry != NULL; entry = entry->next) {
916 		(void) printf("\t%s: ",
917 				(entry->name ? entry->name : "(null)"));
918 		for (addr = entry->addresses; addr; addr = addr->next) {
919 			switch (addr->family) {
920 				case AF_INET:
921 					ipv4_addr = *(struct in_addr *)addr->ah.
922 n_bytes;
923 					(void) printf(" %s (fam %d)",
924 							inet_ntoa(ipv4_addr),
925 							addr->family);
926 					break;
927 				case AF_INET6:
928 					ipv6_addr = (char *)addr->ah.n_bytes;
929 					(void) printf(" %s (fam %d)",
930 							inet_ntop(addr->family,
931 ipv6_addr, abuf, sizeof (abuf)), addr->family);
932 					break;
933 				default:
934 					return;
935 			}
936 		}
937 		printf("\n");
938 	}
939 }
940 
941 /*
942  * Statd has trouble dealing with hostname aliases because two
943  * different aliases for the same machine don't match each other
944  * when using strcmp.  To deal with this, the hostnames must be
945  * translated into some sort of universal identifier.  These
946  * identifiers can be compared.  Universal network addresses are
947  * currently used for this identifier because it is general and
948  * easy to do.  Other schemes are possible and this routine
949  * could be converted if required.
950  *
951  * If it can't find an address for some reason, 0 is returned.
952  */
953 static int
954 hostname_eq(char *host1, char *host2)
955 {
956 	char *sysid1;
957 	char *sysid2;
958 	int rv;
959 
960 	sysid1 = get_system_id(host1);
961 	sysid2 = get_system_id(host2);
962 	if ((sysid1 == NULL) || (sysid2 == NULL))
963 		rv = 0;
964 	else
965 		rv = (strcmp(sysid1, sysid2) == 0);
966 	free(sysid1);
967 	free(sysid2);
968 	return (rv);
969 }
970 
971 /*
972  * Convert a hostname character string into its network address.
973  * A network address is found by searching through all the entries
974  * in /etc/netconfig and doing a netdir_getbyname() for each inet
975  * entry found.  The netbuf structure returned is converted into
976  * a universal address format.
977  *
978  * If a NULL hostname is given, then the name of the current host
979  * is used.  If the hostname doesn't map to an address, a NULL
980  * pointer is returned.
981  *
982  * N.B. the character string returned is allocated in taddr2uaddr()
983  * and should be freed by the caller using free().
984  */
985 static char *
986 get_system_id(char *hostname)
987 {
988 	void *hp;
989 	struct netconfig *ncp;
990 	struct nd_hostserv service;
991 	struct nd_addrlist *addrs;
992 	char *uaddr;
993 	int rv;
994 
995 	if (hostname == NULL)
996 		service.h_host = HOST_SELF;
997 	else
998 		service.h_host = hostname;
999 	service.h_serv = NULL;
1000 	hp = setnetconfig();
1001 	if (hp == (void *) NULL) {
1002 		return (NULL);
1003 	}
1004 	while ((ncp = getnetconfig(hp)) != (struct netconfig *)NULL) {
1005 		if ((strcmp(ncp->nc_protofmly, NC_INET) == 0) ||
1006 			(strcmp(ncp->nc_protofmly, NC_INET6) == 0)) {
1007 			addrs = NULL;
1008 			rv = netdir_getbyname(ncp, &service, &addrs);
1009 			if (rv != 0) {
1010 				continue;
1011 			}
1012 			if (addrs) {
1013 				uaddr = taddr2uaddr(ncp, addrs->n_addrs);
1014 				netdir_free(addrs, ND_ADDRLIST);
1015 				endnetconfig(hp);
1016 				return (uaddr);
1017 			}
1018 		}
1019 		else
1020 			continue;
1021 	}
1022 	endnetconfig(hp);
1023 	return (NULL);
1024 }
1025 
1026 void
1027 merge_hosts(void)
1028 {
1029 	struct lifconf *lifc = NULL;
1030 	int sock = -1;
1031 	struct lifreq *lifrp;
1032 	struct lifreq lifr;
1033 	int n;
1034 	struct sockaddr_in *sin;
1035 	struct sockaddr_in6 *sin6;
1036 	struct sockaddr_storage *sa;
1037 	int af;
1038 	struct hostent *phost;
1039 	char *addr;
1040 	size_t alen;
1041 	int errnum;
1042 
1043 	/*
1044 	 * This function will enumerate all the interfaces for
1045 	 * this platform, then get the hostent for each i/f.
1046 	 * With the hostent structure, we can get all of the
1047 	 * aliases for the i/f. Then we'll merge all the aliases
1048 	 * with the existing host_name[] list to come up with
1049 	 * all of the known names for each interface. This solves
1050 	 * the problem of a multi-homed host not knowing which
1051 	 * name to publish when statd is started. All the aliases
1052 	 * will be stored in the array, host_name.
1053 	 *
1054 	 * NOTE: Even though we will use all of the aliases we
1055 	 * can get from the i/f hostent, the receiving statd
1056 	 * will still need to handle aliases with hostname_eq.
1057 	 * This is because the sender's aliases may not match
1058 	 * those of the receiver.
1059 	 */
1060 	lifc = getmyaddrs();
1061 	if (lifc == (struct lifconf *)NULL) {
1062 		goto finish;
1063 	}
1064 	lifrp = lifc->lifc_req;
1065 	for (n = lifc->lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
1066 
1067 		(void) strncpy(lifr.lifr_name, lifrp->lifr_name,
1068 				sizeof (lifr.lifr_name));
1069 
1070 		af = lifrp->lifr_addr.ss_family;
1071 		sock = socket(af, SOCK_DGRAM, 0);
1072 		if (sock == -1) {
1073 			syslog(LOG_ERR, "statd: socket failed\n");
1074 			goto finish;
1075 		}
1076 
1077 		/* If it's the loopback interface, ignore */
1078 		if (ioctl(sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
1079 			syslog(LOG_ERR,
1080 				"statd: SIOCGLIFFLAGS failed, error: %m\n");
1081 			goto finish;
1082 		}
1083 		if (lifr.lifr_flags & IFF_LOOPBACK)
1084 			continue;
1085 
1086 		if (ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
1087 			syslog(LOG_ERR,
1088 				"statd: SIOCGLIFADDR failed, error: %m\n");
1089 			goto finish;
1090 		}
1091 		sa = (struct sockaddr_storage *)&(lifr.lifr_addr);
1092 
1093 		if (sa->ss_family == AF_INET) {
1094 			sin = (struct sockaddr_in *)&lifr.lifr_addr;
1095 			addr = (char *)(&sin->sin_addr);
1096 			alen = sizeof (struct in_addr);
1097 		} else if (sa->ss_family == AF_INET6) {
1098 			sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
1099 			addr = (char *)(&sin6->sin6_addr);
1100 			alen = sizeof (struct in6_addr);
1101 		} else {
1102 			syslog(LOG_WARNING,
1103 			    "unexpected address family (%d)",
1104 			    sa->ss_family);
1105 			continue;
1106 		}
1107 
1108 		phost = getipnodebyaddr(addr, alen, sa->ss_family, &errnum);
1109 
1110 		if (phost)
1111 			add_aliases(phost);
1112 	}
1113 	/*
1114 	 * Now, just in case we didn't get them all byaddr,
1115 	 * let's look by name.
1116 	 */
1117 	phost = getipnodebyname(hostname, AF_INET6, AI_ALL, &errnum);
1118 
1119 	if (phost)
1120 		add_aliases(phost);
1121 
1122 finish:
1123 	if (sock != -1)
1124 		(void) close(sock);
1125 	if (lifc) {
1126 		free(lifc->lifc_buf);
1127 		free(lifc);
1128 	}
1129 }
1130 
1131 /*
1132  * add_aliases traverses a hostent alias list, compares
1133  * the aliases to the contents of host_name, and if an
1134  * alias is not already present, adds it to host_name[].
1135  */
1136 
1137 static void
1138 add_aliases(struct hostent *phost)
1139 {
1140 	char **aliases;
1141 
1142 	if (!in_host_array(phost->h_name)) {
1143 		add_to_host_array(phost->h_name);
1144 	}
1145 
1146 	if (phost->h_aliases == NULL)
1147 		return;			/* no aliases to register */
1148 
1149 	for (aliases = phost->h_aliases; *aliases != NULL; aliases++) {
1150 		if (!in_host_array(*aliases)) {
1151 			add_to_host_array(*aliases);
1152 		}
1153 	}
1154 }
1155 
1156 /*
1157  * in_host_array checks if the given hostname exists in the host_name
1158  * array. Returns 0 if the host doesn't exist, and 1 if it does exist
1159  */
1160 static int
1161 in_host_array(char *host)
1162 {
1163 	int i;
1164 
1165 	if (debug)
1166 		(void) printf("%s ", host);
1167 
1168 	if ((strcmp(hostname, host) == 0) || (strcmp(LOGHOST, host) == 0))
1169 		return (1);
1170 
1171 	for (i = 0; i < addrix; i++) {
1172 		if (strcmp(host_name[i], host) == 0)
1173 			return (1);
1174 	}
1175 
1176 	return (0);
1177 }
1178 
1179 /*
1180  * add_to_host_array adds a hostname to the host_name array. But if
1181  * the array is already full, then it first reallocates the array with
1182  * HOST_NAME_INCR extra elements. If the realloc fails, then it does
1183  * nothing and leaves host_name the way it was previous to the call.
1184  */
1185 static void
1186 add_to_host_array(char *host) {
1187 
1188 	void *new_block = NULL;
1189 
1190 	/* Make sure we don't overrun host_name. */
1191 	if (addrix >= host_name_count) {
1192 		host_name_count += HOST_NAME_INCR;
1193 		new_block = realloc((void *)host_name,
1194 				    host_name_count*sizeof (char *));
1195 		if (new_block != NULL)
1196 			host_name = new_block;
1197 		else {
1198 			host_name_count -= HOST_NAME_INCR;
1199 			return;
1200 		}
1201 	}
1202 
1203 	if ((host_name[addrix] = strdup(host)) != NULL)
1204 		addrix++;
1205 }
1206 
1207 /*
1208  * Compares the unqualified hostnames for hosts. Returns 0 if the
1209  * names match, and 1 if the names fail to match.
1210  */
1211 int
1212 str_cmp_unqual_hostname(char *rawname1, char *rawname2)
1213 {
1214 	size_t unq_len1, unq_len2;
1215 	char *domain;
1216 
1217 	if (debug) {
1218 		(void) printf("str_cmp_unqual: rawname1= %s, rawname2= %s\n",
1219 				rawname1, rawname2);
1220 	}
1221 
1222 	unq_len1 = strcspn(rawname1, ".");
1223 	unq_len2 = strcspn(rawname2, ".");
1224 	domain = strchr(rawname1, '.');
1225 	if (domain != NULL) {
1226 		if ((strncmp(rawname1, SM_ADDR_IPV4, unq_len1) == 0) ||
1227 			(strncmp(rawname1, SM_ADDR_IPV6, unq_len1) == 0))
1228 		return (1);
1229 	}
1230 
1231 	if ((unq_len1 == unq_len2) &&
1232 			(strncmp(rawname1, rawname2, unq_len1) == 0)) {
1233 		return (0);
1234 	}
1235 
1236 	return (1);
1237 }
1238 
1239 /*
1240  * Compares <family>.<address-specifier> ASCII names for hosts.  Returns
1241  * 0 if the addresses match, and 1 if the addresses fail to match.
1242  * If the args are indeed specifiers, they should look like this:
1243  *
1244  *	ipv4.192.9.200.1 or ipv6.::C009:C801
1245  */
1246 int
1247 str_cmp_address_specifier(char *specifier1, char *specifier2)
1248 {
1249 	size_t unq_len1, unq_len2;
1250 	char *rawaddr1, *rawaddr2;
1251 	int af1, af2, len;
1252 
1253 	if (debug) {
1254 		(void) printf("str_cmp_addr: specifier1= %s, specifier2= %s\n",
1255 				specifier1, specifier2);
1256 	}
1257 
1258 	/*
1259 	 * Verify that:
1260 	 *	1. The family tokens match;
1261 	 *	2. The IP addresses following the `.' are legal; and
1262 	 *	3. These addresses match.
1263 	 */
1264 	unq_len1 = strcspn(specifier1, ".");
1265 	unq_len2 = strcspn(specifier2, ".");
1266 	rawaddr1 = strchr(specifier1, '.');
1267 	rawaddr2 = strchr(specifier2, '.');
1268 
1269 	if (strncmp(specifier1, SM_ADDR_IPV4, unq_len1) == 0) {
1270 		af1 = AF_INET;
1271 		len = 4;
1272 	} else if (strncmp(specifier1, SM_ADDR_IPV6, unq_len1) == 0) {
1273 		af1 = AF_INET6;
1274 		len = 16;
1275 	}
1276 	else
1277 		return (1);
1278 
1279 	if (strncmp(specifier2, SM_ADDR_IPV4, unq_len2) == 0)
1280 		af2 = AF_INET;
1281 	else if (strncmp(specifier2, SM_ADDR_IPV6, unq_len2) == 0)
1282 		af2 = AF_INET6;
1283 	else
1284 		return (1);
1285 
1286 	if (af1 != af2)
1287 		return (1);
1288 
1289 	if (rawaddr1 != NULL && rawaddr2 != NULL) {
1290 		char dst1[16];
1291 		char dst2[16];
1292 		++rawaddr1;
1293 		++rawaddr2;
1294 
1295 		if (inet_pton(af1, rawaddr1, dst1) == 1 &&
1296 			inet_pton(af2, rawaddr1, dst2) == 1 &&
1297 			memcmp(dst1, dst2, len) == 0) {
1298 			return (0);
1299 		}
1300 	}
1301 	return (1);
1302 }
1303