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
57static void *svcctl_scm_interposer_hdl = NULL;
58static 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 */
69static int
70svcctl_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 */
97static int
98svcctl_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 */
181static scf_snapshot_t *
182svcctl_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 */
210static int
211svcctl_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 */
242static void
243svcctl_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 */
256static int
257svcctl_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 */
274static int
275svcctl_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 */
313static int
314svcctl_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 */
358static int
359svcctl_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 */
410static int
411svcctl_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 */
437static int
438svcctl_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 */
513uint32_t
514svcctl_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 */
549uint32_t
550svcctl_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 */
652static int
653svcctl_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 */
669static void
670svcctl_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 */
693uint32_t
694svcctl_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 */
708static char *
709svcctl_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 */
736svcctl_svc_node_t *
737svcctl_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 */
767int
768svcctl_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 */
783int
784svcctl_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 */
822void
823svcctl_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 */
842int
843svcctl_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 */
886void
887svcctl_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 */
921void
922svcctl_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 */
950void
951svcctl_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