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