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