root/usr/src/cmd/picl/picld/picld.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * PICL daemon
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <libintl.h>
#include <locale.h>
#include <alloca.h>
#include <errno.h>
#include <assert.h>
#include <stropts.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <synch.h>
#include <door.h>
#include <sys/door.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <time.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <syslog.h>
#include <poll.h>
#include <limits.h>
#include <picl.h>
#include "picl2door.h"
#include <picltree.h>
#include "ptree_impl.h"

/*
 * Log text messages
 */
#define MUST_BE_ROOT    gettext("this program must be run as root\n")
#define CD_ROOT_FAILED  gettext("chdir to root failed\n")
#define INIT_FAILED     gettext("ptree initialization failed\n")
#define DAEMON_RUNNING  gettext("PICL daemon already running\n")
#define DOOR_FAILED     gettext("Failed creating picld door\n")
#define SIGACT_FAILED   \
                gettext("Failed to install signal handler for %s: %s\n")

/*
 * Constants
 */
#define PICLD                           "picld"
#define DOS_PICL_REQUESTS_LIMIT         10000
#define SLIDING_INTERVAL_MILLISECONDS   1000
#define PICLD_MAJOR_REV                 0x1
#define PICLD_MINOR_REV                 0x0
#define DOS_SLEEPTIME_MS                1000
#define MAX_POOL_SIZE                   _POSIX_THREAD_THREADS_MAX
#define MAX_CONCURRENT_WAITS    (_POSIX_THREAD_THREADS_MAX - 2)
#define MAX_USER_WAITS                  4

/*
 * Macros
 */
#define PICLD_VERSION(x, y)     ((x << 8) | y)
#define PICL_CLIENT_REV(x)      (x & 0xff)
#define MILLI_TO_NANO(x)        (x * 1000000)

extern  char    **environ;

/*
 * Module Variables
 */
static  int             logflag = 1;
static  int             doreinit = 0;
static  int             door_id = -1;
static  pthread_mutex_t door_mutex = PTHREAD_MUTEX_INITIALIZER;
static  pthread_cond_t door_cv = PTHREAD_COND_INITIALIZER;
static  int             service_requests = 0;
static  hrtime_t        orig_time;
static  hrtime_t        sliding_interval_ms;
static  uint32_t        dos_req_limit;
static  uint32_t        dos_ms;
static  pthread_mutex_t dos_mutex = PTHREAD_MUTEX_INITIALIZER;
static  rwlock_t        init_lk;
static  int pool_count = 0;
static  pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER;
static  pthread_mutex_t wait_req_mutex = PTHREAD_MUTEX_INITIALIZER;
static  int wait_count = 0;
static  struct {
        uid_t uid;
        int count;
} user_count[MAX_CONCURRENT_WAITS];

/*
 * This returns an error message to libpicl
 */
static void
picld_return_error(picl_callnumber_t cnum, picl_errno_t err)
{
        picl_reterror_t ret_error;

        ret_error.cnum = PICL_CNUM_ERROR;
        ret_error.in_cnum = cnum;
        ret_error.errnum = err;
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret_error, sizeof (picl_reterror_t), NULL,
            0);
}

/*
 * picld_init is called when a picl_initialize request is received
 */
static void
picld_init(picl_service_t *req)
{
        picl_retinit_t  ret_init;
        int     clmajrev;

        clmajrev = PICL_CLIENT_REV(req->req_init.clrev);

        if (clmajrev < PICL_VERSION_1)
                picld_return_error(req->req_init.cnum, PICL_NOTSUPPORTED);

        ret_init.cnum = req->req_init.cnum;
        ret_init.rev = PICLD_VERSION(PICLD_MAJOR_REV, PICLD_MINOR_REV);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret_init, sizeof (picl_retinit_t), NULL, 0);
}

/*
 * picld_fini is called when a picl_shutdown request is received
 */
static void
picld_fini(picl_service_t *in)
{
        picl_retfini_t  ret;

        ret.cnum = in->req_fini.cnum;

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retfini_t), NULL, 0);
}

