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