Home | History | Annotate | Download | only in nwamd
      1 /*
      2  * CDDL HEADER START
      3  *
      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.
      7  *
      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.
     12  *
     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]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * util.c contains a set of miscellaneous utility functions which:
     29  * - syslog(LOG_DEBUG, ...) if debugging is enabled
     30  * - check for an IP interface being marked running
     31  * - look up all flags for an IP interface
     32  * - start a child process
     33  * - schedule a timer
     34  * - look up the zone name
     35  */
     36 
     37 #include <stdarg.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <unistd.h>
     41 #include <pthread.h>
     42 #include <string.h>
     43 #include <stropts.h>
     44 #include <syslog.h>
     45 #include <sys/types.h>
     46 #include <sys/socket.h>
     47 #include <net/if.h>
     48 #include <netinet/in.h>
     49 #include <arpa/inet.h>
     50 #include <spawn.h>
     51 #include <wait.h>
     52 #include <inetcfg.h>
     53 #include <errno.h>
     54 #include <zone.h>
     55 
     56 #include "defines.h"
     57 #include "structures.h"
     58 #include "functions.h"
     59 #include "variables.h"
     60 
     61 extern char **environ;
     62 boolean_t debug = B_FALSE;
     63 
     64 /* PRINTFLIKE1 */
     65 void
     66 dprintf(const char *fmt, ...)
     67 {
     68 	va_list ap;
     69 	char vbuf[1024];
     70 
     71 	va_start(ap, fmt);
     72 	if (debug) {
     73 		(void) vsnprintf(vbuf, sizeof (vbuf), fmt, ap);
     74 		syslog(LOG_DEBUG, "%d: %s", pthread_self(), vbuf);
     75 	}
     76 	va_end(ap);
     77 }
     78 
     79 uint64_t
     80 get_ifflags(const char *name, sa_family_t family)
     81 {
     82 	icfg_if_t intf;
     83 	icfg_handle_t h;
     84 	uint64_t flags = 0;
     85 
     86 	(void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
     87 	intf.if_protocol = family;
     88 
     89 	if (icfg_open(&h, &intf) != ICFG_SUCCESS)
     90 		return (0);
     91 
     92 	if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
     93 		/*
     94 		 * Interfaces can be ripped out from underneath us (for example
     95 		 * by DHCP).  We don't want to spam the console for those.
     96 		 */
     97 		if (errno == ENOENT)
     98 			dprintf("get_ifflags: icfg_get_flags failed for '%s'",
     99 			    name);
    100 		else
    101 			syslog(LOG_ERR, "get_ifflags: icfg_get_flags %s af "
    102 			    "%d: %m", name, family);
    103 		/* just to be sure... */
    104 		flags = 0;
    105 	}
    106 	icfg_close(h);
    107 
    108 	return (flags);
    109 }
    110 
    111 /* This is just a work-around for CR 6745448: clear out a toxic interface */
    112 void
    113 zero_out_v4addr(const char *name)
    114 {
    115 	icfg_if_t intf;
    116 	icfg_handle_t h;
    117 	struct sockaddr_in sinv;
    118 	socklen_t sinlen;
    119 	int pfxlen;
    120 
    121 	(void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
    122 	intf.if_protocol = AF_INET;
    123 
    124 	if (icfg_open(&h, &intf) != ICFG_SUCCESS)
    125 		return;
    126 
    127 	sinlen = sizeof (sinv);
    128 	if (icfg_get_addr(h, (struct sockaddr *)&sinv, &sinlen, &pfxlen,
    129 	    B_FALSE) == ICFG_SUCCESS &&
    130 	    sinv.sin_addr.s_addr != INADDR_ANY) {
    131 		dprintf("bug workaround: clear out address %s on %s",
    132 		    inet_ntoa(sinv.sin_addr), name);
    133 		sinv.sin_addr.s_addr = INADDR_ANY;
    134 		(void) icfg_set_addr(h, (const struct sockaddr *)&sinv, sinlen);
    135 	}
    136 	icfg_close(h);
    137 }
    138 
    139 /*
    140  *
    141  * This starts a child process determined by command.  If command contains a
    142  * slash then it is assumed to be a full path; otherwise the path is searched
    143  * for an executable file with the name command.  Command is also used as
    144  * argv[0] of the new process.  The rest of the arguments of the function
    145  * up to the first NULL make up pointers to arguments of the new process.
    146  *
    147  * This function returns child exit status on success and -1 on failure.
    148  *
    149  * NOTE: original_sigmask must be set before this function is called.
    150  */
    151 int
    152 start_childv(const char *command, char const * const *argv)
    153 {
    154 	posix_spawnattr_t attr;
    155 	sigset_t fullset;
    156 	int i, rc, status, n;
    157 	pid_t pid;
    158 	char vbuf[1024];
    159 
    160 	vbuf[0] = 0;
    161 	n = sizeof (vbuf);
    162 	for (i = 1; argv[i] != NULL && n > 2; i++) {
    163 		n -= strlcat(vbuf, " ", n);
    164 		n -= strlcat(vbuf, argv[i], n);
    165 	}
    166 	if (argv[i] != NULL || n < 0)
    167 		syslog(LOG_ERR, "start_childv can't log full arg vector");
    168 
    169 	if ((rc = posix_spawnattr_init(&attr)) != 0) {
    170 		dprintf("posix_spawnattr_init %d %s\n", rc, strerror(rc));
    171 		return (-1);
    172 	}
    173 	(void) sigfillset(&fullset);
    174 	if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
    175 		dprintf("setsigdefault %d %s\n", rc, strerror(rc));
    176 		return (-1);
    177 	}
    178 	if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
    179 		dprintf("setsigmask %d %s\n", rc, strerror(rc));
    180 		return (-1);
    181 	}
    182 	if ((rc = posix_spawnattr_setflags(&attr,
    183 	    POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
    184 		dprintf("setflags %d %s\n", rc, strerror(rc));
    185 		return (-1);
    186 	}
    187 
    188 	if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
    189 	    environ)) > 0) {
    190 		dprintf("posix_spawnp failed errno %d", rc);
    191 		return (-1);
    192 	}
    193 
    194 	if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
    195 		dprintf("posix_spawn_attr_destroy %d %s\n", rc, strerror(rc));
    196 		return (-1);
    197 	}
    198 
    199 	(void) waitpid(pid, &status, 0);
    200 	if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
    201 		i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
    202 		syslog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
    203 		    (WIFSIGNALED(status) ? "terminated" : "stopped"), i,
    204 		    strsignal(i));
    205 		return (-2);
    206 	} else {
    207 		syslog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
    208 		    WEXITSTATUS(status));
    209 		return (WEXITSTATUS(status));
    210 	}
    211 }
    212 
    213 int
    214 start_child(const char *command, ...)
    215 {
    216 	const char **argv = NULL;
    217 	int argv_len = 0;
    218 	va_list ap;
    219 	int i = 1, rc;
    220 
    221 	va_start(ap, command);
    222 	do {
    223 		if (i >= argv_len) {
    224 			void *p;
    225 
    226 			argv_len = argv_len != 0 ? argv_len * 2 : 4;
    227 			p = realloc(argv, sizeof (*argv)*argv_len);
    228 			if (p != NULL) {
    229 				argv = p;
    230 			} else {
    231 				syslog(LOG_ERR, "Out of memory in start_child");
    232 				free(argv);
    233 				return (-1);
    234 			}
    235 		}
    236 
    237 		argv[i] = va_arg(ap, const char *);
    238 	} while (argv[i++] != NULL);
    239 	va_end(ap);
    240 	argv[0] = command;
    241 
    242 	rc = start_childv(command, argv);
    243 	free(argv);
    244 
    245 	return (rc);
    246 }
    247 
    248 uint32_t	timer_expire = TIMER_INFINITY;
    249 
    250 /*
    251  * Schedules a SIGALRM in delay seconds, unless one is already
    252  * scheduled sooner.  If one is already scheduled later than
    253  * delay seconds from now, that one will be replaced.
    254  */
    255 void
    256 start_timer(uint32_t now, uint32_t delay)
    257 {
    258 	if (now + delay > timer_expire)
    259 		return;
    260 
    261 	timer_expire = now + delay;
    262 	(void) alarm(delay);
    263 }
    264 
    265 void
    266 lookup_zonename(char *zonename, size_t zonesize)
    267 {
    268 	zoneid_t zoneid = getzoneid();
    269 
    270 	if (getzonenamebyid(zoneid, zonename, zonesize) >= 0)
    271 		return;
    272 	syslog(LOG_ERR, "could not determine zone name");
    273 	(void) strlcpy(zonename, GLOBAL_ZONENAME, zonesize);
    274 }
    275