+/* Returns true if we need to call setgroups().
+
+ Without calling setgroups(), the process will retain any supplementary
+ gids associated with the uid, e.g.:
+
+ % groups root
+ root : root bin daemon sys adm disk wheel
+
+ However, setgroups() can only be called by root, and returns EPERM
+ for other users even if the call would be a no-op (e.g., setting the
+ group list to the current list.) So, to avoid that spurious error,
+ before calling setgroups() we first check whether the current list
+ of groups contains only one element, our target group. If so, we
+ don't need to call setgroups().
+ */
+static int
+setgroups_needed_p (uid_t target_group)
+{
+ gid_t groups[1024];
+ int n, size;
+ size = sizeof(groups) / sizeof(gid_t);
+ n = getgroups (size - 1, groups);
+ if (n < 0)
+ {
+ char buf [1024];
+ sprintf (buf, "%s: getgroups(%ld, ...)", blurb(), (long int)(size - 1));
+ perror (buf);
+ return 1;
+ }
+ else if (n == 0) /* an empty list means only egid is in effect. */
+ return 0;
+ else if (n == 1 && groups[0] == target_group) /* one element, the target */
+ return 0;
+ else /* more than one, or the wrong one. */
+ return 1;
+}
+
+