]> git.ipfire.org Git - thirdparty/glibc.git/blob - login/utmp_file.c
2d0548f6fa29d4e7f7f9d6e10d4025c189928fad
[thirdparty/glibc.git] / login / utmp_file.c
1 /* Copyright (C) 1996-2019 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@cygnus.com>
4 and Paul Janzen <pcj@primenet.com>, 1996.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <utmp.h>
29 #include <not-cancel.h>
30 #include <kernel-features.h>
31 #include <sigsetops.h>
32 #include <not-cancel.h>
33
34 #include "utmp-private.h"
35 #include "utmp-equal.h"
36
37
38 /* Descriptor for the file and position. */
39 static int file_fd = -1;
40 static bool file_writable;
41 static off64_t file_offset;
42
43 /* Cache for the last read entry. */
44 static struct utmp last_entry;
45
46
47 /* Locking timeout. */
48 #ifndef TIMEOUT
49 # define TIMEOUT 10
50 #endif
51
52 /* Do-nothing handler for locking timeout. */
53 static void timeout_handler (int signum) {};
54
55
56 /* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
57 operation failed and recovery needs to be performed.
58
59 file_unlock (FD) removes the lock (which must have been
60 successfully acquired). */
61
62 static bool
63 try_file_lock (int fd, int type)
64 {
65 /* Cancel any existing alarm. */
66 int old_timeout = alarm (0);
67
68 /* Establish signal handler. */
69 struct sigaction old_action;
70 struct sigaction action;
71 action.sa_handler = timeout_handler;
72 __sigemptyset (&action.sa_mask);
73 action.sa_flags = 0;
74 __sigaction (SIGALRM, &action, &old_action);
75
76 alarm (TIMEOUT);
77
78 /* Try to get the lock. */
79 struct flock64 fl =
80 {
81 .l_type = type,
82 fl.l_whence = SEEK_SET,
83 };
84
85 bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
86 int saved_errno = errno;
87
88 /* Reset the signal handler and alarm. We must reset the alarm
89 before resetting the handler so our alarm does not generate a
90 spurious SIGALRM seen by the user. However, we cannot just set
91 the user's old alarm before restoring the handler, because then
92 it's possible our handler could catch the user alarm's SIGARLM and
93 then the user would never see the signal he expected. */
94 alarm (0);
95 __sigaction (SIGALRM, &old_action, NULL);
96 if (old_timeout != 0)
97 alarm (old_timeout);
98
99 __set_errno (saved_errno);
100 return status;
101 }
102
103 static void
104 file_unlock (int fd)
105 {
106 struct flock64 fl =
107 {
108 .l_type = F_UNLCK,
109 };
110 __fcntl64_nocancel (fd, F_SETLKW, &fl);
111 }
112
113 #ifndef TRANSFORM_UTMP_FILE_NAME
114 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
115 #endif
116
117 int
118 __libc_setutent (void)
119 {
120 if (file_fd < 0)
121 {
122 const char *file_name;
123
124 file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
125
126 file_writable = false;
127 file_fd = __open_nocancel
128 (file_name, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
129 if (file_fd == -1)
130 return 0;
131 }
132
133 __lseek64 (file_fd, 0, SEEK_SET);
134 file_offset = 0;
135
136 /* Make sure the entry won't match. */
137 last_entry.ut_type = -1;
138
139 return 1;
140 }
141
142 /* Preform initialization if necessary. */
143 static bool
144 maybe_setutent (void)
145 {
146 return file_fd >= 0 || __libc_setutent ();
147 }
148
149 int
150 __libc_getutent_r (struct utmp *buffer, struct utmp **result)
151 {
152 ssize_t nbytes;
153
154 if (!maybe_setutent () || file_offset == -1l)
155 {
156 /* Not available. */
157 *result = NULL;
158 return -1;
159 }
160
161 if (try_file_lock (file_fd, F_RDLCK))
162 nbytes = 0;
163 else
164 {
165 /* Read the next entry. */
166 nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
167 file_unlock (file_fd);
168 }
169
170 if (nbytes != sizeof (struct utmp))
171 {
172 if (nbytes != 0)
173 file_offset = -1l;
174 *result = NULL;
175 return -1;
176 }
177
178 /* Update position pointer. */
179 file_offset += sizeof (struct utmp);
180
181 memcpy (buffer, &last_entry, sizeof (struct utmp));
182 *result = buffer;
183
184 return 0;
185 }
186
187
188 /* Search for *ID, updating last_entry and file_offset. Return 0 on
189 success and -1 on failure. If the locking operation failed, write
190 true to *LOCK_FAILED. */
191 static int
192 internal_getut_r (const struct utmp *id, bool *lock_failed)
193 {
194 int result = -1;
195
196 if (try_file_lock (file_fd, F_RDLCK))
197 {
198 *lock_failed = true;
199 return -1;
200 }
201
202 if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
203 || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
204 {
205 /* Search for next entry with type RUN_LVL, BOOT_TIME,
206 OLD_TIME, or NEW_TIME. */
207
208 while (1)
209 {
210 /* Read the next entry. */
211 if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
212 != sizeof (struct utmp))
213 {
214 __set_errno (ESRCH);
215 file_offset = -1l;
216 goto unlock_return;
217 }
218 file_offset += sizeof (struct utmp);
219
220 if (id->ut_type == last_entry.ut_type)
221 break;
222 }
223 }
224 else
225 {
226 /* Search for the next entry with the specified ID and with type
227 INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS. */
228
229 while (1)
230 {
231 /* Read the next entry. */
232 if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
233 != sizeof (struct utmp))
234 {
235 __set_errno (ESRCH);
236 file_offset = -1l;
237 goto unlock_return;
238 }
239 file_offset += sizeof (struct utmp);
240
241 if (__utmp_equal (&last_entry, id))
242 break;
243 }
244 }
245
246 result = 0;
247
248 unlock_return:
249 file_unlock (file_fd);
250
251 return result;
252 }
253
254
255 /* For implementing this function we don't use the getutent_r function
256 because we can avoid the reposition on every new entry this way. */
257 int
258 __libc_getutid_r (const struct utmp *id, struct utmp *buffer,
259 struct utmp **result)
260 {
261 if (!maybe_setutent () || file_offset == -1l)
262 {
263 *result = NULL;
264 return -1;
265 }
266
267 /* We don't have to distinguish whether we can lock the file or
268 whether there is no entry. */
269 bool lock_failed = false;
270 if (internal_getut_r (id, &lock_failed) < 0)
271 {
272 *result = NULL;
273 return -1;
274 }
275
276 memcpy (buffer, &last_entry, sizeof (struct utmp));
277 *result = buffer;
278
279 return 0;
280 }
281
282
283 /* For implementing this function we don't use the getutent_r function
284 because we can avoid the reposition on every new entry this way. */
285 int
286 __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
287 struct utmp **result)
288 {
289 if (!maybe_setutent () || file_offset == -1l)
290 {
291 *result = NULL;
292 return -1;
293 }
294
295 if (try_file_lock (file_fd, F_RDLCK))
296 {
297 *result = NULL;
298 return -1;
299 }
300
301 while (1)
302 {
303 /* Read the next entry. */
304 if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
305 != sizeof (struct utmp))
306 {
307 __set_errno (ESRCH);
308 file_offset = -1l;
309 *result = NULL;
310 goto unlock_return;
311 }
312 file_offset += sizeof (struct utmp);
313
314 /* Stop if we found a user or login entry. */
315 if ((last_entry.ut_type == USER_PROCESS
316 || last_entry.ut_type == LOGIN_PROCESS)
317 && (strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line)
318 == 0))
319 break;
320 }
321
322 memcpy (buffer, &last_entry, sizeof (struct utmp));
323 *result = buffer;
324
325 unlock_return:
326 file_unlock (file_fd);
327
328 return ((*result == NULL) ? -1 : 0);
329 }
330
331
332 struct utmp *
333 __libc_pututline (const struct utmp *data)
334 {
335 if (!maybe_setutent () || file_offset == -1l)
336 return NULL;
337
338 struct utmp *pbuf;
339 int found;
340
341 if (! file_writable)
342 {
343 /* We must make the file descriptor writable before going on. */
344 const char *file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
345
346 int new_fd = __open_nocancel
347 (file_name, O_RDWR | O_LARGEFILE | O_CLOEXEC);
348 if (new_fd == -1)
349 return NULL;
350
351 if (__lseek64 (new_fd, __lseek64 (file_fd, 0, SEEK_CUR), SEEK_SET) == -1
352 || __dup2 (new_fd, file_fd) < 0)
353 {
354 __close_nocancel_nostatus (new_fd);
355 return NULL;
356 }
357 __close_nocancel_nostatus (new_fd);
358 file_writable = true;
359 }
360
361 /* Find the correct place to insert the data. */
362 if (file_offset > 0
363 && ((last_entry.ut_type == data->ut_type
364 && (last_entry.ut_type == RUN_LVL
365 || last_entry.ut_type == BOOT_TIME
366 || last_entry.ut_type == OLD_TIME
367 || last_entry.ut_type == NEW_TIME))
368 || __utmp_equal (&last_entry, data)))
369 found = 1;
370 else
371 {
372 bool lock_failed = false;
373 found = internal_getut_r (data, &lock_failed);
374
375 if (__builtin_expect (lock_failed, false))
376 {
377 __set_errno (EAGAIN);
378 return NULL;
379 }
380 }
381
382 if (try_file_lock (file_fd, F_WRLCK))
383 return NULL;
384
385 if (found < 0)
386 {
387 /* We append the next entry. */
388 file_offset = __lseek64 (file_fd, 0, SEEK_END);
389 if (file_offset % sizeof (struct utmp) != 0)
390 {
391 file_offset -= file_offset % sizeof (struct utmp);
392 __ftruncate64 (file_fd, file_offset);
393
394 if (__lseek64 (file_fd, 0, SEEK_END) < 0)
395 {
396 pbuf = NULL;
397 goto unlock_return;
398 }
399 }
400 }
401 else
402 {
403 /* We replace the just read entry. */
404 file_offset -= sizeof (struct utmp);
405 __lseek64 (file_fd, file_offset, SEEK_SET);
406 }
407
408 /* Write the new data. */
409 if (__write_nocancel (file_fd, data, sizeof (struct utmp))
410 != sizeof (struct utmp))
411 {
412 /* If we appended a new record this is only partially written.
413 Remove it. */
414 if (found < 0)
415 (void) __ftruncate64 (file_fd, file_offset);
416 pbuf = NULL;
417 }
418 else
419 {
420 file_offset += sizeof (struct utmp);
421 pbuf = (struct utmp *) data;
422 }
423
424 unlock_return:
425 file_unlock (file_fd);
426
427 return pbuf;
428 }
429
430
431 void
432 __libc_endutent (void)
433 {
434 if (file_fd >= 0)
435 {
436 __close_nocancel_nostatus (file_fd);
437 file_fd = -1;
438 }
439 }
440
441
442 int
443 __libc_updwtmp (const char *file, const struct utmp *utmp)
444 {
445 int result = -1;
446 off64_t offset;
447 int fd;
448
449 /* Open WTMP file. */
450 fd = __open_nocancel (file, O_WRONLY | O_LARGEFILE);
451 if (fd < 0)
452 return -1;
453
454 if (try_file_lock (fd, F_WRLCK))
455 {
456 __close_nocancel_nostatus (fd);
457 return -1;
458 }
459
460 /* Remember original size of log file. */
461 offset = __lseek64 (fd, 0, SEEK_END);
462 if (offset % sizeof (struct utmp) != 0)
463 {
464 offset -= offset % sizeof (struct utmp);
465 __ftruncate64 (fd, offset);
466
467 if (__lseek64 (fd, 0, SEEK_END) < 0)
468 goto unlock_return;
469 }
470
471 /* Write the entry. If we can't write all the bytes, reset the file
472 size back to the original size. That way, no partial entries
473 will remain. */
474 if (__write_nocancel (fd, utmp, sizeof (struct utmp))
475 != sizeof (struct utmp))
476 {
477 __ftruncate64 (fd, offset);
478 goto unlock_return;
479 }
480
481 result = 0;
482
483 unlock_return:
484 file_unlock (fd);
485
486 /* Close WTMP file. */
487 __close_nocancel_nostatus (fd);
488
489 return result;
490 }