~xnox/upstart/read-vs-sigchild

« back to all changes in this revision

Viewing changes to util/utmp.c

  • Committer: Scott James Remnant
  • Date: 2009-07-08 14:42:15 UTC
  • Revision ID: scott@netsplit.com-20090708144215-efmyqayjqi7v6x6k
* util/utmp.c: Set of functions for dealing with utmp and wtmp
(utmp_read_runlevel): Read the current runlevel from the file
(utmp_get_runlevel): Obtain the current runlevel from the
environment, or the file if not set
(utmp_write_runlevel): Write a runlevel change record, and also
deal with the reboot record if the utmp or wtmp files don't quite
match.
(utmp_write_shutdown): Write a shutdown time record
(utmp_entry, utmp_write, wtmp_write): utility functions for creating
and writing utmp and wtmp records.
* util/utmp.h: Prototypes for new functions.
* util/tests/test_utmp.c: Test cases.
* util/Makefile.am (test_utmp_SOURCES, test_utmp_LDFLAGS)
(test_utmp_LDADD): Details for utmp test cases
(tests): Move to the bottom of the file and make PHONY

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* upstart
 
2
 *
 
3
 * utmp.c - utmp and wtmp handling
 
4
 *
 
5
 * Copyright © 2009 Canonical Ltd.
 
6
 * Author: Scott James Remnant <scott@netsplit.com>.
 
7
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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.
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
# include <config.h>
 
24
#endif /* HAVE_CONFIG_H */
 
25
 
 
26
 
 
27
#include <sys/time.h>
 
28
#include <sys/utsname.h>
 
29
 
 
30
#include <utmpx.h>
 
31
#include <stdlib.h>
 
32
#include <string.h>
 
33
 
 
34
#include <nih/macros.h>
 
35
#include <nih/alloc.h>
 
36
#include <nih/logging.h>
 
37
#include <nih/error.h>
 
38
 
 
39
#include "utmp.h"
 
40
 
 
41
 
 
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,
 
45
                             const char *user);
 
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);
 
49
 
 
50
 
 
51
/**
 
52
 * SHUTDOWN_TIME:
 
53
 *
 
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.
 
56
 **/
 
57
#define SHUTDOWN_TIME 254
 
58
 
 
59
 
 
60
/**
 
61
 * utmp_read_runlevel:
 
62
 * @utmp_file: utmp or wtmp file to read from,
 
63
 * @prevlevel: pointer to store previous runlevel in.
 
64
 *
 
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.
 
68
 *
 
69
 * @utmp_file may be either a utmp or wtmp file, if NULL the default
 
70
 * /var/run/utmp is used.
 
71
 *
 
72
 * Returns: runlevel on success, negative value on raised error.
 
73
 **/
 
74
int
 
75
utmp_read_runlevel (const char *utmp_file,
 
76
                    int *       prevlevel)
 
77
{
 
78
        struct utmpx  utmp;
 
79
        struct utmpx *lvl;
 
80
        int           runlevel;
 
81
 
 
82
        memset (&utmp, 0, sizeof utmp);
 
83
        utmp.ut_type = RUN_LVL;
 
84
 
 
85
        utmpxname (utmp_file ?: _PATH_UTMPX);
 
86
        setutxent ();
 
87
 
 
88
        lvl = getutxid (&utmp);
 
89
        if (! lvl) {
 
90
                nih_error_raise_system ();
 
91
                endutxent ();
 
92
                return -1;
 
93
        }
 
94
 
 
95
        runlevel = lvl->ut_pid % 256;
 
96
        if (prevlevel)
 
97
                *prevlevel = lvl->ut_pid / 256 ?: 'N';
 
98
 
 
99
        endutxent ();
 
100
 
 
101
        return runlevel;
 
102
}
 
103
 
 
104
/**
 
105
 * utmp_get_runlevel:
 
106
 * @utmp_file: utmp or wtmp file to read from,
 
107
 * @prevlevel: pointer to store previous runlevel in.
 
108
 *
 
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
 
112
 * @utmp_file.
 
113
 *
 
114
 * Returns: runlevel on success, negative value on raised error.
 
115
 **/
 
