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/>. */
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
49 matches_last_entry (const struct utmp
*data
)
52 /* Nothing has been read. last_entry is stale and cannot match. */
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
;
62 /* For the process-related entries, a full match is needed. */
63 return __utmp_equal (&last_entry
, data
);
66 /* Locking timeout. */
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). */
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
);
93 __sigaction (SIGALRM
, &action
, &old_action
);
97 /* Try to get the lock. */
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. */
114 __sigaction (SIGALRM
, &old_action
, NULL
);
115 if (old_timeout
!= 0)
118 __set_errno (saved_errno
);
129 __fcntl64_nocancel (fd
, F_SETLKW
, &fl
);
132 #ifndef TRANSFORM_UTMP_FILE_NAME
133 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
137 __libc_setutent (void)
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
);
152 __lseek64 (file_fd
, 0, SEEK_SET
);
158 /* Preform initialization if necessary. */
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. */
170 read_last_entry (void)
173 ssize_t nbytes
= __pread64_nocancel (file_fd
, &buffer
, sizeof (buffer
),
177 else if (nbytes
!= sizeof (buffer
))
183 file_offset
+= sizeof (buffer
);
189 __libc_getutent_r (struct utmp
*buffer
, struct utmp
**result
)
191 int saved_errno
= errno
;
193 if (!maybe_setutent ())
200 if (try_file_lock (file_fd
, F_RDLCK
))
203 ssize_t nbytes
= read_last_entry ();
204 file_unlock (file_fd
);
206 if (nbytes
<= 0) /* Read error or EOF. */
209 /* errno should be unchanged to indicate success. A premature
210 EOF is treated like an EOF (missing complete record at the
212 __set_errno (saved_errno
);
217 memcpy (buffer
, &last_entry
, sizeof (struct utmp
));
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. */
228 internal_getut_nolock (const struct utmp
*id
)
232 ssize_t nbytes
= read_last_entry ();
237 /* End of file reached. */
242 if (matches_last_entry (id
))
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. */
253 internal_getut_r (const struct utmp
*id
, bool *lock_failed
)
255 if (try_file_lock (file_fd
, F_RDLCK
))
261 int result
= internal_getut_nolock (id
);
262 file_unlock (file_fd
);
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 ())
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)
287 memcpy (buffer
, &last_entry
, sizeof (struct utmp
));
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 ())
305 if (try_file_lock (file_fd
, F_RDLCK
))
313 ssize_t nbytes
= read_last_entry ();
316 file_unlock (file_fd
);
322 /* End of file reached. */
323 file_unlock (file_fd
);
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
)
337 file_unlock (file_fd
);
338 memcpy (buffer
, &last_entry
, sizeof (struct utmp
));
346 __libc_pututline (const struct utmp
*data
)
348 if (!maybe_setutent ())
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
);
363 if (__dup2 (new_fd
, file_fd
) < 0)
365 __close_nocancel_nostatus (new_fd
);
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
))
376 /* Find the correct place to insert the data. */
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 ();
385 file_unlock (file_fd
);
390 /* End of file reached. */
393 found
= matches_last_entry (data
);
397 /* Search forward for the entry. */
398 found
= internal_getut_nolock (data
) >= 0;
400 off64_t write_offset
;
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
409 write_offset
= (write_offset
/ sizeof (struct utmp
)
410 * sizeof (struct utmp
));
413 /* Overwrite last_entry. */
414 write_offset
= file_offset
- sizeof (struct utmp
);
416 /* Write the new data. */
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
424 file_unlock (file_fd
);
428 if (nbytes
!= sizeof (struct utmp
))
430 /* If we appended a new record this is only partially written.
433 (void) __ftruncate64 (file_fd
, write_offset
);
434 file_unlock (file_fd
);
435 /* Assume that the write failure was due to missing disk
437 __set_errno (ENOSPC
);
441 file_unlock (file_fd
);
442 file_offset
= write_offset
+ sizeof (struct utmp
);
443 pbuf
= (struct utmp
*) data
;
450 __libc_endutent (void)
454 __close_nocancel_nostatus (file_fd
);
461 __libc_updwtmp (const char *file
, const struct utmp
*utmp
)
467 /* Open WTMP file. */
468 fd
= __open_nocancel (file
, O_WRONLY
| O_LARGEFILE
);
472 if (try_file_lock (fd
, F_WRLCK
))
474 __close_nocancel_nostatus (fd
);
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)
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
492 if (__write_nocancel (fd
, utmp
, sizeof (struct utmp
))
493 != sizeof (struct utmp
))
495 __ftruncate64 (fd
, offset
);
504 /* Close WTMP file. */
505 __close_nocancel_nostatus (fd
);