Example #1
0
void
svc_run()
{
#ifdef FD_SETSIZE
	fd_set readfds;
#else
      int readfds;
#endif /* def FD_SETSIZE */
	extern int errno;

	for (;;) {
#ifdef FD_SETSIZE
		readfds = svc_fdset;
#else
		readfds = svc_fds;
#endif /* def FD_SETSIZE */
		switch (select(_rpc_dtablesize(), &readfds, (void *)0, (void *)0,
			       (struct timeval *)0)) {
		case -1:
			if (errno == EINTR) {
				continue;
			}
			perror("svc_run: - select failed");
			return;
		case 0:
			continue;
		default:
			svc_getreqset(&readfds);
		}
	}
}
Example #2
0
/* Activate a transport handle. */
void
xprt_register (SVCXPRT *xprt)
{
  register int sock = xprt->xp_sock;
  register int i;

  if (xports == NULL)
    {
      xports = (SVCXPRT **) malloc (_rpc_dtablesize () * sizeof (SVCXPRT *));
      if (xports == NULL) /* DonĀ“t add handle */
	return;
    }

  if (sock < _rpc_dtablesize ())
    {
      xports[sock] = xprt;
      if (sock < FD_SETSIZE)
	FD_SET (sock, &svc_fdset);

      /* Check if we have an empty slot */
      for (i = 0; i < svc_max_pollfd; ++i)
	if (svc_pollfd[i].fd == -1)
	  {
#ifdef __MINT__
/* XXX not supported yet, result in EINVAL */
#undef POLLRDBAND
#define POLLRDBAND 0
#endif
	    svc_pollfd[i].fd = sock;
	    svc_pollfd[i].events = (POLLIN | POLLPRI |
				    POLLRDNORM | POLLRDBAND);
	    return;
	  }

      ++svc_max_pollfd;
      svc_pollfd = realloc (svc_pollfd,
			    sizeof (struct pollfd) * svc_max_pollfd);
      if (svc_pollfd == NULL) /* Out of memory */
	return;

      svc_pollfd[svc_max_pollfd - 1].fd = sock;
      svc_pollfd[svc_max_pollfd - 1].events = (POLLIN | POLLPRI |
					       POLLRDNORM | POLLRDBAND);
    }
}
Example #3
0
/*
 * returns pid, or -1 for failure
 */
