root/usr/src/cmd/ypcmd/yp_b_subr.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
 * Portions of this source code were derived from Berkeley
 * under license from the Regents of the University of
 * California.
 */

#include "ypsym.h"
#include <stdlib.h>
#include "yp_b.h"
#include <string.h>
#include <limits.h>
#include <netconfig.h>
#include <netdir.h>
#include <rpc/clnt.h>
#include <syslog.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/statvfs.h>
#include <rpcsvc/nis.h>
#include <sys/systeminfo.h>

#ifndef NULL
#define NULL    0
#endif

#define YPSERVERS       "ypservers"

void ypbind_init_default();
static int ypbind_pipe_setdom();

static bool firsttime = TRUE;
static struct domain *known_domains;

extern struct netconfig *__rpc_getconf();
extern void *__rpc_setconf(), *__rpc_endconf();
extern CLIENT *__clnt_tp_create_bootstrap();
extern char *inet_ntoa();
extern int __rpc_get_local_uid();

extern listofnames *names();
extern void free_listofnames();

#define PINGTIME        10      /* Timeout for the ypservers list */
#define PINGTOTTIM 5    /* Total seconds for ping timeout */

static void broadcast_setup();
static void sigcld_handler();
static struct ypbind_binding *dup_ypbind_binding();
static struct netbuf *dup_netbuf();
static void free_ypbind_binding();
static void enable_exit();
static void ypbind_ping();
static struct domain *ypbind_point_to_domain();
static bool ypbind_broadcast_ack();
static int pong_servers();
void cache_binding();
void uncache_binding();

extern int setok;
extern int broadcast;
extern int cache_okay;

/*
 * Need to differentiate between RPC_UNKNOWNHOST returned by the RPC
 * library, and the same error caused by a local lookup failure in
 * /etc/hosts and/or /etc/inet/ipnodes.
 */
int hostNotKnownLocally;

/*ARGSUSED*/
void    *
ypbindproc_null_3(argp, clnt)
void    *argp;
CLIENT *clnt;
{
        static char     res;

        return ((void *) & res);
}

static void
enable_exit()
{
        static bool     done = FALSE;

        if (!done) {
                done = TRUE;
                sigset(SIGCHLD, (void (*)())sigcld_handler);
        }
}

int sigcld_event = 0;

static void
sigcld_handler()
{
        sigcld_event++;
#ifdef DEBUG
        fprintf(stderr, "ypbind sighandler: got SIGCLD signal (event=%d)\n",
                sigcld_event);
#endif
}


/*
 * This is a Unix SIGCHILD handler that notices when a broadcaster child
 * process has exited, and retrieves the exit status.  The broadcaster pid
 * is set to 0.  If the broadcaster succeeded, dom_report_success will be
 * be set to -1.
 */

void
broadcast_proc_exit()
{
        int pid, ret;
        siginfo_t infop;
        register struct domain *pdom;
        bool    succeeded = FALSE;

        sigcld_event = 0;
                                        /* ==== Why WEXITED? */
        while ((ret = waitid(P_ALL, 0, &infop, WNOHANG | WEXITED)) != -1) {
                switch (infop.si_code) {
                case CLD_EXITED:
                        succeeded = infop.si_status == 0;
                        break;
                case CLD_KILLED:
                case CLD_DUMPED:
                        succeeded = FALSE;
                        break;
                case CLD_TRAPPED:
                case CLD_STOPPED:
                case CLD_CONTINUED:
                        enable_exit();
                        return;
                }
                pid = infop.si_pid;

#ifdef DEBUG
                fprintf(stderr,
                        "ypbind event_handler: got wait from %d status = %d\n",
                        pid, infop.si_status);
#endif

        /* to aid the progeny print the infamous "not responding" message */
                firsttime = FALSE;

                for (pdom = known_domains; pdom != (struct domain *)NULL;
                                pdom = pdom->dom_pnext) {

                        if (pdom->dom_broadcaster_pid == pid) {
#ifdef DEBUG
                        fprintf(stderr,
                        "ypbind event_handler: got match %s\n", pdom->dom_name);
#endif
                                if (succeeded) {
                                        broadcast_setup(pdom);
                                }
                                if (pdom->broadcaster_pipe != 0) {
                                        xdr_destroy(&(pdom->broadcaster_xdr));
                                        fclose(pdom->broadcaster_pipe);
                                        pdom->broadcaster_pipe = 0;
                                        pdom->broadcaster_fd = -1;
                                }
                                pdom->dom_broadcaster_pid = 0;

                                break;
                        }
                }
        }       /* while loop */
        enable_exit();
}

static void
broadcast_setup(pdom)
struct domain *pdom;
{
        ypbind_setdom req;

        memset(&req, 0, sizeof (req));
        if (pdom->broadcaster_pipe) {
                pdom->dom_report_success = -1;
                if (xdr_ypbind_setdom(&(pdom->broadcaster_xdr), &req)) {
#ifdef DEBUG
        fprintf(stderr, "parent: broadcast_setup: got xdr ok \n");
#endif
                        ypbindproc_setdom_3(&req, (struct svc_req *)NULL,
                            (SVCXPRT *)NULL);
                        xdr_free(xdr_ypbind_setdom, (char *)&req);
                        gettimeofday(&(pdom->lastping), NULL);
                }
#ifdef DEBUG
                    else {
        fprintf(stderr, "ypbind parent: xdr_ypbind_setdom failed\n");
                }
#endif
        }
#ifdef DEBUG
            else {
        fprintf(stderr, "ypbind: internal error -- no broadcaster pipe\n");
        }
#endif
}

