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