Back to index

glibc  2.9
utmp_file.c
Go to the documentation of this file.
00001 /* Copyright (C) 1996-2004, 2007, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@cygnus.com>
00004    and Paul Janzen <pcj@primenet.com>, 1996.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <assert.h>
00022 #include <errno.h>
00023 #include <fcntl.h>
00024 #include <signal.h>
00025 #include <stdbool.h>
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <utmp.h>
00030 #include <not-cancel.h>
00031 #include <kernel-features.h>
00032 
00033 #include "utmp-private.h"
00034 #include "utmp-equal.h"
00035 
00036 
00037 /* Descriptor for the file and position.  */
00038 static int file_fd = -1;
00039 static off64_t file_offset;
00040 
00041 /* Cache for the last read entry.  */
00042 static struct utmp last_entry;
00043 
00044 
00045 /* Locking timeout.  */
00046 #ifndef TIMEOUT
00047 # define TIMEOUT 1
00048 #endif
00049 
00050 /* Do-nothing handler for locking timeout.  */
00051 static void timeout_handler (int signum) {};
00052 
00053 /* LOCK_FILE(fd, type) failure_statement
00054      attempts to get a lock on the utmp file referenced by FD.  If it fails,
00055      the failure_statement is executed, otherwise it is skipped.
00056    LOCKING_FAILED()
00057      jumps into the UNLOCK_FILE macro and ensures cleanup of LOCK_FILE.
00058    UNLOCK_FILE(fd)
00059      unlocks the utmp file referenced by FD and performs the cleanup of
00060      LOCK_FILE.
00061  */
00062 #define LOCK_FILE(fd, type) \
00063 {                                                                    \
00064   struct flock fl;                                                   \
00065   struct sigaction action, old_action;                                      \
00066   unsigned int old_timeout;                                          \
00067                                                                      \
00068   /* Cancel any existing alarm.  */                                         \
00069   old_timeout = alarm (0);                                           \
00070                                                                      \
00071   /* Establish signal handler.  */                                   \
00072   action.sa_handler = timeout_handler;                                      \
00073   __sigemptyset (&action.sa_mask);                                   \
00074   action.sa_flags = 0;                                                      \
00075   __sigaction (SIGALRM, &action, &old_action);                              \
00076                                                                      \
00077   alarm (TIMEOUT);                                                   \
00078                                                                      \
00079   /* Try to get the lock.  */                                               \
00080   memset (&fl, '\0', sizeof (struct flock));                                \
00081   fl.l_type = (type);                                                       \
00082   fl.l_whence = SEEK_SET;                                            \
00083   if (fcntl_not_cancel ((fd), F_SETLKW, &fl) < 0)
00084 
00085 #define LOCKING_FAILED() \
00086   goto unalarm_return
00087 
00088 #define UNLOCK_FILE(fd) \
00089   /* Unlock the file.  */                                            \
00090   fl.l_type = F_UNLCK;                                                      \
00091   fcntl_not_cancel ((fd), F_SETLKW, &fl);                            \
00092                                                                      \
00093  unalarm_return:                                                     \
00094   /* Reset the signal handler and alarm.  We must reset the alarm           \
00095      before resetting the handler so our alarm does not generate a          \
00096      spurious SIGALRM seen by the user.  However, we cannot just set        \
00097      the user's old alarm before restoring the handler, because then        \
00098      it's possible our handler could catch the user alarm's SIGARLM         \
00099      and then the user would never see the signal he expected.  */          \
00100   alarm (0);                                                         \
00101   __sigaction (SIGALRM, &old_action, NULL);                                 \
00102   if (old_timeout != 0)                                                     \
00103     alarm (old_timeout);                                             \
00104 } while (0)
00105 
00106 
00107 /* Functions defined here.  */
00108 static int setutent_file (void);
00109 static int getutent_r_file (struct utmp *buffer, struct utmp **result);
00110 static int getutid_r_file (const struct utmp *key, struct utmp *buffer,
00111                         struct utmp **result);
00112 static int getutline_r_file (const struct utmp *key, struct utmp *buffer,
00113                           struct utmp **result);
00114 static struct utmp *pututline_file (const struct utmp *data);
00115 static void endutent_file (void);
00116 static int updwtmp_file (const char *file, const struct utmp *utmp);
00117 
00118 /* Jump table for file functions.  */
00119 const struct utfuncs __libc_utmp_file_functions =
00120 {
00121   setutent_file,
00122   getutent_r_file,
00123   getutid_r_file,
00124   getutline_r_file,
00125   pututline_file,
00126   endutent_file,
00127   updwtmp_file
00128 };
00129 
00130 
00131 #ifndef TRANSFORM_UTMP_FILE_NAME
00132 # define TRANSFORM_UTMP_FILE_NAME(file_name) (file_name)
00133 #endif
00134 
00135 static int
00136 setutent_file (void)
00137 {
00138   if (file_fd < 0)
00139     {
00140       const char *file_name;
00141       int result;
00142 
00143       file_name = TRANSFORM_UTMP_FILE_NAME (__libc_utmp_file_name);
00144 
00145 #ifdef O_CLOEXEC
00146 # define O_flags O_LARGEFILE | O_CLOEXEC
00147 #else
00148 # define O_flags O_LARGEFILE
00149 #endif
00150       file_fd = open_not_cancel_2 (file_name, O_RDWR | O_flags);
00151       if (file_fd == -1)
00152        {
00153          /* Hhm, read-write access did not work.  Try read-only.  */
00154          file_fd = open_not_cancel_2 (file_name, O_RDONLY | O_flags);
00155          if (file_fd == -1)
00156            return 0;
00157        }
00158 
00159 #ifndef __ASSUME_O_CLOEXEC
00160 # ifdef O_CLOEXEC
00161       if (__have_o_cloexec <= 0)
00162 # endif
00163        {
00164          /* We have to make sure the file is `closed on exec'.  */
00165          result = fcntl_not_cancel (file_fd, F_GETFD, 0);
00166          if (result >= 0)
00167            {
00168 # ifdef O_CLOEXEC
00169              if (__have_o_cloexec == 0)
00170               __have_o_cloexec = (result & FD_CLOEXEC) ? 1 : -1;
00171 
00172              if (__have_o_cloexec < 0)
00173 # endif
00174               result = fcntl_not_cancel (file_fd, F_SETFD,
00175                                       result | FD_CLOEXEC);
00176            }
00177 
00178          if (result == -1)
00179            {
00180              close_not_cancel_no_status (file_fd);
00181              return 0;
00182            }
00183        }
00184 #endif
00185     }
00186 
00187   __lseek64 (file_fd, 0, SEEK_SET);
00188   file_offset = 0;
00189 
00190   /* Make sure the entry won't match.  */
00191 #if _HAVE_UT_TYPE - 0
00192   last_entry.ut_type = -1;
00193 #else
00194   last_entry.ut_line[0] = '\177';
00195 # if _HAVE_UT_ID - 0
00196   last_entry.ut_id[0] = '\0';
00197 # endif
00198 #endif
00199 
00200   return 1;
00201 }
00202 
00203 
00204 static int
00205 getutent_r_file (struct utmp *buffer, struct utmp **result)
00206 {
00207   ssize_t nbytes;
00208 
00209   assert (file_fd >= 0);
00210 
00211   if (file_offset == -1l)
00212     {
00213       /* Not available.  */
00214       *result = NULL;
00215       return -1;
00216     }
00217 
00218   LOCK_FILE (file_fd, F_RDLCK)
00219     {
00220       nbytes = 0;
00221       LOCKING_FAILED ();
00222     }
00223 
00224   /* Read the next entry.  */
00225   nbytes = read_not_cancel (file_fd, &last_entry, sizeof (struct utmp));
00226 
00227   UNLOCK_FILE (file_fd);
00228 
00229   if (nbytes != sizeof (struct utmp))
00230     {
00231       if (nbytes != 0)
00232        file_offset = -1l;
00233       *result = NULL;
00234       return -1;
00235     }
00236 
00237   /* Update position pointer.  */
00238   file_offset += sizeof (struct utmp);
00239 
00240   memcpy (buffer, &last_entry, sizeof (struct utmp));
00241   *result = buffer;
00242 
00243   return 0;
00244 }
00245 
00246 
00247 static int
00248 internal_getut_r (const struct utmp *id, struct utmp *buffer,
00249                 bool *lock_failed)
00250 {
00251   int result = -1;
00252 
00253   LOCK_FILE (file_fd, F_RDLCK)
00254     {
00255       *lock_failed = true;
00256       LOCKING_FAILED ();
00257     }
00258 
00259 #if _HAVE_UT_TYPE - 0
00260   if (id->ut_type == RUN_LVL || id->ut_type == BOOT_TIME
00261       || id->ut_type == OLD_TIME || id->ut_type == NEW_TIME)
00262     {
00263       /* Search for next entry with type RUN_LVL, BOOT_TIME,
00264         OLD_TIME, or NEW_TIME.  */
00265 
00266       while (1)
00267        {
00268          /* Read the next entry.  */
00269          if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
00270              != sizeof (struct utmp))
00271            {
00272              __set_errno (ESRCH);
00273              file_offset = -1l;
00274              goto unlock_return;
00275            }
00276          file_offset += sizeof (struct utmp);
00277 
00278          if (id->ut_type == buffer->ut_type)
00279            break;
00280        }
00281     }
00282   else
00283 #endif /* _HAVE_UT_TYPE */
00284     {
00285       /* Search for the next entry with the specified ID and with type
00286         INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS, or DEAD_PROCESS.  */
00287 
00288       while (1)
00289        {
00290          /* Read the next entry.  */
00291          if (read_not_cancel (file_fd, buffer, sizeof (struct utmp))
00292              != sizeof (struct utmp))
00293            {
00294              __set_errno (ESRCH);
00295              file_offset = -1l;
00296              goto unlock_return;
00297            }
00298          file_offset += sizeof (struct utmp);
00299 
00300          if (__utmp_equal (buffer, id))
00301            break;
00302        }
00303     }
00304 
00305   result = 0;
00306 
00307 unlock_return:
00308   UNLOCK_FILE (file_fd);
00309 
00310   return result;
00311 }
00312 
00313 
00314 /* For implementing this function we don't use the getutent_r function
00315    because we can avoid the reposition on every new entry this way.  */
00316 static int
00317 getutid_r_file (const struct utmp *id, struct utmp *buffer,
00318               struct utmp **result)
00319 {
00320   assert (file_fd >= 0);
00321 
00322   if (file_offset == -1l)
00323     {
00324       *result = NULL;
00325       return -1;
00326     }
00327 
00328   /* We don't have to distinguish whether we can lock the file or
00329      whether there is no entry.  */
00330   bool lock_failed = false;
00331   if (internal_getut_r (id, &last_entry, &lock_failed) < 0)
00332     {
00333       *result = NULL;
00334       return -1;
00335     }
00336 
00337   memcpy (buffer, &last_entry, sizeof (struct utmp));
00338   *result = buffer;
00339 
00340   return 0;
00341 }
00342 
00343 
00344 /* For implementing this function we don't use the getutent_r function
00345    because we can avoid the reposition on every new entry this way.  */
00346 static int
00347 getutline_r_file (const struct utmp *line, struct utmp *buffer,
00348                 struct utmp **result)
00349 {
00350   assert (file_fd >= 0);
00351 
00352   if (file_offset == -1l)
00353     {
00354       *result = NULL;
00355       return -1;
00356     }
00357 
00358   LOCK_FILE (file_fd, F_RDLCK)
00359     {
00360       *result = NULL;
00361       LOCKING_FAILED ();
00362     }
00363 
00364   while (1)
00365     {
00366       /* Read the next entry.  */
00367       if (read_not_cancel (file_fd, &last_entry, sizeof (struct utmp))
00368          != sizeof (struct utmp))
00369        {
00370          __set_errno (ESRCH);
00371          file_offset = -1l;
00372          *result = NULL;
00373          goto unlock_return;
00374        }
00375       file_offset += sizeof (struct utmp);
00376 
00377       /* Stop if we found a user or login entry.  */
00378       if (
00379 #if _HAVE_UT_TYPE - 0
00380          (last_entry.ut_type == USER_PROCESS
00381           || last_entry.ut_type == LOGIN_PROCESS)
00382          &&
00383 #endif
00384          !strncmp (line->ut_line, last_entry.ut_line, sizeof line->ut_line))
00385        break;
00386     }
00387 
00388   memcpy (buffer, &last_entry, sizeof (struct utmp));
00389   *result = buffer;
00390 
00391 unlock_return:
00392   UNLOCK_FILE (file_fd);
00393 
00394   return ((*result == NULL) ? -1 : 0);
00395 }
00396 
00397 
00398 static struct utmp *
00399 pututline_file (const struct utmp *data)
00400 {
00401   struct utmp buffer;
00402   struct utmp *pbuf;
00403   int found;
00404 
00405   assert (file_fd >= 0);
00406 
00407   /* Find the correct place to insert the data.  */
00408   if (file_offset > 0
00409       && (
00410 #if _HAVE_UT_TYPE - 0
00411          (last_entry.ut_type == data->ut_type
00412           && (last_entry.ut_type == RUN_LVL
00413               || last_entry.ut_type == BOOT_TIME
00414               || last_entry.ut_type == OLD_TIME
00415               || last_entry.ut_type == NEW_TIME))
00416          ||
00417 #endif
00418          __utmp_equal (&last_entry, data)))
00419     found = 1;
00420   else
00421     {
00422       bool lock_failed = false;
00423       found = internal_getut_r (data, &buffer, &lock_failed);
00424 
00425       if (__builtin_expect (lock_failed, false))
00426        {
00427          __set_errno (EAGAIN);
00428          return NULL;
00429        }
00430     }
00431 
00432   LOCK_FILE (file_fd, F_WRLCK)
00433     {
00434       pbuf = NULL;
00435       LOCKING_FAILED ();
00436     }
00437 
00438   if (found < 0)
00439     {
00440       /* We append the next entry.  */
00441       file_offset = __lseek64 (file_fd, 0, SEEK_END);
00442       if (file_offset % sizeof (struct utmp) != 0)
00443        {
00444          file_offset -= file_offset % sizeof (struct utmp);
00445          __ftruncate64 (file_fd, file_offset);
00446 
00447          if (__lseek64 (file_fd, 0, SEEK_END) < 0)
00448            {
00449              pbuf = NULL;
00450              goto unlock_return;
00451            }
00452        }
00453     }
00454   else
00455     {
00456       /* We replace the just read entry.  */
00457       file_offset -= sizeof (struct utmp);
00458       __lseek64 (file_fd, file_offset, SEEK_SET);
00459     }
00460 
00461   /* Write the new data.  */
00462   if (write_not_cancel (file_fd, data, sizeof (struct utmp))
00463       != sizeof (struct utmp))
00464     {
00465       /* If we appended a new record this is only partially written.
00466         Remove it.  */
00467       if (found < 0)
00468        (void) __ftruncate64 (file_fd, file_offset);
00469       pbuf = NULL;
00470     }
00471   else
00472     {
00473       file_offset += sizeof (struct utmp);
00474       pbuf = (struct utmp *) data;
00475     }
00476 
00477  unlock_return:
00478   UNLOCK_FILE (file_fd);
00479 
00480   return pbuf;
00481 }
00482 
00483 
00484 static void
00485 endutent_file (void)
00486 {
00487   assert (file_fd >= 0);
00488 
00489   close_not_cancel_no_status (file_fd);
00490   file_fd = -1;
00491 }
00492 
00493 
00494 static int
00495 updwtmp_file (const char *file, const struct utmp *utmp)
00496 {
00497   int result = -1;
00498   off64_t offset;
00499   int fd;
00500 
00501   /* Open WTMP file.  */
00502   fd = open_not_cancel_2 (file, O_WRONLY | O_LARGEFILE);
00503   if (fd < 0)
00504     return -1;
00505 
00506   LOCK_FILE (fd, F_WRLCK)
00507     LOCKING_FAILED ();
00508 
00509   /* Remember original size of log file.  */
00510   offset = __lseek64 (fd, 0, SEEK_END);
00511   if (offset % sizeof (struct utmp) != 0)
00512     {
00513       offset -= offset % sizeof (struct utmp);
00514       __ftruncate64 (fd, offset);
00515 
00516       if (__lseek64 (fd, 0, SEEK_END) < 0)
00517        goto unlock_return;
00518     }
00519 
00520   /* Write the entry.  If we can't write all the bytes, reset the file
00521      size back to the original size.  That way, no partial entries
00522      will remain.  */
00523   if (write_not_cancel (fd, utmp, sizeof (struct utmp))
00524       != sizeof (struct utmp))
00525     {
00526       __ftruncate64 (fd, offset);
00527       goto unlock_return;
00528     }
00529 
00530   result = 0;
00531 
00532 unlock_return:
00533   UNLOCK_FILE (fd);
00534 
00535   /* Close WTMP file.  */
00536   close_not_cancel_no_status (fd);
00537 
00538   return result;
00539 }