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