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