static void
picld_ping(picl_service_t *in)
{
        picl_retping_t  ret;

        ret.cnum = in->req_ping.cnum;

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retping_t), NULL, 0);
}

static int
check_user(uid_t uid)
{
        int i;
        uid_t tmp_uid;
        int free_idx = -1;

        if (uid == 0)
                return (PICL_SUCCESS);
        for (i = 0; i < MAX_CONCURRENT_WAITS; i++) {
                if ((tmp_uid = user_count[i].uid) == uid) {
                        if (user_count[i].count == MAX_USER_WAITS)
                                return (PICL_FAILURE);
                        user_count[i].count++;
                        return (PICL_SUCCESS);
                }
                if ((free_idx == -1) && (tmp_uid == 0))
                        free_idx = i;
        }
        if (free_idx != -1) {
                user_count[free_idx].uid = uid;
                user_count[free_idx].count = 1;
                return (PICL_SUCCESS);
        }
        return (PICL_FAILURE);
}

static void
done_user(uid_t uid)
{
        int i;

        if (uid == 0)
                return;
        for (i = 0; i < MAX_CONCURRENT_WAITS; i++) {
                if (user_count[i].uid == uid) {
                        if (--user_count[i].count == 0)
                                user_count[i].uid = 0;
                        return;
                }
        }
}

static int
enter_picld_wait(uid_t uid)
{
        int     rv;

        if (pthread_mutex_lock(&wait_req_mutex) != 0)
                return (PICL_FAILURE);
        if ((wait_count < MAX_CONCURRENT_WAITS) &&
            (check_user(uid) == PICL_SUCCESS)) {
                rv = PICL_SUCCESS;
                wait_count++;
        } else {
                rv = PICL_FAILURE;
        }
        (void) pthread_mutex_unlock(&wait_req_mutex);
        return (rv);
}

static void
exit_picld_wait(uid_t uid)
{
        (void) pthread_mutex_lock(&wait_req_mutex);
        done_user(uid);
        wait_count--;
        (void) pthread_mutex_unlock(&wait_req_mutex);
}

/*
 * picld_wait is called when a picl_wait request is received
 */
static void
picld_wait(picl_service_t *in)
{
        picl_retwait_t  ret;
        int             err;
        ucred_t *puc = NULL;
        uid_t uid;

        ret.cnum = in->req_wait.cnum;
        if (door_ucred(&puc) != 0)
                ret.retcode = PICL_FAILURE;
        else {
                uid = ucred_geteuid(puc);
                if (enter_picld_wait(uid) == PICL_FAILURE)
                        ret.retcode = PICL_FAILURE;
                else {
                        err = xptree_refresh_notify(in->req_wait.secs);
                        ret.retcode = err;
                        exit_picld_wait(uid);
                }
                ucred_free(puc);
        }
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retwait_t), NULL, 0);
}

/*
 * This function returns the handle of the root node of the PICL tree
 */
static void
picld_getroot(picl_service_t *in)
{
        picl_retroot_t  ret;
        int             err;

        ret.cnum = PICL_CNUM_GETROOT;
        err = ptree_get_root(&ret.rnode);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);
        cvt_ptree2picl(&ret.rnode);
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retroot_t), NULL, 0);
}

/*
 * This function returns the value of the PICL property
 */
