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 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 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 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 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 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 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 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 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 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 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