]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - login/utmp_file.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / login / utmp_file.c
index 4a9e4094545fe21660271f51867e1ed2f8e639bf..b19d3caf0d8a028caa790d43ba8f54c964c5c8d5 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996-2002, 2003, 2004, 2007 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>
    and Paul Janzen <pcj@primenet.com>, 1996.
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <utmp.h>
 #include <not-cancel.h>
 #include <kernel-features.h>
+#include <sigsetops.h>
+#include <not-cancel.h>
 
 #include "utmp-private.h"
 #include "utmp-equal.h"
@@ -35,6 +37,7 @@
 
 /* Descriptor for the file and position.  */
 static int file_fd = -1;
+static bool file_writable;
 static off64_t file_offset;
 
 /* Cache for the last read entry.  */
@@ -43,7 +46,7 @@ static struct utmp last_entry;
 
 /* Locking timeout.  */
 #ifndef TIMEOUT
-# define TIMEOUT 1
+# define TIMEOUT 10
 #endif
 
 /* Do-nothing handler for locking timeout.  */
@@ -79,7 +82,7 @@ static void timeout_handler (int signum) {};
   memset (&fl, '\0', sizeof (struct flock));                                 \
   fl.l_type = (type);                                                        \
   fl.l_whence = SEEK_SET;                                                    \
-  if (fcntl_not_cancel ((fd), F_SETLKW, &fl) < 0)
+  if (__fcntl64_nocancel ((fd), F_SETLKW, &fl) < 0)
 
 #define LOCKING_FAILED() \
   goto unalarm_return
@@ -87,7 +90,7 @@ static void timeout_handler (int signum) {};
 #define UNLOCK_FILE(fd) \
   /* Unlock the file.  */                                                    \
   fl.l_type = F_UNLCK;                                                       \
-  fcntl_not_cancel ((fd), F_SETLKW, &fl);                                    \
+  __fcntl64_nocancel ((fd), F_SETLKW, &fl);                                  \
                                                                              \
  unalarm_return:                                                             \
   /* Reset the signal handler and alarm.  We must reset the alarm            \
@@ -137,51 +140,14 @@ setutent_file (void)
   if (file_fd < 0)
     {
       const char *file_name;
-      int result;
 
       file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
 
-#ifdef O_CLOEXEC
-# define O_flags O_LARGEFILE | O_CLOEXEC
-#else
-# define O_flags O_LARGEFILE
-#endif
-      file_fd = open_not_cancel_2 (file_name, O_RDWR | O_flags);
+      file_writable = false;
+      file_fd = __open_nocancel
+       (file_name, O_RDONLY | O_LARGEFILE | O_CLOEXEC);
       if (file_fd == -1)
-       {
-         /* Hhm, read-write access did not work.  Try read-only.  */
-         file_fd = open_not_cancel_2 (file_name, O_RDONLY | O_flags);
-         if (file_fd == -1)
-           return 0;
-       }
-
-#ifndef __ASSUME_O_CLOEXEC
-# ifdef O_CLOEXEC
-      static int have_o_cloexec;
-
-      if (have_o_cloexec <= 0)
-# endif
-       {
-         /* We have to make sure the file is `closed on exec'.  */
-         result = fcntl_not_cancel (file_fd, F_GETFD, 0);
-         if (result >= 0)
-           {
-# ifdef O_CLOEXEC
-             if (have_o_cloexec == 0)
-               have_o_cloexec = (result & FD_CLOEXEC) ? 1 : -1;
-# endif
-
-             result = fcntl_not_cancel (file_fd, F_SETFD,
-                                        result | FD_CLOEXEC);
-           }
-
-         if (result == -1)
-           {
-             close_not_cancel_no_status (file_fd);
-             return 0;
-           }
-       }
-#endif
+       return 0;
     }
 
   __lseek64 (file_fd, 0, SEEK_SET);
@@ -222,7 +188,7 @@ getutent_r_file (struct utmp *buffer, struct utmp **result)
     }
 
   /* Read the next entry.  */
-  nbytes = read_not_cancel (file_fd, &last_entry, sizeof (struct utmp));
+  nbytes = __read_nocancel (file_fd, &last_entry, sizeof (struct utmp));
 
   UNLOCK_FILE (file_fd);
 
@@ -245,12 +211,16 @@ getutent_r_file (struct utmp *buffer, struct utmp **result)
 
 
 static int
