xref: /illumos-gate/usr/src/cmd/picl/picld/picld.c (revision 980a6e61aeb2038ab2b640d7ac80b36cf5c7d84b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * PICL daemon
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <string.h>
37 #include <libintl.h>
38 #include <locale.h>
39 #include <alloca.h>
40 #include <errno.h>
41 #include <assert.h>
42 #include <stropts.h>
43 #include <unistd.h>
44 #include <signal.h>
45 #include <pthread.h>
46 #include <synch.h>
47 #include <door.h>
48 #include <sys/door.h>
49 #include <fcntl.h>
50 #include <dlfcn.h>
51 #include <time.h>
52 #include <sys/utsname.h>
53 #include <sys/systeminfo.h>
54 #include <sys/stat.h>
55 #include <sys/wait.h>
56 #include <dirent.h>
57 #include <syslog.h>
58 #include <poll.h>
59 #include <limits.h>
60 #include <picl.h>
61 #include "picl2door.h"
62 #include <picltree.h>
63 #include "ptree_impl.h"
64 
65 /*
66  * Log text messages
67  */
68 #define	MUST_BE_ROOT	gettext("this program must be run as root\n")
69 #define	CD_ROOT_FAILED	gettext("chdir to root failed\n")
70 #define	INIT_FAILED	gettext("ptree initialization failed\n")
71 #define	DAEMON_RUNNING	gettext("PICL daemon already running\n")
72 #define	DOOR_FAILED	gettext("Failed creating picld door\n")
73 #define	SIGACT_FAILED	\
74 		gettext("Failed to install signal handler for %s: %s\n")
75 
76 /*
77  * Constants
78  */
79 #define	PICLD				"picld"
80 #define	DOS_PICL_REQUESTS_LIMIT		10000
81 #define	SLIDING_INTERVAL_MILLISECONDS	1000
82 #define	PICLD_MAJOR_REV			0x1
83 #define	PICLD_MINOR_REV			0x0
84 #define	DOS_SLEEPTIME_MS		1000
85 #define	MAX_POOL_SIZE			_POSIX_THREAD_THREADS_MAX
86 #define	MAX_CONCURRENT_WAITS	(_POSIX_THREAD_THREADS_MAX - 2)
87 #define	MAX_USER_WAITS			4
88 
89 /*
90  * Macros
91  */
92 #define	PICLD_VERSION(x, y)	((x << 8) | y)
93 #define	PICL_CLIENT_REV(x)	(x & 0xff)
94 #define	MILLI_TO_NANO(x)	(x * 1000000)
95 
96 extern	char	**environ;
97 
98 /*
99  * Module Variables
100  */
101 static	int		logflag = 1;
102 static	int		doreinit = 0;
103 static	int		door_id = -1;
104 static	pthread_mutex_t door_mutex = PTHREAD_MUTEX_INITIALIZER;
105 static	pthread_cond_t door_cv = PTHREAD_COND_INITIALIZER;
106 static  int 		service_requests = 0;
107 static	hrtime_t	orig_time;
108 static	hrtime_t	sliding_interval_ms;
109 static	uint32_t	dos_req_limit;
110 static	uint32_t	dos_ms;
111 static	pthread_mutex_t	dos_mutex = PTHREAD_MUTEX_INITIALIZER;
112 static	rwlock_t	init_lk;
113 static	int pool_count = 0;
114 static	pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER;
115 static	pthread_mutex_t	wait_req_mutex = PTHREAD_MUTEX_INITIALIZER;
116 static	int wait_count = 0;
117 static	struct {
118 	uid_t uid;
119 	int count;
120 } user_count[MAX_CONCURRENT_WAITS];
121 
122 /*
123  * This returns an error message to libpicl
124  */
125 static void
126 picld_return_error(picl_callnumber_t cnum, picl_errno_t err)
127 {
128 	picl_reterror_t	ret_error;
129 
130 	ret_error.cnum = PICL_CNUM_ERROR;
131 	ret_error.in_cnum = cnum;
132 	ret_error.errnum = err;
133 	(void) rw_unlock(&init_lk);
134 	(void) door_return((char *)&ret_error, sizeof (picl_reterror_t), NULL,
135 	    0);
136 }
137 
138 /*
139  * picld_init is called when a picl_initialize request is received
140  */
141 static void
142 picld_init(picl_service_t *req)
143 {
144 	picl_retinit_t	ret_init;
145 	int	clmajrev;
146 
147 	clmajrev = PICL_CLIENT_REV(req->req_init.clrev);
148 
149 	if (clmajrev < PICL_VERSION_1)
150 		picld_return_error(req->req_init.cnum, PICL_NOTSUPPORTED);
151 
152 	ret_init.cnum = req->req_init.cnum;
153 	ret_init.rev = PICLD_VERSION(PICLD_MAJOR_REV, PICLD_MINOR_REV);
154 
155 	(void) rw_unlock(&init_lk);
156 	(void) door_return((char *)&ret_init, sizeof (picl_retinit_t), NULL, 0);
157 }
158 
159 /*
160  * picld_fini is called when a picl_shutdown request is received
161  */
162 static void
163 picld_fini(picl_service_t *in)
164 {
165 	picl_retfini_t	ret;
166 
167 	ret.cnum = in->req_fini.cnum;
168 
169 	(void) rw_unlock(&init_lk);
170 	(void) door_return((char *)&ret, sizeof (picl_retfini_t), NULL, 0);
171 }
172 
173 static void
174 picld_ping(picl_service_t *in)
175 {
176 	picl_retping_t	ret;
177 
178 	ret.cnum = in->req_ping.cnum;
179 
180 	(void) rw_unlock(&init_lk);
181 	(void) door_return((char *)&ret, sizeof (picl_retping_t), NULL, 0);
182 }
183 
184 static int
185 check_user(uid_t uid)
186 {
187 	int i;
188 	uid_t tmp_uid;
189 	int free_idx = -1;
190 
191 	if (uid == 0)
192 		return (PICL_SUCCESS);
193 	for (i = 0; i < MAX_CONCURRENT_WAITS; i++) {
194 		if ((tmp_uid = user_count[i].uid) == uid) {
195 			if (user_count[i].count == MAX_USER_WAITS)
196 				return (PICL_FAILURE);
197 			user_count[i].count++;
198 			return (PICL_SUCCESS);
199 		}
200 		if ((free_idx == -1) && (tmp_uid == 0))
201 			free_idx = i;
202 	}
203 	if (free_idx != -1) {
204 		user_count[free_idx].uid = uid;
205 		user_count[free_idx].count = 1;
206 		return (PICL_SUCCESS);
207 	}
208 	return (PICL_FAILURE);
209 }
210 
211 static void
212 done_user(uid_t uid)
213 {
214 	int i;
215 
216 	if (uid == 0)
217 		return;
218 	for (i = 0; i < MAX_CONCURRENT_WAITS; i++) {
219 		if (user_count[i].uid == uid) {
220 			if (--user_count[i].count == 0)
221 				user_count[i].uid = 0;
222 			return;
223 		}
224 	}
225 }
226 
227 static int
228 enter_picld_wait(uid_t uid)
229 {
230 	int	rv;
231 
232 	if (pthread_mutex_lock(&wait_req_mutex) != 0)
233 		return (PICL_FAILURE);
234 	if ((wait_count < MAX_CONCURRENT_WAITS) &&
235 	    (check_user(uid) == PICL_SUCCESS)) {
236 		rv = PICL_SUCCESS;
237 		wait_count++;
238 	} else {
239 		rv = PICL_FAILURE;
240 	}
241 	(void) pthread_mutex_unlock(&wait_req_mutex);
242 	return (rv);
243 }
244 
245 static void
246 exit_picld_wait(uid_t uid)
247 {
248 	(void) pthread_mutex_lock(&wait_req_mutex);
249 	done_user(uid);
250 	wait_count--;
251 	(void) pthread_mutex_unlock(&wait_req_mutex);
252 }
253 
254 /*
255  * picld_wait is called when a picl_wait request is received
256  */
257 static void
258 picld_wait(picl_service_t *in)
259 {
260 	picl_retwait_t	ret;
261 	int		err;
262 	ucred_t	*puc = NULL;
263 	uid_t uid;
264 
265 	ret.cnum = in->req_wait.cnum;
266 	if (door_ucred(&puc) != 0)
267 		ret.retcode = PICL_FAILURE;
268 	else {
269 		uid = ucred_geteuid(puc);
270 		if (enter_picld_wait(uid) == PICL_FAILURE)
271 			ret.retcode = PICL_FAILURE;
272 		else {
273 			err = xptree_refresh_notify(in->req_wait.secs);
274 			ret.retcode = err;
275 			exit_picld_wait(uid);
276 		}
277 		ucred_free(puc);
278 	}
279 	(void) rw_unlock(&init_lk);
280 	(void) door_return((char *)&ret, sizeof (picl_retwait_t), NULL, 0);
281 }
282 
283 /*
284  * This function returns the handle of the root node of the PICL tree
285  */
286 static void
287 picld_getroot(picl_service_t *in)
288 {
289 	picl_retroot_t	ret;
290 	int		err;
291 
292 	ret.cnum = PICL_CNUM_GETROOT;
293 	err = ptree_get_root(&ret.rnode);
294 	if (err != PICL_SUCCESS)
295 		picld_return_error(in->in.cnum, err);
296 	cvt_ptree2picl(&ret.rnode);
297 	(void) rw_unlock(&init_lk);
298 	(void) door_return((char *)&ret, sizeof (picl_retroot_t), NULL, 0);
299 }
300 
301 /*
302  * This function returns the value of the PICL property
303  */
304 static void
305 picld_get_attrval(picl_service_t *in)
306 {
307 	picl_retattrval_t	*ret;
308 	int			err;
309 	size_t			vbufsize;
310 	size_t			len;
311 	door_cred_t		cred;
312 	picl_prophdl_t		ptreeh;
313 	ptree_propinfo_t	pinfo;
314 
315 	if (door_cred(&cred) < 0)
316 		picld_return_error(in->in.cnum, PICL_FAILURE);
317 
318 	err = cvt_picl2ptree(in->req_attrval.attr, &ptreeh);
319 	if (err != PICL_SUCCESS)
320 		picld_return_error(in->in.cnum, err);
321 
322 	err = ptree_get_propinfo(ptreeh, &pinfo);
323 	if (err != PICL_SUCCESS)
324 		picld_return_error(in->in.cnum, err);
325 
326 	if (!(pinfo.piclinfo.accessmode & PICL_READ))
327 		picld_return_error(in->in.cnum, PICL_NOTREADABLE);
328 
329 	vbufsize = pinfo.piclinfo.size;
330 	vbufsize = MIN((size_t)in->req_attrval.bufsize, vbufsize);
331 
332 	len = sizeof (picl_retattrval_t) + vbufsize;
333 	ret = alloca(len);
334 	if (ret == NULL)
335 		picld_return_error(in->in.cnum, PICL_FAILURE);
336 	ret->cnum = PICL_CNUM_GETATTRVAL;
337 	ret->attr = in->req_attrval.attr;
338 	ret->nbytes = (uint32_t)vbufsize;
339 	err = xptree_get_propval_with_cred(ptreeh, ret->ret_buf, vbufsize,
340 	    cred);
341 	if (err != PICL_SUCCESS)
342 		picld_return_error(in->in.cnum, err);
343 
344 	/*
345 	 * adjust returned bytes for charstrings
346 	 */
347 	if (pinfo.piclinfo.type == PICL_PTYPE_CHARSTRING)
348 		ret->nbytes = (uint32_t)strlen(ret->ret_buf) + 1;
349 
350 	/*
351 	 * convert handle values to picl handles
352 	 */
353 	if ((pinfo.piclinfo.type == PICL_PTYPE_TABLE) ||
354 	    (pinfo.piclinfo.type == PICL_PTYPE_REFERENCE))
355 		cvt_ptree2picl(&ret->ret_nodeh);
356 	(void) rw_unlock(&init_lk);
357 	(void) door_return((char *)ret, sizeof (picl_retattrval_t) +
358 	    (size_t)ret->nbytes, NULL, 0);
359 }
360 
361 /*
362  * This function returns the value of the PICL property specified by
363  * its name.
364  */
365 static void
366 picld_get_attrval_by_name(picl_service_t *in)
367 {
368 	picl_retattrvalbyname_t	*ret;
369 	int			err;
370 	size_t			vbufsize;
371 	size_t			len;
372 	door_cred_t		cred;
373 	picl_nodehdl_t		ptreeh;
374 	ptree_propinfo_t	pinfo;
375 
376 	if (door_cred(&cred) < 0)
377 		picld_return_error(in->in.cnum, PICL_FAILURE);
378 
379 	err = cvt_picl2ptree(in->req_attrvalbyname.nodeh, &ptreeh);
380 	if (err != PICL_SUCCESS)
381 		picld_return_error(in->in.cnum, err);
382 
383 	err = xptree_get_propinfo_by_name(ptreeh,
384 	    in->req_attrvalbyname.propname, &pinfo);
385 	if (err != PICL_SUCCESS)
386 		picld_return_error(in->in.cnum, err);
387 
388 	if (!(pinfo.piclinfo.accessmode & PICL_READ))
389 		picld_return_error(in->in.cnum, PICL_NOTREADABLE);
390 
391 	/*
392 	 * allocate the minimum of piclinfo.size and input bufsize
393 	 */
394 	vbufsize = pinfo.piclinfo.size;
395 	vbufsize = MIN((size_t)in->req_attrvalbyname.bufsize, vbufsize);
396 	len = sizeof (picl_retattrvalbyname_t) + vbufsize;
397 	ret = alloca(len);
398 	if (ret == NULL)
399 		picld_return_error(in->in.cnum, PICL_FAILURE);
400 	ret->cnum = PICL_CNUM_GETATTRVALBYNAME;
401 	ret->nodeh = in->req_attrvalbyname.nodeh;
402 	(void) strcpy(ret->propname, in->req_attrvalbyname.propname);
403 	ret->nbytes = (uint32_t)vbufsize;
404 
405 	err = xptree_get_propval_by_name_with_cred(ptreeh,
406 	    in->req_attrvalbyname.propname, ret->ret_buf, vbufsize,
407 	    cred);
408 	if (err != PICL_SUCCESS)
409 		picld_return_error(in->in.cnum, err);
410 	/*
411 	 * adjust returned value size for charstrings
412 	 */
413 	if (pinfo.piclinfo.type == PICL_PTYPE_CHARSTRING)
414 		ret->nbytes = (uint32_t)strlen(ret->ret_buf) + 1;
415 
416 	if ((pinfo.piclinfo.type == PICL_PTYPE_TABLE) ||
417 	    (pinfo.piclinfo.type == PICL_PTYPE_REFERENCE))
418 		cvt_ptree2picl(&ret->ret_nodeh);
419 
420 	(void) rw_unlock(&init_lk);
421 	(void) door_return((char *)ret, sizeof (picl_retattrvalbyname_t) +
422 	    (size_t)ret->nbytes, NULL, 0);
423 }
424 
425 /*
426  * This function sets a property value
427  */
428 static void
429 picld_set_attrval(picl_service_t *in)
430 {
431 	picl_retsetattrval_t	ret;
432 	int			err;
433 	door_cred_t		cred;
434 	picl_prophdl_t		ptreeh;
435 	ptree_propinfo_t	pinfo;
436 
437 	if (door_cred(&cred) < 0)
438 		picld_return_error(in->in.cnum, PICL_FAILURE);
439 
440 	err = cvt_picl2ptree(in->req_setattrval.attr, &ptreeh);
441 	if (err != PICL_SUCCESS)
442 		picld_return_error(in->in.cnum, err);
443 
444 	err = ptree_get_propinfo(ptreeh, &pinfo);
445 	if (err != PICL_SUCCESS)
446 		picld_return_error(in->in.cnum, err);
447 
448 	if (!(pinfo.piclinfo.accessmode & PICL_WRITE))
449 		picld_return_error(in->in.cnum, PICL_NOTWRITABLE);
450 	/*
451 	 * For non-volatile prop, only super user can set its value.
452 	 */
453 	if (!(pinfo.piclinfo.accessmode & PICL_VOLATILE) &&
454 	    (cred.dc_euid != SUPER_USER))
455 		picld_return_error(in->in.cnum, PICL_PERMDENIED);
456 
457 	ret.cnum = PICL_CNUM_SETATTRVAL;
458 	ret.attr = in->req_setattrval.attr;
459 
460 	err = xptree_update_propval_with_cred(ptreeh, in->req_setattrval.valbuf,
461 	    (size_t)in->req_setattrval.bufsize, cred);
462 
463 	if (err != PICL_SUCCESS)
464 		picld_return_error(in->in.cnum, err);
465 
466 	(void) rw_unlock(&init_lk);
467 	(void) door_return((char *)&ret, sizeof (picl_retsetattrval_t), NULL,
468 	    0);
469 }
470 
471 /*
472  * This function sets the value of a property specified by its name.
473  */
474 static void
475 picld_set_attrval_by_name(picl_service_t *in)
476 {
477 	picl_retsetattrvalbyname_t	ret;
478 	int				err;
479 	door_cred_t			cred;
480 	picl_prophdl_t			ptreeh;
481 	ptree_propinfo_t		pinfo;
482 
483 	if (door_cred(&cred) < 0)
484 		picld_return_error(in->in.cnum, PICL_FAILURE);
485 
486 	err = cvt_picl2ptree(in->req_setattrvalbyname.nodeh, &ptreeh);
487 	if (err != PICL_SUCCESS)
488 		picld_return_error(in->in.cnum, err);
489 
490 	err = xptree_get_propinfo_by_name(ptreeh,
491 	    in->req_setattrvalbyname.propname, &pinfo);
492 	if (err != PICL_SUCCESS)
493 		picld_return_error(in->in.cnum, err);
494 
495 	if (!(pinfo.piclinfo.accessmode & PICL_WRITE))
496 		picld_return_error(in->in.cnum, PICL_NOTWRITABLE);
497 
498 	/*
499 	 * For non-volatile prop, only super user can set its value.
500 	 */
501 	if (!(pinfo.piclinfo.accessmode & PICL_VOLATILE) &&
502 	    (cred.dc_euid != SUPER_USER))
503 		picld_return_error(in->in.cnum, PICL_PERMDENIED);
504 
505 	ret.cnum = PICL_CNUM_SETATTRVALBYNAME;
506 	ret.nodeh = in->req_setattrvalbyname.nodeh;
507 	(void) strcpy(ret.propname, in->req_setattrvalbyname.propname);
508 
509 	err = xptree_update_propval_by_name_with_cred(ptreeh,
510 	    in->req_setattrvalbyname.propname,
511 	    in->req_setattrvalbyname.valbuf,
512 	    (size_t)in->req_setattrvalbyname.bufsize,
513 	    cred);
514 
515 	if (err != PICL_SUCCESS)
516 		picld_return_error(in->in.cnum, err);
517 
518 	(void) rw_unlock(&init_lk);
519 	(void) door_return((char *)&ret, sizeof (picl_retsetattrvalbyname_t),
520 	    NULL, 0);
521 }
522 
523 /*
524  * This function returns the property information
525  */
526 static void
527 picld_get_attrinfo(picl_service_t *in)
528 {
529 	picl_retattrinfo_t	ret;
530 	int			err;
531 	ptree_propinfo_t	pinfo;
532 	picl_prophdl_t		ptreeh;
533 
534 	err = cvt_picl2ptree(in->req_attrinfo.attr, &ptreeh);
535 	if (err != PICL_SUCCESS)
536 		picld_return_error(in->in.cnum, err);
537 
538 	ret.cnum = PICL_CNUM_GETATTRINFO;
539 	ret.attr = in->req_attrinfo.attr;
540 
541 	err = ptree_get_propinfo(ptreeh, &pinfo);
542 	if (err != PICL_SUCCESS)
543 		picld_return_error(in->in.cnum, err);
544 
545 	ret.type = pinfo.piclinfo.type;
546 	ret.accessmode = pinfo.piclinfo.accessmode;
547 	ret.size = (uint32_t)pinfo.piclinfo.size;
548 	(void) strcpy(ret.name, pinfo.piclinfo.name);
549 	(void) rw_unlock(&init_lk);
550 	(void) door_return((char *)&ret, sizeof (picl_retattrinfo_t), NULL, 0);
551 }
552 
553 /*
554  * This function returns the node's first property handle
555  */
556 static void
557 picld_get_first_attr(picl_service_t *in)
558 {
559 	picl_retfirstattr_t	ret;
560 	int			err;
561 	picl_prophdl_t		ptreeh;
562 
563 	err = cvt_picl2ptree(in->req_firstattr.nodeh, &ptreeh);
564 	if (err != PICL_SUCCESS)
565 		picld_return_error(in->in.cnum, err);
566 
567 	ret.cnum = PICL_CNUM_GETFIRSTATTR;
568 	ret.nodeh = in->req_firstattr.nodeh;
569 
570 	err = ptree_get_first_prop(ptreeh, &ret.attr);
571 	if (err != PICL_SUCCESS)
572 		picld_return_error(in->in.cnum, err);
573 	cvt_ptree2picl(&ret.attr);
574 	(void) rw_unlock(&init_lk);
575 	(void) door_return((char *)&ret, sizeof (picl_retfirstattr_t), NULL, 0);
576 }
577 
578 /*
579  * This function returns the next property handle in list
580  */
581 static void
582 picld_get_next_attr(picl_service_t *in)
583 {
584 	picl_retnextattr_t	ret;
585 	int			err;
586 	picl_prophdl_t		ptreeh;
587 
588 	err = cvt_picl2ptree(in->req_nextattr.attr, &ptreeh);
589 	if (err != PICL_SUCCESS)
590 		picld_return_error(in->in.cnum, err);
591 
592 	ret.cnum = PICL_CNUM_GETNEXTATTR;
593 	ret.attr = in->req_nextattr.attr;
594 
595 	err = ptree_get_next_prop(ptreeh, &ret.nextattr);
596 	if (err != PICL_SUCCESS)
597 		picld_return_error(in->in.cnum, err);
598 
599 	cvt_ptree2picl(&ret.nextattr);
600 
601 	(void) rw_unlock(&init_lk);
602 	(void) door_return((char *)&ret, sizeof (picl_retnextattr_t), NULL, 0);
603 }
604 
605 /*
606  * This function returns the handle of a property specified by its name
607  */
608 static void
609 picld_get_attr_by_name(picl_service_t *in)
610 {
611 	picl_retattrbyname_t	ret;
612 	int			err;
613 	picl_prophdl_t		ptreeh;
614 
615 	err = cvt_picl2ptree(in->req_attrbyname.nodeh, &ptreeh);
616 	if (err != PICL_SUCCESS)
617 		picld_return_error(in->in.cnum, err);
618 
619 	ret.cnum = PICL_CNUM_GETATTRBYNAME;
620 	ret.nodeh = in->req_attrbyname.nodeh;
621 	(void) strcpy(ret.propname, in->req_attrbyname.propname);
622 
623 	err = ptree_get_prop_by_name(ptreeh, ret.propname, &ret.attr);
624 	if (err != PICL_SUCCESS)
625 		picld_return_error(in->in.cnum, err);
626 
627 	cvt_ptree2picl(&ret.attr);
628 	(void) rw_unlock(&init_lk);
629 	(void) door_return((char *)&ret, sizeof (picl_retattrbyname_t), NULL,
630 	    0);
631 }
632 
633 /*
634  * This function gets the next property on the same row in the table
635  */
636 static void
637 picld_get_attr_by_row(picl_service_t *in)
638 {
639 	picl_retattrbyrow_t	ret;
640 	int			err;
641 	picl_prophdl_t		ptreeh;
642 
643 	err = cvt_picl2ptree(in->req_attrbyrow.attr, &ptreeh);
644 	if (err != PICL_SUCCESS)
645 		picld_return_error(in->in.cnum, err);
646 
647 	ret.cnum = PICL_CNUM_GETATTRBYROW;
648 	ret.attr = in->req_attrbyrow.attr;
649 
650 	err = ptree_get_next_by_row(ptreeh, &ret.rowattr);
651 	if (err != PICL_SUCCESS)
652 		picld_return_error(in->in.cnum, err);
653 	cvt_ptree2picl(&ret.rowattr);
654 
655 	(void) rw_unlock(&init_lk);
656 	(void) door_return((char *)&ret, sizeof (picl_retattrbyrow_t), NULL, 0);
657 }
658 
659 /*
660  * This function returns the handle of the next property in the same column
661  * of the table.
662  */
663 static void
664 picld_get_attr_by_col(picl_service_t *in)
665 {
666 	picl_retattrbycol_t	ret;
667 	int			err;
668 	picl_prophdl_t		ptreeh;
669 
670 	err = cvt_picl2ptree(in->req_attrbycol.attr, &ptreeh);
671 	if (err != PICL_SUCCESS)
672 		picld_return_error(in->in.cnum, err);
673 
674 	ret.cnum = PICL_CNUM_GETATTRBYCOL;
675 	ret.attr = in->req_attrbycol.attr;
676 
677 	err = ptree_get_next_by_col(ptreeh, &ret.colattr);
678 	if (err != PICL_SUCCESS)
679 		picld_return_error(in->in.cnum, err);
680 
681 	cvt_ptree2picl(&ret.colattr);
682 
683 	(void) rw_unlock(&init_lk);
684 	(void) door_return((char *)&ret, sizeof (picl_retattrbycol_t), NULL, 0);
685 }
686 
687 /*
688  * This function finds the node in the PICLTREE that matches the given
689  * criteria and returns its handle.
690  */
691 static void
692 picld_find_node(picl_service_t *in)
693 {
694 	picl_retfindnode_t	ret;
695 	int			err;
696 	picl_nodehdl_t		ptreeh;
697 
698 	err = cvt_picl2ptree(in->req_findnode.nodeh, &ptreeh);
699 	if (err != PICL_SUCCESS)
700 		picld_return_error(in->in.cnum, err);
701 
702 	ret.cnum = PICL_CNUM_FINDNODE;
703 
704 	err = ptree_find_node(ptreeh, in->req_findnode.propname,
705 	    in->req_findnode.ptype, in->req_findnode.valbuf,
706 	    in->req_findnode.valsize, &ret.rnodeh);
707 	if (err != PICL_SUCCESS)
708 		picld_return_error(in->in.cnum, err);
709 
710 	cvt_ptree2picl(&ret.rnodeh);
711 
712 	(void) rw_unlock(&init_lk);
713 	(void) door_return((char *)&ret, sizeof (ret), NULL, 0);
714 }
715 
716 /*
717  * This function finds the property/node that corresponds to the given path
718  * and returns its handle
719  */
720 static void
721 picld_get_node_by_path(picl_service_t *in)
722 {
723 	picl_retnodebypath_t	ret;
724 	int			err;
725 
726 	ret.cnum = PICL_CNUM_NODEBYPATH;
727 	err = ptree_get_node_by_path(in->req_nodebypath.pathbuf, &ret.nodeh);
728 	if (err != PICL_SUCCESS)
729 		picld_return_error(in->in.cnum, err);
730 	cvt_ptree2picl(&ret.nodeh);
731 	(void) rw_unlock(&init_lk);
732 	(void) door_return((char *)&ret, sizeof (ret), NULL, 0);
733 }
734 
735 /*
736  * This function returns finds the frutree parent node for a given node
737  * and returns its handle
738  */
739 static void
740 picld_get_frutree_parent(picl_service_t *in)
741 {
742 	picl_retfruparent_t	ret;
743 	int			err;
744 	picl_nodehdl_t		ptreeh;
745 
746 	err = cvt_picl2ptree(in->req_fruparent.devh, &ptreeh);
747 	if (err != PICL_SUCCESS)
748 		picld_return_error(in->in.cnum, err);
749 
750 	ret.cnum = PICL_CNUM_FRUTREEPARENT;
751 
752 	err = ptree_get_frutree_parent(ptreeh, &ret.fruh);
753 	if (err != PICL_SUCCESS)
754 		picld_return_error(in->in.cnum, err);
755 	cvt_ptree2picl(&ret.fruh);
756 
757 	(void) rw_unlock(&init_lk);
758 	(void) door_return((char *)&ret, sizeof (ret), NULL, 0);
759 }
760 
761 /*
762  * This function is called when an unknown client request is received.
763  */
764 static void
765 picld_unknown_service(picl_service_t *in)
766 {
767 	picld_return_error(in->in.cnum, PICL_UNKNOWNSERVICE);
768 }
769 
770 static void
771 check_denial_of_service(int cnum)
772 {
773 	hrtime_t	window;
774 	hrtime_t	current;
775 	int		dos_flag;
776 
777 	current = gethrtime();
778 	dos_flag = 0;
779 
780 	if (pthread_mutex_lock(&dos_mutex) != 0)
781 		picld_return_error(cnum, PICL_FAILURE);
782 
783 	++service_requests;
784 	window = current - orig_time;
785 	if (window > MILLI_TO_NANO(sliding_interval_ms)) {
786 		orig_time = current;
787 		service_requests = 1;
788 	}
789 
790 	if (service_requests > dos_req_limit)
791 		dos_flag = 1;
792 
793 	if (pthread_mutex_unlock(&dos_mutex) != 0)
794 		picld_return_error(cnum, PICL_FAILURE);
795 
796 	if (dos_flag)
797 		(void) poll(NULL, 0, dos_ms);
798 }
799 
800 /* ARGSUSED */
801 static void
802 picld_door_handler(void *cookie, char *argp, size_t asize,
803     door_desc_t *dp, uint_t n_desc)
804 {
805 	picl_service_t  *req;
806 
807 	/*LINTED*/
808 	req = (picl_service_t *)argp;
809 
810 	if (req == NULL)
811 		(void) door_return((char *)req, 0, NULL, 0);
812 
813 	check_denial_of_service(req->in.cnum);
814 
815 	(void) rw_rdlock(&init_lk);
816 	switch (req->in.cnum) {	/* client call number */
817 	case PICL_CNUM_INIT:
818 		/*LINTED*/
819 		picld_init((picl_service_t *)argp);
820 		break;
821 	case PICL_CNUM_FINI:
822 		/*LINTED*/
823 		picld_fini((picl_service_t *)argp);
824 		break;
825 	case PICL_CNUM_GETROOT:
826 		/*LINTED*/
827 		picld_getroot((picl_service_t *)argp);
828 		break;
829 	case PICL_CNUM_GETATTRVAL:
830 		/*LINTED*/
831 		picld_get_attrval((picl_service_t *)argp);
832 		break;
833 	case PICL_CNUM_GETATTRVALBYNAME:
834 		/*LINTED*/
835 		picld_get_attrval_by_name((picl_service_t *)argp);
836 		break;
837 	case PICL_CNUM_GETATTRINFO:
838 		/*LINTED*/
839 		picld_get_attrinfo((picl_service_t *)argp);
840 		break;
841 	case PICL_CNUM_GETFIRSTATTR:
842 		/*LINTED*/
843 		picld_get_first_attr((picl_service_t *)argp);
844 		break;
845 	case PICL_CNUM_GETNEXTATTR:
846 		/*LINTED*/
847 		picld_get_next_attr((picl_service_t *)argp);
848 		break;
849 	case PICL_CNUM_GETATTRBYNAME:
850 		/*LINTED*/
851 		picld_get_attr_by_name((picl_service_t *)argp);
852 		break;
853 	case PICL_CNUM_GETATTRBYROW:
854 		/*LINTED*/
855 		picld_get_attr_by_row((picl_service_t *)argp);
856 		break;
857 	case PICL_CNUM_GETATTRBYCOL:
858 		/*LINTED*/
859 		picld_get_attr_by_col((picl_service_t *)argp);
860 		break;
861 	case PICL_CNUM_SETATTRVAL:
862 		/*LINTED*/
863 		picld_set_attrval((picl_service_t *)argp);
864 		break;
865 	case PICL_CNUM_SETATTRVALBYNAME:
866 		/*LINTED*/
867 		picld_set_attrval_by_name((picl_service_t *)argp);
868 		break;
869 	case PICL_CNUM_PING:
870 		/*LINTED*/
871 		picld_ping((picl_service_t *)argp);
872 		break;
873 	case PICL_CNUM_WAIT:
874 		/*LINTED*/
875 		picld_wait((picl_service_t *)argp);
876 		break;
877 	case PICL_CNUM_FINDNODE:
878 		/*LINTED*/
879 		picld_find_node((picl_service_t *)argp);
880 		break;
881 	case PICL_CNUM_NODEBYPATH:
882 		/*LINTED*/
883 		picld_get_node_by_path((picl_service_t *)argp);
884 		break;
885 	case PICL_CNUM_FRUTREEPARENT:
886 		/*LINTED*/
887 		picld_get_frutree_parent((picl_service_t *)argp);
888 		break;
889 	default:
890 		/*LINTED*/
891 		picld_unknown_service((picl_service_t *)argp);
892 		break;
893 	};
894 	/*NOTREACHED*/
895 }
896 
897 /* ARGSUSED */
898 static void
899 hup_handler(int sig, siginfo_t *siginfo, void *sigctx)
900 {
901 	doreinit = 1;
902 }
903 
904 /*
905  * "ping" to see if a daemon is already running
906  */
907 static int
908 daemon_exists(void)
909 {
910 	door_arg_t	darg;
911 	picl_reqping_t	req_ping;
912 	picl_retping_t	ret_ping;
913 	int		doorh;
914 	door_info_t	dinfo;
915 
916 	doorh = open(PICLD_DOOR, O_RDONLY);
917 	if (doorh < 0)
918 		return (0);
919 
920 	if (door_info(doorh, &dinfo) < 0) {
921 		(void) close(doorh);
922 		return (0);
923 	}
924 
925 	if ((dinfo.di_attributes & DOOR_REVOKED) ||
926 	    (dinfo.di_data != (uintptr_t)PICLD_DOOR_COOKIE)) {
927 		(void) close(doorh);
928 		return (0);
929 	}
930 
931 	if (dinfo.di_target != getpid()) {
932 		(void) close(doorh);
933 		return (1);
934 	}
935 
936 	req_ping.cnum = PICL_CNUM_PING;
937 
938 	darg.data_ptr = (char *)&req_ping;
939 	darg.data_size = sizeof (picl_reqping_t);
940 	darg.desc_ptr = NULL;
941 	darg.desc_num = 0;
942 	darg.rbuf = (char *)&ret_ping;
943 	darg.rsize = sizeof (picl_retping_t);
944 
945 	if (door_call(doorh, &darg) < 0) {
946 		(void) close(doorh);
947 		return (0);
948 	}
949 
950 	(void) close(doorh);
951 	return (1);
952 }
953 
954 /*
955  * picld_create_server_thread - binds the running thread to the private
956  * door pool, and sets the required cancellation state.
957  */
958 /* ARGSUSED */
959 static void *
960 picld_create_server_thread(void *arg)
961 {
962 	/*
963 	 * wait for door descriptor to be initialized
964 	 */
965 	(void) pthread_mutex_lock(&door_mutex);
966 	while (door_id == -1) {
967 		(void) pthread_cond_wait(&door_cv, &door_mutex);
968 	}
969 	(void) pthread_mutex_unlock(&door_mutex);
970 
971 	/*
972 	 * Bind this thread to the door's private thread pool
973 	 */
974 	if (door_bind(door_id) < 0) {
975 		perror("door_bind");
976 	}
977 
978 	/*
979 	 * Disable thread cancellation mechanism
980 	 */
981 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
982 	(void) door_return(NULL, 0, NULL, 0); /* wait for door invocation */
983 	return (NULL);
984 }
985 
986 /*
987  * picld_server_create_fn - creates threads for the private door pool
988  *
989  */
990 /* ARGSUSED */
991 static void
992 picld_server_create_fn(door_info_t *dip)
993 {
994 	pthread_attr_t attr;
995 
996 	(void) pthread_mutex_lock(&pool_mutex);
997 	if (pool_count < MAX_POOL_SIZE) {
998 		(void) pthread_attr_init(&attr);
999 		(void) pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
1000 		(void) pthread_attr_setdetachstate(&attr,
1001 		    PTHREAD_CREATE_DETACHED);
1002 		if (pthread_create(NULL, &attr, picld_create_server_thread,
1003 		    NULL)) {
1004 			perror("pthread_create");
1005 		} else {
1006 			pool_count++;
1007 		}
1008 	}
1009 	(void) pthread_mutex_unlock(&pool_mutex);
1010 }
1011 
1012 /*
1013  * Create the picld door
1014  */
1015 static int
1016 setup_door(void)
1017 {
1018 	struct stat	stbuf;
1019 
1020 	(void) door_server_create(picld_server_create_fn);
1021 	(void) pthread_mutex_lock(&door_mutex);
1022 	/*
1023 	 * Create the door
1024 	 */
1025 	door_id = door_create(picld_door_handler, PICLD_DOOR_COOKIE,
1026 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL | DOOR_PRIVATE);
1027 
1028 	if (door_id < 0) {
1029 		(void) pthread_mutex_unlock(&door_mutex);
1030 		return (-1);
1031 	} else {
1032 		(void) pthread_cond_signal(&door_cv);
1033 		(void) pthread_mutex_unlock(&door_mutex);
1034 	}
1035 
1036 	if (stat(PICLD_DOOR, &stbuf) < 0) {
1037 		int newfd;
1038 		mode_t old_mask;
1039 		/* ensure that the door file is world-readable */
1040 		old_mask = umask(0);
1041 		newfd = creat(PICLD_DOOR, 0444);
1042 		/* restore the file mode creation mask */
1043 		(void) umask(old_mask);
1044 		if (newfd < 0)
1045 			return (-1);
1046 		(void) close(newfd);
1047 	}
1048 
1049 	if (fattach(door_id, PICLD_DOOR) < 0) {
1050 		if ((errno != EBUSY) ||
1051 		    (fdetach(PICLD_DOOR) < 0) ||
1052 		    (fattach(door_id, PICLD_DOOR) < 0))
1053 			return (-1);
1054 	}
1055 	return (0);
1056 }
1057 
1058 /*
1059  * Main function of picl daemon
1060  */
1061 int
1062 main(int argc, char **argv)
1063 {
1064 	struct	sigaction	act;
1065 	int			c;
1066 	sigset_t		ublk;
1067 
1068 
1069 	(void) setlocale(LC_ALL, "");
1070 	(void) textdomain(TEXT_DOMAIN);
1071 
1072 	if (getuid() != 0) {
1073 		syslog(LOG_CRIT, MUST_BE_ROOT);
1074 		return (0);
1075 	}
1076 
1077 	(void) rwlock_init(&init_lk, USYNC_THREAD, NULL);
1078 	doreinit = 0;
1079 	logflag = 1;
1080 	dos_req_limit = DOS_PICL_REQUESTS_LIMIT;
1081 	sliding_interval_ms = SLIDING_INTERVAL_MILLISECONDS;
1082 	dos_ms = DOS_SLEEPTIME_MS;
1083 	verbose_level = 0;
1084 
1085 	/*
1086 	 * parse arguments
1087 	 */
1088 	while ((c = getopt(argc, argv, "is:t:l:r:v:d:")) != EOF) {
1089 		switch (c) {
1090 		case 'd':
1091 			dos_ms = strtol(optarg, (char **)NULL, 0);
1092 			break;
1093 		case 'i':
1094 			logflag = 0;
1095 			break;
1096 		case 's':
1097 			sliding_interval_ms = strtoll(optarg, (char **)NULL, 0);
1098 			break;
1099 		case 't':
1100 			dos_req_limit = strtol(optarg, (char **)NULL, 0);
1101 			break;
1102 		case 'v':
1103 			verbose_level = strtol(optarg, (char **)NULL, 0);
1104 			logflag = 0;
1105 			break;
1106 		default:
1107 			break;
1108 		}
1109 	}
1110 
1111 	orig_time = gethrtime();
1112 
1113 	/*
1114 	 * is there a daemon already running?
1115 	 */
1116 
1117 	if (daemon_exists()) {
1118 		syslog(LOG_CRIT, DAEMON_RUNNING);
1119 		exit(1);
1120 	}
1121 
1122 	/*
1123 	 * Mask off/block SIGALRM signal so that the environmental plug-in
1124 	 * (piclenvd) can use it to simulate sleep() without being affected
1125 	 * by time being set back. No other PICL plug-in should use SIGALRM
1126 	 * or alarm() for now.
1127 	 */
1128 	(void) sigemptyset(&ublk);
1129 	(void) sigaddset(&ublk, SIGALRM);
1130 	(void) sigprocmask(SIG_BLOCK, &ublk, NULL);
1131 
1132 	/*
1133 	 * Ignore SIGHUP until all the initialization is done.
1134 	 */
1135 	act.sa_handler = SIG_IGN;
1136 	(void) sigemptyset(&act.sa_mask);
1137 	act.sa_flags = 0;
1138 	if (sigaction(SIGHUP, &act, NULL) == -1)
1139 		syslog(LOG_ERR, SIGACT_FAILED, strsignal(SIGHUP),
1140 		    strerror(errno));
1141 
1142 	if (logflag != 0) {	/* daemonize */
1143 		pid_t pid;
1144 
1145 		pid = fork();
1146 		if (pid < 0)
1147 			exit(1);
1148 		if (pid > 0)
1149 			/* parent */
1150 			exit(0);
1151 
1152 		/* child */
1153 		if (chdir("/") == -1) {
1154 			syslog(LOG_CRIT, CD_ROOT_FAILED);
1155 			exit(1);
1156 		}
1157 
1158 		(void) setsid();
1159 		(void) close(STDIN_FILENO);
1160 		(void) close(STDOUT_FILENO);
1161 		(void) close(STDERR_FILENO);
1162 		(void) open("/dev/null", O_RDWR, 0);
1163 		(void) dup2(STDIN_FILENO, STDOUT_FILENO);
1164 		(void) dup2(STDIN_FILENO, STDERR_FILENO);
1165 		openlog(PICLD, LOG_PID, LOG_DAEMON);
1166 	}
1167 
1168 	/*
1169 	 * Initialize the PICL Tree
1170 	 */
1171 	if (xptree_initialize(NULL) != PICL_SUCCESS) {
1172 		syslog(LOG_CRIT, INIT_FAILED);
1173 		exit(1);
1174 	}
1175 
1176 	if (setup_door()) {
1177 		syslog(LOG_CRIT, DOOR_FAILED);
1178 		exit(1);
1179 	}
1180 
1181 	/*
1182 	 * setup signal handlers for post-init
1183 	 */
1184 	act.sa_sigaction = hup_handler;
1185 	(void) sigemptyset(&act.sa_mask);
1186 	act.sa_flags = SA_SIGINFO;
1187 	if (sigaction(SIGHUP, &act, NULL) == -1)
1188 		syslog(LOG_ERR, SIGACT_FAILED, strsignal(SIGHUP),
1189 		    strerror(errno));
1190 
1191 	/*
1192 	 * wait for requests
1193 	 */
1194 	for (;;) {
1195 		(void) pause();
1196 		if (doreinit) {
1197 			/*
1198 			 * Block SIGHUP during reinitialization.
1199 			 * Also mask off/block SIGALRM signal so that the
1200 			 * environmental plug-in (piclenvd) can use it to
1201 			 * simulate sleep() without being affected by time
1202 			 * being set back. No ohter PICL plug-in should use
1203 			 * SIGALRM or alarm() for now.
1204 			 */
1205 			(void) sigemptyset(&ublk);
1206 			(void) sigaddset(&ublk, SIGHUP);
1207 			(void) sigaddset(&ublk, SIGALRM);
1208 			(void) sigprocmask(SIG_BLOCK, &ublk, NULL);
1209 			(void) sigdelset(&ublk, SIGALRM);
1210 			doreinit = 0;
1211 			(void) rw_wrlock(&init_lk);
1212 			xptree_destroy();
1213 			(void) xptree_reinitialize();
1214 			(void) rw_unlock(&init_lk);
1215 			(void) sigprocmask(SIG_UNBLOCK, &ublk, NULL);
1216 		}
1217 	}
1218 }
1219