| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| /* All Rights Reserved */ |
| |
| /* |
| * University Copyright- Copyright (c) 1982, 1986, 1988 |
| * The Regents of the University of California |
| * All Rights Reserved |
| * |
| * University Acknowledgment- Portions of this document are derived from |
| * software developed by the University of California, Berkeley, and its |
| * contributors. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ftw.h> |
| #include <signal.h> |
| #include <string.h> |
| #include <syslog.h> |
| #include <netconfig.h> |
| #include <unistd.h> |
| #include <netdb.h> |
| #include <rpc/rpc.h> |
| #include <netinet/in.h> |
| #include <sys/param.h> |
| #include <sys/resource.h> |
| #include <sys/file.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/sockio.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <rpcsvc/sm_inter.h> |
| #include <rpcsvc/nsm_addr.h> |
| #include <thread.h> |
| #include <synch.h> |
| #include <net/if.h> |
| #include <limits.h> |
| #include <rpcsvc/daemon_utils.h> |
| #include <priv_utils.h> |
| #include "sm_statd.h" |
| |
| |
| #define home0 "/var/statmon" |
| #define current0 "/var/statmon/sm" |
| #define backup0 "/var/statmon/sm.bak" |
| #define state0 "/var/statmon/state" |
| |
| #define home1 "statmon" |
| #define current1 "statmon/sm/" |
| #define backup1 "statmon/sm.bak/" |
| #define state1 "statmon/state" |
| |
| /* |
| * User and group IDs to run as. These are hardwired, rather than looked |
| * up at runtime, because they are very unlikely to change and because they |
| * provide some protection against bogus changes to the passwd and group |
| * files. |
| */ |
| |
| char STATE[MAXPATHLEN], CURRENT[MAXPATHLEN], BACKUP[MAXPATHLEN]; |
| static char statd_home[MAXPATHLEN]; |
| |
| int debug; |
| int regfiles_only = 0; /* 1 => use symlinks in statmon, 0 => don't */ |
| char hostname[MAXHOSTNAMELEN]; |
| |
| /* |
| * These variables will be used to store all the |
| * alias names for the host, as well as the -a |
| * command line hostnames. |
| */ |
| int host_name_count; |
| char **host_name; /* store -a opts */ |
| int addrix; /* # of -a entries */ |
| |
| |
| /* |
| * The following 2 variables are meaningful |
| * only under a HA configuration. |
| * The path_name array is dynamically allocated in main() during |
| * command line argument processing for the -p options. |
| */ |
| char **path_name = NULL; /* store -p opts */ |
| int pathix = 0; /* # of -p entries */ |
| |
| /* Global variables. Refer to sm_statd.h for description */ |
| mutex_t crash_lock; |
| int die; |
| int in_crash; |
| cond_t crash_finish; |
| mutex_t sm_trylock; |
| rwlock_t thr_rwlock; |
| cond_t retrywait; |
| mutex_t name_addrlock; |
| |
| /* forward references */ |
| static void set_statmon_owner(void); |
| static void copy_client_names(void); |
| static void one_statmon_owner(const char *); |
| static int nftw_owner(const char *, const struct stat *, int, struct FTW *); |
| |
| /* |
| * statd protocol |
| * commands: |
| * SM_STAT |
| * returns stat_fail to caller |
| * SM_MON |
| * adds an entry to the monitor_q and the record_q |
| * This message is sent by the server lockd to the server |
| * statd, to indicate that a new client is to be monitored. |
| * It is also sent by the server lockd to the client statd |
| * to indicate that a new server is to be monitored. |
| * SM_UNMON |
| * removes an entry from the monitor_q and the record_q |
| * SM_UNMON_ALL |
| * removes all entries from a particular host from the |
| * monitor_q and the record_q. Our statd has this |
| * disabled. |
| * SM_SIMU_CRASH |
| * simulate a crash. removes everything from the |
| * record_q and the recovery_q, then calls statd_init() |
| * to restart things. This message is sent by the server |
| * lockd to the server statd to have all clients notified |
| * that they should reclaim locks. |
| * SM_NOTIFY |
| * Sent by statd on server to statd on client during |
| * crash recovery. The client statd passes the info |
| * to its lockd so it can attempt to reclaim the locks |
| * held on the server. |
| * |
| * There are three main hash tables used to keep track of things. |
| * mon_table |
| * table that keeps track hosts statd must watch. If one of |
| * these hosts crashes, then any locks held by that host must |
| * be released. |
| * record_table |
| * used to keep track of all the hostname files stored in |
| * the directory /var/statmon/sm. These are client hosts who |
| * are holding or have held a lock at some point. Needed |
| * to determine if a file needs to be created for host in |
| * /var/statmon/sm. |
| * recov_q |
| * used to keep track hostnames during a recovery |
| * |
| * The entries are hashed based upon the name. |
| * |
| * There is a directory /var/statmon/sm which holds a file named |
| * for each host that is holding (or has held) a lock. This is |
| * used during initialization on startup, or after a simulated |
| * crash. |
| */ |
| |
| static void |
| sm_prog_1(rqstp, transp) |
| struct svc_req *rqstp; |
| SVCXPRT *transp; |
| { |
| union { |
| struct sm_name sm_stat_1_arg; |
| struct mon sm_mon_1_arg; |
| struct mon_id sm_unmon_1_arg; |
| struct my_id sm_unmon_all_1_arg; |
| struct stat_chge ntf_arg; |
| struct reg1args reg1_arg; |
| } argument; |
| |
| union { |
| sm_stat_res stat_resp; |
| sm_stat mon_resp; |
| struct reg1res reg1_resp; |
| } result; |
| |
| bool_t (*xdr_argument)(), (*xdr_result)(); |
| char *(*local)(); |
| |
| /* |
| * Dispatch according to which protocol is being used: |
| * NSM_ADDR_PROGRAM is the private lockd address |
| * registration protocol. |
| * SM_PROG is the normal statd (NSM) protocol. |
| */ |
| if (rqstp->rq_prog == NSM_ADDR_PROGRAM) { |
| switch (rqstp->rq_proc) { |
| case NULLPROC: |
| svc_sendreply(transp, xdr_void, (caddr_t)NULL); |
| return; |
| |
| case NSMADDRPROC1_REG: |
| xdr_argument = xdr_reg1args; |
| xdr_result = xdr_reg1res; |
| local = (char *(*)()) nsmaddrproc1_reg; |
| break; |
| |
| default: |
| svcerr_noproc(transp); |
| return; |
| } |
| } else { |
| switch (rqstp->rq_proc) { |
| case NULLPROC: |
| svc_sendreply(transp, xdr_void, (caddr_t)NULL); |
| return; |
| |
| case SM_STAT: |
| xdr_argument = xdr_sm_name; |
| xdr_result = xdr_sm_stat_res; |
| local = (char *(*)()) sm_status; |
| break; |
| |
| case SM_MON: |
| xdr_argument = xdr_mon; |
| xdr_result = xdr_sm_stat_res; |
| local = (char *(*)()) sm_mon; |
| break; |
| |
| case SM_UNMON: |
| xdr_argument = xdr_mon_id; |
| xdr_result = xdr_sm_stat; |
| local = (char *(*)()) sm_unmon; |
| break; |
| |
| case SM_UNMON_ALL: |
| xdr_argument = xdr_my_id; |
| xdr_result = xdr_sm_stat; |
| local = (char *(*)()) sm_unmon_all; |
| break; |
| |
| case SM_SIMU_CRASH: |
| xdr_argument = xdr_void; |
| xdr_result = xdr_void; |
| local = (char *(*)()) sm_simu_crash; |
| break; |
| |
| case SM_NOTIFY: |
| xdr_argument = xdr_stat_chge; |
| xdr_result = xdr_void; |
| local = (char *(*)()) sm_notify; |
| break; |
| |
| default: |
| svcerr_noproc(transp); |
| return; |
| } |
| } |
| |
| (void) memset(&argument, 0, sizeof (argument)); |
| if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) { |
| svcerr_decode(transp); |
| return; |
| } |
| |
| (void) memset(&result, 0, sizeof (result)); |
| (*local)(&argument, &result); |
| if (!svc_sendreply(transp, xdr_result, (caddr_t)&result)) { |
| svcerr_systemerr(transp); |
| } |
| |
| if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) { |
| syslog(LOG_ERR, "statd: unable to free arguments\n"); |
| } |
| } |
| |
| /* |
| * Remove all files under directory path_dir. |
| */ |
| static int |
| remove_dir(path_dir) |
| char *path_dir; |
| { |
| DIR *dp; |
| struct dirent *dirp; |
| char tmp_path[MAXPATHLEN]; |
| |
| if ((dp = opendir(path_dir)) == (DIR *)NULL) { |
| if (debug) |
| syslog(LOG_ERR, |
| "warning: open directory %s failed: %m\n", path_dir); |
| return (1); |
| } |
| |
| while ((dirp = readdir(dp)) != NULL) { |
| if (strcmp(dirp->d_name, ".") != 0 && |
| strcmp(dirp->d_name, "..") != 0) { |
| if (strlen(path_dir) + strlen(dirp->d_name) +2 > |
| MAXPATHLEN) { |
| |
| syslog(LOG_ERR, |
| "statd: remove dir %s/%s failed. Pathname too long.\n", |
| path_dir, dirp->d_name); |
| |
| continue; |
| } |
| (void) strcpy(tmp_path, path_dir); |
| (void) strcat(tmp_path, "/"); |
| (void) strcat(tmp_path, dirp->d_name); |
| delete_file(tmp_path); |
| } |
| } |
| |
| (void) closedir(dp); |
| return (0); |
| } |
| |
| /* |
| * Copy all files from directory `from_dir' to directory `to_dir'. |
| * Symlinks, if any, are preserved. |
| */ |
| void |
| copydir_from_to(from_dir, to_dir) |
| char *from_dir; |
| char *to_dir; |
| { |
| int n; |
| DIR *dp; |
| struct dirent *dirp; |
| char rname[MAXNAMELEN + 1]; |
| char path[MAXPATHLEN+MAXNAMELEN+2]; |
| |
| if ((dp = opendir(from_dir)) == (DIR *)NULL) { |
| if (debug) |
| syslog(LOG_ERR, |
| "warning: open directory %s failed: %m\n", from_dir); |
| return; |
| } |
| |
| while ((dirp = readdir(dp)) != NULL) { |
| if (strcmp(dirp->d_name, ".") == 0 || |
| strcmp(dirp->d_name, "..") == 0) { |
| continue; |
| } |
| |
| (void) strcpy(path, from_dir); |
| (void) strcat(path, "/"); |
| (void) strcat(path, dirp->d_name); |
| |
| if (is_symlink(path)) { |
| /* |
| * Follow the link to get the referenced file name |
| * and make a new link for that file in to_dir. |
| */ |
| n = readlink(path, rname, MAXNAMELEN); |
| if (n <= 0) { |
| if (debug >= 2) { |
| (void) printf( |
| "copydir_from_to: can't read link %s\n", |
| path); |
| } |
| continue; |
| } |
| rname[n] = '\0'; |
| |
| (void) create_symlink(to_dir, rname, dirp->d_name); |
| } else { |
| /* |
| * Simply copy regular files to to_dir. |
| */ |
| (void) strcpy(path, to_dir); |
| (void) strcat(path, "/"); |
| (void) strcat(path, dirp->d_name); |
| (void) create_file(path); |
| } |
| } |
| |
| (void) closedir(dp); |
| } |
| |
| static int |
| init_hostname(void) |
| { |
| struct lifnum lifn; |
| int sock; |
| |
| if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { |
| syslog(LOG_ERR, "statd:init_hostname, socket: %m"); |
| return (-1); |
| } |
| |
| lifn.lifn_family = AF_UNSPEC; |
| lifn.lifn_flags = 0; |
| |
| if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { |
| syslog(LOG_ERR, |
| "statd:init_hostname, get number of interfaces, error: %m"); |
| close(sock); |
| return (-1); |
| } |
| |
| host_name_count = lifn.lifn_count; |
| |
| host_name = (char **)malloc(host_name_count * sizeof (char *)); |
| if (host_name == NULL) { |
| perror("statd -a can't get ip configuration\n"); |
| close(sock); |
| return (-1); |
| } |
| close(sock); |
| return (0); |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| int c; |
| int ppid; |
| extern char *optarg; |
| int choice = 0; |
| struct rlimit rl; |
| int mode; |
| int sz; |
| int connmaxrec = RPC_MAXDATASIZE; |
| |
| addrix = 0; |
| pathix = 0; |
| |
| (void) gethostname(hostname, MAXHOSTNAMELEN); |
| if (init_hostname() < 0) |
| exit(1); |
| |
| while ((c = getopt(argc, argv, "Dd:a:p:r")) != EOF) |
| switch (c) { |
| case 'd': |
| (void) sscanf(optarg, "%d", &debug); |
| break; |
| case 'D': |
| choice = 1; |
| break; |
| case 'a': |
| if (addrix < host_name_count) { |
| if (strcmp(hostname, optarg) != 0) { |
| sz = strlen(optarg); |
| if (sz < MAXHOSTNAMELEN) { |
| host_name[addrix] = |
| (char *)xmalloc(sz+1); |
| if (host_name[addrix] != |
| NULL) { |
| (void) sscanf(optarg, "%s", |
| host_name[addrix]); |
| addrix++; |
| } |
| } else |
| (void) fprintf(stderr, |
| "statd: -a name of host is too long.\n"); |
| } |
| } else |
| (void) fprintf(stderr, |
| "statd: -a exceeding maximum hostnames\n"); |
| break; |
| case 'p': |
| if (strlen(optarg) < MAXPATHLEN) { |
| /* If the path_name array has not yet */ |
| /* been malloc'ed, do that. The array */ |
| /* should be big enough to hold all of the */ |
| /* -p options we might have. An upper */ |
| /* bound on the number of -p options is */ |
| /* argc/2, because each -p option consumes */ |
| /* two arguments. Here the upper bound */ |
| /* is supposing that all the command line */ |
| /* arguments are -p options, which would */ |
| /* actually never be the case. */ |
| if (path_name == NULL) { |
| size_t sz = (argc/2) * sizeof (char *); |
| |
| path_name = (char **)malloc(sz); |
| if (path_name == NULL) { |
| (void) fprintf(stderr, |
| "statd: malloc failed\n"); |
| exit(1); |
| } |
| (void) memset(path_name, 0, sz); |
| } |
| path_name[pathix] = optarg; |
| pathix++; |
| } else { |
| (void) fprintf(stderr, |
| "statd: -p pathname is too long.\n"); |
| } |
| break; |
| case 'r': |
| regfiles_only = 1; |
| break; |
| default: |
| (void) fprintf(stderr, |
| "statd [-d level] [-D]\n"); |
| return (1); |
| } |
| |
| if (choice == 0) { |
| (void) strcpy(statd_home, home0); |
| (void) strcpy(CURRENT, current0); |
| (void) strcpy(BACKUP, backup0); |
| (void) strcpy(STATE, state0); |
| } else { |
| (void) strcpy(statd_home, home1); |
| (void) strcpy(CURRENT, current1); |
| (void) strcpy(BACKUP, backup1); |
| (void) strcpy(STATE, state1); |
| } |
| if (debug) |
| (void) printf("debug is on, create entry: %s, %s, %s\n", |
| CURRENT, BACKUP, STATE); |
| |
| if (getrlimit(RLIMIT_NOFILE, &rl)) |
| (void) printf("statd: getrlimit failed. \n"); |
| |
| /* Set maxfdlimit current soft limit */ |
| rl.rlim_cur = MAX_FDS; |
| if (setrlimit(RLIMIT_NOFILE, &rl) != 0) |
| syslog(LOG_ERR, "statd: unable to set RLIMIT_NOFILE to %d\n", |
| MAX_FDS); |
| |
| if (!debug) { |
| ppid = fork(); |
| if (ppid == -1) { |
| (void) fprintf(stderr, "statd: fork failure\n"); |
| (void) fflush(stderr); |
| abort(); |
| } |
| if (ppid != 0) { |
| exit(0); |
| } |
| closefrom(0); |
| (void) open("/dev/null", O_RDONLY); |
| (void) open("/dev/null", O_WRONLY); |
| (void) dup(1); |
| (void) setsid(); |
| openlog("statd", LOG_PID, LOG_DAEMON); |
| } |
| |
| (void) _create_daemon_lock(STATD, DAEMON_UID, DAEMON_GID); |
| /* |
| * establish our lock on the lock file and write our pid to it. |
| * exit if some other process holds the lock, or if there's any |
| * error in writing/locking the file. |
| */ |
| ppid = _enter_daemon_lock(STATD); |
| switch (ppid) { |
| case 0: |
| break; |
| case -1: |
| syslog(LOG_ERR, "error locking for %s: %s", STATD, |
| strerror(errno)); |
| exit(2); |
| default: |
| /* daemon was already running */ |
| exit(0); |
| } |
| |
| /* Get other aliases from each interface. */ |
| merge_hosts(); |
| |
| /* |
| * Set to automatic mode such that threads are automatically |
| * created |
| */ |
| mode = RPC_SVC_MT_AUTO; |
| if (!rpc_control(RPC_SVC_MTMODE_SET, &mode)) { |
| syslog(LOG_ERR, |
| "statd:unable to set automatic MT mode."); |
| exit(1); |
| } |
| |
| /* |
| * Set non-blocking mode and maximum record size for |
| * connection oriented RPC transports. |
| */ |
| if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { |
| syslog(LOG_INFO, "unable to set maximum RPC record size"); |
| } |
| |
| if (!svc_create(sm_prog_1, SM_PROG, SM_VERS, "netpath")) { |
| syslog(LOG_ERR, |
| "statd: unable to create (SM_PROG, SM_VERS) for netpath."); |
| exit(1); |
| } |
| |
| if (!svc_create(sm_prog_1, NSM_ADDR_PROGRAM, NSM_ADDR_V1, "netpath")) { |
| syslog(LOG_ERR, |
| "statd: unable to create (NSM_ADDR_PROGRAM, NSM_ADDR_V1) for netpath."); |
| } |
| |
| /* |
| * Make sure /var/statmon and any alternate (-p) statmon |
| * directories exist and are owned by daemon. Then change our uid |
| * to daemon. The uid change is to prevent attacks against local |
| * daemons that trust any call from a local root process. |
| */ |
| |
| set_statmon_owner(); |
| |
| /* |
| * |
| * statd now runs as a daemon rather than root and can not |
| * dump core under / because of the permission. It is |
| * important that current working directory of statd be |
| * changed to writable directory /var/statmon so that it |
| * can dump the core upon the receipt of the signal. |
| * One still need to set allow_setid_core to non-zero in |
| * /etc/system to get the core dump. |
| * |
| */ |
| |
| if (chdir(statd_home) < 0) { |
| syslog(LOG_ERR, "can't chdir %s: %m", statd_home); |
| exit(1); |
| } |
| |
| copy_client_names(); |
| |
| rwlock_init(&thr_rwlock, USYNC_THREAD, NULL); |
| mutex_init(&crash_lock, USYNC_THREAD, NULL); |
| mutex_init(&name_addrlock, USYNC_THREAD, NULL); |
| cond_init(&crash_finish, USYNC_THREAD, NULL); |
| cond_init(&retrywait, USYNC_THREAD, NULL); |
| sm_inithash(); |
| die = 0; |
| /* |
| * This variable is set to ensure that an sm_crash |
| * request will not be done at the same time |
| * when a statd_init is being done, since sm_crash |
| * can reset some variables that statd_init will be using. |
| */ |
| in_crash = 1; |
| statd_init(); |
| |
| if (debug) |
| (void) printf("Starting svc_run\n"); |
| svc_run(); |
| syslog(LOG_ERR, "statd: svc_run returned\n"); |
| /* NOTREACHED */ |
| thr_exit((void *) 1); |
| return (0); |
| |
| } |
| |
| /* |
| * Make sure the ownership of the statmon directories is correct, then |
| * change our uid to match. If the top-level directories (/var/statmon, -p |
| * arguments) don't exist, they are created first. The sm and sm.bak |
| * directories are not created here, but if they already exist, they are |
| * chowned to the correct uid, along with anything else in the |
| * directories. |
| */ |
| |
| static void |
| set_statmon_owner(void) |
| { |
| int i; |
| boolean_t can_do_mlp; |
| |
| /* |
| * Recursively chown/chgrp /var/statmon and the alternate paths, |
| * creating them if necessary. |
| */ |
| one_statmon_owner(statd_home); |
| for (i = 0; i < pathix; i++) { |
| char alt_path[MAXPATHLEN]; |
| |
| snprintf(alt_path, MAXPATHLEN, "%s/statmon", path_name[i]); |
| one_statmon_owner(alt_path); |
| } |
| |
| can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP); |
| if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, |
| DAEMON_UID, DAEMON_GID, can_do_mlp ? PRIV_NET_BINDMLP : NULL, |
| NULL) == -1) { |
| syslog(LOG_ERR, "can't run unprivileged: %m"); |
| exit(1); |
| } |
| |
| __fini_daemon_priv(PRIV_PROC_EXEC, PRIV_PROC_SESSION, |
| PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); |
| } |
| |
| /* |
| * Copy client names from the alternate statmon directories into |
| * /var/statmon. The top-level (statmon) directories should already |
| * exist, though the sm and sm.bak directories might not. |
| */ |
| |
| static void |
| copy_client_names() |
| { |
| int i; |
| char buf[MAXPATHLEN+SM_MAXPATHLEN]; |
| |
| /* |
| * Copy all clients from alternate paths to /var/statmon/sm |
| * Remove the files in alternate directory when copying is done. |
| */ |
| for (i = 0; i < pathix; i++) { |
| /* |
| * If the alternate directories do not exist, create it. |
| * If they do exist, just do the copy. |
| */ |
| snprintf(buf, sizeof (buf), "%s/statmon/sm", path_name[i]); |
| if ((mkdir(buf, SM_DIRECTORY_MODE)) == -1) { |
| if (errno != EEXIST) { |
| syslog(LOG_ERR, |
| "can't mkdir %s: %m\n", buf); |
| continue; |
| } |
| copydir_from_to(buf, CURRENT); |
| (void) remove_dir(buf); |
| } |
| |
| (void) snprintf(buf, sizeof (buf), "%s/statmon/sm.bak", |
| path_name[i]); |
| if ((mkdir(buf, SM_DIRECTORY_MODE)) == -1) { |
| if (errno != EEXIST) { |
| syslog(LOG_ERR, |
| "can't mkdir %s: %m\n", buf); |
| continue; |
| } |
| copydir_from_to(buf, BACKUP); |
| (void) remove_dir(buf); |
| } |
| } |
| } |
| |
| /* |
| * Create the given directory if it doesn't already exist. Set the user |
| * and group to daemon for the directory and anything under it. |
| */ |
| |
| static void |
| one_statmon_owner(const char *dir) |
| { |
| if ((mkdir(dir, SM_DIRECTORY_MODE)) == -1) { |
| if (errno != EEXIST) { |
| syslog(LOG_ERR, "can't mkdir %s: %m", |
| dir); |
| return; |
| } |
| } |
| |
| if (debug) |
| printf("Setting owner for %s\n", dir); |
| |
| if (nftw(dir, nftw_owner, MAX_FDS, FTW_PHYS) != 0) { |
| syslog(LOG_WARNING, "error setting owner for %s: %m", |
| dir); |
| } |
| } |
| |
| /* |
| * Set the user and group to daemon for the given file or directory. If |
| * it's a directory, also makes sure that it is mode 755. |
| * Generates a syslog message but does not return an error if there were |
| * problems. |
| */ |
| |
| /*ARGSUSED3*/ |
| static int |
| nftw_owner(const char *path, const struct stat *statp, int info, |
| struct FTW *ftw) |
| { |
| if (!(info == FTW_F || info == FTW_D)) |
| return (0); |
| |
| /* |
| * Some older systems might have mode 777 directories. Fix that. |
| */ |
| |
| if (info == FTW_D && (statp->st_mode & (S_IWGRP | S_IWOTH)) != 0) { |
| mode_t newmode = (statp->st_mode & ~(S_IWGRP | S_IWOTH)) & |
| S_IAMB; |
| |
| if (debug) |
| printf("chmod %03o %s\n", newmode, path); |
| if (chmod(path, newmode) < 0) { |
| int error = errno; |
| |
| syslog(LOG_WARNING, "can't chmod %s to %03o: %m", |
| path, newmode); |
| if (debug) |
| printf(" FAILED: %s\n", strerror(error)); |
| } |
| } |
| |
| /* If already owned by daemon, don't bother changing. */ |
| if (statp->st_uid == DAEMON_UID && |
| statp->st_gid == DAEMON_GID) |
| return (0); |
| |
| if (debug) |
| printf("lchown %s daemon:daemon\n", path); |
| if (lchown(path, DAEMON_UID, DAEMON_GID) < 0) { |
| int error = errno; |
| |
| syslog(LOG_WARNING, "can't chown %s to daemon: %m", |
| path); |
| if (debug) |
| printf(" FAILED: %s\n", strerror(error)); |
| } |
| |
| return (0); |
| } |