#define YPBIND_PINGHOLD_DOWN 5
/* Same as the ypbind_get_binding() routine in SunOS */
/*ARGSUSED*/
ypbind_resp *
ypbindproc_domain_3(argp, clnt)
ypbind_domain *argp;
CLIENT *clnt;
{
        static ypbind_resp resp;
        struct domain *cur_domain;
        int bpid;
        int fildes[2];

        memset((char *)&resp, 0, sizeof (resp));

#ifdef DEBUG
        fprintf(stderr, "\nypbindproc_domain_3: domain: %s\n",
                argp->ypbind_domainname);
#endif

        if ((int)strlen(argp->ypbind_domainname) > YPMAXDOMAIN) {

                resp.ypbind_status = YPBIND_FAIL_VAL;
                resp.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV;
                return (&resp);
        }

        if ((cur_domain = ypbind_point_to_domain(argp->ypbind_domainname)) !=
            (struct domain *)NULL) {
                if (cur_domain->dom_boundp) {

                        struct timeval tp;

                        (void) gettimeofday(&tp, NULL);
                        if ((tp.tv_sec - cur_domain->lastping.tv_sec) >
                                        YPBIND_PINGHOLD_DOWN) {
#ifdef DEBUG
        fprintf(stderr, "domain is bound pinging: %s\n",
                argp->ypbind_domainname);
#endif
                                (void) ypbind_ping(cur_domain);
                        }
                }

                /*
                 * Bound or not, return the current state of the binding.
                 */

                if (cur_domain->dom_boundp) {
#ifdef DEBUG
        fprintf(stderr, "server is up for domain: %s\n",
                argp->ypbind_domainname);
#endif
                        resp.ypbind_status = YPBIND_SUCC_VAL;
                        resp.ypbind_resp_u.ypbind_bindinfo =
                            cur_domain->dom_binding;
                } else {
#ifdef DEBUG
        fprintf(stderr, "domain is NOT bound returning: %s %d\n",
                argp->ypbind_domainname, cur_domain->dom_error);
#endif
                        resp.ypbind_status = YPBIND_FAIL_VAL;
                        resp.ypbind_resp_u.ypbind_error =
                            cur_domain->dom_error;
                }

        } else {
                resp.ypbind_status = YPBIND_FAIL_VAL;
                resp.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
        }
        /*
         * RETURN NOW: if successful, otherwise
         * RETURN LATER: after spawning off a child to do the "broadcast" work.
         */
        if (resp.ypbind_status == YPBIND_SUCC_VAL) {
#ifdef DEBUG
        fprintf(stderr, "yp_b_subr: returning success to yp_b_svc %d\n",
                resp.ypbind_status);
#endif
                return (&resp);
        }

        /* Go about the broadcast (really, pinging here) business */

        if ((cur_domain) && (!cur_domain->dom_boundp) &&
            (!cur_domain->dom_broadcaster_pid)) {
#ifdef DEBUG
        fprintf(stderr, "yp_b_subr: fork: boundp=%d broadcast_pid=%d\n",
                cur_domain->dom_boundp, cur_domain->dom_broadcaster_pid);
#endif
                /*
                 * The current domain is unbound, and there is no child
                 * process active now.  Fork off a child who will beg to the
                 * ypservers list one by one or broadcast and accept whoever
                 * commands the right domain.
                 */
                if (pipe(fildes) < 0) {
#ifdef DEBUG
        fprintf(stderr, "yp_b_subr: returning pipe failure to yp_b_svc %d\n",
                resp.ypbind_status);
#endif
                        return (&resp);
                }

                enable_exit();
                sighold(SIGCLD); /* add it to ypbind's signal mask */
                cur_domain->dom_report_success++;
                bpid = fork();
                if (bpid != 0) { /* parent */
                        if (bpid > 0) { /* parent started */
                                close(fildes[1]);
                                cur_domain->dom_broadcaster_pid = bpid;
                                cur_domain->broadcaster_fd = fildes[0];
                                cur_domain->broadcaster_pipe =
                                                fdopen(fildes[0], "r");
                                if (cur_domain->broadcaster_pipe)
                        xdrstdio_create(&(cur_domain->broadcaster_xdr),
                        (cur_domain->broadcaster_pipe), XDR_DECODE);

#ifdef DEBUG
        fprintf(stderr, "ypbindproc_domain_3: %s starting pid = %d try = %d\n",
            cur_domain->dom_name, bpid,
            cur_domain->dom_report_success);
        fprintf(stderr, "yp_b_subr: returning after spawning, to yp_b_svc %d\n",
                resp.ypbind_status);
#endif
                                sigrelse(SIGCLD);
                                /* remove it from ypbind's signal mask */
                                return (&resp);
                        } else { /* fork failed */
                                perror("fork");
                                close(fildes[0]);
                                close(fildes[1]);
#ifdef DEBUG
        fprintf(stderr, "yp_b_subr: returning fork failure to yp_b_svc %d\n",
                resp.ypbind_status);
#endif
                                sigrelse(SIGCLD);
                                return (&resp);
                        }
                } /* end parent */
                /* child only code */
                sigrelse(SIGCLD);
                close(fildes[0]);
                cur_domain->broadcaster_fd = fildes[1];
                cur_domain->broadcaster_pipe = fdopen(fildes[1], "w");
                if (cur_domain->broadcaster_pipe)
                        xdrstdio_create(&(cur_domain->broadcaster_xdr),
                                (cur_domain->broadcaster_pipe), XDR_ENCODE);
                else {
                        perror("fdopen-pipe");
                        exit(-1);
                }
                exit(pong_servers(cur_domain));
        }
#ifdef DEBUG
        fprintf(stderr, "yp_b_subr: lazy returns failure status yp_b_svc %d\n",
                        resp.ypbind_status);
#endif
        return (&resp);
}


/*
 * call ypbindproc_domain_3 and convert results
 *
 * This adds support for YP clients that send requests on
 * ypbind version 1 & 2 (i.e. clients before we started
 * using universal addresses and netbufs). This is supported
 * for binary compatibility for static 4.x programs. The
 * assumption used to be that clients coming in with ypbind vers 1
 * should be given the address of a server serving ypserv version 1.
 * However, since yp_bind routines in 4.x YP library try
 * to connect with ypserv version 2, even if they requested
 * binding using ypbind version 1, the ypbind process will
 * "always" look for only ypserv version 2 servers for all
 * (ypbind vers 1, 2, & 3) clients.
 */
