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