static void
picld_get_attrval(picl_service_t *in)
{
        picl_retattrval_t       *ret;
        int                     err;
        size_t                  vbufsize;
        size_t                  len;
        door_cred_t             cred;
        picl_prophdl_t          ptreeh;
        ptree_propinfo_t        pinfo;

        if (door_cred(&cred) < 0)
                picld_return_error(in->in.cnum, PICL_FAILURE);

        err = cvt_picl2ptree(in->req_attrval.attr, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        err = ptree_get_propinfo(ptreeh, &pinfo);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        if (!(pinfo.piclinfo.accessmode & PICL_READ))
                picld_return_error(in->in.cnum, PICL_NOTREADABLE);

        vbufsize = pinfo.piclinfo.size;
        vbufsize = MIN((size_t)in->req_attrval.bufsize, vbufsize);

        len = sizeof (picl_retattrval_t) + vbufsize;
        ret = alloca(len);
        if (ret == NULL)
                picld_return_error(in->in.cnum, PICL_FAILURE);
        ret->cnum = PICL_CNUM_GETATTRVAL;
        ret->attr = in->req_attrval.attr;
        ret->nbytes = (uint32_t)vbufsize;
        err = xptree_get_propval_with_cred(ptreeh, ret->ret_buf, vbufsize,
            cred);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        /*
         * adjust returned bytes for charstrings
         */
        if (pinfo.piclinfo.type == PICL_PTYPE_CHARSTRING)
                ret->nbytes = (uint32_t)strlen(ret->ret_buf) + 1;

        /*
         * convert handle values to picl handles
         */
        if ((pinfo.piclinfo.type == PICL_PTYPE_TABLE) ||
            (pinfo.piclinfo.type == PICL_PTYPE_REFERENCE))
                cvt_ptree2picl(&ret->ret_nodeh);
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)ret, sizeof (picl_retattrval_t) +
            (size_t)ret->nbytes, NULL, 0);
}

/*
 * This function returns the value of the PICL property specified by
 * its name.
 */
static void
picld_get_attrval_by_name(picl_service_t *in)
{
        picl_retattrvalbyname_t *ret;
        int                     err;
        size_t                  vbufsize;
        size_t                  len;
        door_cred_t             cred;
        picl_nodehdl_t          ptreeh;
        ptree_propinfo_t        pinfo;

        if (door_cred(&cred) < 0)
                picld_return_error(in->in.cnum, PICL_FAILURE);

        err = cvt_picl2ptree(in->req_attrvalbyname.nodeh, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        err = xptree_get_propinfo_by_name(ptreeh,
            in->req_attrvalbyname.propname, &pinfo);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        if (!(pinfo.piclinfo.accessmode & PICL_READ))
                picld_return_error(in->in.cnum, PICL_NOTREADABLE);

        /*
         * allocate the minimum of piclinfo.size and input bufsize
         */
        vbufsize = pinfo.piclinfo.size;
        vbufsize = MIN((size_t)in->req_attrvalbyname.bufsize, vbufsize);
        len = sizeof (picl_retattrvalbyname_t) + vbufsize;
        ret = alloca(len);
        if (ret == NULL)
                picld_return_error(in->in.cnum, PICL_FAILURE);
        ret->cnum = PICL_CNUM_GETATTRVALBYNAME;
        ret->nodeh = in->req_attrvalbyname.nodeh;
        (void) strcpy(ret->propname, in->req_attrvalbyname.propname);
        ret->nbytes = (uint32_t)vbufsize;

        err = xptree_get_propval_by_name_with_cred(ptreeh,
            in->req_attrvalbyname.propname, ret->ret_buf, vbufsize,
            cred);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);
        /*
         * adjust returned value size for charstrings
         */
        if (pinfo.piclinfo.type == PICL_PTYPE_CHARSTRING)
                ret->nbytes = (uint32_t)strlen(ret->ret_buf) + 1;

        if ((pinfo.piclinfo.type == PICL_PTYPE_TABLE) ||
            (pinfo.piclinfo.type == PICL_PTYPE_REFERENCE))
                cvt_ptree2picl(&ret->ret_nodeh);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)ret, sizeof (picl_retattrvalbyname_t) +
            (size_t)ret->nbytes, NULL, 0);
}

/*
 * This function sets a property value
 */
static void
picld_set_attrval(picl_service_t *in)
{
        picl_retsetattrval_t    ret;
        int                     err;
        door_cred_t             cred;
        picl_prophdl_t          ptreeh;
        ptree_propinfo_t        pinfo;

        if (door_cred(&cred) < 0)
                picld_return_error(in->in.cnum, PICL_FAILURE);

        err = cvt_picl2ptree(in->req_setattrval.attr, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        err = ptree_get_propinfo(ptreeh, &pinfo);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        if (!(pinfo.piclinfo.accessmode & PICL_WRITE))
                picld_return_error(in->in.cnum, PICL_NOTWRITABLE);
        /*
         * For non-volatile prop, only super user can set its value.
         */
        if (!(pinfo.piclinfo.accessmode & PICL_VOLATILE) &&
            (cred.dc_euid != SUPER_USER))
                picld_return_error(in->in.cnum, PICL_PERMDENIED);

        ret.cnum = PICL_CNUM_SETATTRVAL;
        ret.attr = in->req_setattrval.attr;

        err = xptree_update_propval_with_cred(ptreeh, in->req_setattrval.valbuf,
            (size_t)in->req_setattrval.bufsize, cred);

        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retsetattrval_t), NULL,
            0);
}

