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
58 static int svc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
59 nvlist_t *, nvlist_t **);
60 static int svc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
61 nvlist_t *, nvlist_t **);
62 static int svc_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
63 nvlist_t *, nvlist_t **);
64 static int svc_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t,
65 nvlist_t *, nvlist_t **);
66 static int svc_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t,
67 nvlist_t *, nvlist_t **);
68 static int svc_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
69 nvlist_t *, nvlist_t **);
70 static int svc_fmri_prop_get(topo_mod_t *, tnode_t *, topo_version_t,
71 nvlist_t *, nvlist_t **);
72
73 static 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
95 static int svc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
96 topo_instance_t, void *, void *);
97 static void svc_release(topo_mod_t *, tnode_t *);
98
99 static const topo_modops_t svc_ops =
100 { svc_enum, svc_release };
101 static const topo_modinfo_t svc_info =
102 { "svc", FM_FMRI_SCHEME_SVC, SVC_VERSION, &svc_ops };
103
104 static int
svc_error(topo_mod_t * mod)105 svc_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
116 static scf_handle_t *
svc_get_handle(topo_mod_t * mod)117 svc_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
140 int
svc_init(topo_mod_t * mod,topo_version_t version)141 svc_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
158 void
svc_fini(topo_mod_t * mod)159 svc_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
169 static tnode_t *
svc_create_node(topo_mod_t * mod,tnode_t * pnode,char * fmristr)170 svc_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*/
237 static int
svc_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * notused1,void * notused2)238 svc_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;
315 out:
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
325 static void
svc_release(topo_mod_t * mod,tnode_t * node)326 svc_release(topo_mod_t *mod, tnode_t *node)
327 {
328 topo_method_unregister_all(mod, node);
329 }
330
331 static boolean_t
svc_component_valid(const char * str)332 svc_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
346 static int
svc_fmri_prop_get(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)347 svc_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*/
417 static int
svc_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)418 svc_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 */
467 again:
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*/
515 static int
svc_fmri_str2nvl(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)516 svc_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
636 malformed:
637 nvlist_free(fmri);
638 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
639
640 nomem:
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
696 static int
svc_get_state(topo_mod_t * mod,nvlist_t * fmri,boolean_t presence_only,int * ret)697 svc_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
797 error:
798 retval = -1;
799 out:
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*/
811 static int
svc_fmri_present(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)812 svc_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*/
835 static int
svc_fmri_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)836 svc_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*/
860 static int
svc_fmri_service_state(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)861 svc_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*/
884 static int
svc_fmri_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)885 svc_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