Move /var/svc/log to /var/log/svc
[unleashed/lotheac.git] / usr / src / cmd / cmd-inet / lib / nwamd / util.c
blobc558eb6f1c28a57a1ec321baf39bb60787d13d8d
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 <priv.h>
42 #include <pthread.h>
43 #include <pwd.h>
44 #include <spawn.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <strings.h>
50 #include <stropts.h>
51 #include <sys/socket.h>
52 #include <sys/sockio.h>
53 #include <sys/types.h>
54 #include <unistd.h>
55 #include <wait.h>
56 #include <zone.h>
58 #include "util.h"
59 #include "llp.h"
61 extern char **environ;
62 extern sigset_t original_sigmask;
65 * A holder for all the resources needed to get a property value
66 * using libscf.
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;
74 scf_value_t *sr_val;
75 scf_transaction_t *sr_tx;
76 scf_transaction_entry_t *sr_ent;
77 } scf_resources_t;
79 static pthread_mutex_t uid_mutex = PTHREAD_MUTEX_INITIALIZER;
80 static uid_t uid;
81 static int uid_cnt;
83 void
84 nwamd_escalate(void) {
85 priv_set_t *priv_set;
86 priv_set = priv_str_to_set("zone", ",", NULL);
88 if (priv_set == NULL)
89 pfail("creating privilege set: %s", strerror(errno));
91 (void) pthread_mutex_lock(&uid_mutex);
92 if (uid == 0)
93 uid = getuid();
94 if (uid_cnt++ == 0) {
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);
105 void
106 nwamd_deescalate(void) {
107 (void) pthread_mutex_lock(&uid_mutex);
109 assert(uid_cnt > 0);
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
148 * escalate later.
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;
187 sigset_t fullset;
188 int i, rc, status, n;
189 pid_t pid;
190 char vbuf[1024];
192 vbuf[0] = 0;
193 n = sizeof (vbuf);
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",
203 rc, strerror(rc));
204 return (-1);
206 (void) sigfillset(&fullset);
207 if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
208 nlog(LOG_DEBUG, "setsigdefault %d %s\n", rc, strerror(rc));
209 return (-1);
211 if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
212 nlog(LOG_DEBUG, "setsigmask %d %s\n", rc, strerror(rc));
213 return (-1);
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));
218 return (-1);
221 if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
222 environ)) > 0) {
223 nlog(LOG_DEBUG, "posix_spawnp failed errno %d", rc);
224 return (-1);
227 if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
228 nlog(LOG_DEBUG, "posix_spawn_attr_destroy %d %s\n",
229 rc, strerror(rc));
230 return (-1);
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,
238 strsignal(i));
239 return (-2);
240 } else {
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.
255 boolean_t
256 nwamd_link_belongs_to_this_zone(const char *linkname)
258 zoneid_t zoneid;
259 char zonename[ZONENAME_MAX];
260 int ret;
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));
273 return (B_FALSE);
275 zoneid = ALL_ZONES;
276 ret = zone_check_datalink(&zoneid, linkid);
277 if (ret == 0) {
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",
281 linkname, zonename);
282 return (B_FALSE);
285 return (B_TRUE);
289 * Inputs:
290 * res is a pointer to the scf_resources_t to be released.
292 static void
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);
307 * Inputs:
308 * fmri is the instance to look up
309 * Outputs:
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.
318 * Returns:
319 * 0 on success
320 * -1 on failure
323 static int
324 create_scf_resources(const char *fmri, scf_resources_t *res)
326 res->sr_tx = NULL;
327 res->sr_ent = NULL;
328 res->sr_inst = NULL;
329 res->sr_snap = NULL;
330 res->sr_pg = NULL;
331 res->sr_prop = NULL;
332 res->sr_val = NULL;
334 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
335 return (-1);
338 if (scf_handle_bind(res->sr_handle) != 0) {
339 scf_handle_destroy(res->sr_handle);
340 return (-1);
342 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
343 goto failure;
345 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
346 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
347 goto failure;
349 if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) {
350 goto failure;
352 if (scf_instance_get_snapshot(res->sr_inst, "running",
353 res->sr_snap) != 0) {
354 goto failure;
356 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
357 goto failure;
359 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
360 goto failure;
362 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
363 goto failure;
365 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) {
366 goto failure;
368 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) {
369 goto failure;
371 return (0);
373 failure:
374 nlog(LOG_ERR, "create_scf_resources failed: %s",
375 scf_strerror(scf_error()));
376 release_scf_resources(res);
377 return (-1);
381 * Inputs:
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
386 * Outputs:
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.
395 * Returns:
396 * 0 on success
397 * -1 on failure
399 static int
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)
404 return (-1);
406 if (scf_instance_get_pg_composed(res->sr_inst,
407 running ? res->sr_snap : NULL, pg, res->sr_pg) != 0) {
408 goto failure;
410 if (scf_pg_get_property(res->sr_pg, prop, res->sr_prop) != 0) {
411 goto failure;
413 if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) {
414 goto failure;
416 return (0);
418 failure:
419 release_scf_resources(res);
420 return (-1);
424 * Inputs:
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
428 * Outputs:
429 * answer is a pointer to the property value
430 * Returns:
431 * 0 on success
432 * -1 on failure
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)
441 int result = -1;
442 scf_resources_t res;
443 uint8_t prop_val;
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
450 * returning.
452 return (result);
454 if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) {
455 goto cleanup;
457 *answer = (boolean_t)prop_val;
458 result = 0;
459 cleanup:
460 release_scf_resources(&res);
461 return (result);
465 * Inputs:
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
471 * Outputs:
473 * Returns:
474 * 0 on success
475 * -1 on failure
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)
484 int result = -1;
485 scf_resources_t res;
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
497 * returning.
499 return (result);
502 if (scf_value_get_astring(res.sr_val, buf, bufsz) == 0)
503 goto cleanup;
505 result = 0;
506 cleanup:
507 release_scf_resources(&res);
508 return (result);
512 * Inputs:
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
516 * Outputs:
517 * answer is a pointer to the property value
518 * Returns:
519 * 0 on success
520 * -1 on failure
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)
529 int result = -1;
530 scf_resources_t res;
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
537 * returning.
539 return (result);
541 if (scf_value_get_count(res.sr_val, answer) != 0) {
542 goto cleanup;
544 result = 0;
545 cleanup:
546 release_scf_resources(&res);
547 return (result);
550 static int
551 set_property_value(scf_resources_t *res, const char *propname,
552 scf_type_t proptype)
554 int result = -1;
555 boolean_t new;
557 retry:
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) {
561 goto failure;
563 if (new) {
564 if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
565 propname, proptype) == -1) {
566 goto failure;
568 } else {
569 if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
570 propname, proptype) == -1) {
571 goto failure;
575 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) {
576 goto failure;
579 result = scf_transaction_commit(res->sr_tx);
580 if (result == 0) {
581 scf_transaction_reset(res->sr_tx);
582 if (scf_pg_update(res->sr_pg) == -1) {
583 goto failure;
585 nlog(LOG_INFO, "set_property_value: transaction commit failed "
586 "for %s; retrying", propname);
587 goto retry;
589 if (result == -1)
590 goto failure;
591 return (0);
593 failure:
594 return (-1);
598 nwamd_set_count_property(const char *fmri, const char *pg, const char *prop,
599 uint64_t count)
601 scf_resources_t res;
603 if (create_scf_resources(fmri, &res) != 0)
604 return (-1);
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)
609 goto failure;
610 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
611 res.sr_pg) != 0)
612 goto failure;
615 scf_value_set_count(res.sr_val, (uint64_t)count);
617 if (set_property_value(&res, prop, SCF_TYPE_COUNT) != 0)
618 goto failure;
620 release_scf_resources(&res);
621 return (0);
623 failure:
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);
627 return (-1);
631 nwamd_set_string_property(const char *fmri, const char *pg, const char *prop,
632 const char *str)
634 scf_resources_t res;
636 if (create_scf_resources(fmri, &res) != 0)
637 return (-1);
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)
642 goto failure;
643 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
644 res.sr_pg) != 0)
645 goto failure;
648 if (scf_value_set_astring(res.sr_val, str) != 0)
649 goto failure;
651 if (set_property_value(&res, prop, SCF_TYPE_ASTRING) != 0)
652 goto failure;
654 release_scf_resources(&res);
655 return (0);
657 failure:
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);
661 return (-1);
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)
671 scf_resources_t res;
672 int result = -1;
674 if (create_scf_resources(fmri, &res) != 0)
675 return (-1);
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)
680 goto failure;
681 if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
682 res.sr_pg) != 0)
683 goto failure;
686 if (scf_pg_get_property(res.sr_pg, prop, res.sr_prop) != 0)
687 goto failure;
688 retry:
689 if (scf_transaction_start(res.sr_tx, res.sr_pg) == -1)
690 goto failure;
692 if (scf_transaction_property_delete(res.sr_tx, res.sr_ent, prop) == -1)
693 goto failure;
695 result = scf_transaction_commit(res.sr_tx);
696 if (result == 0) {
697 scf_transaction_reset(res.sr_tx);
698 if (scf_pg_update(res.sr_pg) == -1)
699 goto failure;
700 goto retry;
702 if (result == -1)
703 goto failure;
705 release_scf_resources(&res);
706 return (0);
707 failure:
708 release_scf_resources(&res);
709 return (-1);