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