8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / lib / nwamd / util.c
blob561031958d247aac0dfb9c117f4e0990a0e10854
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
27 * util.c contains a set of miscellaneous utility functions which,
28 * among other things:
29 * - start a child process
30 * - look up the zone name
31 * - look up/set SMF properties
32 * - drop/escalate privs
35 #include <assert.h>
36 #include <errno.h>
37 #include <libdllink.h>
38 #include <limits.h>
39 #include <libscf.h>
40 #include <net/if.h>
41 #include <pthread.h>
42 #include <pwd.h>
43 #include <spawn.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <strings.h>
49 #include <stropts.h>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 #include <sys/types.h>
53 #include <unistd.h>
54 #include <wait.h>
55 #include <zone.h>
57 #include "util.h"
58 #include "llp.h"
60 extern char **environ;
61 extern sigset_t original_sigmask;
64 * A holder for all the resources needed to get a property value
65 * using libscf.
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;
73 scf_value_t *sr_val;
74 scf_transaction_t *sr_tx;
75 scf_transaction_entry_t *sr_ent;
76 } scf_resources_t;
78 static pthread_mutex_t uid_mutex = PTHREAD_MUTEX_INITIALIZER;
79 static uid_t uid;
80 static int uid_cnt;
82 void
83 nwamd_escalate(void) {
84 priv_set_t *priv_set;
85 priv_set = priv_str_to_set("zone", ",", NULL);
87 if (priv_set == NULL)
88 pfail("creating privilege set: %s", strerror(errno));
90 (void) pthread_mutex_lock(&uid_mutex);
91 if (uid == 0)
92 uid = getuid();
93 if (uid_cnt++ == 0) {
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);
104 void
105 nwamd_deescalate(void) {
106 (void) pthread_mutex_lock(&uid_mutex);
108 assert(uid_cnt > 0);
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
147 * escalate later.
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;
186 sigset_t fullset;
187 int i, rc, status, n;
188 pid_t pid;
189 char vbuf[1024];
191 vbuf[0] = 0;
192 n = sizeof (vbuf);
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",
202 rc, strerror(rc));
203 return (-1);
205 (void) sigfillset(&fullset);
206 if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
207 nlog(LOG_DEBUG, "setsigdefault %d %s\n", rc, strerror(rc));
208 return (-1);
210 if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
211 nlog(LOG_DEBUG, "setsigmask %d %s\n", rc, strerror(rc));
212 return (-1);
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));
217 return (-1);
220 if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
221 environ)) > 0) {
222 nlog(LOG_DEBUG, "posix_spawnp failed errno %d", rc);
223 return (-1);
226 if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
227 nlog(LOG_DEBUG, "posix_spawn_attr_destroy %d %s\n",
228 rc, strerror(rc));
229 return (-1);
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,
237 strsignal(i));
238 return (-2);
239 } else {
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.
254 boolean_t
255 nwamd_link_belongs_to_this_zone(const char *linkname)
257 zoneid_t zoneid;
258 char zonename[ZONENAME_MAX];
259 int ret;
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));
272 return (B_FALSE);
274 zoneid = ALL_ZONES;
275 ret = zone_check_datalink(&zoneid, linkid);
276 if (ret == 0) {
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",
280 linkname, zonename);
281 return (B_FALSE);
284 return (B_TRUE);
288 * Inputs:
289 * res is a pointer to the scf_resources_t to be released.
291 static void
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);
306 * Inputs:
307 * fmri is the instance to look up
308 * Outputs:
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.
317 * Returns:
318 * 0 on success
319 * -1 on failure
322 static int
323 create_scf_resources(const char *fmri, scf_resources_t *res)
325 res->sr_tx = NULL;
326 res->sr_ent = NULL;
327 res->sr_inst = NULL;
328 res->sr_snap = NULL;
329 res->sr_pg = NULL;
330 res->sr_prop = NULL;
331 res->sr_val = NULL;
333 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
334 return (-1);
337 if (scf_handle_bind(res->sr_handle) != 0) {
338 scf_handle_destroy(res->sr_handle);
339 return (-1);
341 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
342 goto failure;
344 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
345 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
346 goto failure;
348 if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) {
349 goto failure;
351 if (scf_instance_get_snapshot(res->sr_inst, "running",
352 res->sr_snap) != 0) {
353 goto failure;
355 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
356 goto failure;
358 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
359 goto failure;
361 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
362 goto failure;
364 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) {
365 goto failure;
367 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) {
368 goto failure;
370 return (0);
372 failure:
373 nlog(LOG_ERR, "create_scf_resources failed: %s",
374 scf_strerror(scf_error()));
375 release_scf_resources(res);
376 return (-1);
380 * Inputs:
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
385 * Outputs:
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.
394 * Returns:
395 * 0 on success
396 * -1 on failure
398 static int
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)
403 return (-1);
405 if (scf_instance_get_pg_composed(res->sr_inst,
406 running ? res->sr_snap : NULL, pg, res->sr_pg) != 0) {
407 goto failure;
409 if (scf_pg_get_property(res->sr_pg, prop, res->sr_prop) != 0) {
410 goto failure;
412 if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) {
413 goto failure;
415 return (0);
417 failure:
418 release_scf_resources(res);
419 return (-1);
423 * Inputs:
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
427 * Outputs:
428 * answer is a pointer to the property value
429 * Returns:
430 * 0 on success
431 * -1 on failure
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)
440 int result = -1;
441 scf_resources_t res;
442 uint8_t prop_val;
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
449 * returning.
451 return (result);
453 if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) {
454 goto cleanup;
456 *answer = (boolean_t)prop_val;
457 result = 0;
458 cleanup:
459 release_scf_resources(&res);
460 return (result);
464 * Inputs:
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
470 * Outputs:
472 * Returns:
473 * 0 on success
474 * -1 on failure
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)
483 int result = -1;
484 scf_resources_t res;
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
496 * returning.
498 return (result);
501 if (scf_value_get_astring(res.sr_val, buf, bufsz) == 0)
502 goto cleanup;
504 result = 0;
505 cleanup:
506 release_scf_resources(&res);
507 return (result);
511 * Inputs:
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
515 * Outputs:
516 * answer is a pointer to the property value
517 * Returns:
518 * 0 on success
519 * -1 on failure
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)
528 int result = -1;
529 scf_resources_t res;
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
536 * returning.
538 return (result);
540 if (scf_value_get_count(res.sr_val, answer) != 0) {
541 goto cleanup;
543 result = 0;
544 cleanup:
545 release_scf_resources(&res);
546 return (result);
549 static int
550 set_property_value(scf_resources_t *res, const char *propname,
551 scf_type_t proptype)
553 int result = -1;
554 boolean_t new;
556 retry:
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) {
560 goto failure;
562 if (new) {
563 if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
564 propname, proptype) == -1) {
565 goto failure;
567 } else {
568 if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
569 propname, proptype) == -1) {
570 goto failure;
574 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) {
575 goto failure;
578 result = scf_transaction_commit(res->sr_tx);
579 if (result == 0) {
580 scf_transaction_reset(res->sr_tx);
581 if (scf_pg_update(res->sr_pg) == -1) {
582 goto failure;
584 nlog(LOG_INFO, "set_property_value: transaction commit failed "
585 "for %s; retrying", propname);
586 goto retry;
588 if (result == -1)
589 goto failure;
590 return (0);
592 failure:
593 return (-1);
597 nwamd_set_count_property(const char *fmri, const char *pg, const char *prop,
598 uint64_t count)
600 scf_resources_t res;
602 if (create_scf_resources(fmri, &res) != 0)
603 return (-1);
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)
608 goto failure;
609 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
610 res.sr_pg) != 0)
611 goto failure;
614 scf_value_set_count(res.sr_val, (uint64_t)count);
616 if (set_property_value(&res, prop, SCF_TYPE_COUNT) != 0)
617 goto failure;
619 release_scf_resources(&res);
620 return (0);
622 failure:
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);
626 return (-1);
630 nwamd_set_string_property(const char *fmri, const char *pg, const char *prop,
631 const char *str)
633 scf_resources_t res;
635 if (create_scf_resources(fmri, &res) != 0)
636 return (-1);
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)
641 goto failure;
642 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
643 res.sr_pg) != 0)
644 goto failure;
647 if (scf_value_set_astring(res.sr_val, str) != 0)
648 goto failure;
650 if (set_property_value(&res, prop, SCF_TYPE_ASTRING) != 0)
651 goto failure;
653 release_scf_resources(&res);
654 return (0);
656 failure:
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);
660 return (-1);
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)
670 scf_resources_t res;
671 int result = -1;
673 if (create_scf_resources(fmri, &res) != 0)
674 return (-1);
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)
679 goto failure;
680 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
681 res.sr_pg) != 0)
682 goto failure;
685 if (scf_pg_get_property(res.sr_pg, prop, res.sr_prop) != 0)
686 goto failure;
687 retry:
688 if (scf_transaction_start(res.sr_tx, res.sr_pg) == -1)
689 goto failure;
691 if (scf_transaction_property_delete(res.sr_tx, res.sr_ent, prop) == -1)
692 goto failure;
694 result = scf_transaction_commit(res.sr_tx);
695 if (result == 0) {
696 scf_transaction_reset(res.sr_tx);
697 if (scf_pg_update(res.sr_pg) == -1)
698 goto failure;
699 goto retry;
701 if (result == -1)
702 goto failure;
704 release_scf_resources(&res);
705 return (0);
706 failure:
707 release_scf_resources(&res);
708 return (-1);