/*
 * This function sets the value of a property specified by its name.
 */
static void
picld_set_attrval_by_name(picl_service_t *in)
{
        picl_retsetattrvalbyname_t      ret;
        int                             err;
        door_cred_t                     cred;
        picl_prophdl_t                  ptreeh;
        ptree_propinfo_t                pinfo;

        if (door_cred(&cred) < 0)
                picld_return_error(in->in.cnum, PICL_FAILURE);

        err = cvt_picl2ptree(in->req_setattrvalbyname.nodeh, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        err = xptree_get_propinfo_by_name(ptreeh,
            in->req_setattrvalbyname.propname, &pinfo);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        if (!(pinfo.piclinfo.accessmode & PICL_WRITE))
                picld_return_error(in->in.cnum, PICL_NOTWRITABLE);

        /*
         * For non-volatile prop, only super user can set its value.
         */
        if (!(pinfo.piclinfo.accessmode & PICL_VOLATILE) &&
            (cred.dc_euid != SUPER_USER))
                picld_return_error(in->in.cnum, PICL_PERMDENIED);

        ret.cnum = PICL_CNUM_SETATTRVALBYNAME;
        ret.nodeh = in->req_setattrvalbyname.nodeh;
        (void) strcpy(ret.propname, in->req_setattrvalbyname.propname);

        err = xptree_update_propval_by_name_with_cred(ptreeh,
            in->req_setattrvalbyname.propname,
            in->req_setattrvalbyname.valbuf,
            (size_t)in->req_setattrvalbyname.bufsize,
            cred);

        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retsetattrvalbyname_t),
            NULL, 0);
}

/*
 * This function returns the property information
 */
static void
picld_get_attrinfo(picl_service_t *in)
{
        picl_retattrinfo_t      ret;
        int                     err;
        ptree_propinfo_t        pinfo;
        picl_prophdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_attrinfo.attr, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_GETATTRINFO;
        ret.attr = in->req_attrinfo.attr;

        err = ptree_get_propinfo(ptreeh, &pinfo);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.type = pinfo.piclinfo.type;
        ret.accessmode = pinfo.piclinfo.accessmode;
        ret.size = (uint32_t)pinfo.piclinfo.size;
        (void) strcpy(ret.name, pinfo.piclinfo.name);
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retattrinfo_t), NULL, 0);
}

/*
 * This function returns the node's first property handle
 */
static void
picld_get_first_attr(picl_service_t *in)
{
        picl_retfirstattr_t     ret;
        int                     err;
        picl_prophdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_firstattr.nodeh, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_GETFIRSTATTR;
        ret.nodeh = in->req_firstattr.nodeh;

        err = ptree_get_first_prop(ptreeh, &ret.attr);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);
        cvt_ptree2picl(&ret.attr);
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retfirstattr_t), NULL, 0);
}

/*
 * This function returns the next property handle in list
 */
static void
picld_get_next_attr(picl_service_t *in)
{
        picl_retnextattr_t      ret;
        int                     err;
        picl_prophdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_nextattr.attr, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_GETNEXTATTR;
        ret.attr = in->req_nextattr.attr;

        err = ptree_get_next_prop(ptreeh, &ret.nextattr);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        cvt_ptree2picl(&ret.nextattr);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retnextattr_t), NULL, 0);
}

/*
 * This function returns the handle of a property specified by its name
 */
