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