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