int
_openchild (const char *command, FILE ** fto, FILE ** ffrom)
{
  int i;
  int pid;
  int pdto[2];
  int pdfrom[2];

  if (__pipe (pdto) < 0)
    goto error1;
  if (__pipe (pdfrom) < 0)
    goto error2;
  switch (pid = __fork ())
    {
    case -1:
      goto error3;

    case 0:
      /*
       * child: read from pdto[0], write into pdfrom[1]
       */
      __close (0);
      __dup (pdto[0]);
      __close (1);
      __dup (pdfrom[1]);
      fflush (stderr);
      for (i = _rpc_dtablesize () - 1; i >= 3; i--)
	__close (i);
      fflush (stderr);
      execlp (command, command, NULL);
      perror ("exec");
      _exit (~0);

    default:
      /*
       * parent: write into pdto[1], read from pdfrom[0]
       */
      *fto = __fdopen (pdto[1], "w");
      __close (pdto[0]);
      *ffrom = __fdopen (pdfrom[0], "r");
      __close (pdfrom[1]);
      break;
    }
  return pid;

  /*
   * error cleanup and return
   */
error3:
  __close (pdfrom[0]);
  __close (pdfrom[1]);
error2:
  __close (pdto[0]);
  __close (pdto[1]);
error1:
  return -1;
}
Example #4
0
/* Activate a transport handle. */
void
xprt_register (SVCXPRT *xprt)
{
  register int sock = xprt->xp_sock;
  register int i;

  if (xports == NULL)
    {
      xports = (SVCXPRT **) calloc (_rpc_dtablesize (), sizeof (SVCXPRT *));
      if (xports == NULL) /* Don't add handle */
	return;
    }

  if (sock < _rpc_dtablesize ())
    {
      struct pollfd *new_svc_pollfd;

      xports[sock] = xprt;
      if (sock < FD_SETSIZE)
	FD_SET (sock, &svc_fdset);

      /* Check if we have an empty slot */
      for (i = 0; i < svc_max_pollfd; ++i)
	if (svc_pollfd[i].fd == -1)
	  {
	    svc_pollfd[i].fd = sock;
	    svc_pollfd[i].events = (POLLIN | POLLPRI |
				    POLLRDNORM | POLLRDBAND);
	    return;
	  }

      new_svc_pollfd = (struct pollfd *) realloc (svc_pollfd,
						  sizeof (struct pollfd)
						  * (svc_max_pollfd + 1));
      if (new_svc_pollfd == NULL) /* Out of memory */
	return;
      svc_pollfd = new_svc_pollfd;
      ++svc_max_pollfd;

      svc_pollfd[svc_max_pollfd - 1].fd = sock;
      svc_pollfd[svc_max_pollfd - 1].events = (POLLIN | POLLPRI |
					       POLLRDNORM | POLLRDBAND);
    }
}
Example #5
0
void
svc_getreqset (fd_set *readfds)
{
  register u_int32_t mask;
  register u_int32_t *maskp;
  register int setsize;
  register int sock;
  register int bit;

  setsize = _rpc_dtablesize ();
  maskp = (u_int32_t *) readfds->fds_bits;
  for (sock = 0; sock < setsize; sock += 32)
    for (mask = *maskp++; (bit = ffs (mask)); mask ^= (1 << (bit - 1)))
      svc_getreq_common (sock + bit - 1);
}
Example #6
0
void
svc_getreqset (fd_set *readfds)
{
  register fd_mask mask;
  register fd_mask *maskp;
  register int setsize;
  register int sock;
  register int bit;

  setsize = _rpc_dtablesize ();
  if (setsize > FD_SETSIZE)
    setsize = FD_SETSIZE;
  maskp = readfds->fds_bits;
  for (sock = 0; sock < setsize; sock += NFDBITS)
    for (mask = *maskp++; (bit = ffsl (mask)); mask ^= (1L << (bit - 1)))
      svc_getreq_common (sock + bit - 1);
}
Example #7
0
/* De-activate a transport handle. */
void
xprt_unregister (SVCXPRT *xprt)
{
  register int sock = xprt->xp_sock;
  register int i;

  if ((sock < _rpc_dtablesize ()) && (xports[sock] == xprt))
    {
      xports[sock] = (SVCXPRT *) 0;

      if (sock < FD_SETSIZE)
	FD_CLR (sock, &svc_fdset);

      for (i = 0; i < svc_max_pollfd; ++i)
	if (svc_pollfd[i].fd == sock)
	  svc_pollfd[i].fd = -1;
    }
}
Example #8
0
static void
ypupdated_svc_run(void)
{
#ifdef FD_SETSIZE
	fd_set readfds;
#else
	int readfds;
#endif /* def FD_SETSIZE */
	extern int forked;
	int pid;
	int fd_setsize = _rpc_dtablesize();

	/* Establish the identity of the parent ypupdated process. */
	pid = getpid();

	for (;;) {
#ifdef FD_SETSIZE
		readfds = svc_fdset;
#else
		readfds = svc_fds;
#endif /* def FD_SETSIZE */
		switch (select(fd_setsize, &readfds, NULL, NULL,
			       (struct timeval *)0)) {
		case -1:
			if (errno == EINTR) {
				continue;
			}
			warn("svc_run: - select failed");
			return;
		case 0:
			continue;
		default:
			svc_getreqset(&readfds);
			if (forked && pid != getpid())
				exit(0);
		}
	}
}
Example #9
0
int
rtime(struct sockaddr_in *addrp, struct timeval *timep,
    struct timeval *timeout)
{
	int s;
	fd_set readfds;
	int res;
	unsigned long thetime;
	struct sockaddr_in from;
	socklen_t fromlen;
	int type;
	struct servent *serv;

	if (timeout == NULL) {
		type = SOCK_STREAM;
	} else {
		type = SOCK_DGRAM;
	}
	s = _socket(AF_INET, type, 0);
	if (s < 0) {
		return(-1);
	}
	addrp->sin_family = AF_INET;

	/* TCP and UDP port are the same in this case */
	if ((serv = getservbyname("time", "tcp")) == NULL) {
		return(-1);
	}

	addrp->sin_port = serv->s_port;

