xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/util.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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 void
71 dprintf(const char *fmt, ...)
72 {
73 	va_list ap;
74 	char vbuf[1024];
75 
76 	va_start(ap, fmt);
77 	if (debug) {
78 		(void) vsnprintf(vbuf, sizeof (vbuf), fmt, ap);
79 		syslog(LOG_DEBUG, "%d: %s", pthread_self(), vbuf);
80 	}
81 	va_end(ap);
82 }
83 
84 boolean_t
85 is_plugged_in(struct interface *i)
86 {
87 	if (i->if_type == IF_WIRELESS)
88 		return (B_TRUE);
89 
90 	return ((get_ifflags(i->if_name, i->if_family) & IFF_RUNNING) != 0);
91 }
92 
93 uint64_t
94 get_ifflags(const char *name, sa_family_t family)
95 {
96 	icfg_if_t intf;
97 	icfg_handle_t h;
98 	uint64_t flags = 0;
99 
100 	(void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
101 	intf.if_protocol = family;
102 
103 	if (icfg_open(&h, &intf) != ICFG_SUCCESS)
104 		return (0);
105 
106 	if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
107 		/*
108 		 * Interfaces can be ripped out from underneath us (for example
109 		 * by DHCP).  We don't want to spam the console for those.
110 		 */
111 		if (errno == ENOENT)
112 			dprintf("get_ifflags: icfg_get_flags failed for '%s'",
113 			    name);
114 		else
115 			syslog(LOG_ERR, "get_ifflags: icfg_get_flags %s af "
116 			    "%d: %m", name, family);
117 		/* just to be sure... */
118 		flags = 0;
119 	}
120 	icfg_close(h);
121 
122 	return (flags);
123 }
124 
125 /*
126  *
127  * This starts a child process determined by command.  If command contains a
128  * slash then it is assumed to be a full path; otherwise the path is searched
129  * for an executable file with the name command.  Command is also used as
130  * argv[0] of the new process.  The rest of the arguments of the function
131  * up to the first NULL make up pointers to arguments of the new process.
132  *
133  * This function returns child exit status on success and -1 on failure.
134  *
135  * NOTE: original_sigmask must be set before this function is called.
136  */
137 int
138 start_childv(const char *command, char const * const *argv)
139 {
140 	posix_spawnattr_t attr;
141 	sigset_t fullset;
142 	int i, rc, status, n;
143 	pid_t pid;
144 	char vbuf[1024];
145 
146 	vbuf[0] = 0;
147 	n = sizeof (vbuf);
148 	for (i = 1; argv[i] != NULL && n > 2; i++) {
149 		n -= strlcat(vbuf, " ", n);
150 		n -= strlcat(vbuf, argv[i], n);
151 	}
152 	if (argv[i] != NULL || n < 0)
153 		syslog(LOG_ERR, "start_childv can't log full arg vector");
154 
155 	if ((rc = posix_spawnattr_init(&attr)) != 0) {
156 		dprintf("posix_spawnattr_init %d %s\n", rc, strerror(rc));
157 		return (-1);
158 	}
159 	(void) sigfillset(&fullset);
160 	if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
161 		dprintf("setsigdefault %d %s\n", rc, strerror(rc));
162 		return (-1);
163 	}
164 	if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
165 		dprintf("setsigmask %d %s\n", rc, strerror(rc));
166 		return (-1);
167 	}
168 	if ((rc = posix_spawnattr_setflags(&attr,
169 	    POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
170 		dprintf("setflags %d %s\n", rc, strerror(rc));
171 		return (-1);
172 	}
173 
174 	if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
175 	    environ)) > 0) {
176 		dprintf("posix_spawnp failed errno %d", rc);
177 		return (-1);
178 	}
179 
180 	if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
181 		dprintf("posix_spawn_attr_destroy %d %s\n", rc, strerror(rc));
182 		return (-1);
183 	}
184 
185 	(void) waitpid(pid, &status, 0);
186 	if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
187 		i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
188 		syslog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
189 		    (WIFSIGNALED(status) ? "terminated" : "stopped"), i,
190 		    strsignal(i));
191 		return (-2);
192 	} else {
193 		syslog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
194 		    WEXITSTATUS(status));
195 		return (WEXITSTATUS(status));
196 	}
197 }
198 
199 int
200 start_child(const char *command, ...)
201 {
202 	const char **argv = NULL;
203 	int argv_len = 0;
204 	va_list ap;
205 	int i = 1, rc;
206 
207 	va_start(ap, command);
208 	do {
209 		if (i >= argv_len) {
210 			void *p;
211 
212 			argv_len = argv_len != 0 ? argv_len * 2 : 4;
213 			p = realloc(argv, sizeof (*argv)*argv_len);
214 			if (p != NULL) {
215 				argv = p;
216 			} else {
217 				syslog(LOG_ERR, "Out of memory in start_child");
218 				free(argv);
219 				return (-1);
220 			}
221 		}
222 
223 		argv[i] = va_arg(ap, const char *);
224 	} while (argv[i++] != NULL);
225 	va_end(ap);
226 	argv[0] = command;
227 
228 	rc = start_childv(command, argv);
229 	free(argv);
230 
231 	return (rc);
232 }
233 
234 uint32_t	timer_expire = TIMER_INFINITY;
235 
236 /*
237  * Schedules a SIGALRM in delay seconds, unless one is already
238  * scheduled sooner.  If one is already scheduled later than
239  * delay seconds from now, that one will be replaced.
240  */
241 void
242 start_timer(uint32_t now, uint32_t delay)
243 {
244 	if (now + delay > timer_expire)
245 		return;
246 
247 	timer_expire = now + delay;
248 	(void) alarm(delay);
249 }
250 
251 boolean_t
252 valid_graphical_user(boolean_t query)
253 {
254 	struct utmpx *utp;
255 	char *user = NULL;
256 	const char HOMESTR[] = "HOME=";
257 	char buf[1024]; /* == sysconf(_SC_GETPW_R_SIZE_MAX) == NSS_BUFSIZ */
258 	static char home_dir[PATH_MAX + sizeof (HOMESTR)];
259 	struct passwd passwd;
260 	struct passwd *pw;
261 	boolean_t popup_ok;
262 
263 	/*
264 	 * Check to see if our SMF property says popups are OK.
265 	 */
266 	if ((lookup_boolean_property(OUR_PG, query ? "popup_query" :
267 	    "popup_info", &popup_ok) == 0) && !popup_ok)
268 		return (B_FALSE);
269 
270 	/*
271 	 * Look for someone logged into the console from host ":0" (i.e.,
272 	 * the X display.  Down the road, we should generalize this so
273 	 * ":0" is not hard-coded.  Note that the entry we want is usually
274 	 * an ordinary user process but sometimes if a session leader has
275 	 * exited, it can come from a DEAD_PROCESS, as is known to happen
276 	 * when the user logs in via gdm(1m).
277 	 */
278 	setutxent();
279 	while ((utp = getutxent()) != NULL) {
280 		if (((utp->ut_type == USER_PROCESS) ||
281 		    (utp->ut_type == DEAD_PROCESS)) &&
282 		    (strcmp(utp->ut_line, "console") == 0) &&
283 		    (strcmp(utp->ut_host, ":0") == 0)) {
284 			user = strdup(utp->ut_user);
285 			break;
286 		}
287 	}
288 	endutxent();
289 	dprintf("utmpx: done %s", user != NULL ? user : "");
290 
291 	if (user == NULL)
292 		return (B_FALSE);
293 
294 	pw = getpwnam_r(user, &passwd, buf, sizeof (buf));
295 	if (pw == NULL) {
296 		syslog(LOG_ERR, "couldn't get user %s: %m", user);
297 		free(user);
298 		return (B_FALSE);
299 	}
300 	free(user);
301 
302 	/*
303 	 * We shouldn't be dumping this into our environment or changing
304 	 * our uid/gid but instead starting up the zenity processes with
305 	 * this display as this user.  RFE to change this.
306 	 */
307 	(void) putenv("DISPLAY=:0.0");
308 
309 	(void) strlcpy(home_dir, HOMESTR, sizeof (home_dir));
310 	(void) strlcat(home_dir, pw->pw_dir, sizeof (home_dir));
311 	(void) putenv(home_dir);
312 
313 	return (pw != NULL);
314 }
315 
316 void
317 lookup_zonename(char *zonename, size_t zonesize)
318 {
319 	zoneid_t zoneid = getzoneid();
320 
321 	if (getzonenamebyid(zoneid, zonename, zonesize) >= 0)
322 		return;
323 	syslog(LOG_ERR, "could not determine zone name");
324 	(void) strlcpy(zonename, GLOBAL_ZONENAME, zonesize);
325 }
326