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