195efa35Eric Schrock/*
295efa35Eric Schrock * CDDL HEADER START
395efa35Eric Schrock *
495efa35Eric Schrock * The contents of this file are subject to the terms of the
595efa35Eric Schrock * Common Development and Distribution License (the "License").
695efa35Eric Schrock * You may not use this file except in compliance with the License.
795efa35Eric Schrock *
895efa35Eric Schrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
995efa35Eric Schrock * or http://www.opensolaris.org/os/licensing.
1095efa35Eric Schrock * See the License for the specific language governing permissions
1195efa35Eric Schrock * and limitations under the License.
1295efa35Eric Schrock *
1395efa35Eric Schrock * When distributing Covered Code, include this CDDL HEADER in each
1495efa35Eric Schrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1595efa35Eric Schrock * If applicable, add the following below this CDDL HEADER, with the
1695efa35Eric Schrock * fields enclosed by brackets "[]" replaced with your own identifying
1795efa35Eric Schrock * information: Portions Copyright [yyyy] [name of copyright owner]
1895efa35Eric Schrock *
1995efa35Eric Schrock * CDDL HEADER END
2095efa35Eric Schrock */
2195efa35Eric Schrock
2295efa35Eric Schrock/*
23f6e214cGavin Maltby * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2495efa35Eric Schrock */
2595efa35Eric Schrock
2695efa35Eric Schrock/*
2795efa35Eric Schrock * This provides the basic mechanisms (str2nvl and nvl2str) for dealing with
2895efa35Eric Schrock * the service schema.  The official version of a svc FMRI has the form:
2995efa35Eric Schrock *
30f6e214cGavin Maltby *	svc://[scope@][system-fqn]/service[:instance][@contract-id]
3195efa35Eric Schrock *
3295efa35Eric Schrock * Where 'service' is a slash-delimited list of names.  Of these fields, the
3395efa35Eric Schrock * scope, constract-id, and system-fqn are rarely used, leaving the much more
3495efa35Eric Schrock * common form such as:
3595efa35Eric Schrock *
36f6e214cGavin Maltby *	svc:///network/ssh:default
3795efa35Eric Schrock *
3895efa35Eric Schrock * Note that the SMF software typically uses a shorthard form, where the
3995efa35Eric Schrock * authority is elided (svc:/network/ssh:default).  As this module deals with
4095efa35Eric Schrock * FMA FMRIs, we only support fully specified FMRIs.
4195efa35Eric Schrock *
4295efa35Eric Schrock * This module does not support enumeration, but implements methods for FMRI
4395efa35Eric Schrock * state (present, unusable, service state, and replaced).
4495efa35Eric Schrock */
4595efa35Eric Schrock
4695efa35Eric Schrock#include <fm/topo_mod.h>
4795efa35Eric Schrock#include <fm/fmd_fmri.h>
4895efa35Eric Schrock#include <sys/fm/protocol.h>
4995efa35Eric Schrock#include <topo_method.h>
5095efa35Eric Schrock#include <topo_subr.h>
51f6e214cGavin Maltby#include <topo_prop.h>
5295efa35Eric Schrock#include <alloca.h>
5395efa35Eric Schrock#include <assert.h>
5495efa35Eric Schrock#include <svc.h>
5595efa35Eric Schrock#include <strings.h>
5695efa35Eric Schrock#include <libscf.h>
5795efa35Eric Schrock
5895efa35Eric Schrockstatic int svc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
5995efa35Eric Schrock    nvlist_t *, nvlist_t **);
6095efa35Eric Schrockstatic int svc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
6195efa35Eric Schrock    nvlist_t *, nvlist_t **);
6295efa35Eric Schrockstatic int svc_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
6395efa35Eric Schrock    nvlist_t *, nvlist_t **);
6495efa35Eric Schrockstatic int svc_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
6595efa35Eric Schrock    nvlist_t *, nvlist_t **);
6695efa35Eric Schrockstatic int svc_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
6795efa35Eric Schrock    nvlist_t *, nvlist_t **);
6895efa35Eric Schrockstatic int svc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
6995efa35Eric Schrock    nvlist_t *, nvlist_t **);
70f6e214cGavin Maltbystatic int svc_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t,
71f6e214cGavin Maltby    nvlist_t *, nvlist_t **);
7295efa35Eric Schrock
7395efa35Eric Schrockstatic const topo_method_t svc_methods[] = {
76f6e214cGavin Maltby	    svc_fmri_prop_get },
7895efa35Eric Schrock	    TOPO_STABILITY_INTERNAL, svc_fmri_nvl2str },
8095efa35Eric Schrock	    TOPO_STABILITY_INTERNAL, svc_fmri_str2nvl },
8295efa35Eric Schrock	    TOPO_STABILITY_INTERNAL, svc_fmri_present },
8595efa35Eric Schrock	    svc_fmri_replaced },
8895efa35Eric Schrock	    svc_fmri_service_state },
9195efa35Eric Schrock	    svc_fmri_unusable },
9295efa35Eric Schrock	{ NULL }
9395efa35Eric Schrock};
9495efa35Eric Schrock
95f6e214cGavin Maltbystatic int svc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
96f6e214cGavin Maltby    topo_instance_t, void *, void *);
97f6e214cGavin Maltbystatic void svc_release(topo_mod_t *, tnode_t *);
98f6e214cGavin Maltby
9995efa35Eric Schrockstatic const topo_modops_t svc_ops =
10095efa35Eric Schrock	{ svc_enum, svc_release };
10195efa35Eric Schrockstatic const topo_modinfo_t svc_info =
10295efa35Eric Schrock	{ "svc", FM_FMRI_SCHEME_SVC, SVC_VERSION, &svc_ops };
10395efa35Eric Schrock
10495efa35Eric Schrockstatic int
10595efa35Eric Schrocksvc_error(topo_mod_t *mod)
10695efa35Eric Schrock{
10795efa35Eric Schrock	switch (scf_error()) {
10895efa35Eric Schrock	case SCF_ERROR_NO_MEMORY:
10995efa35Eric Schrock		return (topo_mod_seterrno(mod, EMOD_NOMEM));
11095efa35Eric Schrock
11195efa35Eric Schrock	default:
11295efa35Eric Schrock		return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
11395efa35Eric Schrock	}
11495efa35Eric Schrock}
11595efa35Eric Schrock
1169f342a5Eric Schrockstatic scf_handle_t *
1179f342a5Eric Schrocksvc_get_handle(topo_mod_t *mod)
11895efa35Eric Schrock{
1199f342a5Eric Schrock	scf_handle_t *hdl = topo_mod_getspecific(mod);
12095efa35Eric Schrock
1219f342a5Eric Schrock	if (hdl != NULL)
1229f342a5Eric Schrock		return (hdl);
12395efa35Eric Schrock
1249f342a5Eric Schrock	if ((hdl = scf_handle_create(SCF_VERSION)) == NULL) {
1259f342a5Eric Schrock		(void) svc_error(mod);
1269f342a5Eric Schrock		return (NULL);
1279f342a5Eric Schrock	}
12895efa35Eric Schrock
12995efa35Eric Schrock	if (scf_handle_bind(hdl) != 0) {
13095efa35Eric Schrock		scf_handle_destroy(hdl);
1319f342a5Eric Schrock		(void) svc_error(mod);
1329f342a5Eric Schrock		return (NULL);
13395efa35Eric Schrock	}
13495efa35Eric Schrock
1359f342a5Eric Schrock	topo_mod_setspecific(mod, hdl);
1369f342a5Eric Schrock
1379f342a5Eric Schrock	return (hdl);
1389f342a5Eric Schrock}
1399f342a5Eric Schrock
1409f342a5Eric Schrockint
1419f342a5Eric Schrocksvc_init(topo_mod_t *mod, topo_version_t version)
1429f342a5Eric Schrock{
143f6e214cGavin Maltby	if (getenv("TOPOSVCDEBUG"))
144f6e214cGavin Maltby		topo_mod_setdebug(mod);
145f6e214cGavin Maltby
1469f342a5Eric Schrock	if (version != SVC_VERSION)
1479f342a5Eric Schrock		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
1489f342a5Eric Schrock
14995efa35Eric Schrock	if (topo_mod_register(mod, &svc_info, TOPO_VERSION) != 0) {
15095efa35Eric Schrock		topo_mod_dprintf(mod, "failed to register svc_info: "
15195efa35Eric Schrock		    "%s\n", topo_mod_errmsg(mod));
15295efa35Eric Schrock		return (-1);
15395efa35Eric Schrock	}
15495efa35Eric Schrock
15595efa35Eric Schrock	return (0);
15695efa35Eric Schrock}
15795efa35Eric Schrock
15895efa35Eric Schrockvoid
15995efa35Eric Schrocksvc_fini(topo_mod_t *mod)
16095efa35Eric Schrock{
16195efa35Eric Schrock	scf_handle_t *hdl = topo_mod_getspecific(mod);
16295efa35Eric Schrock
1639f342a5Eric Schrock	if (hdl != NULL)
1649f342a5Eric Schrock		scf_handle_destroy(hdl);
16595efa35Eric Schrock
16695efa35Eric Schrock	topo_mod_unregister(mod);
16795efa35Eric Schrock}
16895efa35Eric Schrock
169f6e214cGavin Maltbystatic tnode_t *
170f6e214cGavin Maltbysvc_create_node(topo_mod_t *mod, tnode_t *pnode, char *fmristr)
171f6e214cGavin Maltby{
172f6e214cGavin Maltby	nvlist_t *fmri;
173f6e214cGavin Maltby	tnode_t *tn;
174f6e214cGavin Maltby	char *fixed;
175f6e214cGavin Maltby	ssize_t len;
176f6e214cGavin Maltby	int i, j, err;
177f6e214cGavin Maltby
178f6e214cGavin Maltby	/*
179f6e214cGavin Maltby	 * the scf_{x}_to_fmri interfaces return short-hand svc-scheme FMRI's
180f6e214cGavin Maltby	 * that look like:
181f6e214cGavin Maltby	 *
182f6e214cGavin Maltby	 * svc:/service[:instance]
183f6e214cGavin Maltby	 *
184f6e214cGavin Maltby	 * But all our other code assumes a proper svc-scheme FMRI, so we
185f6e214cGavin Maltby	 * correct the fmri string before we try to convert it to an nvlist.
186f6e214cGavin Maltby	 *
187f6e214cGavin Maltby	 * The short-hand version is kept as the label and can be used when
188f6e214cGavin Maltby	 * dealing with the SMF libraries and CLI's.
189f6e214cGavin Maltby	 */
190f6e214cGavin Maltby	len = strlen(fmristr) + 1;
191f6e214cGavin Maltby	if ((fixed = topo_mod_zalloc(mod, len + 1)) == NULL) {
192f6e214cGavin Maltby		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
193f6e214cGavin Maltby		topo_mod_dprintf(mod, "topo_mod_zalloc() failed: %s",
194f6e214cGavin Maltby		    topo_mod_errmsg(mod));
195f6e214cGavin Maltby		return (NULL);
196f6e214cGavin Maltby	}
197f6e214cGavin Maltby	for (i = 0, j = 0; i < len; i++)
198f6e214cGavin Maltby		if (i == 5)
199f6e214cGavin Maltby			fixed[i] = '/';
200f6e214cGavin Maltby		else
201f6e214cGavin Maltby			fixed[i] = fmristr[j++];
202f6e214cGavin Maltby	fixed[i] = '\0';
203f6e214cGavin Maltby
204f6e214cGavin Maltby	if (topo_mod_str2nvl(mod, fixed, &fmri) < 0) {
205f6e214cGavin Maltby		topo_mod_dprintf(mod, "topo_mod_str2nvl() failed: %s",
206f6e214cGavin Maltby		    topo_mod_errmsg(mod));
207f6e214cGavin Maltby		topo_mod_free(mod, fixed, len + 1);
208f6e214cGavin Maltby		return (NULL);
209f6e214cGavin Maltby	}
210f6e214cGavin Maltby	topo_mod_free(mod, fixed, len + 1);
211f6e214cGavin Maltby
212f6e214cGavin Maltby	if (topo_node_range_create(mod, pnode, fmristr, 0, 0) < 0) {
213f6e214cGavin Maltby		topo_mod_dprintf(mod, "topo_node_range_create() failed: %s",
214f6e214cGavin Maltby		    topo_mod_errmsg(mod));
215f6e214cGavin Maltby		nvlist_free(fmri);
216f6e214cGavin Maltby		return (NULL);
217f6e214cGavin Maltby	}
218f6e214cGavin Maltby	if ((tn = topo_node_bind(mod, pnode, fmristr, 0, fmri)) == NULL) {
219f6e214cGavin Maltby		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
220f6e214cGavin Maltby		    topo_mod_errmsg(mod));
221f6e214cGavin Maltby		nvlist_free(fmri);
222f6e214cGavin Maltby		return (NULL);
223f6e214cGavin Maltby	}
224f6e214cGavin Maltby	nvlist_free(fmri);
225f6e214cGavin Maltby
226f6e214cGavin Maltby	if (topo_node_label_set(tn, fmristr, &err) != 0) {
227f6e214cGavin Maltby		topo_mod_dprintf(mod, "failed to set label: %s\n",
228f6e214cGavin Maltby		    topo_strerror(err));
229f6e214cGavin Maltby		return (NULL);
230f6e214cGavin Maltby	}
231f6e214cGavin Maltby	(void) topo_method_register(mod, tn, svc_methods);
232f6e214cGavin Maltby
233f6e214cGavin Maltby	return (tn);
234f6e214cGavin Maltby}
235f6e214cGavin Maltby
23695efa35Eric Schrock/*ARGSUSED*/
23795efa35Eric Schrockstatic int
23895efa35Eric Schrocksvc_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
23995efa35Eric Schrock    topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
24095efa35Eric Schrock{
241f6e214cGavin Maltby	scf_handle_t *hdl;
242f6e214cGavin Maltby	scf_scope_t *sc = NULL;
243f6e214cGavin Maltby	scf_iter_t *svc_iter = NULL;
244f6e214cGavin Maltby	scf_iter_t *inst_iter = NULL;
245f6e214cGavin Maltby	scf_service_t *svc = NULL;
246f6e214cGavin Maltby	scf_instance_t *inst = NULL;
247f6e214cGavin Maltby	int ret = -1;
248f6e214cGavin Maltby	char *sfmri, *ifmri;
249f6e214cGavin Maltby	ssize_t slen, ilen;
250f6e214cGavin Maltby	tnode_t *svc_node;
251f6e214cGavin Maltby
25295efa35Eric Schrock	(void) topo_method_register(mod, pnode, svc_methods);
253f6e214cGavin Maltby
254f6e214cGavin Maltby	if ((hdl = svc_get_handle(mod)) == NULL)
255f6e214cGavin Maltby		goto out;
256f6e214cGavin Maltby
257f6e214cGavin Maltby	if ((sc = scf_scope_create(hdl)) == NULL ||
258f6e214cGavin Maltby	    (svc = scf_service_create(hdl)) == NULL ||
259f6e214cGavin Maltby	    (inst = scf_instance_create(hdl)) == NULL ||
260f6e214cGavin Maltby	    (svc_iter = scf_iter_create(hdl)) == NULL ||
261f6e214cGavin Maltby	    (inst_iter = scf_iter_create(hdl)) == NULL)
262f6e214cGavin Maltby		goto out;
263f6e214cGavin Maltby
264f6e214cGavin Maltby	if (scf_handle_get_scope(hdl, SCF_SCOPE_LOCAL, sc) != 0)
265f6e214cGavin Maltby		goto out;
266f6e214cGavin Maltby
267f6e214cGavin Maltby	if (scf_iter_scope_services(svc_iter, sc) != 0)
268f6e214cGavin Maltby		goto out;
269f6e214cGavin Maltby
270f6e214cGavin Maltby	while (scf_iter_next_service(svc_iter, svc) == 1) {
271f6e214cGavin Maltby		if (scf_iter_service_instances(inst_iter, svc) != 0)
272f6e214cGavin Maltby			continue;
273f6e214cGavin Maltby
274f6e214cGavin Maltby		if ((slen = scf_service_to_fmri(svc, NULL, 0)) < 0)
275f6e214cGavin Maltby			continue;
276f6e214cGavin Maltby
277f6e214cGavin Maltby		if ((sfmri = topo_mod_zalloc(mod, slen + 1)) == NULL) {
278f6e214cGavin Maltby			(void) topo_mod_seterrno(mod, EMOD_NOMEM);
279f6e214cGavin Maltby			goto out;
280f6e214cGavin Maltby		}
281f6e214cGavin Maltby		if (scf_service_to_fmri(svc, sfmri, slen + 1) == -1)
282f6e214cGavin Maltby			goto out;
283f6e214cGavin Maltby
284f6e214cGavin Maltby		if ((svc_node = svc_create_node(mod, pnode, sfmri)) == NULL) {
285f6e214cGavin Maltby			topo_mod_free(mod, sfmri, slen + 1);
286f6e214cGavin Maltby			/* topo mod errno set */
287f6e214cGavin Maltby			goto out;
288f6e214cGavin Maltby		}
289f6e214cGavin Maltby
290f6e214cGavin Maltby		while (scf_iter_next_instance(inst_iter, inst) == 1) {
291f6e214cGavin Maltby			if ((ilen = scf_instance_to_fmri(inst, NULL, 0)) < 0)
292f6e214cGavin Maltby				continue;
293f6e214cGavin Maltby
294f6e214cGavin Maltby			if ((ifmri = topo_mod_zalloc(mod, ilen + 1))
295f6e214cGavin Maltby			    == NULL) {
296f6e214cGavin Maltby				(void) topo_mod_seterrno(mod, EMOD_NOMEM);
297f6e214cGavin Maltby				topo_mod_free(mod, sfmri, slen + 1);
298f6e214cGavin Maltby				goto out;
299f6e214cGavin Maltby			}
300f6e214cGavin Maltby			if (scf_instance_to_fmri(inst, ifmri, ilen + 1) == -1)
301f6e214cGavin Maltby				goto out;
302f6e214cGavin Maltby
303f6e214cGavin Maltby			if ((svc_node = svc_create_node(mod, svc_node, ifmri))
304f6e214cGavin Maltby			    == NULL) {
305f6e214cGavin Maltby				topo_mod_free(mod, sfmri, slen + 1);
306f6e214cGavin Maltby				topo_mod_free(mod, ifmri, ilen + 1);
307f6e214cGavin Maltby				/* topo mod errno set */
308f6e214cGavin Maltby				goto out;
309f6e214cGavin Maltby			}
310f6e214cGavin Maltby			topo_mod_free(mod, ifmri, ilen + 1);
311f6e214cGavin Maltby		}
312f6e214cGavin Maltby		topo_mod_free(mod, sfmri, slen + 1);
313f6e214cGavin Maltby	}
314f6e214cGavin Maltby	ret = 0;
315f6e214cGavin Maltbyout:
316f6e214cGavin Maltby	scf_scope_destroy(sc);
317f6e214cGavin Maltby	scf_service_destroy(svc);
318f6e214cGavin Maltby	scf_instance_destroy(inst);
319f6e214cGavin Maltby	scf_iter_destroy(svc_iter);
320f6e214cGavin Maltby	scf_iter_destroy(inst_iter);
321f6e214cGavin Maltby
322f6e214cGavin Maltby	return (ret);
32395efa35Eric Schrock}
32495efa35Eric Schrock
32595efa35Eric Schrockstatic void
32695efa35Eric Schrocksvc_release(topo_mod_t *mod, tnode_t *node)
32795efa35Eric Schrock{
32895efa35Eric Schrock	topo_method_unregister_all(mod, node);
32995efa35Eric Schrock}
33095efa35Eric Schrock
33195efa35Eric Schrockstatic boolean_t
33295efa35Eric Schrocksvc_component_valid(const char *str)
33395efa35Eric Schrock{
33495efa35Eric Schrock	if (str == NULL)
33595efa35Eric Schrock		return (B_TRUE);
33695efa35Eric Schrock
33795efa35Eric Schrock	if (*str == '\0')
33895efa35Eric Schrock		return (B_FALSE);
33995efa35Eric Schrock
34095efa35Eric Schrock	if (strpbrk(str, "@/:") != NULL)
34195efa35Eric Schrock		return (B_FALSE);
34295efa35Eric Schrock
34395efa35Eric Schrock	return (B_TRUE);
34495efa35Eric Schrock}
34595efa35Eric Schrock
346f6e214cGavin Maltbystatic int
347f6e214cGavin Maltbysvc_fmri_prop_get(topo_mod_t *mod, tnode_t *node, topo_version_t version,
348f6e214cGavin Maltby    nvlist_t *in, nvlist_t **out)
349f6e214cGavin Maltby{
350f6e214cGavin Maltby	char *svc_name, *svc_inst = NULL;
351f6e214cGavin Maltby	nvlist_t *rsrc, *args;
352f6e214cGavin Maltby	char *pgroup, *pname;
353f6e214cGavin Maltby	tnode_t *svc_node;
354f6e214cGavin Maltby	char *search;
355f6e214cGavin Maltby	size_t len;
356f6e214cGavin Maltby	int err;
357f6e214cGavin Maltby
358f6e214cGavin Maltby	if (version > TOPO_METH_PROP_GET_VERSION)
359f6e214cGavin Maltby		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
360f6e214cGavin Maltby
361f6e214cGavin Maltby	err = nvlist_lookup_string(in, TOPO_PROP_GROUP, &pgroup);
362f6e214cGavin Maltby	err |= nvlist_lookup_string(in, TOPO_PROP_VAL_NAME, &pname);
363f6e214cGavin Maltby	err |= nvlist_lookup_nvlist(in, TOPO_PROP_RESOURCE, &rsrc);
364f6e214cGavin Maltby	if (err != 0)
365f6e214cGavin Maltby		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
366f6e214cGavin Maltby
367f6e214cGavin Maltby	/*
368f6e214cGavin Maltby	 * Private args to prop method are optional
369f6e214cGavin Maltby	 */
370f6e214cGavin Maltby	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &args)) != 0) {
371f6e214cGavin Maltby		if (err != ENOENT)
372f6e214cGavin Maltby			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
373f6e214cGavin Maltby		else
374f6e214cGavin Maltby			args = NULL;