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