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.
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.
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]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
27 * util.c contains a set of miscellaneous utility functions which,
29 * - start a child process
30 * - look up the zone name
31 * - look up/set SMF properties
32 * - drop/escalate privs
37 #include <libdllink.h>
51 #include <sys/socket.h>
52 #include <sys/sockio.h>
53 #include <sys/types.h>
61 extern char **environ
;
62 extern sigset_t original_sigmask
;
65 * A holder for all the resources needed to get a property value
68 typedef struct scf_resources
{
69 scf_handle_t
*sr_handle
;
70 scf_instance_t
*sr_inst
;
71 scf_snapshot_t
*sr_snap
;
72 scf_propertygroup_t
*sr_pg
;
73 scf_property_t
*sr_prop
;
75 scf_transaction_t
*sr_tx
;
76 scf_transaction_entry_t
*sr_ent
;
79 static pthread_mutex_t uid_mutex
= PTHREAD_MUTEX_INITIALIZER
;
84 nwamd_escalate(void) {
86 priv_set
= priv_str_to_set("zone", ",", NULL
);
89 pfail("creating privilege set: %s", strerror(errno
));
91 (void) pthread_mutex_lock(&uid_mutex
);
95 if (setppriv(PRIV_SET
, PRIV_EFFECTIVE
, priv_set
) == -1) {
96 priv_freeset(priv_set
);
97 pfail("setppriv effective: %s", strerror(errno
));
100 (void) pthread_mutex_unlock(&uid_mutex
);
102 priv_freeset(priv_set
);
106 nwamd_deescalate(void) {
107 (void) pthread_mutex_lock(&uid_mutex
);
110 if (--uid_cnt
== 0) {
111 priv_set_t
*priv_set
, *allpriv_set
;
113 /* build up our minimal set of privs. */
114 priv_set
= priv_str_to_set("basic", ",", NULL
);
115 allpriv_set
= priv_str_to_set("zone", ",", NULL
);
116 if (priv_set
== NULL
|| allpriv_set
== NULL
)
117 pfail("converting privilege sets: %s", strerror(errno
));
119 (void) priv_addset(priv_set
, PRIV_FILE_CHOWN_SELF
);
120 (void) priv_addset(priv_set
, PRIV_FILE_DAC_READ
);
121 (void) priv_addset(priv_set
, PRIV_FILE_DAC_WRITE
);
122 (void) priv_addset(priv_set
, PRIV_NET_RAWACCESS
);
123 (void) priv_addset(priv_set
, PRIV_NET_PRIVADDR
);
124 (void) priv_addset(priv_set
, PRIV_PROC_AUDIT
);
125 (void) priv_addset(priv_set
, PRIV_PROC_OWNER
);
126 (void) priv_addset(priv_set
, PRIV_PROC_SETID
);
127 (void) priv_addset(priv_set
, PRIV_SYS_CONFIG
);
128 (void) priv_addset(priv_set
, PRIV_SYS_IP_CONFIG
);
129 (void) priv_addset(priv_set
, PRIV_SYS_IPC_CONFIG
);
130 (void) priv_addset(priv_set
, PRIV_SYS_MOUNT
);
131 (void) priv_addset(priv_set
, PRIV_SYS_NET_CONFIG
);
132 (void) priv_addset(priv_set
, PRIV_SYS_RES_CONFIG
);
133 (void) priv_addset(priv_set
, PRIV_SYS_RESOURCE
);
136 * Since our zone might not have all these privs,
137 * just ask for those that are available.
139 priv_intersect(allpriv_set
, priv_set
);
141 if (setppriv(PRIV_SET
, PRIV_INHERITABLE
, priv_set
) == -1) {
142 priv_freeset(allpriv_set
);
143 priv_freeset(priv_set
);
144 pfail("setppriv inheritable: %s", strerror(errno
));
147 * Need to ensure permitted set contains all privs so we can
150 if (setppriv(PRIV_SET
, PRIV_PERMITTED
, allpriv_set
) == -1) {
151 priv_freeset(allpriv_set
);
152 priv_freeset(priv_set
);
153 pfail("setppriv permitted: %s", strerror(errno
));
156 * We need to find a smaller set of privs that are important to
157 * us. Otherwise we really are not gaining much by doing this.
159 if (setppriv(PRIV_SET
, PRIV_EFFECTIVE
, priv_set
) == -1) {
160 priv_freeset(allpriv_set
);
161 priv_freeset(priv_set
);
162 pfail("setppriv effective: %s", strerror(errno
));
165 priv_freeset(priv_set
);
166 priv_freeset(allpriv_set
);
168 (void) pthread_mutex_unlock(&uid_mutex
);
173 * This starts a child process determined by command. If command contains a
174 * slash then it is assumed to be a full path; otherwise the path is searched
175 * for an executable file with the name command. Command is also used as
176 * argv[0] of the new process. The rest of the arguments of the function
177 * up to the first NULL make up pointers to arguments of the new process.
179 * This function returns child exit status on success and -1 on failure.
181 * NOTE: original_sigmask must be set before this function is called.
184 nwamd_start_childv(const char *command
, char const * const *argv
)
186 posix_spawnattr_t attr
;
188 int i
, rc
, status
, n
;
194 for (i
= 1; argv
[i
] != NULL
&& n
> 2; i
++) {
195 n
-= strlcat(vbuf
, " ", n
);
196 n
-= strlcat(vbuf
, argv
[i
], n
);
198 if (argv
[i
] != NULL
|| n
< 0)
199 nlog(LOG_ERR
, "nwamd_start_childv can't log full arg vector");
201 if ((rc
= posix_spawnattr_init(&attr
)) != 0) {
202 nlog(LOG_DEBUG
, "posix_spawnattr_init %d %s\n",
206 (void) sigfillset(&fullset
);
207 if ((rc
= posix_spawnattr_setsigdefault(&attr
, &fullset
)) != 0) {
208 nlog(LOG_DEBUG
, "setsigdefault %d %s\n", rc
, strerror(rc
));
211 if ((rc
= posix_spawnattr_setsigmask(&attr
, &original_sigmask
)) != 0) {
212 nlog(LOG_DEBUG
, "setsigmask %d %s\n", rc
, strerror(rc
));
215 if ((rc
= posix_spawnattr_setflags(&attr
,
216 POSIX_SPAWN_SETSIGDEF
|POSIX_SPAWN_SETSIGMASK
)) != 0) {
217 nlog(LOG_DEBUG
, "setflags %d %s\n", rc
, strerror(rc
));
221 if ((rc
= posix_spawnp(&pid
, command
, NULL
, &attr
, (char * const *)argv
,
223 nlog(LOG_DEBUG
, "posix_spawnp failed errno %d", rc
);
227 if ((rc
= posix_spawnattr_destroy(&attr
)) != 0) {
228 nlog(LOG_DEBUG
, "posix_spawn_attr_destroy %d %s\n",
233 (void) waitpid(pid
, &status
, 0);
234 if (WIFSIGNALED(status
) || WIFSTOPPED(status
)) {
235 i
= WIFSIGNALED(status
) ? WTERMSIG(status
) : WSTOPSIG(status
);
236 nlog(LOG_ERR
, "'%s%s' %s with signal %d (%s)", command
, vbuf
,
237 (WIFSIGNALED(status
) ? "terminated" : "stopped"), i
,
241 nlog(LOG_INFO
, "'%s%s' completed normally: %d", command
, vbuf
,
242 WEXITSTATUS(status
));
243 return (WEXITSTATUS(status
));
248 * For global zone, check if the link is used by a non-global
249 * zone, note that the non-global zones doesn't need this check,
250 * because zoneadm has taken care of this when the zone boots.
251 * In the global zone, we ignore events for local-zone-owned links
252 * since these are taken care of by the local zone's network
253 * configuration services.
256 nwamd_link_belongs_to_this_zone(const char *linkname
)
259 char zonename
[ZONENAME_MAX
];
262 zoneid
= getzoneid();
263 if (zoneid
== GLOBAL_ZONEID
) {
264 datalink_id_t linkid
;
265 dladm_status_t status
;
266 char errstr
[DLADM_STRSIZE
];
268 if ((status
= dladm_name2info(dld_handle
, linkname
, &linkid
,
269 NULL
, NULL
, NULL
)) != DLADM_STATUS_OK
) {
270 nlog(LOG_DEBUG
, "nwamd_link_belongs_to_this_zone: "
271 "could not get linkid for %s: %s",
272 linkname
, dladm_status2str(status
, errstr
));
276 ret
= zone_check_datalink(&zoneid
, linkid
);
278 (void) getzonenamebyid(zoneid
, zonename
, ZONENAME_MAX
);
279 nlog(LOG_DEBUG
, "nwamd_link_belongs_to_this_zone: "
280 "%s is used by non-global zone: %s",
290 * res is a pointer to the scf_resources_t to be released.
293 release_scf_resources(scf_resources_t
*res
)
295 scf_entry_destroy(res
->sr_ent
);
296 scf_transaction_destroy(res
->sr_tx
);
297 scf_value_destroy(res
->sr_val
);
298 scf_property_destroy(res
->sr_prop
);
299 scf_pg_destroy(res
->sr_pg
);
300 scf_snapshot_destroy(res
->sr_snap
);
301 scf_instance_destroy(res
->sr_inst
);
302 (void) scf_handle_unbind(res
->sr_handle
);
303 scf_handle_destroy(res
->sr_handle
);
308 * fmri is the instance to look up
310 * res is a pointer to an scf_resources_t. This is an internal
311 * structure that holds all the handles needed to get a specific
312 * property from the running snapshot; on a successful return it
313 * contains the scf_value_t that should be passed to the desired
314 * scf_value_get_foo() function, and must be freed after use by
315 * calling release_scf_resources(). On a failure return, any
316 * resources that may have been assigned to res are released, so
317 * the caller does not need to do any cleanup in the failure case.
324 create_scf_resources(const char *fmri
, scf_resources_t
*res
)
334 if ((res
->sr_handle
= scf_handle_create(SCF_VERSION
)) == NULL
) {
338 if (scf_handle_bind(res
->sr_handle
) != 0) {
339 scf_handle_destroy(res
->sr_handle
);
342 if ((res
->sr_inst
= scf_instance_create(res
->sr_handle
)) == NULL
) {
345 if (scf_handle_decode_fmri(res
->sr_handle
, fmri
, NULL
, NULL
,
346 res
->sr_inst
, NULL
, NULL
, SCF_DECODE_FMRI_REQUIRE_INSTANCE
) != 0) {
349 if ((res
->sr_snap
= scf_snapshot_create(res
->sr_handle
)) == NULL
) {
352 if (scf_instance_get_snapshot(res
->sr_inst
, "running",
353 res
->sr_snap
) != 0) {
356 if ((res
->sr_pg
= scf_pg_create(res
->sr_handle
)) == NULL
) {
359 if ((res
->sr_prop
= scf_property_create(res
->sr_handle
)) == NULL
) {
362 if ((res
->sr_val
= scf_value_create(res
->sr_handle
)) == NULL
) {
365 if ((res
->sr_tx
= scf_transaction_create(res
->sr_handle
)) == NULL
) {
368 if ((res
->sr_ent
= scf_entry_create(res
->sr_handle
)) == NULL
) {
374 nlog(LOG_ERR
, "create_scf_resources failed: %s",
375 scf_strerror(scf_error()));
376 release_scf_resources(res
);
382 * fmri is the instance to look up
383 * pg is the property group to look up
384 * prop is the property within that group to look up
385 * running specifies if running snapshot is to be used
387 * res is a pointer to an scf_resources_t. This is an internal
388 * structure that holds all the handles needed to get a specific
389 * property from the running snapshot; on a successful return it
390 * contains the scf_value_t that should be passed to the desired
391 * scf_value_get_foo() function, and must be freed after use by
392 * calling release_scf_resources(). On a failure return, any
393 * resources that may have been assigned to res are released, so
394 * the caller does not need to do any cleanup in the failure case.
400 get_property_value(const char *fmri
, const char *pg
, const char *prop
,
401 boolean_t running
, scf_resources_t
*res
)
403 if (create_scf_resources(fmri
, res
) != 0)
406 if (scf_instance_get_pg_composed(res
->sr_inst
,
407 running
? res
->sr_snap
: NULL
, pg
, res
->sr_pg
) != 0) {
410 if (scf_pg_get_property(res
->sr_pg
, prop
, res
->sr_prop
) != 0) {
413 if (scf_property_get_value(res
->sr_prop
, res
->sr_val
) != 0) {
419 release_scf_resources(res
);
425 * lfmri is the instance fmri to look up
426 * lpg is the property group to look up
427 * lprop is the property within that group to look up
429 * answer is a pointer to the property value
433 * If successful, the property value is retured in *answer.
434 * Otherwise, *answer is undefined, and it is up to the caller to decide
435 * how to handle that case.
438 nwamd_lookup_boolean_property(const char *lfmri
, const char *lpg
,
439 const char *lprop
, boolean_t
*answer
)
445 if (get_property_value(lfmri
, lpg
, lprop
, B_TRUE
, &res
) != 0) {
448 * an error was already logged by get_property_value,
449 * and it released any resources assigned to res before
454 if (scf_value_get_boolean(res
.sr_val
, &prop_val
) != 0) {
457 *answer
= (boolean_t
)prop_val
;
460 release_scf_resources(&res
);
466 * lfmri is the instance fmri to look up
467 * lpg is the property group to look up
468 * lprop is the property within that group to look up
469 * buf is the place to put the answer
470 * bufsz is the size of buf
476 * If successful, the property value is retured in buf.
477 * Otherwise, buf is undefined, and it is up to the caller to decide
478 * how to handle that case.
481 nwamd_lookup_string_property(const char *lfmri
, const char *lpg
,
482 const char *lprop
, char *buf
, size_t bufsz
)
487 if (get_property_value(lfmri
, lpg
, lprop
, B_TRUE
, &res
) != 0) {
489 * The above function fails when trying to get a
490 * non-persistent property group from the running snapshot.
491 * Try going for the non-running snapshot.
493 if (get_property_value(lfmri
, lpg
, lprop
, B_FALSE
, &res
) != 0) {
495 * an error was already logged by get_property_value,
496 * and it released any resources assigned to res before
502 if (scf_value_get_astring(res
.sr_val
, buf
, bufsz
) == 0)
507 release_scf_resources(&res
);
513 * lfmri is the instance fmri to look up
514 * lpg is the property group to look up
515 * lprop is the property within that group to look up
517 * answer is a pointer to the property value
521 * If successful, the property value is retured in *answer.
522 * Otherwise, *answer is undefined, and it is up to the caller to decide
523 * how to handle that case.
526 nwamd_lookup_count_property(const char *lfmri
, const char *lpg
,
527 const char *lprop
, uint64_t *answer
)
532 if (get_property_value(lfmri
, lpg
, lprop
, B_TRUE
, &res
) != 0) {
535 * an error was already logged by get_property_value,
536 * and it released any resources assigned to res before
541 if (scf_value_get_count(res
.sr_val
, answer
) != 0) {
546 release_scf_resources(&res
);
551 set_property_value(scf_resources_t
*res
, const char *propname
,
558 new = (scf_pg_get_property(res
->sr_pg
, propname
, res
->sr_prop
) != 0);
560 if (scf_transaction_start(res
->sr_tx
, res
->sr_pg
) == -1) {
564 if (scf_transaction_property_new(res
->sr_tx
, res
->sr_ent
,
565 propname
, proptype
) == -1) {
569 if (scf_transaction_property_change(res
->sr_tx
, res
->sr_ent
,
570 propname
, proptype
) == -1) {
575 if (scf_entry_add_value(res
->sr_ent
, res
->sr_val
) != 0) {
579 result
= scf_transaction_commit(res
->sr_tx
);
581 scf_transaction_reset(res
->sr_tx
);
582 if (scf_pg_update(res
->sr_pg
) == -1) {
585 nlog(LOG_INFO
, "set_property_value: transaction commit failed "
586 "for %s; retrying", propname
);
598 nwamd_set_count_property(const char *fmri
, const char *pg
, const char *prop
,
603 if (create_scf_resources(fmri
, &res
) != 0)
606 if (scf_instance_add_pg(res
.sr_inst
, pg
, SCF_GROUP_APPLICATION
,
607 SCF_PG_FLAG_NONPERSISTENT
, res
.sr_pg
) != 0) {
608 if (scf_error() != SCF_ERROR_EXISTS
)
610 if (scf_instance_get_pg_composed(res
.sr_inst
, NULL
, pg
,
615 scf_value_set_count(res
.sr_val
, (uint64_t)count
);
617 if (set_property_value(&res
, prop
, SCF_TYPE_COUNT
) != 0)
620 release_scf_resources(&res
);
624 nlog(LOG_INFO
, "nwamd_set_count_property: scf failure %s while "
625 "setting %s", scf_strerror(scf_error()), prop
);
626 release_scf_resources(&res
);
631 nwamd_set_string_property(const char *fmri
, const char *pg
, const char *prop
,
636 if (create_scf_resources(fmri
, &res
) != 0)
639 if (scf_instance_add_pg(res
.sr_inst
, pg
, SCF_GROUP_APPLICATION
,
640 SCF_PG_FLAG_NONPERSISTENT
, res
.sr_pg
) != 0) {
641 if (scf_error() != SCF_ERROR_EXISTS
)
643 if (scf_instance_get_pg_composed(res
.sr_inst
, NULL
, pg
,
648 if (scf_value_set_astring(res
.sr_val
, str
) != 0)
651 if (set_property_value(&res
, prop
, SCF_TYPE_ASTRING
) != 0)
654 release_scf_resources(&res
);
658 nlog(LOG_INFO
, "nwamd_set_string_property: scf failure %s while "
659 "setting %s", scf_strerror(scf_error()), prop
);
660 release_scf_resources(&res
);
665 * Deletes property prop from property group pg in SMF instance fmri.
666 * Returns 0 on success, -1 on failure.
669 nwamd_delete_scf_property(const char *fmri
, const char *pg
, const char *prop
)
674 if (create_scf_resources(fmri
, &res
) != 0)
677 if (scf_instance_add_pg(res
.sr_inst
, pg
, SCF_GROUP_APPLICATION
,
678 SCF_PG_FLAG_NONPERSISTENT
, res
.sr_pg
) != 0) {
679 if (scf_error() != SCF_ERROR_EXISTS
)
681 if (scf_instance_get_pg_composed(res
.sr_inst
, NULL
, pg
,
686 if (scf_pg_get_property(res
.sr_pg
, prop
, res
.sr_prop
) != 0)
689 if (scf_transaction_start(res
.sr_tx
, res
.sr_pg
) == -1)
692 if (scf_transaction_property_delete(res
.sr_tx
, res
.sr_ent
, prop
) == -1)
695 result
= scf_transaction_commit(res
.sr_tx
);
697 scf_transaction_reset(res
.sr_tx
);
698 if (scf_pg_update(res
.sr_pg
) == -1)
705 release_scf_resources(&res
);
708 release_scf_resources(&res
);