]>
Commit | Line | Data |
---|---|---|
e537352b LP |
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2010 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <utmpx.h> | |
23 | #include <errno.h> | |
24 | #include <assert.h> | |
25 | #include <string.h> | |
26 | #include <sys/utsname.h> | |
27 | ||
28 | #include "macro.h" | |
29 | #include "utmp-wtmp.h" | |
30 | ||
31 | int utmp_get_runlevel(int *runlevel, int *previous) { | |
32 | struct utmpx lookup, *found; | |
33 | int r; | |
34 | const char *e; | |
35 | ||
36 | assert(runlevel); | |
37 | ||
38 | /* If these values are set in the environment this takes | |
39 | * precedence. Presumably, sysvinit does this to work around a | |
40 | * race condition that would otherwise exist where we'd always | |
41 | * go to disk and hence might read runlevel data that might be | |
42 | * very new and does not apply to the current script being | |
43 | * executed. */ | |
44 | ||
45 | if ((e = getenv("RUNLEVEL")) && e[0] > 0) { | |
46 | *runlevel = e[0]; | |
47 | ||
48 | if (previous) { | |
49 | /* $PREVLEVEL seems to be an Upstart thing */ | |
50 | ||
51 | if ((e = getenv("PREVLEVEL")) && e[0] > 0) | |
52 | *previous = e[0]; | |
53 | else | |
54 | *previous = 0; | |
55 | } | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | if (utmpxname(_PATH_UTMPX) < 0) | |
61 | return -errno; | |
62 | ||
63 | setutxent(); | |
64 | ||
65 | zero(lookup); | |
66 | lookup.ut_type = RUN_LVL; | |
67 | ||
68 | if (!(found = getutxid(&lookup))) | |
69 | r = -errno; | |
70 | else { | |
71 | int a, b; | |
72 | ||
73 | a = found->ut_pid & 0xFF; | |
74 | b = (found->ut_pid >> 8) & 0xFF; | |
75 | ||
76 | if (a < 0 || b < 0) | |
77 | r = -EIO; | |
78 | else { | |
79 | *runlevel = a; | |
80 | ||
81 | if (previous) | |
82 | *previous = b; | |
83 | r = 0; | |
84 | } | |
85 | } | |
86 | ||
87 | endutxent(); | |
88 | ||
89 | return r; | |
90 | } | |
91 | ||
92 | static void init_entry(struct utmpx *store, usec_t timestamp) { | |
93 | struct utsname uts; | |
94 | ||
95 | assert(store); | |
96 | ||
97 | zero(*store); | |
98 | zero(uts); | |
99 | ||
100 | if (timestamp <= 0) | |
101 | timestamp = now(CLOCK_REALTIME); | |
102 | ||
103 | store->ut_tv.tv_sec = timestamp / USEC_PER_SEC; | |
104 | store->ut_tv.tv_usec = timestamp % USEC_PER_SEC; | |
105 | ||
106 | if (uname(&uts) >= 0) | |
107 | strncpy(store->ut_host, uts.release, sizeof(store->ut_host)); | |
108 | ||
109 | strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */ | |
110 | strncpy(store->ut_id, "~~", sizeof(store->ut_id)); | |
111 | } | |
112 | ||
113 | static int write_entry_utmp(const struct utmpx *store) { | |
114 | int r; | |
115 | ||
116 | assert(store); | |
117 | ||
118 | /* utmp is similar to wtmp, but there is only one entry for | |
119 | * each entry type resp. user; i.e. basically a key/value | |
120 | * table. */ | |
121 | ||
122 | if (utmpxname(_PATH_UTMPX) < 0) | |
123 | return -errno; | |
124 | ||
125 | setutxent(); | |
126 | ||
127 | if (!pututxline(store)) | |
128 | r = -errno; | |
129 | else | |
130 | r = 0; | |
131 | ||
132 | endutxent(); | |
133 | ||
134 | return r; | |
135 | } | |
136 | ||
137 | static int write_entry_wtmp(const struct utmpx *store) { | |
138 | assert(store); | |
139 | ||
140 | /* wtmp is a simple append-only file where each entry is | |
141 | simply appended to * the end; i.e. basically a log. */ | |
142 | ||
143 | errno = 0; | |
144 | updwtmpx(_PATH_WTMPX, store); | |
145 | return -errno; | |
146 | } | |
147 | ||
148 | static int write_entry_both(const struct utmpx *store) { | |
149 | int r, s; | |
150 | ||
151 | r = write_entry_utmp(store); | |
152 | s = write_entry_wtmp(store); | |
153 | ||
154 | if (r >= 0) | |
155 | r = s; | |
156 | ||
157 | /* If utmp/wtmp have been disabled, that's a good thing, hence | |
158 | * ignore the errors */ | |
159 | if (r == -ENOENT) | |
160 | r = 0; | |
161 | ||
162 | return r; | |
163 | } | |
164 | ||
165 | int utmp_put_shutdown(usec_t timestamp) { | |
166 | struct utmpx store; | |
167 | ||
168 | init_entry(&store, timestamp); | |
169 | ||
170 | store.ut_type = RUN_LVL; | |
171 | strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); | |
172 | ||
173 | return write_entry_both(&store); | |
174 | } | |
175 | ||
176 | int utmp_put_reboot(usec_t timestamp) { | |
177 | struct utmpx store; | |
178 | ||
179 | init_entry(&store, timestamp); | |
180 | ||
181 | store.ut_type = BOOT_TIME; | |
55e39f40 | 182 | strncpy(store.ut_user, "reboot", sizeof(store.ut_user)); |
e537352b LP |
183 | |
184 | return write_entry_both(&store); | |
185 | } | |
186 | ||
187 | int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous) { | |
188 | struct utmpx store; | |
189 | int r; | |
190 | ||
191 | assert(runlevel > 0); | |
192 | ||
193 | if (previous <= 0) { | |
194 | /* Find the old runlevel automatically */ | |
195 | ||
d7fc909d LP |
196 | if ((r = utmp_get_runlevel(&previous, NULL)) < 0) { |
197 | if (r != -ESRCH) | |
198 | return r; | |
199 | ||
200 | previous = 0; | |
201 | } | |
e537352b LP |
202 | |
203 | if (previous == runlevel) | |
204 | return 0; | |
205 | } | |
206 | ||
207 | init_entry(&store, timestamp); | |
208 | ||
209 | store.ut_type = RUN_LVL; | |
210 | store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); | |
211 | strncpy(store.ut_user, "runlevel", sizeof(store.ut_user)); | |
212 | ||
213 | return write_entry_both(&store); | |
214 | } |