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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright (c) 2013, Joyent, Inc. All rights reserved. 29 */ 30 31 /* 32 * utmpx.c - utmpx utility routines 33 * 34 * Since svc.startd(1M) places utmpx records for its launched instances, it must 35 * also mark them as dead once completed. 36 */ 37 38 #include <sys/stat.h> 39 #include <sys/types.h> 40 #include <sys/wait.h> 41 #include <sys/stat.h> 42 #include <errno.h> 43 #include <pthread.h> 44 #include <sac.h> 45 #include <string.h> 46 #include <strings.h> 47 #include <time.h> 48 #include <unistd.h> 49 #include <utmpx.h> 50 #include <fcntl.h> 51 52 #include "startd.h" 53 54 static const char rlevels[] = { 'S', '0', '1', '2', '3', '4', '5', '6', 0 }; 55 static int n_prev[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 56 57 static pthread_mutex_t utmpx_lock; 58 static int utmpx_truncated = 0; 59 60 #define USEC_PER_MSEC 1000 61 62 int 63 utmpx_mark_init(pid_t pid, char *prefix) 64 { 65 struct utmpx ut, *oldu; 66 int tmplen; 67 int ret; 68 69 while (st->st_initial && !utmpx_truncated) 70 (void) usleep(200 * USEC_PER_MSEC); 71 72 /* 73 * Clean out any preexisting records for this PID, as they must be 74 * inaccurate. 75 */ 76 utmpx_mark_dead(pid, 0, B_TRUE); 77 78 /* 79 * Construct a new record with the appropriate prefix. 80 */ 81 (void) memset(&ut, 0, sizeof (ut)); 82 (void) strncpy(ut.ut_user, ".startd", sizeof (ut.ut_user)); 83 ut.ut_pid = pid; 84 85 ut.ut_id[0] = ut.ut_id[1] = ut.ut_id[2] = ut.ut_id[3] = (char)SC_WILDC; 86 87 for (ret = 0; ret < strlen(prefix); ret++) 88 ut.ut_id[ret] = prefix[ret]; 89 90 ut.ut_type = INIT_PROCESS; 91 (void) time(&ut.ut_tv.tv_sec); 92 93 for (;;) { 94 MUTEX_LOCK(&utmpx_lock); 95 setutxent(); 96 97 if ((oldu = getutxid(&ut)) != NULL) { 98 /* 99 * Copy in the old "line" and "host" fields. 100 */ 101 bcopy(oldu->ut_line, ut.ut_line, sizeof (ut.ut_line)); 102 bcopy(oldu->ut_host, ut.ut_host, sizeof (ut.ut_host)); 103 ut.ut_syslen = (tmplen = strlen(ut.ut_host)) ? 104 min(tmplen + 1, sizeof (ut.ut_host)) : 0; 105 } 106 107 if (makeutx(&ut) != NULL) 108 break; 109 110 if (errno != EROFS) 111 log_framework(LOG_WARNING, 112 "makeutx failed, retrying: %s\n", strerror(errno)); 113 114 MUTEX_UNLOCK(&utmpx_lock); 115 116 (void) sleep(1); 117 } 118 119 updwtmpx(WTMPX_FILE, &ut); 120 121 endutxent(); 122 MUTEX_UNLOCK(&utmpx_lock); 123 124 return (ret); 125 } 126 127 void 128 utmpx_mark_dead(pid_t pid, int status, boolean_t blocking) 129 { 130 struct utmpx *up; 131 int logged = 0; 132 133 for (;;) { 134 int found = 0; 135 136 MUTEX_LOCK(&utmpx_lock); 137 setutxent(); 138 139 while (up = getutxent()) { 140 if (up->ut_pid == pid) { 141 found = 1; 142 143 if (up->ut_type == DEAD_PROCESS) { 144 /* 145 * Cleaned up elsewhere. 146 */ 147 endutxent(); 148 MUTEX_UNLOCK(&utmpx_lock); 149 return; 150 } 151 152 up->ut_type = DEAD_PROCESS; 153 up->ut_exit.e_termination = WTERMSIG(status); 154 up->ut_exit.e_exit = WEXITSTATUS(status); 155 (void) time(&up->ut_tv.tv_sec); 156 157 if (pututxline(up) != NULL) { 158 /* 159 * Now attempt to add to the end of the 160 * wtmp and wtmpx files. Do not create 161 * if they don't already exist. 162 */ 163 updwtmpx(WTMPX_FILE, up); 164 endutxent(); 165 MUTEX_UNLOCK(&utmpx_lock); 166 167 return; 168 } 169 } 170 } 171 172 endutxent(); 173 MUTEX_UNLOCK(&utmpx_lock); 174 175 if (!found || !blocking) 176 return; 177 178 if (!logged) { 179 log_framework(LOG_INFO, "retrying utmpx_dead on PID " 180 "%ld\n", pid); 181 logged++; 182 } 183 184 (void) sleep(1); 185 } 186 } 187 188 static void 189 utmpx_check() 190 { 191 struct stat sb; 192 193 if (stat(_UTMPX_FILE, &sb) == 0 && 194 sb.st_mode != (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) 195 (void) chmod(_UTMPX_FILE, S_IRUSR | S_IWUSR | S_IRGRP | 196 S_IROTH); 197 198 if (stat(_WTMPX_FILE, &sb) == 0 && 199 sb.st_mode != (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) 200 (void) chmod(_WTMPX_FILE, S_IRUSR | S_IWUSR | S_IRGRP | 201 S_IROTH); 202 } 203 204 /* 205 * Retrieve the runlevel utmpx entry if there is one; used to recover 206 * state when svc.startd is restarted. 207 */ 208 char 209 utmpx_get_runlevel(void) 210 { 211 struct utmpx *up; 212 char rl = '\0'; 213 214 MUTEX_LOCK(&utmpx_lock); 215 setutxent(); 216 217 while (up = getutxent()) { 218 if (up->ut_type == RUN_LVL && 219 sscanf(up->ut_line, RUNLVL_MSG, &rl) == 1) 220 break; 221 } 222 endutxent(); 223 MUTEX_UNLOCK(&utmpx_lock); 224 225 return (rl); 226 } 227 228 void 229 utmpx_set_runlevel(char runlevel, char oldrl, boolean_t do_bump) 230 { 231 struct utmpx u; 232 struct utmpx *oup; 233 size_t tmplen; 234 int i; 235 236 if (runlevel == 's') 237 runlevel = 'S'; 238 if (oldrl == 's') 239 oldrl = 'S'; 240 241 bzero(&u, sizeof (struct utmpx)); 242 243 u.ut_id[0] = u.ut_id[1] = u.ut_id[2] = u.ut_id[3] = '\0'; 244 u.ut_pid = 0; 245 u.ut_type = RUN_LVL; 246 247 (void) time(&u.ut_tv.tv_sec); 248 249 MUTEX_LOCK(&utmpx_lock); 250 setutxent(); 251 252 if ((oup = getutxid(&u)) != NULL) { 253 bcopy(oup->ut_host, u.ut_host, sizeof (u.ut_host)); 254 bcopy(oup->ut_line, u.ut_line, sizeof (u.ut_line)); 255 bcopy(oup->ut_user, u.ut_user, sizeof (u.ut_user)); 256 257 tmplen = strlen(u.ut_host); 258 if (tmplen) 259 u.ut_syslen = min(tmplen + 1, sizeof (u.ut_host)); 260 else 261 u.ut_syslen = 0; 262 } 263 264 if (oldrl != '\0') 265 u.ut_exit.e_exit = oldrl; 266 else if (oup != NULL) 267 u.ut_exit.e_exit = oup->ut_exit.e_termination; 268 else 269 u.ut_exit.e_exit = '0'; 270 271 u.ut_exit.e_termination = runlevel; 272 273 for (i = 0; rlevels[i] != '\0'; ++i) { 274 if (rlevels[i] == runlevel) 275 break; 276 } 277 278 u.ut_pid = n_prev[i]; 279 280 if (do_bump) { 281 for (i = 0; rlevels[i] != '\0'; ++i) { 282 if (rlevels[i] == u.ut_exit.e_exit) 283 break; 284 } 285 286 ++n_prev[i]; 287 } 288 289 (void) sprintf(u.ut_line, RUNLVL_MSG, runlevel); 290 291 if (pututxline(&u) == NULL) { 292 endutxent(); 293 MUTEX_UNLOCK(&utmpx_lock); 294 295 return; 296 } 297 298 updwtmpx(WTMPX_FILE, &u); 299 300 endutxent(); 301 MUTEX_UNLOCK(&utmpx_lock); 302 303 utmpx_check(); 304 } 305 306 static void 307 utmpx_write_entry(short type, const char *msg, time_t tstamp) 308 { 309 struct utmpx u; 310 struct utmpx *oup; 311 size_t tmplen; 312 313 bzero(&u, sizeof (struct utmpx)); 314 315 u.ut_id[0] = u.ut_id[1] = u.ut_id[2] = u.ut_id[3] = '\0'; 316 u.ut_pid = 0; 317 318 u.ut_exit.e_termination = WTERMSIG(0); 319 u.ut_exit.e_exit = WEXITSTATUS(0); 320 u.ut_type = type; 321 u.ut_tv.tv_sec = tstamp; 322 323 MUTEX_LOCK(&utmpx_lock); 324 setutxent(); 325 326 if ((oup = getutxid(&u)) != NULL) { 327 bcopy(oup->ut_user, u.ut_user, sizeof (u.ut_user)); 328 bcopy(oup->ut_line, u.ut_line, sizeof (u.ut_line)); 329 bcopy(oup->ut_host, u.ut_host, sizeof (u.ut_host)); 330 331 tmplen = strlen(u.ut_host); 332 if (tmplen) 333 u.ut_syslen = min(tmplen + 1, sizeof (u.ut_host)); 334 else 335 u.ut_syslen = 0; 336 } 337 338 (void) sprintf(u.ut_line, "%.12s", msg); 339 340 if (pututxline(&u) == NULL) { 341 endutxent(); 342 MUTEX_UNLOCK(&utmpx_lock); 343 344 return; 345 } 346 347 updwtmpx(WTMPX_FILE, &u); 348 349 endutxent(); 350 MUTEX_UNLOCK(&utmpx_lock); 351 352 utmpx_check(); 353 } 354 355 void 356 utmpx_write_boottime(void) 357 { 358 time_t tstamp; 359 struct stat stbuf; 360 361 /* 362 * The DOWN_TIME record tracks when the OS became unavailable 363 * during the previous boot. We stat(2) WTMPX and check its 364 * attributes to determine when (and how) the OS became 365 * unavailable. If the file is empty, skip writing a DOWN_TIME 366 * record. Otherwise, check the access and modify times and 367 * use whichever is latest as the time that the OS became 368 * unavailable. If st_atime is latest, the instance crashed or 369 * the machine lost power. If st_mtime is latest, the shutdown 370 * was controlled. 371 */ 372 if (stat(WTMPX_FILE, &stbuf) == 0 && stbuf.st_size != 0) { 373 tstamp = (stbuf.st_atime >= stbuf.st_mtime) ? 374 stbuf.st_atime : stbuf.st_mtime; 375 utmpx_write_entry(DOWN_TIME, DOWN_MSG, tstamp); 376 } 377 378 /* 379 * The boot time (or start time, for a non-global zone) is retrieved in 380 * log_init(). 381 */ 382 tstamp = st->st_start_time.tv_sec; 383 384 utmpx_write_entry(BOOT_TIME, BOOT_MSG, tstamp); 385 } 386 387 /* 388 * void utmpx_clear_old(void) 389 * At boot and only at boot, truncate the utmpx file. 390 * 391 */ 392 void 393 utmpx_clear_old(void) 394 { 395 int fd; 396 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 397 398 if (!st->st_initial || utmpx_truncated) 399 return; 400 401 MUTEX_LOCK(&utmpx_lock); 402 403 if ((fd = open(_UTMPX_FILE, 404 O_WRONLY | O_CREAT | O_TRUNC, mode)) != -1) { 405 (void) fchmod(fd, mode); /* force mode regardless of umask() */ 406 (void) fchown(fd, 0, 2); /* force owner to root/bin */ 407 (void) close(fd); 408 } else { 409 log_framework(LOG_NOTICE, "Unable to create %s: %s\n", 410 _UTMPX_FILE, strerror(errno)); 411 } 412 413 utmpx_truncated = 1; 414 415 MUTEX_UNLOCK(&utmpx_lock); 416 } 417 418 void 419 utmpx_init() 420 { 421 (void) pthread_mutex_init(&utmpx_lock, &mutex_attrs); 422 } 423 424 void 425 utmpx_prefork() 426 { 427 /* 428 * The libc utmpx routines are entirely MT-unsafe; we must assure 429 * that no other thread is in these routines when we fork lest we 430 * leave the child with inconsistent library state. 431 */ 432 MUTEX_LOCK(&utmpx_lock); 433 } 434 435 void 436 utmpx_postfork() 437 { 438 MUTEX_UNLOCK(&utmpx_lock); 439 } 440