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>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 #include <sys/types.h>
60 extern char **environ
;
61 extern sigset_t original_sigmask
;
64 * A holder for all the resources needed to get a property value
67 typedef struct scf_resources
{
68 scf_handle_t
*sr_handle
;
69 scf_instance_t
*sr_inst
;
70 scf_snapshot_t
*sr_snap
;
71 scf_propertygroup_t
*sr_pg
;
72 scf_property_t
*sr_prop
;
74 scf_transaction_t
*sr_tx
;
75 scf_transaction_entry_t
*sr_ent
;
78 static pthread_mutex_t uid_mutex
= PTHREAD_MUTEX_INITIALIZER
;
83 nwamd_escalate(void) {
85 priv_set
= priv_str_to_set("zone", ",", NULL
);
88 pfail("creating privilege set: %s", strerror(errno
));
90 (void) pthread_mutex_lock(&uid_mutex
);
94 if (setppriv(PRIV_SET
, PRIV_EFFECTIVE
, priv_set
) == -1) {
95 priv_freeset(priv_set
);
96 pfail("setppriv effective: %s", strerror(errno
));
99 (void) pthread_mutex_unlock(&uid_mutex
);
101 priv_freeset(priv_set
);
105 nwamd_deescalate(void) {
106 (void) pthread_mutex_lock(&uid_mutex
);
109 if (--uid_cnt
== 0) {
110 priv_set_t
*priv_set
, *allpriv_set
;
112 /* build up our minimal set of privs. */
113 priv_set
= priv_str_to_set("basic", ",", NULL
);
114 allpriv_set
= priv_str_to_set("zone", ",", NULL
);
115 if (priv_set
== NULL
|| allpriv_set
== NULL
)
116 pfail("converting privilege sets: %s", strerror(errno
));
118 (void) priv_addset(priv_set
, PRIV_FILE_CHOWN_SELF
);
119 (void) priv_addset(priv_set
, PRIV_FILE_DAC_READ
);
120 (void) priv_addset(priv_set
, PRIV_FILE_DAC_WRITE
);
121 (void) priv_addset(priv_set
, PRIV_NET_RAWACCESS
);
122 (void) priv_addset(priv_set
, PRIV_NET_PRIVADDR
);
123 (void) priv_addset(priv_set
, PRIV_PROC_AUDIT
);
124 (void) priv_addset(priv_set
, PRIV_PROC_OWNER
);
125 (void) priv_addset(priv_set
, PRIV_PROC_SETID
);
126 (void) priv_addset(priv_set
, PRIV_SYS_CONFIG
);
127 (void) priv_addset(priv_set
, PRIV_SYS_IP_CONFIG
);
128 (void) priv_addset(priv_set
, PRIV_SYS_IPC_CONFIG
);
129 (void) priv_addset(priv_set
, PRIV_SYS_MOUNT
);
130 (void) priv_addset(priv_set
, PRIV_SYS_NET_CONFIG
);
131 (void) priv_addset(priv_set
, PRIV_SYS_RES_CONFIG
);
132 (void) priv_addset(priv_set
, PRIV_SYS_RESOURCE
);
135 * Since our zone might not have all these privs,
136 * just ask for those that are available.
138 priv_intersect(allpriv_set
, priv_set
);
140 if (setppriv(PRIV_SET
, PRIV_INHERITABLE
, priv_set
) == -1) {
141 priv_freeset(allpriv_set
);
142 priv_freeset(priv_set
);
143 pfail("setppriv inheritable: %s", strerror(errno
));
146 * Need to ensure permitted set contains all privs so we can
149 if (setppriv(PRIV_SET
, PRIV_PERMITTED
, allpriv_set
) == -1) {
150 priv_freeset(allpriv_set
);
151 priv_freeset(priv_set
);
152 pfail("setppriv permitted: %s", strerror(errno
));
155 * We need to find a smaller set of privs that are important to
156 * us. Otherwise we really are not gaining much by doing this.
158 if (setppriv(PRIV_SET
, PRIV_EFFECTIVE
, priv_set
) == -1) {
159 priv_freeset(allpriv_set
);
160 priv_freeset(priv_set
);
161 pfail("setppriv effective: %s", strerror(errno
));
164 priv_freeset(priv_set
);
165 priv_freeset(allpriv_set
);
167 (void) pthread_mutex_unlock(&uid_mutex
);
172 * This starts a child process determined by command. If command contains a
173 * slash then it is assumed to be a full path; otherwise the path is searched
174 * for an executable file with the name command. Command is also used as
175 * argv[0] of the new process. The rest of the arguments of the function
176 * up to the first NULL make up pointers to arguments of the new process.
178 * This function returns child exit status on success and -1 on failure.
180 * NOTE: original_sigmask must be set before this function is called.
183 nwamd_start_childv(const char *command
, char const * const *argv
)
185 posix_spawnattr_t attr
;
187 int i
, rc
, status
, n
;
193 for (i
= 1; argv
[i
] != NULL
&& n
> 2; i
++) {
194 n
-= strlcat(vbuf
, " ", n
);
195 n
-= strlcat(vbuf
, argv
[i
], n
);
197 if (argv
[i
] != NULL
|| n
< 0)
198 nlog(LOG_ERR
, "nwamd_start_childv can't log full arg vector");
200 if ((rc
= posix_spawnattr_init(&attr
)) != 0) {
201 nlog(LOG_DEBUG
, "posix_spawnattr_init %d %s\n",
205 (void) sigfillset(&fullset
);
206 if ((rc
= posix_spawnattr_setsigdefault(&attr
, &fullset
)) != 0) {
207 nlog(LOG_DEBUG
, "setsigdefault %d %s\n", rc
, strerror(rc
));
210 if ((rc
= posix_spawnattr_setsigmask(&attr
, &original_sigmask
)) != 0) {
211 nlog(LOG_DEBUG
, "setsigmask %d %s\n", rc
, strerror(rc
));
214 if ((rc
= posix_spawnattr_setflags(&attr
,
215 POSIX_SPAWN_SETSIGDEF
|POSIX_SPAWN_SETSIGMASK
)) != 0) {
216 nlog(LOG_DEBUG
, "setflags %d %s\n", rc
, strerror(rc
));
220 if ((rc
= posix_spawnp(&pid
, command
, NULL
, &attr
, (char * const *)argv
,
222 nlog(LOG_DEBUG
, "posix_spawnp failed errno %d", rc
);
226 if ((rc
= posix_spawnattr_destroy(&attr
)) != 0) {
227 nlog(LOG_DEBUG
, "posix_spawn_attr_destroy %d %s\n",
232 (void) waitpid(pid
, &status
, 0);
233 if (WIFSIGNALED(status
) || WIFSTOPPED(status
)) {
234 i
= WIFSIGNALED(status
) ? WTERMSIG(status
) : WSTOPSIG(status
);
235 nlog(LOG_ERR
, "'%s%s' %s with signal %d (%s)", command
, vbuf
,
236 (WIFSIGNALED(status
) ? "terminated" : "stopped"), i
,
240 nlog(LOG_INFO
, "'%s%s' completed normally: %d", command
, vbuf
,
241 WEXITSTATUS(status
));
242 return (WEXITSTATUS(status
));
247 * For global zone, check if the link is used by a non-global
248 * zone, note that the non-global zones doesn't need this check,
249 * because zoneadm has taken care of this when the zone boots.
250 * In the global zone, we ignore events for local-zone-owned links
251 * since these are taken care of by the local zone's network
252 * configuration services.
255 nwamd_link_belongs_to_this_zone(const char *linkname
)
258 char zonename
[ZONENAME_MAX
];
261 zoneid
= getzoneid();
262 if (zoneid
== GLOBAL_ZONEID
) {
263 datalink_id_t linkid
;
264 dladm_status_t status
;
265 char errstr
[DLADM_STRSIZE
];
267 if ((status
= dladm_name2info(dld_handle
, linkname
, &linkid
,
268 NULL
, NULL
, NULL
)) != DLADM_STATUS_OK
) {
269 nlog(LOG_DEBUG
, "nwamd_link_belongs_to_this_zone: "
270 "could not get linkid for %s: %s",
271 linkname
, dladm_status2str(status
, errstr
));
275 ret
= zone_check_datalink(&zoneid
, linkid
);
277 (void) getzonenamebyid(zoneid
, zonename
, ZONENAME_MAX
);
278 nlog(LOG_DEBUG
, "nwamd_link_belongs_to_this_zone: "
279 "%s is used by non-global zone: %s",
289 * res is a pointer to the scf_resources_t to be released.
292 release_scf_resources(scf_resources_t
*res
)
294 scf_entry_destroy(res
->sr_ent
);
295 scf_transaction_destroy(res
->sr_tx
);
296 scf_value_destroy(res
->sr_val
);
297 scf_property_destroy(res
->sr_prop
);
298 scf_pg_destroy(res
->sr_pg
);
299 scf_snapshot_destroy(res
->sr_snap
);
300 scf_instance_destroy(res
->sr_inst
);
301 (void) scf_handle_unbind(res
->sr_handle
);
302 scf_handle_destroy(res
->sr_handle
);
307 * fmri is the instance to look up
309 * res is a pointer to an scf_resources_t. This is an internal
310 * structure that holds all the handles needed to get a specific
311 * property from the running snapshot; on a successful return it
312 * contains the scf_value_t that should be passed to the desired
313 * scf_value_get_foo() function, and must be freed after use by
314 * calling release_scf_resources(). On a failure return, any
315 * resources that may have been assigned to res are released, so
316 * the caller does not need to do any cleanup in the failure case.
323 create_scf_resources(const char *fmri
, scf_resources_t
*res
)
333 if ((res
->sr_handle
= scf_handle_create(SCF_VERSION
)) == NULL
) {
337 if (scf_handle_bind(res
->sr_handle
) != 0) {
338 scf_handle_destroy(res
->sr_handle
);
341 if ((res
->sr_inst
= scf_instance_create(res
->sr_handle
)) == NULL
) {
344 if (scf_handle_decode_fmri(res
->sr_handle
, fmri
, NULL
, NULL
,
345 res
->sr_inst
, NULL
, NULL
, SCF_DECODE_FMRI_REQUIRE_INSTANCE
) != 0) {
348 if ((res
->sr_snap
= scf_snapshot_create(res
->sr_handle
)) == NULL
) {
351 if (scf_instance_get_snapshot(res
->sr_inst
, "running",
352 res
->sr_snap
) != 0) {
355 if ((res
->sr_pg
= scf_pg_create(res
->sr_handle
)) == NULL
) {
358 if ((res
->sr_prop
= scf_property_create(res
->sr_handle
)) == NULL
) {
361 if ((res
->sr_val
= scf_value_create(res
->sr_handle
)) == NULL
) {
364 if ((res
->sr_tx
= scf_transaction_create(res
->sr_handle
)) == NULL
) {
367 if ((res
->sr_ent
= scf_entry_create(res
->sr_handle
)) == NULL
) {
373 nlog(LOG_ERR
, "create_scf_resources failed: %s",
374 scf_strerror(scf_error()));
375 release_scf_resources(res
);
381 * fmri is the instance to look up
382 * pg is the property group to look up
383 * prop is the property within that group to look up
384 * running specifies if running snapshot is to be used
386 * res is a pointer to an scf_resources_t. This is an internal
387 * structure that holds all the handles needed to get a specific
388 * property from the running snapshot; on a successful return it
389 * contains the scf_value_t that should be passed to the desired
390 * scf_value_get_foo() function, and must be freed after use by
391 * calling release_scf_resources(). On a failure return, any
392 * resources that may have been assigned to res are released, so
393 * the caller does not need to do any cleanup in the failure case.
399 get_property_value(const char *fmri
, const char *pg
, const char *prop
,
400 boolean_t running
, scf_resources_t
*res
)
402 if (create_scf_resources(fmri
, res
) != 0)
405 if (scf_instance_get_pg_composed(res
->sr_inst
,
406 running
? res
->sr_snap
: NULL
, pg
, res
->sr_pg
) != 0) {
409 if (scf_pg_get_property(res
->sr_pg
, prop
, res
->sr_prop
) != 0) {
412 if (scf_property_get_value(res
->sr_prop
, res
->sr_val
) != 0) {
418 release_scf_resources(res
);
424 * lfmri is the instance fmri to look up
425 * lpg is the property group to look up
426 * lprop is the property within that group to look up
428 * answer is a pointer to the property value
432 * If successful, the property value is retured in *answer.
433 * Otherwise, *answer is undefined, and it is up to the caller to decide
434 * how to handle that case.
437 nwamd_lookup_boolean_property(const char *lfmri
, const char *lpg
,
438 const char *lprop
, boolean_t
*answer
)
444 if (get_property_value(lfmri
, lpg
, lprop
, B_TRUE
, &res
) != 0) {
447 * an error was already logged by get_property_value,
448 * and it released any resources assigned to res before
453 if (scf_value_get_boolean(res
.sr_val
, &prop_val
) != 0) {
456 *answer
= (boolean_t
)prop_val
;
459 release_scf_resources(&res
);
465 * lfmri is the instance fmri to look up
466 * lpg is the property group to look up
467 * lprop is the property within that group to look up
468 * buf is the place to put the answer
469 * bufsz is the size of buf
475 * If successful, the property value is retured in buf.
476 * Otherwise, buf is undefined, and it is up to the caller to decide
477 * how to handle that case.
480 nwamd_lookup_string_property(const char *lfmri
, const char *lpg
,
481 const char *lprop
, char *buf
, size_t bufsz
)
486 if (get_property_value(lfmri
, lpg
, lprop
, B_TRUE
, &res
) != 0) {
488 * The above function fails when trying to get a
489 * non-persistent property group from the running snapshot.
490 * Try going for the non-running snapshot.
492 if (get_property_value(lfmri
, lpg
, lprop
, B_FALSE
, &res
) != 0) {
494 * an error was already logged by get_property_value,
495 * and it released any resources assigned to res before
501 if (scf_value_get_astring(res
.sr_val
, buf
, bufsz
) == 0)
506 release_scf_resources(&res
);
512 * lfmri is the instance fmri to look up
513 * lpg is the property group to look up
514 * lprop is the property within that group to look up
516 * answer is a pointer to the property value
520 * If successful, the property value is retured in *answer.
521 * Otherwise, *answer is undefined, and it is up to the caller to decide
522 * how to handle that case.
525 nwamd_lookup_count_property(const char *lfmri
, const char *lpg
,
526 const char *lprop
, uint64_t *answer
)
531 if (get_property_value(lfmri
, lpg
, lprop
, B_TRUE
, &res
) != 0) {
534 * an error was already logged by get_property_value,
535 * and it released any resources assigned to res before
540 if (scf_value_get_count(res
.sr_val
, answer
) != 0) {
545 release_scf_resources(&res
);
550 set_property_value(scf_resources_t
*res
, const char *propname
,
557 new = (scf_pg_get_property(res
->sr_pg
, propname
, res
->sr_prop
) != 0);
559 if (scf_transaction_start(res
->sr_tx
, res
->sr_pg
) == -1) {
563 if (scf_transaction_property_new(res
->sr_tx
, res
->sr_ent
,
564 propname
, proptype
) == -1) {
568 if (scf_transaction_property_change(res
->sr_tx
, res
->sr_ent
,
569 propname
, proptype
) == -1) {
574 if (scf_entry_add_value(res
->sr_ent
, res
->sr_val
) != 0) {
578 result
= scf_transaction_commit(res
->sr_tx
);
580 scf_transaction_reset(res
->sr_tx
);
581 if (scf_pg_update(res
->sr_pg
) == -1) {
584 nlog(LOG_INFO
, "set_property_value: transaction commit failed "
585 "for %s; retrying", propname
);
597 nwamd_set_count_property(const char *fmri
, const char *pg
, const char *prop
,
602 if (create_scf_resources(fmri
, &res
) != 0)
605 if (scf_instance_add_pg(res
.sr_inst
, pg
, SCF_GROUP_APPLICATION
,
606 SCF_PG_FLAG_NONPERSISTENT
, res
.sr_pg
) != 0) {
607 if (scf_error() != SCF_ERROR_EXISTS
)
609 if (scf_instance_get_pg_composed(res
.sr_inst
, NULL
, pg
,
614 scf_value_set_count(res
.sr_val
, (uint64_t)count
);
616 if (set_property_value(&res
, prop
, SCF_TYPE_COUNT
) != 0)
619 release_scf_resources(&res
);
623 nlog(LOG_INFO
, "nwamd_set_count_property: scf failure %s while "
624 "setting %s", scf_strerror(scf_error()), prop
);
625 release_scf_resources(&res
);
630 nwamd_set_string_property(const char *fmri
, const char *pg
, const char *prop
,
635 if (create_scf_resources(fmri
, &res
) != 0)
638 if (scf_instance_add_pg(res
.sr_inst
, pg
, SCF_GROUP_APPLICATION
,
639 SCF_PG_FLAG_NONPERSISTENT
, res
.sr_pg
) != 0) {
640 if (scf_error() != SCF_ERROR_EXISTS
)
642 if (scf_instance_get_pg_composed(res
.sr_inst
, NULL
, pg
,
647 if (scf_value_set_astring(res
.sr_val
, str
) != 0)
650 if (set_property_value(&res
, prop
, SCF_TYPE_ASTRING
) != 0)
653 release_scf_resources(&res
);
657 nlog(LOG_INFO
, "nwamd_set_string_property: scf failure %s while "
658 "setting %s", scf_strerror(scf_error()), prop
);
659 release_scf_resources(&res
);
664 * Deletes property prop from property group pg in SMF instance fmri.
665 * Returns 0 on success, -1 on failure.
668 nwamd_delete_scf_property(const char *fmri
, const char *pg
, const char *prop
)
673 if (create_scf_resources(fmri
, &res
) != 0)
676 if (scf_instance_add_pg(res
.sr_inst
, pg
, SCF_GROUP_APPLICATION
,
677 SCF_PG_FLAG_NONPERSISTENT
, res
.sr_pg
) != 0) {
678 if (scf_error() != SCF_ERROR_EXISTS
)
680 if (scf_instance_get_pg_composed(res
.sr_inst
, NULL
, pg
,
685 if (scf_pg_get_property(res
.sr_pg
, prop
, res
.sr_prop
) != 0)
688 if (scf_transaction_start(res
.sr_tx
, res
.sr_pg
) == -1)
691 if (scf_transaction_property_delete(res
.sr_tx
, res
.sr_ent
, prop
) == -1)
694 result
= scf_transaction_commit(res
.sr_tx
);
696 scf_transaction_reset(res
.sr_tx
);
697 if (scf_pg_update(res
.sr_pg
) == -1)
704 release_scf_resources(&res
);
707 release_scf_resources(&res
);