ypbind_resp_2 *
ypbindproc_domain_2(argp, clnt)
domainname_2 *argp;
CLIENT *clnt;
{
        ypbind_domain arg_3;
        ypbind_resp *resp_3;
        static ypbind_resp_2 resp;

        arg_3.ypbind_domainname = *argp;
        resp_3 = ypbindproc_domain_3(&arg_3, clnt);
        if (resp_3 == NULL)
                return (NULL);
        resp.ypbind_status = resp_3->ypbind_status;
        if (resp_3->ypbind_status == YPBIND_SUCC_VAL) {
                struct sockaddr_in *sin;
                struct ypbind_binding_2 *bi;

                sin = (struct sockaddr_in *)
                    resp_3->ypbind_resp_u.ypbind_bindinfo->ypbind_svcaddr->buf;
                if (sin->sin_family == AF_INET) {
                        bi = &resp.ypbind_respbody_2.ypbind_bindinfo;
                        memcpy(&(bi->ypbind_binding_port), &sin->sin_port, 2);
                        memcpy(&(bi->ypbind_binding_addr), &sin->sin_addr, 4);
                } else {
                        resp.ypbind_respbody_2.ypbind_error = YPBIND_ERR_NOSERV;
                }
        } else {
                resp.ypbind_respbody_2.ypbind_error =
                    resp_3->ypbind_resp_u.ypbind_error;
        }
        return (&resp);
}

/* used to exchange information between pong_servers and ypbind_broadcast_ack */
struct domain *process_current_domain;

int
pong_servers(domain_struct)
struct domain *domain_struct; /* to pass back */
{
        char *domain = domain_struct->dom_name;
        CLIENT *clnt2;
        char *servername;
        listofnames *list, *lin;
        char serverfile[MAXNAMLEN];
        struct timeval timeout;
        int isok = 0, res = -1;
        struct netconfig *nconf;
        void *handle;
        int nconf_count;
        char rpcdomain[YPMAXDOMAIN+1];
        long inforet;

        /*
         * If the ``domain'' name passed in is not the same as the RPC
         * domain set from /etc/defaultdomain.  Then we set ``firsttime''
         * to TRUE so no error messages are ever syslog()-ed this
         * prevents a possible Denial of Service attack.
         */
        inforet = sysinfo(SI_SRPC_DOMAIN, &(rpcdomain[0]), YPMAXDOMAIN);
        if ((inforet > 0) && (strcmp(domain, rpcdomain) != 0))
                firsttime = TRUE;

        if (broadcast) {
                enum clnt_stat stat = RPC_SUCCESS;
#ifdef DEBUG
        fprintf(stderr, "pong_servers: doing an rpc_broadcast\n");
#endif
                /*
                 * Here we do the real SunOS thing that users love. Do a
                 * broadcast on the network and find out the ypserv. No need
                 * to do "ypinit -c", no setting up /etc/hosts file, and no
                 * recursion looking up the server's IP address.
                 */
                process_current_domain = domain_struct;
                stat = rpc_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
                        (xdrproc_t)xdr_ypdomain_wrap_string, (caddr_t)&domain,
                        xdr_int,  (caddr_t)&isok,
                        (resultproc_t)ypbind_broadcast_ack, "udp");
                if (stat == RPC_SYSTEMERROR || stat == RPC_UNKNOWNPROTO ||
                        stat == RPC_CANTRECV || stat == RPC_CANTSEND ||
                        stat == RPC_NOBROADCAST ||
                        stat == RPC_N2AXLATEFAILURE) {
                        syslog(LOG_ERR, "RPC/Transport subsystem failure %s\n",
                                clnt_sperrno(stat));
                        exit(-1);
                }
                if (domain_struct->broadcaster_pipe == 0)
                        /* init binding case */
                        return (domain_struct->dom_boundp - 1);
                if (domain_struct->dom_boundp) {
                        res = ypbind_pipe_setdom(NULL, domain,
                                        NULL, domain_struct);
                        if (domain_struct->dom_report_success > 0)
                                syslog(LOG_ERR,
                        "NIS server for domain \"%s\" OK", domain);
                } else if (firsttime == FALSE)
                        syslog(LOG_ERR,
        "NIS server not responding for domain \"%s\"; still trying", domain);
                return (res);
        }
#ifdef DEBUG
        fprintf(stderr, "pong_servers: ponging servers one by one\n");
#endif
        /*
         * Do the politically correct thing.. transport independent and
         * secure (trusts only listed servers).
         */

        /*
         * get list of possible servers for this domain
         */

        /*
         * get alias for domain: Things of the past..
         * sysvconfig();
         * (void) yp_getalias(domain, domain_alias, NAME_MAX);
         */
        sprintf(serverfile, "%s/%s/%s", BINDING, domain, YPSERVERS);
#ifdef DEBUG
        fprintf(stderr, "pong_servers: serverfile %s\n", serverfile);
