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