Add nsswitch support.
[dragonfly.git] / lib / libc / gen / getusershell.c
index 6a6347a..e0a563e 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/lib/libc/gen/getusershell.c,v 1.3.2.1 2001/03/05 09:17:52 obrien Exp $
- * $DragonFly: src/lib/libc/gen/getusershell.c,v 1.4 2005/11/13 00:07:42 swildner Exp $
- *
  * @(#)getusershell.c  8.1 (Berkeley) 6/4/93
- * $FreeBSD: src/lib/libc/gen/getusershell.c,v 1.3.2.1 2001/03/05 09:17:52 obrien Exp $
+ * $NetBSD: getusershell.c,v 1.17 1999/01/25 01:09:34 lukem Exp $
+ * $FreeBSD: src/lib/libc/gen/getusershell.c,v 1.10 2007/01/09 00:27:54 imp Exp $
+ * $DragonFly: src/lib/libc/gen/getusershell.c,v 1.4 2005/11/13 00:07:42 swildner Exp $
  */
 
+#include "namespace.h"
 #include <sys/param.h>
 #include <sys/file.h>
-#include <sys/stat.h>
 
 #include <ctype.h>
+#include <errno.h>
+#include <nsswitch.h>
+#include <paths.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
 #include <unistd.h>
-#include <paths.h>
+
+#ifdef HESIOD
+#include <hesiod.h>
+#endif
+#ifdef YP
+#include <rpc/rpc.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yp_prot.h>
+#endif
+#include "un-namespace.h"
 
 /*
  * Local shells should NOT be added here.  They should be added in
  * /etc/shells.
  */
 
-static char *okshells[] = { _PATH_BSHELL, _PATH_CSHELL, NULL };
-static char **curshell, **shells, *strings;
-static char **initshells (void);
+static const char *const okshells[] = { _PATH_BSHELL, _PATH_CSHELL, NULL };
+static const char *const *curshell;
+static StringList       *sl;
+
+static const char *const *initshells(void);
 
 /*
- * Get a list of shells from _PATH_SHELLS, if it exists.
+ * Get a list of shells from "shells" nsswitch database
  */
 char *
 getusershell(void)
@@ -66,7 +77,8 @@ getusershell(void)
 
        if (curshell == NULL)
                curshell = initshells();
-       ret = *curshell;
+       /*LINTED*/
+       ret = (char *)*curshell;
        if (ret != NULL)
                curshell++;
        return (ret);
@@ -75,13 +87,10 @@ getusershell(void)
 void
 endusershell(void)
 {
-
-       if (shells != NULL)
-               free(shells);
-       shells = NULL;
-       if (strings != NULL)
-               free(strings);
-       strings = NULL;
+       if (sl) {
+               sl_free(sl, 1);
+               sl = NULL;
+       }
        curshell = NULL;
 }
 
@@ -92,49 +101,159 @@ setusershell(void)
        curshell = initshells();
 }
 