#endif
        list = names(serverfile);
        if (list == NULL) {
                if (firsttime == FALSE)
                    syslog(LOG_ERR,
                        "service not installed, use /usr/sbin/ypinit -c");
                return (-1);
        }
        lin = list;
        for (list = lin; list; list = list->nextname) {
                servername = strtok(list->name, " \t\n");
                if (servername == NULL) continue;

                /* Check all datagram_v transports for this server */
                if ((handle = __rpc_setconf("datagram_v")) == NULL) {
                        syslog(LOG_ERR,
                        "ypbind: RPC operation on /etc/netconfig failed");
                        free_listofnames(lin);
                        return (-1);
                }

                nconf_count = 0;
                clnt2 = 0;
                while (clnt2 == 0 && (nconf = __rpc_getconf(handle)) != 0) {
                        nconf_count++;
                        /*
                         * We use only datagram here. It is expected to be udp.
                         * VERY IMPORTANT: __clnt_tp_create_bootstrap is a
                         * hacked up version that does not do netdir_getbyname.
                         */
                        hostNotKnownLocally = 0;
                        clnt2 =
        __clnt_tp_create_bootstrap(servername, YPPROG, YPVERS, nconf);
                }
                if (nconf_count == 0) {
                        syslog(LOG_ERR,
                        "ypbind: RPC operation on /etc/netconfig failed");
                        free_listofnames(lin);
                        return (-1);
                }

                if (clnt2 == 0) {
                        if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST &&
                                hostNotKnownLocally) {
                                syslog(LOG_ERR,
                "NIS server %s is not in local host files !", servername);
                        }
                        perror(servername);
                        clnt_pcreateerror("ypbind");
                        continue;
                }

                timeout.tv_sec = PINGTIME;
                timeout.tv_usec = 0;
                if ((enum clnt_stat) clnt_call(clnt2,
                    YPPROC_DOMAIN, (xdrproc_t)xdr_ypdomain_wrap_string,
                    (char *)&domain, xdr_int,
                    (char *)&isok, timeout) == RPC_SUCCESS) {
                        if (isok) {
                                if (domain_struct->dom_report_success > 0) {
                                        syslog(LOG_ERR,
                                "NIS server for domain \"%s\" OK", domain);
                                }
                                if (domain_struct->broadcaster_pipe == 0) {
                                        /* init binding case --parent */
                                        struct netconfig *setnc;
                                        struct netbuf setua;
                                        struct ypbind_binding *b =
                                                domain_struct->dom_binding;

                                        setnc =
                                getnetconfigent(clnt2->cl_netid);
                                        if (b == NULL) {
                                        /* ASSERT: This shouldn't happen ! */
                                                b =
                                (struct ypbind_binding *)calloc(1, sizeof (*b));
                                                domain_struct->dom_binding = b;
                                                if (b == NULL) {
                                                        __rpc_endconf(handle);
                                                        clnt_destroy(clnt2);
                                                        free_listofnames(lin);
                                                        return (-2);
                                                }
                                        }


                                        b->ypbind_nconf = setnc;
                                        clnt_control(clnt2, CLGET_SVC_ADDR,
                                                (char *)&setua);
                                        if (b->ypbind_svcaddr) {
                                                if (b->ypbind_svcaddr->buf)
                                free(b->ypbind_svcaddr->buf);
                                                free(b->ypbind_svcaddr);
                                        }
                                        b->ypbind_svcaddr = dup_netbuf(&setua);
                                        if (b->ypbind_servername)
                                                free(b->ypbind_servername);
                                        b->ypbind_servername =
                                                strdup(servername);
                                        b->ypbind_hi_vers = YPVERS;
                                        b->ypbind_lo_vers = YPVERS;
                                        __rpc_endconf(handle);
                                        domain_struct->dom_boundp = TRUE;
                                        clnt_destroy(clnt2);
                                        free_listofnames(lin);
                                        return (0);
                                }
                                res = ypbind_pipe_setdom(clnt2, domain,
                                        servername, domain_struct);
                                __rpc_endconf(handle);
                                clnt_destroy(clnt2);
                                free_listofnames(lin);
                                return (res);
                        } else {
                                syslog(LOG_ERR,
                                    "server %s doesn't serve domain %s\n",
                                    servername, domain);
                        }
                } else {
                        clnt_perror(clnt2, servername);
                }
                clnt_destroy(clnt2);
        }
        /*
         * We tried all servers, none obliged !
         * After ypbind is started up it will not be bound
         * immediately.  This is normal, no error message
         * is needed. Although, with the ypbind_init_default
         * it will be bound immediately.
         */
        if (firsttime == FALSE) {
                syslog(LOG_ERR,
"NIS server not responding for domain \"%s\"; still trying", domain);
        }
        free_listofnames(lin);
        __rpc_endconf(handle);
        return (-2);
}

struct netbuf *
dup_netbuf(inbuf)
        struct netbuf *inbuf;
{
        struct netbuf *outbuf;

        if (inbuf == NULL)
                return (NULL);
        if ((outbuf =
                (struct netbuf *)calloc(1, sizeof (struct netbuf))) == NULL)
                return (NULL);
        if ((outbuf->buf = malloc(inbuf->len)) == NULL) {
                free(outbuf);
                return (NULL);
        }
        outbuf->len = inbuf->len;
        outbuf->maxlen = inbuf->len;
        (void) memcpy(outbuf->buf, inbuf->buf, inbuf->len);
        return (outbuf);
}

/*
 * This is called by the broadcast rpc routines to process the responses
 * coming back from the broadcast request. Since the form of the request
 * which is used in ypbind_broadcast_bind is "respond only in the positive
 * case", we know that we have a server.
 * The internet address of the responding server will be picked up from
 * the saddr parameter, and stuffed into the domain.  The domain's boundp
 * field will be set TRUE.  The first responding server (or the first one
 * which is on a reserved port) will be the bound server for the domain.
 */
bool
ypbind_broadcast_ack(ptrue, nbuf, nconf)
        bool *ptrue;
        struct netbuf *nbuf;
        struct netconfig *nconf;
{
        struct ypbind_binding b;

        process_current_domain->dom_boundp = TRUE;
        b.ypbind_nconf = nconf;
        b.ypbind_svcaddr = nbuf;
        b.ypbind_servername = "\000";
        b.ypbind_hi_vers = YPVERS;
        b.ypbind_lo_vers = YPVERS;
        free_ypbind_binding(process_current_domain->dom_binding);
        process_current_domain->dom_binding = dup_ypbind_binding(&b);
        return (TRUE);
}

/*
 * WARNING: This routine is entered only by the child process.
 * Called if it pongs/broadcasts okay.
 */
static int
ypbind_pipe_setdom(client, domain, servername, opaque_domain)
CLIENT *client;
char *servername;
char *domain;
struct domain *opaque_domain;
{
        struct netconfig *setnc;
        struct netbuf setua;
        ypbind_binding setb;
        ypbind_setdom setd;
        int retval;

        setd.ypsetdom_domain = domain;
        if (client == NULL && opaque_domain->dom_binding) {
#ifdef DEBUG
        fprintf(stderr, "ypbind_pipe_setdom: child broadcast case ");
#endif
                /* ypbind_broadcast_ack already setup dom_binding for us */
                setd.ypsetdom_bindinfo = opaque_domain->dom_binding;
        } else if (client) {
#ifdef DEBUG
        fprintf(stderr, "ypbind_pipe_setdom: child unicast case ");
#endif
                setnc = getnetconfigent(client->cl_netid);
                if (setnc == NULL) {
#ifdef DEBUG
        fprintf(stderr, "PANIC: shouldn't happen\n");
#endif
                        fclose(opaque_domain->broadcaster_pipe);
                        close(opaque_domain->broadcaster_fd);
                        return (-2);
                }
                clnt_control(client, CLGET_SVC_ADDR, (char *)&setua);
                setb.ypbind_nconf = setnc;
                setb.ypbind_svcaddr = &setua;
                setb.ypbind_servername = servername;
                setb.ypbind_hi_vers = YPVERS;
                setb.ypbind_lo_vers = YPVERS;
                setd.ypsetdom_bindinfo = &setb;
        /*
         * Let's hardcode versions, that is the only ypserv we support anyway.
         * Avoid the song and dance of recursively calling ypbind_ping
         * for no reason. Consistent with the 4.1 policy, that if ypbind gets
         * a request on new binder protocol, the requestor is looking for the
         * new ypserv. And, we have even higher binder protocol version i.e. 3.
         */
        } else
                return (-1);
#ifdef DEBUG
        fprintf(stderr,
                " saving server settings, \nsupports versions %d thru %d\n",
                setd.ypsetdom_bindinfo->ypbind_lo_vers,
                setd.ypsetdom_bindinfo->ypbind_hi_vers);
#endif

