Home | History | Annotate | Download | only in statd
      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