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