-internal_getut_r (const struct utmp *id, struct utmp *buffer)
+internal_getut_r (const struct utmp *id, struct utmp *buffer,
+                 bool *lock_failed)
 {
   int result = -1;
 
   LOCK_FILE (file_fd, F_RDLCK)
-    LOCKING_FAILED ();
+    {
+      *lock_failed = true;
+      LOCKING_FAILED ();
+    }
 
 #if _HAVE_UT_TYPE - 0
   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
@@ -262,7 +232,7 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer)
       while (1)
        {
          /* Read the next entry.  */
-         if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
+         if (__read_nocancel (file_fd, buffer, sizeof (struct utmp))
              != sizeof (struct utmp))
            {
              __set_errno (ESRCH);
@@ -284,7 +254,7 @@ internal_getut_r (const struct utmp *id, struct utmp *buffer)
       while (1)
        {
          /* Read the next entry.  */
-         if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
+         if (__read_nocancel (file_fd, buffer, sizeof (struct utmp))
              != sizeof (struct utmp))
            {
              __set_errno (ESRCH);
@@ -321,7 +291,10 @@ getutid_r_file (const struct utmp *id, struct utmp *buffer,
       return -1;
     }
 
-  if (internal_getut_r (id, &last_entry) < 0)
+  /* We don't have to distinguish whether we can lock the file or
+     whether there is no entry.  */
+  bool lock_failed = false;
+  if (internal_getut_r (id, &last_entry, &lock_failed) < 0)
     {
       *result = NULL;
       return -1;
@@ -357,7 +330,7 @@ getutline_r_file (const struct utmp *line, struct utmp *buffer,
   while (1)
     {
       /* Read the next entry.  */
-      if (read_not_cancel (file_fd, &last_entry, sizeof (struct utmp))
+      if (__read_nocancel (file_fd, &last_entry, sizeof (struct utmp))
          != sizeof (struct utmp))
        {
          __set_errno (ESRCH);
@@ -397,6 +370,26 @@ pututline_file (const struct utmp *data)
 
   assert (file_fd >= 0);
 
+  if (! file_writable)
+    {
+      /* We must make the file descriptor writable before going on.  */
+      const char *file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
+
+      int new_fd = __open_nocancel
+       (file_name, O_RDWR | O_LARGEFILE | O_CLOEXEC);
+      if (new_fd == -1)
+       return NULL;
+
+      if (__lseek64 (new_fd, __lseek64 (file_fd, 0, SEEK_CUR), SEEK_SET) == -1
+         || __dup2 (new_fd, file_fd) < 0)
+       {
+         __close_nocancel_nostatus (new_fd);
+         return NULL;
+       }
+      __close_nocancel_nostatus (new_fd);
+      file_writable = true;
+    }
+
   /* Find the correct place to insert the data.  */
   if (file_offset > 0
       && (
@@ -411,7 +404,16 @@ pututline_file (const struct utmp *data)
          __utmp_equal (&last_entry, data)))
     found = 1;
   else
-    found = internal_getut_r (data, &buffer);
+    {
+      bool lock_failed = false;
+      found = internal_getut_r (data, &buffer, &lock_failed);
+
+      if (__builtin_expect (lock_failed, false))
+       {
+         __set_errno (EAGAIN);
+         return NULL;
+       }
+    }
 
   LOCK_FILE (file_fd, F_WRLCK)
     {
@@ -443,7 +445,7 @@ pututline_file (const struct utmp *data)
     }
 
   /* Write the new data.  */
-  if (write_not_cancel (file_fd, data, sizeof (struct utmp))
+  if (__write_nocancel (file_fd, data, sizeof (struct utmp))
       != sizeof (struct utmp))
     {
       /* If we appended a new record this is only partially written.
@@ -470,7 +472,7 @@ endutent_file (void)
 {
   assert (file_fd >= 0);
 
-  close_not_cancel_no_status (file_fd);
+  __close_nocancel_nostatus (file_fd);
   file_fd = -1;
 }
 
@@ -483,7 +485,7 @@ updwtmp_file (const char *file, const struct utmp *utmp)
   int fd;
 
   /* Open WTMP file.  */
-  fd = open_not_cancel_2 (file, O_WRONLY | O_LARGEFILE);
+  fd = __open_nocancel (file, O_WRONLY | O_LARGEFILE);
   if (fd < 0)
     return -1;
 
@@ -504,7 +506,7 @@ updwtmp_file (const char *file, const struct utmp *utmp)
   /* Write the entry.  If we can't write all the bytes, reset the file
      size back to the original size.  That way, no partial entries
      will remain.  */
-  if (write_not_cancel (fd, utmp, sizeof (struct utmp))
+  if (__write_nocancel (fd, utmp, sizeof (struct utmp))
       != sizeof (struct utmp))
     {
       __ftruncate64 (fd, offset);
@@ -517,7 +519,7 @@ unlock_return:
   UNLOCK_FILE (fd);
 
   /* Close WTMP file.  */
-  close_not_cancel_no_status (fd);
+  __close_nocancel_nostatus (fd);
 
   return result;
 }