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