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