sm_svc.c revision 7832:862415b8632b
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
80extern void __use_portmapper(int);
81extern 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 */
89uid_t	daemon_uid = DAEMON_UID;
90gid_t	daemon_gid = DAEMON_GID;
91
92char STATE[MAXPATHLEN], CURRENT[MAXPATHLEN], BACKUP[MAXPATHLEN];
93static char statd_home[MAXPATHLEN];
94
95int debug;
96int regfiles_only = 0;		/* 1 => use symlinks in statmon, 0 => don't */
97char 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 */
104int host_name_count;
105char **host_name; /* store -a opts */
106int  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 */
115char **path_name = NULL;  /* store -p opts */
116int  pathix = 0;  /* # of -p entries */
117
118/* Global variables.  Refer to sm_statd.h for description */
119mutex_t crash_lock;
120int die;
121int in_crash;
122cond_t crash_finish;
123mutex_t sm_trylock;
124rwlock_t thr_rwlock;
125cond_t retrywait;
126mutex_t name_addrlock;
127
128/* forward references */
129static void set_statmon_owner(void);
130static void copy_client_names(void);
131static void one_statmon_owner(const char *);
132static 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
185static void
186sm_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 */
298static int
299remove_dir(path_dir)
300char *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 */
340void
341copydir_from_to(from_dir, to_dir)
342char *from_dir;
343char *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
399static int
400init_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
432int
433main(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
694static void
695set_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
730static void
731copy_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
775static void
776one_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*/
803static int
804nftw_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