	if (type == SOCK_DGRAM) {
		res = _sendto(s, (char *)&thetime, sizeof(thetime), 0,
			     (struct sockaddr *)addrp, sizeof(*addrp));
		if (res < 0) {
			do_close(s);
			return(-1);
		}
		do {
			FD_ZERO(&readfds);
			FD_SET(s, &readfds);
			res = _select(_rpc_dtablesize(), &readfds,
				     (fd_set *)NULL, (fd_set *)NULL, timeout);
		} while (res < 0 && errno == EINTR);
		if (res <= 0) {
			if (res == 0) {
				errno = ETIMEDOUT;
			}
			do_close(s);
			return(-1);
		}
		fromlen = sizeof(from);
		res = _recvfrom(s, (char *)&thetime, sizeof(thetime), 0,
			       (struct sockaddr *)&from, &fromlen);
		do_close(s);
		if (res < 0) {
			return(-1);
		}
	} else {
		if (_connect(s, (struct sockaddr *)addrp, sizeof(*addrp)) < 0) {
			do_close(s);
			return(-1);
		}
		res = _read(s, (char *)&thetime, sizeof(thetime));
		do_close(s);
		if (res < 0) {
			return(-1);
		}
	}
	if (res != sizeof(thetime)) {
		errno = EIO;
		return(-1);
	}
	thetime = ntohl(thetime);
	timep->tv_sec = thetime - TOFFSET;
	timep->tv_usec = 0;
	return(0);
}
Example #10
0
/*
 * __rpc_get_time_offset()
 *
 * This function uses a nis_server structure to contact the a remote
 * machine (as named in that structure) and returns the offset in time
 * between that machine and this one. This offset is returned in seconds
 * and may be positive or negative.
 *
 * The first time through, a lot of fiddling is done with the netconfig
 * stuff to find a suitable transport. The function is very aggressive
 * about choosing UDP or at worst TCP if it can. This is because
 * those transports support both the RCPBIND call and the internet
 * time service.
 *
 * Once through, *uaddr is set to the universal address of
 * the machine and *netid is set to the local netid for the transport
 * that uaddr goes with. On the second call, the netconfig stuff
 * is skipped and the uaddr/netid pair are used to fetch the netconfig
 * structure and to then contact the machine for the time.
 *
 * td = "server" - "client"
 */
int
__rpc_get_time_offset(struct timeval *td, /* Time difference */
                      nis_server *srv,  /* NIS Server description */
                      char  *thost,  /* if no server, this is the timehost */
                      char  **uaddr, /* known universal address */
                      struct sockaddr_in *netid /* known network identifier */)
{
    CLIENT   *clnt;   /* Client handle  */
    AUTH     *auth;
    endpoint  *ep,  /* useful endpoints */
        *useep = NULL; /* endpoint of xp */
    char   *useua = NULL; /* uaddr of selected xp */
    int   epl, i;  /* counters  */
    enum clnt_stat  status;  /* result of clnt_call */
    u_long   thetime, delta;
    int   needfree = 0;
    struct timeval tv;
    struct timespec ts;
    int   time_valid;
    int   udp_ep = -1, tcp_ep = -1;
    int   a1, a2, a3, a4;
    char   ut[64], ipuaddr[64];
    endpoint  teps[32];
    nis_server  tsrv;
    void   (*oldsig)() = NULL; /* old alarm handler */
    struct sockaddr_in sin;
    int   s = RPC_ANYSOCK;
    socklen_t len;
    int   type = 0;
    div_t q;

    td->tv_sec = 0;
    td->tv_usec = 0;

    /*
     * First check to see if we need to find and address for this
     * server.
     */
    if (*uaddr == NULL) {
        if ((srv != NULL) && (thost != NULL)) {
            msg("both timehost and srv pointer used!");
            return (0);
        }
        if (! srv) {
            srv = get_server(netid, thost, &tsrv, teps, 32);
            if (srv == NULL) {
                msg("unable to contruct server data.");
                return (0);
            }
            needfree = 1; /* need to free data in endpoints */
        }

        ep = srv->ep.ep_val;
        epl = srv->ep.ep_len;

        /* Identify the TCP and UDP endpoints */
        for (i = 0;
             (i < epl) && ((udp_ep == -1) || (tcp_ep == -1)); i++) {
            if (strcasecmp(ep[i].proto, "udp") == 0)
                udp_ep = i;
            if (strcasecmp(ep[i].proto, "tcp") == 0)
                tcp_ep = i;
        }

        /* Check to see if it is UDP or TCP */
        if (tcp_ep > -1) {
            useep = &ep[tcp_ep];
            useua = ep[tcp_ep].uaddr;
            type = SOCK_STREAM;
        } else if (udp_ep > -1) {
            useep = &ep[udp_ep];
            useua = ep[udp_ep].uaddr;
            type = SOCK_DGRAM;
        }

        if (useep == NULL) {
            msg("no acceptable transport endpoints.");
            if (needfree)
                free_eps(teps, tsrv.ep.ep_len);
            return (0);
        }
    }

