Print this page
7569 statd support to run on a fixed port
Portions contributed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sebastien Roy <sebastien.roy@delphix.com>

@@ -18,12 +18,13 @@
  *
  * CDDL HEADER END
  */
 
 /*
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
 /*        All Rights Reserved   */
 

@@ -35,25 +36,23 @@
  * University Acknowledgment- Portions of this document are derived from
  * software developed by the University of California, Berkeley, and its
  * contributors.
  */
 
-/*
- * Copyright (c) 2012 by Delphix. All rights reserved.
- */
-
 #include <stdio.h>
 #include <stdio_ext.h>
 #include <stdlib.h>
 #include <ftw.h>
 #include <signal.h>
 #include <string.h>
 #include <syslog.h>
 #include <netconfig.h>
+#include <netdir.h>
 #include <unistd.h>
 #include <netdb.h>
 #include <rpc/rpc.h>
+#include <rpc/svc.h>
 #include <netinet/in.h>
 #include <sys/param.h>
 #include <sys/resource.h>
 #include <sys/file.h>
 #include <sys/types.h>

@@ -67,10 +66,11 @@
 #include <synch.h>
 #include <net/if.h>
 #include <limits.h>
 #include <rpcsvc/daemon_utils.h>
 #include <priv_utils.h>
+#include "smfcfg.h"
 #include "sm_statd.h"
 
 
 #define home0           "/var/statmon"
 #define current0        "/var/statmon/sm"

@@ -97,10 +97,11 @@
 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 */
+int statd_port = 0;
 char hostname[MAXHOSTNAMELEN];
 
 /*
  * These variables will be used to store all the
  * alias names for the host, as well as the -a

@@ -455,10 +456,82 @@
         in_merges = B_FALSE;
         (void) cond_broadcast(&merges_cond);
         (void) mutex_unlock(&merges_lock);
 }
 
+/*
+ * This function is called for each configured network type to
+ * bind and register our RPC service programs.
+ *
+ * On TCP or UDP, we may want to bind SM_PROG on a specific port
+ * (when statd_port is specified) in which case we'll use the
+ * variant of svc_tp_create() that lets us pass a bind address.
+ */
+static void
+sm_svc_tp_create(struct netconfig *nconf)
+{
+        char port_str[8];
+        struct nd_hostserv hs;
+        struct nd_addrlist *al = NULL;
+        SVCXPRT *xprt = NULL;
+
+        /*
+         * If statd_port is set and this is an inet transport,
+         * bind this service on the specified port.  The TLI way
+         * to create such a bind address is netdir_getbyname()
+         * with the special "host" HOST_SELF_BIND.  This builds
+         * an all-zeros IP address with the specified port.
+         */
+        if (statd_port != 0 &&
+            (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
+            strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
+                int err;
+
+                snprintf(port_str, sizeof (port_str), "%u",
+                    (unsigned short)statd_port);
+
+                hs.h_host = HOST_SELF_BIND;
+                hs.h_serv = port_str;
+                err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
+                if (err == 0 && al != NULL) {
+                        xprt = svc_tp_create_addr(sm_prog_1, SM_PROG, SM_VERS,
+                            nconf, al->n_addrs);
+                        netdir_free(al, ND_ADDRLIST);
+                }
+                if (xprt == NULL) {
+                        syslog(LOG_ERR, "statd: unable to create "
+                            "(SM_PROG, SM_VERS) on transport %s (port %d)",
+                            nconf->nc_netid, statd_port);
+                }
+                /* fall-back to default bind */
+        }
+        if (xprt == NULL) {
+                /*
+                 * Had statd_port=0, or non-inet transport,
+                 * or the bind to a specific port failed.
+                 * Do a default bind.
+                 */
+                xprt = svc_tp_create(sm_prog_1, SM_PROG, SM_VERS, nconf);
+        }
+        if (xprt == NULL) {
+                syslog(LOG_ERR, "statd: unable to create "
+                    "(SM_PROG, SM_VERS) for transport %s",
+                    nconf->nc_netid);
+                return;
+        }
+
+        /*
+         * Also register the NSM_ADDR program on this
+         * transport handle (same dispatch function).
+         */
+        if (!svc_reg(xprt, NSM_ADDR_PROGRAM, NSM_ADDR_V1, sm_prog_1, nconf)) {
+                syslog(LOG_ERR, "statd: failed to register "
+                    "(NSM_ADDR_PROGRAM, NSM_ADDR_V1) for "
+                    "netconfig %s", nconf->nc_netid);
+        }
+}
+
 int
 main(int argc, char *argv[])
 {
         int c;
         int ppid;

@@ -466,20 +539,30 @@
         int choice = 0;
         struct rlimit rl;
         int mode;
         int sz;
         int pipe_fd = -1;
+        int ret;
         int connmaxrec = RPC_MAXDATASIZE;
+        struct netconfig *nconf;
+        NCONF_HANDLE *nc;
 
         addrix = 0;
         pathix = 0;
 
         (void) gethostname(hostname, MAXHOSTNAMELEN);
         if (init_hostname() < 0)
                 exit(1);
 
-        while ((c = getopt(argc, argv, "Dd:a:G:p:rU:")) != EOF)
+        ret = nfs_smf_get_iprop("statd_port", &statd_port,
+            DEFAULT_INSTANCE, SCF_TYPE_INTEGER, STATD);
+        if (ret != SA_OK) {
+                syslog(LOG_ERR, "Reading of statd_port from SMF "
+                    "failed, using default value");
+        }
+
+        while ((c = getopt(argc, argv, "Dd:a:G:p:P:rU:")) != EOF)
                 switch (c) {
                 case 'd':
                         (void) sscanf(optarg, "%d", &debug);
                         break;
                 case 'D':

@@ -540,10 +623,18 @@
                         } else {
                                 (void) fprintf(stderr,
                                 "statd: -p pathname is too long.\n");
                         }
                         break;
+                case 'P':
+                        (void) sscanf(optarg, "%d", &statd_port);
+                        if (statd_port < 1 || statd_port > UINT16_MAX) {
+                                (void) fprintf(stderr,
+                                    "statd: -P port invalid.\n");
+                                statd_port = 0;
+                        }
+                        break;
                 case 'r':
                         regfiles_only = 1;
                         break;
                 default:
                         (void) fprintf(stderr,

@@ -634,20 +725,33 @@
          */
         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);
+        /*
+         * Enumerate network transports and create service listeners
+         * as appropriate for each.
+         */
+        if ((nc = setnetconfig()) == NULL) {
+                syslog(LOG_ERR, "setnetconfig failed: %m");
+                return (-1);
         }
+        while ((nconf = getnetconfig(nc)) != NULL) {
 
-        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.");
+                /*
+                 * Skip things like tpi_raw, invisible...
+                 */
+                if ((nconf->nc_flag & NC_VISIBLE) == 0)
+                        continue;
+                if (nconf->nc_semantics != NC_TPI_CLTS &&
+                    nconf->nc_semantics != NC_TPI_COTS &&
+                    nconf->nc_semantics != NC_TPI_COTS_ORD)
+                        continue;
+
+                sm_svc_tp_create(nconf);
         }
+        (void) endnetconfig(nc);
 
         /*
          * 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