Back to index

glibc  2.9
db-open.c
Go to the documentation of this file.
00001 /* Common database routines for nss_db.
00002    Copyright (C) 2000 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <errno.h>
00021 #include <fcntl.h>
00022 #include <dlfcn.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <bits/libc-lock.h>
00026 
00027 #include "dummy-db.h"
00028 #include "nss_db.h"
00029 
00030 /* This file contains the functions used to open and close the databases
00031    read by the rest of libnss_db.  Not all of them are thread safe;
00032    make sure the caller does the appropriate locking.
00033 
00034    We dynamically load the database library, so that it does not have
00035    to be present when glibc is compiled.  Once loaded, the database
00036    library is never never unloaded again until the libnss_db module is
00037    unloaded (from the free_mem routine in nsswitch.c) -- we catch the
00038    unload by providing a shlib destructor.  (XXX Does that actually
00039    work?)  */
00040 
00041 /* Handle for the shared Berkeley DB library.  If non-null, the
00042    database library is completely loaded and ready to be used by
00043    multithreaded code.  */
00044 static void *libdb_handle;
00045 
00046 /* The version of the Berkeley DB library we are using.  */
00047 enum {
00048   nodb,
00049   db24,
00050   db27,
00051   db30
00052 } libdb_version;
00053 
00054 /* Pointer to the db_open function.  For use with DB 2.x.  */
00055 static int (*libdb_db_open) (const char *, int,
00056                           uint32_t, int, void *, void *, void **);
00057 
00058 /* Pointer to the db_create function.  For use with DB 3.x.  */
00059 static int (*libdb_db_create) (void *, void *, uint32_t);
00060 
00061 /* Constants which vary from version to version are actually variables
00062    here.  */
00063 int db_first;
00064 int db_next;
00065 int db_nooverwrite;
00066 int db_truncate;
00067 int db_rdonly;
00068 /* Variables which keep track of the error values.  */
00069 int db_keyexist;
00070 int db_notfound;
00071 
00072 /* Locks the static variables in this file.  */
00073 __libc_lock_define_initialized (static, lock)
00074 
00075 /* Dynamically load the database library.  Return zero if successful,
00076    non-zero if no suitable version of the library could be loaded.
00077    Must be called with the above lock held if it might run in a
00078    multithreaded context.
00079 
00080    We try currently:
00081    - libdb.so.3: the name used by glibc 2.1
00082    - libdb-3.0.so: the name used by db-3.0.x
00083    and maybe others in the future.  */
00084 
00085 enum nss_status
00086 load_db (void)
00087 {
00088   static const char *libnames[] = { "libdb.so.3", "libdb-3.0.so" };
00089   int x;
00090 
00091   for (x = 0; x < sizeof (libnames) / sizeof (libnames[0]); ++x)
00092     {
00093       libdb_handle = dlopen (libnames[x], RTLD_LAZY);
00094       if (libdb_handle == NULL)
00095        continue;
00096 
00097       /* DB 3.0 has db_create instead of db_open.  */
00098       libdb_db_create = dlsym (libdb_handle, "db_create");
00099 
00100       if (libdb_db_create == NULL)
00101        /* DB 2.x uses db_open.  */
00102        libdb_db_open = dlsym (libdb_handle, "db_open");
00103 
00104       if (libdb_db_open != NULL || libdb_db_create != NULL)
00105        {
00106          /* Alright, we got a library.  Now find out which version it is.  */
00107          const char *(*db_version) (int *, int *, int *);
00108 
00109          db_version = dlsym (libdb_handle, "db_version");
00110          if (db_version != NULL)
00111            {
00112              /* Call the function and get the information.  */
00113              int major, minor, subminor;
00114 
00115              DL_CALL_FCT (db_version, (&major, &minor, &subminor));
00116              switch (major)
00117               {
00118               case 2:
00119                 /* Sanity check: Do we have db_open?  */
00120                 if (libdb_db_open != NULL)
00121                   {
00122                     if (minor < 6 || (minor == 6 && subminor < 4))
00123                      {
00124                        libdb_version = db24;
00125                        db_first = DB24_FIRST;
00126                        db_next = DB24_NEXT;
00127                        db_nooverwrite = DB24_NOOVERWRITE;
00128                        db_truncate = DB24_TRUNCATE;
00129                      }
00130                     else
00131                      {
00132                        libdb_version = db27;
00133                        db_first = DB27_FIRST;
00134                        db_next = DB27_NEXT;
00135                        db_nooverwrite = DB27_NOOVERWRITE;
00136                        db_truncate = DB27_TRUNCATE;
00137                      }
00138                     db_keyexist = DB2x_KEYEXIST;
00139                     db_notfound = DB2x_NOTFOUND;
00140                     db_rdonly = DB2x_RDONLY;
00141                   }
00142                 break;
00143 
00144               case 3:
00145                 /* Sanity check: Do we have db_create?  */
00146                 if (libdb_db_create != NULL)
00147                   {
00148                     libdb_version = db30;
00149                     db_first = DB30_FIRST;
00150                     db_next = DB30_NEXT;
00151                     db_keyexist = DB30_KEYEXIST;
00152                     db_notfound = DB30_NOTFOUND;
00153                     db_rdonly = DB30_RDONLY;
00154                   }
00155                 break;
00156 
00157               default:
00158                 break;
00159               }
00160            }
00161 
00162          if (libdb_version != nodb)
00163            return NSS_STATUS_SUCCESS;
00164 
00165          /* Clear variables.  */
00166          libdb_db_open = NULL;
00167          libdb_db_create = NULL;
00168        }
00169 
00170       dlclose (libdb_handle);
00171     }
00172 
00173   (void) dlerror ();
00174   return NSS_STATUS_UNAVAIL;
00175 }
00176 
00177 /* Set the `FD_CLOEXEC' flag of FD.  Return 0 on success, or -1 on
00178    error with `errno' set. */
00179 static int
00180 set_cloexec_flag (int fd)
00181 {
00182   int oldflags = fcntl (fd, F_GETFD, 0);
00183 
00184   if (oldflags < 0)
00185     return oldflags;
00186 
00187   oldflags |= FD_CLOEXEC;
00188 
00189   return fcntl (fd, F_SETFD, oldflags);
00190 }
00191 
00192 /* Make sure we don't use the library anymore once we are shutting down.  */
00193 static void __attribute__ ((destructor))
00194 unload_db (void)
00195 {
00196   if (libdb_handle != NULL)
00197     {
00198       libdb_db_open = NULL;
00199       libdb_db_create = NULL;
00200       libdb_version = nodb;
00201       dlclose (libdb_handle);
00202     }
00203 }
00204 
00205 /* Open the database stored in FILE.  If succesful, store the database
00206    handle in *DBP and return NSS_STATUS_SUCCESS.  On failure, return
00207    the appropriate lookup status.  */
00208 enum nss_status
00209 internal_setent (const char *file, NSS_DB **dbp)
00210 {
00211   enum nss_status status = NSS_STATUS_SUCCESS;
00212 
00213   if (*dbp == NULL)
00214     {
00215       if (libdb_db_open == NULL && libdb_db_create == NULL)
00216        {
00217          __libc_lock_lock (lock);
00218 
00219          if (libdb_db_open == NULL && libdb_db_create == NULL)
00220            status = load_db ();
00221 
00222          __libc_lock_unlock (lock);
00223        }
00224 
00225       if (status == NSS_STATUS_SUCCESS)
00226        status = dbopen (file, db_rdonly, 0, dbp);
00227     }
00228 
00229   return status;
00230 }
00231 
00232 
00233 /* Close the database *DBP.  */
00234 void
00235 internal_endent (NSS_DB **dbp)
00236 {
00237   NSS_DB *db = *dbp;
00238 
00239   if (db != NULL)
00240     {
00241       DL_CALL_FCT (db->close, (db->db, 0));
00242       *dbp = NULL;
00243     }
00244 }
00245 
00246 /* Allocate a cursor for database DB and transaction TXN.  On success,
00247    store the cursor in *DBCP and return zero.  Otherwise return an
00248    error value.  */
00249 int
00250 db_cursor (void *db, void *txn, NSS_DBC **dbcp)
00251 {
00252   NSS_DBC *dbc;
00253   int ret;
00254 
00255   dbc = (NSS_DBC *) malloc (sizeof (NSS_DBC));
00256   if (dbc == NULL)
00257     return ENOMEM;
00258 
00259   switch (libdb_version)
00260     {
00261     case db24:
00262       ret = ((struct db24 *) db)->cursor (db, txn, &dbc->cursor);
00263 
00264       if (ret == 0)
00265        dbc->c_get = ((struct dbc24 *) dbc->cursor)->c_get;
00266       break;
00267 
00268     case db27:
00269       ret = ((struct db27 *) db)->cursor (db, txn, &dbc->cursor, 0);
00270 
00271       if (ret == 0)
00272        dbc->c_get = ((struct dbc27 *) dbc->cursor)->c_get;
00273       break;
00274 
00275     case db30:
00276       ret = ((struct db30 *) db)->cursor (db, txn, &dbc->cursor, 0);
00277 
00278       if (ret == 0)
00279        dbc->c_get = ((struct dbc30 *) dbc->cursor)->c_get;
00280       break;
00281 
00282     default:
00283       abort ();
00284     }
00285 
00286   if (ret != 0)
00287     {
00288       free (dbc);
00289       return ret;
00290     }
00291 
00292   *dbcp = dbc;
00293 
00294   return 0;
00295 }
00296 
00297 
00298 /* Open the database in FNAME, for access specified by FLAGS.  If
00299    opening the database causes the file FNAME to be created, it is
00300    created with MODE.  If succesful, store the database handle in *DBP
00301    and return NSS_STATUS_SUCCESS.  On failure, return the appropriate
00302    lookup status.  */
00303 int
00304 dbopen (const char *fname, int oper, int mode, NSS_DB **dbp)
00305 {
00306   int err;
00307   int fd;
00308   NSS_DB *db;
00309 
00310   /* Construct the object we pass up.  */
00311   db = (NSS_DB *) calloc (1, sizeof (NSS_DB));
00312   if (db == NULL)
00313     return NSS_STATUS_UNAVAIL;
00314 
00315   /* Initialize the object.  */
00316   db->cursor = db_cursor;
00317 
00318   /* Actually open the database.  */
00319   switch (libdb_version)
00320     {
00321     case db24:
00322     case db27:
00323       err = DL_CALL_FCT (libdb_db_open,
00324                       (fname, DB_BTREE, oper, mode, NULL, NULL, &db->db));
00325       if (err != 0)
00326        goto fail;
00327 
00328       if (libdb_version)
00329        {
00330          db->close = ((struct db24 *) db->db)->close;
00331          db->fd = ((struct db24 *) db->db)->fd;
00332          db->get = ((struct db24 *) db->db)->get;
00333          db->put = ((struct db24 *) db->db)->put;
00334        }
00335       else
00336        {
00337          db->close = ((struct db27 *) db->db)->close;
00338          db->fd = ((struct db27 *) db->db)->fd;
00339          db->get = ((struct db27 *) db->db)->get;
00340          db->put = ((struct db27 *) db->db)->put;
00341        }
00342       break;
00343 
00344     case db30:
00345       err = DL_CALL_FCT (libdb_db_create, (db->db, NULL, 0));
00346       if (err != 0)
00347        goto fail;
00348 
00349       db->close = ((struct db30 *) db->db)->close;
00350       db->fd = ((struct db30 *) db->db)->fd;
00351       db->get = ((struct db30 *) db->db)->get;
00352       db->put = ((struct db30 *) db->db)->put;
00353 
00354       err = ((struct db30 *) db->db)->open (db->db, fname, NULL, DB_BTREE,
00355                                        oper, mode);
00356       if (err != 0)
00357        goto fail;
00358       break;
00359 
00360     default:
00361       abort ();
00362     }
00363 
00364   /* We have to make sure the file is `closed on exec'.  */
00365   err = DL_CALL_FCT (db->fd, (db->db, &fd));
00366   if (err != 0)
00367     goto fail;
00368   if (set_cloexec_flag (fd) < 0)
00369     goto fail;
00370 
00371   *dbp = db;
00372 
00373   return NSS_STATUS_UNAVAIL;
00374 
00375  fail:
00376   /* Something went wrong.  Close the database if necessary.  */
00377   if (db)
00378     {
00379       if (db->db && db->close)
00380        DL_CALL_FCT (db->close, (db->db, 0));
00381       free (db);
00382     }
00383 
00384   /* Make sure `errno' is set.  */
00385   if (err)
00386     __set_errno (err);
00387 
00388   return err == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
00389 }