        if (opaque_domain->broadcaster_pipe == 0) {
#ifdef DEBUG
        fprintf(stderr, "PANIC: shouldn't be in this function\n");
#endif
                return (-2);
        }
#ifdef DEBUG
        fprintf(stderr, "child: doing xdr_ypbind_setdom\n");
#endif
        retval = xdr_ypbind_setdom(&(opaque_domain->broadcaster_xdr), &setd);
        xdr_destroy(&(opaque_domain->broadcaster_xdr));
        fclose(opaque_domain->broadcaster_pipe);
        close(opaque_domain->broadcaster_fd);
        /*
         * This child process is about to exit. Don't bother freeing memory.
         */
        if (!retval) {
#ifdef DEBUG
        fprintf(stderr,
                "YPBIND pipe_setdom failed \n(xdr failure) to server %s\n",
                servername ? servername : "");
#endif
                return (-3);
        }
#ifdef DEBUG
        fprintf(stderr, "ypbind_pipe_setdom: YPBIND OK-set to server %s\n",
                servername ? servername : "");
#endif
        return (0);
}

/* Same as ypbind_set_binding in SunOS */
/*
 *  We use a trick from SunOS to return an error to the ypset command
 *  when we are not allowing the domain to be set.  We do a svcerr_noprog()
 *  to send RPC_PROGUNAVAIL to ypset.  We also return NULL so that
 *  our caller (ypbindprog_3) won't try to return a result.  This
 *  hack is necessary because the YPBINDPROC_SETDOM procedure is defined
 *  in the protocol to return xdr_void, so we don't have a direct way to
 *  return an error to the client.
 */
/*ARGSUSED*/
void    *
ypbindproc_setdom_3(argp, rqstp, transp)
ypbind_setdom *argp;
struct svc_req *rqstp;
SVCXPRT *transp;
{
        struct domain *a_domain;
        struct netbuf *who;
        static char res; /* dummy for void * return */
        uid_t caller_uid;

        if ((int)strlen(argp->ypsetdom_domain) > YPMAXDOMAIN) {

                if (transp) {
                        svcerr_systemerr(transp);
                        return (0);
                }
                return (&res);
        }

        if (transp != NULL) {
                /* find out who originated the request */
                char *uaddr;
                struct netconfig *nconf;

                who = svc_getrpccaller(transp);
                if ((nconf = getnetconfigent(transp->xp_netid))
                        == (struct netconfig *)NULL) {
                        svcerr_systemerr(transp);
                        return (0);
                }
                if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
                        uaddr = strdup("local host");
                } else {
                        uaddr = taddr2uaddr(nconf, who);
                }
                if (setok != YPSETALL) {
                /* for -ypset, it falls through and let anybody do a setdom ! */
                        if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0) {
                                syslog(LOG_ERR,
"ypset request from %s not on loopback, \
cannot set ypbind to %s", uaddr ? uaddr : "unknown source",
argp->ypsetdom_bindinfo->ypbind_servername);
                                if (uaddr)
                                        free(uaddr);
                                freenetconfigent(nconf);
                                svcerr_noprog(transp);
                                return (0);
                        }
                        switch (setok) {
                        case YPSETNONE:
                                if (strcmp(nconf->nc_protofmly,
                                        NC_LOOPBACK) == 0)
                                        syslog(LOG_ERR,
"ypset request to %s from %s failed - ypset not allowed",
argp->ypsetdom_bindinfo->ypbind_servername, uaddr);
                                if (uaddr)
                                        free(uaddr);
                                freenetconfigent(nconf);
                                svcerr_noprog(transp);
                                return (0);
                        case YPSETLOCAL:
                                if (__rpc_get_local_uid(transp,
                                        &caller_uid) < 0) {
                                        syslog(LOG_ERR, "ypset request from \
unidentified local user on %s - ypset not allowed",
transp->xp_netid);
                                        if (uaddr)
                                                free(uaddr);
                                        freenetconfigent(nconf);
                                        svcerr_noprog(transp);
                                        return (0);
                                }
                                if (caller_uid != 0) {
                                        syslog(LOG_ERR,
"Set domain request to host %s \
from local non-root user %ld failed - ypset not allowed",
argp->ypsetdom_bindinfo->ypbind_servername, caller_uid);
                                        if (uaddr)
                                                free(uaddr);
                                        freenetconfigent(nconf);
                                        svcerr_noprog(transp);
                                        return (0);
                                }
                        }
                }
                syslog(LOG_ERR, "Set domain request from %s : \
setting server for domain %s to %s", uaddr ? uaddr : "UNKNOWN SOURCE",
argp->ypsetdom_domain, argp->ypsetdom_bindinfo->ypbind_servername);
                if (uaddr)
                        free(uaddr);
                freenetconfigent(nconf);
        }

        if ((a_domain = ypbind_point_to_domain(argp->ypsetdom_domain))
            != (struct domain *)NULL) {
                /* setting binding; old may be invalid */
                uncache_binding(a_domain);

                /* this does the set -- should copy the structure */
                free_ypbind_binding(a_domain->dom_binding);
                if ((a_domain->dom_binding =
                    dup_ypbind_binding(argp->ypsetdom_bindinfo)) == NULL) {
                        syslog(LOG_ERR, "ypbindproc_setdom_3: out of memory, ",
                                "dup_ypbind_binding failed\n");
                        if (transp) {
                                svcerr_noprog(transp);
                                return (0);
                        }
                        return (&res);
                }
                gettimeofday(&(a_domain->lastping), NULL);
                a_domain->dom_boundp = TRUE;
                cache_binding(a_domain);
#ifdef DEBUG
        fprintf(stderr, "ypbindproc_setdom_3: setting domain %s to server %s\n",
                argp->ypsetdom_domain,
                argp->ypsetdom_bindinfo->ypbind_servername);
#endif
        }

        return (&res);
}

