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