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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * This provides the basic mechanisms (str2nvl and nvl2str) for dealing with
28 * the service schema.  The official version of a svc FMRI has the form:
29 *
30 *	svc://[scope@][system-fqn]/service[:instance][@contract-id]
31 *
32 * Where 'service' is a slash-delimited list of names.  Of these fields, the
33 * scope, constract-id, and system-fqn are rarely used, leaving the much more
34 * common form such as:
35 *
36 *	svc:///network/ssh:default
37 *
38 * Note that the SMF software typically uses a shorthard form, where the
39 * authority is elided (svc:/network/ssh:default).  As this module deals with
40 * FMA FMRIs, we only support fully specified FMRIs.
41 *
42 * This module does not support enumeration, but implements methods for FMRI
43 * state (present, unusable, service state, and replaced).
44 */
45
46#include <fm/topo_mod.h>
47#include <fm/fmd_fmri.h>
48#include <sys/fm/protocol.h>
49#include <topo_method.h>
50#include <topo_subr.h>
51#include <topo_prop.h>
52#include <alloca.h>
53#include <assert.h>
54#include <svc.h>
55#include <strings.h>
56#include <libscf.h>
57
58static int svc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
59    nvlist_t *, nvlist_t **);
60static int svc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
61    nvlist_t *, nvlist_t **);
62static int svc_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
63    nvlist_t *, nvlist_t **);
64static int svc_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
65    nvlist_t *, nvlist_t **);
66static int svc_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
67    nvlist_t *, nvlist_t **);
68static int svc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
69    nvlist_t *, nvlist_t **);
70static int svc_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t,
71    nvlist_t *, nvlist_t **);
72
73static const topo_method_t svc_methods[] = {
74	{ TOPO_METH_PROP_GET, TOPO_METH_PROP_GET_DESC,
75	    TOPO_METH_PROP_GET_VERSION, TOPO_STABILITY_INTERNAL,
76	    svc_fmri_prop_get },
77	{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
78	    TOPO_STABILITY_INTERNAL, svc_fmri_nvl2str },
79	{ TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION,
80	    TOPO_STABILITY_INTERNAL, svc_fmri_str2nvl },
81	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
82	    TOPO_STABILITY_INTERNAL, svc_fmri_present },
83	{ TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
84	    TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL,
85	    svc_fmri_replaced },
86	{ TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC,
87	    TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL,
88	    svc_fmri_service_state },
89	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
90	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
91	    svc_fmri_unusable },
92	{ NULL }
93};
94
95static int svc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
96    topo_instance_t, void *, void *);
97static void svc_release(topo_mod_t *, tnode_t *);
98
99static const topo_modops_t svc_ops =
100	{ svc_enum, svc_release };
101static const topo_modinfo_t svc_info =
102	{ "svc", FM_FMRI_SCHEME_SVC, SVC_VERSION, &svc_ops };
103
104static int
105svc_error(topo_mod_t *mod)
106{
107	switch (scf_error()) {
108	case SCF_ERROR_NO_MEMORY:
109		return (topo_mod_seterrno(mod, EMOD_NOMEM));
110
111	default:
112		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
113	}
114}
115
116static scf_handle_t *
117svc_get_handle(topo_mod_t *mod)
118{
119	scf_handle_t *hdl = topo_mod_getspecific(mod);
120
121	if (hdl != NULL)
122		return (hdl);
123
124	if ((hdl = scf_handle_create(SCF_VERSION)) == NULL) {
125		(void) svc_error(mod);
126		return (NULL);
127	}
128
129	if (scf_handle_bind(hdl) != 0) {
130		scf_handle_destroy(hdl);
131		(void) svc_error(mod);
132		return (NULL);
133	}
134
135	topo_mod_setspecific(mod, hdl);
136
137	return (hdl);
138}
139
140int
141svc_init(topo_mod_t *mod, topo_version_t version)
142{
143	if (getenv("TOPOSVCDEBUG"))
144		topo_mod_setdebug(mod);
145
146	if (version != SVC_VERSION)
147		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
148
149	if (topo_mod_register(mod, &svc_info, TOPO_VERSION) != 0) {
150		topo_mod_dprintf(mod, "failed to register svc_info: "
151		    "%s\n", topo_mod_errmsg(mod));
152		return (-1);
153	}
154
155	return (0);
156}
157
158void
159svc_fini(topo_mod_t *mod)
160{
161	scf_handle_t *hdl = topo_mod_getspecific(mod);
162
163	if (hdl != NULL)
164		scf_handle_destroy(hdl);
165
166	topo_mod_unregister(mod);
167}
168
169static tnode_t *
170svc_create_node(topo_mod_t *mod, tnode_t *pnode, char *fmristr)
171{
172	nvlist_t *fmri;
173	tnode_t *tn;
174	char *fixed;
175	ssize_t len;
176	int i, j, err;
177
178	/*
179	 * the scf_{x}_to_fmri interfaces return short-hand svc-scheme FMRI's
180	 * that look like:
181	 *
182	 * svc:/service[:instance]
183	 *
184	 * But all our other code assumes a proper svc-scheme FMRI, so we
185	 * correct the fmri string before we try to convert it to an nvlist.
186	 *
187	 * The short-hand version is kept as the label and can be used when
188	 * dealing with the SMF libraries and CLI's.
189	 */
190	len = strlen(fmristr) + 1;
191	if ((fixed = topo_mod_zalloc(mod, len + 1)) == NULL) {
192		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
193		topo_mod_dprintf(mod, "topo_mod_zalloc() failed: %s",
194		    topo_mod_errmsg(mod));
195		return (NULL);
196	}
197	for (i = 0, j = 0; i < len; i++)
198		if (i == 5)
199			fixed[i] = '/';
200		else
201			fixed[i] = fmristr[j++];
202	fixed[i] = '\0';
203
204	if (topo_mod_str2nvl(mod, fixed, &fmri) < 0) {
205		topo_mod_dprintf(mod, "topo_mod_str2nvl() failed: %s",
206		    topo_mod_errmsg(mod));
207		topo_mod_free(mod, fixed, len + 1);
208		return (NULL);
209	}
210	topo_mod_free(mod, fixed, len + 1);
211
212	if (topo_node_range_create(mod, pnode, fmristr, 0, 0) < 0) {
213		topo_mod_dprintf(mod, "topo_node_range_create() failed: %s",
214		    topo_mod_errmsg(mod));
215		nvlist_free(fmri);
216		return (NULL);
217	}
218	if ((tn = topo_node_bind(mod, pnode, fmristr, 0, fmri)) == NULL) {
219		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
220		    topo_mod_errmsg(mod));
221		nvlist_free(fmri);
222		return (NULL);
223	}
224	nvlist_free(fmri);
225
226	if (topo_node_label_set(tn, fmristr, &err) != 0) {
227		topo_mod_dprintf(mod, "failed to set label: %s\n",
228		    topo_strerror(err));
229		return (NULL);
230	}
231	(void) topo_method_register(mod, tn, svc_methods);
232
233	return (tn);
234}
235
236/*ARGSUSED*/
237static int
238svc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
239    topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
240{
241	scf_handle_t *hdl;
242	scf_scope_t *sc = NULL;
243	scf_iter_t *svc_iter = NULL;
244	scf_iter_t *inst_iter = NULL;
245	scf_service_t *svc = NULL;
246	scf_instance_t *inst = NULL;
247	int ret = -1;
248	char *sfmri, *ifmri;
249	ssize_t slen, ilen;
250	tnode_t *svc_node;
251
252	(void) topo_method_register(mod, pnode, svc_methods);
253
254	if ((hdl = svc_get_handle(mod)) == NULL)
255		goto out;
256
257	if ((sc = scf_scope_create(hdl)) == NULL ||
258	    (svc = scf_service_create(hdl)) == NULL ||
259	    (inst = scf_instance_create(hdl)) == NULL ||
260	    (svc_iter = scf_iter_create(hdl)) == NULL ||
261	    (inst_iter = scf_iter_create(hdl)) == NULL)
262		goto out;
263
264	if (scf_handle_get_scope(hdl, SCF_SCOPE_LOCAL, sc) != 0)
265		goto out;
266
267	if (scf_iter_scope_services(svc_iter, sc) != 0)
268		goto out;
269
270	while (scf_iter_next_service(svc_iter, svc) == 1) {
271		if (scf_iter_service_instances(inst_iter, svc) != 0)
272			continue;
273
274		if ((slen = scf_service_to_fmri(svc, NULL, 0)) < 0)
275			continue;
276
277		if ((sfmri = topo_mod_zalloc(mod, slen + 1)) == NULL) {
278			(void) topo_mod_seterrno(mod, EMOD_NOMEM);
279			goto out;
280		}
281		if (scf_service_to_fmri(svc, sfmri, slen + 1) == -1)
282			goto out;
283
284		if ((svc_node = svc_create_node(mod, pnode, sfmri)) == NULL) {
285			topo_mod_free(mod, sfmri, slen + 1);
286			/* topo mod errno set */
287			goto out;
288		}
289
290		while (scf_iter_next_instance(inst_iter, inst) == 1) {
291			if ((ilen = scf_instance_to_fmri(inst, NULL, 0)) < 0)
292				continue;
293
294			if ((ifmri = topo_mod_zalloc(mod, ilen + 1))
295			    == NULL) {
296				(void) topo_mod_seterrno(mod, EMOD_NOMEM);
297				topo_mod_free(mod, sfmri, slen + 1);
298				goto out;
299			}
300			if (scf_instance_to_fmri(inst, ifmri, ilen + 1) == -1)
301				goto out;
302
303			if ((svc_node = svc_create_node(mod, svc_node, ifmri))
304			    == NULL) {
305				topo_mod_free(mod, sfmri, slen + 1);
306				topo_mod_free(mod, ifmri, ilen + 1);
307				/* topo mod errno set */
308				goto out;
309			}
310			topo_mod_free(mod, ifmri, ilen + 1);
311		}
312		topo_mod_free(mod, sfmri, slen + 1);
313	}
314	ret = 0;
315out:
316	scf_scope_destroy(sc);
317	scf_service_destroy(svc);
318	scf_instance_destroy(inst);
319	scf_iter_destroy(svc_iter);
320	scf_iter_destroy(inst_iter);
321
322	return (ret);
323}
324
325static void
326svc_release(topo_mod_t *mod, tnode_t *node)
327{
328	topo_method_unregister_all(mod, node);
329}
330
331static boolean_t
332svc_component_valid(const char *str)
333{
334	if (str == NULL)
335		return (B_TRUE);
336
337	if (*str == '\0')
338		return (B_FALSE);
339
340	if (strpbrk(str, "@/:") != NULL)
341		return (B_FALSE);
342
343	return (B_TRUE);
344}
345
346static int
347svc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
348    nvlist_t *in, nvlist_t **out)
349{
350	char *svc_name, *svc_inst = NULL;
351	nvlist_t *rsrc, *args;
352	char *pgroup, *pname;
353	tnode_t *svc_node;
354	char *search;
355	size_t len;
356	int err;
357
358	if (version > TOPO_METH_PROP_GET_VERSION)
359		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
360
361	err = nvlist_lookup_string(in, TOPO_PROP_GROUP, &pgroup);
362	err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME, &pname);
363	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &rsrc);
364	if (err != 0)
365		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
366
367	/*
368	 * Private args to prop method are optional
369	 */
370	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &args)) != 0) {
371		if (err != ENOENT)
372			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
373		else
374			args = NULL;
375	}
376
377	/*
378	 * Lookup a topo node named svc:/svc_name[:svc_inst]
379	 */
380	if (nvlist_lookup_string(rsrc, FM_FMRI_SVC_NAME, &svc_name) != 0)
381		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
382
383	(void) nvlist_lookup_string(rsrc, FM_FMRI_SVC_INSTANCE, &svc_inst);
384
385	len = 5 + strlen(svc_name) +
386	    (svc_inst != NULL ? 1 + strlen(svc_inst) : 0) + 1;
387
388	if ((search = topo_mod_alloc(mod, len)) == NULL)
389		return (topo_mod_seterrno(mod, EMOD_NOMEM));
390
391	(void) snprintf(search, len, "svc:/%s", svc_name);
392	svc_node = topo_node_lookup(node, (const char *)search, 0);
393
394	if (svc_node == NULL) {
395		topo_mod_free(mod, search, len);
396		return (topo_mod_seterrno(mod, EMOD_NODE_NOENT));
397	}
398
399	if (svc_inst != NULL) {
400		(void) snprintf(search, len, "svc:/%s:%s", svc_name, svc_inst);
401		svc_node = topo_node_lookup(svc_node, (const char *)search, 0);
402		if (svc_node == NULL) {
403			topo_mod_free(mod, search, len);
404			return (topo_mod_seterrno(mod, EMOD_NODE_NOENT));
405		}
406	}
407
408	topo_mod_free(mod, search, len);
409
410	err = 0;
411	(void) topo_prop_getprop(svc_node, pgroup, pname, args, out, &err);
412
413	return (err);
414}
415
416/*ARGSUSED*/
417static int
418svc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
419    nvlist_t *nvl, nvlist_t **out)
420{
421	uint8_t scheme_version;
422	char *scope = NULL;
423	char *fqn = NULL;
424	char *contract = NULL;
425	char *instance = NULL;
426	char *service;
427	int err;
428	char *buf = NULL;
429	size_t buflen = 0;
430	ssize_t size = 0;
431	nvlist_t *fmristr;
432
433	if (version > TOPO_METH_NVL2STR_VERSION)
434		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
435
436	if (nvlist_lookup_uint8(nvl, FM_VERSION, &scheme_version) != 0 ||
437	    scheme_version > FM_SVC_SCHEME_VERSION)
438		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
439
440	/*
441	 * Check for optional members.
442	 */
443	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_INSTANCE, &instance);
444	if ((err != 0 && err != ENOENT) || !svc_component_valid(instance))
445		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
446	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_AUTH_SCOPE, &scope);
447	if ((err != 0 && err != ENOENT) || !svc_component_valid(scope))
448		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
449	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_AUTH_SYSTEM_FQN, &fqn);
450	if ((err != 0 && err != ENOENT) || !svc_component_valid(scope))
451		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
452	err = nvlist_lookup_string(nvl, FM_FMRI_SVC_CONTRACT_ID, &contract);
453	if ((err != 0 && err != ENOENT) || !svc_component_valid(contract))
454		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
455
456	/*
457	 * Get the service name.
458	 */
459	if (nvlist_lookup_string(nvl, FM_FMRI_SVC_NAME, &service) != 0)
460		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
461
462	/*
463	 * We make two passes through this code.  The first time through we
464	 * calculate the size of buffer that we'll need, and the second time
465	 * through we fill it in.
466	 */
467again:
468	/*
469	 * svc://[scope@][system-fqn]
470	 */
471	topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_SVC,
472	    NULL, "://");
473	topo_fmristr_build(&size, buf, buflen, scope, NULL, "@");
474	topo_fmristr_build(&size, buf, buflen, fqn, NULL, NULL);
475
476	/* svc path */
477	if (*service == '\0')
478		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
479
480	topo_fmristr_build(&size, buf, buflen, service, "/", NULL);
481
482	/* [:instance][@contract-id] */
483	topo_fmristr_build(&size, buf, buflen, instance, ":", NULL);
484	topo_fmristr_build(&size, buf, buflen, contract, "@", NULL);
485
486	if (buf == NULL) {
487		if ((buf = topo_mod_alloc(mod, size + 1)) == NULL)
488			return (topo_mod_seterrno(mod, EMOD_NOMEM));
489
490		buflen = size + 1;
491		size = 0;
492		goto again;
493	}
494
495	/*
496	 * Construct the nvlist to return as the result.
497	 */
498	if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) {
499		topo_mod_strfree(mod, buf);
500		return (topo_mod_seterrno(mod, EMOD_NOMEM));
501	}
502
503	if (nvlist_add_string(fmristr, "fmri-string", buf) != 0) {
504		topo_mod_strfree(mod, buf);
505		nvlist_free(fmristr);
506		return (topo_mod_seterrno(mod, EMOD_NOMEM));
507	}
508	topo_mod_strfree(mod, buf);
509	*out = fmristr;
510
511	return (0);
512}
513
514/*ARGSUSED*/
515static int
516svc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
517    nvlist_t *in, nvlist_t **out)
518{
519	nvlist_t *fmri;
520	char *str, *loc, val;
521
522	if (version > TOPO_METH_STR2NVL_VERSION)
523		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
524
525	if (nvlist_lookup_string(in, "fmri-string", &str) != 0)
526		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
527
528	if (strncmp(str, "svc://", 6) != 0)
529		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
530
531	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
532		return (topo_mod_seterrno(mod, EMOD_NOMEM));
533
534	str += 6;
535	if ((loc = strpbrk(str, "@/")) == NULL)
536		goto malformed;
537
538	if (*loc == '@') {
539		/* scope */
540		*loc = '\0';
541		if (!svc_component_valid(str)) {
542			*loc = '@';
543			goto malformed;
544		}
545
546		if (nvlist_add_string(fmri, FM_FMRI_SVC_AUTH_SCOPE, str) != 0) {
547			*loc = '@';
548			goto nomem;
549		}
550
551		*loc = '@';
552		str = loc + 1;
553		if ((loc = strchr(str, '/')) == NULL)
554			goto malformed;
555	}
556
557	if (loc != str) {
558		/* system-fqn */
559		*loc = '\0';
560		if (!svc_component_valid(str)) {
561			*loc = '/';
562			goto malformed;
563		}
564
565		if (nvlist_add_string(fmri, FM_FMRI_SVC_AUTH_SYSTEM_FQN,
566		    str) != 0) {
567			*loc = '/';
568			goto nomem;
569		}
570
571		*loc = '/';
572	}
573
574	str = loc + 1;
575	loc = strpbrk(str, ":@");
576
577	if (str[0] == '\0' || loc == str)
578		goto malformed;
579
580	if (loc != NULL) {
581		val = *loc;
582		*loc = '\0';
583	}
584
585	/* service name */
586	if (nvlist_add_string(fmri, FM_FMRI_SVC_NAME, str) != 0) {
587		if (loc != NULL)
588			*loc = val;
589		goto nomem;
590	}
591
592	if (loc != NULL)
593		*loc = val;
594
595	if (loc != NULL && *loc == ':') {
596		/* instance */
597		str = loc + 1;
598		if (str[0] == '\0' || str[0] == '@')
599			goto malformed;
600
601		loc = strchr(str, '@');
602		if (loc != NULL)
603			*loc = '\0';
604
605		if (nvlist_add_string(fmri, FM_FMRI_SVC_INSTANCE,
606		    str) != 0) {
607			if (loc != NULL)
608				*loc = '@';
609			goto nomem;
610		}
611
612		if (loc != NULL)
613			*loc = '@';
614	}
615
616	if (loc != NULL) {
617		/* contract-id */
618		assert(*loc == '@');
619		str = loc + 1;
620		if (str[0] == '\0')
621			goto malformed;
622
623		if (nvlist_add_string(fmri, FM_FMRI_SVC_CONTRACT_ID,
624		    str) != 0) {
625			goto nomem;
626		}
627	}
628
629	if (nvlist_add_uint8(fmri, FM_VERSION, FM_SVC_SCHEME_VERSION) != 0 ||
630	    nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SVC) != 0)
631		goto nomem;
632
633	*out = fmri;
634	return (0);
635
636malformed:
637	nvlist_free(fmri);
638	return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
639
640nomem:
641	nvlist_free(fmri);
642	return (topo_mod_seterrno(mod, EMOD_NOMEM));
643}
644
645/*
646 * This common function is shared by all consumers (present, replaced,
647 * service state and unusable).
648 *
649 *				svc_get_state succeeds
650 * Case				with FMD_SERVICE_STATE_*
651 * ----------------------------	------------------------
652 * svc name deleted		UNKNOWN
653 * svc name not found		UNKNOWN
654 * no fmri instance		OK
655 * instance deleted		UNKNOWN
656 * instance not found		UNKNOWN
657 *
658 * If none of the above apply and this is a call from the "present"
659 * or "replaced" method (presence_only == B_TRUE) then
660 * svc_get_state returns FMD_SERVICE_STATE_OK.
661 *
662 * The "present" method maps a svc_get_state return of UNKNOWN to
663 * "not present" and a svc_get_state return of OK to "present".
664 *
665 * The "replaced" methods maps a return of UNKNOWN to FMD_OBJ_STATE_NOT_PRESENT
666 * and OK to FMD_OBJ_STATE_UNKNOWN.
667 *
668 * For the "service state" and "unusable" methods svc_get_state goes on
669 * to return the instance state as below, and the two methods map that
670 * result as in the last two columns of the following table:
671 *
672 *			svc_get_state succeeds		Service
673 * Instance state	with FMD_SERVICE_STATE_*	State		Unusable
674 * --------------	-------------------------------	---------------	--------
675 * none			OK				OK
676 * uninitialized	OK				OK
677 * maintenance		UNUSABLE			UNUSABLE	Yes
678 * offline		OK				OK
679 * disabled		OK				OK
680 * online		OK				OK
681 * degraded		DEGRADED			DEGRADED
682 * legacy_run		OK (XXX can we see this?)	OK
683 *
684 * Note that *only* "maintenance" state should map to an unusable service state
685 * or unusable status.  That's because a service entering maintenance state
686 * is modelled as a defect fault diagnosis in FMA, but there is no
687 * corresponding isolation action from a response agent since the the service
688 * is already isolated by virtue of being in maintenance state.  Any transition
689 * from maintenance state, even to offline, is considered a repair.  If on
690 * repair fmd does not see the service usable again then the case hangs
691 * around in the "resolved but not all resources back online" state and
692 * further maintenance events for this service will not show up in fmd state
693 * because case duplicate checking code will find the old case.
694 */
695
696static int
697svc_get_state(topo_mod_t *mod, nvlist_t *fmri, boolean_t presence_only,
698    int *ret)
699{
700	scf_handle_t *hdl;
701	uint8_t fmversion;
702	char *instance, *name;
703	scf_service_t *svc = NULL;
704	scf_scope_t *scope = NULL;
705	scf_instance_t *inst = NULL;
706	scf_property_t *prop = NULL;
707	scf_iter_t *iter = NULL;
708	scf_value_t *val = NULL;
709	scf_propertygroup_t *pg = NULL;
710	int err, retval = 0;
711	ssize_t len;
712	char *state;
713
714	if ((hdl = svc_get_handle(mod)) == NULL)
715		return (-1);
716
717	if (nvlist_lookup_uint8(fmri, FM_VERSION, &fmversion) != 0 ||
718	    fmversion > FM_SVC_SCHEME_VERSION ||
719	    nvlist_lookup_string(fmri, FM_FMRI_SVC_NAME, &name) != 0)
720		return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
721
722	if ((svc = scf_service_create(hdl)) == NULL ||
723	    (inst = scf_instance_create(hdl)) == NULL ||
724	    (scope = scf_scope_create(hdl)) == NULL ||
725	    (prop = scf_property_create(hdl)) == NULL ||
726	    (iter = scf_iter_create(hdl)) == NULL ||
727	    (pg = scf_pg_create(hdl)) == NULL ||
728	    (val = scf_value_create(hdl)) == NULL)
729		goto error;
730
731	if (scf_handle_get_scope(hdl, SCF_SCOPE_LOCAL, scope) != 0)
732		goto error;
733
734	/*
735	 * If we fail to get the service due to _DELETED or _NOT_FOUND, then we
736	 * treat this as not present.
737	 */
738	if (scf_scope_get_service(scope, name, svc) != 0) {
739		err = scf_error();
740		if (err == SCF_ERROR_NOT_FOUND || err == SCF_ERROR_DELETED) {
741			*ret = FMD_SERVICE_STATE_UNKNOWN;
742			goto out;
743		} else {
744			goto error;
745		}
746	}
747
748	if (nvlist_lookup_string(fmri, FM_FMRI_SVC_INSTANCE, &instance) != 0) {
749		*ret = FMD_SERVICE_STATE_OK;
750		goto out;
751	}
752
753	/*
754	 * Again, check for _DELETED or _NOT_FOUND.
755	 */
756	if (scf_service_get_instance(svc, instance, inst) != 0) {
757		err = scf_error();
758		if (err == SCF_ERROR_NOT_FOUND || err == SCF_ERROR_DELETED) {
759			*ret = FMD_SERVICE_STATE_UNKNOWN;
760			goto out;
761		} else {
762			goto error;
763		}
764	}
765
766	/*
767	 * For presence, we are done.  Otherwise, we need to get the current
768	 * state of the instance.
769	 */
770	if (presence_only) {
771		*ret = FMD_SERVICE_STATE_OK;
772		goto out;
773	}
774
775	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != 0 ||
776	    scf_pg_get_property(pg, SCF_PROPERTY_STATE, prop) != 0 ||
777	    scf_iter_property_values(iter, prop) != 0 ||
778	    scf_iter_next_value(iter, val) != 1)
779		goto error;
780
781	if ((len = scf_value_get_astring(val, NULL, 0)) < 0)
782		goto error;
783
784	state = alloca(len + 1);
785	if (scf_value_get_astring(val, state, len + 1) < 0)
786		goto error;
787
788	if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
789		*ret = FMD_SERVICE_STATE_UNUSABLE;
790	} else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
791		*ret = FMD_SERVICE_STATE_DEGRADED;
792	} else {
793		*ret = FMD_SERVICE_STATE_OK;
794	}
795	goto out;
796
797error:
798	retval = -1;
799out:
800	scf_value_destroy(val);
801	scf_pg_destroy(pg);
802	scf_iter_destroy(iter);
803	scf_property_destroy(prop);
804	scf_instance_destroy(inst);
805	scf_scope_destroy(scope);
806	scf_service_destroy(svc);
807	return (retval);
808}
809
810/*ARGSUSED*/
811static int
812svc_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
813    nvlist_t *in, nvlist_t **out)
814{
815	int state;
816
817	if (version > TOPO_METH_PRESENT_VERSION)
818		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
819
820	if (svc_get_state(mod, in, B_TRUE, &state) != 0)
821		return (-1);
822
823	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
824		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
825	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET,
826	    state != FMD_SERVICE_STATE_UNKNOWN) != 0) {
827		nvlist_free(*out);
828		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
829	}
830
831	return (0);
832}
833
834/*ARGSUSED*/
835static int
836svc_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
837    nvlist_t *in, nvlist_t **out)
838{
839	int state;
840
841	if (version > TOPO_METH_REPLACED_VERSION)
842		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
843
844	if (svc_get_state(mod, in, B_TRUE, &state) != 0)
845		return (-1);
846
847	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
848		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
849	if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET,
850	    state == FMD_SERVICE_STATE_UNKNOWN ?
851	    FMD_OBJ_STATE_NOT_PRESENT : FMD_OBJ_STATE_UNKNOWN) != 0) {
852		nvlist_free(*out);
853		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
854	}
855
856	return (0);
857}
858
859/*ARGSUSED*/
860static int
861svc_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
862    nvlist_t *in, nvlist_t **out)
863{
864	int state;
865
866	if (version > TOPO_METH_SERVICE_STATE_VERSION)
867		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
868
869	if (svc_get_state(mod, in, B_FALSE, &state) != 0)
870		return (-1);
871
872	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
873		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
874	if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET,
875	    state) != 0) {
876		nvlist_free(*out);
877		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
878	}
879
880	return (0);
881}
882
883/*ARGSUSED*/
884static int
885svc_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
886    nvlist_t *in, nvlist_t **out)
887{
888	int state;
889
890	if (version > TOPO_METH_UNUSABLE_VERSION)
891		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
892
893	if (svc_get_state(mod, in, B_FALSE, &state) != 0)
894		return (-1);
895
896	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
897		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
898	if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET,
899	    (state == FMD_SERVICE_STATE_UNKNOWN ||
900	    state == FMD_SERVICE_STATE_UNUSABLE)) != 0) {
901		nvlist_free(*out);
902		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
903	}
904
905	return (0);
906}
907