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