xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/util.c (revision a194faf8907a6722dcf10ad16c6ca72c9b7bd0ba)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * util.c contains a set of miscellaneous utility functions which:
31  * - syslog(LOG_DEBUG, ...) if debugging is enabled
32  * - check for an IP interface being marked running
33  * - look up all flags for an IP interface
34  * - start a child process
35  * - schedule a timer
36  * - check to see if a user is logged in to a graphical console
37  * - look up the zone name
38  */
39 
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <pthread.h>
45 #include <string.h>
46 #include <strings.h>
47 #include <stropts.h>
48 #include <syslog.h>
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <sys/sockio.h>
52 #include <net/if.h>
53 #include <spawn.h>
54 #include <wait.h>
55 #include <inetcfg.h>
56 #include <utmpx.h>
57 #include <pwd.h>
58 #include <limits.h>
59 #include <errno.h>
60 #include <zone.h>
61 
62 #include "defines.h"
63 #include "structures.h"
64 #include "functions.h"
65 #include "variables.h"
66 
67 extern char **environ;
68 boolean_t debug = B_FALSE;
69 
70 /* PRINTFLIKE1 */
71 void
72 dprintf(const char *fmt, ...)
73 {
74 	va_list ap;
75 	char vbuf[1024];
76 
77 	va_start(ap, fmt);
78 	if (debug) {
79 		(void) vsnprintf(vbuf, sizeof (vbuf), fmt, ap);
80 		syslog(LOG_DEBUG, "%d: %s", pthread_self(), vbuf);
81 	}
82 	va_end(ap);
83 }
84 
85 boolean_t
86 is_plugged_in(struct interface *i)
87 {
88 	if (i->if_type == IF_WIRELESS)
89 		return (B_TRUE);
90 
91 	return ((get_ifflags(i->if_name, i->if_family) & IFF_RUNNING) != 0);
92 }
93 
94 uint64_t
95 get_ifflags(const char *name, sa_family_t family)
96 {
97 	icfg_if_t intf;
98 	icfg_handle_t h;
99 	uint64_t flags = 0;
100 
101 	(void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
102 	intf.if_protocol = family;
103 
104 	if (icfg_open(&h, &intf) != ICFG_SUCCESS)
105 		return (0);
106 
107 	if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
108 		/*
109 		 * Interfaces can be ripped out from underneath us (for example
110 		 * by DHCP).  We don't want to spam the console for those.
111 		 */
112 		if (errno == ENOENT)
113 			dprintf("get_ifflags: icfg_get_flags failed for '%s'",
114 			    name);
115 		else
116 			syslog(LOG_ERR, "get_ifflags: icfg_get_flags %s af "
117 			    "%d: %m", name, family);
118 		/* just to be sure... */
119 		flags = 0;
120 	}
121 	icfg_close(h);
122 
123 	return (flags);
124 }
125 
126 /*
127  *
128  * This starts a child process determined by command.  If command contains a
129  * slash then it is assumed to be a full path; otherwise the path is searched
130  * for an executable file with the name command.  Command is also used as
131  * argv[0] of the new process.  The rest of the arguments of the function
132  * up to the first NULL make up pointers to arguments of the new process.
133  *
134  * This function returns child exit status on success and -1 on failure.
135  *
136  * NOTE: original_sigmask must be set before this function is called.
137  */
138 int
139 start_childv(const char *command, char const * const *argv)
140 {
141 	posix_spawnattr_t attr;
142 	sigset_t fullset;
143 	int i, rc, status, n;
144 	pid_t pid;
145 	char vbuf[1024];
146 
147 	vbuf[0] = 0;
148 	n = sizeof (vbuf);
149 	for (i = 1; argv[i] != NULL && n > 2; i++) {
150 		n -= strlcat(vbuf, " ", n);
151 		n -= strlcat(vbuf, argv[i], n);
152 	}
153 	if (argv[i] != NULL || n < 0)
154 		syslog(LOG_ERR, "start_childv can't log full arg vector");
155 
156 	if ((rc = posix_spawnattr_init(&attr)) != 0) {
157 		dprintf("posix_spawnattr_init %d %s\n", rc, strerror(rc));
158 		return (-1);
159 	}
160 	(void) sigfillset(&fullset);
161 	if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
162 		dprintf("setsigdefault %d %s\n", rc, strerror(rc));
163 		return (-1);
164 	}
165 	if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
166 		dprintf("setsigmask %d %s\n", rc, strerror(rc));
167 		return (-1);
168 	}
169 	if ((rc = posix_spawnattr_setflags(&attr,
170 	    POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
171 		dprintf("setflags %d %s\n", rc, strerror(rc));
172 		return (-1);
173 	}
174 
175 	if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
176 	    environ)) > 0) {
177 		dprintf("posix_spawnp failed errno %d", rc);
178 		return (-1);
179 	}
180 
181 	if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
182 		dprintf("posix_spawn_attr_destroy %d %s\n", rc, strerror(rc));
183 		return (-1);
184 	}
185 
186 	(void) waitpid(pid, &status, 0);
187 	if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
188 		i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
189 		syslog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
190 		    (WIFSIGNALED(status) ? "terminated" : "stopped"), i,
191 		    strsignal(i));
192 		return (-2);
193 	} else {
194 		syslog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
195 		    WEXITSTATUS(status));
196 		return (WEXITSTATUS(status));
197 	}
198 }
199 
200 int
201 start_child(const char *command, ...)
202 {
203 	const char **argv = NULL;
204 	int argv_len = 0;
205 	va_list ap;
206 	int i = 1, rc;
207 
208 	va_start(ap, command);
209 	do {
210 		if (i >= argv_len) {
211 			void *p;
212 
213 			argv_len = argv_len != 0 ? argv_len * 2 : 4;
214 			p = realloc(argv, sizeof (*argv)*argv_len);
215 			if (p != NULL) {
216 				argv = p;
217 			} else {
218 				syslog(LOG_ERR, "Out of memory in start_child");
219 				free(argv);
220 				return (-1);
221 			}
222 		}
223 
224 		argv[i] = va_arg(ap, const char *);
225 	} while (argv[i++] != NULL);
226 	va_end(ap);
227 	argv[0] = command;
228 
229 	rc = start_childv(command, argv);
230 	free(argv);
231 
232 	return (rc);
233 }
234 
235 uint32_t	timer_expire = TIMER_INFINITY;
236 
237 /*
238  * Schedules a SIGALRM in delay seconds, unless one is already
239  * scheduled sooner.  If one is already scheduled later than
240  * delay seconds from now, that one will be replaced.
241  */
242 void
243 start_timer(uint32_t now, uint32_t delay)
244 {
245 	if (now + delay > timer_expire)
246 		return;
247 
248 	timer_expire = now + delay;
249 	(void) alarm(delay);
250 }
251 
252 boolean_t
253 valid_graphical_user(boolean_t query)
254 {
255 	struct utmpx *utp;
256 	char *user = NULL;
257 	const char HOMESTR[] = "HOME=";
258 	char buf[1024]; /* == sysconf(_SC_GETPW_R_SIZE_MAX) == NSS_BUFSIZ */
259 	static char home_dir[PATH_MAX + sizeof (HOMESTR)];
260 	struct passwd passwd;
261 	struct passwd *pw;
262 	boolean_t popup_ok;
263 
264 	/*
265 	 * Check to see if our SMF property says popups are OK.
266 	 */
267 	if ((lookup_boolean_property(OUR_PG, query ? "popup_query" :
268 	    "popup_info", &popup_ok) == 0) && !popup_ok)
269 		return (B_FALSE);
270 
271 	/*
272 	 * Look for someone logged into the console from host ":0" (i.e.,
273 	 * the X display.  Down the road, we should generalize this so
274 	 * ":0" is not hard-coded.  Note that the entry we want is usually
275 	 * an ordinary user process but sometimes if a session leader has
276 	 * exited, it can come from a DEAD_PROCESS, as is known to happen
277 	 * when the user logs in via gdm(1m).
278 	 */
279 	setutxent();
280 	while ((utp = getutxent()) != NULL) {
281 		if (((utp->ut_type == USER_PROCESS) ||
282 		    (utp->ut_type == DEAD_PROCESS)) &&
283 		    (strcmp(utp->ut_line, "console") == 0) &&
284 		    (strcmp(utp->ut_host, ":0") == 0)) {
285 			user = strdup(utp->ut_user);
286 			break;
287 		}
288 	}
289 	endutxent();
290 	dprintf("utmpx: done %s", user != NULL ? user : "");
291 
292 	if (user == NULL)
293 		return (B_FALSE);
294 
295 	pw = getpwnam_r(user, &passwd, buf, sizeof (buf));
296 	if (pw == NULL) {
297 		syslog(LOG_ERR, "couldn't get user %s: %m", user);
298 		free(user);
299 		return (B_FALSE);
300 	}
301 	free(user);
302 
303 	/*
304 	 * We shouldn't be dumping this into our environment or changing
305 	 * our uid/gid but instead starting up the zenity processes with
306 	 * this display as this user.  RFE to change this.
307 	 */
308 	(void) putenv("DISPLAY=:0.0");
309 
310 	(void) strlcpy(home_dir, HOMESTR, sizeof (home_dir));
311 	(void) strlcat(home_dir, pw->pw_dir, sizeof (home_dir));
312 	(void) putenv(home_dir);
313 
314 	return (pw != NULL);
315 }
316 
317 void
318 lookup_zonename(char *zonename, size_t zonesize)
319 {
320 	zoneid_t zoneid = getzoneid();
321 
322 	if (getzonenamebyid(zoneid, zonename, zonesize) >= 0)
323 		return;
324 	syslog(LOG_ERR, "could not determine zone name");
325 	(void) strlcpy(zonename, GLOBAL_ZONENAME, zonesize);
326 }
327 
328 /* return B_TRUE if sin_family and sin_addr are the same, B_FALSE if not */
329 boolean_t
330 cmpsockaddr(const struct sockaddr *addr1, const struct sockaddr *addr2)
331 {
332 	struct sockaddr_in *sina, *sinb;
333 	struct sockaddr_in6 *sin6a, *sin6b;
334 
335 	if (addr1 == addr2)
336 		return (B_TRUE);
337 
338 	if (addr1 == NULL || addr2 == NULL)
339 		return (B_FALSE);
340 
341 	if (addr1->sa_family != addr2->sa_family)
342 		return (B_FALSE);
343 
344 	switch (addr1->sa_family) {
345 	case AF_INET:
346 		/* LINTED E_BAD_PTR_CAST_ALIGN */
347 		sina = (struct sockaddr_in *)addr1;
348 		/* LINTED E_BAD_PTR_CAST_ALIGN */
349 		sinb = (struct sockaddr_in *)addr2;
350 		return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr);
351 	case AF_INET6:
352 		/* LINTED E_BAD_PTR_CAST_ALIGN */
353 		sin6a = (struct sockaddr_in6 *)addr1;
354 		/* LINTED E_BAD_PTR_CAST_ALIGN */
355 		sin6b = (struct sockaddr_in6 *)addr2;
356 		return
357 		    (IN6_ARE_ADDR_EQUAL(&sin6a->sin6_addr, &sin6b->sin6_addr));
358 	default:
359 		dprintf("cmpsockaddr: unsupported af (%d)", addr1->sa_family);
360 		return (B_FALSE);
361 	}
362 }
363 
364 /*
365  * Duplicate a sockaddr. Caller will be responsible for freeing memory when it
366  * is no longer needed. Currently only supports AF_INET and AF_INET6
367  * (returns NULL otherwise).
368  */
369 struct sockaddr *
370 dupsockaddr(const struct sockaddr *addr)
371 {
372 	struct sockaddr_in *t1, *ret1;
373 	struct sockaddr_in6 *t2, *ret2;
374 
375 	switch (addr->sa_family) {
376 	case AF_INET:
377 		if ((ret1 = calloc(1, sizeof (struct sockaddr_in))) == NULL) {
378 			syslog(LOG_ERR, "dupsockaddr: calloc failed");
379 			return (NULL);
380 		}
381 		/* LINTED E_BAD_PTR_CAST_ALIGN */
382 		t1 = (struct sockaddr_in *)addr;
383 		ret1->sin_family = t1->sin_family;
384 		ret1->sin_addr.s_addr = t1->sin_addr.s_addr;
385 		return ((struct sockaddr *)ret1);
386 	case AF_INET6:
387 		if ((ret2 = calloc(1, sizeof (struct sockaddr_in6))) == NULL) {
388 			syslog(LOG_ERR, "dupsockaddr: calloc failed");
389 			return (NULL);
390 		}
391 		/* LINTED E_BAD_PTR_CAST_ALIGN */
392 		t2 = (struct sockaddr_in6 *)addr;
393 		ret2->sin6_family = t2->sin6_family;
394 		(void) memcpy((void *)&ret2->sin6_addr,
395 		    (const void *)&t2->sin6_addr, sizeof (struct in6_addr));
396 		return ((struct sockaddr *)ret2);
397 	default:
398 		dprintf("dupsockaddr: unsupported af (%d)", addr->sa_family);
399 		return (NULL);
400 	}
401 }
402