diff options
Diffstat (limited to 'usr/src/cmd/avs/rdc/sndrd.c')
-rw-r--r-- | usr/src/cmd/avs/rdc/sndrd.c | 2013 |
1 files changed, 0 insertions, 2013 deletions
diff --git a/usr/src/cmd/avs/rdc/sndrd.c b/usr/src/cmd/avs/rdc/sndrd.c deleted file mode 100644 index aa04f19127..0000000000 --- a/usr/src/cmd/avs/rdc/sndrd.c +++ /dev/null @@ -1,2013 +0,0 @@ -/* - * 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 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ -/* - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. - * Copyright 2014 Gary Mills - */ - -/* - * Network SNDR/ncall-ip server - based on nfsd - */ -#include <sys/types.h> -#include <rpc/types.h> -#include <errno.h> -#include <netdb.h> -#include <sys/socket.h> -#include <netconfig.h> -#include <stropts.h> -#include <fcntl.h> -#include <stdio.h> -#include <strings.h> -#include <signal.h> -#include <unistd.h> -#include <stdlib.h> -#include <netdir.h> -#include <rpc/rpc_com.h> -#include <rpc/rpc.h> -#include <tiuser.h> -#include <netinet/tcp.h> -#include <netinet/in.h> -#include <syslog.h> -#include <locale.h> -#include <langinfo.h> -#include <libintl.h> -#include <libgen.h> -#include <deflt.h> -#include <sys/resource.h> - -#include <sys/nsctl/nsctl.h> - -#ifdef __NCALL__ - -#include <sys/ncall/ncall.h> -#include <sys/ncall/ncall_ip.h> -#include <sys/nsctl/libncall.h> - -#define RDC_POOL_CREATE NC_IOC_POOL_CREATE -#define RDC_POOL_RUN NC_IOC_POOL_RUN -#define RDC_POOL_WAIT NC_IOC_POOL_WAIT -#define RDC_PROGRAM NCALL_PROGRAM -#define RDC_SERVICE "ncall" -#undef RDC_SVCPOOL_ID /* We are overloading this value */ -#define RDC_SVCPOOL_ID NCALL_SVCPOOL_ID -#define RDC_SVC_NAME "NCALL" -#define RDC_VERS_MIN NCALL_VERS_MIN -#define RDC_VERS_MAX NCALL_VERS_MAX - -#else /* !__NCALL__ */ - -#include <sys/nsctl/rdc_ioctl.h> -#include <sys/nsctl/rdc_io.h> -#include <sys/nsctl/librdc.h> - -#define RDC_SERVICE "rdc" -#define RDC_SVC_NAME "RDC" - -#endif /* __NCALL__ */ - -#define RDCADMIN "/etc/default/sndr" - -#include <nsctl.h> - -struct conn_ind { - struct conn_ind *conn_next; - struct conn_ind *conn_prev; - struct t_call *conn_call; -}; - -struct conn_entry { - bool_t closing; - struct netconfig nc; -}; - -static char *progname; -static struct conn_entry *conn_polled; -static int num_conns; /* Current number of connections */ -static struct pollfd *poll_array; /* array of poll descriptors for poll */ -static size_t num_fds = 0; /* number of transport fds opened */ -static void poll_for_action(); -static void remove_from_poll_list(int); -static int do_poll_cots_action(int, int); -static int do_poll_clts_action(int, int); -static void add_to_poll_list(int, struct netconfig *); -static int bind_to_provider(char *, char *, struct netbuf **, - struct netconfig **); -static int set_addrmask(int, struct netconfig *, struct netbuf *); -static void conn_close_oldest(void); -static boolean_t conn_get(int, struct netconfig *, struct conn_ind **); -static void cots_listen_event(int, int); -static int discon_get(int, struct netconfig *, struct conn_ind **); -static int nofile_increase(int); -static int is_listen_fd_index(int); -#if !defined(_SunOS_5_6) && !defined(_SunOS_5_7) && !defined(_SunOS_5_8) -static int sndrsvcpool(int); -static int svcwait(int id); -#endif - - -/* - * RPC protocol block. Useful for passing registration information. - */ -struct protob { - char *serv; /* ASCII service name, e.g. "RDC" */ - int versmin; /* minimum version no. to be registered */ - int versmax; /* maximum version no. to be registered */ - int program; /* program no. to be registered */ - struct protob *next; /* next entry on list */ -}; - - - -static size_t end_listen_fds; -static int debugflg = 0; -static int max_conns_allowed = -1; -static int listen_backlog = 10; -static char *trans_provider = (char *)NULL; -static int rdcsvc(int, struct netbuf, struct netconfig *); - -/* used by cots_listen_event() */ -static int (*Mysvc)(int, struct netbuf, struct netconfig *) = rdcsvc; - -/* - * Determine valid semantics for rdc. - */ -#define OK_TPI_TYPE(_nconf) \ - (_nconf->nc_semantics == NC_TPI_CLTS || \ - _nconf->nc_semantics == NC_TPI_COTS || \ - _nconf->nc_semantics == NC_TPI_COTS_ORD) - -#define BE32_TO_U32(a) \ - ((((uint32_t)((uchar_t *)a)[0] & 0xFF) << (uint32_t)24) |\ - (((uint32_t)((uchar_t *)a)[1] & 0xFF) << (uint32_t)16) |\ - (((uint32_t)((uchar_t *)a)[2] & 0xFF) << (uint32_t)8) |\ - ((uint32_t)((uchar_t *)a)[3] & 0xFF)) - -#ifdef DEBUG -/* - * Only support UDP in DEBUG mode for now - */ -static char *defaultproviders[] = { "/dev/tcp", "/dev/tcp6", "/dev/udp", - "/dev/udp6", NULL }; -#else -static char *defaultproviders[] = { "/dev/tcp6", "/dev/tcp", NULL }; -#endif - -/* - * Number of elements to add to the poll array on each allocation. - */ -#define POLL_ARRAY_INC_SIZE 64 -#define NOFILE_INC_SIZE 64 - -#ifdef __NCALL__ -const char *rdc_devr = "/dev/ncallip"; -#else -const char *rdc_devr = "/dev/rdc"; -#endif - -static int rdc_fdr; -static int - -open_rdc(void) -{ - int fd = open(rdc_devr, O_RDONLY); - - if (fd < 0) - return (-1); - - return (rdc_fdr = fd); -} - -static int -sndrsys(int type, void *arg) -{ - int ret = -1; - if (!rdc_fdr && open_rdc() < 0) { /* open failed */ - syslog(LOG_ERR, "open_rdc() failed: %m\n"); - } else { - if ((ret = ioctl(rdc_fdr, type, arg)) < 0) { - syslog(LOG_ERR, "ioctl(rdc_ioctl) failed: %m\n"); - } - } - return (ret); -} - -int -rdc_transport_open(struct netconfig *nconf) -{ - int fd; - struct strioctl strioc; - - if ((nconf == (struct netconfig *)NULL) || - (nconf->nc_device == (char *)NULL)) { - syslog(LOG_ERR, "No netconfig device"); - return (-1); - } - - /* - * Open the transport device. - */ - fd = t_open(nconf->nc_device, O_RDWR, (struct t_info *)NULL); - if (fd == -1) { - if (t_errno == TSYSERR && errno == EMFILE && - (nofile_increase(0) == 0)) { - /* Try again with a higher NOFILE limit. */ - fd = t_open(nconf->nc_device, O_RDWR, NULL); - } - if (fd == -1) { - if (t_errno == TSYSERR) { - syslog(LOG_ERR, "t_open failed: %m"); - } else { - syslog(LOG_ERR, "t_open failed: %s", - t_errlist[t_errno]); - } - return (-1); - } - } - - /* - * Pop timod because the RPC module must be as close as possible - * to the transport. - */ - if (ioctl(fd, I_POP, 0) < 0) { - syslog(LOG_ERR, "I_POP of timod failed: %m"); - if (t_close(fd) == -1) { - if (t_errno == TSYSERR) { - syslog(LOG_ERR, "t_close failed on %d: %m", fd); - } else { - syslog(LOG_ERR, "t_close failed on %d: %s", - fd, t_errlist[t_errno]); - } - } - return (-1); - } - - if (nconf->nc_semantics == NC_TPI_CLTS) { - /* - * Push rpcmod to filter data traffic to KRPC. - */ - if (ioctl(fd, I_PUSH, "rpcmod") < 0) { - syslog(LOG_ERR, "I_PUSH of rpcmod failed: %m"); - (void) t_close(fd); - return (-1); - } - } else { - if (ioctl(fd, I_PUSH, "rpcmod") < 0) { - syslog(LOG_ERR, "I_PUSH of CONS rpcmod failed: %m"); - if (t_close(fd) == -1) { - if (t_errno == TSYSERR) { - syslog(LOG_ERR, - "t_close failed on %d: %m", fd); - } else { - syslog(LOG_ERR, - "t_close failed on %d: %s", - fd, t_errlist[t_errno]); - } - } - return (-1); - } - - strioc.ic_cmd = RPC_SERVER; - strioc.ic_dp = (char *)0; - strioc.ic_len = 0; - strioc.ic_timout = -1; - /* Tell CONS rpcmod to act like a server stream. */ - if (ioctl(fd, I_STR, &strioc) < 0) { - syslog(LOG_ERR, "CONS rpcmod set-up ioctl failed: %m"); - if (t_close(fd) == -1) { - if (t_errno == TSYSERR) { - syslog(LOG_ERR, - "t_close failed on %d: %m", fd); - } else { - syslog(LOG_ERR, - "t_close failed on %d: %s", - fd, t_errlist[t_errno]); - } - } - return (-1); - } - } - - /* - * Re-push timod so that we will still be doing TLI - * operations on the descriptor. - */ - if (ioctl(fd, I_PUSH, "timod") < 0) { - syslog(LOG_ERR, "I_PUSH of timod failed: %m"); - if (t_close(fd) == -1) { - if (t_errno == TSYSERR) { - syslog(LOG_ERR, "t_close failed on %d: %m", fd); - } else { - syslog(LOG_ERR, "t_close failed on %d: %s", - fd, t_errlist[t_errno]); - } - } - return (-1); - } - - return (fd); -} - - -void -rdcd_log_tli_error(char *tli_name, int fd, struct netconfig *nconf) -{ - int error; - - /* - * Save the error code across syslog(), just in case syslog() - * gets its own error and, therefore, overwrites errno. - */ - error = errno; - if (t_errno == TSYSERR) { - syslog(LOG_ERR, "%s(file descriptor %d/transport %s) %m", - tli_name, fd, nconf->nc_proto); - } else { - syslog(LOG_ERR, - "%s(file descriptor %d/transport %s) TLI error %d", - tli_name, fd, nconf->nc_proto, t_errno); - } - errno = error; -} - -/* - * Called to set up service over a particular transport - */ -void -do_one(char *provider, char *proto, struct protob *protobp0, - int (*svc)(int, struct netbuf, struct netconfig *)) -{ - struct netbuf *retaddr; - struct netconfig *retnconf; - struct netbuf addrmask; - int vers; - int sock; - - if (provider) { - sock = bind_to_provider(provider, protobp0->serv, &retaddr, - &retnconf); - } else { - (void) syslog(LOG_ERR, - "Cannot establish %s service over %s: transport setup problem.", - protobp0->serv, provider ? provider : proto); - return; - } - - if (sock == -1) { - if ((Is_ipv6present() && - (strcmp(provider, "/dev/tcp6") == 0)) || - (!Is_ipv6present() && (strcmp(provider, "/dev/tcp") == 0))) - (void) syslog(LOG_ERR, - "Cannot establish %s service over %s: transport " - "setup problem.", - protobp0->serv, provider ? provider : proto); - return; - } - - if (set_addrmask(sock, retnconf, &addrmask) < 0) { - (void) syslog(LOG_ERR, - "Cannot set address mask for %s", retnconf->nc_netid); - return; - } - - - /* - * Register all versions of the programs in the protocol block list - */ - for (vers = protobp0->versmin; vers <= protobp0->versmax; vers++) { - (void) rpcb_unset(protobp0->program, vers, retnconf); - (void) rpcb_set(protobp0->program, vers, retnconf, retaddr); - } - - if (retnconf->nc_semantics == NC_TPI_CLTS) { - /* Don't drop core if supporting module(s) aren't loaded. */ - (void) signal(SIGSYS, SIG_IGN); - - /* - * svc() doesn't block, it returns success or failure. - */ - if ((*svc)(sock, addrmask, retnconf) < 0) { - (void) syslog(LOG_ERR, "Cannot establish %s service " - "over <file desc. %d, protocol %s> : %m. Exiting", - protobp0->serv, sock, retnconf->nc_proto); - exit(1); - } - } - /* - * We successfully set up the server over this transport. - * Add this descriptor to the one being polled on. - */ - add_to_poll_list(sock, retnconf); -} - -/* - * Set up the SNDR/ncall-ip service over all the available transports. - * Returns -1 for failure, 0 for success. - */ -int -do_all(struct protob *protobp, - int (*svc)(int, struct netbuf, struct netconfig *)) -{ - struct netconfig *nconf; - NCONF_HANDLE *nc; - - if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) { - syslog(LOG_ERR, "setnetconfig failed: %m"); - return (-1); - } - while (nconf = getnetconfig(nc)) { - if ((nconf->nc_flag & NC_VISIBLE) && - strcmp(nconf->nc_protofmly, "loopback") != 0 && - OK_TPI_TYPE(nconf)) - do_one(nconf->nc_device, nconf->nc_proto, protobp, svc); - } - (void) endnetconfig(nc); - return (0); -} - -/* - * Read the /etc/default/sndr configuration file to determine if the - * client has been configured for number of threads, backlog or transport - * provider. - */ - -static void -read_default(void) -{ - char *defval, *tmp_str; - int errno; - int tmp; - - /* Fail silently if error in opening the default rdc config file */ - if ((defopen(RDCADMIN)) == 0) { - if ((defval = defread("SNDR_THREADS=")) != NULL) { - errno = 0; - tmp = strtol(defval, (char **)NULL, 10); - if (errno == 0) { - max_conns_allowed = tmp; - } - } - if ((defval = defread("SNDR_LISTEN_BACKLOG=")) != NULL) { - errno = 0; - tmp = strtol(defval, (char **)NULL, 10); - if (errno == 0) { - listen_backlog = tmp; - } - } - if ((defval = defread("SNDR_TRANSPORT=")) != NULL) { - errno = 0; - tmp_str = strdup(defval); - if (errno == 0) { - trans_provider = tmp_str; - } - } - /* close defaults file */ - (void) defopen(NULL); - } -} -#ifdef lint -int -sndrd_lintmain(int ac, char **av) -#else -int -main(int ac, char **av) -#endif -{ - const char *dir = "/"; - int allflag = 0; - int pid; - int i, rc; - struct protob *protobp0, *protobp; - char **providerp; - char *required; -#if !defined(_SunOS_5_6) && !defined(_SunOS_5_7) && !defined(_SunOS_5_8) - int maxservers; -#endif - - (void) setlocale(LC_ALL, ""); -#ifdef __NCALL__ - (void) textdomain("ncall"); -#else - (void) textdomain("rdc"); -#endif - - progname = basename(av[0]); - -#ifdef __NCALL__ - rc = ncall_check_release(&required); -#else - rc = rdc_check_release(&required); -#endif - if (rc < 0) { - (void) fprintf(stderr, - gettext("%s: unable to determine the current " - "Solaris release: %s\n"), progname, strerror(errno)); - exit(1); - } else if (rc == FALSE) { - (void) fprintf(stderr, - gettext("%s: incorrect Solaris release (requires %s)\n"), - progname, required); - exit(1); - } - - openlog(progname, LOG_PID|LOG_CONS, LOG_DAEMON); - read_default(); - - /* - * Usage: <progname> [-c <number of threads>] [-t protocol] \ - * [-d] [-l <listen backlog>] - */ - while ((i = getopt(ac, av, "ac:t:dl:")) != EOF) { - switch (i) { - case 'a': - allflag = 1; - break; - case 'c': - max_conns_allowed = atoi(optarg); - if (max_conns_allowed <= 0) - max_conns_allowed = 16; - break; - - case 'd': - debugflg++; - break; - - case 't': - trans_provider = optarg; - break; - - case 'l': - listen_backlog = atoi(optarg); - if (listen_backlog < 0) - listen_backlog = 32; - break; - - default: - syslog(LOG_ERR, - "Usage: %s [-c <number of threads>] " - "[-d] [-t protocol] " - "[-l <listen backlog>]\n", progname); - exit(1); - break; - } - } - - if (chroot(dir) < 0) { - syslog(LOG_ERR, "chroot failed: %m"); - exit(1); - } - - if (chdir(dir) < 0) { - syslog(LOG_ERR, "chdir failed: %m"); - exit(1); - } - - if (!debugflg) { - pid = fork(); - if (pid < 0) { - syslog(LOG_ERR, "Fork failed\n"); - exit(1); - } - if (pid != 0) - exit(0); - - /* - * Close existing file descriptors, open "/dev/null" as - * standard input, output, and error, and detach from - * controlling terminal. - */ -#if !defined(_SunOS_5_6) && !defined(_SunOS_5_7) && !defined(_SunOS_5_8) - /* use closefrom(3C) from PSARC/2000/193 when possible */ - closefrom(0); -#else - for (i = 0; i < _NFILE; i++) - (void) close(i); -#endif - (void) open("/dev/null", O_RDONLY); - (void) open("/dev/null", O_WRONLY); - (void) dup(1); - (void) setsid(); - - /* - * ignore all signals apart from SIGTERM. - */ - for (i = 1; i < _sys_nsig; i++) - (void) sigset(i, SIG_IGN); - - (void) sigset(SIGTERM, SIG_DFL); - } - -#if !defined(_SunOS_5_6) && !defined(_SunOS_5_7) && !defined(_SunOS_5_8) - /* - * Set up kernel RPC thread pool for the SNDR/ncall-ip server. - */ - maxservers = (max_conns_allowed < 0 ? 16 : max_conns_allowed); - if (sndrsvcpool(maxservers)) { - (void) syslog(LOG_ERR, - "Can't set up kernel %s service: %m. Exiting", progname); - exit(1); - } - - /* - * Set up blocked thread to do LWP creation on behalf of the kernel. - */ - if (svcwait(RDC_SVCPOOL_ID)) { - (void) syslog(LOG_ERR, - "Can't set up %s pool creator: %m, Exiting", progname); - exit(1); - } -#endif - - /* - * Build a protocol block list for registration. - */ - protobp0 = protobp = (struct protob *)malloc(sizeof (struct protob)); - protobp->serv = RDC_SVC_NAME; - protobp->versmin = RDC_VERS_MIN; - protobp->versmax = RDC_VERS_MAX; - protobp->program = RDC_PROGRAM; - protobp->next = (struct protob *)NULL; - - if (allflag) { - if (do_all(protobp0, rdcsvc) == -1) - exit(1); - } else if (trans_provider) - do_one(trans_provider, NULL, protobp0, rdcsvc); - else { - for (providerp = defaultproviders; - *providerp != NULL; providerp++) { - trans_provider = *providerp; - do_one(trans_provider, NULL, protobp0, rdcsvc); - } - } - -done: - free(protobp); - - end_listen_fds = num_fds; - /* - * Poll for non-data control events on the transport descriptors. - */ - poll_for_action(); - - syslog(LOG_ERR, "%s fatal server error\n", progname); - - return (-1); -} - -static int -reuseaddr(int fd) -{ - struct t_optmgmt req, resp; - struct opthdr *opt; - char reqbuf[128]; - int *ip; - - /* LINTED pointer alignment */ - opt = (struct opthdr *)reqbuf; - opt->level = SOL_SOCKET; - opt->name = SO_REUSEADDR; - opt->len = sizeof (int); - - /* LINTED pointer alignment */ - ip = (int *)&reqbuf[sizeof (struct opthdr)]; - *ip = 1; - - req.flags = T_NEGOTIATE; - req.opt.len = sizeof (struct opthdr) + opt->len; - req.opt.buf = (char *)opt; - - resp.flags = 0; - resp.opt.buf = reqbuf; - resp.opt.maxlen = sizeof (reqbuf); - - if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) { - if (t_errno == TSYSERR) { - syslog(LOG_ERR, "reuseaddr() t_optmgmt failed: %m\n"); - } else { - syslog(LOG_ERR, "reuseaddr() t_optmgmt failed: %s\n", - t_errlist[t_errno]); - } - return (-1); - } - return (0); -} - -/* - * poll on the open transport descriptors for events and errors. - */ -void -poll_for_action(void) -{ - int nfds; - int i; - - /* - * Keep polling until all transports have been closed. When this - * happens, we return. - */ - while ((int)num_fds > 0) { - nfds = poll(poll_array, num_fds, INFTIM); - switch (nfds) { - case 0: - continue; - - case -1: - /* - * Some errors from poll could be - * due to temporary conditions, and we try to - * be robust in the face of them. Other - * errors (should never happen in theory) - * are fatal (eg. EINVAL, EFAULT). - */ - switch (errno) { - case EINTR: - continue; - - case EAGAIN: - case ENOMEM: - (void) sleep(10); - continue; - - default: - (void) syslog(LOG_ERR, - "poll failed: %m. Exiting"); - exit(1); - } - default: - break; - } - - /* - * Go through the poll list looking for events. - */ - for (i = 0; i < num_fds && nfds > 0; i++) { - if (poll_array[i].revents) { - nfds--; - /* - * We have a message, so try to read it. - * Record the error return in errno, - * so that syslog(LOG_ERR, "...%m") - * dumps the corresponding error string. - */ - if (conn_polled[i].nc.nc_semantics == - NC_TPI_CLTS) { - errno = do_poll_clts_action( - poll_array[i].fd, i); - } else { - errno = do_poll_cots_action( - poll_array[i].fd, i); - } - - if (errno == 0) - continue; - /* - * Most returned error codes mean that there is - * fatal condition which we can only deal with - * by closing the transport. - */ - if (errno != EAGAIN && errno != ENOMEM) { - (void) syslog(LOG_ERR, - "Error (%m) reading descriptor %d" - "/transport %s. Closing it.", - poll_array[i].fd, - conn_polled[i].nc.nc_proto); - (void) t_close(poll_array[i].fd); - remove_from_poll_list(poll_array[i].fd); - } else if (errno == ENOMEM) - (void) sleep(5); - } - } - } - - (void) syslog(LOG_ERR, - "All transports have been closed with errors. Exiting."); -} - -/* - * Allocate poll/transport array entries for this descriptor. - */ -static void -add_to_poll_list(int fd, struct netconfig *nconf) -{ - static int poll_array_size = 0; - - /* - * If the arrays are full, allocate new ones. - */ - if (num_fds == poll_array_size) { - struct pollfd *tpa; - struct conn_entry *tnp; - - if (poll_array_size != 0) { - tpa = poll_array; - tnp = conn_polled; - } else - tpa = (struct pollfd *)0; - - poll_array_size += POLL_ARRAY_INC_SIZE; - - /* - * Allocate new arrays. - */ - poll_array = (struct pollfd *) - malloc(poll_array_size * sizeof (struct pollfd) + 256); - conn_polled = (struct conn_entry *) - malloc(poll_array_size * sizeof (struct conn_entry) + 256); - if (poll_array == (struct pollfd *)NULL || - conn_polled == (struct conn_entry *)NULL) { - syslog(LOG_ERR, "malloc failed for poll array"); - exit(1); - } - - /* - * Copy the data of the old ones into new arrays, and - * free the old ones. - * num_fds is guaranteed to be less than - * poll_array_size, so this memcpy is safe. - */ - if (tpa) { - (void) memcpy((void *)poll_array, (void *)tpa, - num_fds * sizeof (struct pollfd)); - (void) memcpy((void *)conn_polled, (void *)tnp, - num_fds * sizeof (struct conn_entry)); - free((void *)tpa); - free((void *)tnp); - } - } - - /* - * Set the descriptor and event list. All possible events are - * polled for. - */ - poll_array[num_fds].fd = fd; - poll_array[num_fds].events = POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI; - - /* - * Copy the transport data over too. - */ - conn_polled[num_fds].nc = *nconf; /* structure copy */ - conn_polled[num_fds].closing = 0; - - /* - * Set the descriptor to non-blocking. Avoids a race - * between data arriving on the stream and then having it - * flushed before we can read it. - */ - if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - (void) syslog(LOG_ERR, - "fcntl(file desc. %d/transport %s, F_SETFL, " - "O_NONBLOCK): %m. Exiting", - num_fds, nconf->nc_proto); - exit(1); - } - - /* - * Count this descriptor. - */ - ++num_fds; -} - -static void -remove_from_poll_list(int fd) -{ - int i; - int num_to_copy; - - for (i = 0; i < num_fds; i++) { - if (poll_array[i].fd == fd) { - --num_fds; - num_to_copy = num_fds - i; - (void) memcpy((void *)&poll_array[i], - (void *)&poll_array[i+1], - num_to_copy * sizeof (struct pollfd)); - (void) memset((void *)&poll_array[num_fds], 0, - sizeof (struct pollfd)); - (void) memcpy((void *)&conn_polled[i], - (void *)&conn_polled[i+1], - num_to_copy * sizeof (struct conn_entry)); - (void) memset((void *)&conn_polled[num_fds], 0, - sizeof (struct conn_entry)); - return; - } - } - syslog(LOG_ERR, "attempt to remove nonexistent fd from poll list"); - -} - -static void -conn_close_oldest(void) -{ - int fd; - int i1; - - /* - * Find the oldest connection that is not already in the - * process of shutting down. - */ - for (i1 = end_listen_fds; /* no conditional expression */; i1++) { - if (i1 >= num_fds) - return; - if (conn_polled[i1].closing == 0) - break; - } -#ifdef DEBUG - (void) printf("too many connections (%d), releasing oldest (%d)\n", - num_conns, poll_array[i1].fd); -#else - syslog(LOG_WARNING, "too many connections (%d), releasing oldest (%d)", - num_conns, poll_array[i1].fd); -#endif - fd = poll_array[i1].fd; - if (conn_polled[i1].nc.nc_semantics == NC_TPI_COTS) { - /* - * For politeness, send a T_DISCON_REQ to the transport - * provider. We close the stream anyway. - */ - (void) t_snddis(fd, (struct t_call *)0); - num_conns--; - remove_from_poll_list(fd); - (void) t_close(fd); - } else { - /* - * For orderly release, we do not close the stream - * until the T_ORDREL_IND arrives to complete - * the handshake. - */ - if (t_sndrel(fd) == 0) - conn_polled[i1].closing = 1; - } -} - -static boolean_t -conn_get(int fd, struct netconfig *nconf, struct conn_ind **connp) -{ - struct conn_ind *conn; - struct conn_ind *next_conn; - - conn = (struct conn_ind *)malloc(sizeof (*conn)); - if (conn == NULL) { - syslog(LOG_ERR, "malloc for listen indication failed"); - return (FALSE); - } - - /* LINTED pointer alignment */ - conn->conn_call = (struct t_call *)t_alloc(fd, T_CALL, T_ALL); - if (conn->conn_call == NULL) { - free((char *)conn); - rdcd_log_tli_error("t_alloc", fd, nconf); - return (FALSE); - } - - if (t_listen(fd, conn->conn_call) == -1) { - rdcd_log_tli_error("t_listen", fd, nconf); - (void) t_free((char *)conn->conn_call, T_CALL); - free((char *)conn); - return (FALSE); - } - - if (conn->conn_call->udata.len > 0) { - syslog(LOG_WARNING, - "rejecting inbound connection(%s) with %d bytes " - "of connect data", - nconf->nc_proto, conn->conn_call->udata.len); - - conn->conn_call->udata.len = 0; - (void) t_snddis(fd, conn->conn_call); - (void) t_free((char *)conn->conn_call, T_CALL); - free((char *)conn); - return (FALSE); - } - - if ((next_conn = *connp) != NULL) { - next_conn->conn_prev->conn_next = conn; - conn->conn_next = next_conn; - conn->conn_prev = next_conn->conn_prev; - next_conn->conn_prev = conn; - } else { - conn->conn_next = conn; - conn->conn_prev = conn; - *connp = conn; - } - return (TRUE); -} - -static int -discon_get(int fd, struct netconfig *nconf, struct conn_ind **connp) -{ - struct conn_ind *conn; - struct t_discon discon; - - discon.udata.buf = (char *)0; - discon.udata.maxlen = 0; - if (t_rcvdis(fd, &discon) == -1) { - rdcd_log_tli_error("t_rcvdis", fd, nconf); - return (-1); - } - - conn = *connp; - if (conn == NULL) - return (0); - - do { - if (conn->conn_call->sequence == discon.sequence) { - if (conn->conn_next == conn) - *connp = (struct conn_ind *)0; - else { - if (conn == *connp) { - *connp = conn->conn_next; - } - conn->conn_next->conn_prev = conn->conn_prev; - conn->conn_prev->conn_next = conn->conn_next; - } - free((char *)conn); - break; - } - conn = conn->conn_next; - } while (conn != *connp); - - return (0); -} - -static void -cots_listen_event(int fd, int conn_index) -{ - struct t_call *call; - struct conn_ind *conn; - struct conn_ind *conn_head; - int event; - struct netconfig *nconf = &conn_polled[conn_index].nc; - int new_fd; - struct netbuf addrmask; - int ret = 0; - - conn_head = NULL; - (void) conn_get(fd, nconf, &conn_head); - - while ((conn = conn_head) != NULL) { - conn_head = conn->conn_next; - if (conn_head == conn) - conn_head = NULL; - else { - conn_head->conn_prev = conn->conn_prev; - conn->conn_prev->conn_next = conn_head; - } - call = conn->conn_call; - free(conn); - - /* - * If we have already accepted the maximum number of - * connections allowed on the command line, then drop - * the oldest connection (for any protocol) before - * accepting the new connection. Unless explicitly - * set on the command line, max_conns_allowed is -1. - */ - if (max_conns_allowed != -1 && num_conns >= max_conns_allowed) - conn_close_oldest(); - - /* - * Create a new transport endpoint for the same proto as - * the listener. - */ - new_fd = rdc_transport_open(nconf); - if (new_fd == -1) { - call->udata.len = 0; - (void) t_snddis(fd, call); - (void) t_free((char *)call, T_CALL); - syslog(LOG_ERR, "Cannot establish transport over %s", - nconf->nc_device); - continue; - } - - /* Bind to a generic address/port for the accepting stream. */ - if (t_bind(new_fd, NULL, NULL) == -1) { - rdcd_log_tli_error("t_bind", new_fd, nconf); - call->udata.len = 0; - (void) t_snddis(fd, call); - (void) t_free((char *)call, T_CALL); - (void) t_close(new_fd); - continue; - } - - while (t_accept(fd, new_fd, call) == -1) { - if (t_errno != TLOOK) { - rdcd_log_tli_error("t_accept", fd, nconf); - call->udata.len = 0; - (void) t_snddis(fd, call); - (void) t_free((char *)call, T_CALL); - (void) t_close(new_fd); - goto do_next_conn; - } - while (event = t_look(fd)) { - switch (event) { - case T_LISTEN: - (void) conn_get(fd, nconf, &conn_head); - continue; - - case T_DISCONNECT: - (void) discon_get(fd, nconf, - &conn_head); - continue; - - default: - syslog(LOG_ERR, - "unexpected event 0x%x during " - "accept processing (%s)", - event, nconf->nc_proto); - call->udata.len = 0; - (void) t_snddis(fd, call); - (void) t_free((char *)call, T_CALL); - (void) t_close(new_fd); - goto do_next_conn; - } - } - } - - if (set_addrmask(new_fd, nconf, &addrmask) < 0) { - (void) syslog(LOG_ERR, "Cannot set address mask for %s", - nconf->nc_netid); - (void) t_snddis(new_fd, NULL); - (void) t_free((char *)call, T_CALL); - (void) t_close(new_fd); - continue; - } - - /* Tell kRPC about the new stream. */ - ret = (*Mysvc)(new_fd, addrmask, nconf); - if (ret < 0) { - syslog(LOG_ERR, - "unable to register with kernel rpc: %m"); - free(addrmask.buf); - (void) t_snddis(new_fd, NULL); - (void) t_free((char *)call, T_CALL); - (void) t_close(new_fd); - goto do_next_conn; - } - - free(addrmask.buf); - (void) t_free((char *)call, T_CALL); - - /* - * Poll on the new descriptor so that we get disconnect - * and orderly release indications. - */ - num_conns++; - add_to_poll_list(new_fd, nconf); - - /* Reset nconf in case it has been moved. */ - nconf = &conn_polled[conn_index].nc; -do_next_conn:; - } -} - -static int -do_poll_cots_action(int fd, int conn_index) -{ - char buf[256]; - int event; - int i1; - int flags; - struct conn_entry *connent = &conn_polled[conn_index]; - struct netconfig *nconf = &(connent->nc); - const char *errorstr; - - while (event = t_look(fd)) { - switch (event) { - case T_LISTEN: - cots_listen_event(fd, conn_index); - break; - - case T_DATA: - /* - * Receive a private notification from CONS rpcmod. - */ - i1 = t_rcv(fd, buf, sizeof (buf), &flags); - if (i1 == -1) { - syslog(LOG_ERR, "t_rcv failed"); - break; - } - if (i1 < sizeof (int)) - break; - i1 = BE32_TO_U32(buf); - if (i1 == 1 || i1 == 2) { - /* - * This connection has been idle for too long, - * so release it as politely as we can. If we - * have already initiated an orderly release - * and we get notified that the stream is - * still idle, pull the plug. This prevents - * hung connections from continuing to consume - * resources. - */ - if (nconf->nc_semantics == NC_TPI_COTS || - connent->closing != 0) { - (void) t_snddis(fd, (struct t_call *)0); - goto fdclose; - } - /* - * For NC_TPI_COTS_ORD, the stream is closed - * and removed from the poll list when the - * T_ORDREL is received from the provider. We - * don't wait for it here because it may take - * a while for the transport to shut down. - */ - if (t_sndrel(fd) == -1) { - syslog(LOG_ERR, - "unable to send orderly release %m"); - } - connent->closing = 1; - } else - syslog(LOG_ERR, - "unexpected event from CONS rpcmod %d", i1); - break; - - case T_ORDREL: - /* Perform an orderly release. */ - if (t_rcvrel(fd) == 0) { - /* T_ORDREL on listen fd's should be ignored */ - if (!is_listen_fd_index(fd)) { - (void) t_sndrel(fd); - goto fdclose; - } - break; - - } else if (t_errno == TLOOK) { - break; - } else { - rdcd_log_tli_error("t_rcvrel", fd, nconf); - /* - * check to make sure we do not close - * listen fd - */ - if (!is_listen_fd_index(fd)) - break; - else - goto fdclose; - } - - case T_DISCONNECT: - if (t_rcvdis(fd, (struct t_discon *)NULL) == -1) - rdcd_log_tli_error("t_rcvdis", fd, nconf); - - /* - * T_DISCONNECT on listen fd's should be ignored. - */ - if (!is_listen_fd_index(fd)) - break; - else - goto fdclose; - - default: - if (t_errno == TSYSERR) { - if ((errorstr = strerror(errno)) == NULL) { - (void) snprintf(buf, sizeof (buf), - "Unknown error num %d", errno); - errorstr = (const char *)buf; - } - } else if (event == -1) - errorstr = t_strerror(t_errno); - else - errorstr = ""; -#ifdef DEBUG - syslog(LOG_ERR, - "unexpected TLI event (0x%x) on " - "connection-oriented transport(%s, %d):%s", - event, nconf->nc_proto, fd, errorstr); -#endif - -fdclose: - num_conns--; - remove_from_poll_list(fd); - (void) t_close(fd); - return (0); - } - } - - return (0); -} - - -/* - * Called to read and interpret the event on a connectionless descriptor. - * Returns 0 if successful, or a UNIX error code if failure. - */ -static int -do_poll_clts_action(int fd, int conn_index) -{ - int error; - int ret; - int flags; - struct netconfig *nconf = &conn_polled[conn_index].nc; - static struct t_unitdata *unitdata = NULL; - static struct t_uderr *uderr = NULL; - static int oldfd = -1; - struct nd_hostservlist *host = NULL; - struct strbuf ctl[1], data[1]; - /* - * We just need to have some space to consume the - * message in the event we can't use the TLI interface to do the - * job. - * - * We flush the message using getmsg(). For the control part - * we allocate enough for any TPI header plus 32 bytes for address - * and options. For the data part, there is nothing magic about - * the size of the array, but 256 bytes is probably better than - * 1 byte, and we don't expect any data portion anyway. - * - * If the array sizes are too small, we handle this because getmsg() - * (called to consume the message) will return MOREDATA|MORECTL. - * Thus we just call getmsg() until it's read the message. - */ - char ctlbuf[sizeof (union T_primitives) + 32]; - char databuf[256]; - - /* - * If this is the same descriptor as the last time - * do_poll_clts_action was called, we can save some - * de-allocation and allocation. - */ - if (oldfd != fd) { - oldfd = fd; - - if (unitdata) { - (void) t_free((char *)unitdata, T_UNITDATA); - unitdata = NULL; - } - if (uderr) { - (void) t_free((char *)uderr, T_UDERROR); - uderr = NULL; - } - } - - /* - * Allocate a unitdata structure for receiving the event. - */ - if (unitdata == NULL) { - /* LINTED pointer alignment */ - unitdata = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ALL); - if (unitdata == NULL) { - if (t_errno == TSYSERR) { - /* - * Save the error code across - * syslog(), just in case - * syslog() gets its own error - * and therefore overwrites errno. - */ - error = errno; - (void) syslog(LOG_ERR, - "t_alloc(file descriptor %d/transport %s, " - "T_UNITDATA) failed: %m", - fd, nconf->nc_proto); - return (error); - } - (void) syslog(LOG_ERR, "t_alloc(file descriptor %d/" - "transport %s, T_UNITDATA) failed TLI error %d", - fd, nconf->nc_proto, t_errno); - goto flush_it; - } - } - -try_again: - flags = 0; - - /* - * The idea is we wait for T_UNITDATA_IND's. Of course, - * we don't get any, because rpcmod filters them out. - * However, we need to call t_rcvudata() to let TLI - * tell us we have a T_UDERROR_IND. - * - * algorithm is: - * t_rcvudata(), expecting TLOOK. - * t_look(), expecting T_UDERR. - * t_rcvuderr(), expecting success (0). - * expand destination address into ASCII, - * and dump it. - */ - - ret = t_rcvudata(fd, unitdata, &flags); - if (ret == 0 || t_errno == TBUFOVFLW) { - (void) syslog(LOG_WARNING, "t_rcvudata(file descriptor %d/" - "transport %s) got unexpected data, %d bytes", - fd, nconf->nc_proto, unitdata->udata.len); - - /* - * Even though we don't expect any data, in case we do, - * keep reading until there is no more. - */ - if (flags & T_MORE) - goto try_again; - - return (0); - } - - switch (t_errno) { - case TNODATA: - return (0); - case TSYSERR: - /* - * System errors are returned to caller. - * Save the error code across - * syslog(), just in case - * syslog() gets its own error - * and therefore overwrites errno. - */ - error = errno; - (void) syslog(LOG_ERR, - "t_rcvudata(file descriptor %d/transport %s) %m", - fd, nconf->nc_proto); - return (error); - case TLOOK: - break; - default: - (void) syslog(LOG_ERR, - "t_rcvudata(file descriptor %d/transport %s) TLI error %d", - fd, nconf->nc_proto, t_errno); - goto flush_it; - } - - ret = t_look(fd); - switch (ret) { - case 0: - return (0); - case -1: - /* - * System errors are returned to caller. - */ - if (t_errno == TSYSERR) { - /* - * Save the error code across - * syslog(), just in case - * syslog() gets its own error - * and therefore overwrites errno. - */ - error = errno; - (void) syslog(LOG_ERR, - "t_look(file descriptor %d/transport %s) %m", - fd, nconf->nc_proto); - return (error); - } - (void) syslog(LOG_ERR, - "t_look(file descriptor %d/transport %s) TLI error %d", - fd, nconf->nc_proto, t_errno); - goto flush_it; - case T_UDERR: - break; - default: - (void) syslog(LOG_WARNING, "t_look(file descriptor %d/" - "transport %s) returned %d not T_UDERR (%d)", - fd, nconf->nc_proto, ret, T_UDERR); - } - - if (uderr == NULL) { - /* LINTED pointer alignment */ - uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL); - if (uderr == NULL) { - if (t_errno == TSYSERR) { - /* - * Save the error code across - * syslog(), just in case - * syslog() gets its own error - * and therefore overwrites errno. - */ - error = errno; - (void) syslog(LOG_ERR, - "t_alloc(file descriptor %d/transport %s, " - "T_UDERROR) failed: %m", - fd, nconf->nc_proto); - return (error); - } - (void) syslog(LOG_ERR, "t_alloc(file descriptor %d/" - "transport %s, T_UDERROR) failed TLI error: %d", - fd, nconf->nc_proto, t_errno); - goto flush_it; - } - } - - ret = t_rcvuderr(fd, uderr); - if (ret == 0) { - - /* - * Save the datagram error in errno, so that the - * %m argument to syslog picks up the error string. - */ - errno = uderr->error; - - /* - * Log the datagram error, then log the host that - * probably triggerred. Cannot log both in the - * same transaction because of packet size limitations - * in /dev/log. - */ - (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, - "%s response over <file descriptor %d/transport %s> " - "generated error: %m", - progname, fd, nconf->nc_proto); - - /* - * Try to map the client's address back to a - * name. - */ - ret = netdir_getbyaddr(nconf, &host, &uderr->addr); - if (ret != -1 && host && host->h_cnt > 0 && - host->h_hostservs) { - (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING, - "Bad %s response was sent to client with " - "host name: %s; service port: %s", - progname, host->h_hostservs->h_host, - host->h_hostservs->h_serv); - } else { - int i, j; - char *buf; - char *hex = "0123456789abcdef"; - - /* - * Mapping failed, print the whole thing - * in ASCII hex. - */ - buf = (char *)malloc(uderr->addr.len * 2 + 1); - for (i = 0, j = 0; i < uderr->addr.len; i++, j += 2) { - buf[j] = hex[((uderr->addr.buf[i]) >> 4) & 0xf]; - buf[j+1] = hex[uderr->addr.buf[i] & 0xf]; - } - buf[j] = '\0'; - (void) syslog((errno == ECONNREFUSED) ? - LOG_DEBUG : LOG_WARNING, - "Bad %s response was sent to client with " - "transport address: 0x%s", - progname, buf); - free((void *)buf); - } - - if (ret == 0 && host != NULL) - netdir_free((void *)host, ND_HOSTSERVLIST); - return (0); - } - - switch (t_errno) { - case TNOUDERR: - goto flush_it; - case TSYSERR: - /* - * System errors are returned to caller. - * Save the error code across - * syslog(), just in case - * syslog() gets its own error - * and therefore overwrites errno. - */ - error = errno; - (void) syslog(LOG_ERR, - "t_rcvuderr(file descriptor %d/transport %s) %m", - fd, nconf->nc_proto); - return (error); - default: - (void) syslog(LOG_ERR, - "t_rcvuderr(file descriptor %d/transport %s) TLI error %d", - fd, nconf->nc_proto, t_errno); - goto flush_it; - } - -flush_it: - /* - * If we get here, then we could not cope with whatever message - * we attempted to read, so flush it. If we did read a message, - * and one isn't present, that is all right, because fd is in - * nonblocking mode. - */ - (void) syslog(LOG_ERR, - "Flushing one input message from <file descriptor %d/transport %s>", - fd, nconf->nc_proto); - - /* - * Read and discard the message. Do this this until there is - * no more control/data in the message or until we get an error. - */ - do { - ctl->maxlen = sizeof (ctlbuf); - ctl->buf = ctlbuf; - data->maxlen = sizeof (databuf); - data->buf = databuf; - flags = 0; - ret = getmsg(fd, ctl, data, &flags); - if (ret == -1) - return (errno); - } while (ret != 0); - - return (0); -} - -/* - * Establish service thread. - */ -static int -rdcsvc(int fd, struct netbuf addrmask, struct netconfig *nconf) -{ -#ifdef __NCALL__ - struct ncall_svc_args nsa; -#else /* !__NCALL__ */ - struct rdc_svc_args nsa; - _rdc_ioctl_t rdc_args = { 0, }; -#endif /* __NCALL__ */ - - nsa.fd = fd; - nsa.nthr = (max_conns_allowed < 0 ? 16 : max_conns_allowed); - (void) strncpy(nsa.netid, nconf->nc_netid, sizeof (nsa.netid)); - nsa.addrmask.len = addrmask.len; - nsa.addrmask.maxlen = addrmask.maxlen; - nsa.addrmask.buf = addrmask.buf; - -#ifdef __NCALL__ - return (sndrsys(NC_IOC_SERVER, &nsa)); -#else /* !__NCALL__ */ - rdc_args.arg0 = (long)&nsa; - return (sndrsys(RDC_ENABLE_SVR, &rdc_args)); -#endif /* __NCALL__ */ -} - - - -static int -nofile_increase(int limit) -{ - struct rlimit rl; - - if (getrlimit(RLIMIT_NOFILE, &rl) == -1) { - syslog(LOG_ERR, - "nofile_increase() getrlimit of NOFILE failed: %m"); - return (-1); - } - - if (limit > 0) - rl.rlim_cur = limit; - else - rl.rlim_cur += NOFILE_INC_SIZE; - - if (rl.rlim_cur > rl.rlim_max && rl.rlim_max != RLIM_INFINITY) - rl.rlim_max = rl.rlim_cur; - - if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { - syslog(LOG_ERR, - "nofile_increase() setrlimit of NOFILE to %d failed: %m", - rl.rlim_cur); - return (-1); - } - - return (0); -} - -int -rdcd_bindit(struct netconfig *nconf, struct netbuf **addr, - struct nd_hostserv *hs, int backlog) -{ - int fd; - struct t_bind *ntb; - struct t_bind tb; - struct nd_addrlist *addrlist; - struct t_optmgmt req, resp; - struct opthdr *opt; - char reqbuf[128]; - - if ((fd = rdc_transport_open(nconf)) == -1) { - syslog(LOG_ERR, "cannot establish transport service over %s", - nconf->nc_device); - return (-1); - } - - addrlist = (struct nd_addrlist *)NULL; - if (netdir_getbyname(nconf, hs, &addrlist) != 0) { - if (strncmp(nconf->nc_netid, "udp", 3) != 0) { - syslog(LOG_ERR, "Cannot get address for transport " - "%s host %s service %s", - nconf->nc_netid, hs->h_host, hs->h_serv); - } - (void) t_close(fd); - return (-1); - } - - if (strcmp(nconf->nc_proto, "tcp") == 0) { - /* - * If we're running over TCP, then set the - * SO_REUSEADDR option so that we can bind - * to our preferred address even if previously - * left connections exist in FIN_WAIT states. - * This is somewhat bogus, but otherwise you have - * to wait 2 minutes to restart after killing it. - */ - if (reuseaddr(fd) == -1) { - syslog(LOG_WARNING, - "couldn't set SO_REUSEADDR option on transport"); - } - } - - if (nconf->nc_semantics == NC_TPI_CLTS) - tb.qlen = 0; - else - tb.qlen = backlog; - - /* LINTED pointer alignment */ - ntb = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL); - if (ntb == (struct t_bind *)NULL) { - syslog(LOG_ERR, "t_alloc failed: t_errno %d, %m", t_errno); - (void) t_close(fd); - netdir_free((void *)addrlist, ND_ADDRLIST); - return (-1); - } - - tb.addr = *(addrlist->n_addrs); /* structure copy */ - - if (t_bind(fd, &tb, ntb) == -1) { - syslog(LOG_ERR, "t_bind failed: t_errno %d, %m", t_errno); - (void) t_free((char *)ntb, T_BIND); - netdir_free((void *)addrlist, ND_ADDRLIST); - (void) t_close(fd); - return (-1); - } - - /* make sure we bound to the right address */ - if (tb.addr.len != ntb->addr.len || - memcmp(tb.addr.buf, ntb->addr.buf, tb.addr.len) != 0) { - syslog(LOG_ERR, "t_bind to wrong address"); - (void) t_free((char *)ntb, T_BIND); - netdir_free((void *)addrlist, ND_ADDRLIST); - (void) t_close(fd); - return (-1); - } - - *addr = &ntb->addr; - netdir_free((void *)addrlist, ND_ADDRLIST); - - if (strcmp(nconf->nc_proto, "tcp") == 0 || - strcmp(nconf->nc_proto, "tcp6") == 0) { - /* - * Disable the Nagle algorithm on TCP connections. - * Connections accepted from this listener will - * inherit the listener options. - */ - - /* LINTED pointer alignment */ - opt = (struct opthdr *)reqbuf; - opt->level = IPPROTO_TCP; - opt->name = TCP_NODELAY; - opt->len = sizeof (int); - - /* LINTED pointer alignment */ - *(int *)((char *)opt + sizeof (*opt)) = 1; - - req.flags = T_NEGOTIATE; - req.opt.len = sizeof (*opt) + opt->len; - req.opt.buf = (char *)opt; - resp.flags = 0; - resp.opt.buf = reqbuf; - resp.opt.maxlen = sizeof (reqbuf); - - if (t_optmgmt(fd, &req, &resp) < 0 || - resp.flags != T_SUCCESS) { - syslog(LOG_ERR, "couldn't set NODELAY option for " - "proto %s: t_errno = %d, %m", nconf->nc_proto, - t_errno); - } - } - - return (fd); -} - - -/* ARGSUSED */ -static int -bind_to_provider(char *provider, char *serv, struct netbuf **addr, - struct netconfig **retnconf) -{ - struct netconfig *nconf; - NCONF_HANDLE *nc; - struct nd_hostserv hs; - - hs.h_host = HOST_SELF; - hs.h_serv = RDC_SERVICE; /* serv_name_to_port_name(serv); */ - - if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) { - syslog(LOG_ERR, "setnetconfig failed: %m"); - return (-1); - } - while (nconf = getnetconfig(nc)) { - if (OK_TPI_TYPE(nconf) && - strcmp(nconf->nc_device, provider) == 0) { - *retnconf = nconf; - return (rdcd_bindit(nconf, addr, &hs, listen_backlog)); - } - } - (void) endnetconfig(nc); - if ((Is_ipv6present() && (strcmp(provider, "/dev/tcp6") == 0)) || - (!Is_ipv6present() && (strcmp(provider, "/dev/tcp") == 0))) - syslog(LOG_ERR, "couldn't find netconfig entry for provider %s", - provider); - return (-1); -} - - -/* - * For listen fd's index is always less than end_listen_fds. - * It's value is equal to the number of open file descriptors after the - * last listen end point was opened but before any connection was accepted. - */ -static int -is_listen_fd_index(int index) -{ - return (index < end_listen_fds); -} - - -/* - * Create an address mask appropriate for the transport. - * The mask is used to obtain the host-specific part of - * a network address when comparing addresses. - * For an internet address the host-specific part is just - * the 32 bit IP address and this part of the mask is set - * to all-ones. The port number part of the mask is zeroes. - */ -static int -set_addrmask(int fd, struct netconfig *nconf, struct netbuf *mask) -{ - struct t_info info; - - /* - * Find the size of the address we need to mask. - */ - if (t_getinfo(fd, &info) < 0) { - t_error("t_getinfo"); - return (-1); - } - mask->len = mask->maxlen = info.addr; - if (info.addr <= 0) { - syslog(LOG_ERR, "set_addrmask: address size: %ld", info.addr); - return (-1); - } - - mask->buf = (char *)malloc(mask->len); - if (mask->buf == NULL) { - syslog(LOG_ERR, "set_addrmask: no memory"); - return (-1); - } - (void) memset(mask->buf, 0, mask->len); /* reset all mask bits */ - - if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { - /* - * Set the mask so that the port is ignored. - */ - /* LINTED pointer alignment */ - ((struct sockaddr_in *)mask->buf)->sin_addr.s_addr = - (in_addr_t)~0; - /* LINTED pointer alignment */ - ((struct sockaddr_in *)mask->buf)->sin_family = (sa_family_t)~0; - } -#ifdef NC_INET6 - else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) { - /* LINTED pointer alignment */ - (void) memset(&((struct sockaddr_in6 *)mask->buf)->sin6_addr, - (uchar_t)~0, sizeof (struct in6_addr)); - /* LINTED pointer alignment */ - ((struct sockaddr_in6 *)mask->buf)->sin6_family = - (sa_family_t)~0; - } -#endif - else { - /* - * Set all mask bits. - */ - (void) memset(mask->buf, (uchar_t)~0, mask->len); - } - return (0); -} - -#if !defined(_SunOS_5_6) && !defined(_SunOS_5_7) && !defined(_SunOS_5_8) - -static int -sndrsvcpool(int maxservers) -{ - struct svcpool_args npa; - - npa.id = RDC_SVCPOOL_ID; - npa.maxthreads = maxservers; - npa.redline = 0; - npa.qsize = 0; - npa.timeout = 0; - npa.stksize = 0; - npa.max_same_xprt = 0; - return (sndrsys(RDC_POOL_CREATE, &npa)); -} - - -/* - * The following stolen from cmd/fs.d/nfs/lib/thrpool.c - */ - -#include <thread.h> - -/* - * Thread to call into the kernel and do work on behalf of SNDR/ncall-ip. - */ -static void * -svcstart(void *arg) -{ - int id = (int)arg; - int err; - - while ((err = sndrsys(RDC_POOL_RUN, &id)) != 0) { - /* - * Interrupted by a signal while in the kernel. - * this process is still alive, try again. - */ - if (err == EINTR) - continue; - else - break; - } - - /* - * If we weren't interrupted by a signal, but did - * return from the kernel, this thread's work is done, - * and it should exit. - */ - thr_exit(NULL); - return (NULL); -} - -/* - * User-space "creator" thread. This thread blocks in the kernel - * until new worker threads need to be created for the service - * pool. On return to userspace, if there is no error, create a - * new thread for the service pool. - */ -static void * -svcblock(void *arg) -{ - int id = (int)arg; - - /* CONSTCOND */ - while (1) { - thread_t tid; - int err; - - /* - * Call into the kernel, and hang out there - * until a thread needs to be created. - */ - if (err = sndrsys(RDC_POOL_WAIT, &id)) { - if (err == ECANCELED || err == EBUSY) - /* - * If we get back ECANCELED, the service - * pool is exiting, and we may as well - * clean up this thread. If EBUSY is - * returned, there's already a thread - * looping on this pool, so we should - * give up. - */ - break; - else - continue; - } - - (void) thr_create(NULL, NULL, svcstart, (void *)id, - THR_BOUND | THR_DETACHED, &tid); - } - - thr_exit(NULL); - return (NULL); -} - -static int -svcwait(int id) -{ - thread_t tid; - - /* - * Create a bound thread to wait for kernel LWPs that - * need to be created. - */ - if (thr_create(NULL, NULL, svcblock, (void *)id, - THR_BOUND | THR_DETACHED, &tid)) - return (1); - - return (0); -} -#endif /* Solaris 9+ */ |