xref: /titanic_41/usr/src/lib/smbsrv/libmlsvc/common/svcctl_scm.c (revision c6d6228cbba828ab5b2b6db6c280a44b2d841653)
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 2009 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 SMF services: handles wide-char or ascii requests.
537  *
538  * Returns the number of services written to buf.
539  */
540 uint32_t
541 svcctl_scm_enum_services(svcctl_manager_context_t *mgr_ctx, uint8_t *buf,
542     size_t buflen, uint32_t *resume_handle, boolean_t use_wchar)
543 {
544 	svcctl_svc_node_t *node;
545 	int base_offset, offset;
546 	mts_wchar_t *w_name;
547 	char *a_name;
548 	char *node_name;
549 	size_t namelen;
550 	uint32_t numsvcs = mgr_ctx->mc_scf_numsvcs;
551 	uint32_t ns;
552 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
553 	svc_enum_status_t *svc = (svc_enum_status_t *)buf;
554 
555 	if (buf == NULL || buflen == 0 || *resume_handle >= numsvcs) {
556 		*resume_handle = 0;
557 		return (0);
558 	}
559 
560 	base_offset = numsvcs * sizeof (svc_enum_status_t);
561 	if (buflen < mgr_ctx->mc_bytes_needed) {
562 		while (base_offset > (buflen / 4)) {
563 			--numsvcs;
564 			base_offset = numsvcs * sizeof (svc_enum_status_t);
565 		}
566 	}
567 
568 	offset = base_offset;
569 	node = uu_avl_first(mgr_ctx->mc_svcs);
570 
571 	for (ns = 0; ((ns < *resume_handle) && (node != NULL)); ++ns)
572 		node = uu_avl_next(mgr_ctx->mc_svcs, node);
573 
574 	if (node == NULL) {
575 		*resume_handle = 0;
576 		return (0);
577 	}
578 
579 	for (ns = 0; ((ns < numsvcs) && (node != NULL)); ++ns) {
580 		node_name = node->sn_name;
581 		namelen = strlen(node_name) + 1;
582 		svc[ns].svc_name = offset;
583 
584 		if (use_wchar) {
585 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
586 			w_name = (mts_wchar_t *)&buf[offset];
587 			(void) mts_mbstowcs(w_name, node_name, namelen);
588 			offset += SVCCTL_WNSTRLEN(node_name);
589 		} else {
590 			a_name = (char *)&buf[offset];
591 			(void) strlcpy(a_name, node_name, namelen);
592 			offset += namelen;
593 		}
594 
595 		if (offset >= buflen)
596 			break;
597 
598 		node_name = node->sn_fmri;
599 		namelen = strlen(node_name) + 1;
600 		svc[ns].display_name = offset;
601 
602 		if (use_wchar) {
603 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
604 			w_name = (mts_wchar_t *)&buf[offset];
605 			(void) mts_mbstowcs(w_name, node_name, namelen);
606 			offset += SVCCTL_WNSTRLEN(node_name);
607 		} else {
608 			a_name = (char *)&buf[offset];
609 			(void) strlcpy(a_name, node_name, namelen);
610 			offset += namelen;
611 		}
612 
613 		if (offset >= buflen)
614 			break;
615 
616 		svc[ns].svc_status.cur_state =
617 		    svcctl_scm_map_status(node->sn_state);
618 		svc[ns].svc_status.service_type = SERVICE_WIN32_SHARE_PROCESS;
619 		svc[ns].svc_status.ctrl_accepted = 0;
620 		svc[ns].svc_status.w32_exitcode = 0;
621 		svc[ns].svc_status.svc_specified_exitcode = 0;
622 		svc[ns].svc_status.check_point = 0;
623 		svc[ns].svc_status.wait_hint = 0;
624 
625 		node = uu_avl_next(mgr_ctx->mc_svcs, node);
626 	}
627 
628 	if (node == NULL) {
629 		*resume_handle = 0;
630 	} else {
631 		*resume_handle += ns;
632 
633 		if (*resume_handle >= mgr_ctx->mc_scf_numsvcs)
634 			*resume_handle = 0;
635 	}
636 
637 	return (ns);
638 }
639 
640 /*
641  * svcctl_scm_cb_bytes_needed
642  *
643  * Callback function to calculate bytes needed to enumerate SMF services.
644  */
645 static int
646 svcctl_scm_cb_bytes_needed(void *svc_node, void *byte_cnt)
647 {
648 	svcctl_svc_node_t *node = svc_node;
649 	int *cnt = byte_cnt;
650 
651 	*cnt += (strlen(node->sn_fmri) + 1) * sizeof (mts_wchar_t);
652 	*cnt += (strlen(node->sn_name) + 1) * sizeof (mts_wchar_t);
653 
654 	return (UU_WALK_NEXT);
655 }
656 
657 /*
658  * svcctl_scm_bytes_needed
659  *
660  * Calculates bytes needed to enumerate SMF services.
661  */
662 void
663 svcctl_scm_bytes_needed(svcctl_manager_context_t *mgr_ctx)
664 {
665 	int bytes_needed = 0, svc_enum_status_size = 0;
666 
667 	(void) uu_avl_walk(mgr_ctx->mc_svcs, svcctl_scm_cb_bytes_needed,
668 	    &bytes_needed, 0);
669 
670 	svc_enum_status_size =
671 	    mgr_ctx->mc_scf_numsvcs * sizeof (svc_enum_status_t);
672 	bytes_needed += svc_enum_status_size;
673 
674 	mgr_ctx->mc_bytes_needed = bytes_needed;
675 }
676 
677 /*
678  * svcctl_scm_validate_service
679  *
680  * Check to see whether or not a service is supported.
681  *
682  * Returns:
683  *	ERROR_SUCCESS
684  *	ERROR_SERVICE_DOES_NOT_EXIST
685  */
686 uint32_t
687 svcctl_scm_validate_service(svcctl_manager_context_t *mgr_ctx, char *svc_name)
688 {
689 	if (svcctl_scm_find_service(mgr_ctx, svc_name) != NULL)
690 		return (ERROR_SUCCESS);
691 
692 	return (ERROR_SERVICE_DOES_NOT_EXIST);
693 }
694 
695 /*
696  * svcctl_scm_map_windows_svc
697  *
698  * Windows client send windows service name. This method maps windows
699  * service names to Solaris service names.
700  */
701 static char *
702 svcctl_scm_map_windows_svc(char *svc_name)
703 {
704 	int i, size = 0;
705 	struct {
706 		char *win_svc_name;
707 		char *solaris_svc_name;
708 	} win2solaris_svc_map[] = {
709 		{ "eventlog", "system/system-log:default" },
710 		{ "RemoteRegistry", "system/svc/restarter:default" },
711 		{ "spooler",  "application/print/ppd-cache-update:default" }
712 	};
713 
714 	size = sizeof (win2solaris_svc_map)/sizeof (win2solaris_svc_map[0]);
715 	for (i = 0; i < size; ++i) {
716 		if (strcasecmp(svc_name,
717 		    win2solaris_svc_map[i].win_svc_name) == 0)
718 			return (win2solaris_svc_map[i].solaris_svc_name);
719 	}
720 
721 	return (NULL);
722 }
723 
724 /*
725  * svcctl_scm_find_service
726  *
727  * Lookup a service.
728  */
729 svcctl_svc_node_t *
730 svcctl_scm_find_service(svcctl_manager_context_t *mgr_ctx, char *svc_name)
731 {
732 	svcctl_svc_node_t node;
733 	uu_avl_index_t idx;
734 	svcctl_svc_node_t *f_node = NULL;
735 
736 	if (svc_name == NULL)
737 		return (NULL);
738 
739 	bzero(&node, sizeof (svcctl_svc_node_t));
740 	node.sn_name = svc_name;
741 	f_node = uu_avl_find(mgr_ctx->mc_svcs, &node,
742 	    &mgr_ctx->mc_scf_max_fmri_len, &idx);
743 	if (f_node != NULL)
744 		return (f_node);
745 
746 	bzero(&node, sizeof (svcctl_svc_node_t));
747 	node.sn_name = svcctl_scm_map_windows_svc(svc_name);
748 	if (node.sn_name != NULL)
749 		f_node = uu_avl_find(mgr_ctx->mc_svcs, &node,
750 		    &mgr_ctx->mc_scf_max_fmri_len, &idx);
751 
752 	return (f_node);
753 }
754 
755 /*
756  * svcctl_scm_refresh
757  *
758  * Refresh SCM services per context.
759  */
760 int
761 svcctl_scm_refresh(svcctl_manager_context_t *mgr_ctx)
762 {
763 	svcctl_scm_fini(mgr_ctx);
764 	return (svcctl_scm_init(mgr_ctx));
765 }
766 
767 /*
768  * svcctl_scm_scf_handle_init
769  *
770  * Initialize SCF handle per context.
771  */
772 int
773 svcctl_scm_scf_handle_init(svcctl_manager_context_t *mgr_ctx)
774 {
775 	mgr_ctx->mc_scf_hdl = scf_handle_create(SCF_VERSION);
776 	if (mgr_ctx->mc_scf_hdl == NULL)
777 		return (-1);
778 
779 	if (scf_handle_bind(mgr_ctx->mc_scf_hdl) == -1) {
780 		scf_handle_destroy(mgr_ctx->mc_scf_hdl);
781 		return (-1);
782 	}
783 
784 	mgr_ctx->mc_scf_gpg = scf_pg_create(mgr_ctx->mc_scf_hdl);
785 	mgr_ctx->mc_scf_gprop = scf_property_create(mgr_ctx->mc_scf_hdl);
786 	mgr_ctx->mc_scf_gval = scf_value_create(mgr_ctx->mc_scf_hdl);
787 
788 	if ((mgr_ctx->mc_scf_gpg == NULL) ||
789 	    (mgr_ctx->mc_scf_gprop == NULL) ||
790 	    (mgr_ctx->mc_scf_gval == NULL)) {
791 		(void) scf_handle_unbind(mgr_ctx->mc_scf_hdl);
792 		scf_handle_destroy(mgr_ctx->mc_scf_hdl);
793 		return (-1);
794 	}
795 
796 	mgr_ctx->mc_scf_max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
797 	mgr_ctx->mc_scf_max_value_len = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
798 
799 	return (0);
800 }
801 
802 /*
803  * svcctl_scm_scf_handle_init
804  *
805  * Destroy SCF handle per context.
806  */
807 void
808 svcctl_scm_scf_handle_fini(svcctl_manager_context_t *mgr_ctx)
809 {
810 	scf_value_destroy(mgr_ctx->mc_scf_gval);
811 	scf_property_destroy(mgr_ctx->mc_scf_gprop);
812 	scf_pg_destroy(mgr_ctx->mc_scf_gpg);
813 	(void) scf_handle_unbind(mgr_ctx->mc_scf_hdl);
814 	scf_handle_destroy(mgr_ctx->mc_scf_hdl);
815 }
816 
817 /*
818  * svcctl_scm_init
819  *
820  * Initialize SCM repository per context.
821  * SCM repository holds a list of SMF services.
822  * Each SMF service node contains state, description and FMRI.
823  */
824 int
825 svcctl_scm_init(svcctl_manager_context_t *mgr_ctx)
826 {
827 	int exit_status = 0;
828 
829 	assert(mgr_ctx->mc_svcs_pool == NULL);
830 	assert(mgr_ctx->mc_svcs == NULL);
831 
832 	mgr_ctx->mc_svcs_pool = uu_avl_pool_create("smf_svcs_pool",
833 	    sizeof (svcctl_svc_node_t), offsetof(svcctl_svc_node_t, sn_node),
834 	    svcctl_scm_avl_nodecmp, UU_AVL_DEBUG);
835 
836 	if (mgr_ctx->mc_svcs_pool == NULL)
837 		return (-1);
838 
839 	mgr_ctx->mc_svcs = uu_avl_create(mgr_ctx->mc_svcs_pool, NULL, 0);
840 	if (mgr_ctx->mc_svcs == NULL) {
841 		uu_avl_pool_destroy(mgr_ctx->mc_svcs_pool);
842 		return (-1);
843 	}
844 
845 	if (scf_walk_fmri(mgr_ctx->mc_scf_hdl, 0, NULL,
846 	    SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
847 	    svcctl_scm_cb_list_svcinst, mgr_ctx, &exit_status, NULL) != 0) {
848 		uu_avl_destroy(mgr_ctx->mc_svcs);
849 		uu_avl_pool_destroy(mgr_ctx->mc_svcs_pool);
850 		return (-1);
851 	}
852 
853 	mgr_ctx->mc_scf_numsvcs = uu_avl_numnodes(mgr_ctx->mc_svcs);
854 	if (mgr_ctx->mc_scf_numsvcs > 0)
855 		svcctl_scm_bytes_needed(mgr_ctx);
856 
857 	return (0);
858 }
859 
860 /*
861  * svcctl_scm_fini
862  *
863  * Destroy SCM repository per context.
864  */
865 void
866 svcctl_scm_fini(svcctl_manager_context_t *mgr_ctx)
867 {
868 	uu_avl_walk_t *walk;
869 	svcctl_svc_node_t *node;
870 
871 	if ((mgr_ctx == NULL) || (mgr_ctx->mc_svcs_pool == NULL) ||
872 	    (mgr_ctx->mc_svcs == NULL))
873 		return;
874 
875 	if ((walk =
876 	    uu_avl_walk_start(mgr_ctx->mc_svcs, UU_WALK_ROBUST)) == NULL)
877 		return;
878 
879 	while ((node = uu_avl_walk_next(walk)) != NULL) {
880 		uu_avl_remove(mgr_ctx->mc_svcs, node);
881 		free(node->sn_name);
882 		free(node->sn_fmri);
883 		free(node->sn_desc);
884 		free(node->sn_state);
885 		free(node);
886 	}
887 	uu_avl_walk_end(walk);
888 	uu_avl_destroy(mgr_ctx->mc_svcs);
889 	uu_avl_pool_destroy(mgr_ctx->mc_svcs_pool);
890 	mgr_ctx->mc_svcs_pool = NULL;
891 	mgr_ctx->mc_svcs = NULL;
892 }
893