1 /* Copyright (C) 1996-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
27 #include <not-cancel.h>
28 #include <kernel-features.h>
29 #include <sigsetops.h>
30 #include <not-cancel.h>
32 #include "utmp-private.h"
33 #include "utmp-equal.h"
36 /* Descriptor for the file and position. */
37 static int file_fd
= -1;
38 static bool file_writable
;
39 static off64_t file_offset
;
41 /* Cache for the last read entry. */
42 static struct utmp last_entry
;
44 /* Returns true if *ENTRY matches last_entry, based on
47 matches_last_entry (const struct utmp
*data
)
50 /* Nothing has been read. last_entry is stale and cannot match. */
53 if (data
->ut_type
== RUN_LVL
54 || data
->ut_type
== BOOT_TIME
55 || data
->ut_type
== OLD_TIME
56 || data
->ut_type
== NEW_TIME
)
57 /* For some entry types, only a type match is required. */
58 return data
->ut_type
== last_entry
.ut_type
;
60 /* For the process-related entries, a full match is needed. */
61 return __utmp_equal (&last_entry
, data
);
64 /* Locking timeout. */
69 /* Do-nothing handler for locking timeout. */
70 static void timeout_handler (int signum
) {};
73 /* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
74 operation failed and recovery needs to be performed.
76 file_unlock (FD) removes the lock (which must have been
77 successfully acquired). */
80 try_file_lock (int fd
, int type
)
82 /* Cancel any existing alarm. */
83 int old_timeout
= alarm (0);
85 /* Establish signal handler. */
86 struct sigaction old_action
;
87 struct sigaction action
;
88 action
.sa_handler
= timeout_handler
;
89 __sigemptyset (&action
.sa_mask
);
91 __sigaction (SIGALRM
, &action
, &old_action
);
95 /* Try to get the lock. */
102 bool status
= __fcntl64_nocancel (fd
, F_SETLKW
, &fl
) < 0;
103 int saved_errno
= errno
;
105 /* Reset the signal handler and alarm. We must reset the alarm
106 before resetting the handler so our alarm does not generate a
107 spurious SIGALRM seen by the user. However, we cannot just set
108 the user's old alarm before restoring the handler, because then
109 it's possible our handler could catch the user alarm's SIGARLM and
110 then the user would never see the signal he expected. */
112 __sigaction (SIGALRM
, &old_action
, NULL
);
113 if (old_timeout
!= 0)
116 __set_errno (saved_errno
);
127 __fcntl64_nocancel (fd
, F_SETLKW
, &fl
);
130 #ifndef TRANSFORM_UTMP_FILE_NAME
131 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
135 __libc_setutent (void)
139 const char *file_name
;
141 file_name
= TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name
);
143 file_writable
= false;
144 file_fd
= __open_nocancel
145 (file_name
, O_RDONLY
| O_LARGEFILE
| O_CLOEXEC
);
150 __lseek64 (file_fd
, 0, SEEK_SET
);
156 /* Perform initialization if necessary. */
158 maybe_setutent (void)
160 return file_fd
>= 0 || __libc_setutent ();
163 /* Reads the entry at file_offset, storing it in last_entry and
164 updating file_offset on success. Returns -1 for a read error, 0
165 for EOF, and 1 for a successful read. last_entry and file_offset
166 are only updated on a successful and complete read. */
168 read_last_entry (void)
171 ssize_t nbytes
= __pread64_nocancel (file_fd
, &buffer
, sizeof (buffer
),
175 else if (nbytes
!= sizeof (buffer
))
181 file_offset
+= sizeof (buffer
);
187 __libc_getutent_r (struct utmp
*buffer
, struct utmp
**result
)
189 int saved_errno
= errno
;
191 if (!maybe_setutent ())
198 if (try_file_lock (file_fd
, F_RDLCK
))
201 ssize_t nbytes
= read_last_entry ();
202 file_unlock (file_fd
);
204 if (nbytes
<= 0) /* Read error or EOF. */
207 /* errno should be unchanged to indicate success. A premature
208 EOF is treated like an EOF (missing complete record at the
210 __set_errno (saved_errno
);
215 memcpy (buffer
, &last_entry
, sizeof (struct utmp
));
222 /* Search for *ID, updating last_entry and file_offset. Return 0 on
223 success and -1 on failure. Does not perform locking; for that see
224 internal_getut_r below. */
226 internal_getut_nolock (const struct utmp
*id
)
230 ssize_t nbytes
= read_last_entry ();
235 /* End of file reached. */
240 if (matches_last_entry (id
))
247 /* Search for *ID, updating last_entry and file_offset. Return 0 on
248 success and -1 on failure. If the locking operation failed, write
249 true to *LOCK_FAILED. */
251 internal_getut_r (const struct utmp
*id
, bool *lock_failed
)
253 if (try_file_lock (file_fd
, F_RDLCK
))
259 int result
= internal_getut_nolock (id
);
260 file_unlock (file_fd
);
264 /* For implementing this function we don't use the getutent_r function
265 because we can avoid the reposition on every new entry this way. */
267 __libc_getutid_r (const struct utmp
*id
, struct utmp
*buffer
,
268 struct utmp
**result
)
270 if (!maybe_setutent ())
276 /* We don't have to distinguish whether we can lock the file or
277 whether there is no entry. */
278 bool lock_failed
= false;
279 if (internal_getut_r (id
, &lock_failed
) < 0)
285 memcpy (buffer
, &last_entry
, sizeof (struct utmp
));
291 /* For implementing this function we don't use the getutent_r function
292 because we can avoid the reposition on every new entry this way. */
294 __libc_getutline_r (const struct utmp
*line
, struct utmp
*buffer
,
295 struct utmp
**result
)
297 if (!maybe_setutent ())
303 if (try_file_lock (file_fd
, F_RDLCK
))
311 ssize_t nbytes
= read_last_entry ();
314 file_unlock (file_fd
);
320 /* End of file reached. */
321 file_unlock (file_fd
);
327 /* Stop if we found a user or login entry. */
328 if ((last_entry
.ut_type
== USER_PROCESS
329 || last_entry
.ut_type
== LOGIN_PROCESS
)
330 && (strncmp (line
->ut_line
, last_entry
.ut_line
, sizeof line
->ut_line
)
335 file_unlock (file_fd
);
336 memcpy (buffer
, &last_entry
, sizeof (struct utmp
));
344 __libc_pututline (const struct utmp
*data
)
346 if (!maybe_setutent ())
353 /* We must make the file descriptor writable before going on. */
354 const char *file_name
= TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name
);
356 int new_fd
= __open_nocancel
357 (file_name
, O_RDWR
| O_LARGEFILE
| O_CLOEXEC
);
361 if (__dup2 (new_fd
, file_fd
) < 0)
363 __close_nocancel_nostatus (new_fd
);
366 __close_nocancel_nostatus (new_fd
);
367 file_writable
= true;
370 /* Exclude other writers before validating the cache. */
371 if (try_file_lock (file_fd
, F_WRLCK
))
374 /* Find the correct place to insert the data. */
376 if (matches_last_entry (data
))
378 /* Read back the entry under the write lock. */
379 file_offset
-= sizeof (last_entry
);
380 ssize_t nbytes
= read_last_entry ();
383 file_unlock (file_fd
);
388 /* End of file reached. */
391 found
= matches_last_entry (data
);
395 /* Search forward for the entry. */
396 found
= internal_getut_nolock (data
) >= 0;
398 off64_t write_offset
;
401 /* We append the next entry. */
402 write_offset
= __lseek64 (file_fd
, 0, SEEK_END
);
404 /* Round down to the next multiple of the entry size. This
405 ensures any partially-written record is overwritten by the
407 write_offset
= (write_offset
/ sizeof (struct utmp
)
408 * sizeof (struct utmp
));
411 /* Overwrite last_entry. */
412 write_offset
= file_offset
- sizeof (struct utmp
);
414 /* Write the new data. */
416 if (__lseek64 (file_fd
, write_offset
, SEEK_SET
) < 0
417 || (nbytes
= __write_nocancel (file_fd
, data
, sizeof (struct utmp
))) < 0)
419 /* There is no need to recover the file position because all
420 reads use pread64, and any future write is preceded by
422 file_unlock (file_fd
);
426 if (nbytes
!= sizeof (struct utmp
))
428 /* If we appended a new record this is only partially written.
431 (void) __ftruncate64 (file_fd
, write_offset
);
432 file_unlock (file_fd
);
433 /* Assume that the write failure was due to missing disk
435 __set_errno (ENOSPC
);
439 file_unlock (file_fd
);
440 file_offset
= write_offset
+ sizeof (struct utmp
);
441 pbuf
= (struct utmp
*) data
;
448 __libc_endutent (void)
452 __close_nocancel_nostatus (file_fd
);
459 __libc_updwtmp (const char *file
, const struct utmp
*utmp
)
465 /* Open WTMP file. */
466 fd
= __open_nocancel (file
, O_WRONLY
| O_LARGEFILE
| O_CLOEXEC
);
470 if (try_file_lock (fd
, F_WRLCK
))
472 __close_nocancel_nostatus (fd
);
476 /* Remember original size of log file. */
477 offset
= __lseek64 (fd
, 0, SEEK_END
);
478 if (offset
% sizeof (struct utmp
) != 0)
480 offset
-= offset
% sizeof (struct utmp
);
481 __ftruncate64 (fd
, offset
);
483 if (__lseek64 (fd
, 0, SEEK_END
) < 0)
487 /* Write the entry. If we can't write all the bytes, reset the file
488 size back to the original size. That way, no partial entries
490 if (__write_nocancel (fd
, utmp
, sizeof (struct utmp
))
491 != sizeof (struct utmp
))
493 __ftruncate64 (fd
, offset
);
502 /* Close WTMP file. */
503 __close_nocancel_nostatus (fd
);