116
int
 
117
utmp_get_runlevel (const char *utmp_file,
 
118
                   int *       prevlevel)
 
119
{
 
120
        const char *renv;
 
121
        const char *penv;
 
122
 
 
123
        renv = getenv ("RUNLEVEL");
 
124
        penv = getenv ("PREVLEVEL");
 
125
 
 
126
        if (renv && renv[0]) {
 
127
                if (prevlevel)
 
128
                        *prevlevel = penv && penv[0] ? penv[0] : 'N';
 
129
 
 
130
                return renv[0];
 
131
        }
 
132
 
 
133
        return utmp_read_runlevel (utmp_file, prevlevel);
 
134
}
 
135
 
 
136
 
 
137
/**
 
138
 * utmp_write_runlevel:
 
139
 * @utmp_file: utmp file,
 
140
 * @wtmp_file: wtmp file,
 
141
 * @runlevel: new runlevel,
 
142
 * @prevlevel: previous runlevel.
 
143
 *
 
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.
 
147
 *
 
148
 * Errors writing to the wtmp file are ignored.
 
149
 *
 
150
 * Returns: zero on success, negative value on raised error.
 
151
 **/
 
152
int
 
153
utmp_write_runlevel (const char *utmp_file,
 
154
                     const char *wtmp_file,
 
155
                     int         runlevel,
 
156
                     int         prevlevel)
 
157
{
 
158
        struct utmpx reboot;
 
159
        struct utmpx utmp;
 
160
        int          savedlevel;
 
161
        int          ret;
 
162
 
 
163
        nih_assert (runlevel > 0);
 
164
        nih_assert (prevlevel > 0);
 
165
 
 
166
        utmp_entry (&reboot, BOOT_TIME, 0, NULL, NULL, NULL);
 
167
        utmp_entry (&utmp, RUN_LVL, runlevel + prevlevel * 256,
 
168
                    NULL, NULL, NULL);
 
169
 
 
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
 
172
         * record out first.
 
173
         */
 
174
        savedlevel = utmp_read_runlevel (utmp_file, NULL);
 
175
        if (savedlevel != prevlevel) {
 
176
                if (savedlevel < 0)
 
177
                        nih_free (nih_error_get ());
 
178
 
 
179
                if (utmp_write (utmp_file, &reboot) < 0)
 
180
                        nih_free (nih_error_get ());
 
181
        }
 
182
 
 
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
 
185
         * record out first.
 
186
         */
 
187
        savedlevel = utmp_read_runlevel (wtmp_file, NULL);
 
188
        if (savedlevel != prevlevel) {
 
189
                if (savedlevel < 0)
 
190
                        nih_free (nih_error_get ());
 
191
 
 
192
                wtmp_write (wtmp_file, &reboot);
 
193
        }
 
194
 
 
195
        /* Write the runlevel change record */
 
196
        ret = utmp_write (utmp_file, &utmp);
 
197
        wtmp_write (wtmp_file, &utmp);
 
198
 
 
199
        return ret;
 
200
}
 
201
 
 
202
 
 
203
/**
 
204
 * utmp_write_shutdown:
 
205
 * @utmp_file: utmp file to write to,
 
206
 * @wtmp_file: wtmp file to write to.
 
207
 *
 
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
 
210
 * is NULL.
 
211
 *
 
212
 * Errors writing to the wtmp file are ignored.
 
213
 *
 
214
 * Returns: zero on success, negative value on raised error.
 
215
 **/
 
216
int
 
217
utmp_write_shutdown (const char *utmp_file,
 
218
                     const char *wtmp_file)
 
219
{
 
220
        struct utmpx utmp;
 
221
        int          ret;
 
222
 
 
223
        utmp_entry (&utmp, SHUTDOWN_TIME, 0, NULL, NULL, NULL);
 
224
 
 
225
        ret = utmp_write (utmp_file, &utmp);
 
226
        wtmp_write (wtmp_file, &utmp);
 
227
 
 
228
        return ret;
 
229
}
 