/*
 * This returns a pointer to a domain entry.  If no such domain existed on
 * the list previously, an entry will be allocated, initialized, and linked
 * to the list.  Note:  If no memory can be malloc-ed for the domain structure,
 * the functional value will be (struct domain *) NULL.
 */
static struct domain *
ypbind_point_to_domain(pname)
register char   *pname;
{
        register struct domain *pdom;
        char buf[300];

        for (pdom = known_domains; pdom != (struct domain *)NULL;
            pdom = pdom->dom_pnext) {
                if (strcmp(pname, pdom->dom_name) == 0)
                        return (pdom);
        }

        /* Not found.  Add it to the list */

        if (pdom = (struct domain *)calloc(1, sizeof (struct domain))) {
                pdom->dom_name = strdup(pname);
                if (pdom->dom_name == NULL) {
                        free((char *)pdom);
                        syslog(LOG_ERR,
                                "ypbind_point_to_domain: strdup failed\n");
                        return (NULL);
                }
                pdom->dom_pnext = known_domains;
                known_domains = pdom;
                pdom->dom_boundp = FALSE;
                pdom->dom_vers = YPVERS; /* This doesn't talk to old ypserv */
                pdom->dom_binding = NULL;
                pdom->dom_error = YPBIND_ERR_NOSERV;
                pdom->ping_clnt = (CLIENT *)NULL;
                pdom->dom_report_success = -1;
                pdom->dom_broadcaster_pid = 0;
                pdom->broadcaster_pipe = 0;
                pdom->bindfile = -1;
                pdom->lastping.tv_sec = 0;
                pdom->lastping.tv_usec = 0; /* require ping */
                pdom->cache_fp = 0;
                sprintf(buf, "%s/%s/cache_binding", BINDING, pdom->dom_name);
                pdom->cache_file = strdup(buf);
                /*
                 *  We don't give an error if pdom->cache_file is not set.
                 *  If we got null (out of memory), then we just won't use
                 *  the cache file in cache_binding() (assuming the
                 *  application gets that far.
                 */
        }
        else
                syslog(LOG_ERR, "ypbind_point_to_domain: malloc failed\n");

        return (pdom);
}

static void
ypbind_ping(pdom)
struct domain *pdom;
{
        struct timeval timeout;
        int     vers;
        int     isok;

        if (pdom->dom_boundp == FALSE)
                return;
        vers = pdom->dom_vers;

        if (pdom->ping_clnt == (CLIENT *) NULL) {
                pdom->ping_clnt = __nis_clnt_create(RPC_ANYFD,
                                        pdom->dom_binding->ypbind_nconf, 0,
                                        pdom->dom_binding->ypbind_svcaddr, 0,
                                        YPPROG, vers, 0, 0);
        }

        if (pdom->ping_clnt == (CLIENT *) NULL) {
                perror("clnt_tli_create");
                clnt_pcreateerror("ypbind_ping()");
                pdom->dom_boundp = FALSE;
                pdom->dom_error = YPBIND_ERR_NOSERV;
                return;
        }


#ifdef DEBUG
        fprintf(stderr, "ypbind: ypbind_ping()\n");
#endif
        timeout.tv_sec = PINGTOTTIM;
        timeout.tv_usec =  0;
        if (clnt_call(pdom->ping_clnt,
            YPPROC_DOMAIN, (xdrproc_t)xdr_ypdomain_wrap_string,
                (char *)&pdom->dom_name, xdr_int, (char *)&isok,
                timeout) == RPC_SUCCESS) {
                        pdom->dom_boundp = isok;
                        pdom->dom_binding->ypbind_lo_vers = vers;
                        pdom->dom_binding->ypbind_hi_vers = vers;
#ifdef DEBUG
        fprintf(stderr,
                "Server pinged successfully, supports versions %d thru %d\n",
                pdom->dom_binding->ypbind_lo_vers,
                pdom->dom_binding->ypbind_hi_vers);
#endif
        } else {
                clnt_perror(pdom->ping_clnt, "ping");
                pdom->dom_boundp = FALSE;
                pdom->dom_error = YPBIND_ERR_NOSERV;
        }
        (void) gettimeofday(&(pdom->lastping), NULL);
        if (pdom->ping_clnt)
                clnt_destroy(pdom->ping_clnt);
        pdom->ping_clnt = (CLIENT *)NULL;
        if (pdom->dom_boundp)
                cache_binding(pdom);
}

static struct ypbind_binding *
dup_ypbind_binding(a)
struct ypbind_binding *a;
{
        struct ypbind_binding *b;
        struct netconfig *nca, *ncb;
        struct netbuf *nxa, *nxb;
        int i;

