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
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.
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
32 * utmpx.c - utmpx utility routines
34 * Since svc.startd(1M) places utmpx records for its launched instances, it must
35 * also mark them as dead once completed.
39 #include <sys/types.h>
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 };
57 static pthread_mutex_t utmpx_lock
;
58 static int utmpx_truncated
= 0;
60 #define USEC_PER_MSEC 1000
63 utmpx_mark_init(pid_t pid
, char *prefix
)
65 struct utmpx ut
, *oldu
;
69 while (st
->st_initial
&& !utmpx_truncated
)
70 (void) usleep(200 * USEC_PER_MSEC
);
73 * Clean out any preexisting records for this PID, as they must be
76 utmpx_mark_dead(pid
, 0, B_TRUE
);
79 * Construct a new record with the appropriate prefix.
81 (void) memset(&ut
, 0, sizeof (ut
));
82 (void) strncpy(ut
.ut_user
, ".startd", sizeof (ut
.ut_user
));
85 ut
.ut_id
[0] = ut
.ut_id
[1] = ut
.ut_id
[2] = ut
.ut_id
[3] = (char)SC_WILDC
;
87 for (ret
= 0; ret
< strlen(prefix
); ret
++)
88 ut
.ut_id
[ret
] = prefix
[ret
];
90 ut
.ut_type
= INIT_PROCESS
;
91 (void) time(&ut
.ut_tv
.tv_sec
);
94 MUTEX_LOCK(&utmpx_lock
);
97 if ((oldu
= getutxid(&ut
)) != NULL
) {
99 * Copy in the old "line" and "host" fields.
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;
107 if (makeutx(&ut
) != NULL
)
111 log_framework(LOG_WARNING
,
112 "makeutx failed, retrying: %s\n", strerror(errno
));
114 MUTEX_UNLOCK(&utmpx_lock
);
119 updwtmpx(WTMPX_FILE
, &ut
);
122 MUTEX_UNLOCK(&utmpx_lock
);
128 utmpx_mark_dead(pid_t pid
, int status
, boolean_t blocking
)
136 MUTEX_LOCK(&utmpx_lock
);
139 while (up
= getutxent()) {
140 if (up
->ut_pid
== pid
) {
143 if (up
->ut_type
== DEAD_PROCESS
) {
145 * Cleaned up elsewhere.
148 MUTEX_UNLOCK(&utmpx_lock
);
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
);
157 if (pututxline(up
) != NULL
) {
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.
163 updwtmpx(WTMPX_FILE
, up
);
165 MUTEX_UNLOCK(&utmpx_lock
);
173 MUTEX_UNLOCK(&utmpx_lock
);
175 if (!found
|| !blocking
)
179 log_framework(LOG_INFO
, "retrying utmpx_dead on PID "
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
|
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
|
205 * Retrieve the runlevel utmpx entry if there is one; used to recover
206 * state when svc.startd is restarted.
209 utmpx_get_runlevel(void)
214 MUTEX_LOCK(&utmpx_lock
);
217 while (up
= getutxent()) {
218 if (up
->ut_type
== RUN_LVL
&&
219 sscanf(up
->ut_line
, RUNLVL_MSG
, &rl
) == 1)
223 MUTEX_UNLOCK(&utmpx_lock
);
229 utmpx_set_runlevel(char runlevel
, char oldrl
, boolean_t do_bump
)
241 bzero(&u
, sizeof (struct utmpx
));
243 u
.ut_id
[0] = u
.ut_id
[1] = u
.ut_id
[2] = u
.ut_id
[3] = '\0';
247 (void) time(&u
.ut_tv
.tv_sec
);
249 MUTEX_LOCK(&utmpx_lock
);
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
));
257 tmplen
= strlen(u
.ut_host
);
259 u
.ut_syslen
= min(tmplen
+ 1, sizeof (u
.ut_host
));
265 u
.ut_exit
.e_exit
= oldrl
;
266 else if (oup
!= NULL
)
267 u
.ut_exit
.e_exit
= oup
->ut_exit
.e_termination
;
269 u
.ut_exit
.e_exit
= '0';
271 u
.ut_exit
.e_termination
= runlevel
;
273 for (i
= 0; rlevels
[i
] != '\0'; ++i
) {
274 if (rlevels
[i
] == runlevel
)
278 u
.ut_pid
= n_prev
[i
];
281 for (i
= 0; rlevels
[i
] != '\0'; ++i
) {
282 if (rlevels
[i
] == u
.ut_exit
.e_exit
)
289 (void) sprintf(u
.ut_line
, RUNLVL_MSG
, runlevel
);
291 if (pututxline(&u
) == NULL
) {
293 MUTEX_UNLOCK(&utmpx_lock
);
298 updwtmpx(WTMPX_FILE
, &u
);
301 MUTEX_UNLOCK(&utmpx_lock
);
307 utmpx_write_entry(short type
, const char *msg
, time_t tstamp
)
313 bzero(&u
, sizeof (struct utmpx
));
315 u
.ut_id
[0] = u
.ut_id
[1] = u
.ut_id
[2] = u
.ut_id
[3] = '\0';
318 u
.ut_exit
.e_termination
= WTERMSIG(0);
319 u
.ut_exit
.e_exit
= WEXITSTATUS(0);
321 u
.ut_tv
.tv_sec
= tstamp
;
323 MUTEX_LOCK(&utmpx_lock
);
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
));
331 tmplen
= strlen(u
.ut_host
);
333 u
.ut_syslen
= min(tmplen
+ 1, sizeof (u
.ut_host
));
338 (void) sprintf(u
.ut_line
, "%.12s", msg
);
340 if (pututxline(&u
) == NULL
) {
342 MUTEX_UNLOCK(&utmpx_lock
);
347 updwtmpx(WTMPX_FILE
, &u
);
350 MUTEX_UNLOCK(&utmpx_lock
);
356 utmpx_write_boottime(void)
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
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
);
379 * The boot time (or start time, for a non-global zone) is retrieved in
382 tstamp
= st
->st_start_time
.tv_sec
;
384 utmpx_write_entry(BOOT_TIME
, BOOT_MSG
, tstamp
);
388 * void utmpx_clear_old(void)
389 * At boot and only at boot, truncate the utmpx file.
393 utmpx_clear_old(void)
396 mode_t mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
398 if (!st
->st_initial
|| utmpx_truncated
)
401 MUTEX_LOCK(&utmpx_lock
);
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 */
409 log_framework(LOG_NOTICE
, "Unable to create %s: %s\n",
410 _UTMPX_FILE
, strerror(errno
));
415 MUTEX_UNLOCK(&utmpx_lock
);
421 (void) pthread_mutex_init(&utmpx_lock
, &mutex_attrs
);
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.
432 MUTEX_LOCK(&utmpx_lock
);
438 MUTEX_UNLOCK(&utmpx_lock
);