xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/util.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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