(linenum→info "unix/slp.c:2238")

glibc/2.7/login/utmp_file.c

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