Update copyright notices with scripts/update-copyrights.
[jlayton/glibc.git] / login / utmp_file.c
1 /* Copyright (C) 1996-2013 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    <http://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
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
45 /* Locking timeout.  */
46 #ifndef TIMEOUT
47 # define TIMEOUT 10
48 #endif
49
50 /* Do-nothing handler for locking timeout.  */
51 static void timeout_handler (int signum) {};
52
53 /* LOCK_FILE(fd, type) failure_statement
54      attempts to get a lock on the utmp file referenced by FD.  If it fails,
55      the failure_statement is executed, otherwise it is skipped.
56    LOCKING_FAILED()
57      jumps into the UNLOCK_FILE macro and ensures cleanup of LOCK_FILE.
58    UNLOCK_FILE(fd)
59      unlocks the utmp file referenced by FD and performs the cleanup of
60      LOCK_FILE.
61  */
62 #define LOCK_FILE(fd, type) \
63 {                                                                             \
64   struct flock fl;                                                            \
65   struct sigaction action, old_action;                                        \
66   unsigned int old_timeout;                                                   \
67                                                                               \
68   /* Cancel any existing alarm.  */                                           \
69   old_timeout = alarm (0);                                                    \
70                                                                               \
71   /* Establish signal handler.  */                                            \
72   action.sa_handler = timeout_handler;                                        \
73   __sigemptyset (&action.sa_mask);                                            \
74   action.sa_flags = 0;                                                        \
75   __sigaction (SIGALRM, &action, &old_action);                                \
76                                                                               \
77   alarm (TIMEOUT);                                                            \
78                                                                               \
79   /* Try to get the lock.  */                                                 \
80   memset (&fl, '\0', sizeof (struct flock));                                  \
81   fl.l_type = (type);                                                         \
82   fl.l_whence = SEEK_SET;                                                     \
83   if (fcntl_not_cancel ((fd), F_SETLKW, &fl) < 0)
84
85 #define LOCKING_FAILED() \
86   goto unalarm_return
87
88 #define UNLOCK_FILE(fd) \
89   /* Unlock the file.  */                                                     \
90   fl.l_type = F_UNLCK;                                                        \
91   fcntl_not_cancel ((fd), F_SETLKW, &fl);                                     \
92                                                                               \
93  unalarm_return:                                                              \
94   /* Reset the signal handler and alarm.  We must reset the alarm             \
95      before resetting the handler so our alarm does not generate a            \
96      spurious SIGALRM seen by the user.  However, we cannot just set          \
97      the user's old alarm before restoring the handler, because then          \
98      it's possible our handler could catch the user alarm's SIGARLM           \
99      and then the user would never see the signal he expected.  */            \
100   alarm (0);                                                                  \
101   __sigaction (SIGALRM, &old_action, NULL);                                   \
102   if (old_timeout != 0)                                                       \
103     alarm (old_timeout);                                                      \
104 } while (0)
105
106
107 /* Functions defined here.  */
108 static int setutent_file (void);
109 static int getutent_r_file (struct utmp *buffer, struct utmp **result);
110 static int getutid_r_file (const struct utmp *key, struct utmp *buffer,
111                            struct utmp **result);
112 static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
113                              struct utmp **result);
114 static struct utmp *pututline_file (const struct utmp *data);
115 static void endutent_file (void);
116 static int updwtmp_file (const char *file, const struct utmp *utmp);
117
118 /* Jump table for file functions.  */
119 const struct utfuncs __libc_utmp_file_functions =
120 {
121   setutent_file,
122   getutent_r_file,
123   getutid_r_file,
124   getutline_r_file,
125   pututline_file,
126   endutent_file,
127   updwtmp_file
128 };
129
130
131 #ifndef TRANSFORM_UTMP_FILE_NAME
132 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
133 #endif
134
135 static int
136 setutent_file (void)
137 {
138   if (file_fd < 0)
139     {
140       const char *file_name;
141
142       file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
143
144 #ifdef O_CLOEXEC
145 # define O_flags O_LARGEFILE | O_CLOEXEC
146 #else
147 # define O_flags O_LARGEFILE
148 #endif
149       file_writable = false;
150       file_fd = open_not_cancel_2 (file_name, O_RDONLY | O_flags);
151       if (file_fd == -1)
152         return 0;
153
154 #ifndef __ASSUME_O_CLOEXEC
155 # ifdef O_CLOEXEC
156       if (__have_o_cloexec <= 0)
157 # endif
158         {
159           /* We have to make sure the file is `closed on exec'.  */
160           int result = fcntl_not_cancel (file_fd, F_GETFD, 0);
161           if (result >= 0)
162             {
163 # ifdef O_CLOEXEC
164               if (__have_o_cloexec == 0)
165                 __have_o_cloexec = (result & FD_CLOEXEC) ? 1 : -1;
166
167               if (__have_o_cloexec < 0)
168 # endif
169                 result = fcntl_not_cancel (file_fd, F_SETFD,
170                                            result | FD_CLOEXEC);
171             }
172
173           if (result == -1)
174             {
175               close_not_cancel_no_status (file_fd);
176               return 0;
177             }
178         }
179 #endif
180     }
181
182   __lseek64 (file_fd, 0, SEEK_SET);
183   file_offset = 0;
184
185   /* Make sure the entry won't match.  */
186 #if _HAVE_UT_TYPE - 0
187   last_entry.ut_type = -1;
188 #else
189   last_entry.ut_line[0] = '\177';
190 # if _HAVE_UT_ID - 0
191   last_entry.ut_id[0] = '\0';
192 # endif
193 #endif
194
195   return 1;
196 }
197
198
199 static int
200 getutent_r_file (struct utmp *buffer, struct utmp **result)
201 {
202   ssize_t nbytes;
203
204   assert (file_fd >= 0);
205
206   if (file_offset == -1l)
207     {
208       /* Not available.  */
209       *result = NULL;
210       return -1;
211     }
212
213   LOCK_FILE (file_fd, F_RDLCK)
214     {
215       nbytes = 0;
216       LOCKING_FAILED ();
217     }
218
219   /* Read the next entry.  */
220   nbytes = read_not_cancel (file_fd, &last_entry, sizeof (struct utmp));
221
222   UNLOCK_FILE (file_fd);
223
224   if (nbytes != sizeof (struct utmp))
225     {
226       if (nbytes != 0)
227         file_offset = -1l;
228       *result = NULL;
229       return -1;
230     }
231
232   /* Update position pointer.  */
233   file_offset += sizeof (struct utmp);
234
235   memcpy (buffer, &last_entry, sizeof (struct utmp));
236   *result = buffer;
237
238   return 0;
239 }
240
241
242 static int
243 internal_getut_r (const struct utmp *id, struct utmp *buffer,
244                   bool *lock_failed)
245 {
246   int result = -1;
247
248   LOCK_FILE (file_fd, F_RDLCK)
249     {
250       *lock_failed = true;
251       LOCKING_FAILED ();
252     }
253
254 #if _HAVE_UT_TYPE - 0
255   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
256       || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
257     {
258       /* Search for next entry with type RUN_LVL, BOOT_TIME,
259          OLD_TIME, or NEW_TIME.  */
260
261       while (1)
262         {
263           /* Read the next entry.  */
264           if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
265               != sizeof (struct utmp))
266             {
267               __set_errno (ESRCH);
268               file_offset = -1l;
269               goto unlock_return;
270             }
271           file_offset += sizeof (struct utmp);
272
273           if (id->ut_type == buffer->ut_type)
274             break;
275         }
276     }
277   else
278 #endif /* _HAVE_UT_TYPE */
279     {
280       /* Search for the next entry with the specified ID and with type
281          INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS.  */
282
283       while (1)
284         {
285           /* Read the next entry.  */
286           if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
287               != sizeof (struct utmp))
288             {
289               __set_errno (ESRCH);
290               file_offset = -1l;
291               goto unlock_return;
292             }
293           file_offset += sizeof (struct utmp);
294
295           if (__utmp_equal (buffer, id))
296             break;
297         }
298     }
299
300   result = 0;
301
302 unlock_return:
303   UNLOCK_FILE (file_fd);
304
305   return result;
306 }
307
308
309 /* For implementing this function we don't use the getutent_r function
310    because we can avoid the reposition on every new entry this way.  */
311 static int
312 getutid_r_file (const struct utmp *id, struct utmp *buffer,
313                 struct utmp **result)
314 {
315   assert (file_fd >= 0);
316
317   if (file_offset == -1l)
318     {
319       *result = NULL;
320       return -1;
321     }
322
323   /* We don't have to distinguish whether we can lock the file or
324      whether there is no entry.  */
325   bool lock_failed = false;
326   if (internal_getut_r (id, &last_entry, &lock_failed) < 0)
327     {
328       *result = NULL;
329       return -1;
330     }
331
332   memcpy (buffer, &last_entry, sizeof (struct utmp));
333   *result = buffer;
334
335   return 0;
336 }
337
338
339 /* For implementing this function we don't use the getutent_r function
340    because we can avoid the reposition on every new entry this way.  */
341 static int
342 getutline_r_file (const struct utmp *line, struct utmp *buffer,
343                   struct utmp **result)
344 {
345   assert (file_fd >= 0);
346
347   if (file_offset == -1l)
348     {
349       *result = NULL;
350       return -1;
351     }
352
353   LOCK_FILE (file_fd, F_RDLCK)
354     {
355       *result = NULL;
356       LOCKING_FAILED ();
357     }
358
359   while (1)
360     {
361       /* Read the next entry.  */
362       if (read_not_cancel (file_fd, &last_entry, sizeof (struct utmp))
363           != sizeof (struct utmp))
364         {
365           __set_errno (ESRCH);
366           file_offset = -1l;
367           *result = NULL;
368           goto unlock_return;
369         }
370       file_offset += sizeof (struct utmp);
371
372       /* Stop if we found a user or login entry.  */
373       if (
374 #if _HAVE_UT_TYPE - 0
375           (last_entry.ut_type == USER_PROCESS
376            || last_entry.ut_type == LOGIN_PROCESS)
377           &&
378 #endif
379           !strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line))
380         break;
381     }
382
383   memcpy (buffer, &last_entry, sizeof (struct utmp));
384   *result = buffer;
385
386 unlock_return:
387   UNLOCK_FILE (file_fd);
388
389   return ((*result == NULL) ? -1 : 0);
390 }
391
392
393 static struct utmp *
394 pututline_file (const struct utmp *data)
395 {
396   struct utmp buffer;
397   struct utmp *pbuf;
398   int found;
399
400   assert (file_fd >= 0);
401
402   if (! file_writable)
403     {
404       /* We must make the file descriptor writable before going on.  */
405       const char *file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
406
407       int new_fd = open_not_cancel_2 (file_name, O_RDWR | O_flags);
408       if (new_fd == -1)
409         return NULL;
410
411 #ifndef __ASSUME_O_CLOEXEC
412 # ifdef O_CLOEXEC
413       if (__have_o_cloexec <= 0)
414 # endif
415         {
416           /* We have to make sure the file is `closed on exec'.  */
417           int result = fcntl_not_cancel (file_fd, F_GETFD, 0);
418           if (result >= 0)
419             {
420 # ifdef O_CLOEXEC
421               if (__have_o_cloexec == 0)
422                 __have_o_cloexec = (result & FD_CLOEXEC) ? 1 : -1;
423
424               if (__have_o_cloexec < 0)
425 # endif
426                 result = fcntl_not_cancel (file_fd, F_SETFD,
427                                            result | FD_CLOEXEC);
428             }
429
430           if (result == -1)
431             {
432               close_not_cancel_no_status (file_fd);
433               return NULL;
434             }
435         }
436 #endif
437
438       if (__lseek64 (new_fd, __lseek64 (file_fd, 0, SEEK_CUR), SEEK_SET) == -1
439           || __dup2 (new_fd, file_fd) < 0)
440         {
441           close_not_cancel_no_status (new_fd);
442           return NULL;
443         }
444       close_not_cancel_no_status (new_fd);
445       file_writable = true;
446     }
447
448   /* Find the correct place to insert the data.  */
449   if (file_offset > 0
450       && (
451 #if _HAVE_UT_TYPE - 0
452           (last_entry.ut_type == data->ut_type
453            && (last_entry.ut_type == RUN_LVL
454                || last_entry.ut_type == BOOT_TIME
455                || last_entry.ut_type == OLD_TIME
456                || last_entry.ut_type == NEW_TIME))
457           ||
458 #endif
459           __utmp_equal (&last_entry, data)))
460     found = 1;
461   else
462     {
463       bool lock_failed = false;
464       found = internal_getut_r (data, &buffer, &lock_failed);
465
466       if (__builtin_expect (lock_failed, false))
467         {
468           __set_errno (EAGAIN);
469           return NULL;
470         }
471     }
472
473   LOCK_FILE (file_fd, F_WRLCK)
474     {
475       pbuf = NULL;
476       LOCKING_FAILED ();
477     }
478
479   if (found < 0)
480     {
481       /* We append the next entry.  */
482       file_offset = __lseek64 (file_fd, 0, SEEK_END);
483       if (file_offset % sizeof (struct utmp) != 0)
484         {
485           file_offset -= file_offset % sizeof (struct utmp);
486           __ftruncate64 (file_fd, file_offset);
487
488           if (__lseek64 (file_fd, 0, SEEK_END) < 0)
489             {
490               pbuf = NULL;
491               goto unlock_return;
492             }
493         }
494     }
495   else
496     {
497       /* We replace the just read entry.  */
498       file_offset -= sizeof (struct utmp);
499       __lseek64 (file_fd, file_offset, SEEK_SET);
500     }
501
502   /* Write the new data.  */
503   if (write_not_cancel (file_fd, data, sizeof (struct utmp))
504       != sizeof (struct utmp))
505     {
506       /* If we appended a new record this is only partially written.
507          Remove it.  */
508       if (found < 0)
509         (void) __ftruncate64 (file_fd, file_offset);
510       pbuf = NULL;
511     }
512   else
513     {
514       file_offset += sizeof (struct utmp);
515       pbuf = (struct utmp *) data;
516     }
517
518  unlock_return:
519   UNLOCK_FILE (file_fd);
520
521   return pbuf;
522 }
523
524
525 static void
526 endutent_file (void)
527 {
528   assert (file_fd >= 0);
529
530   close_not_cancel_no_status (file_fd);
531   file_fd = -1;
532 }
533
534
535 static int
536 updwtmp_file (const char *file, const struct utmp *utmp)
537 {
538   int result = -1;
539   off64_t offset;
540   int fd;
541
542   /* Open WTMP file.  */
543   fd = open_not_cancel_2 (file, O_WRONLY | O_LARGEFILE);
544   if (fd < 0)
545     return -1;
546
547   LOCK_FILE (fd, F_WRLCK)
548     LOCKING_FAILED ();
549
550   /* Remember original size of log file.  */
551   offset = __lseek64 (fd, 0, SEEK_END);
552   if (offset % sizeof (struct utmp) != 0)
553     {
554       offset -= offset % sizeof (struct utmp);
555       __ftruncate64 (fd, offset);
556
557       if (__lseek64 (fd, 0, SEEK_END) < 0)
558         goto unlock_return;
559     }
560
561   /* Write the entry.  If we can't write all the bytes, reset the file
562      size back to the original size.  That way, no partial entries
563      will remain.  */
564   if (write_not_cancel (fd, utmp, sizeof (struct utmp))
565       != sizeof (struct utmp))
566     {
567       __ftruncate64 (fd, offset);
568       goto unlock_return;
569     }
570
571   result = 0;
572
573 unlock_return:
574   UNLOCK_FILE (fd);
575
576   /* Close WTMP file.  */
577   close_not_cancel_no_status (fd);
578
579   return result;
580 }