about summary refs log tree commit diff
path: root/Src/Modules/parameter.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Modules/parameter.c')
-rw-r--r--Src/Modules/parameter.c139
1 files changed, 138 insertions, 1 deletions
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 793249f32..092efa0c3 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -1820,6 +1820,141 @@ scanpmdissaliases(HashTable ht, ScanFunc func, int flags)
     scanaliases(sufaliastab, ht, func, flags, ALIAS_SUFFIX|DISABLED);
 }
 
+
+/* Functions for the usergroups special parameter */
+
+/*
+ * Get GID and names for groups of which the current user is a member.
+ */
+
+/**/
+static Groupset get_all_groups(void)
+{
+    Groupset gs = zhalloc(sizeof(*gs));
+    Groupmap gaptr;
+    gid_t *list, *lptr, egid;
+    int add_egid;
+    struct group *grptr;
+
+    egid = getegid();
+    add_egid = 1;
+    gs->num = getgroups(0, NULL);
+    if (gs->num > 0) {
+	list = zhalloc(gs->num * sizeof(*list));
+	if (getgroups(gs->num, list) < 0) {
+	    return NULL;
+	}
+
+	/*
+	 * It's unspecified whether $EGID is included in the
+	 * group set, so check.
+	 */
+	for (lptr = list; lptr < list + gs->num; lptr++) {
+	    if (*lptr == egid) {
+		add_egid = 0;
+		break;
+	    }
+	}
+	gs->array = zhalloc((gs->num + add_egid) * sizeof(*gs->array));
+	/* Put EGID if needed first */
+	gaptr = gs->array + add_egid;
+	for (lptr = list; lptr < list + gs->num; lptr++) {
+	    gaptr->gid = *lptr;
+	    gaptr++;
+	}
+	gs->num += add_egid;
+    } else {
+	/* Just use effective GID */
+	gs->num = 1;
+	gs->array = zhalloc(sizeof(*gs->array));
+    }
+    if (add_egid) {
+	gs->array->gid = egid;
+    }
+
+    /* Get group names */
+    for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
+	grptr = getgrgid(gaptr->gid);
+	if (!grptr) {
+	    return NULL;
+	}
+	gaptr->name = dupstring(grptr->gr_name);
+    }
+
+    return gs;
+}
+
+/* Standard hash element lookup. */
+
+/**/
+static HashNode
+getpmusergroups(UNUSED(HashTable ht), const char *name)
+{
+    Param pm = NULL;
+    Groupset gs = get_all_groups();
+    Groupmap gaptr;
+
+    pm = (Param)hcalloc(sizeof(struct param));
+    pm->node.nam = dupstring(name);
+    pm->node.flags = PM_SCALAR | PM_READONLY;
+    pm->gsu.s = &nullsetscalar_gsu;
+
+    if (!gs) {
+	zerr("failed to retrieve groups for user: %e", errno);
+	pm->u.str = dupstring("");
+	pm->node.flags |= PM_UNSET;
+	return &pm->node;
+    }
+
+    for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
+	if (!strcmp(name, gaptr->name)) {
+	    char buf[DIGBUFSIZE];
+
+	    sprintf(buf, "%d", (int)gaptr->gid);
+	    pm->u.str = dupstring(buf);
+	    return &pm->node;
+	}
+    }
+
+    pm->u.str = dupstring("");
+    pm->node.flags |= PM_UNSET;
+    return &pm->node;
+}
+
+/* Standard hash scan. */
+
+/**/
+static void
+scanpmusergroups(UNUSED(HashTable ht), ScanFunc func, int flags)
+{
+    struct param pm;
+    Groupset gs = get_all_groups();
+    Groupmap gaptr;
+
+    if (!gs) {
+	zerr("failed to retrieve groups for user: %e", errno);
+	return;
+    }
+
+    memset((void *)&pm, 0, sizeof(pm));
+    pm.node.flags = PM_SCALAR | PM_READONLY;
+    pm.gsu.s = &nullsetscalar_gsu;
+
+    for (gaptr = gs->array; gaptr < gs->array + gs->num; gaptr++) {
+	pm.node.nam = gaptr->name;
+	if (func != scancountparams &&
+	    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
+	     !(flags & SCANPM_WANTKEYS))) {
+	    char buf[DIGBUFSIZE];
+
+	    sprintf(buf, "%d", (int)gaptr->gid);
+	    pm.u.str = dupstring(buf);
+	}
+	func(&pm.node, flags);
+    }
+}
+
+
 /* Table for defined parameters. */
 
 struct pardef {
@@ -1926,7 +2061,9 @@ static struct paramdef partab[] = {
     SPECIALPMDEF("saliases", 0,
 	    &pmsaliases_gsu, getpmsalias, scanpmsaliases),
     SPECIALPMDEF("userdirs", PM_READONLY,
-	    NULL, getpmuserdir, scanpmuserdirs)
+	    NULL, getpmuserdir, scanpmuserdirs),
+    SPECIALPMDEF("usergroups", PM_READONLY,
+	    NULL, getpmusergroups, scanpmusergroups)
 };
 
 static struct features module_features = {