xref: /illumos-gate/usr/src/cmd/fs.d/nfs/statd/sm_svc.c (revision 79492562b32b5e6bc03e14ad2b51f986335f3709)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
25  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved	*/
30 
31 /*
32  * University Copyright- Copyright (c) 1982, 1986, 1988
33  * The Regents of the University of California
34  * All Rights Reserved
35  *
36  * University Acknowledgment- Portions of this document are derived from
37  * software developed by the University of California, Berkeley, and its
38  * contributors.
39  */
40 
41 #include <stdio.h>
42 #include <stdio_ext.h>
43 #include <stdlib.h>
44 #include <ftw.h>
45 #include <signal.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <netconfig.h>
49 #include <netdir.h>
50 #include <unistd.h>
51 #include <netdb.h>
52 #include <rpc/rpc.h>
53 #include <rpc/svc.h>
54 #include <netinet/in.h>
55 #include <sys/param.h>
56 #include <sys/resource.h>
57 #include <sys/file.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <sys/sockio.h>
61 #include <dirent.h>
62 #include <errno.h>
63 #include <rpcsvc/sm_inter.h>
64 #include <rpcsvc/nsm_addr.h>
65 #include <thread.h>
66 #include <synch.h>
67 #include <net/if.h>
68 #include <limits.h>
69 #include <rpcsvc/daemon_utils.h>
70 #include <priv_utils.h>
71 #include "smfcfg.h"
72 #include "sm_statd.h"
73 
74 
75 #define	home0		"/var/statmon"
76 #define	current0	"/var/statmon/sm"
77 #define	backup0		"/var/statmon/sm.bak"
78 #define	state0		"/var/statmon/state"
79 
80 #define	home1		"statmon"
81 #define	current1	"statmon/sm/"
82 #define	backup1		"statmon/sm.bak/"
83 #define	state1		"statmon/state"
84 
85 extern int	daemonize_init(void);
86 extern void	daemonize_fini(int fd);
87 
88 /*
89  * User and group IDs to run as.  These are hardwired, rather than looked
90  * up at runtime, because they are very unlikely to change and because they
91  * provide some protection against bogus changes to the passwd and group
92  * files.
93  */
94 uid_t	daemon_uid = DAEMON_UID;
95 gid_t	daemon_gid = DAEMON_GID;
96 
97 char STATE[MAXPATHLEN], CURRENT[MAXPATHLEN], BACKUP[MAXPATHLEN];
98 static char statd_home[MAXPATHLEN];
99 
100 int debug;
101 int regfiles_only = 0;		/* 1 => use symlinks in statmon, 0 => don't */
102 int statd_port = 0;
103 char hostname[MAXHOSTNAMELEN];
104 
105 /*
106  * These variables will be used to store all the
107  * alias names for the host, as well as the -a
108  * command line hostnames.
109  */
110 int host_name_count;
111 char **host_name; /* store -a opts */
112 int  addrix; /* # of -a entries */
113 
114 
115 /*
116  * The following 2 variables are meaningful
117  * only under a HA configuration.
118  * The path_name array is dynamically allocated in main() during
119  * command line argument processing for the -p options.
120  */
121 char **path_name = NULL;  /* store -p opts */
122 int  pathix = 0;  /* # of -p entries */
123 
124 /* Global variables.  Refer to sm_statd.h for description */
125 mutex_t crash_lock;
126 int die;
127 int in_crash;
128 mutex_t sm_trylock;
129 rwlock_t thr_rwlock;
130 cond_t retrywait;
131 mutex_t name_addrlock;
132 
133 mutex_t merges_lock;
134 cond_t merges_cond;
135 boolean_t in_merges;
136 
137 /* forward references */
138 static void set_statmon_owner(void);
139 static void copy_client_names(void);
140 static void one_statmon_owner(const char *);
141 static int nftw_owner(const char *, const struct stat *, int, struct FTW *);
142 
143 /*
144  * statd protocol
145  *	commands:
146  *		SM_STAT
147  *			returns stat_fail to caller
148  *		SM_MON
149  *			adds an entry to the monitor_q and the record_q.
150  *			This message is sent by the server lockd to the server
151  *			statd, to indicate that a new client is to be monitored.
152  *			It is also sent by the server lockd to the client statd
153  *			to indicate that a new server is to be monitored.
154  *		SM_UNMON
155  *			removes an entry from the monitor_q and the record_q
156  *		SM_UNMON_ALL
157  *			removes all entries from a particular host from the
158  *			monitor_q and the record_q.  Our statd has this
159  *			disabled.
160  *		SM_SIMU_CRASH
161  *			simulate a crash.  Removes everything from the
162  *			record_q and the recovery_q, then calls statd_init()
163  *			to restart things.  This message is sent by the server
164  *			lockd to the server statd to have all clients notified
165  *			that they should reclaim locks.
166  *		SM_NOTIFY
167  *			Sent by statd on server to statd on client during
168  *			crash recovery.  The client statd passes the info
169  *			to its lockd so it can attempt to reclaim the locks
170  *			held on the server.
171  *
172  * There are three main hash tables used to keep track of things.
173  *	mon_table
174  *		table that keeps track hosts statd must watch.  If one of
175  *		these hosts crashes, then any locks held by that host must
176  *		be released.
177  *	record_table
178  *		used to keep track of all the hostname files stored in
179  *		the directory /var/statmon/sm.  These are client hosts who
180  *		are holding or have held a lock at some point.  Needed
181  *		to determine if a file needs to be created for host in
182  *		/var/statmon/sm.
183  *	recov_q
184  *		used to keep track hostnames during a recovery
185  *
186  * The entries are hashed based upon the name.
187  *
188  * There is a directory /var/statmon/sm which holds a file named
189  * for each host that is holding (or has held) a lock.  This is
190  * used during initialization on startup, or after a simulated
191  * crash.
192  */
193 
194 static void
195 sm_prog_1(struct svc_req *rqstp, SVCXPRT *transp)
196 {
197 	union {
198 		struct sm_name sm_stat_1_arg;
199 		struct mon sm_mon_1_arg;
200 		struct mon_id sm_unmon_1_arg;
201 		struct my_id sm_unmon_all_1_arg;
202 		struct stat_chge ntf_arg;
203 		struct reg1args reg1_arg;
204 	} argument;
205 
206 	union {
207 		sm_stat_res stat_resp;
208 		sm_stat	mon_resp;
209 		struct reg1res reg1_resp;
210 	} result;
211 
212 	bool_t (*xdr_argument)(), (*xdr_result)();
213 	void (*local)(void *, void *);
214 
215 	/*
216 	 * Dispatch according to which protocol is being used:
217 	 *	NSM_ADDR_PROGRAM is the private lockd address
218 	 *		registration protocol.
219 	 *	SM_PROG is the normal statd (NSM) protocol.
220 	 */
221 	if (rqstp->rq_prog == NSM_ADDR_PROGRAM) {
222 		switch (rqstp->rq_proc) {
223 		case NULLPROC:
224 			svc_sendreply(transp, xdr_void, (caddr_t)NULL);
225 			return;
226 
227 		case NSMADDRPROC1_REG:
228 			xdr_argument = xdr_reg1args;
229 			xdr_result = xdr_reg1res;
230 			local = nsmaddrproc1_reg;
231 			break;
232 
233 		case NSMADDRPROC1_UNREG: /* Not impl. */
234 		default:
235 			svcerr_noproc(transp);
236 			return;
237 		}
238 	} else {
239 		/* Must be SM_PROG */
240 		switch (rqstp->rq_proc) {
241 		case NULLPROC:
242 			svc_sendreply(transp, xdr_void, (caddr_t)NULL);
243 			return;
244 
245 		case SM_STAT:
246 			xdr_argument = xdr_sm_name;
247 			xdr_result = xdr_sm_stat_res;
248 			local = sm_stat_svc;
249 			break;
250 
251 		case SM_MON:
252 			xdr_argument = xdr_mon;
253 			xdr_result = xdr_sm_stat_res;
254 			local = sm_mon_svc;
255 			break;
256 
257 		case SM_UNMON:
258 			xdr_argument = xdr_mon_id;
259 			xdr_result = xdr_sm_stat;
260 			local = sm_unmon_svc;
261 			break;
262 
263 		case SM_UNMON_ALL:
264 			xdr_argument = xdr_my_id;
265 			xdr_result = xdr_sm_stat;
266 			local = sm_unmon_all_svc;
267 			break;
268 
269 		case SM_SIMU_CRASH:
270 			xdr_argument = xdr_void;
271 			xdr_result = xdr_void;
272 			local = sm_simu_crash_svc;
273 			break;
274 
275 		case SM_NOTIFY:
276 			xdr_argument = xdr_stat_chge;
277 			xdr_result = xdr_void;
278 			local = sm_notify_svc;
279 			break;
280 
281 		default:
282 			svcerr_noproc(transp);
283 			return;
284 		}
285 	}
286 
287 	(void) memset(&argument, 0, sizeof (argument));
288 	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
289 		svcerr_decode(transp);
290 		return;
291 	}
292 
293 	(void) memset(&result, 0, sizeof (result));
294 	(*local)(&argument, &result);
295 	if (!svc_sendreply(transp, xdr_result, (caddr_t)&result)) {
296 		svcerr_systemerr(transp);
297 	}
298 
299 	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
300 		syslog(LOG_ERR, "statd: unable to free arguments\n");
301 	}
302 }
303 
304 /*
305  * Remove all files under directory path_dir.
306  */
307 static int
308 remove_dir(char *path_dir)
309 {
310 	DIR	*dp;
311 	struct dirent   *dirp;
312 	char tmp_path[MAXPATHLEN];
313 
314 	if ((dp = opendir(path_dir)) == NULL) {
315 		if (debug)
316 			syslog(LOG_ERR,
317 			    "warning: open directory %s failed: %m\n",
318 			    path_dir);
319 		return (1);
320 	}
321 
322 	while ((dirp = readdir(dp)) != NULL) {
323 		if (strcmp(dirp->d_name, ".") != 0 &&
324 		    strcmp(dirp->d_name, "..") != 0) {
325 			if (strlen(path_dir) + strlen(dirp->d_name) +2 >
326 			    MAXPATHLEN) {
327 
328 				syslog(LOG_ERR, "statd: remove dir %s/%s "
329 				    "failed.  Pathname too long.\n", path_dir,
330 				    dirp->d_name);
331 
332 				continue;
333 			}
334 			(void) strcpy(tmp_path, path_dir);
335 			(void) strcat(tmp_path, "/");
336 			(void) strcat(tmp_path, dirp->d_name);
337 			delete_file(tmp_path);
338 		}
339 	}
340 
341 	(void) closedir(dp);
342 	return (0);
343 }
344 
345 /*
346  * Copy all files from directory `from_dir' to directory `to_dir'.
347  * Symlinks, if any, are preserved.
348  */
349 void
350 copydir_from_to(char *from_dir, char *to_dir)
351 {
352 	int	n;
353 	DIR	*dp;
354 	struct dirent   *dirp;
355 	char rname[MAXNAMELEN + 1];
356 	char path[MAXPATHLEN+MAXNAMELEN+2];
357 
358 	if ((dp = opendir(from_dir)) == NULL) {
359 		if (debug)
360 			syslog(LOG_ERR,
361 			    "warning: open directory %s failed: %m\n",
362 			    from_dir);
363 		return;
364 	}
365 
366 	while ((dirp = readdir(dp)) != NULL) {
367 		if (strcmp(dirp->d_name, ".") == 0 ||
368 		    strcmp(dirp->d_name, "..") == 0) {
369 			continue;
370 		}
371 
372 		(void) strcpy(path, from_dir);
373 		(void) strcat(path, "/");
374 		(void) strcat(path, dirp->d_name);
375 
376 		if (is_symlink(path)) {
377 			/*
378 			 * Follow the link to get the referenced file name
379 			 * and make a new link for that file in to_dir.
380 			 */
381 			n = readlink(path, rname, MAXNAMELEN);
382 			if (n <= 0) {
383 				if (debug >= 2) {
384 					(void) printf("copydir_from_to: can't "
385 					    "read link %s\n", path);
386 				}
387 				continue;
388 			}
389 			rname[n] = '\0';
390 
391 			(void) create_symlink(to_dir, rname, dirp->d_name);
392 		} else {
393 			/*
394 			 * Simply copy regular files to to_dir.
395 			 */
396 			(void) strcpy(path, to_dir);
397 			(void) strcat(path, "/");
398 			(void) strcat(path, dirp->d_name);
399 			(void) create_file(path);
400 		}
401 	}
402 
403 	(void) closedir(dp);
404 }
405 
406 static int
407 init_hostname(void)
408 {
409 	struct lifnum lifn;
410 	int sock;
411 
412 	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
413 		syslog(LOG_ERR, "statd:init_hostname, socket: %m");
414 		return (-1);
415 	}
416 
417 	lifn.lifn_family = AF_UNSPEC;
418 	lifn.lifn_flags = 0;
419 
420 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
421 		syslog(LOG_ERR,
422 		"statd:init_hostname, get number of interfaces, error: %m");
423 		close(sock);
424 		return (-1);
425 	}
426 
427 	host_name_count = lifn.lifn_count;
428 
429 	host_name = malloc(host_name_count * sizeof (char *));
430 	if (host_name == NULL) {
431 		perror("statd -a can't get ip configuration\n");
432 		close(sock);
433 		return (-1);
434 	}
435 	close(sock);
436 	return (0);
437 }
438 
439 static void
440 thr_statd_merges(void)
441 {
442 	/*
443 	 * Get other aliases from each interface.
444 	 */
445 	merge_hosts();
446 
447 	/*
448 	 * Get all of the configured IP addresses.
449 	 */
450 	merge_ips();
451 
452 	/*
453 	 * Notify the waiters.
454 	 */
455 	(void) mutex_lock(&merges_lock);
456 	in_merges = B_FALSE;
457 	(void) cond_broadcast(&merges_cond);
458 	(void) mutex_unlock(&merges_lock);
459 }
460 
461 /*
462  * This function is called for each configured network type to
463  * bind and register our RPC service programs.
464  *
465  * On TCP or UDP, we may want to bind SM_PROG on a specific port
466  * (when statd_port is specified) in which case we'll use the
467  * variant of svc_tp_create() that lets us pass a bind address.
468  */
469 static void
470 sm_svc_tp_create(struct netconfig *nconf)
471 {
472 	char port_str[8];
473 	struct nd_hostserv hs;
474 	struct nd_addrlist *al = NULL;
475 	SVCXPRT *xprt = NULL;
476 
477 	/*
478 	 * If statd_port is set and this is an inet transport,
479 	 * bind this service on the specified port.  The TLI way
480 	 * to create such a bind address is netdir_getbyname()
481 	 * with the special "host" HOST_SELF_BIND.  This builds
482 	 * an all-zeros IP address with the specified port.
483 	 */
484 	if (statd_port != 0 &&
485 	    (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
486 	    strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
487 		int err;
488 
489 		snprintf(port_str, sizeof (port_str), "%u",
490 		    (unsigned short)statd_port);
491 
492 		hs.h_host = HOST_SELF_BIND;
493 		hs.h_serv = port_str;
494 		err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
495 		if (err == 0 && al != NULL) {
496 			xprt = svc_tp_create_addr(sm_prog_1, SM_PROG, SM_VERS,
497 			    nconf, al->n_addrs);
498 			netdir_free(al, ND_ADDRLIST);
499 		}
500 		if (xprt == NULL) {
501 			syslog(LOG_ERR, "statd: unable to create "
502 			    "(SM_PROG, SM_VERS) on transport %s (port %d)",
503 			    nconf->nc_netid, statd_port);
504 		}
505 		/* fall-back to default bind */
506 	}
507 	if (xprt == NULL) {
508 		/*
509 		 * Had statd_port=0, or non-inet transport,
510 		 * or the bind to a specific port failed.
511 		 * Do a default bind.
512 		 */
513 		xprt = svc_tp_create(sm_prog_1, SM_PROG, SM_VERS, nconf);
514 	}
515 	if (xprt == NULL) {
516 		syslog(LOG_ERR, "statd: unable to create "
517 		    "(SM_PROG, SM_VERS) for transport %s",
518 		    nconf->nc_netid);
519 		return;
520 	}
521 
522 	/*
523 	 * Also register the NSM_ADDR program on this
524 	 * transport handle (same dispatch function).
525 	 */
526 	if (!svc_reg(xprt, NSM_ADDR_PROGRAM, NSM_ADDR_V1, sm_prog_1, nconf)) {
527 		syslog(LOG_ERR, "statd: failed to register "
528 		    "(NSM_ADDR_PROGRAM, NSM_ADDR_V1) for "
529 		    "netconfig %s", nconf->nc_netid);
530 	}
531 }
532 
533 int
534 main(int argc, char *argv[])
535 {
536 	int c;
537 	int ppid;
538 	extern char *optarg;
539 	int choice = 0;
540 	struct rlimit rl;
541 	int mode;
542 	int sz;
543 	int pipe_fd = -1;
544 	int ret;
545 	int connmaxrec = RPC_MAXDATASIZE;
546 	struct netconfig *nconf;
547 	NCONF_HANDLE *nc;
548 
549 	addrix = 0;
550 	pathix = 0;
551 
552 	(void) gethostname(hostname, MAXHOSTNAMELEN);
553 	if (init_hostname() < 0)
554 		exit(1);
555 
556 	ret = nfs_smf_get_iprop("statd_port", &statd_port,
557 	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, STATD);
558 	if (ret != SA_OK) {
559 		syslog(LOG_ERR, "Reading of statd_port from SMF "
560 		    "failed, using default value");
561 	}
562 
563 	while ((c = getopt(argc, argv, "Dd:a:G:p:P:rU:")) != EOF)
564 		switch (c) {
565 		case 'd':
566 			(void) sscanf(optarg, "%d", &debug);
567 			break;
568 		case 'D':
569 			choice = 1;
570 			break;
571 		case 'a':
572 			if (addrix < host_name_count) {
573 				if (strcmp(hostname, optarg) != 0) {
574 					sz = strlen(optarg);
575 					if (sz < MAXHOSTNAMELEN) {
576 						host_name[addrix] =
577 						    (char *)xmalloc(sz+1);
578 						if (host_name[addrix] !=
579 						    NULL) {
580 						(void) sscanf(optarg, "%s",
581 						    host_name[addrix]);
582 							addrix++;
583 						}
584 					} else
585 					(void) fprintf(stderr,
586 				    "statd: -a name of host is too long.\n");
587 				}
588 			} else
589 				(void) fprintf(stderr,
590 				    "statd: -a exceeding maximum hostnames\n");
591 			break;
592 		case 'U':
593 			(void) sscanf(optarg, "%d", &daemon_uid);
594 			break;
595 		case 'G':
596 			(void) sscanf(optarg, "%d", &daemon_gid);
597 			break;
598 		case 'p':
599 			if (strlen(optarg) < MAXPATHLEN) {
600 				/* If the path_name array has not yet	   */
601 				/* been malloc'ed, do that.  The array	   */
602 				/* should be big enough to hold all of the */
603 				/* -p options we might have.  An upper	   */
604 				/* bound on the number of -p options is	   */
605 				/* argc/2, because each -p option consumes */
606 				/* two arguments.  Here the upper bound	   */
607 				/* is supposing that all the command line  */
608 				/* arguments are -p options, which would   */
609 				/* actually never be the case.		   */
610 				if (path_name == NULL) {
611 					size_t sz = (argc/2) * sizeof (char *);
612 
613 					path_name = (char **)malloc(sz);
614 					if (path_name == NULL) {
615 						(void) fprintf(stderr,
616 						"statd: malloc failed\n");
617 						exit(1);
618 					}
619 					(void) memset(path_name, 0, sz);
620 				}
621 				path_name[pathix] = optarg;
622 				pathix++;
623 			} else {
624 				(void) fprintf(stderr,
625 				    "statd: -p pathname is too long.\n");
626 			}
627 			break;
628 		case 'P':
629 			(void) sscanf(optarg, "%d", &statd_port);
630 			if (statd_port < 1 || statd_port > UINT16_MAX) {
631 				(void) fprintf(stderr,
632 				    "statd: -P port invalid.\n");
633 				statd_port = 0;
634 			}
635 			break;
636 		case 'r':
637 			regfiles_only = 1;
638 			break;
639 		default:
640 			(void) fprintf(stderr,
641 			    "statd [-d level] [-D]\n");
642 			return (1);
643 		}
644 
645 	if (choice == 0) {
646 		(void) strcpy(statd_home, home0);
647 		(void) strcpy(CURRENT, current0);
648 		(void) strcpy(BACKUP, backup0);
649 		(void) strcpy(STATE, state0);
650 	} else {
651 		(void) strcpy(statd_home, home1);
652 		(void) strcpy(CURRENT, current1);
653 		(void) strcpy(BACKUP, backup1);
654 		(void) strcpy(STATE, state1);
655 	}
656 	if (debug)
657 		(void) printf("debug is on, create entry: %s, %s, %s\n",
658 		    CURRENT, BACKUP, STATE);
659 
660 	if (getrlimit(RLIMIT_NOFILE, &rl))
661 		(void) printf("statd: getrlimit failed. \n");
662 
663 	/* Set maxfdlimit current soft limit */
664 	rl.rlim_cur = rl.rlim_max;
665 	if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
666 		syslog(LOG_ERR, "statd: unable to set RLIMIT_NOFILE to %d\n",
667 		    rl.rlim_cur);
668 
669 	(void) enable_extended_FILE_stdio(-1, -1);
670 
671 	if (!debug) {
672 		pipe_fd = daemonize_init();
673 
674 		openlog("statd", LOG_PID, LOG_DAEMON);
675 	}
676 
677 	(void) _create_daemon_lock(STATD, daemon_uid, daemon_gid);
678 	/*
679 	 * establish our lock on the lock file and write our pid to it.
680 	 * exit if some other process holds the lock, or if there's any
681 	 * error in writing/locking the file.
682 	 */
683 	ppid = _enter_daemon_lock(STATD);
684 	switch (ppid) {
685 	case 0:
686 		break;
687 	case -1:
688 		syslog(LOG_ERR, "error locking for %s: %s", STATD,
689 		    strerror(errno));
690 		exit(2);
691 	default:
692 		/* daemon was already running */
693 		exit(0);
694 	}
695 
696 	mutex_init(&merges_lock, USYNC_THREAD, NULL);
697 	cond_init(&merges_cond, USYNC_THREAD, NULL);
698 	in_merges = B_TRUE;
699 
700 	/*
701 	 * Create thr_statd_merges() thread to populate the host_name list
702 	 * asynchronously.
703 	 */
704 	if (thr_create(NULL, 0, (void *(*)(void *))thr_statd_merges, NULL,
705 	    THR_DETACHED, NULL) != 0) {
706 		syslog(LOG_ERR, "statd: unable to create thread for "
707 		    "thr_statd_merges().");
708 		exit(1);
709 	}
710 
711 	/*
712 	 * Set to automatic mode such that threads are automatically
713 	 * created
714 	 */
715 	mode = RPC_SVC_MT_AUTO;
716 	if (!rpc_control(RPC_SVC_MTMODE_SET, &mode)) {
717 		syslog(LOG_ERR,
718 		    "statd:unable to set automatic MT mode.");
719 		exit(1);
720 	}
721 
722 	/*
723 	 * Set non-blocking mode and maximum record size for
724 	 * connection oriented RPC transports.
725 	 */
726 	if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) {
727 		syslog(LOG_INFO, "unable to set maximum RPC record size");
728 	}
729 
730 	/*
731 	 * Enumerate network transports and create service listeners
732 	 * as appropriate for each.
733 	 */
734 	if ((nc = setnetconfig()) == NULL) {
735 		syslog(LOG_ERR, "setnetconfig failed: %m");
736 		return (-1);
737 	}
738 	while ((nconf = getnetconfig(nc)) != NULL) {
739 
740 		/*
741 		 * Skip things like tpi_raw, invisible...
742 		 */
743 		if ((nconf->nc_flag & NC_VISIBLE) == 0)
744 			continue;
745 		if (nconf->nc_semantics != NC_TPI_CLTS &&
746 		    nconf->nc_semantics != NC_TPI_COTS &&
747 		    nconf->nc_semantics != NC_TPI_COTS_ORD)
748 			continue;
749 
750 		sm_svc_tp_create(nconf);
751 	}
752 	(void) endnetconfig(nc);
753 
754 	/*
755 	 * Make sure /var/statmon and any alternate (-p) statmon
756 	 * directories exist and are owned by daemon.  Then change our uid
757 	 * to daemon.  The uid change is to prevent attacks against local
758 	 * daemons that trust any call from a local root process.
759 	 */
760 
761 	set_statmon_owner();
762 
763 	/*
764 	 *
765 	 * statd now runs as a daemon rather than root and can not
766 	 * dump core under / because of the permission. It is
767 	 * important that current working directory of statd be
768 	 * changed to writable directory /var/statmon so that it
769 	 * can dump the core upon the receipt of the signal.
770 	 * One still need to set allow_setid_core to non-zero in
771 	 * /etc/system to get the core dump.
772 	 *
773 	 */
774 
775 	if (chdir(statd_home) < 0) {
776 		syslog(LOG_ERR, "can't chdir %s: %m", statd_home);
777 		exit(1);
778 	}
779 
780 	copy_client_names();
781 
782 	rwlock_init(&thr_rwlock, USYNC_THREAD, NULL);
783 	mutex_init(&crash_lock, USYNC_THREAD, NULL);
784 	mutex_init(&name_addrlock, USYNC_THREAD, NULL);
785 	cond_init(&retrywait, USYNC_THREAD, NULL);
786 	sm_inithash();
787 	die = 0;
788 	/*
789 	 * This variable is set to ensure that an sm_crash
790 	 * request will not be done at the same time
791 	 * when a statd_init is being done, since sm_crash
792 	 * can reset some variables that statd_init will be using.
793 	 */
794 	in_crash = 1;
795 	statd_init();
796 
797 	/*
798 	 * statd is up and running as far as we are concerned.
799 	 */
800 	daemonize_fini(pipe_fd);
801 
802 	if (debug)
803 		(void) printf("Starting svc_run\n");
804 	svc_run();
805 	syslog(LOG_ERR, "statd: svc_run returned\n");
806 	/* NOTREACHED */
807 	thr_exit((void *)1);
808 	return (0);
809 
810 }
811 
812 /*
813  * Make sure the ownership of the statmon directories is correct, then
814  * change our uid to match.  If the top-level directories (/var/statmon, -p
815  * arguments) don't exist, they are created first.  The sm and sm.bak
816  * directories are not created here, but if they already exist, they are
817  * chowned to the correct uid, along with anything else in the
818  * directories.
819  */
820 
821 static void
822 set_statmon_owner(void)
823 {
824 	int i;
825 	boolean_t can_do_mlp;
826 
827 	/*
828 	 * Recursively chown/chgrp /var/statmon and the alternate paths,
829 	 * creating them if necessary.
830 	 */
831 	one_statmon_owner(statd_home);
832 	for (i = 0; i < pathix; i++) {
833 		char alt_path[MAXPATHLEN];
834 
835 		snprintf(alt_path, MAXPATHLEN, "%s/statmon", path_name[i]);
836 		one_statmon_owner(alt_path);
837 	}
838 
839 	can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
840 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
841 	    daemon_uid, daemon_gid, can_do_mlp ? PRIV_NET_BINDMLP : NULL,
842 	    NULL) == -1) {
843 		syslog(LOG_ERR, "can't run unprivileged: %m");
844 		exit(1);
845 	}
846 
847 	__fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_SESSION,
848 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, NULL);
849 }
850 
851 /*
852  * Copy client names from the alternate statmon directories into
853  * /var/statmon.  The top-level (statmon) directories should already
854  * exist, though the sm and sm.bak directories might not.
855  */
856 
857 static void
858 copy_client_names(void)
859 {
860 	int i;
861 	char buf[MAXPATHLEN+SM_MAXPATHLEN];
862 
863 	/*
864 	 * Copy all clients from alternate paths to /var/statmon/sm
865 	 * Remove the files in alternate directory when copying is done.
866 	 */
867 	for (i = 0; i < pathix; i++) {
868 		/*
869 		 * If the alternate directories do not exist, create it.
870 		 * If they do exist, just do the copy.
871 		 */
872 		snprintf(buf, sizeof (buf), "%s/statmon/sm", path_name[i]);
873 		if ((mkdir(buf, SM_DIRECTORY_MODE)) == -1) {
874 			if (errno != EEXIST) {
875 				syslog(LOG_ERR,
876 				    "can't mkdir %s: %m\n", buf);
877 				continue;
878 			}
879 			copydir_from_to(buf, CURRENT);
880 			(void) remove_dir(buf);
881 		}
882 
883 		(void) snprintf(buf, sizeof (buf), "%s/statmon/sm.bak",
884 		    path_name[i]);
885 		if ((mkdir(buf, SM_DIRECTORY_MODE)) == -1) {
886 			if (errno != EEXIST) {
887 				syslog(LOG_ERR,
888 				    "can't mkdir %s: %m\n", buf);
889 				continue;
890 			}
891 			copydir_from_to(buf, BACKUP);
892 			(void) remove_dir(buf);
893 		}
894 	}
895 }
896 
897 /*
898  * Create the given directory if it doesn't already exist.  Set the user
899  * and group to daemon for the directory and anything under it.
900  */
901 
902 static void
903 one_statmon_owner(const char *dir)
904 {
905 	if ((mkdir(dir, SM_DIRECTORY_MODE)) == -1) {
906 		if (errno != EEXIST) {
907 			syslog(LOG_ERR, "can't mkdir %s: %m",
908 			    dir);
909 			return;
910 		}
911 	}
912 
913 	if (debug)
914 		printf("Setting owner for %s\n", dir);
915 
916 	if (nftw(dir, nftw_owner, MAX_FDS, FTW_PHYS) != 0) {
917 		syslog(LOG_WARNING, "error setting owner for %s: %m",
918 		    dir);
919 	}
920 }
921 
922 /*
923  * Set the user and group to daemon for the given file or directory.  If
924  * it's a directory, also makes sure that it is mode 755.
925  * Generates a syslog message but does not return an error if there were
926  * problems.
927  */
928 
929 /*ARGSUSED3*/
930 static int
931 nftw_owner(const char *path, const struct stat *statp, int info,
932     struct FTW *ftw)
933 {
934 	if (!(info == FTW_F || info == FTW_D))
935 		return (0);
936 
937 	/*
938 	 * Some older systems might have mode 777 directories.  Fix that.
939 	 */
940 
941 	if (info == FTW_D && (statp->st_mode & (S_IWGRP | S_IWOTH)) != 0) {
942 		mode_t newmode = (statp->st_mode & ~(S_IWGRP | S_IWOTH)) &
943 		    S_IAMB;
944 
945 		if (debug)
946 			printf("chmod %03o %s\n", newmode, path);
947 		if (chmod(path, newmode) < 0) {
948 			int error = errno;
949 
950 			syslog(LOG_WARNING, "can't chmod %s to %03o: %m",
951 			    path, newmode);
952 			if (debug)
953 				printf("  FAILED: %s\n", strerror(error));
954 		}
955 	}
956 
957 	/* If already owned by daemon, don't bother changing. */
958 	if (statp->st_uid == daemon_uid &&
959 	    statp->st_gid == daemon_gid)
960 		return (0);
961 
962 	if (debug)
963 		printf("lchown %s daemon:daemon\n", path);
964 	if (lchown(path, daemon_uid, daemon_gid) < 0) {
965 		int error = errno;
966 
967 		syslog(LOG_WARNING, "can't chown %s to daemon: %m",
968 		    path);
969 		if (debug)
970 			printf("  FAILED: %s\n", strerror(error));
971 	}
972 
973 	return (0);
974 }
975