230
 
 
231
 
 
232
/**
 
233
 * utmp_entry:
 
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,
 
239
 * @user: username.
 
240
 *
 
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.
 
244
 *
 
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
 
247
 * ignored.
 
248
 *
 
249
 * Any existing values in @utmp before this call will be lost.
 
250
 **/
 
251
static void
 
252
utmp_entry (struct utmpx *utmp,
 
253
            short         type,
 
254
            pid_t         pid,
 
255
            const char *  line,
 
256
            const char *  id,
 
257
            const char *  user)
 
258
{
 
259
        struct utsname uts;
 
260
        struct timeval tv;
 
261
 
 
262
        nih_assert (utmp != NULL);
 
263
        nih_assert (type != EMPTY);
 
264
 
 
265
        switch (type) {
 
266
        case BOOT_TIME:
 
267
                pid = 0;
 
268
                line = "~";
 
269
                id = "~~";
 
270
                user = "reboot";
 
271
                break;
 
272
        case SHUTDOWN_TIME:
 
273
                type = RUN_LVL;
 
274
                pid = 0;
 
275
                line = "~";
 
276
                id = "~~";
 
277
                user = "shutdown";
 
278
                break;
 
279
        case RUN_LVL:
 
280
                nih_assert (pid != 0);
 
281
                line = "~";
 
282
                id = "~~";
 
283
                user = "runlevel";
 
284
                break;
 
285
        default:
 
286
                nih_assert (line != NULL);
 
287
                nih_assert (id != NULL);
 
288
                nih_assert (user != NULL);
 
289
        }
 
290
 
 
291
        memset (utmp, 0, sizeof (struct utmpx));
 
292
 
 
293
        utmp->ut_type = type;
 
294
        utmp->ut_pid = pid;
 
295
 
 
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);
 
299
 
 
300
        if (uname (&uts) == 0)
 
301
                strncpy (utmp->ut_host, uts.release, sizeof utmp->ut_host);
 
302
 
 
303
        gettimeofday (&tv, NULL);
 
304
        utmp->ut_tv.tv_sec = tv.tv_sec;
 
305
        utmp->ut_tv.tv_usec = tv.tv_usec;
 
306
}
 
307
 
 
308
/**
 
309
 * utmp_write:
 
310
 * @utmp_file: utmp file to write to,
 
311
 * @utmp: utmp entry to write.
 
312
 *
 
313
 * Write the utmp entry @utmp to @utmp_file, or /var/run/utmp if @utmp_file
 
314
 * is NULL.
 
315
 *
 
316
 * Returns: zero on success, negative value on raised error.
 
317
 **/
 
318
static int
 
319
utmp_write (const char *        utmp_file,
 
320
            const struct utmpx *utmp)
 
321
{
 
322
        nih_assert (utmp != NULL);
 
323
 
 
324
        utmpxname (utmp_file ?: _PATH_UTMPX);
 
325
 
 
326
        setutxent ();
 
327
 
 
328
        if (! pututxline (utmp)) {
 
329
                nih_error_raise_system ();
 
330
                endutxent ();
 
331
                return -1;
 
332
        }
 
333
 
 
334
        endutxent ();
 
335
 
 
336
        return 0;
 
337
}
 
338
 
 
339
/**
 
340
 * wtmp_write:
 
341
 * @wtmp_file: wtmp file to write to,
 
342
 * @utmp: utmp entry to write.
 
343
 *
 
344
 * Write the utmp entry @utmp to @wtmp_file, or /var/log/wtmp if @utmp_file
 
345
 * is NULL.
 
346
 **/
 
347
static void
 
348
wtmp_write (const char *        wtmp_file,
 
349
            const struct utmpx *utmp)
 
350
{
 
351
        nih_assert (utmp != NULL);
 
352
 
 
353
        updwtmpx (wtmp_file ?: _PATH_WTMPX, utmp);
 
354
}