[Pkg-shadow-commits] r1754 - in upstream/trunk: . lib libmisc

nekral-guest at alioth.debian.org nekral-guest at alioth.debian.org
Sun Feb 3 16:53:07 UTC 2008


Author: nekral-guest
Date: 2008-02-03 16:53:07 +0000 (Sun, 03 Feb 2008)
New Revision: 1754

Added:
   upstream/trunk/libmisc/find_new_ids.c
Modified:
   upstream/trunk/ChangeLog
   upstream/trunk/lib/prototypes.h
Log:
Add new generic functions to find the next user or group ID available:
find_new_uid() and find_new_gid(). They work the same way as the functions
with the same name of useradd or groupadd, except that they check in the
local database to make sure an ID was not reserved in an uncommitted
change (this is needed to be used in newusers), they report a status
instead of calling exit(), and they can receive a preferred ID. They
should later support system IDs. This should be a little bit slower, but
not too much (if the database is not open the checks against the local
database will exit immediately, and if it is already open, all the checks
will be done regarding the data in memory).


Modified: upstream/trunk/ChangeLog
===================================================================
--- upstream/trunk/ChangeLog	2008-02-03 16:51:08 UTC (rev 1753)
+++ upstream/trunk/ChangeLog	2008-02-03 16:53:07 UTC (rev 1754)
@@ -4,6 +4,18 @@
 	its UID on the local database.
 	* lib/groupio.c, lib/groupio.h: New function to find a group by
 	its GID on the local database.
+	* libmisc/find_new_ids.c, lib/prototypes.h: Add new generic
+	functions to find the next user or group ID available:
+	find_new_uid() and find_new_gid(). They work the same way as the
+	functions with the same name of useradd or groupadd, except that
+	they check in the local database to make sure an ID was not
+	reserved in an uncommitted change (this is needed to be used in
+	newusers), they report a status instead of calling exit(), and
+	they can receive a preferred ID. They should later support system
+	IDs. This should be a little bit slower, but not too much (if the
+	database is not open the checks against the local database will
+	exit immediately, and if it is already open, all the checks will be
+	done regarding the data in memory).
 
 2008-02-02  Nicolas François  <nicolas.francois at centraliens.net>
 

Modified: upstream/trunk/lib/prototypes.h
===================================================================
--- upstream/trunk/lib/prototypes.h	2008-02-03 16:51:08 UTC (rev 1753)
+++ upstream/trunk/lib/prototypes.h	2008-02-03 16:53:07 UTC (rev 1754)
@@ -67,6 +67,10 @@
 extern void change_field (char *, size_t, const char *);
 extern int valid_field (const char *, const char *);
 
+/* find_new_ids.c */
+extern int find_new_uid (int sys_user, uid_t *uid, uid_t const *preferred_uid);
+extern int find_new_gid (int sys_group, gid_t *gid, gid_t const *preferred_gid);
+
 /* getlong.c */
 extern int getlong(const char *numstr, long int *result);
 

