3
* utmp.c - utmp and wtmp handling
5
* Copyright © 2009 Canonical Ltd.
6
* Author: Scott James Remnant <scott@netsplit.com>.
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License version 2, as
10
* published by the Free Software Foundation.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License along
18
* with this program; if not, write to the Free Software Foundation, Inc.,
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
#endif /* HAVE_CONFIG_H */
28
#include <sys/utsname.h>
34
#include <nih/macros.h>
35
#include <nih/alloc.h>
36
#include <nih/logging.h>
37
#include <nih/error.h>
42
/* Prototypes for static functions */
43
static void utmp_entry (struct utmpx *utmp, short type, pid_t pid,
44
const char *line, const char *id,
46
static int utmp_write (const char *utmp_file, const struct utmpx *utmp)
47
__attribute__ ((warn_unused_result));
48
static void wtmp_write (const char *wtmp_file, const struct utmpx *utmp);
54
* The sysvinit last utility expects a special "shutdown" RUN_LVL entry,
55
* and abuses the type to distinguish that. We'll do the same.
57
#define SHUTDOWN_TIME 254
62
* @utmp_file: utmp or wtmp file to read from,
63
* @prevlevel: pointer to store previous runlevel in.
65
* Reads the the most recent runlevel entry from @utmp_file, returning
66
* the runlevel from it. If @prevlevel is not NULL, the previous runlevel
67
* will be stored in that variable.
69
* @utmp_file may be either a utmp or wtmp file, if NULL the default
70
* /var/run/utmp is used.
72
* Returns: runlevel on success, negative value on raised error.
75
utmp_read_runlevel (const char *utmp_file,
82
memset (&utmp, 0, sizeof utmp);
83
utmp.ut_type = RUN_LVL;
85
utmpxname (utmp_file ?: _PATH_UTMPX);
88
lvl = getutxid (&utmp);
90
nih_error_raise_system ();
95
runlevel = lvl->ut_pid % 256;
97
*prevlevel = lvl->ut_pid / 256 ?: 'N';
106
* @utmp_file: utmp or wtmp file to read from,
107
* @prevlevel: pointer to store previous runlevel in.
109
* If the RUNLEVEL and PREVLEVEL environment variables are set, returns
110
* the current and previous runlevels from those otherwise calls
111
* utmp_read_runlevel() to read the most recent runlevel entry from
114
* Returns: runlevel on success, negative value on raised error.
117
utmp_get_runlevel (const char *utmp_file,
123
renv = getenv ("RUNLEVEL");
124
penv = getenv ("PREVLEVEL");
126
if (renv && renv[0]) {
128
*prevlevel = penv && penv[0] ? penv[0] : 'N';
133
return utmp_read_runlevel (utmp_file, prevlevel);
138
* utmp_write_runlevel:
139
* @utmp_file: utmp file,
140
* @wtmp_file: wtmp file,
141
* @runlevel: new runlevel,
142
* @prevlevel: previous runlevel.
144
* Write a runlevel change record from @prevlevel to @runlevel to @utmp_file,
145
* or /var/run/utmp if @utmp_file is NULL, and to @wtmp_file, or /var/log/wtmp
146
* if @wtmp_file is NULL.
148
* Errors writing to the wtmp file are ignored.
150
* Returns: zero on success, negative value on raised error.
153
utmp_write_runlevel (const char *utmp_file,
154
const char *wtmp_file,
163
nih_assert (runlevel > 0);
164
nih_assert (prevlevel > 0);
166
utmp_entry (&reboot, BOOT_TIME, 0, NULL, NULL, NULL);
167
utmp_entry (&utmp, RUN_LVL, runlevel + prevlevel * 256,
170
/* Check for the previous runlevel entry in utmp, if it doesn't
171
* match then we assume a missed reboot so write the boot time
174
savedlevel = utmp_read_runlevel (utmp_file, NULL);
175
if (savedlevel != prevlevel) {
177
nih_free (nih_error_get ());
179
if (utmp_write (utmp_file, &reboot) < 0)
180
nih_free (nih_error_get ());
183
/* Check for the previous runlevel entry in wtmp, if it doesn't
184
* match then we assume a missed reboot so write the boot time
187
savedlevel = utmp_read_runlevel (wtmp_file, NULL);
188
if (savedlevel != prevlevel) {
190
nih_free (nih_error_get ());
192
wtmp_write (wtmp_file, &reboot);
195
/* Write the runlevel change record */
196
ret = utmp_write (utmp_file, &utmp);
197
wtmp_write (wtmp_file, &utmp);
204
* utmp_write_shutdown:
205
* @utmp_file: utmp file to write to,
206
* @wtmp_file: wtmp file to write to.
208
* Write a shutdown utmp record to @utmp_file, or /var/run/utmp if
209
* @utmp_file is NULL, and to @wtmp_file, or /var/log/wtmp if @wtmp_file
212
* Errors writing to the wtmp file are ignored.
214
* Returns: zero on success, negative value on raised error.
217
utmp_write_shutdown (const char *utmp_file,
218
const char *wtmp_file)
223
utmp_entry (&utmp, SHUTDOWN_TIME, 0, NULL, NULL, NULL);
225
ret = utmp_write (utmp_file, &utmp);
226
wtmp_write (wtmp_file, &utmp);
234
* @utmp: utmp entry to fill,
235
* @type: type of record,
236
* @pid: process id of login process,
237
* @line: device name of tty,
238
* @id: terminal name suffix,
241
* Fill the utmp entry @utmp with the details passed, setting the auxiliary
242
* information such as host and time to sensible defaults. Depending on
243
* @type, the other arguments may be ignored.
245
* When @type is BOOT_TIME, or the special SHUTDOWN_TIME, all arguments
246
* are ignored. When @type is RUN_LVL, the @line, @id and @user lines are
249
* Any existing values in @utmp before this call will be lost.
252
utmp_entry (struct utmpx *utmp,
262
nih_assert (utmp != NULL);
263
nih_assert (type != EMPTY);
280
nih_assert (pid != 0);
286
nih_assert (line != NULL);
287
nih_assert (id != NULL);
288
nih_assert (user != NULL);
291
memset (utmp, 0, sizeof (struct utmpx));
293
utmp->ut_type = type;
296
strncpy (utmp->ut_line, line, sizeof utmp->ut_line);
297
strncpy (utmp->ut_id, id, sizeof utmp->ut_id);
298
strncpy (utmp->ut_user, user, sizeof utmp->ut_user);
300
if (uname (&uts) == 0)
301
strncpy (utmp->ut_host, uts.release, sizeof utmp->ut_host);
303
gettimeofday (&tv, NULL);
304
utmp->ut_tv.tv_sec = tv.tv_sec;
305
utmp->ut_tv.tv_usec = tv.tv_usec;
310
* @utmp_file: utmp file to write to,
311
* @utmp: utmp entry to write.
313
* Write the utmp entry @utmp to @utmp_file, or /var/run/utmp if @utmp_file
316
* Returns: zero on success, negative value on raised error.
319
utmp_write (const char * utmp_file,
320
const struct utmpx *utmp)
322
nih_assert (utmp != NULL);
324
utmpxname (utmp_file ?: _PATH_UTMPX);
328
if (! pututxline (utmp)) {
329
nih_error_raise_system ();
341
* @wtmp_file: wtmp file to write to,
342
* @utmp: utmp entry to write.
344
* Write the utmp entry @utmp to @wtmp_file, or /var/log/wtmp if @utmp_file
348
wtmp_write (const char * wtmp_file,
349
const struct utmpx *utmp)
351
nih_assert (utmp != NULL);
353
updwtmpx (wtmp_file ?: _PATH_WTMPX, utmp);