13938Sjbeck /*
23938Sjbeck  * CDDL HEADER START
33938Sjbeck  *
43938Sjbeck  * The contents of this file are subject to the terms of the
53938Sjbeck  * Common Development and Distribution License (the "License").
63938Sjbeck  * You may not use this file except in compliance with the License.
73938Sjbeck  *
83938Sjbeck  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93938Sjbeck  * or http://www.opensolaris.org/os/licensing.
103938Sjbeck  * See the License for the specific language governing permissions
113938Sjbeck  * and limitations under the License.
123938Sjbeck  *
133938Sjbeck  * When distributing Covered Code, include this CDDL HEADER in each
143938Sjbeck  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153938Sjbeck  * If applicable, add the following below this CDDL HEADER, with the
163938Sjbeck  * fields enclosed by brackets "[]" replaced with your own identifying
173938Sjbeck  * information: Portions Copyright [yyyy] [name of copyright owner]
183938Sjbeck  *
193938Sjbeck  * CDDL HEADER END
203938Sjbeck  */
213938Sjbeck 
223938Sjbeck /*
23*7645Sjames.d.carlson@sun.com  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
243938Sjbeck  * Use is subject to license terms.
253938Sjbeck  */
263938Sjbeck 
273938Sjbeck /*
283938Sjbeck  * util.c contains a set of miscellaneous utility functions which:
293938Sjbeck  * - syslog(LOG_DEBUG, ...) if debugging is enabled
303938Sjbeck  * - check for an IP interface being marked running
313938Sjbeck  * - look up all flags for an IP interface
323938Sjbeck  * - start a child process
333938Sjbeck  * - schedule a timer
343938Sjbeck  * - look up the zone name
353938Sjbeck  */
363938Sjbeck 
373938Sjbeck #include <stdarg.h>
383938Sjbeck #include <stdio.h>
393938Sjbeck #include <stdlib.h>
403938Sjbeck #include <unistd.h>
413938Sjbeck #include <pthread.h>
423938Sjbeck #include <string.h>
433938Sjbeck #include <stropts.h>
443938Sjbeck #include <syslog.h>
453938Sjbeck #include <sys/types.h>
463938Sjbeck #include <sys/socket.h>
473938Sjbeck #include <net/if.h>
48*7645Sjames.d.carlson@sun.com #include <netinet/in.h>
49*7645Sjames.d.carlson@sun.com #include <arpa/inet.h>
503938Sjbeck #include <spawn.h>
513938Sjbeck #include <wait.h>
523938Sjbeck #include <inetcfg.h>
533938Sjbeck #include <errno.h>
543938Sjbeck #include <zone.h>
553938Sjbeck 
563938Sjbeck #include "defines.h"
573938Sjbeck #include "structures.h"
583938Sjbeck #include "functions.h"
593938Sjbeck #include "variables.h"
603938Sjbeck 
613938Sjbeck extern char **environ;
623938Sjbeck boolean_t debug = B_FALSE;
633938Sjbeck 
645439Smeem /* PRINTFLIKE1 */
653938Sjbeck void
663938Sjbeck dprintf(const char *fmt, ...)
673938Sjbeck {
683938Sjbeck 	va_list ap;
693938Sjbeck 	char vbuf[1024];
703938Sjbeck 
713938Sjbeck 	va_start(ap, fmt);
723938Sjbeck 	if (debug) {
733938Sjbeck 		(void) vsnprintf(vbuf, sizeof (vbuf), fmt, ap);
743938Sjbeck 		syslog(LOG_DEBUG, "%d: %s", pthread_self(), vbuf);
753938Sjbeck 	}
763938Sjbeck 	va_end(ap);
773938Sjbeck }
783938Sjbeck 
793938Sjbeck uint64_t
803938Sjbeck get_ifflags(const char *name, sa_family_t family)
813938Sjbeck {
823938Sjbeck 	icfg_if_t intf;
833938Sjbeck 	icfg_handle_t h;
843938Sjbeck 	uint64_t flags = 0;
853938Sjbeck 
863938Sjbeck 	(void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
873938Sjbeck 	intf.if_protocol = family;
883938Sjbeck 
893938Sjbeck 	if (icfg_open(&h, &intf) != ICFG_SUCCESS)
903938Sjbeck 		return (0);
913938Sjbeck 
923938Sjbeck 	if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
933938Sjbeck 		/*
943938Sjbeck 		 * Interfaces can be ripped out from underneath us (for example
953938Sjbeck 		 * by DHCP).  We don't want to spam the console for those.
963938Sjbeck 		 */
973938Sjbeck 		if (errno == ENOENT)
983938Sjbeck 			dprintf("get_ifflags: icfg_get_flags failed for '%s'",
993938Sjbeck 			    name);
1003938Sjbeck 		else
1013938Sjbeck 			syslog(LOG_ERR, "get_ifflags: icfg_get_flags %s af "
1023938Sjbeck 			    "%d: %m", name, family);
1033938Sjbeck 		/* just to be sure... */
1043938Sjbeck 		flags = 0;
1053938Sjbeck 	}
1063938Sjbeck 	icfg_close(h);
1073938Sjbeck 
1083938Sjbeck 	return (flags);
1093938Sjbeck }
1103938Sjbeck 
111*7645Sjames.d.carlson@sun.com /* This is just a work-around for CR 6745448: clear out a toxic interface */
112*7645Sjames.d.carlson@sun.com void
113*7645Sjames.d.carlson@sun.com zero_out_v4addr(const char *name)
114*7645Sjames.d.carlson@sun.com {
115*7645Sjames.d.carlson@sun.com 	icfg_if_t intf;
116*7645Sjames.d.carlson@sun.com 	icfg_handle_t h;
117*7645Sjames.d.carlson@sun.com 	struct sockaddr_in sinv;
118*7645Sjames.d.carlson@sun.com 	socklen_t sinlen;
119*7645Sjames.d.carlson@sun.com 	int pfxlen;
120*7645Sjames.d.carlson@sun.com 
121*7645Sjames.d.carlson@sun.com 	(void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
122*7645Sjames.d.carlson@sun.com 	intf.if_protocol = AF_INET;
123*7645Sjames.d.carlson@sun.com 
124*7645Sjames.d.carlson@sun.com 	if (icfg_open(&h, &intf) != ICFG_SUCCESS)
125*7645Sjames.d.carlson@sun.com 		return;
126*7645Sjames.d.carlson@sun.com 
127*7645Sjames.d.carlson@sun.com 	sinlen = sizeof (sinv);
128*7645Sjames.d.carlson@sun.com 	if (icfg_get_addr(h, (struct sockaddr *)&sinv, &sinlen, &pfxlen,
129*7645Sjames.d.carlson@sun.com 	    B_FALSE) == ICFG_SUCCESS &&
130*7645Sjames.d.carlson@sun.com 	    sinv.sin_addr.s_addr != INADDR_ANY) {
131*7645Sjames.d.carlson@sun.com 		dprintf("bug workaround: clear out address %s on %s",
132*7645Sjames.d.carlson@sun.com 		    inet_ntoa(sinv.sin_addr), name);
133*7645Sjames.d.carlson@sun.com 		sinv.sin_addr.s_addr = INADDR_ANY;
134*7645Sjames.d.carlson@sun.com 		(void) icfg_set_addr(h, (const struct sockaddr *)&sinv, sinlen);
135*7645Sjames.d.carlson@sun.com 	}
136*7645Sjames.d.carlson@sun.com 	icfg_close(h);
137*7645Sjames.d.carlson@sun.com }
138*7645Sjames.d.carlson@sun.com 
1393938Sjbeck /*
1403938Sjbeck  *
1413938Sjbeck  * This starts a child process determined by command.  If command contains a
1423938Sjbeck  * slash then it is assumed to be a full path; otherwise the path is searched
1433938Sjbeck  * for an executable file with the name command.  Command is also used as
1443938Sjbeck  * argv[0] of the new process.  The rest of the arguments of the function
1453938Sjbeck  * up to the first NULL make up pointers to arguments of the new process.
1463938Sjbeck  *
1473938Sjbeck  * This function returns child exit status on success and -1 on failure.
1483938Sjbeck  *
1493938Sjbeck  * NOTE: original_sigmask must be set before this function is called.
1503938Sjbeck  */
1513938Sjbeck int
1523938Sjbeck start_childv(const char *command, char const * const *argv)
1533938Sjbeck {
1543938Sjbeck 	posix_spawnattr_t attr;
1553938Sjbeck 	sigset_t fullset;
1563938Sjbeck 	int i, rc, status, n;
1573938Sjbeck 	pid_t pid;
1583938Sjbeck 	char vbuf[1024];
1593938Sjbeck 
1603938Sjbeck 	vbuf[0] = 0;
1613938Sjbeck 	n = sizeof (vbuf);
1623938Sjbeck 	for (i = 1; argv[i] != NULL && n > 2; i++) {
1633938Sjbeck 		n -= strlcat(vbuf, " ", n);
1643938Sjbeck 		n -= strlcat(vbuf, argv[i], n);
1653938Sjbeck 	}
1663938Sjbeck 	if (argv[i] != NULL || n < 0)
1673938Sjbeck 		syslog(LOG_ERR, "start_childv can't log full arg vector");
1683938Sjbeck 
1693938Sjbeck 	if ((rc = posix_spawnattr_init(&attr)) != 0) {
1703938Sjbeck 		dprintf("posix_spawnattr_init %d %s\n", rc, strerror(rc));
1713938Sjbeck 		return (-1);
1723938Sjbeck 	}
1733938Sjbeck 	(void) sigfillset(&fullset);
1743938Sjbeck 	if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
1753938Sjbeck 		dprintf("setsigdefault %d %s\n", rc, strerror(rc));
1763938Sjbeck 		return (-1);
1773938Sjbeck 	}
1783938Sjbeck 	if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
1793938Sjbeck 		dprintf("setsigmask %d %s\n", rc, strerror(rc));
1803938Sjbeck 		return (-1);
1813938Sjbeck 	}
1823938Sjbeck 	if ((rc = posix_spawnattr_setflags(&attr,
1833938Sjbeck 	    POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
1843938Sjbeck 		dprintf("setflags %d %s\n", rc, strerror(rc));
1853938Sjbeck 		return (-1);
1863938Sjbeck 	}
1873938Sjbeck 
1883938Sjbeck 	if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
1893938Sjbeck 	    environ)) > 0) {
1903938Sjbeck 		dprintf("posix_spawnp failed errno %d", rc);
1913938Sjbeck 		return (-1);
1923938Sjbeck 	}
1933938Sjbeck 
1943938Sjbeck 	if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
1953938Sjbeck 		dprintf("posix_spawn_attr_destroy %d %s\n", rc, strerror(rc));
1963938Sjbeck 		return (-1);
1973938Sjbeck 	}
1983938Sjbeck 
1993938Sjbeck 	(void) waitpid(pid, &status, 0);
2003938Sjbeck 	if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
2013938Sjbeck 		i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
2023938Sjbeck 		syslog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
2033938Sjbeck 		    (WIFSIGNALED(status) ? "terminated" : "stopped"), i,
2043938Sjbeck 		    strsignal(i));
2053938Sjbeck 		return (-2);
2063938Sjbeck 	} else {
2073938Sjbeck 		syslog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
2083938Sjbeck 		    WEXITSTATUS(status));
2093938Sjbeck 		return (WEXITSTATUS(status));
2103938Sjbeck 	}
2113938Sjbeck }
2123938Sjbeck 
2133938Sjbeck int
2143938Sjbeck start_child(const char *command, ...)
2153938Sjbeck {
2163938Sjbeck 	const char **argv = NULL;
2173938Sjbeck 	int argv_len = 0;
2183938Sjbeck 	va_list ap;
2193938Sjbeck 	int i = 1, rc;
2203938Sjbeck 
2213938Sjbeck 	va_start(ap, command);
2223938Sjbeck 	do {
2233938Sjbeck 		if (i >= argv_len) {
2243938Sjbeck 			void *p;
2253938Sjbeck 
2263938Sjbeck 			argv_len = argv_len != 0 ? argv_len * 2 : 4;
2273938Sjbeck 			p = realloc(argv, sizeof (*argv)*argv_len);
2283938Sjbeck 			if (p != NULL) {
2293938Sjbeck 				argv = p;
2303938Sjbeck 			} else {
2313938Sjbeck 				syslog(LOG_ERR, "Out of memory in start_child");
2323938Sjbeck 				free(argv);
2333938Sjbeck 				return (-1);
2343938Sjbeck 			}
2353938Sjbeck 		}
2363938Sjbeck 
2373938Sjbeck 		argv[i] = va_arg(ap, const char *);
2383938Sjbeck 	} while (argv[i++] != NULL);
2393938Sjbeck 	va_end(ap);
2403938Sjbeck 	argv[0] = command;
2413938Sjbeck 
2423938Sjbeck 	rc = start_childv(command, argv);
2433938Sjbeck 	free(argv);
2443938Sjbeck 
2453938Sjbeck 	return (rc);
2463938Sjbeck }
2473938Sjbeck 
2483938Sjbeck uint32_t	timer_expire = TIMER_INFINITY;
2493938Sjbeck 
2503938Sjbeck /*
2513938Sjbeck  * Schedules a SIGALRM in delay seconds, unless one is already
2523938Sjbeck  * scheduled sooner.  If one is already scheduled later than
2533938Sjbeck  * delay seconds from now, that one will be replaced.
2543938Sjbeck  */
2553938Sjbeck void
2563938Sjbeck start_timer(uint32_t now, uint32_t delay)
2573938Sjbeck {
2583938Sjbeck 	if (now + delay > timer_expire)
2593938Sjbeck 		return;
2603938Sjbeck 
2613938Sjbeck 	timer_expire = now + delay;
2623938Sjbeck 	(void) alarm(delay);
2633938Sjbeck }
2643938Sjbeck 
2653938Sjbeck void
2663938Sjbeck lookup_zonename(char *zonename, size_t zonesize)
2673938Sjbeck {
2683938Sjbeck 	zoneid_t zoneid = getzoneid();
2693938Sjbeck 
2703938Sjbeck 	if (getzonenamebyid(zoneid, zonename, zonesize) >= 0)
2713938Sjbeck 		return;
2723938Sjbeck 	syslog(LOG_ERR, "could not determine zone name");
2733938Sjbeck 	(void) strlcpy(zonename, GLOBAL_ZONENAME, zonesize);
2743938Sjbeck }
275