e6272334781ce51337c2e910460b7717c5458c23
[platform/upstream/glibc.git] / login / utmp_file.c
1 /* Copyright (C) 1996-2019 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.
5
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.
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
14    Lesser General Public License for more details.
15
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/>.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <utmp.h>
29 #include <not-cancel.h>
30 #include <kernel-features.h>
31 #include <sigsetops.h>
32 #include <not-cancel.h>
33
34 #include "utmp-private.h"
35 #include "utmp-equal.h"
36
37
38 /* Descriptor for the file and position.  */
39 static int file_fd = -1;
40 static bool file_writable;
41 static off64_t file_offset;
42
43 /* Cache for the last read entry.  */
44 static struct utmp last_entry;
45
46
47 /* Locking timeout.  */
48 #ifndef TIMEOUT
49 # define TIMEOUT 10
50 #endif
51
52 /* Do-nothing handler for locking timeout.  */
53 static void timeout_handler (int signum) {};
54
55
56 /* try_file_lock (LOCKING, FD, TYPE) returns true if the locking
57    operation failed and recovery needs to be performed.
58
59    file_unlock (FD) removes the lock (which must have been
60    successfully acquired). */
61
62 static bool
63 try_file_lock (int fd, int type)
64 {
65   /* Cancel any existing alarm.  */
66   int old_timeout = alarm (0);
67
68   /* Establish signal handler.  */
69   struct sigaction old_action;
70   struct sigaction action;
71   action.sa_handler = timeout_handler;
72   __sigemptyset (&action.sa_mask);
73   action.sa_flags = 0;
74   __sigaction (SIGALRM, &action, &old_action);
75
76   alarm (TIMEOUT);
77
78   /* Try to get the lock.  */
79  struct flock64 fl =
80    {
81     .l_type = type,
82     .l_whence = SEEK_SET,
83    };
84
85  bool status = __fcntl64_nocancel (fd, F_SETLKW, &fl) < 0;
86  int saved_errno = errno;
87
88  /* Reset the signal handler and alarm.  We must reset the alarm
89     before resetting the handler so our alarm does not generate a
90     spurious SIGALRM seen by the user.  However, we cannot just set
91     the user's old alarm before restoring the handler, because then
92     it's possible our handler could catch the user alarm's SIGARLM and
93     then the user would never see the signal he expected.  */
94   alarm (0);
95   __sigaction (SIGALRM, &old_action, NULL);
96   if (old_timeout != 0)
97     alarm (old_timeout);
98
99   __set_errno (saved_errno);
100   return status;
101 }
102
103 static void
104 file_unlock (int fd)
105 {
106   struct flock64 fl =
107     {
108       .l_type = F_UNLCK,
109     };
110   __fcntl64_nocancel (fd, F_SETLKW, &fl);
111 }
112
113 #ifndef TRANSFORM_UTMP_FILE_NAME
114 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
115 #endif
116
117 int
118 __libc_setutent (void)
119 {
120   if (file_fd < 0)
121     {
122       const char *file_name;
123
124       file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
125
126       file_writable = false;
127       file_fd = __open_nocancel
128         (file_name, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
129       if (file_fd == -1)
130         return 0;
131     }
132
133   __lseek64 (file_fd, 0, SEEK_SET);
134   file_offset = 0;
135
136   /* Make sure the entry won't match.  */
137   last_entry.ut_type = -1;
138
139   return 1;
140 }
141
142 /* Preform initialization if necessary.  */
143 static bool
144 maybe_setutent (void)
145 {
146   return file_fd >= 0 || __libc_setutent ();
147 }
148
149 int
150 __libc_getutent_r (struct utmp *buffer, struct utmp **result)
151 {
152   ssize_t nbytes;
153
154   if (!maybe_setutent () || file_offset == -1l)
155     {
156       /* Not available.  */
157       *result = NULL;
158       return -1;
159     }
160
161   if (try_file_lock (file_fd, F_RDLCK))
162     nbytes = 0;
163   else
164     {
165       /* Read the next entry.  */
166       nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
167       file_unlock (file_fd);
168     }
169
170   if (nbytes != sizeof (struct utmp))
171     {
172       if (nbytes != 0)
173         file_offset = -1l;
174       *result = NULL;
175       return -1;
176     }
177
178   /* Update position pointer.  */
179   file_offset += sizeof (struct utmp);
180
181   memcpy (buffer, &last_entry, sizeof (struct utmp));
182   *result = buffer;
183
184   return 0;
185 }
186
187
188 /* Search for *ID, updating last_entry and file_offset.  Return 0 on
189    success and -1 on failure.  If the locking operation failed, write
190    true to *LOCK_FAILED.  */
191 static int
192 internal_getut_r (const struct utmp *id, bool *lock_failed)
193 {
194   int result = -1;
195
196   if (try_file_lock (file_fd, F_RDLCK))
197     {
198       *lock_failed = true;
199       return -1;
200     }
201
202   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
203       || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
204     {
205       /* Search for next entry with type RUN_LVL, BOOT_TIME,
206          OLD_TIME, or NEW_TIME.  */
207
208       while (1)
209         {
210           /* Read the next entry.  */
211           if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
212               != sizeof (struct utmp))
213             {
214               __set_errno (ESRCH);
215               file_offset = -1l;
216               goto unlock_return;
217             }
218           file_offset += sizeof (struct utmp);
219
220           if (id->ut_type == last_entry.ut_type)
221             break;
222         }
223     }
224   else
225     {
226       /* Search for the next entry with the specified ID and with type
227          INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS.  */
228
229       while (1)
230         {
231           /* Read the next entry.  */
232           if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
233               != sizeof (struct utmp))
234             {
235               __set_errno (ESRCH);
236               file_offset = -1l;
237               goto unlock_return;
238             }
239           file_offset += sizeof (struct utmp);
240
241           if (__utmp_equal (&last_entry, id))
242             break;
243         }
244     }
245
246   result = 0;
247
248 unlock_return:
249   file_unlock (file_fd);
250
251   return result;
252 }
253
254
255 /* For implementing this function we don't use the getutent_r function
256    because we can avoid the reposition on every new entry this way.  */
257 int
258 __libc_getutid_r (const struct utmp *id, struct utmp *buffer,
259                   struct utmp **result)
260 {
261   if (!maybe_setutent () || file_offset == -1l)
262     {
263       *result = NULL;
264       return -1;
265     }
266
267   /* We don't have to distinguish whether we can lock the file or
268      whether there is no entry.  */
269   bool lock_failed = false;
270   if (internal_getut_r (id, &lock_failed) < 0)
271     {
272       *result = NULL;
273       return -1;
274     }
275
276   memcpy (buffer, &last_entry, sizeof (struct utmp));
277   *result = buffer;
278
279   return 0;
280 }
281
282
283 /* For implementing this function we don't use the getutent_r function
284    because we can avoid the reposition on every new entry this way.  */
285 int
286 __libc_getutline_r (const struct utmp *line, struct utmp *buffer,
287                     struct utmp **result)
288 {
289   if (!maybe_setutent () || file_offset == -1l)
290     {
291       *result = NULL;
292       return -1;
293     }
294
295   if (try_file_lock (file_fd, F_RDLCK))
296     {
297       *result = NULL;
298       return -1;
299     }
300
301   while (1)
302     {
303       /* Read the next entry.  */
304       if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
305           != sizeof (struct utmp))
306         {
307           __set_errno (ESRCH);
308           file_offset = -1l;
309           *result = NULL;
310           goto unlock_return;
311         }
312       file_offset += sizeof (struct utmp);
313
314       /* Stop if we found a user or login entry.  */
315       if ((last_entry.ut_type == USER_PROCESS
316            || last_entry.ut_type == LOGIN_PROCESS)
317           && (strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line)
318               == 0))
319         break;
320     }
321
322   memcpy (buffer, &last_entry, sizeof (struct utmp));
323   *result = buffer;
324
325 unlock_return:
326   file_unlock (file_fd);
327
328   return ((*result == NULL) ? -1 : 0);
329 }
330
331
332 struct utmp *
333 __libc_pututline (const struct utmp *data)
334 {
335   if (!maybe_setutent () || file_offset == -1l)
336     return NULL;
337
338   struct utmp *pbuf;
339   int found;
340
341   if (! file_writable)
342     {
343       /* We must make the file descriptor writable before going on.  */
344       const char *file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
345
346       int new_fd = __open_nocancel
347         (file_name, O_RDWR | O_LARGEFILE | O_CLOEXEC);
348       if (new_fd == -1)
349         return NULL;
350
351       if (__lseek64 (new_fd, __lseek64 (file_fd, 0, SEEK_CUR), SEEK_SET) == -1
352           || __dup2 (new_fd, file_fd) < 0)
353         {
354           __close_nocancel_nostatus (new_fd);
355           return NULL;
356         }
357       __close_nocancel_nostatus (new_fd);
358       file_writable = true;
359     }
360
361   /* Find the correct place to insert the data.  */
362   if (file_offset > 0
363       && ((last_entry.ut_type == data->ut_type
364            && (last_entry.ut_type == RUN_LVL
365                || last_entry.ut_type == BOOT_TIME
366                || last_entry.ut_type == OLD_TIME
367                || last_entry.ut_type == NEW_TIME))
368           || __utmp_equal (&last_entry, data)))
369     found = 1;
370   else
371     {
372       bool lock_failed = false;
373       found = internal_getut_r (data, &lock_failed);
374
375       if (__builtin_expect (lock_failed, false))
376         {
377           __set_errno (EAGAIN);
378           return NULL;
379         }
380     }
381
382   if (try_file_lock (file_fd, F_WRLCK))
383     return NULL;
384
385   if (found < 0)
386     {
387       /* We append the next entry.  */
388       file_offset = __lseek64 (file_fd, 0, SEEK_END);
389       if (file_offset % sizeof (struct utmp) != 0)
390         {
391           file_offset -= file_offset % sizeof (struct utmp);
392           __ftruncate64 (file_fd, file_offset);
393
394           if (__lseek64 (file_fd, 0, SEEK_END) < 0)
395             {
396               pbuf = NULL;
397               goto unlock_return;
398             }
399         }
400     }
401   else
402     {
403       /* We replace the just read entry.  */
404       file_offset -= sizeof (struct utmp);
405       __lseek64 (file_fd, file_offset, SEEK_SET);
406     }
407
408   /* Write the new data.  */
409   if (__write_nocancel (file_fd, data, sizeof (struct utmp))
410       != sizeof (struct utmp))
411     {
412       /* If we appended a new record this is only partially written.
413          Remove it.  */
414       if (found < 0)
415         (void) __ftruncate64 (file_fd, file_offset);
416       pbuf = NULL;
417     }
418   else
419     {
420       file_offset += sizeof (struct utmp);
421       pbuf = (struct utmp *) data;
422     }
423
424  unlock_return:
425   file_unlock (file_fd);
426
427   return pbuf;
428 }
429
430
431 void
432 __libc_endutent (void)
433 {
434   if (file_fd >= 0)
435     {
436       __close_nocancel_nostatus (file_fd);
437       file_fd = -1;
438     }
439 }
440
441
442 int
443 __libc_updwtmp (const char *file, const struct utmp *utmp)
444 {
445   int result = -1;
446   off64_t offset;
447   int fd;
448
449   /* Open WTMP file.  */
450   fd = __open_nocancel (file, O_WRONLY | O_LARGEFILE);
451   if (fd < 0)
452     return -1;
453
454   if (try_file_lock (fd, F_WRLCK))
455     {
456       __close_nocancel_nostatus (fd);
457       return -1;
458     }
459
460   /* Remember original size of log file.  */
461   offset = __lseek64 (fd, 0, SEEK_END);
462   if (offset % sizeof (struct utmp) != 0)
463     {
464       offset -= offset % sizeof (struct utmp);
465       __ftruncate64 (fd, offset);
466
467       if (__lseek64 (fd, 0, SEEK_END) < 0)
468         goto unlock_return;
469     }
470
471   /* Write the entry.  If we can't write all the bytes, reset the file
472      size back to the original size.  That way, no partial entries
473      will remain.  */
474   if (__write_nocancel (fd, utmp, sizeof (struct utmp))
475       != sizeof (struct utmp))
476     {
477       __ftruncate64 (fd, offset);
478       goto unlock_return;
479     }
480
481   result = 0;
482
483 unlock_return:
484   file_unlock (fd);
485
486   /* Close WTMP file.  */
487   __close_nocancel_nostatus (fd);
488
489   return result;
490 }