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
57 static void *svcctl_scm_interposer_hdl = NULL;
58 static 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 */
69 static int
svcctl_scm_avl_nodecmp(const void * l_arg,const void * r_arg,void * m_name_len)70 svcctl_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 */
97 static int
svcctl_scm_pg_get_val(svcctl_manager_context_t * mgr_ctx,scf_propertygroup_t * pg,const char * propname,scf_type_t ty,void * vp,size_t sz,uint_t flags)98 svcctl_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 */
181 static scf_snapshot_t *
svcctl_scm_get_running_snapshot(svcctl_manager_context_t * mgr_ctx,scf_instance_t * inst)182 svcctl_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 */
210 static int
svcctl_scm_inst_get_val(svcctl_manager_context_t * mgr_ctx,scf_instance_t * inst,const char * pgname,const char * propname,scf_type_t ty,void * vp,size_t sz,uint_t flags,int use_running,int composed)211 svcctl_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 */
242 static void
svcctl_scm_get_restarter_string_prop(svcctl_manager_context_t * mgr_ctx,scf_instance_t * inst,const char * pname,char * buf,size_t buf_sz)243 svcctl_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 */
256 static int
svcctl_scm_svc_transitioning(svcctl_manager_context_t * mgr_ctx,scf_instance_t * inst)257 svcctl_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 */
274 static int
svcctl_scm_get_svcstate(svcctl_manager_context_t * mgr_ctx,char ** buf,scf_walkinfo_t * wip)275 svcctl_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 */
313 static int
svcctl_scm_get_svcdesc(svcctl_manager_context_t * mgr_ctx,char ** buf,scf_walkinfo_t * wip)314 svcctl_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 */
358 static int
svcctl_scm_get_svcfmri(svcctl_manager_context_t * mgr_ctx,char ** buf,scf_walkinfo_t * wip)359 svcctl_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 */
410 static int
svcctl_scm_get_svcname(char ** buf,char * fmri)411 svcctl_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 */
437 static int
svcctl_scm_cb_list_svcinst(void * context,scf_walkinfo_t * wip)438 svcctl_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 */
513 uint32_t
svcctl_scm_map_status(const char * state)514 svcctl_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 */
549 uint32_t
svcctl_scm_enum_services(svcctl_manager_context_t * mgr_ctx,uint8_t * buf,size_t buflen,uint32_t * resume_handle,boolean_t use_wchar)550 svcctl_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 */
652 static int
svcctl_scm_cb_bytes_needed(void * svc_node,void * byte_cnt)653 svcctl_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 */
669 static void
svcctl_scm_bytes_needed(svcctl_manager_context_t * mgr_ctx)670 svcctl_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 */
693 uint32_t
svcctl_scm_validate_service(svcctl_manager_context_t * mgr_ctx,char * svc_name)694 svcctl_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 */
708 static char *
svcctl_scm_map_windows_svc(char * svc_name)709 svcctl_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 */
736 svcctl_svc_node_t *
svcctl_scm_find_service(svcctl_manager_context_t * mgr_ctx,char * svc_name)737 svcctl_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 */
767 int
svcctl_scm_refresh(svcctl_manager_context_t * mgr_ctx)768 svcctl_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 */
783 int
svcctl_scm_scf_handle_init(svcctl_manager_context_t * mgr_ctx)784 svcctl_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 */
822 void
svcctl_scm_scf_handle_fini(svcctl_manager_context_t * mgr_ctx)823 svcctl_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 */
842 int
svcctl_scm_init(svcctl_manager_context_t * mgr_ctx)843 svcctl_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 */
886 void
svcctl_scm_fini(svcctl_manager_context_t * mgr_ctx)887 svcctl_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 */
921 void
svcctl_init(void)922 svcctl_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 */
950 void
svcctl_fini(void)951 svcctl_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