/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "picl2door.h" #include #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); } } }