chiark / gitweb /
man: Fix minor issues in systemd.unit.xml
[elogind.git] / src / utmp-wtmp.c
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 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/poll.h>
30
31 #include "macro.h"
32 #include "utmp-wtmp.h"
33
34 int utmp_get_runlevel(int *runlevel, int *previous) {
35         struct utmpx lookup, *found;
36         int r;
37         const char *e;
38
39         assert(runlevel);
40
41         /* If these values are set in the environment this takes
42          * precedence. Presumably, sysvinit does this to work around a
43          * race condition that would otherwise exist where we'd always
44          * go to disk and hence might read runlevel data that might be
45          * very new and does not apply to the current script being
46          * executed. */
47
48         if ((e = getenv("RUNLEVEL")) && e[0] > 0) {
49                 *runlevel = e[0];
50
51                 if (previous) {
52                         /* $PREVLEVEL seems to be an Upstart thing */
53
54                         if ((e = getenv("PREVLEVEL")) && e[0] > 0)
55                                 *previous = e[0];
56                         else
57                                 *previous = 0;
58                 }
59
60                 return 0;
61         }
62
63         if (utmpxname(_PATH_UTMPX) < 0)
64                 return -errno;
65
66         setutxent();
67
68         zero(lookup);
69         lookup.ut_type = RUN_LVL;
70
71         if (!(found = getutxid(&lookup)))
72                 r = -errno;
73         else {
74                 int a, b;
75
76                 a = found->ut_pid & 0xFF;
77                 b = (found->ut_pid >> 8) & 0xFF;
78
79                 if (a < 0 || b < 0)
80                         r = -EIO;
81                 else {
82                         *runlevel = a;
83
84                         if (previous)
85                                 *previous = b;
86                         r = 0;
87                 }
88         }
89
90         endutxent();
91
92         return r;
93 }
94
95 static void init_entry(struct utmpx *store, usec_t t) {
96         struct utsname uts;
97
98         assert(store);
99
100         zero(*store);
101         zero(uts);
102
103         if (t <= 0)
104                 t = now(CLOCK_REALTIME);
105
106         store->ut_tv.tv_sec = t / USEC_PER_SEC;
107         store->ut_tv.tv_usec = t % USEC_PER_SEC;
108
109         if (uname(&uts) >= 0)
110                 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
111
112         strncpy(store->ut_line, "~", sizeof(store->ut_line));  /* or ~~ ? */
113         strncpy(store->ut_id, "~~", sizeof(store->ut_id));
114 }
115
116 static int write_entry_utmp(const struct utmpx *store) {
117         int r;
118
119         assert(store);
120
121         /* utmp is similar to wtmp, but there is only one entry for
122          * each entry type resp. user; i.e. basically a key/value
123          * table. */
124
125         if (utmpxname(_PATH_UTMPX) < 0)
126                 return -errno;
127
128         setutxent();
129
130         if (!pututxline(store))
131                 r = -errno;
132         else
133                 r = 0;
134
135         endutxent();
136
137         return r;
138 }
139
140 static int write_entry_wtmp(const struct utmpx *store) {
141         assert(store);
142
143         /* wtmp is a simple append-only file where each entry is
144         simply appended to * the end; i.e. basically a log. */
145
146         errno = 0;
147         updwtmpx(_PATH_WTMPX, store);
148         return -errno;
149 }
150
151 static int write_entry_both(const struct utmpx *store) {
152         int r, s;
153
154         r = write_entry_utmp(store);
155         s = write_entry_wtmp(store);
156
157         if (r >= 0)
158                 r = s;
159
160         /* If utmp/wtmp have been disabled, that's a good thing, hence
161          * ignore the errors */
162         if (r == -ENOENT)
163                 r = 0;
164
165         return r;
166 }
167
168 int utmp_put_shutdown(usec_t t) {
169         struct utmpx store;
170
171         init_entry(&store, t);
172
173         store.ut_type = RUN_LVL;
174         strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
175
176         return write_entry_both(&store);
177 }
178
179 int utmp_put_reboot(usec_t t) {
180         struct utmpx store;
181
182         init_entry(&store, t);
183
184         store.ut_type = BOOT_TIME;
185         strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
186
187         return write_entry_both(&store);
188 }
189
190 int utmp_put_runlevel(usec_t t, int runlevel, int previous) {
191         struct utmpx store;
192         int r;
193
194         assert(runlevel > 0);
195
196         if (previous <= 0) {
197                 /* Find the old runlevel automatically */
198
199                 if ((r = utmp_get_runlevel(&previous, NULL)) < 0) {
200                         if (r != -ESRCH)
201                                 return r;
202
203                         previous = 0;
204                 }
205
206                 if (previous == runlevel)
207                         return 0;
208         }
209
210         init_entry(&store, t);
211
212         store.ut_type = RUN_LVL;
213         store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
214         strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
215
216         return write_entry_both(&store);
217 }
218
219 #define TIMEOUT_MSEC 50
220
221 static int write_to_terminal(const char *tty, const char *message) {
222         int fd, r;
223         const char *p;
224         size_t left;
225         usec_t end;
226
227         assert(tty);
228         assert(message);
229
230         if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0)
231                 return -errno;
232
233         if (!isatty(fd)) {
234                 r = -errno;
235                 goto finish;
236         }
237
238         p = message;
239         left = strlen(message);
240
241         end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
242
243         while (left > 0) {
244                 ssize_t n;
245                 struct pollfd pollfd;
246                 usec_t t;
247                 int k;
248
249                 t = now(CLOCK_MONOTONIC);
250
251                 if (t >= end) {
252                         r = -ETIME;
253                         goto finish;
254                 }
255
256                 zero(pollfd);
257                 pollfd.fd = fd;
258                 pollfd.events = POLLOUT;
259
260                 if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0)
261                         return -errno;
262
263                 if (k <= 0) {
264                         r = -ETIME;
265                         goto finish;
266                 }
267
268                 if ((n = write(fd, p, left)) < 0) {
269
270                         if (errno == EAGAIN)
271                                 continue;
272
273                         r = -errno;
274                         goto finish;
275                 }
276
277                 assert((size_t) n <= left);
278
279                 p += n;
280                 left -= n;
281         }
282
283         r = 0;
284
285 finish:
286         close_nointr_nofail(fd);
287
288         return r;
289 }
290
291 int utmp_wall(const char *message) {
292         struct utmpx *u;
293         char date[26];
294         char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
295         int r;
296         time_t t;
297
298         if (!(hn = gethostname_malloc()) ||
299             !(un = getlogname_malloc())) {
300                 r = -ENOMEM;
301                 goto finish;
302         }
303
304         if ((r = getttyname_malloc(&tty)) < 0)
305                 goto finish;
306
307         time(&t);
308         assert_se(ctime_r(&t, date));
309         delete_chars(date, "\n\r");
310
311         if (asprintf(&text,
312                      "\a\r\n"
313                      "Broadcast message from %s@%s on %s (%s):\r\n\r\n"
314                      "%s\r\n\r\n",
315                      un, hn, tty, date, message) < 0) {
316                 r = -ENOMEM;
317                 goto finish;
318         }
319
320         setutxent();
321
322         r = 0;
323
324         while ((u = getutxent())) {
325                 int q;
326                 const char *path;
327                 char *buf = NULL;
328
329                 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
330                         continue;
331
332                 if (path_startswith(u->ut_line, "/dev/"))
333                         path = u->ut_line;
334                 else {
335                         if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) {
336                                 r = -ENOMEM;
337                                 goto finish;
338                         }
339
340                         path = buf;
341                 }
342
343                 if ((q = write_to_terminal(path, text)) < 0)
344                         r = q;
345
346                 free(buf);
347         }
348
349 finish:
350         free(hn);
351         free(un);
352         free(tty);
353         free(text);
354
355         return r;
356 }