Back to index

glibc  2.9
files-alias.c
Go to the documentation of this file.
00001 /* Mail alias file parser in nss_files module.
00002    Copyright (C) 1996,97,98,99,2002,2006,2007 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.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 <aliases.h>
00022 #include <ctype.h>
00023 #include <errno.h>
00024 #include <fcntl.h>
00025 #include <bits/libc-lock.h>
00026 #include <stdlib.h>
00027 #include <stdio.h>
00028 #include <string.h>
00029 
00030 #include <kernel-features.h>
00031 
00032 #include "nsswitch.h"
00033 
00034 /* Locks the static variables in this file.  */
00035 __libc_lock_define_initialized (static, lock)
00036 
00037 /* Maintenance of the shared stream open on the database file.  */
00038 
00039 static FILE *stream;
00040 static fpos_t position;
00041 static enum { nouse, getent, getby } last_use;
00042 
00043 
00044 static enum nss_status
00045 internal_setent (void)
00046 {
00047   enum nss_status status = NSS_STATUS_SUCCESS;
00048 
00049   if (stream == NULL)
00050     {
00051       stream = fopen ("/etc/aliases", "re");
00052 
00053       if (stream == NULL)
00054        status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
00055       else
00056        {
00057 #if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
00058 # ifdef O_CLOEXEC
00059          if (__have_o_cloexec <= 0)
00060 # endif
00061            {
00062              /* We have to make sure the file is  `closed on exec'.  */
00063              int result;
00064              int flags;
00065 
00066              result = flags = fcntl (fileno (stream), F_GETFD, 0);
00067              if (result >= 0)
00068               {
00069 # ifdef O_CLOEXEC
00070                 if (__have_o_cloexec == 0)
00071                   __have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1;
00072                 if (__have_o_cloexec < 0)
00073 # endif
00074                   {
00075                     flags |= FD_CLOEXEC;
00076                     result = fcntl (fileno (stream), F_SETFD, flags);
00077                   }
00078               }
00079              if (result < 0)
00080               {
00081                 /* Something went wrong.  Close the stream and return a
00082                    failure.  */
00083                 fclose (stream);
00084                 stream = NULL;
00085                 status = NSS_STATUS_UNAVAIL;
00086               }
00087            }
00088 #endif
00089        }
00090     }
00091   else
00092     rewind (stream);
00093 
00094   return status;
00095 }
00096 
00097 
00098 /* Thread-safe, exported version of that.  */
00099 enum nss_status
00100 _nss_files_setaliasent (void)
00101 {
00102   enum nss_status status;
00103 
00104   __libc_lock_lock (lock);
00105 
00106   status = internal_setent ();
00107 
00108   if (status == NSS_STATUS_SUCCESS && fgetpos (stream, &position) < 0)
00109     {
00110       fclose (stream);
00111       stream = NULL;
00112       status = NSS_STATUS_UNAVAIL;
00113     }
00114 
00115   last_use = getent;
00116 
00117   __libc_lock_unlock (lock);
00118 
00119   return status;
00120 }
00121 
00122 
00123 /* Close the database file.  */
00124 static void
00125 internal_endent (void)
00126 {
00127   if (stream != NULL)
00128     {
00129       fclose (stream);
00130       stream = NULL;
00131     }
00132 }
00133 
00134 
00135 /* Thread-safe, exported version of that.  */
00136 enum nss_status
00137 _nss_files_endaliasent (void)
00138 {
00139   __libc_lock_lock (lock);
00140 
00141   internal_endent ();
00142 
00143   __libc_lock_unlock (lock);
00144 
00145   return NSS_STATUS_SUCCESS;
00146 }
00147 
00148 /* Parsing the database file into `struct aliasent' data structures.  */
00149 static enum nss_status
00150 get_next_alias (const char *match, struct aliasent *result,
00151               char *buffer, size_t buflen, int *errnop)
00152 {
00153   enum nss_status status = NSS_STATUS_NOTFOUND;
00154   int ignore = 0;
00155 
00156   result->alias_members_len = 0;
00157 
00158   while (1)
00159     {
00160       /* Now we are ready to process the input.  We have to read a
00161         line and all its continuations and construct the array of
00162         string pointers.  This pointers and the names itself have to
00163         be placed in BUFFER.  */
00164       char *first_unused = buffer;
00165       size_t room_left = buflen - (buflen % __alignof__ (char *));
00166       char *line;
00167 
00168       /* Check whether the buffer is large enough for even trying to
00169          read something.  */
00170       if (room_left < 2)
00171        goto no_more_room;
00172 
00173       /* Read the first line.  It must contain the alias name and
00174         possibly some alias names.  */
00175       first_unused[room_left - 1] = '\xff';
00176       line = fgets_unlocked (first_unused, room_left, stream);
00177       if (line == NULL)
00178        /* Nothing to read.  */
00179        break;
00180       else if (first_unused[room_left - 1] != '\xff')
00181        {
00182          /* The line is too long for our buffer.  */
00183        no_more_room:
00184          *errnop = ERANGE;
00185          status = NSS_STATUS_TRYAGAIN;
00186          break;
00187        }
00188       else
00189        {
00190          char *cp;
00191 
00192          /* If we are in IGNORE mode and the first character in the
00193             line is a white space we ignore the line and start
00194             reading the next.  */
00195          if (ignore && isspace (*first_unused))
00196            continue;
00197 
00198          /* Terminate the line for any case.  */
00199          cp = strpbrk (first_unused, "#\n");
00200          if (cp != NULL)
00201            *cp = '\0';
00202 
00203          /* Skip leading blanks.  */
00204          while (isspace (*line))
00205            ++line;
00206 
00207          result->alias_name = first_unused;
00208          while (*line != '\0' && *line != ':')
00209            *first_unused++ = *line++;
00210          if (*line == '\0' || result->alias_name == first_unused)
00211            /* No valid name.  Ignore the line.  */
00212            continue;
00213 
00214          *first_unused++ = '\0';
00215          if (room_left < (size_t) (first_unused - result->alias_name))
00216            goto no_more_room;
00217          room_left -= first_unused - result->alias_name;
00218          ++line;
00219 
00220          /* When we search for a specific alias we can avoid all the
00221             difficult parts and compare now with the name we are
00222             looking for.  If it does not match we simply ignore all
00223             lines until the next line containing the start of a new
00224             alias is found.  */
00225          ignore = (match != NULL
00226                   && __strcasecmp (result->alias_name, match) != 0);
00227 
00228          while (! ignore)
00229            {
00230              while (isspace (*line))
00231               ++line;
00232 
00233              cp = first_unused;
00234              while (*line != '\0' && *line != ',')
00235               *first_unused++ = *line++;
00236 
00237              if (first_unused != cp)
00238               {
00239                 /* OK, we can have a regular entry or an include
00240                    request.  */
00241                 if (*line != '\0')
00242                   ++line;
00243                 *first_unused++ = '\0';
00244 
00245                 if (strncmp (cp, ":include:", 9) != 0)
00246                   {
00247                     if (room_left < (first_unused - cp) + sizeof (char *))
00248                      goto no_more_room;
00249                     room_left -= (first_unused - cp) + sizeof (char *);
00250 
00251                     ++result->alias_members_len;
00252                   }
00253                 else
00254                   {
00255                     /* Oh well, we have to read the addressed file.  */
00256                     FILE *listfile;
00257                     char *old_line = NULL;
00258 
00259                     first_unused = cp;
00260 
00261                     listfile = fopen (&cp[9], "r");
00262                     /* If the file does not exist we simply ignore
00263                       the statement.  */
00264                     if (listfile != NULL
00265                        && (old_line = strdup (line)) != NULL)
00266                      {
00267                        while (! feof (listfile))
00268                          {
00269                            first_unused[room_left - 1] = '\xff';
00270                            line = fgets_unlocked (first_unused, room_left,
00271                                                listfile);
00272                            if (line == NULL)
00273                             break;
00274                            if (first_unused[room_left - 1] != '\xff')
00275                             {
00276                               free (old_line);
00277                               goto no_more_room;
00278                             }
00279 
00280                            /* Parse the line.  */
00281                            cp = strpbrk (line, "#\n");
00282                            if (cp != NULL)
00283                             *cp = '\0';
00284 
00285                            do
00286                             {
00287                               while (isspace (*line))
00288                                 ++line;
00289 
00290                               cp = first_unused;
00291                               while (*line != '\0' && *line != ',')
00292                                 *first_unused++ = *line++;
00293 
00294                               if (*line != '\0')
00295                                 ++line;
00296 
00297                               if (first_unused != cp)
00298                                 {
00299                                   *first_unused++ = '\0';
00300                                   if (room_left < ((first_unused - cp)
00301                                                  + __alignof__ (char *)))
00302                                    {
00303                                      free (old_line);
00304                                      goto no_more_room;
00305                                    }
00306                                   room_left -= ((first_unused - cp)
00307                                               + __alignof__ (char *));
00308                                   ++result->alias_members_len;
00309                                 }
00310                             }
00311                            while (*line != '\0');
00312                          }
00313                        fclose (listfile);
00314 
00315                        first_unused[room_left - 1] = '\0';
00316                        strncpy (first_unused, old_line, room_left);
00317 
00318                        free (old_line);
00319                        line = first_unused;
00320 
00321                        if (first_unused[room_left - 1] != '\0')
00322                          goto no_more_room;
00323                      }
00324                   }
00325               }
00326 
00327              if (*line == '\0')
00328               {
00329                 /* Get the next line.  But we must be careful.  We
00330                    must not read the whole line at once since it
00331                    might belong to the current alias.  Simply read
00332                    the first character.  If it is a white space we
00333                    have a continuation line.  Otherwise it is the
00334                    beginning of a new alias and we can push back the
00335                    just read character.  */
00336                 int ch;
00337 
00338                 ch = fgetc (stream);
00339                 if (ch == EOF || ch == '\n' || !isspace (ch))
00340                   {
00341                     size_t cnt;
00342 
00343                     /* Now prepare the return.  Provide string
00344                       pointers for the currently selected aliases.  */
00345                     if (ch != EOF)
00346                      ungetc (ch, stream);
00347 
00348                     /* Adjust the pointer so it is aligned for
00349                       storing pointers.  */
00350                     first_unused += __alignof__ (char *) - 1;
00351                     first_unused -= ((first_unused - (char *) 0)
00352                                    % __alignof__ (char *));
00353                     result->alias_members = (char **) first_unused;
00354 
00355                     /* Compute addresses of alias entry strings.  */
00356                     cp = result->alias_name;
00357                     for (cnt = 0; cnt < result->alias_members_len; ++cnt)
00358                      {
00359                        cp = strchr (cp, '\0') + 1;
00360                        result->alias_members[cnt] = cp;
00361                      }
00362 
00363                     status = (result->alias_members_len == 0
00364                             ? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
00365                     break;
00366                   }
00367 
00368                 /* The just read character is a white space and so
00369                    can be ignored.  */
00370                 first_unused[room_left - 1] = '\xff';
00371                 line = fgets_unlocked (first_unused, room_left, stream);
00372                 if (first_unused[room_left - 1] != '\xff')
00373                   goto no_more_room;
00374                 cp = strpbrk (line, "#\n");
00375                 if (cp != NULL)
00376                   *cp = '\0';
00377               }
00378            }
00379        }
00380 
00381       if (status != NSS_STATUS_NOTFOUND)
00382        /* We read something.  In any case break here.  */
00383        break;
00384     }
00385 
00386   return status;
00387 }
00388 
00389 
00390 enum nss_status
00391 _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,
00392                        int *errnop)
00393 {
00394   /* Return next entry in host file.  */
00395   enum nss_status status = NSS_STATUS_SUCCESS;
00396 
00397   __libc_lock_lock (lock);
00398 
00399   /* Be prepared that the set*ent function was not called before.  */
00400   if (stream == NULL)
00401     status = internal_setent ();
00402 
00403   if (status == NSS_STATUS_SUCCESS)
00404     {
00405       /* If the last use was not by the getent function we need the
00406         position the stream.  */
00407       if (last_use != getent)
00408        {
00409          if (fsetpos (stream, &position) < 0)
00410            status = NSS_STATUS_UNAVAIL;
00411          else
00412            last_use = getent;
00413        }
00414 
00415       if (status == NSS_STATUS_SUCCESS)
00416        {
00417          result->alias_local = 1;
00418 
00419          /* Read lines until we get a definite result.  */
00420          do
00421            status = get_next_alias (NULL, result, buffer, buflen, errnop);
00422          while (status == NSS_STATUS_RETURN);
00423 
00424          /* If we successfully read an entry remember this position.  */
00425          if (status == NSS_STATUS_SUCCESS)
00426            fgetpos (stream, &position);
00427          else
00428            last_use = nouse;
00429        }
00430     }
00431 
00432   __libc_lock_unlock (lock);
00433 
00434   return status;
00435 }
00436 
00437 
00438 enum nss_status
00439 _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
00440                           char *buffer, size_t buflen, int *errnop)
00441 {
00442   /* Return next entry in host file.  */
00443   enum nss_status status = NSS_STATUS_SUCCESS;
00444 
00445   if (name == NULL)
00446     {
00447       __set_errno (EINVAL);
00448       return NSS_STATUS_UNAVAIL;
00449     }
00450 
00451   __libc_lock_lock (lock);
00452 
00453   /* Open the stream or rest it.  */
00454   status = internal_setent ();
00455   last_use = getby;
00456 
00457   if (status == NSS_STATUS_SUCCESS)
00458     {
00459       result->alias_local = 1;
00460 
00461       /* Read lines until we get a definite result.  */
00462       do
00463        status = get_next_alias (name, result, buffer, buflen, errnop);
00464       while (status == NSS_STATUS_RETURN);
00465     }
00466 
00467   internal_endent ();
00468 
00469   __libc_lock_unlock (lock);
00470 
00471   return status;
00472 }