static void
picld_get_attr_by_name(picl_service_t *in)
{
        picl_retattrbyname_t    ret;
        int                     err;
        picl_prophdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_attrbyname.nodeh, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_GETATTRBYNAME;
        ret.nodeh = in->req_attrbyname.nodeh;
        (void) strcpy(ret.propname, in->req_attrbyname.propname);

        err = ptree_get_prop_by_name(ptreeh, ret.propname, &ret.attr);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        cvt_ptree2picl(&ret.attr);
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retattrbyname_t), NULL,
            0);
}

/*
 * This function gets the next property on the same row in the table
 */
static void
picld_get_attr_by_row(picl_service_t *in)
{
        picl_retattrbyrow_t     ret;
        int                     err;
        picl_prophdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_attrbyrow.attr, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_GETATTRBYROW;
        ret.attr = in->req_attrbyrow.attr;

        err = ptree_get_next_by_row(ptreeh, &ret.rowattr);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);
        cvt_ptree2picl(&ret.rowattr);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retattrbyrow_t), NULL, 0);
}

/*
 * This function returns the handle of the next property in the same column
 * of the table.
 */
static void
picld_get_attr_by_col(picl_service_t *in)
{
        picl_retattrbycol_t     ret;
        int                     err;
        picl_prophdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_attrbycol.attr, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_GETATTRBYCOL;
        ret.attr = in->req_attrbycol.attr;

        err = ptree_get_next_by_col(ptreeh, &ret.colattr);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        cvt_ptree2picl(&ret.colattr);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (picl_retattrbycol_t), NULL, 0);
}

/*
 * This function finds the node in the PICLTREE that matches the given
 * criteria and returns its handle.
 */
static void
picld_find_node(picl_service_t *in)
{
        picl_retfindnode_t      ret;
        int                     err;
        picl_nodehdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_findnode.nodeh, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_FINDNODE;

        err = ptree_find_node(ptreeh, in->req_findnode.propname,
            in->req_findnode.ptype, in->req_findnode.valbuf,
            in->req_findnode.valsize, &ret.rnodeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        cvt_ptree2picl(&ret.rnodeh);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (ret), NULL, 0);
}

/*
 * This function finds the property/node that corresponds to the given path
 * and returns its handle
 */
static void
picld_get_node_by_path(picl_service_t *in)
{
        picl_retnodebypath_t    ret;
        int                     err;

        ret.cnum = PICL_CNUM_NODEBYPATH;
        err = ptree_get_node_by_path(in->req_nodebypath.pathbuf, &ret.nodeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);
        cvt_ptree2picl(&ret.nodeh);
        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (ret), NULL, 0);
}

/*
 * This function returns finds the frutree parent node for a given node
 * and returns its handle
 */
static void
picld_get_frutree_parent(picl_service_t *in)
{
        picl_retfruparent_t     ret;
        int                     err;
        picl_nodehdl_t          ptreeh;

        err = cvt_picl2ptree(in->req_fruparent.devh, &ptreeh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);

        ret.cnum = PICL_CNUM_FRUTREEPARENT;

        err = ptree_get_frutree_parent(ptreeh, &ret.fruh);
        if (err != PICL_SUCCESS)
                picld_return_error(in->in.cnum, err);
        cvt_ptree2picl(&ret.fruh);

        (void) rw_unlock(&init_lk);
        (void) door_return((char *)&ret, sizeof (ret), NULL, 0);
}

/*
 * This function is called when an unknown client request is received.
 */
static void
picld_unknown_service(picl_service_t *in)
{
        picld_return_error(in->in.cnum, PICL_UNKNOWNSERVICE);
}

static void
check_denial_of_service(int cnum)
{
        hrtime_t        window;
        hrtime_t        current;
        int             dos_flag;

        current = gethrtime();
        dos_flag = 0;

        if (pthread_mutex_lock(&dos_mutex) != 0)
                picld_return_error(cnum, PICL_FAILURE);

        ++service_requests;
        window = current - orig_time;
        if (window > MILLI_TO_NANO(sliding_interval_ms)) {
                orig_time = current;
                service_requests = 1;
        }

        if (service_requests > dos_req_limit)
                dos_flag = 1;

        if (pthread_mutex_unlock(&dos_mutex) != 0)
                picld_return_error(cnum, PICL_FAILURE);

        if (dos_flag)
                (void) poll(NULL, 0, dos_ms);
}

