1 /* Copyright (C) 1996-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
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.
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
12    Lesser General Public License for more details.
13 
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/>.  */
17 
18 #include <assert.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <signal.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <utmp.h>
27 #include <not-cancel.h>
28 #include <kernel-features.h>
29 #include <sigsetops.h>
30 #include <not-cancel.h>
31 
32 #include "utmp-private.h"
33 #include "utmp-equal.h"
34 
35 
36 /* Descriptor for the file and position.  */
37 static int file_fd = -1;
38 static bool file_writable;
39 static off64_t file_offset;
40 
41 /* Cache for the last read entry.  */
42 static struct utmp last_entry;
43 
44 /* Returns true if *ENTRY matches last_entry, based on
45    data->ut_type.  */
46 static bool
matches_last_entry(const struct utmp * data)47 matches_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 }
63 
64 /* Locking timeout.  */
65 #ifndef TIMEOUT
66 # define TIMEOUT 10
67 #endif
68 
69 /* Do-nothing handler for locking timeout.  */
timeout_handler(int signum)70 static void timeout_handler (int signum) {};
71 
72 
73 /* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
74    operation failed and recovery needs to be performed.
75 
76    file_unlock (FD) removes the lock (which must have been
77    successfully acquired). */
78 
79 static bool
try_file_lock(int fd,int type)80 try_file_lock (int fd, int type)
81 {
82   /* Cancel any existing alarm.  */
83   int old_timeout = alarm (0);
84 
85   /* Establish signal handler.  */
86   struct sigaction old_action;
87   struct sigaction action;
88   action.sa_handler = timeout_handler;
89   __sigemptyset (&action.sa_mask);
90   action.sa_flags = 0;
91   __sigaction (SIGALRM, &action, &old_action);
92 
93   alarm (TIMEOUT);
94 
95   /* Try to get the lock.  */
96  struct flock64 fl =
97    {
98     .l_type = type,
99     .l_whence = SEEK_SET,
100    };
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;
118 }
119 
120 static void
file_unlock(int fd)121 file_unlock (int fd)
122 {
123   struct flock64 fl =
124     {
125       .l_type = F_UNLCK,
126     };
127   __fcntl64_nocancel (fd, F_SETLKW, &fl);
128 }
129 
130 #ifndef TRANSFORM_UTMP_FILE_NAME
131 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
132 #endif
133 
134 int
__libc_setutent(void)135 __libc_setutent (void)
136 {
137   if (file_fd < 0)
138     {
139       const char *file_name;
140 
141       file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
142 
143       file_writable = false;
144       file_fd = __open_nocancel
145 	(file_name, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
146       if (file_fd == -1)
147 	return 0;
148     }
149 
150   __lseek64 (file_fd, 0, SEEK_SET);
151   file_offset = 0;
152 
153   return 1;
154 }
155 
156 /* Preform initialization if necessary.  */
157 static bool
maybe_setutent(void)158 maybe_setutent (void)
159 {
160   return file_fd >= 0 || __libc_setutent ();
161 }
162 
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.  */
167 static ssize_t
read_last_entry(void)168 read_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 
186 int
__libc_getutent_r(struct utmp * buffer,struct utmp ** result)187 __libc_getutent_r (struct utmp *buffer, struct utmp **result)
188 {
189   int saved_errno = errno;
190 
191   if (!maybe_setutent ())
192     {
193       /* Not available.  */
194       *result = NULL;
195       return -1;
196     }
197 
198   if (try_file_lock (file_fd, F_RDLCK))
199     return -1;
200 
201   ssize_t nbytes = read_last_entry ();
202   file_unlock (file_fd);
203 
204   if (nbytes <= 0)		/* Read error or EOF.  */
205     {
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);
211       *result = NULL;
212       return -1;
213     }
214 
215   memcpy (buffer, &last_entry, sizeof (struct utmp));
216   *result = buffer;
217 
218   return 0;
219 }
220 
221 
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.  */
225 static int
internal_getut_nolock(const struct utmp * id)226 internal_getut_nolock (const struct utmp *id)
227 {
228   while (1)
229     {
230       ssize_t nbytes = read_last_entry ();
231       if (nbytes < 0)
232 	return -1;
233       if (nbytes == 0)
234 	{
235 	  /* End of file reached.  */
236 	  __set_errno (ESRCH);
237 	  return -1;
238 	}
239 
240       if (matches_last_entry (id))
241 	break;
242     }
243 
244   return 0;
245 }
246 
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.  */
250 static int
internal_getut_r(const struct utmp * id,bool * lock_failed)251 internal_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     }
258 
259   int result = internal_getut_nolock (id);
260   file_unlock (file_fd);
261   return result;
262 }
263 
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.  */
266 int
__libc_getutid_r(const struct utmp * id,struct utmp * buffer,struct utmp ** result)267 __libc_getutid_r (const struct utmp *id, struct utmp *buffer,
268 		  struct utmp **result)
269 {
270   if (!maybe_setutent ())
271     {
272       *result = NULL;
273       return -1;
274     }
275 
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)
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 
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.  */
293 int
__libc_getutline_r(const struct utmp * line,struct utmp * buffer,struct utmp ** result)294 __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
295 		    struct utmp **result)
296 {
297   if (!maybe_setutent ())
298     {
299       *result = NULL;
300       return -1;
301     }
302 
303   if (try_file_lock (file_fd, F_RDLCK))
304     {
305       *result = NULL;
306       return -1;
307     }
308 
309   while (1)
310     {
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)
319 	{
320 	  /* End of file reached.  */
321 	  file_unlock (file_fd);
322 	  __set_errno (ESRCH);
323 	  *result = NULL;
324 	  return -1;
325 	}
326 
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)
331 	      == 0))
332 	break;
333     }
334 
335   file_unlock (file_fd);
336   memcpy (buffer, &last_entry, sizeof (struct utmp));
337   *result = buffer;
338 
339   return 0;
340 }
341 
342 
343 struct utmp *
__libc_pututline(const struct utmp * data)344 __libc_pututline (const struct utmp *data)
345 {
346   if (!maybe_setutent ())
347     return NULL;
348 
349   struct utmp *pbuf;
350 
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 
356       int new_fd = __open_nocancel
357 	(file_name, O_RDWR | O_LARGEFILE | O_CLOEXEC);
358       if (new_fd == -1)
359 	return NULL;
360 
361       if (__dup2 (new_fd, file_fd) < 0)
362 	{
363 	  __close_nocancel_nostatus (new_fd);
364 	  return NULL;
365 	}
366       __close_nocancel_nostatus (new_fd);
367       file_writable = true;
368     }
369 
370   /* Exclude other writers before validating the cache.  */
371   if (try_file_lock (file_fd, F_WRLCK))
372     return NULL;
373 
374   /* Find the correct place to insert the data.  */
375   bool found = false;
376   if (matches_last_entry (data))
377     {
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)
382 	{
383 	  file_unlock (file_fd);
384 	  return NULL;
385 	}
386 
387       if (nbytes == 0)
388 	/* End of file reached.  */
389 	found = false;
390       else
391 	found = matches_last_entry (data);
392     }
393 
394   if (!found)
395     /* Search forward for the entry.  */
396     found = internal_getut_nolock (data) >= 0;
397 
398   off64_t write_offset;
399   if (!found)
400     {
401       /* We append the next entry.  */
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));
409     }
410   else
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)
418     {
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;
424     }
425 
426   if (nbytes != sizeof (struct utmp))
427     {
428       /* If we appended a new record this is only partially written.
429 	 Remove it.  */
430       if (!found)
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;
437     }
438 
439   file_unlock (file_fd);
440   file_offset = write_offset + sizeof (struct utmp);
441   pbuf = (struct utmp *) data;
442 
443   return pbuf;
444 }
445 
446 
447 void
__libc_endutent(void)448 __libc_endutent (void)
449 {
450   if (file_fd >= 0)
451     {
452       __close_nocancel_nostatus (file_fd);
453       file_fd = -1;
454     }
455 }
456 
457 
458 int
__libc_updwtmp(const char * file,const struct utmp * utmp)459 __libc_updwtmp (const char *file, const struct utmp *utmp)
460 {
461   int result = -1;
462   off64_t offset;
463   int fd;
464 
465   /* Open WTMP file.  */
466   fd = __open_nocancel (file, O_WRONLY | O_LARGEFILE);
467   if (fd < 0)
468     return -1;
469 
470   if (try_file_lock (fd, F_WRLCK))
471     {
472       __close_nocancel_nostatus (fd);
473       return -1;
474     }
475 
476   /* Remember original size of log file.  */
477   offset = __lseek64 (fd, 0, SEEK_END);
478   if (offset % sizeof (struct utmp) != 0)
479     {
480       offset -= offset % sizeof (struct utmp);
481       __ftruncate64 (fd, offset);
482 
483       if (__lseek64 (fd, 0, SEEK_END) < 0)
484 	goto unlock_return;
485     }
486 
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.  */
490   if (__write_nocancel (fd, utmp, sizeof (struct utmp))
491       != sizeof (struct utmp))
492     {
493       __ftruncate64 (fd, offset);
494       goto unlock_return;
495     }
496 
497   result = 0;
498 
499 unlock_return:
500   file_unlock (fd);
501 
502   /* Close WTMP file.  */
503   __close_nocancel_nostatus (fd);
504 
505   return result;
506 }
507