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