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 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 char *(*local)(); 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 = (char *(*)()) 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 = (char *(*)()) sm_stat_svc; 249 break; 250 251 case SM_MON: 252 xdr_argument = xdr_mon; 253 xdr_result = xdr_sm_stat_res; 254 local = (char *(*)()) sm_mon_svc; 255 break; 256 257 case SM_UNMON: 258 xdr_argument = xdr_mon_id; 259 xdr_result = xdr_sm_stat; 260 local = (char *(*)()) 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 = (char *(*)()) sm_unmon_all_svc; 267 break; 268 269 case SM_SIMU_CRASH: 270 xdr_argument = xdr_void; 271 xdr_result = xdr_void; 272 local = (char *(*)()) sm_simu_crash_svc; 273 break; 274 275 case SM_NOTIFY: 276 xdr_argument = xdr_stat_chge; 277 xdr_result = xdr_void; 278 local = (char *(*)()) 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 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 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 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 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 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 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 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 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 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 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 }