xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/svcctl_scm.c (revision b89a8333f5e1f75ec0c269b22524bd2eccb972ba)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Service Control Manager (SCM) for SVCCTL service.
28  *
29  * This routine maintains a list of SMF service and their states. A list
30  * of Solaris SMF service are displayed on the Server/Connection Manager
31  * Windows client.
32  */
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <strings.h>
38 #include <assert.h>
39 #include <errno.h>
40 #include <libscf.h>
41 #include <libscf_priv.h>
42 #include <time.h>
43 #include <sys/types.h>
44 
45 #include "svcctl_scm.h"
46 
47 #define	LEGACY_UNKNOWN	"unknown"
48 #define	SVC_NAME_PROP	"name"
49 
50 /* Flags for svcctl_scm_pg_get_val() */
51 #define	EMPTY_OK	0x01
52 #define	MULTI_OK	0x02
53 
54 /*
55  * svcctl_scm_avl_nodecmp
56  *
57  * Comparision function for nodes in an AVL tree of services.
58  */
59 /* ARGSUSED */
60 static int
61 svcctl_scm_avl_nodecmp(const void *l_arg, const void *r_arg, void *m_name_len)
62 {
63 	const svcctl_svc_node_t *l = l_arg;
64 	const svcctl_svc_node_t *r = r_arg;
65 	int *max_name_len = m_name_len;
66 	int ret = 0;
67 
68 	ret = strncasecmp(l->sn_name, r->sn_name, *max_name_len);
69 
70 	if (ret > 0)
71 		return (1);
72 	if (ret < 0)
73 		return (-1);
74 	return (0);
75 }
76 
77 /*
78  * svcctl_scm_pg_get_val
79  *
80  * Get the single value of the named property in the given property group,
81  * which must have type ty, and put it in *vp.  If ty is SCF_TYPE_ASTRING, vp
82  * is taken to be a char **, and sz is the size of the buffer.  sz is unused
83  * otherwise.  Return 0 on success, -1 if the property doesn't exist, has the
84  * wrong type, or doesn't have a single value.  If flags has EMPTY_OK, don't
85  * complain if the property has no values (but return nonzero).  If flags has
86  * MULTI_OK and the property has multiple values, succeed with E2BIG.
87  */
88 static int
89 svcctl_scm_pg_get_val(svcctl_manager_context_t *mgr_ctx,
90     scf_propertygroup_t *pg, const char *propname, scf_type_t ty, void *vp,
91     size_t sz, uint_t flags)
92 {
93 	int ret = -1, r;
94 	boolean_t multi = B_FALSE;
95 
96 	assert((flags & ~(EMPTY_OK | MULTI_OK)) == 0);
97 
98 	if (scf_pg_get_property(pg, propname, mgr_ctx->mc_scf_gprop) == -1)
99 		return (ret);
100 
101 	if (scf_property_is_type(mgr_ctx->mc_scf_gprop, ty) != SCF_SUCCESS)
102 		return (ret);
103 
104 	if (scf_property_get_value(mgr_ctx->mc_scf_gprop,
105 	    mgr_ctx->mc_scf_gval) != SCF_SUCCESS) {
106 		switch (scf_error()) {
107 		case SCF_ERROR_NOT_FOUND:
108 			return (ret);
109 
110 		case SCF_ERROR_CONSTRAINT_VIOLATED:
111 			if (flags & MULTI_OK) {
112 				multi = B_TRUE;
113 				break;
114 			}
115 			return (ret);
116 
117 		case SCF_ERROR_PERMISSION_DENIED:
118 		default:
119 			return (ret);
120 		}
121 	}
122 
123 	switch (ty) {
124 	case SCF_TYPE_ASTRING:
125 		r = scf_value_get_astring
126 		    (mgr_ctx->mc_scf_gval, vp, sz) > 0 ? SCF_SUCCESS : -1;
127 		break;
128 
129 	case SCF_TYPE_BOOLEAN:
130 		r = scf_value_get_boolean(mgr_ctx->mc_scf_gval, (uint8_t *)vp);
131 		break;
132 
133 	case SCF_TYPE_COUNT:
134 		r = scf_value_get_count(mgr_ctx->mc_scf_gval, (uint64_t *)vp);
135 		break;
136 
137 	case SCF_TYPE_INTEGER:
138 		r = scf_value_get_integer(mgr_ctx->mc_scf_gval, (int64_t *)vp);
139 		break;
140 
141 	case SCF_TYPE_TIME: {
142 		int64_t sec;
143 		int32_t ns;
144 		r = scf_value_get_time(mgr_ctx->mc_scf_gval, &sec, &ns);
145 		((struct timeval *)vp)->tv_sec = sec;
146 		((struct timeval *)vp)->tv_usec = ns / 1000;
147 		break;
148 	}
149 
150 	case SCF_TYPE_USTRING:
151 		r = scf_value_get_ustring(mgr_ctx->mc_scf_gval, vp, sz) > 0 ?
152 		    SCF_SUCCESS : -1;
153 		break;
154 
155 	default:
156 		return (ret);
157 	}
158 
159 	if (r != SCF_SUCCESS)
160 		return (ret);
161 
162 	ret = multi ? E2BIG : 0;
163 
164 	return (ret);
165 }
166 
167 /*
168  * svcctl_scm_get_running_snapshot
169  *
170  * Get running snapshot of a service instance.
171  */
172 static scf_snapshot_t *
173 svcctl_scm_get_running_snapshot(svcctl_manager_context_t *mgr_ctx,
174     scf_instance_t *inst)
175 {
176 	scf_snapshot_t *snap;
177 
178 	snap = scf_snapshot_create(mgr_ctx->mc_scf_hdl);
179 	if (snap == NULL)
180 		return (NULL);
181 
182 	if (scf_instance_get_snapshot(inst, "running", snap) == 0)
183 		return (snap);
184 
185 	if (scf_error() != SCF_ERROR_NOT_FOUND)
186 		return (NULL);
187 
188 	scf_snapshot_destroy(snap);
189 	return (NULL);
190 }
191 
192 /*
193  * svcctl_scm_inst_get_val
194  *
195  * As svcctl_scm_pg_get_val(), except look the property group up in an
196  * instance.  If "use_running" is set, and the running snapshot exists,
197  * do a composed lookup there.  Otherwise, do an (optionally composed)
198  * lookup on the current values.  Note that lookups using snapshots are
199  * always composed.
200  */
201 static int
202 svcctl_scm_inst_get_val(svcctl_manager_context_t *mgr_ctx, scf_instance_t *inst,
203     const char *pgname, const char *propname, scf_type_t ty, void *vp,
204     size_t sz, uint_t flags, int use_running, int composed)
205 {
206 	scf_snapshot_t *snap = NULL;
207 	int r;
208 
209 	if (use_running)
210 		snap = svcctl_scm_get_running_snapshot(mgr_ctx, inst);
211 	if (composed || use_running)
212 		r = scf_instance_get_pg_composed(inst, snap, pgname,
213 		    mgr_ctx->mc_scf_gpg);
214 	else
215 		r = scf_instance_get_pg(inst, pgname, mgr_ctx->mc_scf_gpg);
216 	if (snap)
217 		scf_snapshot_destroy(snap);
218 	if (r == -1)
219 		return (-1);
220 
221 	r = svcctl_scm_pg_get_val(mgr_ctx, mgr_ctx->mc_scf_gpg, propname, ty,
222 	    vp, sz, flags);
223 
224 	return (r);
225 }
226 
227 /*
228  * svcctl_scm_get_restarter_string_prop
229  *
230  * Get a string property from the restarter property group of the given
231  * instance.  Return an empty string on normal problems.
232  */
233 static void
234 svcctl_scm_get_restarter_string_prop(svcctl_manager_context_t *mgr_ctx,
235     scf_instance_t *inst, const char *pname, char *buf, size_t buf_sz)
236 {
237 	if (svcctl_scm_inst_get_val(mgr_ctx, inst, SCF_PG_RESTARTER, pname,
238 	    SCF_TYPE_ASTRING, buf, buf_sz, 0, 0, 1) != 0)
239 		*buf = '\0';
240 }
241 
242 /*
243  * svcctl_scm_svc_transitioning
244  *
245  * Return true if a service instance is transitioning.
246  */
247 static int
248 svcctl_scm_svc_transitioning(svcctl_manager_context_t *mgr_ctx,
249     scf_instance_t *inst)
250 {
251 	char nstate_name[MAX_SCF_STATE_STRING_SZ];
252 
253 	bzero(nstate_name, MAX_SCF_STATE_STRING_SZ);
254 	svcctl_scm_get_restarter_string_prop(mgr_ctx, inst,
255 	    SCF_PROPERTY_NEXT_STATE, nstate_name, sizeof (nstate_name));
256 
257 	return ((*nstate_name == '\0'));
258 }
259 
260 /*
261  * svcctl_scm_get_svcstate
262  *
263  * Gets the state of an SMF service.
264  */
265 static int
266 svcctl_scm_get_svcstate(svcctl_manager_context_t *mgr_ctx,
267     char **buf, scf_walkinfo_t *wip)
268 {
269 	char *state_name;
270 	size_t max_state_size;
271 
272 	max_state_size = MAX_SCF_STATE_STRING_SZ + 1;
273 
274 	if ((state_name = malloc(max_state_size)) == NULL)
275 		return (-1);
276 
277 	if (wip->pg == NULL) {
278 		svcctl_scm_get_restarter_string_prop(mgr_ctx, wip->inst,
279 		    SCF_PROPERTY_STATE, state_name, max_state_size);
280 
281 		/* Don't print blank fields, to ease parsing. */
282 		if (state_name[0] == '\0') {
283 			state_name[0] = '-';
284 			state_name[1] = '\0';
285 		}
286 
287 		if (svcctl_scm_svc_transitioning(mgr_ctx, wip->inst))
288 			/* Append an asterisk if new state is valid. */
289 			(void) strlcat(state_name, "*", max_state_size);
290 
291 	} else
292 		(void) strlcpy(state_name, SCF_STATE_STRING_LEGACY,
293 		    max_state_size);
294 
295 	*buf = state_name;
296 	return (0);
297 }
298 
299 /*
300  * svcctl_scm_get_svcdesc
301  *
302  * Gets the description of an SMF service.
303  */
304 static int
305 svcctl_scm_get_svcdesc(svcctl_manager_context_t *mgr_ctx,
306     char **buf, scf_walkinfo_t *wip)
307 {
308 	char *x;
309 	size_t newsize;
310 	char *newbuf;
311 	char *desc_buf = NULL;
312 
313 	if ((desc_buf = malloc(mgr_ctx->mc_scf_max_value_len + 1)) == NULL)
314 		return (-1);
315 
316 	bzero(desc_buf, mgr_ctx->mc_scf_max_value_len + 1);
317 	if (wip->pg != NULL)
318 		desc_buf[0] = '-';
319 	else if (svcctl_scm_inst_get_val(mgr_ctx, wip->inst,
320 	    SCF_PG_TM_COMMON_NAME, "C", SCF_TYPE_USTRING, desc_buf,
321 	    mgr_ctx->mc_scf_max_value_len, 0, 1, 1) == -1)
322 		desc_buf[0] = '-';
323 
324 	/*
325 	 * Collapse multi-line tm_common_name values into a single line.
326 	 */
327 	for (x = desc_buf; *x != '\0'; x++)
328 		if (*x == '\n')
329 			*x = ' ';
330 
331 	newsize = strlen(desc_buf) + 1;
332 	if ((newbuf = malloc(newsize)) == NULL) {
333 		free(desc_buf);
334 		return (-1);
335 	}
336 
337 	(void) snprintf(newbuf, newsize, "%s", desc_buf);
338 	free(desc_buf);
339 
340 	*buf = newbuf;
341 	return (0);
342 }
343 
344 /*
345  * svcctl_scm_get_svcfmri
346  *
347  * Gets the FMRI of an SMF service.
348  */
349 static int
350 svcctl_scm_get_svcfmri(svcctl_manager_context_t *mgr_ctx,
351     char **buf, scf_walkinfo_t *wip)
352 {
353 	size_t newsize;
354 	char *newbuf;
355 	char *fmri_buf = NULL;
356 	void *fmri_p = NULL;
357 	size_t fmri_size;
358 
359 	if ((fmri_buf = malloc(mgr_ctx->mc_scf_max_fmri_len + 1)) == NULL)
360 		return (-1);
361 
362 	if (wip->pg == NULL) {
363 		if (scf_instance_to_fmri(wip->inst, fmri_buf,
364 		    mgr_ctx->mc_scf_max_fmri_len + 1) == -1) {
365 			free(fmri_buf);
366 			return (-1);
367 		}
368 	} else {
369 		(void) strlcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX,
370 		    mgr_ctx->mc_scf_max_fmri_len + 1);
371 
372 		fmri_p = fmri_buf + sizeof (SCF_FMRI_LEGACY_PREFIX) - 1;
373 		fmri_size = mgr_ctx->mc_scf_max_fmri_len + 1 - \
374 		    (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1);
375 
376 		if (svcctl_scm_pg_get_val(mgr_ctx, wip->pg,
377 		    SCF_LEGACY_PROPERTY_NAME, SCF_TYPE_ASTRING,
378 		    fmri_p, fmri_size, 0) != 0)
379 			(void) strlcat(fmri_buf, LEGACY_UNKNOWN,
380 			    mgr_ctx->mc_scf_max_fmri_len + 1);
381 	}
382 
383 	newsize = strlen(fmri_buf) + 1;
384 	if ((newbuf = malloc(newsize)) == NULL) {
385 		free(fmri_buf);
386 		return (-1);
387 	}
388 
389 	(void) snprintf(newbuf, newsize, "%s", fmri_buf);
390 	free(fmri_buf);
391 
392 	*buf = newbuf;
393 	return (0);
394 }
395 
396 /*
397  * svcctl_scm_get_svcname
398  *
399  * Gets the FMRI of an SMF service.
400  */
401 static int
402 svcctl_scm_get_svcname(char **buf, char *fmri)
403 {
404 	char *nm_buf = NULL;
405 	char *newbuf;
406 	size_t newsize;
407 
408 	if (fmri == NULL)
409 		return (-1);
410 
411 	newsize = strlen(fmri);
412 	if ((newbuf = malloc(newsize)) == NULL)
413 		return (-1);
414 
415 	if ((nm_buf = strchr(fmri, '/')) == NULL)
416 		return (-1);
417 
418 	(void) snprintf(newbuf, newsize, "%s", ++nm_buf);
419 	*buf = newbuf;
420 	return (0);
421 }
422 
423 /*
424  * svcctl_scm_cb_list_svcinst
425  *
426  * Callback function to walk all the services in an SCF repository.
427  */
428 static int
429 svcctl_scm_cb_list_svcinst(void *context, scf_walkinfo_t *wip)
430 {
431 	svcctl_svc_node_t *node = NULL;
432 	uu_avl_index_t idx;
433 	svcctl_manager_context_t *mgr_ctx = (svcctl_manager_context_t *)context;
434 
435 	node = malloc(sizeof (*node));
436 	if (node == NULL)
437 		return (-1);
438 
439 	node->sn_fmri = NULL;
440 	if (svcctl_scm_get_svcfmri(mgr_ctx, &node->sn_fmri, wip) != 0)
441 		return (-1);
442 
443 	node->sn_name = NULL;
444 	if (svcctl_scm_get_svcname(&node->sn_name, node->sn_fmri) != 0)
445 		return (-1);
446 
447 	node->sn_desc = NULL;
448 	if (svcctl_scm_get_svcdesc(mgr_ctx, &node->sn_desc, wip) != 0)
449 		return (-1);
450 
451 	node->sn_state = NULL;
452 	if (svcctl_scm_get_svcstate(mgr_ctx, &node->sn_state, wip) != 0)
453 		return (-1);
454 
455 	/* Insert into AVL tree. */
456 	uu_avl_node_init(node, &node->sn_node, mgr_ctx->mc_svcs_pool);
457 	(void) uu_avl_find(mgr_ctx->mc_svcs, node,
458 	    &mgr_ctx->mc_scf_max_fmri_len, &idx);
459 	uu_avl_insert(mgr_ctx->mc_svcs, node, idx);
460 
461 	return (0);
462 }
463 
464 /*
465  * svcctl_scm_map_status
466  *
467  * Report the service status.
468  *
469  * The mapping between the Microsoft service states and SMF service states
470  * are as follows.
471  *
472  * SMF service states
473  * ==================
474  *	SCF_STATE_UNINIT                0x00000001
475  *	SCF_STATE_MAINT                 0x00000002
476  *	SCF_STATE_OFFLINE               0x00000004
477  *	SCF_STATE_DISABLED              0x00000008
478  *	SCF_STATE_ONLINE                0x00000010
479  *	SCF_STATE_DEGRADED              0x00000020
480  *	SCF_STATE_ALL                   0x0000003F
481  *
482  * Microsoft service states
483  * ========================
484  *	SERVICE_CONTINUE_PENDING	0x00000005
485  *	SERVICE_PAUSE_PENDING		0x00000006
486  *	SERVICE_PAUSED			0x00000007
487  *	SERVICE_RUNNING			0x00000004
488  *	SERVICE_START_PENDING		0x00000002
489  *	SERVICE_STOP_PENDING		0x00000003
490  *	SERVICE_STOPPED			0x00000001
491  *
492  * Mapping
493  * =======
494  *
495  *	SCF_STATE_ONLINE	<->	SERVICE_RUNNING
496  *	SCF_STATE_OFFLINE	<->	SERVICE_PAUSED
497  *	SCF_STATE_DISABLED	<->	SERVICE_STOPPED
498  *	SCF_STATE_UNINIT	<->	SERVICE_START_PENDING
499  *	SCF_STATE_DEGRADED	<->	SERVICE_STOP_PENDING
500  *	SCF_STATE_MAINT		<->	SERVICE_PAUSE_PENDING
501  *	SCF_STATE_STRING_LEGACY <->	SERVICE_RUNNING
502  *	Service Transitioning	<->	SERVICE_STOP_PENDING
503  */
504 uint32_t
505 svcctl_scm_map_status(const char *state)
506 {
507 	int i;
508 
509 	struct {
510 		const char	*scf_state;
511 		uint32_t	scm_state;
512 	} state_map[] = {
513 		{ SCF_STATE_STRING_ONLINE,	SERVICE_RUNNING },
514 		{ SCF_STATE_STRING_OFFLINE,	SERVICE_PAUSED },
515 		{ SCF_STATE_STRING_DISABLED,	SERVICE_STOPPED },
516 		{ SCF_STATE_STRING_UNINIT,	SERVICE_START_PENDING },
517 		{ SCF_STATE_STRING_DEGRADED,	SERVICE_STOP_PENDING },
518 		{ SCF_STATE_STRING_MAINT,	SERVICE_PAUSE_PENDING },
519 		{ SCF_STATE_STRING_LEGACY,	SERVICE_RUNNING }
520 	};
521 
522 	for (i = 0; i < (sizeof (state_map)/sizeof (state_map[0])); ++i) {
523 		if (strcmp(state, state_map[i].scf_state) == 0)
524 			return (state_map[i].scm_state);
525 	}
526 
527 	if (strrchr(state, '*') != 0)	/* State Transitioning */
528 		return (SERVICE_STOP_PENDING);
529 
530 	return (SERVICE_RUNNING);
531 }
532 
533 /*
534  * svcctl_scm_enum_services
535  *
536  * Enumerates all SMF services.
537  */
538 void
539 svcctl_scm_enum_services(svcctl_manager_context_t *mgr_ctx,
540     unsigned char *services)
541 {
542 	svcctl_svc_node_t *node = NULL;
543 	int base_offset, offset, i;
544 	mts_wchar_t *wide_name;
545 	char *name;
546 
547 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
548 	svc_enum_status_t *svc = (svc_enum_status_t *)services;
549 
550 	base_offset = mgr_ctx->mc_scf_numsvcs * sizeof (svc_enum_status_t);
551 
552 	offset = base_offset;
553 	node = uu_avl_first(mgr_ctx->mc_svcs);
554 
555 	for (i = 0; ((i < mgr_ctx->mc_scf_numsvcs) && (node != NULL)); ++i) {
556 		svc[i].svc_name = offset;
557 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
558 		wide_name = (mts_wchar_t *)&services[offset];
559 		name = node->sn_name;
560 		(void) mts_mbstowcs(wide_name, name, (strlen(name) + 1));
561 
562 		offset += SVCCTL_WNSTRLEN(name);
563 
564 		svc[i].display_name = offset;
565 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
566 		wide_name = (mts_wchar_t *)&services[offset];
567 		name = node->sn_fmri;
568 		(void) mts_mbstowcs(wide_name, name, (strlen(name) + 1));
569 
570 		offset += SVCCTL_WNSTRLEN(name);
571 
572 		svc[i].svc_status.cur_state =
573 		    svcctl_scm_map_status(node->sn_state);
574 		svc[i].svc_status.service_type = SERVICE_WIN32_SHARE_PROCESS;
575 		svc[i].svc_status.ctrl_accepted = 0;
576 		svc[i].svc_status.w32_exitcode = 0;
577 		svc[i].svc_status.svc_specified_exitcode = 0;
578 		svc[i].svc_status.check_point = 0;
579 		svc[i].svc_status.wait_hint = 0;
580 
581 		node = uu_avl_next(mgr_ctx->mc_svcs, node);
582 	}
583 }
584 
585 /*
586  * svcctl_scm_cb_bytes_needed
587  *
588  * Callback function to calculate bytes needed to enumerate SMF services.
589  */
590 static int
591 svcctl_scm_cb_bytes_needed(void *svc_node, void *byte_cnt)
592 {
593 	svcctl_svc_node_t *node = svc_node;
594 	int *cnt = byte_cnt;
595 
596 	*cnt += (strlen(node->sn_fmri) + 1) * sizeof (mts_wchar_t);
597 	*cnt += (strlen(node->sn_name) + 1) * sizeof (mts_wchar_t);
598 
599 	return (UU_WALK_NEXT);
600 }
601 
602 /*
603  * svcctl_scm_bytes_needed
604  *
605  * Calculates bytes needed to enumerate SMF services.
606  */
607 void
608 svcctl_scm_bytes_needed(svcctl_manager_context_t *mgr_ctx)
609 {
610 	int bytes_needed = 0, svc_enum_status_size = 0;
611 
612 	(void) uu_avl_walk(mgr_ctx->mc_svcs, svcctl_scm_cb_bytes_needed,
613 	    &bytes_needed, 0);
614 
615 	svc_enum_status_size =
616 	    mgr_ctx->mc_scf_numsvcs * sizeof (svc_enum_status_t);
617 	bytes_needed += svc_enum_status_size;
618 
619 	mgr_ctx->mc_bytes_needed = bytes_needed;
620 }
621 
622 /*
623  * svcctl_scm_validate_service
624  *
625  * Check to see whether or not a service is supported.
626  *
627  * Returns:
628  *	ERROR_SUCCESS
629  *	ERROR_SERVICE_DOES_NOT_EXIST
630  */
631 uint32_t
632 svcctl_scm_validate_service(svcctl_manager_context_t *mgr_ctx, char *svc_name)
633 {
634 	svcctl_svc_node_t node;
635 	uu_avl_index_t idx;
636 
637 	if (svc_name == NULL)
638 		return (ERROR_SERVICE_DOES_NOT_EXIST);
639 
640 	bzero(&node, sizeof (svcctl_svc_node_t));
641 	node.sn_name = svc_name;
642 	if (uu_avl_find(mgr_ctx->mc_svcs, &node,
643 	    &mgr_ctx->mc_scf_max_fmri_len, &idx) != NULL)
644 		return (ERROR_SUCCESS);
645 
646 	return (ERROR_SERVICE_DOES_NOT_EXIST);
647 }
648 
649 /*
650  * svcctl_scm_find_service
651  *
652  * Lookup a service.
653  */
654 svcctl_svc_node_t *
655 svcctl_scm_find_service(svcctl_manager_context_t *mgr_ctx, char *svc_name)
656 {
657 	svcctl_svc_node_t node;
658 	uu_avl_index_t idx;
659 	svcctl_svc_node_t *f_node = NULL;
660 
661 	if (svc_name == NULL)
662 		return (NULL);
663 
664 	bzero(&node, sizeof (svcctl_svc_node_t));
665 	node.sn_name = svc_name;
666 	f_node = uu_avl_find(mgr_ctx->mc_svcs, &node,
667 	    &mgr_ctx->mc_scf_max_fmri_len, &idx);
668 	if (f_node != NULL)
669 		return (f_node);
670 
671 	return (NULL);
672 }
673 
674 /*
675  * svcctl_scm_refresh
676  *
677  * Refresh SCM services per context.
678  */
679 int
680 svcctl_scm_refresh(svcctl_manager_context_t *mgr_ctx)
681 {
682 	svcctl_scm_fini(mgr_ctx);
683 	return (svcctl_scm_init(mgr_ctx));
684 }
685 
686 /*
687  * svcctl_scm_scf_handle_init
688  *
689  * Initialize SCF handle per context.
690  */
691 int
692 svcctl_scm_scf_handle_init(svcctl_manager_context_t *mgr_ctx)
693 {
694 	mgr_ctx->mc_scf_hdl = scf_handle_create(SCF_VERSION);
695 	if (mgr_ctx->mc_scf_hdl == NULL)
696 		return (-1);
697 
698 	if (scf_handle_bind(mgr_ctx->mc_scf_hdl) == -1) {
699 		scf_handle_destroy(mgr_ctx->mc_scf_hdl);
700 		return (-1);
701 	}
702 
703 	mgr_ctx->mc_scf_gpg = scf_pg_create(mgr_ctx->mc_scf_hdl);
704 	mgr_ctx->mc_scf_gprop = scf_property_create(mgr_ctx->mc_scf_hdl);
705 	mgr_ctx->mc_scf_gval = scf_value_create(mgr_ctx->mc_scf_hdl);
706 
707 	if ((mgr_ctx->mc_scf_gpg == NULL) ||
708 	    (mgr_ctx->mc_scf_gprop == NULL) ||
709 	    (mgr_ctx->mc_scf_gval == NULL)) {
710 		(void) scf_handle_unbind(mgr_ctx->mc_scf_hdl);
711 		scf_handle_destroy(mgr_ctx->mc_scf_hdl);
712 		return (-1);
713 	}
714 
715 	mgr_ctx->mc_scf_max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
716 	mgr_ctx->mc_scf_max_value_len = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
717 
718 	return (0);
719 }
720 
721 /*
722  * svcctl_scm_scf_handle_init
723  *
724  * Destroy SCF handle per context.
725  */
726 void
727 svcctl_scm_scf_handle_fini(svcctl_manager_context_t *mgr_ctx)
728 {
729 	scf_value_destroy(mgr_ctx->mc_scf_gval);
730 	scf_property_destroy(mgr_ctx->mc_scf_gprop);
731 	scf_pg_destroy(mgr_ctx->mc_scf_gpg);
732 	(void) scf_handle_unbind(mgr_ctx->mc_scf_hdl);
733 	scf_handle_destroy(mgr_ctx->mc_scf_hdl);
734 }
735 
736 /*
737  * svcctl_scm_init
738  *
739  * Initialize SCM repository per context.
740  * SCM repository holds a list of SMF services.
741  * Each SMF service node contains state, description and FMRI.
742  */
743 int
744 svcctl_scm_init(svcctl_manager_context_t *mgr_ctx)
745 {
746 	int exit_status = 0;
747 
748 	assert(mgr_ctx->mc_svcs_pool == NULL);
749 	assert(mgr_ctx->mc_svcs == NULL);
750 
751 	mgr_ctx->mc_svcs_pool = uu_avl_pool_create("smf_svcs_pool",
752 	    sizeof (svcctl_svc_node_t), offsetof(svcctl_svc_node_t, sn_node),
753 	    svcctl_scm_avl_nodecmp, UU_AVL_DEBUG);
754 
755 	if (mgr_ctx->mc_svcs_pool == NULL)
756 		return (-1);
757 
758 	mgr_ctx->mc_svcs = uu_avl_create(mgr_ctx->mc_svcs_pool, NULL, 0);
759 	if (mgr_ctx->mc_svcs == NULL) {
760 		uu_avl_pool_destroy(mgr_ctx->mc_svcs_pool);
761 		return (-1);
762 	}
763 
764 	if (scf_walk_fmri(mgr_ctx->mc_scf_hdl, 0, NULL,
765 	    SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
766 	    svcctl_scm_cb_list_svcinst, mgr_ctx, &exit_status, NULL) != 0) {
767 		uu_avl_destroy(mgr_ctx->mc_svcs);
768 		uu_avl_pool_destroy(mgr_ctx->mc_svcs_pool);
769 		return (-1);
770 	}
771 
772 	mgr_ctx->mc_scf_numsvcs = uu_avl_numnodes(mgr_ctx->mc_svcs);
773 	if (mgr_ctx->mc_scf_numsvcs > 0)
774 		svcctl_scm_bytes_needed(mgr_ctx);
775 
776 	return (0);
777 }
778 
779 /*
780  * svcctl_scm_fini
781  *
782  * Destroy SCM repository per context.
783  */
784 void
785 svcctl_scm_fini(svcctl_manager_context_t *mgr_ctx)
786 {
787 	uu_avl_walk_t *walk;
788 	svcctl_svc_node_t *node;
789 
790 	if ((mgr_ctx == NULL) || (mgr_ctx->mc_svcs_pool == NULL) ||
791 	    (mgr_ctx->mc_svcs == NULL))
792 		return;
793 
794 	if ((walk =
795 	    uu_avl_walk_start(mgr_ctx->mc_svcs, UU_WALK_ROBUST)) == NULL)
796 		return;
797 
798 	while ((node = uu_avl_walk_next(walk)) != NULL) {
799 		uu_avl_remove(mgr_ctx->mc_svcs, node);
800 		free(node->sn_name);
801 		free(node->sn_fmri);
802 		free(node->sn_desc);
803 		free(node->sn_state);
804 		free(node);
805 	}
806 	uu_avl_walk_end(walk);
807 	uu_avl_destroy(mgr_ctx->mc_svcs);
808 	uu_avl_pool_destroy(mgr_ctx->mc_svcs_pool);
809 	mgr_ctx->mc_svcs_pool = NULL;
810 	mgr_ctx->mc_svcs = NULL;
811 }
812