]> git.ipfire.org Git - thirdparty/glibc.git/blame - login/utmp_file.c
Fix all the remaining misspellings -- BZ 25337
[thirdparty/glibc.git] / login / utmp_file.c
CommitLineData
6d7e8eda 1/* Copyright (C) 1996-2023 Free Software Foundation, Inc.
8a523922 2 This file is part of the GNU C Library.
8a523922
UD
3
4 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
8a523922
UD
8
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
41bdb6e2 12 Lesser General Public License for more details.
8a523922 13
41bdb6e2 14 You should have received a copy of the GNU Lesser General Public
59ba27a6 15 License along with the GNU C Library; if not, see
5a82c748 16 <https://www.gnu.org/licenses/>. */
8a523922 17
0413b54c 18#include <assert.h>
8a523922
UD
19#include <errno.h>
20#include <fcntl.h>
efea7158 21#include <signal.h>
1bfa05cf 22#include <stdbool.h>
8a523922 23#include <stdio.h>
8a523922
UD
24#include <string.h>
25#include <unistd.h>
26#include <utmp.h>
ce42435c 27#include <not-cancel.h>
c942388d 28#include <kernel-features.h>
a992f506 29#include <sigsetops.h>
2b0b9a1c 30#include <not-cancel.h>
8a523922
UD
31
32#include "utmp-private.h"
82c26126 33#include "utmp-equal.h"
8a523922 34
8a523922
UD
35
36/* Descriptor for the file and position. */
0413b54c 37static int file_fd = -1;
37a6a271 38static bool file_writable;
2958e6cc 39static off64_t file_offset;
8a523922 40
0413b54c 41/* Cache for the last read entry. */
8a523922
UD
42static struct utmp last_entry;
43
76a7c103
FW
44/* Returns true if *ENTRY matches last_entry, based on
45 data->ut_type. */
46static bool
47matches_last_entry (const struct utmp *data)
48{
49 if (file_offset <= 0)
50 /* Nothing has been read. last_entry is stale and cannot match. */
51 return false;
52
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;
59 else
60 /* For the process-related entries, a full match is needed. */
61 return __utmp_equal (&last_entry, data);
62}
76b87c03 63
efea7158
UD
64/* Locking timeout. */
65#ifndef TIMEOUT
5debe363 66# define TIMEOUT 10
efea7158
UD
67#endif
68
69/* Do-nothing handler for locking timeout. */
70static void timeout_handler (int signum) {};
71
5a3afa97
FW
72
73/* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
74 operation failed and recovery needs to be performed.
5a3afa97
FW
75
76 file_unlock (FD) removes the lock (which must have been
628598be 77 successfully acquired). */
5a3afa97
FW
78
79static bool
628598be 80try_file_lock (int fd, int type)
5a3afa97
FW
81{
82 /* Cancel any existing alarm. */
628598be 83 int old_timeout = alarm (0);
5a3afa97
FW
84
85 /* Establish signal handler. */
628598be 86 struct sigaction old_action;
5a3afa97
FW
87 struct sigaction action;
88 action.sa_handler = timeout_handler;
89 __sigemptyset (&action.sa_mask);
90 action.sa_flags = 0;
628598be 91 __sigaction (SIGALRM, &action, &old_action);
5a3afa97
FW
92
93 alarm (TIMEOUT);
94
95 /* Try to get the lock. */
0d5b2917 96 struct flock64 fl =
5a3afa97
FW
97 {
98 .l_type = type,
b0a83ae7 99 .l_whence = SEEK_SET,
5a3afa97 100 };
628598be
FW
101
102 bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
103 int saved_errno = errno;
104
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. */
111 alarm (0);
112 __sigaction (SIGALRM, &old_action, NULL);
113 if (old_timeout != 0)
114 alarm (old_timeout);
115
116 __set_errno (saved_errno);
117 return status;
5a3afa97
FW
118}
119
120static void
121file_unlock (int fd)
122{
0d5b2917 123 struct flock64 fl =
5a3afa97
FW
124 {
125 .l_type = F_UNLCK,
126 };
127 __fcntl64_nocancel (fd, F_SETLKW, &fl);
128}
129
8619129f
UD
130#ifndef TRANSFORM_UTMP_FILE_NAME
131# define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
132#endif
133
1a7fe2eb
FW
134int
135__libc_setutent (void)
8a523922 136{
8f2ece69 137 if (file_fd < 0)
8a523922 138 {
8619129f 139 const char *file_name;
0413b54c 140
8619129f 141 file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
0413b54c 142
37a6a271 143 file_writable = false;
c2284574 144 file_fd = __open_nocancel
cef9b653 145 (file_name, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
8a523922 146 if (file_fd == -1)
37a6a271 147 return 0;
8a523922 148 }
68dbb3a6 149
2958e6cc 150 __lseek64 (file_fd, 0, SEEK_SET);
8f2ece69 151 file_offset = 0;
8a523922 152
0413b54c 153 return 1;
8a523922
UD
154}
155
7f0d9e61 156/* Perform initialization if necessary. */
1a7fe2eb
FW
157static bool
158maybe_setutent (void)
159{
160 return file_fd >= 0 || __libc_setutent ();
161}
8a523922 162
d4625a19
FW
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. */
167static ssize_t
168read_last_entry (void)
169{
170 struct utmp buffer;
171 ssize_t nbytes = __pread64_nocancel (file_fd, &buffer, sizeof (buffer),
172 file_offset);
173 if (nbytes < 0)
174 return -1;
175 else if (nbytes != sizeof (buffer))
176 /* Assume EOF. */
177 return 0;
178 else
179 {
180 last_entry = buffer;
181 file_offset += sizeof (buffer);
182 return 1;
183 }
184}
185
1a7fe2eb
FW
186int
187__libc_getutent_r (struct utmp *buffer, struct utmp **result)
8a523922 188{
d4625a19 189 int saved_errno = errno;
8a523922 190
d4625a19 191 if (!maybe_setutent ())
8a523922
UD
192 {
193 /* Not available. */
194 *result = NULL;
195 return -1;
196 }
197
628598be 198 if (try_file_lock (file_fd, F_RDLCK))
d4625a19 199 return -1;
8a523922 200
d4625a19
FW
201 ssize_t nbytes = read_last_entry ();
202 file_unlock (file_fd);
203
204 if (nbytes <= 0) /* Read error or EOF. */
8a523922 205 {
d4625a19
FW
206 if (nbytes == 0)
207 /* errno should be unchanged to indicate success. A premature
208 EOF is treated like an EOF (missing complete record at the
209 end). */
210 __set_errno (saved_errno);
8a523922
UD
211 *result = NULL;
212 return -1;
213 }
214
8a523922
UD
215 memcpy (buffer, &last_entry, sizeof (struct utmp));
216 *result = buffer;
217
218 return 0;
219}
220
221
61d3db42 222/* Search for *ID, updating last_entry and file_offset. Return 0 on
be6b16d9
FW
223 success and -1 on failure. Does not perform locking; for that see
224 internal_getut_r below. */
ced858d0 225static int
be6b16d9 226internal_getut_nolock (const struct utmp *id)
8a523922 227{
76a7c103 228 while (1)
8a523922 229 {
d4625a19
FW
230 ssize_t nbytes = read_last_entry ();
231 if (nbytes < 0)
232 return -1;
233 if (nbytes == 0)
8a523922 234 {
d4625a19 235 /* End of file reached. */
76a7c103 236 __set_errno (ESRCH);
76a7c103 237 return -1;
8a523922 238 }
8a4b65b4 239
76a7c103
FW
240 if (matches_last_entry (id))
241 break;
8a523922
UD
242 }
243
be6b16d9
FW
244 return 0;
245}
d705269e 246
be6b16d9
FW
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. */
250static int
251internal_getut_r (const struct utmp *id, bool *lock_failed)
252{
253 if (try_file_lock (file_fd, F_RDLCK))
254 {
255 *lock_failed = true;
256 return -1;
257 }
d705269e 258
be6b16d9
FW
259 int result = internal_getut_nolock (id);
260 file_unlock (file_fd);
d705269e 261 return result;
8a523922
UD
262}
263
8a523922
UD
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. */
1a7fe2eb
FW
266int
267__libc_getutid_r (const struct utmp *id, struct utmp *buffer,
268 struct utmp **result)
8a523922 269{
d4625a19 270 if (!maybe_setutent ())
8a523922
UD
271 {
272 *result = NULL;
273 return -1;
274 }
275
1bfa05cf
UD
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;
61d3db42 279 if (internal_getut_r (id, &lock_failed) < 0)
8a523922
UD
280 {
281 *result = NULL;
282 return -1;
283 }
284
285 memcpy (buffer, &last_entry, sizeof (struct utmp));
286 *result = buffer;
287
288 return 0;
289}
290
0413b54c
UD
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. */
1a7fe2eb
FW
293int
294__libc_getutline_r (const struct utmp *line, struct utmp *buffer,
295 struct utmp **result)
0413b54c 296{
d4625a19 297 if (!maybe_setutent ())
0413b54c
UD
298 {
299 *result = NULL;
300 return -1;
301 }
302
628598be 303 if (try_file_lock (file_fd, F_RDLCK))
52c82852
RM
304 {
305 *result = NULL;
5a3afa97 306 return -1;
52c82852 307 }
0413b54c
UD
308
309 while (1)
310 {
d4625a19
FW
311 ssize_t nbytes = read_last_entry ();
312 if (nbytes < 0)
313 {
314 file_unlock (file_fd);
315 *result = NULL;
316 return -1;
317 }
318 if (nbytes == 0)
0413b54c 319 {
d4625a19
FW
320 /* End of file reached. */
321 file_unlock (file_fd);
0413b54c 322 __set_errno (ESRCH);
0413b54c 323 *result = NULL;
d4625a19 324 return -1;
0413b54c 325 }
0413b54c
UD
326
327 /* Stop if we found a user or login entry. */
a33b817f 328 if ((last_entry.ut_type == USER_PROCESS
0413b54c 329 || last_entry.ut_type == LOGIN_PROCESS)
a33b817f
FW
330 && (strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line)
331 == 0))
0413b54c
UD
332 break;
333 }
334
d4625a19 335 file_unlock (file_fd);
0413b54c
UD
336 memcpy (buffer, &last_entry, sizeof (struct utmp));
337 *result = buffer;
338
d4625a19 339 return 0;
0413b54c
UD
340}
341
342
1a7fe2eb
FW
343struct utmp *
344__libc_pututline (const struct utmp *data)
8a523922 345{
d4625a19 346 if (!maybe_setutent ())
1a7fe2eb
FW
347 return NULL;
348
8a523922 349 struct utmp *pbuf;
8a523922 350
37a6a271
UD
351 if (! file_writable)
352 {
353 /* We must make the file descriptor writable before going on. */
354 const char *file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
355
c2284574 356 int new_fd = __open_nocancel
cef9b653 357 (file_name, O_RDWR | O_LARGEFILE | O_CLOEXEC);
37a6a271
UD
358 if (new_fd == -1)
359 return NULL;
360
d4625a19 361 if (__dup2 (new_fd, file_fd) < 0)
37a6a271 362 {
c181840c 363 __close_nocancel_nostatus (new_fd);
37a6a271
UD
364 return NULL;
365 }
c181840c 366 __close_nocancel_nostatus (new_fd);
37a6a271
UD
367 file_writable = true;
368 }
369
be6b16d9
FW
370 /* Exclude other writers before validating the cache. */
371 if (try_file_lock (file_fd, F_WRLCK))
372 return NULL;
373
8a523922 374 /* Find the correct place to insert the data. */
be6b16d9 375 bool found = false;
76a7c103 376 if (matches_last_entry (data))
1bfa05cf 377 {
d4625a19
FW
378 /* Read back the entry under the write lock. */
379 file_offset -= sizeof (last_entry);
380 ssize_t nbytes = read_last_entry ();
381 if (nbytes < 0)
1bfa05cf 382 {
be6b16d9 383 file_unlock (file_fd);
1bfa05cf
UD
384 return NULL;
385 }
d4625a19
FW
386
387 if (nbytes == 0)
388 /* End of file reached. */
389 found = false;
be6b16d9 390 else
76a7c103 391 found = matches_last_entry (data);
1bfa05cf 392 }
8a523922 393
be6b16d9 394 if (!found)
d4625a19 395 /* Search forward for the entry. */
be6b16d9 396 found = internal_getut_nolock (data) >= 0;
8a523922 397
d4625a19 398 off64_t write_offset;
be6b16d9 399 if (!found)
8a523922
UD
400 {
401 /* We append the next entry. */
d4625a19
FW
402 write_offset = __lseek64 (file_fd, 0, SEEK_END);
403
404 /* Round down to the next multiple of the entry size. This
405 ensures any partially-written record is overwritten by the
406 new record. */
407 write_offset = (write_offset / sizeof (struct utmp)
408 * sizeof (struct utmp));
8a523922
UD
409 }
410 else
d4625a19
FW
411 /* Overwrite last_entry. */
412 write_offset = file_offset - sizeof (struct utmp);
413
414 /* Write the new data. */
415 ssize_t nbytes;
416 if (__lseek64 (file_fd, write_offset, SEEK_SET) < 0
417 || (nbytes = __write_nocancel (file_fd, data, sizeof (struct utmp))) < 0)
8a523922 418 {
d4625a19
FW
419 /* There is no need to recover the file position because all
420 reads use pread64, and any future write is preceded by
421 another seek. */
422 file_unlock (file_fd);
423 return NULL;
8a523922
UD
424 }
425
d4625a19 426 if (nbytes != sizeof (struct utmp))
6591c335 427 {
8a523922
UD
428 /* If we appended a new record this is only partially written.
429 Remove it. */
be6b16d9 430 if (!found)
d4625a19
FW
431 (void) __ftruncate64 (file_fd, write_offset);
432 file_unlock (file_fd);
433 /* Assume that the write failure was due to missing disk
434 space. */
435 __set_errno (ENOSPC);
436 return NULL;
8a4b65b4 437 }
8a523922 438
5a3afa97 439 file_unlock (file_fd);
d4625a19
FW
440 file_offset = write_offset + sizeof (struct utmp);
441 pbuf = (struct utmp *) data;
8a523922
UD
442
443 return pbuf;
444}
445
446
1a7fe2eb
FW
447void
448__libc_endutent (void)
0413b54c 449{
1a7fe2eb
FW
450 if (file_fd >= 0)
451 {
452 __close_nocancel_nostatus (file_fd);
453 file_fd = -1;
454 }
0413b54c
UD
455}
456
457
1a7fe2eb
FW
458int
459__libc_updwtmp (const char *file, const struct utmp *utmp)
8a523922 460{
76b87c03 461 int result = -1;
2958e6cc 462 off64_t offset;
76b87c03 463 int fd;
f21acc89 464
76b87c03 465 /* Open WTMP file. */
533deafb 466 fd = __open_nocancel (file, O_WRONLY | O_LARGEFILE | O_CLOEXEC);
76b87c03
UD
467 if (fd < 0)
468 return -1;
469
628598be 470 if (try_file_lock (fd, F_WRLCK))
5a3afa97 471 {
5a3afa97
FW
472 __close_nocancel_nostatus (fd);
473 return -1;
474 }
f21acc89 475
d705269e 476 /* Remember original size of log file. */
2958e6cc 477 offset = __lseek64 (fd, 0, SEEK_END);
d705269e 478 if (offset % sizeof (struct utmp) != 0)
8a523922 479 {
d705269e 480 offset -= offset % sizeof (struct utmp);
2958e6cc 481 __ftruncate64 (fd, offset);
8a523922 482
2958e6cc 483 if (__lseek64 (fd, 0, SEEK_END) < 0)
d705269e 484 goto unlock_return;
76b87c03 485 }
8a523922 486
76b87c03
UD
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
489 will remain. */
c647fb88 490 if (__write_nocancel (fd, utmp, sizeof (struct utmp))
ce42435c 491 != sizeof (struct utmp))
76b87c03 492 {
2958e6cc 493 __ftruncate64 (fd, offset);
d705269e 494 goto unlock_return;
8a523922 495 }
76b87c03
UD
496
497 result = 0;
f21acc89 498
d705269e 499unlock_return:
341da5b4 500 file_unlock (fd);
76b87c03
UD
501
502 /* Close WTMP file. */
c181840c 503 __close_nocancel_nostatus (fd);
76b87c03
UD
504
505 return result;
8a523922 506}