#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"
#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")
#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
#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;
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];
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);
}
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);
}
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);
}
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);
}
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);
}
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);
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_retattrval_t) +
(size_t)ret->nbytes, NULL, 0);
}
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);
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);
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);
}
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);
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);
}
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);
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
static void
picld_door_handler(void *cookie, char *argp, size_t asize,
door_desc_t *dp, uint_t n_desc)
{
picl_service_t *req;
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) {
case PICL_CNUM_INIT:
picld_init((picl_service_t *)argp);
break;
case PICL_CNUM_FINI:
picld_fini((picl_service_t *)argp);
break;
case PICL_CNUM_GETROOT:
picld_getroot((picl_service_t *)argp);
break;
case PICL_CNUM_GETATTRVAL:
picld_get_attrval((picl_service_t *)argp);
break;
case PICL_CNUM_GETATTRVALBYNAME:
picld_get_attrval_by_name((picl_service_t *)argp);
break;
case PICL_CNUM_GETATTRINFO:
picld_get_attrinfo((picl_service_t *)argp);
break;
case PICL_CNUM_GETFIRSTATTR:
picld_get_first_attr((picl_service_t *)argp);
break;
case PICL_CNUM_GETNEXTATTR:
picld_get_next_attr((picl_service_t *)argp);
break;
case PICL_CNUM_GETATTRBYNAME:
picld_get_attr_by_name((picl_service_t *)argp);
break;
case PICL_CNUM_GETATTRBYROW:
picld_get_attr_by_row((picl_service_t *)argp);
break;
case PICL_CNUM_GETATTRBYCOL:
picld_get_attr_by_col((picl_service_t *)argp);
break;
case PICL_CNUM_SETATTRVAL:
picld_set_attrval((picl_service_t *)argp);
break;
case PICL_CNUM_SETATTRVALBYNAME:
picld_set_attrval_by_name((picl_service_t *)argp);
break;
case PICL_CNUM_PING:
picld_ping((picl_service_t *)argp);
break;
case PICL_CNUM_WAIT:
picld_wait((picl_service_t *)argp);
break;
case PICL_CNUM_FINDNODE:
picld_find_node((picl_service_t *)argp);
break;
case PICL_CNUM_NODEBYPATH:
picld_get_node_by_path((picl_service_t *)argp);
break;
case PICL_CNUM_FRUTREEPARENT:
picld_get_frutree_parent((picl_service_t *)argp);
break;
default:
picld_unknown_service((picl_service_t *)argp);
break;
};
}
static void
hup_handler(int sig, siginfo_t *siginfo, void *sigctx)
{
doreinit = 1;
}
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);
}
static void *
picld_create_server_thread(void *arg)
{
(void) pthread_mutex_lock(&door_mutex);
while (door_id == -1) {
(void) pthread_cond_wait(&door_cv, &door_mutex);
}
(void) pthread_mutex_unlock(&door_mutex);
if (door_bind(door_id) < 0) {
perror("door_bind");
}
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
(void) door_return(NULL, 0, NULL, 0);
return (NULL);
}
static void
picld_server_create_fn(door_info_t *dip)
{
pthread_attr_t attr;
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);
}
static int
setup_door(void)
{
struct stat stbuf;
(void) door_server_create(picld_server_create_fn);
(void) pthread_mutex_lock(&door_mutex);
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;
old_mask = umask(0);
newfd = creat(PICLD_DOOR, 0444);
(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);
}
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;
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();
if (daemon_exists()) {
syslog(LOG_CRIT, DAEMON_RUNNING);
exit(1);
}
(void) sigemptyset(&ublk);
(void) sigaddset(&ublk, SIGALRM);
(void) sigprocmask(SIG_BLOCK, &ublk, NULL);
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) {
pid_t pid;
pid = fork();
if (pid < 0)
exit(1);
if (pid > 0)
exit(0);
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);
}
if (xptree_initialize(0) != PICL_SUCCESS) {
syslog(LOG_CRIT, INIT_FAILED);
exit(1);
}
if (setup_door()) {
syslog(LOG_CRIT, DOOR_FAILED);
exit(1);
}
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));
for (;;) {
(void) pause();
if (doreinit) {
(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);
}
}
}