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