Print this page
4078 groupadd execs getent unnecessarily
Reviewed by: Rich Lowe <richlowe@richlowe.net>
Reviewed by: Gary Mills <gary_mills@fastmail.fm>
Reviewed by: Milan Jurik <milan.jurik@xylab.cz>
Reviewed by: Gordon Ross <Gordon.W.Ross@gmail.com>

@@ -20,90 +20,92 @@
  */
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
+
 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
 /*        All Rights Reserved   */
 
+/*
+ * Copyright (c) 2013 RackTop Systems.
+ */
 
-#pragma ident   "%Z%%M% %I%     %E% SMI"        /* SVr4.0 1.5 */
-
+#include <errno.h>
 #include <sys/types.h>
 #include <stdio.h>
 #include <userdefs.h>
+#include <grp.h>
+#include <libcmdutils.h>
 
-#include <sys/param.h>
-#ifndef MAXUID
-#include <limits.h>
-#ifdef UID_MAX
-#define MAXUID  UID_MAX
-#else
-#define MAXUID  60000
-#endif
-#endif
+static int findunusedgid(gid_t start, gid_t stop, gid_t *ret);
+static boolean_t isreservedgid(gid_t gid);
 
 /*
- * Check to see that the gid is not a reserved gid
- * -- nobody, noaccess or nogroup
+ * Find the highest unused uid. If the highest unused gid is "stop",
+ * then attempt to find a hole in the range. Returns 0 on success.
  */
-static int
-isvalidgid(gid_t gid)
+int
+findnextgid(gid_t start, gid_t stop, gid_t *ret)
 {
-        return (gid != 60001 && gid != 60002 && gid != 65534);
+        gid_t gid = start;
+        struct group *grp;
+        boolean_t overflow = B_FALSE;
+
+        setgrent();
+        for (grp = getgrent(); grp != NULL; grp = getgrent()) {
+                if (isreservedgid(grp->gr_gid))         /* Skip reserved IDs */
+                        continue;
+                if (grp->gr_gid >= gid) {
+                        if (grp->gr_gid == stop) {      /* Overflow check */
+                                overflow = B_TRUE;
+                                break;
+                        }
+                        gid = grp->gr_gid + 1;
+                }
+        }
+        if (grp == NULL && errno != 0) {
+                endgrent();
+                return (-1);
+        }
+        endgrent();
+        if (overflow == B_TRUE)                         /* Find a hole */
+                return (findunusedgid(start, stop, ret));
+        while (isreservedgid(gid) && gid < stop)        /* Skip reserved IDs */
+                gid++;
+        *ret = gid;
+        return (0);
 }
 
-gid_t
-findnextgid()
+/*
+ * Check to see whether the gid is a reserved gid
+ * -- nobody, noaccess or nogroup
+ */
+static boolean_t
+isreservedgid(gid_t gid)
 {
-        FILE *fptr;
-        gid_t last, next;
-        gid_t gid;
+        return (gid == 60001 || gid == 60002 || gid == 65534);
+}
 
-        /*
-         * Sort the used GIDs in decreasing order to return MAXUSED + 1
+/*
+ * findunusedgid() attempts to return the next valid usable id between the
+ * supplied upper and lower limits. Returns 0 on success.
          */
-        if ((fptr = popen("exec sh -c "
-            "\"getent group|cut -f3 -d:|sort -nr|uniq \" 2>/dev/null",
-            "r")) == NULL)
-                return (-1);
+static int
+findunusedgid(gid_t start, gid_t stop, gid_t *ret)
+{
+        gid_t gid;
 
-        if (fscanf(fptr, "%u\n", &next) == EOF) {
-                (void) pclose(fptr);
-                return (DEFRID + 1);
-        }
-
-        /*
-         * 'next' is now the highest allocated gid.
-         *
-         * The simplest allocation is where we just add one, and obtain
-         * a valid gid.  If this fails look for a hole in the gid range ..
-         */
-
-        last = MAXUID;          /* upper limit */
-        gid = -1;               /* start invalid */
-        do {
-                if (!isvalidgid(next))
+        for (gid = start; gid <= stop; gid++) {
+                if (isreservedgid(gid))
                         continue;
-
-                if (next <= DEFRID) {
-                        if (last != DEFRID + 1)
-                                gid = DEFRID + 1;
+                if (getgrgid(gid) == NULL) {
+                        if (errno != 0)
+                                return (-1);
                         break;
                 }
-
-                if ((gid = next + 1) != last) {
-                        while (!isvalidgid(gid))
-                                gid++;
-                        if (gid > 0 && gid < last)
-                                break;
                 }
-
-                gid = -1;
-                last = next;
-
-        } while (fscanf(fptr, "%u\n", &next) != EOF);
-
-        (void) pclose(fptr);
-
-        return (gid);
+        if (gid > stop)
+                return (-1);
+        *ret = gid;
+        return (0);
 }