Added: upstream/trunk/libmisc/find_new_ids.c
===================================================================
--- upstream/trunk/libmisc/find_new_ids.c	                        (rev 0)
+++ upstream/trunk/libmisc/find_new_ids.c	2008-02-03 16:53:07 UTC (rev 1754)
@@ -0,0 +1,165 @@
+#include <config.h>
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "prototypes.h"
+#include "pwio.h"
+#include "groupio.h"
+#include "getdef.h"
+
+/*
+ * find_new_uid - Find a new unused UID.
+ *
+ * If successful, find_new_uid provides an unused user ID in the
+ * [UID_MIN:UID_MAX] range.
+ * This ID should be higher than all the used UID, but if not possible,
+ * the lowest unused ID in the range will be returned.
+ * 
+ * Return 0 on success, -1 if no unused UIDs are available.
+ */
+int find_new_uid (int sys_user, uid_t *uid, uid_t const *preferred_uid)
+{
+	const struct passwd *pwd;
+	uid_t uid_min, uid_max, user_id;
+
+	assert (uid != NULL);
+	/* TODO: add support for system users */
+	assert (sys_user == 0);
+
+	uid_min = getdef_unum ("UID_MIN", 1000);
+	uid_max = getdef_unum ("UID_MAX", 60000);
+
+	if (   (NULL != preferred_uid)
+	    && (*preferred_uid >= uid_min)
+	    && (*preferred_uid <= uid_max)
+	    /* Check if the user exists according to NSS */
+	    && (getpwuid (*preferred_uid) == NULL)
+	    /* Check also the local database in case of uncommitted
+	     * changes */
+	    && (pw_locate_uid (*preferred_uid) == NULL)) {
+		*uid = *preferred_uid;
+		return 0;
+	}
+
+
+	user_id = uid_min;
+
+	/*
+	 * Search the entire password file,
+	 * looking for the largest unused value.
+	 *
+	 * We check the list of users according to NSS (setpwent/getpwent),
+	 * but we also check the local database (pw_rewind/pw_next) in case
+	 * some users were created but the changes were not committed yet.
+	 */
+	setpwent ();
+	pw_rewind ();
+	while (   ((pwd = getpwent ()) != NULL)
+	       || ((pwd = pw_next ()) != NULL)) {
+		if ((pwd->pw_uid >= user_id) && (pwd->pw_uid <= uid_max)) {
+			user_id = pwd->pw_uid + 1;
+		}
+	}
+
+	/*
+	 * If a user with UID equal to UID_MAX exists, the above algorithm
+	 * will give us UID_MAX+1 even if not unique. Search for the first
+	 * free UID starting with UID_MIN (it's O(n*n) but can be avoided
+	 * by not having users with UID equal to UID_MAX).  --marekm
+	 */
+	if (user_id == uid_max + 1) {
+		for (user_id = uid_min; user_id < uid_max; user_id++) {
+			/* local, no need for xgetpwuid */
+			if (   (getpwuid (user_id) == NULL)
+			    && (pw_locate_uid (user_id) == NULL)) {
+				break;
+			}
+		}
+		if (user_id == uid_max) {
+			fputs (_("Can't get unique UID (no more available UIDs)\n"), stderr);
+			return -1;
+		}
+	}
+
+	*uid = user_id;
+	return 0;
+}
+
+/*
+ * find_new_gid - Find a new unused GID.
+ *
+ * If successful, find_new_gid provides an unused group ID in the
+ * [GID_MIN:GID_MAX] range.
+ * This ID should be higher than all the used GID, but if not possible,
+ * the lowest unused ID in the range will be returned.
+ * 
+ * Return 0 on success, -1 if no unused GIDs are available.
+ */
+int find_new_gid (int sys_group, gid_t *gid, gid_t const *preferred_gid)
+{
+	const struct group *grp;
+	gid_t gid_min, gid_max, group_id;
+
+	assert (gid != NULL);
+	/* TODO: add support for system groups */
+	assert (sys_group == 0);
+
+	gid_min = getdef_unum ("GID_MIN", 1000);
+	gid_max = getdef_unum ("GID_MAX", 60000);
+
+	if (   (NULL != preferred_gid)
+	    && (*preferred_gid >= gid_min)
+	    && (*preferred_gid <= gid_max)
+	    /* Check if the user exists according to NSS */
+	    && (getgrgid (*preferred_gid) == NULL)
+	    /* Check also the local database in case of uncommitted
+	     * changes */
+	    && (gr_locate_gid (*preferred_gid) == NULL)) {
+		*gid = *preferred_gid;
+		return 0;
+	}
+
+	group_id = gid_min;
+
+	/*
+	 * Search the entire group file,
+	 * looking for the largest unused value.
+	 *
+	 * We check the list of users according to NSS (setpwent/getpwent),
+	 * but we also check the local database (pw_rewind/pw_next) in case
+	 * some groups were created but the changes were not committed yet.
+	 */
+	setgrent ();
+	gr_rewind ();
+	while (   ((grp = getgrent ()) != NULL)
+	       || ((grp = gr_next ()) != NULL)) {
+		if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) {
+			group_id = grp->gr_gid + 1;
+		}
+	}
+
+	/*
+	 * If a group with GID equal to GID_MAX exists, the above algorithm
+	 * will give us GID_MAX+1 even if not unique. Search for the first
+	 * free GID starting with GID_MIN (it's O(n*n) but can be avoided
+	 * by not having users with GID equal to GID_MAX).  --marekm
+	 */
+	if (group_id == gid_max + 1) {
+		for (group_id = gid_min; group_id < gid_max; group_id++) {
+			/* local, no need for xgetgrgid */
+			if (   (getgrgid (group_id) == NULL)
+			    && (gr_locate_gid (group_id) == NULL)) {
+				break;
+			}
+		}
+		if (group_id == gid_max) {
+			fputs (_("Can't get unique GID (no more available GIDs)\n"), stderr);
+			return -1;
+		}
+	}
+
+	*gid = group_id;
+	return 0;
+}
+




More information about the Pkg-shadow-commits mailing list