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