-static char **
-initshells(void)
+
+static int     _local_initshells(void *, void *, va_list);
+
+/*ARGSUSED*/
+static int
+_local_initshells(void *rv, void *cb_data, va_list ap)
 {
-       char **sp, *cp;
-       FILE *fp;
-       struct stat statb;
-
-       if (shells != NULL)
-               free(shells);
-       shells = NULL;
-       if (strings != NULL)
-               free(strings);
-       strings = NULL;
+       char    *sp, *cp;
+       FILE    *fp;
+       char     line[MAXPATHLEN + 2];
+
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+
        if ((fp = fopen(_PATH_SHELLS, "r")) == NULL)
-               return (okshells);
-       if (fstat(fileno(fp), &statb) == -1) {
-               fclose(fp);
-               return (okshells);
-       }
-       if ((strings = malloc((u_int)statb.st_size)) == NULL) {
-               fclose(fp);
-               return (okshells);
-       }
-       shells = calloc((unsigned)statb.st_size / 3, sizeof (char *));
-       if (shells == NULL) {
-               fclose(fp);
-               free(strings);
-               strings = NULL;
-               return (okshells);
-       }
-       sp = shells;
-       cp = strings;
+               return NS_UNAVAIL;
+
+       sp = cp = line;
        while (fgets(cp, MAXPATHLEN + 1, fp) != NULL) {
                while (*cp != '#' && *cp != '/' && *cp != '\0')
                        cp++;
                if (*cp == '#' || *cp == '\0')
                        continue;
-               *sp++ = cp;
-               while (!isspace((unsigned char)*cp) && *cp != '#' && *cp != '\0')
+               sp = cp;
+               while (!isspace(*cp) && *cp != '#' && *cp != '\0')
                        cp++;
                *cp++ = '\0';
+               sl_add(sl, strdup(sp));
        }
-       *sp = NULL;
        fclose(fp);
-       return (shells);
+       return NS_SUCCESS;
+}
+
+#ifdef HESIOD
+static int     _dns_initshells(void *, void *, va_list);
+
+/*ARGSUSED*/
+static int
+_dns_initshells(void *rv, void *cb_data, va_list ap)
+{
+       char      shellname[] = "shells-XXXXX";
+       int       hsindex, hpi, r;
+       char    **hp;
+       void     *context;
+
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+       r = NS_UNAVAIL;
+       if (hesiod_init(&context) == -1)
+               return (r);
+
+       for (hsindex = 0; ; hsindex++) {
+               snprintf(shellname, sizeof(shellname)-1, "shells-%d", hsindex);
+               hp = hesiod_resolve(context, shellname, "shells");
+               if (hp == NULL) {
+                       if (errno == ENOENT) {
+                               if (hsindex == 0)
+                                       r = NS_NOTFOUND;
+                               else
+                                       r = NS_SUCCESS;
+                       }
+                       break;
+               } else {
+                       for (hpi = 0; hp[hpi]; hpi++)
+                               sl_add(sl, hp[hpi]);
+                       free(hp);
+               }
+       }
+       hesiod_end(context);
+       return (r);
+}
+#endif /* HESIOD */
+
+#ifdef YP
+static int     _nis_initshells(void *, void *, va_list);
+
+/*ARGSUSED*/
+static int
+_nis_initshells(void *rv, void *cb_data, va_list ap)
+{
+       static char *ypdomain;
+       char    *key, *data;
+       char    *lastkey;
+       int      keylen, datalen;
+       int      r;
+
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+
+       if (ypdomain == NULL) {
+               switch (yp_get_default_domain(&ypdomain)) {
+               case 0:
+                       break;
+               case YPERR_RESRC:
+                       return NS_TRYAGAIN;
+               default:
+                       return NS_UNAVAIL;
+               }
+       }
+
+       /*
+        * `key' and `data' point to strings dynamically allocated by
+        * the yp_... functions.
+        * `data' is directly put into the stringlist of shells.
+        */
+       key = data = NULL;
+       if (yp_first(ypdomain, "shells", &key, &keylen, &data, &datalen))
+               return NS_UNAVAIL;
+       do {
+               data[datalen] = '\0';           /* clear trailing \n */
+               sl_add(sl, data);
+
+               lastkey = key;
+               r = yp_next(ypdomain, "shells", lastkey, keylen,
+                   &key, &keylen, &data, &datalen);
+               free(lastkey);
+       } while (r == 0);
+
+       if (r == YPERR_NOMORE) {
+               /*
+                * `data' and `key' ought to be NULL - do not try to free them.
+                */
+               return NS_SUCCESS;
+       }
+
+       return NS_UNAVAIL;
+}
+#endif /* YP */
+
+static const char *const *
+initshells(void)
+{
+       static const ns_dtab dtab[] = {
+               NS_FILES_CB(_local_initshells, NULL)
+               NS_DNS_CB(_dns_initshells, NULL)
+               NS_NIS_CB(_nis_initshells, NULL)
+               { 0 }
+       };
+       if (sl)
+               sl_free(sl, 1);
+       sl = sl_init();
+
+       if (_nsdispatch(NULL, dtab, NSDB_SHELLS, "initshells", __nsdefaultsrc)
+           != NS_SUCCESS) {
+               if (sl)
+                       sl_free(sl, 1);
+               sl = NULL;
+               return (okshells);
+       }
+       sl_add(sl, NULL);
+
+       return (const char *const *)(sl->sl_str);
 }