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.
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.
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]
23 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
41 * Copyright (c) 2012 by Delphix. All rights reserved.
45 #include <stdio_ext.h>
51 #include <netconfig.h>
55 #include <netinet/in.h>
56 #include <sys/param.h>
57 #include <sys/resource.h>
59 #include <sys/types.h>
61 #include <sys/sockio.h>
64 #include <rpcsvc/sm_inter.h>
65 #include <rpcsvc/nsm_addr.h>
70 #include <rpcsvc/daemon_utils.h>
71 #include <priv_utils.h>
75 #define home0 "/var/statmon"
76 #define current0 "/var/statmon/sm"
77 #define backup0 "/var/statmon/sm.bak"
78 #define state0 "/var/statmon/state"
80 #define home1 "statmon"
81 #define current1 "statmon/sm/"
82 #define backup1 "statmon/sm.bak/"
83 #define state1 "statmon/state"
85 extern int daemonize_init(void);
86 extern void daemonize_fini(int fd
);
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
94 uid_t daemon_uid
= DAEMON_UID
;
95 gid_t daemon_gid
= DAEMON_GID
;
97 char STATE
[MAXPATHLEN
], CURRENT
[MAXPATHLEN
], BACKUP
[MAXPATHLEN
];
98 static char statd_home
[MAXPATHLEN
];
101 int regfiles_only
= 0; /* 1 => use symlinks in statmon, 0 => don't */
102 char hostname
[MAXHOSTNAMELEN
];
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.
110 char **host_name
; /* store -a opts */
111 int addrix
; /* # of -a entries */
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.
120 char **path_name
= NULL
; /* store -p opts */
121 int pathix
= 0; /* # of -p entries */
123 /* Global variables. Refer to sm_statd.h for description */
130 mutex_t name_addrlock
;
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
*);
146 * returns stat_fail to caller
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.
154 * removes an entry from the monitor_q and the record_q
156 * removes all entries from a particular host from the
157 * monitor_q and the record_q. Our statd has this
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.
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.
171 * There are three main hash tables used to keep track of things.
173 * table that keeps track hosts statd must watch. If one of
174 * these hosts crashes, then any locks held by that host must
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
183 * used to keep track hostnames during a recovery
185 * The entries are hashed based upon the name.
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
194 sm_prog_1(struct svc_req
*rqstp
, SVCXPRT
*transp
)
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
;
206 sm_stat_res stat_resp
;
208 struct reg1res reg1_resp
;
211 bool_t (*xdr_argument
)(), (*xdr_result
)();
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.
220 if (rqstp
->rq_prog
== NSM_ADDR_PROGRAM
) {
221 switch (rqstp
->rq_proc
) {
223 svc_sendreply(transp
, xdr_void
, (caddr_t
)NULL
);
226 case NSMADDRPROC1_REG
:
227 xdr_argument
= xdr_reg1args
;
228 xdr_result
= xdr_reg1res
;
229 local
= (char *(*)()) nsmaddrproc1_reg
;
232 case NSMADDRPROC1_UNREG
: /* Not impl. */
234 svcerr_noproc(transp
);
238 /* Must be SM_PROG */
239 switch (rqstp
->rq_proc
) {
241 svc_sendreply(transp
, xdr_void
, (caddr_t
)NULL
);
245 xdr_argument
= xdr_sm_name
;
246 xdr_result
= xdr_sm_stat_res
;
247 local
= (char *(*)()) sm_stat_svc
;
251 xdr_argument
= xdr_mon
;
252 xdr_result
= xdr_sm_stat_res
;
253 local
= (char *(*)()) sm_mon_svc
;
257 xdr_argument
= xdr_mon_id
;
258 xdr_result
= xdr_sm_stat
;
259 local
= (char *(*)()) sm_unmon_svc
;
263 xdr_argument
= xdr_my_id
;
264 xdr_result
= xdr_sm_stat
;
265 local
= (char *(*)()) sm_unmon_all_svc
;
269 xdr_argument
= xdr_void
;
270 xdr_result
= xdr_void
;
271 local
= (char *(*)()) sm_simu_crash_svc
;
275 xdr_argument
= xdr_stat_chge
;
276 xdr_result
= xdr_void
;
277 local
= (char *(*)()) sm_notify_svc
;
281 svcerr_noproc(transp
);
286 (void) memset(&argument
, 0, sizeof (argument
));
287 if (!svc_getargs(transp
, xdr_argument
, (caddr_t
)&argument
)) {
288 svcerr_decode(transp
);
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
);
298 if (!svc_freeargs(transp
, xdr_argument
, (caddr_t
)&argument
)) {
299 syslog(LOG_ERR
, "statd: unable to free arguments\n");
304 * Remove all files under directory path_dir.
307 remove_dir(char *path_dir
)
311 char tmp_path
[MAXPATHLEN
];
313 if ((dp
= opendir(path_dir
)) == NULL
) {
316 "warning: open directory %s failed: %m\n",
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 >
327 syslog(LOG_ERR
, "statd: remove dir %s/%s "
328 "failed. Pathname too long.\n", path_dir
,
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
);
345 * Copy all files from directory `from_dir' to directory `to_dir'.
346 * Symlinks, if any, are preserved.
349 copydir_from_to(char *from_dir
, char *to_dir
)
354 char rname
[MAXNAMELEN
+ 1];
355 char path
[MAXPATHLEN
+MAXNAMELEN
+2];
357 if ((dp
= opendir(from_dir
)) == NULL
) {
360 "warning: open directory %s failed: %m\n",
365 while ((dirp
= readdir(dp
)) != NULL
) {
366 if (strcmp(dirp
->d_name
, ".") == 0 ||
367 strcmp(dirp
->d_name
, "..") == 0) {
371 (void) strcpy(path
, from_dir
);
372 (void) strcat(path
, "/");
373 (void) strcat(path
, dirp
->d_name
);
375 if (is_symlink(path
)) {
377 * Follow the link to get the referenced file name
378 * and make a new link for that file in to_dir.
380 n
= readlink(path
, rname
, MAXNAMELEN
);
383 (void) printf("copydir_from_to: can't "
384 "read link %s\n", path
);
390 (void) create_symlink(to_dir
, rname
, dirp
->d_name
);
393 * Simply copy regular files to to_dir.
395 (void) strcpy(path
, to_dir
);
396 (void) strcat(path
, "/");
397 (void) strcat(path
, dirp
->d_name
);
398 (void) create_file(path
);
411 if ((sock
= socket(AF_INET6
, SOCK_DGRAM
, 0)) < 0) {
412 syslog(LOG_ERR
, "statd:init_hostname, socket: %m");
416 lifn
.lifn_family
= AF_UNSPEC
;
419 if (ioctl(sock
, SIOCGLIFNUM
, (char *)&lifn
) < 0) {
421 "statd:init_hostname, get number of interfaces, error: %m");
426 host_name_count
= lifn
.lifn_count
;
428 host_name
= malloc(host_name_count
* sizeof (char *));
429 if (host_name
== NULL
) {
430 perror("statd -a can't get ip configuration\n");
439 thr_statd_merges(void)
442 * Get other aliases from each interface.
447 * Get all of the configured IP addresses.
452 * Notify the waiters.
454 (void) mutex_lock(&merges_lock
);
456 (void) cond_broadcast(&merges_cond
);
457 (void) mutex_unlock(&merges_lock
);
461 main(int argc
, char *argv
[])
471 int connmaxrec
= RPC_MAXDATASIZE
;
476 (void) gethostname(hostname
, MAXHOSTNAMELEN
);
477 if (init_hostname() < 0)
480 while ((c
= getopt(argc
, argv
, "Dd:a:G:p:rU:")) != EOF
)
483 (void) sscanf(optarg
, "%d", &debug
);
489 if (addrix
< host_name_count
) {
490 if (strcmp(hostname
, optarg
) != 0) {
492 if (sz
< MAXHOSTNAMELEN
) {
494 (char *)xmalloc(sz
+1);
495 if (host_name
[addrix
] !=
497 (void) sscanf(optarg
, "%s",
502 (void) fprintf(stderr
,
503 "statd: -a name of host is too long.\n");
506 (void) fprintf(stderr
,
507 "statd: -a exceeding maximum hostnames\n");
510 (void) sscanf(optarg
, "%d", &daemon_uid
);
513 (void) sscanf(optarg
, "%d", &daemon_gid
);
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 *);
530 path_name
= (char **)malloc(sz
);
531 if (path_name
== NULL
) {
532 (void) fprintf(stderr
,
533 "statd: malloc failed\n");
536 (void) memset(path_name
, 0, sz
);
538 path_name
[pathix
] = optarg
;
541 (void) fprintf(stderr
,
542 "statd: -p pathname is too long.\n");
549 (void) fprintf(stderr
,
550 "statd [-d level] [-D]\n");
555 (void) strcpy(statd_home
, home0
);
556 (void) strcpy(CURRENT
, current0
);
557 (void) strcpy(BACKUP
, backup0
);
558 (void) strcpy(STATE
, state0
);
560 (void) strcpy(statd_home
, home1
);
561 (void) strcpy(CURRENT
, current1
);
562 (void) strcpy(BACKUP
, backup1
);
563 (void) strcpy(STATE
, state1
);
566 (void) printf("debug is on, create entry: %s, %s, %s\n",
567 CURRENT
, BACKUP
, STATE
);
569 if (getrlimit(RLIMIT_NOFILE
, &rl
))
570 (void) printf("statd: getrlimit failed. \n");
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",
578 (void) enable_extended_FILE_stdio(-1, -1);
581 pipe_fd
= daemonize_init();
583 openlog("statd", LOG_PID
, LOG_DAEMON
);
586 (void) _create_daemon_lock(STATD
, daemon_uid
, daemon_gid
);
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.
592 ppid
= _enter_daemon_lock(STATD
);
597 syslog(LOG_ERR
, "error locking for %s: %s", STATD
,
601 /* daemon was already running */
605 mutex_init(&merges_lock
, USYNC_THREAD
, NULL
);
606 cond_init(&merges_cond
, USYNC_THREAD
, NULL
);
610 * Create thr_statd_merges() thread to populate the host_name list
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().");
621 * Set to automatic mode such that threads are automatically
624 mode
= RPC_SVC_MT_AUTO
;
625 if (!rpc_control(RPC_SVC_MTMODE_SET
, &mode
)) {
627 "statd:unable to set automatic MT mode.");
632 * Set non-blocking mode and maximum record size for
633 * connection oriented RPC transports.
635 if (!rpc_control(RPC_SVC_CONNMAXREC_SET
, &connmaxrec
)) {
636 syslog(LOG_INFO
, "unable to set maximum RPC record size");
639 if (!svc_create(sm_prog_1
, SM_PROG
, SM_VERS
, "netpath")) {
640 syslog(LOG_ERR
, "statd: unable to create (SM_PROG, SM_VERS) "
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.");
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.
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.
671 if (chdir(statd_home
) < 0) {
672 syslog(LOG_ERR
, "can't chdir %s: %m", statd_home
);
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
);
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.
694 * statd is up and running as far as we are concerned.
696 daemonize_fini(pipe_fd
);
699 (void) printf("Starting svc_run\n");
701 syslog(LOG_ERR
, "statd: svc_run returned\n");
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
718 set_statmon_owner(void)
721 boolean_t can_do_mlp
;
724 * Recursively chown/chgrp /var/statmon and the alternate paths,
725 * creating them if necessary.
727 one_statmon_owner(statd_home
);
728 for (i
= 0; i
< pathix
; i
++) {
729 char alt_path
[MAXPATHLEN
];
731 snprintf(alt_path
, MAXPATHLEN
, "%s/statmon", path_name
[i
]);
732 one_statmon_owner(alt_path
);
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
,
739 syslog(LOG_ERR
, "can't run unprivileged: %m");
743 __fini_daemon_priv(PRIV_PROC_EXEC
, PRIV_PROC_SESSION
,
744 PRIV_FILE_LINK_ANY
, PRIV_PROC_INFO
, NULL
);
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.
754 copy_client_names(void)
757 char buf
[MAXPATHLEN
+SM_MAXPATHLEN
];
760 * Copy all clients from alternate paths to /var/statmon/sm
761 * Remove the files in alternate directory when copying is done.
763 for (i
= 0; i
< pathix
; i
++) {
765 * If the alternate directories do not exist, create it.
766 * If they do exist, just do the copy.
768 snprintf(buf
, sizeof (buf
), "%s/statmon/sm", path_name
[i
]);
769 if ((mkdir(buf
, SM_DIRECTORY_MODE
)) == -1) {
770 if (errno
!= EEXIST
) {
772 "can't mkdir %s: %m\n", buf
);
775 copydir_from_to(buf
, CURRENT
);
776 (void) remove_dir(buf
);
779 (void) snprintf(buf
, sizeof (buf
), "%s/statmon/sm.bak",
781 if ((mkdir(buf
, SM_DIRECTORY_MODE
)) == -1) {
782 if (errno
!= EEXIST
) {
784 "can't mkdir %s: %m\n", buf
);
787 copydir_from_to(buf
, BACKUP
);
788 (void) remove_dir(buf
);
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.
799 one_statmon_owner(const char *dir
)
801 if ((mkdir(dir
, SM_DIRECTORY_MODE
)) == -1) {
802 if (errno
!= EEXIST
) {
803 syslog(LOG_ERR
, "can't mkdir %s: %m",
810 printf("Setting owner for %s\n", dir
);
812 if (nftw(dir
, nftw_owner
, MAX_FDS
, FTW_PHYS
) != 0) {
813 syslog(LOG_WARNING
, "error setting owner for %s: %m",
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
827 nftw_owner(const char *path
, const struct stat
*statp
, int info
,
830 if (!(info
== FTW_F
|| info
== FTW_D
))
834 * Some older systems might have mode 777 directories. Fix that.
837 if (info
== FTW_D
&& (statp
->st_mode
& (S_IWGRP
| S_IWOTH
)) != 0) {
838 mode_t newmode
= (statp
->st_mode
& ~(S_IWGRP
| S_IWOTH
)) &
842 printf("chmod %03o %s\n", newmode
, path
);
843 if (chmod(path
, newmode
) < 0) {
846 syslog(LOG_WARNING
, "can't chmod %s to %03o: %m",
849 printf(" FAILED: %s\n", strerror(error
));
853 /* If already owned by daemon, don't bother changing. */
854 if (statp
->st_uid
== daemon_uid
&&
855 statp
->st_gid
== daemon_gid
)
859 printf("lchown %s daemon:daemon\n", path
);
860 if (lchown(path
, daemon_uid
, daemon_gid
) < 0) {
863 syslog(LOG_WARNING
, "can't chown %s to daemon: %m",
866 printf(" FAILED: %s\n", strerror(error
));