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 */
85uid_t	daemon_uid = DAEMON_UID;
86gid_t	daemon_gid = DAEMON_GID;
87
88char STATE[MAXPATHLEN], CURRENT[MAXPATHLEN], BACKUP[MAXPATHLEN];
89static char statd_home[MAXPATHLEN];
90
91int debug;
92int regfiles_only = 0;		/* 1 => use symlinks in statmon, 0 => don't */
93char 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 */
100int host_name_count;
101char **host_name; /* store -a opts */
102int  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 */
111char **path_name = NULL;  /* store -p opts */
112int  pathix = 0;  /* # of -p entries */
113
114/* Global variables.  Refer to sm_statd.h for description */
115mutex_t crash_lock;
116int die;
117int in_crash;
118cond_t crash_finish;
119mutex_t sm_trylock;
120rwlock_t thr_rwlock;
121cond_t retrywait;
122mutex_t name_addrlock;
123
124/* forward references */
125static void set_statmon_owner(void);
126static void copy_client_names(void);
127static void one_statmon_owner(const char *);
128static 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
181static void
182sm_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 */
294static int
295remove_dir(path_dir)
296char *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 */
336void
337copydir_from_to(from_dir, to_dir)
338char *from_dir;
339char *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
395static int
396init_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
428int
429main(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
680static void
681set_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
716static void
717copy_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
761static void
762one_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*/
789static int
790nftw_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