        b = (struct ypbind_binding *)calloc(1, sizeof (*b));
        if (b == NULL)
                return (b);
        b->ypbind_hi_vers = a->ypbind_hi_vers;
        b->ypbind_lo_vers = a->ypbind_lo_vers;
        b->ypbind_servername =
                a->ypbind_servername ? strdup(a->ypbind_servername) : NULL;
        ncb = (b->ypbind_nconf =
                (struct netconfig *)calloc(1, sizeof (struct netconfig)));
        nxb = (b->ypbind_svcaddr =
                (struct netbuf *)calloc(1, sizeof (struct netbuf)));
        nca = a->ypbind_nconf;
        nxa = a->ypbind_svcaddr;
        ncb->nc_flag = nca->nc_flag;
        ncb->nc_protofmly =
                nca->nc_protofmly ? strdup(nca->nc_protofmly) : NULL;
        ncb->nc_proto =
                nca->nc_proto ? strdup(nca->nc_proto) : NULL;
        ncb->nc_semantics = nca->nc_semantics;
        ncb->nc_netid =
                nca->nc_netid ? strdup(nca->nc_netid) : NULL;
        ncb->nc_device =
                nca->nc_device ? strdup(nca->nc_device) : NULL;
        ncb->nc_nlookups = nca->nc_nlookups;
        ncb->nc_lookups = (char **)calloc(nca->nc_nlookups, sizeof (char *));
        if (ncb->nc_lookups == NULL) {
                if (ncb->nc_device)
                        free(ncb->nc_device);
                if (ncb->nc_netid)
                        free(ncb->nc_netid);
                if (ncb->nc_proto)
                        free(ncb->nc_proto);
                if (ncb->nc_protofmly)
                        free(ncb->nc_protofmly);
                if (nxb)
                        free(nxb);
                if (ncb)
                        free(ncb);
                if (b->ypbind_servername)
                        free(b->ypbind_servername);
                if (b)
                        free(b);
                return (NULL);
        }
        for (i = 0; i < nca->nc_nlookups; i++)
                ncb->nc_lookups[i] =
                        nca->nc_lookups[i] ? strdup(nca->nc_lookups[i]) : NULL;
        for (i = 0; i < 8; i++)
                ncb->nc_unused[i] = nca->nc_unused[i];
        nxb->maxlen = nxa->maxlen;
        nxb->len = nxa->len;
        nxb->buf = malloc(nxa->maxlen);
        if (nxb->buf == NULL) {
                for (i = 0; i < nca->nc_nlookups; i++)
                        if (ncb->nc_lookups[i])
                                free(ncb->nc_lookups[i]);
                free(ncb->nc_lookups);
                if (ncb->nc_device)
                        free(ncb->nc_device);
                if (ncb->nc_netid)
                        free(ncb->nc_netid);
                if (ncb->nc_proto)
                        free(ncb->nc_proto);
                if (ncb->nc_protofmly)
                        free(ncb->nc_protofmly);
                if (nxb)
                        free(nxb);
                if (ncb)
                        free(ncb);
                if (b->ypbind_servername)
                        free(b->ypbind_servername);
                if (b)
                        free(b);
                return (NULL);
        }
        memcpy(nxb->buf, nxa->buf, nxb->len);
        return (b);
}

static void
free_ypbind_binding(b)
struct ypbind_binding *b;
{
        if (b == NULL)
                return;
        netdir_free((char *)b->ypbind_svcaddr, ND_ADDR);
        free(b->ypbind_servername);
        freenetconfigent(b->ypbind_nconf);
        free(b);
}

/*
 * Preloads teh default domain's domain binding. Domain binding for the
 * local node's default domain for ypserv version 2 (YPVERS) will be
 * set up. This may make it a little slower to start ypbind during
 * boot time, but would make it easy on other domains that rely on
 * this binding.
 */
void
ypbind_init_default()
{
        char domain[256];
        struct domain *cur_domain;

        if (getdomainname(domain, 256) == 0) {
                cur_domain = ypbind_point_to_domain(domain);

                if (cur_domain == (struct domain *)NULL) {
                        abort();
                }
                (void) pong_servers(cur_domain);
        }
}

bool_t
xdr_ypbind_binding_2(xdrs, objp)
        register XDR *xdrs;
        ypbind_binding_2 *objp;
{
        if (!xdr_opaque(xdrs, (char *)&(objp->ypbind_binding_addr), 4))
                return (FALSE);
        if (!xdr_opaque(xdrs, (char *)&(objp->ypbind_binding_port), 2))
                return (FALSE);
        return (TRUE);
}

bool_t
xdr_ypbind_resp_2(xdrs, objp)
        register XDR *xdrs;
        ypbind_resp_2 *objp;
{
        if (!xdr_ypbind_resptype(xdrs, &objp->ypbind_status))
                return (FALSE);
        switch (objp->ypbind_status) {
        case YPBIND_FAIL_VAL:
                if (!xdr_u_long(xdrs, &objp->ypbind_respbody_2.ypbind_error))
                        return (FALSE);
                break;
        case YPBIND_SUCC_VAL:
                if (!xdr_ypbind_binding_2(xdrs,
                    &objp->ypbind_respbody_2.ypbind_bindinfo))
                        return (FALSE);
                break;
        default:
                return (FALSE);
        }
        return (TRUE);
}

/*
 *  The following is some caching code to improve the performance of
 *  yp clients.  In the days of yore, a client would talk to rpcbind
 *  to get the address for ypbind, then talk to ypbind to get the
 *  address of the server.  If a lot of clients are doing this at
 *  the same time, then rpcbind and ypbind get bogged down and clients
 *  start to time out.
 *
 *  We cache two things:  the current address for ypserv, and the
 *  transport addresses for talking to ypbind.  These are saved in
 *  files in /var/yp.  To get the address of ypserv, the client opens
 *  a file and reads the address.  It does not have to talk to rpcbind
 *  or ypbind.  If this file is not available, then it can read the
 *  the transport address for talking to ypbind without bothering
 *  rpcbind.  If this also fails, then it uses the old method of
 *  talking to rpcbind and then ypbind.
 *
 *  We lock the first byte of the cache files after writing to them.
 *  This indicates to the client that they contents are valid.  The
 *  client should test the lock.  If the lock is held, then it can
 *  use the contents.  If the lock test fails, then the contents should
 *  be ignored.
 */

/*
 *  Cache new binding information for a domain in a file.  If the
 *  new binding is the same as the old, then we skip it.  We xdr
 *  a 'ypbind_resp', which is what would be returned by a call to
 *  the YBINDPROCP_DOMAIN service.  We xdr the data because it is
 *  easier than writing the data out field by field.  It would be
 *  nice if there were an xdrfd_create() that was similar to
 *  xdrstdio_create().  Instead, we do an fdopen and use xdrstdio_create().
 */
void
cache_binding(pdom)
        struct domain *pdom;
{
        int st;
        int fd;
        XDR xdrs;
        struct ypbind_resp resp;

        if (!cache_okay)
                return;

        /* if the domain doesn't have a cache file, then skip it */
        if (pdom->cache_file == 0)
                return;

        /*
         *  If we already had a cache file for this domain, remove it.  If
         *  a client just started accessing it, then it will either find
         *  it unlocked (and not use it), or continue to use it with
         *  old information.  This is not a problem, the client will
         *  either fail to talk to ypserv and try to bind again, or
         *  will continue to use the old server.
         */
        if (pdom->cache_fp) {
                fclose(pdom->cache_fp);    /* automatically unlocks */
                unlink(pdom->cache_file);
                pdom->cache_fp = 0;
        }