    /*
     * Create a sockaddr from the uaddr.
     */
    if (*uaddr != NULL)
        useua = *uaddr;

    /* Fixup test for NIS+ */
    sscanf(useua, "%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
    sprintf(ipuaddr, "%d.%d.%d.%d.0.111", a1, a2, a3, a4);
    useua = &ipuaddr[0];

    bzero((char *)&sin, sizeof(sin));
    if (uaddr_to_sockaddr(useua, &sin)) {
        msg("unable to translate uaddr to sockaddr.");
        if (needfree)
            free_eps(teps, tsrv.ep.ep_len);
        return (0);
    }

    /*
     * Create the client handle to rpcbind. Note we always try
     * version 3 since that is the earliest version that supports
     * the RPCB_GETTIME call. Also it is the version that comes
     * standard with SVR4. Since most everyone supports TCP/IP
     * we could consider trying the rtime call first.
     */
    clnt = clnttcp_create(&sin, RPCBPROG, RPCBVERS, &s, 0, 0);
    if (clnt == NULL) {
        msg("unable to create client handle to rpcbind.");
        if (needfree)
            free_eps(teps, tsrv.ep.ep_len);
        return (0);
    }

    auth = authnone_ncreate(); /* idempotent */

    tv.tv_sec = 5;
    tv.tv_usec = 0;
    time_valid = 0;
    status = clnt_call(clnt, auth, RPCBPROC_GETTIME, (xdrproc_t)xdr_void, NULL,
                       (xdrproc_t)xdr_u_long, &thetime, tv);
    /*
     * The only error we check for is anything but success. In
     * fact we could have seen PROGMISMATCH if talking to a 4.1
     * machine (pmap v2) or TIMEDOUT if the net was busy.
     */
    if (status == RPC_SUCCESS)
        time_valid = 1;
    else {
        int save;

        /* Blow away possible stale CLNT handle. */
        if (clnt != NULL) {
            clnt_destroy(clnt);
            clnt = NULL;
        }

        /*
         * Convert PMAP address into timeservice address
         * We take advantage of the fact that we "know" what
         * the universal address looks like for inet transports.
         *
         * We also know that the internet timeservice is always
         * listening on port 37.
         */
        sscanf(useua, "%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
        sprintf(ut, "%d.%d.%d.%d.0.37", a1, a2, a3, a4);

        if (uaddr_to_sockaddr(ut, &sin)) {
            msg("cannot convert timeservice uaddr to sockaddr.");
            goto error;
        }

        s = socket(AF_INET, type, 0);
        if (s == -1) {
            msg("unable to open fd to network.");
            goto error;
        }

        /*
         * Now depending on whether or not we're talking to
         * UDP we set a timeout or not.
         */
        if (type == SOCK_DGRAM) {
            struct timeval timeout = { 20, 0 };
            struct sockaddr_in from;
            fd_set readfds;
            int res;

            if (sendto(s, &thetime, sizeof(thetime), 0,
                       (struct sockaddr *)&sin, sizeof(sin)) == -1) {
                msg("udp : sendto failed.");
                goto error;
            }
            do {
                FD_ZERO(&readfds);
                FD_SET(s, &readfds);
                res = select(_rpc_dtablesize(), &readfds,
                             (fd_set *)NULL, (fd_set *)NULL, &timeout);
            } while (res < 0 && errno == EINTR);
            if (res <= 0)
                goto error;
            len = sizeof(from);
            res = recvfrom(s, (char *)&thetime, sizeof(thetime), 0,
                           (struct sockaddr *)&from, &len);
            if (res == -1) {
                msg("recvfrom failed on udp transport.");
                goto error;
            }
            time_valid = 1;
        } else {
            int res;

            oldsig = (void (*)())signal(SIGALRM, alarm_hndler);
            saw_alarm = 0; /* global tracking the alarm */
            alarm(20); /* only wait 20 seconds */
            res = connect(s, (struct sockaddr *)&sin, sizeof(sin));
            if (res == -1) {
                msg("failed to connect to tcp endpoint.");
                goto error;
            }
            if (saw_alarm) {
                msg("alarm caught it, must be unreachable.");
                goto error;
            }
            res = read(s, (char *)&thetime, sizeof(thetime));
            if (res != sizeof(thetime)) {
                if (saw_alarm)
                    msg("timed out TCP call.");
                else
                    msg("wrong size of results returned");

                goto error;
            }
            time_valid = 1;
        }
        save = errno;
        (void)close(s);
        errno = save;
        s = RPC_ANYSOCK;

        if (time_valid) {
            thetime = ntohl(thetime);
            thetime = thetime - TOFFSET; /* adjust to UNIX time */
        } else
            thetime = 0;
    }

    (void) clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
    tv.tv_sec = ts.tv_sec;
    q = div(ts.tv_nsec, 1000);
    tv.tv_usec = q.quot;

error:
    /*
     * clean up our allocated data structures.
     */

    if (s != RPC_ANYSOCK)
        (void)close(s);

    if (clnt != NULL)
        clnt_destroy(clnt);

    alarm(0); /* reset that alarm if its outstanding */
    if (oldsig) {
        signal(SIGALRM, oldsig);
    }

    /*
     * note, don't free uaddr strings until after we've made a
     * copy of them.
     */
    if (time_valid) {
        if (*uaddr == NULL)
            *uaddr = rpc_strdup(useua);

        /* Round to the nearest second */
        tv.tv_sec += (tv.tv_sec > 500000) ? 1 : 0;
        delta = (thetime > tv.tv_sec) ? thetime - tv.tv_sec :
            tv.tv_sec - thetime;
        td->tv_sec = (thetime < tv.tv_sec) ? - delta : delta;
        td->tv_usec = 0;
    } else {
        msg("unable to get the server's time.");
    }

    if (needfree)
        free_eps(teps, tsrv.ep.ep_len);

    return (time_valid);
}
Example #11
0
int
main(int argc, char *argv[])
{
    ni_status status;
    ni_name myname = argv[0];
    int create = 0;
    int log_pri = LOG_NOTICE;
    ni_name dbsource_name = NULL;
    ni_name dbsource_addr = NULL;
    ni_name dbsource_tag = NULL;
    struct rlimit rlim;
    char *str;
    unsigned db_checksum;
    FILE *logf;
    int nctoken;

    logf = NULL;
    forcedIsRoot = 0;

    Argv = argv;	/* Save program and argument information for setproctitle */
    Argc = argc;

    argc--;
    argv++;
    while (argc > 0 && **argv == '-')
    {
        if (strcmp(*argv, "-d") == 0)
        {
            debug = 1;
            log_pri = LOG_DEBUG;
            if (argc < 2) logf = stderr;
            else
            {
                debug = atoi(argv[1]);
                argc -= 1;
                argv += 1;
            }
        }
        else if (strcmp(*argv, "-l") == 0)
        {
            if (argc < 2) usage(myname);
            else
            {
                log_pri = atoi(argv[1]);
                argc -= 1;
                argv += 1;
            }
        }
        else if (strcmp(*argv, "-n") == 0) forcedIsRoot = 1;
        else if (strcmp(*argv, "-s") == 0) standalone = 1;
        else if (strcmp(*argv, "-m") == 0) create++;
        else if (strcmp(*argv, "-c") == 0)
        {
            if (argc < 4) usage(myname);

            create++;
            dbsource_name = argv[1];
            dbsource_addr = argv[2];
            dbsource_tag = argv[3];
            argc -= 3;
            argv += 3;
        }
        else usage(myname);

        argc--;
        argv++;
    }

    if (argc != 1) usage(myname);

    if (debug == 0)
    {
        closeall();
        if (standalone == 1) daemon(1, 1);
    }

    db_tag = malloc(strlen(argv[0]) + 1);
    strcpy(db_tag, argv[0]);

    str = malloc(strlen("netinfod ") + strlen(db_tag) + 1);
    sprintf(str, "netinfod %s", db_tag);
    system_log_open(str, (LOG_NDELAY | LOG_PID), LOG_NETINFO, logf);
    free(str);
    system_log_set_max_priority(log_pri);

    system_log(LOG_DEBUG, "version %s (pid %d) - starting",
               _PROJECT_VERSION_, getpid());

    rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
    setrlimit(RLIMIT_CORE, &rlim);

    rlim.rlim_cur = rlim.rlim_max = FD_SETSIZE;
    setrlimit(RLIMIT_NOFILE, &rlim);

    umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);

    srandom(gethostid() ^ time(NULL));

    readall_syslock = syslock_new(0);
    lockup_syslock= syslock_new(0);
    cleanupwait = CLEANUPWAIT;
    auth_count[GOOD] = 0;
    auth_count[BAD] = 0;
    auth_count[WGOOD] = 0;
    auth_count[WBAD] = 0;

    if (create)
    {
        if (dbsource_addr == NULL)
        {
            system_log(LOG_DEBUG, "creating master");
            status = dir_mastercreate(db_tag);
        }
        else
        {
            system_log(LOG_DEBUG, "creating clone");
            status = dir_clonecreate(db_tag, dbsource_name,
                                     dbsource_addr, dbsource_tag);
        }

        if (status != NI_OK)
        {
            system_log_close();
            exit(status);
        }
    }

    nctoken = -1;
    notify_register_signal(NETWORK_CHANGE_NOTIFICATION, SIGHUP, &nctoken);

    if (standalone == 0) signal(SIGTERM, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGHUP, (void *)catch_sighup);
    signal(SIGCHLD, (void *)readall_catcher);
    if (debug == 0)
    {
        signal(SIGINT, (void *)dblock_catcher);
        if (standalone == 0)
        {
            if (setsid() < 0) syslog(LOG_WARNING, "setsid failed: %m");
        }
    }

    writepid(db_tag);

    status = start_service(db_tag);
    if (status != NI_OK)
    {
        system_log(LOG_ERR, "start_service failed: %s - exiting", ni_error(status));
        system_log_close();
        exit(status);
    }

    setproctitle("netinfod %s (%s)", db_tag, i_am_clone ? "clone" : "master");

    if (i_am_clone)
    {
        system_log(LOG_DEBUG, "checking clone");
        cloneReadallResponseOK = get_clone_readall(db_ni);
        dir_clonecheck();
        if (get_sanitycheck(db_ni)) sanitycheck(db_tag);
        system_log(LOG_DEBUG, "finished clone check");
    }
    else
    {
        system_log(LOG_DEBUG, "setting up master server");
        promote_admins = get_promote_admins(db_ni);
        get_readall_info(db_ni, &max_readall_proxies, &strict_proxies);
        max_subthreads = get_max_subthreads(db_ni);
        update_latency_secs = get_update_latency(db_ni);

        /* Tracking readall proxy pids uses ObjC, so isolate it */
        initialize_readall_proxies(-1 == max_readall_proxies ?
                                   MAX_READALL_PROXIES : max_readall_proxies);

        system_log(LOG_DEBUG, "starting notify thread");
        (void) notify_start();
    }

    /* Shutdown gracefully after this point */
    if (standalone == 1) signal(SIGTERM, (void *)sig_shutdown);
    signal(SIGUSR1, (void *)sig_shutdown);

    system_log(LOG_DEBUG, "starting RPC service");

    ni_svc_run(_rpc_dtablesize() - (FD_SLOPSIZE + max_subthreads));

    system_log(LOG_DEBUG, "shutting down");

    /*
     * Tell the readall proxies to shut down
     */
    if (readall_proxies > 0)
    {
        system_log(LOG_INFO, "killing %d readall prox%s", readall_proxies,
                   1 == readall_proxies ? "y" : "ies");
        if (!kill_proxies())
            system_log(LOG_WARNING, "some readall proxies still running");
    }

    db_checksum = ni_getchecksum(db_ni);
    ni_shutdown(db_ni, db_checksum);
    system_log(LOG_INFO, "exiting; checksum %u", db_checksum);
    system_log_close();
    exit(0);
}