/*
* 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
* 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
*/
/*
*/
#include <errno.h>
#include <netdb.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 <tiuser.h>
#include <syslog.h>
#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#include <libgen.h>
#include <deflt.h>
#include <sys/resource.h>
#ifdef __NCALL__
#else /* !__NCALL__ */
#endif /* __NCALL__ */
#include <nsctl.h>
struct conn_ind {
};
struct conn_entry {
};
static char *progname;
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 void conn_close_oldest(void);
static void cots_listen_event(int, int);
static int nofile_increase(int);
static int is_listen_fd_index(int);
static int sndrsvcpool(int);
#endif
/*
* RPC protocol block. Useful for passing registration information.
*/
struct protob {
};
static int debugflg = 0;
/* used by cots_listen_event() */
/*
* Determine valid semantics for rdc.
*/
#define BE32_TO_U32(a) \
#ifdef DEBUG
/*
* Only support UDP in DEBUG mode for now
*/
#else
#endif
/*
* Number of elements to add to the poll array on each allocation.
*/
#ifdef __NCALL__
#else
#endif
static int rdc_fdr;
static int
open_rdc(void)
{
if (fd < 0)
return (-1);
}
static int
{
} else {
}
}
return (ret);
}
int
{
int fd;
return (-1);
}
/*
* Open the transport device.
*/
if (fd == -1) {
(nofile_increase(0) == 0)) {
/* Try again with a higher NOFILE limit. */
}
if (fd == -1) {
} else {
}
return (-1);
}
}
/*
* Pop timod because the RPC module must be as close as possible
* to the transport.
*/
} else {
}
}
return (-1);
}
/*
* Push rpcmod to filter data traffic to KRPC.
*/
return (-1);
}
} else {
"t_close failed on %d: %m", fd);
} else {
"t_close failed on %d: %s",
}
}
return (-1);
}
/* Tell CONS rpcmod to act like a server stream. */
"t_close failed on %d: %m", fd);
} else {
"t_close failed on %d: %s",
}
}
return (-1);
}
}
/*
* Re-push timod so that we will still be doing TLI
* operations on the descriptor.
*/
} else {
}
}
return (-1);
}
return (fd);
}
void
{
int error;
/*
* Save the error code across syslog(), just in case syslog()
* gets its own error and, therefore, overwrites errno.
*/
} else {
}
}
/*
* Called to set up service over a particular transport
*/
void
{
int vers;
int sock;
if (provider) {
&retnconf);
} else {
"Cannot establish %s service over %s: transport setup problem.",
return;
}
if (sock == -1) {
if ((Is_ipv6present() &&
"Cannot establish %s service over %s: transport "
"setup problem.",
return;
}
return;
}
/*
* Register all versions of the programs in the protocol block list
*/
}
/* Don't drop core if supporting module(s) aren't loaded. */
/*
* svc() doesn't block, it returns success or failure.
*/
"over <file desc. %d, protocol %s> : %m. Exiting",
exit(1);
}
}
/*
* We successfully set up the server over this transport.
* Add this descriptor to the one being polled on.
*/
}
/*
* Returns -1 for failure, 0 for success.
*/
int
{
return (-1);
}
}
(void) endnetconfig(nc);
return (0);
}
/*
* client has been configured for number of threads, backlog or transport
* provider.
*/
static void
read_default(void)
{
int errno;
int tmp;
/* Fail silently if error in opening the default rdc config file */
errno = 0;
if (errno == 0) {
}
}
errno = 0;
if (errno == 0) {
}
}
errno = 0;
if (errno == 0) {
}
}
/* close defaults file */
}
}
#ifdef lint
int
#else
int
#endif
{
int allflag = 0;
int pid;
int i, rc;
char **providerp;
char *required;
int maxservers;
#endif
#ifdef __NCALL__
(void) textdomain("ncall");
#else
(void) textdomain("rdc");
#endif
#ifdef __NCALL__
#else
#endif
if (rc < 0) {
gettext("%s: unable to determine the current "
exit(1);
gettext("%s: incorrect Solaris release (requires %s)\n"),
exit(1);
}
read_default();
/*
* Usage: <progname> [-c <number of threads>] [-t protocol] \
* [-d] [-l <listen backlog>]
*/
switch (i) {
case 'a':
allflag = 1;
break;
case 'c':
if (max_conns_allowed <= 0)
max_conns_allowed = 16;
break;
case 'd':
debugflg++;
break;
case 't':
break;
case 'l':
if (listen_backlog < 0)
listen_backlog = 32;
break;
default:
"Usage: %s [-c <number of threads>] "
"[-d] [-t protocol] "
"[-l <listen backlog>]\n", progname);
exit(1);
break;
}
}
exit(1);
}
exit(1);
}
if (!debugflg) {
if (pid < 0) {
exit(1);
}
if (pid != 0)
exit(0);
/*
* standard input, output, and error, and detach from
* controlling terminal.
*/
/* use closefrom(3C) from PSARC/2000/193 when possible */
closefrom(0);
#else
for (i = 0; i < _NFILE; i++)
(void) close(i);
#endif
(void) dup(1);
(void) setsid();
/*
* ignore all signals apart from SIGTERM.
*/
for (i = 1; i < _sys_nsig; i++)
}
/*
*/
if (sndrsvcpool(maxservers)) {
"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)) {
"Can't set up %s pool creator: %m, Exiting", progname);
exit(1);
}
#endif
/*
* Build a protocol block list for registration.
*/
if (allflag) {
exit(1);
} else if (trans_provider)
else {
for (providerp = defaultproviders;
}
}
done:
/*
* Poll for non-data control events on the transport descriptors.
*/
return (-1);
}
static int
{
int *ip;
/* LINTED pointer alignment */
/* LINTED pointer alignment */
*ip = 1;
} else {
}
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) {
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:
"poll failed: %m. Exiting");
exit(1);
}
default:
break;
}
/*
* Go through the poll list looking for events.
*/
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.
*/
NC_TPI_CLTS) {
poll_array[i].fd, i);
} else {
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.
*/
"Error (%m) reading descriptor %d"
"/transport %s. Closing it.",
poll_array[i].fd,
(void) sleep(5);
}
}
}
"All transports have been closed with errors. Exiting.");
}
/*
*/
static void
{
static int poll_array_size = 0;
/*
* If the arrays are full, allocate new ones.
*/
if (num_fds == poll_array_size) {
if (poll_array_size != 0) {
tpa = poll_array;
tnp = conn_polled;
} else
/*
* Allocate new arrays.
*/
poll_array = (struct pollfd *)
conn_polled = (struct conn_entry *)
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) {
num_fds * sizeof (struct conn_entry));
}
}
/*
* Set the descriptor and event list. All possible events are
* polled for.
*/
/*
* Copy the transport data over too.
*/
/*
* 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.
*/
"O_NONBLOCK): %m. Exiting",
exit(1);
}
/*
* Count this descriptor.
*/
++num_fds;
}
static void
{
int i;
int num_to_copy;
for (i = 0; i < num_fds; i++) {
--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));
sizeof (struct pollfd));
(void) memcpy((void *)&conn_polled[i],
(void *)&conn_polled[i+1],
num_to_copy * sizeof (struct conn_entry));
sizeof (struct conn_entry));
return;
}
}
}
static void
conn_close_oldest(void)
{
int fd;
int i1;
/*
* Find the oldest connection that is not already in the
* process of shutting down.
*/
return;
break;
}
#ifdef DEBUG
(void) printf("too many connections (%d), releasing oldest (%d)\n",
#else
#endif
/*
* For politeness, send a T_DISCON_REQ to the transport
* provider. We close the stream anyway.
*/
num_conns--;
} else {
/*
* For orderly release, we do not close the stream
* until the T_ORDREL_IND arrives to complete
* the handshake.
*/
}
}
static boolean_t
{
return (FALSE);
}
/* LINTED pointer alignment */
return (FALSE);
}
return (FALSE);
}
"rejecting inbound connection(%s) with %d bytes "
"of connect data",
return (FALSE);
}
} else {
}
return (TRUE);
}
static int
{
return (-1);
}
return (0);
do {
else {
}
}
break;
}
return (0);
}
static void
{
int event;
int new_fd;
int ret = 0;
else {
}
/*
* 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.
*/
/*
* Create a new transport endpoint for the same proto as
* the listener.
*/
if (new_fd == -1) {
continue;
}
continue;
}
goto do_next_conn;
}
switch (event) {
case T_LISTEN:
continue;
case T_DISCONNECT:
&conn_head);
continue;
default:
"unexpected event 0x%x during "
"accept processing (%s)",
goto do_next_conn;
}
}
}
continue;
}
/* Tell kRPC about the new stream. */
if (ret < 0) {
"unable to register with kernel rpc: %m");
goto do_next_conn;
}
/*
* Poll on the new descriptor so that we get disconnect
* and orderly release indications.
*/
num_conns++;
/* Reset nconf in case it has been moved. */
}
}
static int
{
int event;
int i1;
int flags;
const char *errorstr;
switch (event) {
case T_LISTEN:
break;
case T_DATA:
/*
* Receive a private notification from CONS rpcmod.
*/
if (i1 == -1) {
break;
}
if (i1 < sizeof (int))
break;
/*
* 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.
*/
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.
*/
"unable to send orderly release %m");
}
} else
"unexpected event from CONS rpcmod %d", i1);
break;
case T_ORDREL:
/* Perform an orderly release. */
/* T_ORDREL on listen fd's should be ignored */
if (!is_listen_fd_index(fd)) {
goto fdclose;
}
break;
break;
} else {
/*
* check to make sure we do not close
* listen fd
*/
if (!is_listen_fd_index(fd))
break;
else
goto fdclose;
}
case T_DISCONNECT:
/*
* T_DISCONNECT on listen fd's should be ignored.
*/
if (!is_listen_fd_index(fd))
break;
else
goto fdclose;
default:
"Unknown error num %d", errno);
}
} else if (event == -1)
else
errorstr = "";
#ifdef DEBUG
"unexpected TLI event (0x%x) on "
"connection-oriented transport(%s, %d):%s",
#endif
num_conns--;
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
{
int error;
int ret;
int flags;
/*
* 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.
*/
/*
* 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 (unitdata) {
}
if (uderr) {
}
}
/*
* Allocate a unitdata structure for receiving the event.
*/
/* LINTED pointer alignment */
/*
* Save the error code across
* syslog(), just in case
* syslog() gets its own error
* and therefore overwrites errno.
*/
"T_UNITDATA) failed: %m",
return (error);
}
"transport %s, T_UNITDATA) failed TLI error %d",
goto flush_it;
}
}
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.
*/
"transport %s) got unexpected data, %d bytes",
/*
* Even though we don't expect any data, in case we do,
* keep reading until there is no 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.
*/
return (error);
case TLOOK:
break;
default:
goto flush_it;
}
switch (ret) {
case 0:
return (0);
case -1:
/*
* System errors are returned to caller.
*/
/*
* Save the error code across
* syslog(), just in case
* syslog() gets its own error
* and therefore overwrites errno.
*/
return (error);
}
goto flush_it;
case T_UDERR:
break;
default:
"transport %s) returned %d not T_UDERR (%d)",
}
/* LINTED pointer alignment */
/*
* Save the error code across
* syslog(), just in case
* syslog() gets its own error
* and therefore overwrites errno.
*/
"T_UDERROR) failed: %m",
return (error);
}
"transport %s, T_UDERROR) failed TLI error: %d",
goto flush_it;
}
}
if (ret == 0) {
/*
* Save the datagram error in errno, so that the
* %m argument to syslog picks up the error string.
*/
/*
* Log the datagram error, then log the host that
* probably triggerred. Cannot log both in the
* same transaction because of packet size limitations
*/
"generated error: %m",
/*
* Try to map the client's address back to a
* name.
*/
host->h_hostservs) {
"Bad %s response was sent to client with "
"host name: %s; service port: %s",
} else {
int i, j;
char *buf;
/*
* Mapping failed, print the whole thing
* in ASCII hex.
*/
}
buf[j] = '\0';
"Bad %s response was sent to client with "
"transport address: 0x%s",
}
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.
*/
return (error);
default:
goto 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.
*/
/*
* Read and discard the message. Do this this until there is
*/
do {
flags = 0;
if (ret == -1)
return (errno);
} while (ret != 0);
return (0);
}
/*
* Establish service thread.
*/
static int
{
#ifdef __NCALL__
#else /* !__NCALL__ */
struct rdc_svc_args nsa;
_rdc_ioctl_t rdc_args = { 0, };
#endif /* __NCALL__ */
#ifdef __NCALL__
#else /* !__NCALL__ */
#endif /* __NCALL__ */
}
static int
{
"nofile_increase() getrlimit of NOFILE failed: %m");
return (-1);
}
if (limit > 0)
else
"nofile_increase() setrlimit of NOFILE to %d failed: %m",
return (-1);
}
return (0);
}
int
{
int fd;
return (-1);
}
"%s host %s service %s",
}
return (-1);
}
/*
* 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.
*/
"couldn't set SO_REUSEADDR option on transport");
}
}
else
/* LINTED pointer alignment */
return (-1);
}
return (-1);
}
/* make sure we bound to the right address */
return (-1);
}
/*
* Disable the Nagle algorithm on TCP connections.
* Connections accepted from this listener will
* inherit the listener options.
*/
/* LINTED pointer alignment */
/* LINTED pointer alignment */
t_errno);
}
}
return (fd);
}
/* ARGSUSED */
static int
{
return (-1);
}
if (OK_TPI_TYPE(nconf) &&
}
}
(void) endnetconfig(nc);
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
{
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
{
/*
* Find the size of the address we need to mask.
*/
t_error("t_getinfo");
return (-1);
}
return (-1);
}
return (-1);
}
/*
* Set the mask so that the port is ignored.
*/
/* LINTED pointer alignment */
(in_addr_t)~0;
/* LINTED pointer alignment */
}
#ifdef NC_INET6
/* LINTED pointer alignment */
/* LINTED pointer alignment */
(sa_family_t)~0;
}
#endif
else {
/*
* Set all mask bits.
*/
}
return (0);
}
static int
{
npa.max_same_xprt = 0;
}
/*
*/
#include <thread.h>
/*
*/
static void *
{
int err;
/*
* Interrupted by a signal while in the kernel.
* this process is still alive, try again.
*/
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.
*/
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 *
{
/* CONSTCOND */
while (1) {
int err;
/*
* Call into the kernel, and hang out there
* until a thread needs to be created.
*/
/*
* 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;
}
}
return (NULL);
}
static int
{
/*
* Create a bound thread to wait for kernel LWPs that
* need to be created.
*/
return (1);
return (0);
}
#endif /* Solaris 9+ */