/* ARGSUSED */
static void
picld_door_handler(void *cookie, char *argp, size_t asize,
    door_desc_t *dp, uint_t n_desc)
{
        picl_service_t  *req;

        /*LINTED*/
        req = (picl_service_t *)argp;

        if (req == NULL)
                (void) door_return((char *)req, 0, NULL, 0);

        check_denial_of_service(req->in.cnum);

        (void) rw_rdlock(&init_lk);
        switch (req->in.cnum) { /* client call number */
        case PICL_CNUM_INIT:
                /*LINTED*/
                picld_init((picl_service_t *)argp);
                break;
        case PICL_CNUM_FINI:
                /*LINTED*/
                picld_fini((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETROOT:
                /*LINTED*/
                picld_getroot((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETATTRVAL:
                /*LINTED*/
                picld_get_attrval((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETATTRVALBYNAME:
                /*LINTED*/
                picld_get_attrval_by_name((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETATTRINFO:
                /*LINTED*/
                picld_get_attrinfo((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETFIRSTATTR:
                /*LINTED*/
                picld_get_first_attr((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETNEXTATTR:
                /*LINTED*/
                picld_get_next_attr((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETATTRBYNAME:
                /*LINTED*/
                picld_get_attr_by_name((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETATTRBYROW:
                /*LINTED*/
                picld_get_attr_by_row((picl_service_t *)argp);
                break;
        case PICL_CNUM_GETATTRBYCOL:
                /*LINTED*/
                picld_get_attr_by_col((picl_service_t *)argp);
                break;
        case PICL_CNUM_SETATTRVAL:
                /*LINTED*/
                picld_set_attrval((picl_service_t *)argp);
                break;
        case PICL_CNUM_SETATTRVALBYNAME:
                /*LINTED*/
                picld_set_attrval_by_name((picl_service_t *)argp);
                break;
        case PICL_CNUM_PING:
                /*LINTED*/
                picld_ping((picl_service_t *)argp);
                break;
        case PICL_CNUM_WAIT:
                /*LINTED*/
                picld_wait((picl_service_t *)argp);
                break;
        case PICL_CNUM_FINDNODE:
                /*LINTED*/
                picld_find_node((picl_service_t *)argp);
                break;
        case PICL_CNUM_NODEBYPATH:
                /*LINTED*/
                picld_get_node_by_path((picl_service_t *)argp);
                break;
        case PICL_CNUM_FRUTREEPARENT:
                /*LINTED*/
                picld_get_frutree_parent((picl_service_t *)argp);
                break;
        default:
                /*LINTED*/
                picld_unknown_service((picl_service_t *)argp);
                break;
        };
        /*NOTREACHED*/
}

/* ARGSUSED */
static void
hup_handler(int sig, siginfo_t *siginfo, void *sigctx)
{
        doreinit = 1;
}

/*
 * "ping" to see if a daemon is already running
 */
static int
daemon_exists(void)
{
        door_arg_t      darg;
        picl_reqping_t  req_ping;
        picl_retping_t  ret_ping;
        int             doorh;
        door_info_t     dinfo;

        doorh = open(PICLD_DOOR, O_RDONLY);
        if (doorh < 0)
                return (0);

        if (door_info(doorh, &dinfo) < 0) {
                (void) close(doorh);
                return (0);
        }

        if ((dinfo.di_attributes & DOOR_REVOKED) ||
            (dinfo.di_data != (uintptr_t)PICLD_DOOR_COOKIE)) {
                (void) close(doorh);
                return (0);
        }

        if (dinfo.di_target != getpid()) {
                (void) close(doorh);
                return (1);
        }

        req_ping.cnum = PICL_CNUM_PING;

        darg.data_ptr = (char *)&req_ping;
        darg.data_size = sizeof (picl_reqping_t);
        darg.desc_ptr = NULL;
        darg.desc_num = 0;
        darg.rbuf = (char *)&ret_ping;
        darg.rsize = sizeof (picl_retping_t);

        if (door_call(doorh, &darg) < 0) {
                (void) close(doorh);
                return (0);
        }

        (void) close(doorh);
        return (1);
}

/*
 * picld_create_server_thread - binds the running thread to the private
 * door pool, and sets the required cancellation state.
 */
/* ARGSUSED */
static void *
picld_create_server_thread(void *arg)
{
        /*
         * wait for door descriptor to be initialized
         */
        (void) pthread_mutex_lock(&door_mutex);
        while (door_id == -1) {
                (void) pthread_cond_wait(&door_cv, &door_mutex);
        }
        (void) pthread_mutex_unlock(&door_mutex);

        /*
         * Bind this thread to the door's private thread pool
         */
        if (door_bind(door_id) < 0) {
                perror("door_bind");
        }

        /*
         * Disable thread cancellation mechanism
         */
        (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
        (void) door_return(NULL, 0, NULL, 0); /* wait for door invocation */
        return (NULL);
}

/*
 * picld_server_create_fn - creates threads for the private door pool
 *
 */
/* ARGSUSED */
static void
picld_server_create_fn(door_info_t *dip)
{
        pthread_attr_t attr;

        /*
         * For the non-private pool do nothing. It's used for events which are
         * single threaded anyway. The single thread servicing that pool is
         * created when the event plugin creates its door. Note that the event
         * plugin runs before setup_door instantiates picld_server_create_fn as
         * the new create_proc so the door library default create_proc is used.
         */
        if (dip == NULL)
                return;

        (void) pthread_mutex_lock(&pool_mutex);
        if (pool_count < MAX_POOL_SIZE) {
                (void) pthread_attr_init(&attr);
                (void) pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
                (void) pthread_attr_setdetachstate(&attr,
                    PTHREAD_CREATE_DETACHED);
                if (pthread_create(NULL, &attr, picld_create_server_thread,
                    NULL)) {
                        perror("pthread_create");
                } else {
                        pool_count++;
                }
        }
        (void) pthread_mutex_unlock(&pool_mutex);
}

/*
 * Create the picld door
 */
static int
setup_door(void)
{
        struct stat     stbuf;

        (void) door_server_create(picld_server_create_fn);
        (void) pthread_mutex_lock(&door_mutex);
        /*
         * Create the door
         */
        door_id = door_create(picld_door_handler, PICLD_DOOR_COOKIE,
            DOOR_REFUSE_DESC | DOOR_NO_CANCEL | DOOR_PRIVATE);

        if (door_id < 0) {
                (void) pthread_mutex_unlock(&door_mutex);
                return (-1);
        } else {
                (void) pthread_cond_signal(&door_cv);
                (void) pthread_mutex_unlock(&door_mutex);
        }

        if (stat(PICLD_DOOR, &stbuf) < 0) {
                int newfd;
                mode_t old_mask;
                /* ensure that the door file is world-readable */
                old_mask = umask(0);
                newfd = creat(PICLD_DOOR, 0444);
                /* restore the file mode creation mask */
                (void) umask(old_mask);
                if (newfd < 0)
                        return (-1);
                (void) close(newfd);
        }

        if (fattach(door_id, PICLD_DOOR) < 0) {
                if ((errno != EBUSY) ||
                    (fdetach(PICLD_DOOR) < 0) ||
                    (fattach(door_id, PICLD_DOOR) < 0))
                        return (-1);
        }
        return (0);
}

/*
 * Main function of picl daemon
 */
int
main(int argc, char **argv)
{
        struct  sigaction       act;
        int                     c;
        sigset_t                ublk;


        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        if (getuid() != 0) {
                syslog(LOG_CRIT, MUST_BE_ROOT);
                return (0);
        }

        (void) rwlock_init(&init_lk, USYNC_THREAD, NULL);
        doreinit = 0;
        logflag = 1;
        dos_req_limit = DOS_PICL_REQUESTS_LIMIT;
        sliding_interval_ms = SLIDING_INTERVAL_MILLISECONDS;
        dos_ms = DOS_SLEEPTIME_MS;
        verbose_level = 0;

        /*
         * parse arguments
         */
        while ((c = getopt(argc, argv, "is:t:l:r:v:d:")) != EOF) {
                switch (c) {
                case 'd':
                        dos_ms = strtol(optarg, (char **)NULL, 0);
                        break;
                case 'i':
                        logflag = 0;
                        break;
                case 's':
                        sliding_interval_ms = strtoll(optarg, (char **)NULL, 0);
                        break;
                case 't':
                        dos_req_limit = strtol(optarg, (char **)NULL, 0);
                        break;
                case 'v':
                        verbose_level = strtol(optarg, (char **)NULL, 0);
                        logflag = 0;
                        break;
                default:
                        break;
                }
        }

        orig_time = gethrtime();

        /*
         * is there a daemon already running?
         */

        if (daemon_exists()) {
                syslog(LOG_CRIT, DAEMON_RUNNING);
                exit(1);
        }

        /*
         * Mask off/block SIGALRM signal so that the environmental plug-in
         * (piclenvd) can use it to simulate sleep() without being affected
         * by time being set back. No other PICL plug-in should use SIGALRM
         * or alarm() for now.
         */
        (void) sigemptyset(&ublk);
        (void) sigaddset(&ublk, SIGALRM);
        (void) sigprocmask(SIG_BLOCK, &ublk, NULL);

        /*
         * Ignore SIGHUP until all the initialization is done.
         */
        act.sa_handler = SIG_IGN;
        (void) sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        if (sigaction(SIGHUP, &act, NULL) == -1)
                syslog(LOG_ERR, SIGACT_FAILED, strsignal(SIGHUP),
                    strerror(errno));

        if (logflag != 0) {     /* daemonize */
                pid_t pid;

                pid = fork();
                if (pid < 0)
                        exit(1);
                if (pid > 0)
                        /* parent */
                        exit(0);

                /* child */
                if (chdir("/") == -1) {
                        syslog(LOG_CRIT, CD_ROOT_FAILED);
                        exit(1);
                }

                (void) setsid();
                closefrom(0);
                (void) open("/dev/null", O_RDWR, 0);
                (void) dup2(STDIN_FILENO, STDOUT_FILENO);
                (void) dup2(STDIN_FILENO, STDERR_FILENO);
                openlog(PICLD, LOG_PID, LOG_DAEMON);
        }

        /*
         * Initialize the PICL Tree
         */
        if (xptree_initialize(0) != PICL_SUCCESS) {
                syslog(LOG_CRIT, INIT_FAILED);
                exit(1);
        }

        if (setup_door()) {
                syslog(LOG_CRIT, DOOR_FAILED);
                exit(1);
        }

        /*
         * setup signal handlers for post-init
         */
        act.sa_sigaction = hup_handler;
        (void) sigemptyset(&act.sa_mask);
        act.sa_flags = SA_SIGINFO;
        if (sigaction(SIGHUP, &act, NULL) == -1)
                syslog(LOG_ERR, SIGACT_FAILED, strsignal(SIGHUP),
                    strerror(errno));

        /*
         * wait for requests
         */
        for (;;) {
                (void) pause();
                if (doreinit) {
                        /*
                         * Block SIGHUP during reinitialization.
                         * Also mask off/block SIGALRM signal so that the
                         * environmental plug-in (piclenvd) can use it to
                         * simulate sleep() without being affected by time
                         * being set back. No ohter PICL plug-in should use
                         * SIGALRM or alarm() for now.
                         */
                        (void) sigemptyset(&ublk);
                        (void) sigaddset(&ublk, SIGHUP);
                        (void) sigaddset(&ublk, SIGALRM);
                        (void) sigprocmask(SIG_BLOCK, &ublk, NULL);
                        (void) sigdelset(&ublk, SIGALRM);
                        doreinit = 0;
                        (void) rw_wrlock(&init_lk);
                        xptree_destroy();
                        (void) xptree_reinitialize();
                        (void) rw_unlock(&init_lk);
                        (void) sigprocmask(SIG_UNBLOCK, &ublk, NULL);
                }
        }
}