        fd = open(pdom->cache_file, O_CREAT|O_WRONLY, 0444);
        if (fd == -1)
                return;

        pdom->cache_fp = fdopen(fd, "w");
        if (pdom->cache_fp == 0) {
                close(fd);
                return;
        }

        xdrstdio_create(&xdrs, pdom->cache_fp, XDR_ENCODE);
        resp.ypbind_status = YPBIND_SUCC_VAL;
        resp.ypbind_resp_u.ypbind_bindinfo = pdom->dom_binding;

        if (!xdr_ypbind_resp(&xdrs, &resp)) {
                xdr_destroy(&xdrs);
                unlink(pdom->cache_file);
                fclose(pdom->cache_fp);
                pdom->cache_fp = 0;
                return;
        }
        xdr_destroy(&xdrs);    /* flushes xdr but leaves fp open */

        /* we lock the first byte to indicate that the file is valid */
        lseek(fd, 0L, SEEK_SET);
        st = lockf(fd, F_LOCK, 1);
        if (st == -1) {
                unlink(pdom->cache_file);
                fclose(pdom->cache_fp);
                pdom->cache_fp = 0;
        }
}

void
uncache_binding(pdom)
        struct domain *pdom;
{
        if (!cache_okay)
                return;

        if (pdom->cache_fp != 0) {
                unlink(pdom->cache_file);
                fclose(pdom->cache_fp);
                pdom->cache_fp = 0;
        }
}

/*
 *  Cache a transport address for talking to ypbind.  We convert the
 *  transport address to a universal address and save that in a file.
 *  The file lives in the binding directory because it does not depend
 *  on the domain.
 */
void
cache_transport(nconf, xprt, vers)
        struct netconfig *nconf;
        SVCXPRT *xprt;
        int vers;
{
        char filename[300];
        char *uaddr;
        int fd;
        int st;
        int len;

        if (!cache_okay)
                return;

        sprintf(filename, "%s/xprt.%s.%d",
                BINDING, nconf->nc_netid, vers);

        unlink(filename);    /* remove any old version */

        uaddr = taddr2uaddr(nconf, &xprt->xp_ltaddr);
        if (uaddr == 0)
                return;

        fd = open(filename, O_CREAT|O_WRONLY, 0444);    /* readable by all */
        if (fd == -1) {
                free(uaddr);
                return;
        }

        len = strlen(uaddr) + 1;    /* include terminating null */
        st = write(fd, uaddr, len);
        if (st != len) {
                close(fd);
                unlink(filename);
                free(uaddr);
                return;
        }

        free(uaddr);

        /* we lock the first byte to indicate that the file is valid */
        lseek(fd, 0L, SEEK_SET);
        st = lockf(fd, F_LOCK, 1);
        if (st == -1) {
                close(fd);
                unlink(filename);
        }
}

/*
 *  Create a file that clients can check to see if we are running.
 */
void
cache_pid()
{
        char filename[300];
        char spid[15];
        int fd;
        int st;
        int len;

        if (!cache_okay)
                return;

        sprintf(filename, "%s/ypbind.pid", BINDING);

        unlink(filename);    /* remove any old version */

        fd = open(filename, O_CREAT|O_WRONLY, 0444);    /* readable by all */
        if (fd == -1) {
                return;
        }

        sprintf(spid, "%d\n", getpid());

        len = strlen(spid);
        st = write(fd, spid, len);
        if (st != len) {
                close(fd);
                unlink(filename);
                return;
        }

        /* we lock the first byte to indicate that the file is valid */
        lseek(fd, 0L, SEEK_SET);
        st = lockf(fd, F_LOCK, 1);
        if (st == -1) {
                close(fd);
                unlink(filename);
        }

        /* we keep 'fd' open so that the lock will continue to be held */
}

/*
 *  We are called once at startup (when the known_domains list is empty)
 *  to clean up left-over files.  We are also called right before
 *  exiting.  In the latter case case we don't bother closing descriptors
 *  in the entries in the domain list because they will be closed
 *  automatically (and unlocked) when we exit.
 *
 *  We ignore the cache_okay flag because it is important that we remove
 *  all cache files (left-over files can temporarily confuse clients).
 */
void
clean_cache()
{
        struct domain *pdom;
        DIR *dir;
        struct dirent *dirent;
        char filename[300];

        /* close and unlink cache files for each domain */
        for (pdom = known_domains; pdom != (struct domain *)NULL;
            pdom = pdom->dom_pnext) {
                if (pdom->cache_file)
                        unlink(pdom->cache_file);
        }

        sprintf(filename, "%s/ypbind.pid", BINDING);
        unlink(filename);

        dir = opendir(BINDING);

        if (dir == NULL) {
                /* Directory could not be opened. */
                syslog(LOG_ERR, "opendir failed with [%s]", strerror(errno));
                return;
        }

        while ((dirent = readdir(dir)) != 0) {
                if (strncmp(dirent->d_name, "xprt.", 5) == 0) {
                        sprintf(filename, "%s/%s", BINDING, dirent->d_name);
                        unlink(filename);
                        rewinddir(dir);  /* removing file may harm iteration */
                }
        }
        closedir(dir);
}

/*
 *  We only want to use the cache stuff on local file systems.
 *  For remote file systems (e.g., NFS, the locking overhead is
 *  worse than the overhead of loopback RPC, so the caching
 *  wouldn't buy us anything.  In addition, if the remote locking
 *  software isn't configured before we start, then we would
 *  block when we try to lock.
 *
 *  We don't have a direct way to tell if a file system is local
 *  or remote, so we assume it is local unless it is NFS.
 */
int
cache_check()
{
        int st;
        struct statvfs stbuf;

        st = statvfs(BINDING, &stbuf);
        if (st == -1) {
                syslog(LOG_ERR, "statvfs failed with [%s]", strerror(errno));
                return (0);
        }

        /* we use strncasecmp to get NFS, NFS3, nfs, nfs3, etc. */
        if (strncasecmp(stbuf.f_basetype, "NFS", 3) == 0)
                return (0);
        return (1);
}