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