Sync FDL from https://www.gnu.org/licenses/fdl-1.3.texi
[glibc.git] / login / utmp_file.c
blob8c0b3a0bb49863997a7c4799315a7d1297e6c0d3
1 /* Copyright (C) 1996-2021 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.
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.
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.
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 <https://www.gnu.org/licenses/>. */
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>
34 #include "utmp-private.h"
35 #include "utmp-equal.h"
38 /* Descriptor for the file and position. */
39 static int file_fd = -1;
40 static bool file_writable;
41 static off64_t file_offset;
43 /* Cache for the last read entry. */
44 static struct utmp last_entry;
46 /* Returns true if *ENTRY matches last_entry, based on
47 data->ut_type. */
48 static bool
49 matches_last_entry (const struct utmp *data)
51 if (file_offset <= 0)
52 /* Nothing has been read. last_entry is stale and cannot match. */
53 return false;
55 if (data->ut_type == RUN_LVL
56 || data->ut_type == BOOT_TIME
57 || data->ut_type == OLD_TIME
58 || data->ut_type == NEW_TIME)
59 /* For some entry types, only a type match is required. */
60 return data->ut_type == last_entry.ut_type;
61 else
62 /* For the process-related entries, a full match is needed. */
63 return __utmp_equal (&last_entry, data);
66 /* Locking timeout. */
67 #ifndef TIMEOUT
68 # define TIMEOUT 10
69 #endif
71 /* Do-nothing handler for locking timeout. */
72 static void timeout_handler (int signum) {};
75 /* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
76 operation failed and recovery needs to be performed.
78 file_unlock (FD) removes the lock (which must have been
79 successfully acquired). */
81 static bool
82 try_file_lock (int fd, int type)
84 /* Cancel any existing alarm. */
85 int old_timeout = alarm (0);
87 /* Establish signal handler. */
88 struct sigaction old_action;
89 struct sigaction action;
90 action.sa_handler = timeout_handler;
91 __sigemptyset (&action.sa_mask);
92 action.sa_flags = 0;
93 __sigaction (SIGALRM, &action, &old_action);
95 alarm (TIMEOUT);
97 /* Try to get the lock. */
98 struct flock64 fl =
100 .l_type = type,
101 .l_whence = SEEK_SET,
104 bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
105 int saved_errno = errno;
107 /* Reset the signal handler and alarm. We must reset the alarm
108 before resetting the handler so our alarm does not generate a
109 spurious SIGALRM seen by the user. However, we cannot just set
110 the user's old alarm before restoring the handler, because then
111 it's possible our handler could catch the user alarm's SIGARLM and
112 then the user would never see the signal he expected. */
113 alarm (0);
114 __sigaction (SIGALRM, &old_action, NULL);
115 if (old_timeout != 0)
116 alarm (old_timeout);
118 __set_errno (saved_errno);
119 return status;
122 static void
123 file_unlock (int fd)
125 struct flock64 fl =
127 .l_type = F_UNLCK,
129 __fcntl64_nocancel (fd, F_SETLKW, &fl);
132 #ifndef TRANSFORM_UTMP_FILE_NAME
133 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
134 #endif
137 __libc_setutent (void)
139 if (file_fd < 0)
141 const char *file_name;
143 file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
145 file_writable = false;
146 file_fd = __open_nocancel
147 (file_name, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
148 if (file_fd == -1)
149 return 0;
152 __lseek64 (file_fd, 0, SEEK_SET);
153 file_offset = 0;
155 return 1;
158 /* Preform initialization if necessary. */
159 static bool
160 maybe_setutent (void)
162 return file_fd >= 0 || __libc_setutent ();
165 /* Reads the entry at file_offset, storing it in last_entry and
166 updating file_offset on success. Returns -1 for a read error, 0
167 for EOF, and 1 for a successful read. last_entry and file_offset
168 are only updated on a successful and complete read. */
169 static ssize_t
170 read_last_entry (void)
172 struct utmp buffer;
173 ssize_t nbytes = __pread64_nocancel (file_fd, &buffer, sizeof (buffer),
174 file_offset);
175 if (nbytes < 0)
176 return -1;
177 else if (nbytes != sizeof (buffer))
178 /* Assume EOF. */
179 return 0;
180 else
182 last_entry = buffer;
183 file_offset += sizeof (buffer);
184 return 1;
189 __libc_getutent_r (struct utmp *buffer, struct utmp **result)
191 int saved_errno = errno;
193 if (!maybe_setutent ())
195 /* Not available. */
196 *result = NULL;
197 return -1;
200 if (try_file_lock (file_fd, F_RDLCK))
201 return -1;
203 ssize_t nbytes = read_last_entry ();
204 file_unlock (file_fd);
206 if (nbytes <= 0) /* Read error or EOF. */
208 if (nbytes == 0)
209 /* errno should be unchanged to indicate success. A premature
210 EOF is treated like an EOF (missing complete record at the
211 end). */
212 __set_errno (saved_errno);
213 *result = NULL;
214 return -1;
217 memcpy (buffer, &last_entry, sizeof (struct utmp));
218 *result = buffer;
220 return 0;
224 /* Search for *ID, updating last_entry and file_offset. Return 0 on
225 success and -1 on failure. Does not perform locking; for that see
226 internal_getut_r below. */
227 static int
228 internal_getut_nolock (const struct utmp *id)
230 while (1)
232 ssize_t nbytes = read_last_entry ();
233 if (nbytes < 0)
234 return -1;
235 if (nbytes == 0)
237 /* End of file reached. */
238 __set_errno (ESRCH);
239 return -1;
242 if (matches_last_entry (id))
243 break;
246 return 0;
249 /* Search for *ID, updating last_entry and file_offset. Return 0 on
250 success and -1 on failure. If the locking operation failed, write
251 true to *LOCK_FAILED. */
252 static int
253 internal_getut_r (const struct utmp *id, bool *lock_failed)
255 if (try_file_lock (file_fd, F_RDLCK))
257 *lock_failed = true;
258 return -1;
261 int result = internal_getut_nolock (id);
262 file_unlock (file_fd);
263 return result;
266 /* For implementing this function we don't use the getutent_r function
267 because we can avoid the reposition on every new entry this way. */
269 __libc_getutid_r (const struct utmp *id, struct utmp *buffer,
270 struct utmp **result)
272 if (!maybe_setutent ())
274 *result = NULL;
275 return -1;
278 /* We don't have to distinguish whether we can lock the file or
279 whether there is no entry. */
280 bool lock_failed = false;
281 if (internal_getut_r (id, &lock_failed) < 0)
283 *result = NULL;
284 return -1;
287 memcpy (buffer, &last_entry, sizeof (struct utmp));
288 *result = buffer;
290 return 0;
293 /* For implementing this function we don't use the getutent_r function
294 because we can avoid the reposition on every new entry this way. */
296 __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
297 struct utmp **result)
299 if (!maybe_setutent ())
301 *result = NULL;
302 return -1;
305 if (try_file_lock (file_fd, F_RDLCK))
307 *result = NULL;
308 return -1;
311 while (1)
313 ssize_t nbytes = read_last_entry ();
314 if (nbytes < 0)
316 file_unlock (file_fd);
317 *result = NULL;
318 return -1;
320 if (nbytes == 0)
322 /* End of file reached. */
323 file_unlock (file_fd);
324 __set_errno (ESRCH);
325 *result = NULL;
326 return -1;
329 /* Stop if we found a user or login entry. */
330 if ((last_entry.ut_type == USER_PROCESS
331 || last_entry.ut_type == LOGIN_PROCESS)
332 && (strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line)
333 == 0))
334 break;
337 file_unlock (file_fd);
338 memcpy (buffer, &last_entry, sizeof (struct utmp));
339 *result = buffer;
341 return 0;
345 struct utmp *
346 __libc_pututline (const struct utmp *data)
348 if (!maybe_setutent ())
349 return NULL;
351 struct utmp *pbuf;
353 if (! file_writable)
355 /* We must make the file descriptor writable before going on. */
356 const char *file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
358 int new_fd = __open_nocancel
359 (file_name, O_RDWR | O_LARGEFILE | O_CLOEXEC);
360 if (new_fd == -1)
361 return NULL;
363 if (__dup2 (new_fd, file_fd) < 0)
365 __close_nocancel_nostatus (new_fd);
366 return NULL;
368 __close_nocancel_nostatus (new_fd);
369 file_writable = true;
372 /* Exclude other writers before validating the cache. */
373 if (try_file_lock (file_fd, F_WRLCK))
374 return NULL;
376 /* Find the correct place to insert the data. */
377 bool found = false;
378 if (matches_last_entry (data))
380 /* Read back the entry under the write lock. */
381 file_offset -= sizeof (last_entry);
382 ssize_t nbytes = read_last_entry ();
383 if (nbytes < 0)
385 file_unlock (file_fd);
386 return NULL;
389 if (nbytes == 0)
390 /* End of file reached. */
391 found = false;
392 else
393 found = matches_last_entry (data);
396 if (!found)
397 /* Search forward for the entry. */
398 found = internal_getut_nolock (data) >= 0;
400 off64_t write_offset;
401 if (!found)
403 /* We append the next entry. */
404 write_offset = __lseek64 (file_fd, 0, SEEK_END);
406 /* Round down to the next multiple of the entry size. This
407 ensures any partially-written record is overwritten by the
408 new record. */
409 write_offset = (write_offset / sizeof (struct utmp)
410 * sizeof (struct utmp));
412 else
413 /* Overwrite last_entry. */
414 write_offset = file_offset - sizeof (struct utmp);
416 /* Write the new data. */
417 ssize_t nbytes;
418 if (__lseek64 (file_fd, write_offset, SEEK_SET) < 0
419 || (nbytes = __write_nocancel (file_fd, data, sizeof (struct utmp))) < 0)
421 /* There is no need to recover the file position because all
422 reads use pread64, and any future write is preceded by
423 another seek. */
424 file_unlock (file_fd);
425 return NULL;
428 if (nbytes != sizeof (struct utmp))
430 /* If we appended a new record this is only partially written.
431 Remove it. */
432 if (!found)
433 (void) __ftruncate64 (file_fd, write_offset);
434 file_unlock (file_fd);
435 /* Assume that the write failure was due to missing disk
436 space. */
437 __set_errno (ENOSPC);
438 return NULL;
441 file_unlock (file_fd);
442 file_offset = write_offset + sizeof (struct utmp);
443 pbuf = (struct utmp *) data;
445 return pbuf;
449 void
450 __libc_endutent (void)
452 if (file_fd >= 0)
454 __close_nocancel_nostatus (file_fd);
455 file_fd = -1;
461 __libc_updwtmp (const char *file, const struct utmp *utmp)
463 int result = -1;
464 off64_t offset;
465 int fd;
467 /* Open WTMP file. */
468 fd = __open_nocancel (file, O_WRONLY | O_LARGEFILE);
469 if (fd < 0)
470 return -1;
472 if (try_file_lock (fd, F_WRLCK))
474 __close_nocancel_nostatus (fd);
475 return -1;
478 /* Remember original size of log file. */
479 offset = __lseek64 (fd, 0, SEEK_END);
480 if (offset % sizeof (struct utmp) != 0)
482 offset -= offset % sizeof (struct utmp);
483 __ftruncate64 (fd, offset);
485 if (__lseek64 (fd, 0, SEEK_END) < 0)
486 goto unlock_return;
489 /* Write the entry. If we can't write all the bytes, reset the file
490 size back to the original size. That way, no partial entries
491 will remain. */
492 if (__write_nocancel (fd, utmp, sizeof (struct utmp))
493 != sizeof (struct utmp))
495 __ftruncate64 (fd, offset);
496 goto unlock_return;
499 result = 0;
501 unlock_return:
502 file_unlock (fd);
504 /* Close WTMP file. */
505 __close_nocancel_nostatus (fd);
507 return result;