Skip to content

Instantly share code, notes, and snippets.

@clausecker
Last active March 6, 2016 00:59
Show Gist options
  • Save clausecker/9f84b8be7591897f8473 to your computer and use it in GitHub Desktop.
Save clausecker/9f84b8be7591897f8473 to your computer and use it in GitHub Desktop.
Caching functions for the group and passwd database
/*-
* Copyright (c) 2016 Robert Clausecker. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define _XOPEN_SOURCE 700
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <grp.h>
#include "perm.h"
static size_t grpcachesize = 0;
static struct grentry {
char *groupname;
gid_t groupid;
} *grpcache = NULL;
static void clear_grpcache(struct grentry**, size_t*);
static int sort_by_groupid(const void*, const void*);
/*
* Load group to gid mappings from the group database and save them
* for future quick lookups. This function returns 0 on success, -1 on
* failure. errno is set to indicate the source of failure. Failure of
* this function should not be considered to be a hard failure and does
* not make calls to gid_to_group() fail. Such calls may be (depending
* on the implementation of getgrgid()) much slower though.
*/
extern int
fill_grpcache()
{
struct grentry *cache = NULL, *newcache;
struct group *group;
size_t i = 0, n = 0;
int saved_errno = errno;
errno = 0;
clear_grpcache(&grpcache, &grpcachesize);
while ((group = getgrent()) != NULL) {
if (i + 1 > n) {
/* approximate golden ratio in allocation scheme */
n = n > 0 ? (n * 13) / 8 : 64;
newcache = realloc(cache, n * sizeof *cache);
if (newcache == NULL) {
clear_grpcache(&cache, &i);
return (-1);
}
cache = newcache;
}
cache[i].groupid = group->gr_gid;
cache[i].groupname = strdup(group->gr_name);
if (cache[i++].groupname == NULL) {
clear_grpcache(&cache, &i);
return (-1);
}
}
if (errno != 0) {
clear_grpcache(&cache, &i);
return (-1);
}
/* ignore error when closing passwd database */
endgrent();
/* reclaim some memory */
newcache = realloc(cache, i * sizeof *cache);
if (newcache != NULL)
cache = newcache;
qsort(cache, i, sizeof *cache, &sort_by_groupid);
grpcache = cache;
grpcachesize = i;
errno = saved_errno;
return (0);
}
/*
* Lookup a group name from a gid. It's unspecified which group is
* returned if multiple group entries exist for a given gid. Calling
* gid_to_group(() without a succesful call to fill_grpcache()
* beforehand is not an error, in this case an attempt is made to lookup
* the desired information using getgrgid(). NULL is returned if the
* gid could not be found or if an error occured. Check errno to tell
* these two cases apart. The string returned by this function is only
* valid until the next call to this function or fill_grpcache().
* No attempt is made to keep data up to date.
*/
extern const char*
gid_to_group(gid_t groupid)
{
struct group *group;
struct grentry *match, key;
if (grpcache == NULL) {
group = getgrgid(groupid);
if (group == NULL)
return (NULL);
else
return (group->gr_name);
}
key.groupid = groupid;
match = bsearch(&key, grpcache, grpcachesize, sizeof *grpcache, &sort_by_groupid);
if (match == NULL)
return (NULL);
else
return (match->groupname);
}
/*
* Clear out a grp cache and free() its contents.
*/
static void
clear_grpcache(struct grentry **cachevar, size_t *cachesizevar)
{
size_t i, n = *cachesizevar;
struct grentry *cache = *cachevar;
*cachesizevar = 0;
*cachevar = NULL;
if (cache == NULL)
return;
for (i = 0; i < n; i++)
free(cache[i].groupname);
free(cache);
}
/*
* Sort struct grentry by groupid.
*/
static int
sort_by_groupid(const void *a, const void *b)
{
return (((const struct grentry*)a)->groupid - ((const struct grentry*)b)->groupid);
}
/*-
* Copyright (c) 2014, 2016 Robert Clausecker. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* perm.h - utility functions for permissions.
*/
#ifndef PERM_H
#define PERM_H
#include <pwd.h>
#include <grp.h>
extern int fill_pwdcache(void);
extern const char *uid_to_user(uid_t);
extern int fill_grpcache(void);
extern const char *gid_to_group(gid_t);
#endif /* PERM_H */
/*-
* Copyright (c) 2016 Robert Clausecker. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#define _XOPEN_SOURCE 700
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include "perm.h"
static size_t pwdcachesize = 0;
static struct pwentry {
char *username;
uid_t userid;
} *pwdcache = NULL;
static void clear_pwdcache(struct pwentry**, size_t*);
static int sort_by_userid(const void*, const void*);
/*
* Load user name to uid mappings from the passwd database and save them
* for future quick lookups. This function returns 0 on success, -1 on
* failure. errno is set to indicate the source of failure. Failure of
* this function should not be considered to be a hard failure and does
* not make calls to uid_to_user() fail. Such calls may be (depending
* on the implementation of getpwuid()) much slower though.
*/
extern int
fill_pwdcache()
{
struct pwentry *cache = NULL, *newcache;
struct passwd *passwd;
size_t i = 0, n = 0;
int saved_errno = errno;
errno = 0;
clear_pwdcache(&pwdcache, &pwdcachesize);
while ((passwd = getpwent()) != NULL) {
if (i + 1 > n) {
/* approximate golden ratio in allocation scheme */
n = n > 0 ? (n * 13) / 8 : 64;
newcache = realloc(cache, n * sizeof *cache);
if (newcache == NULL) {
clear_pwdcache(&cache, &i);
return (-1);
}
cache = newcache;
}
cache[i].userid = passwd->pw_uid;
cache[i].username = strdup(passwd->pw_name);
if (cache[i++].username == NULL) {
clear_pwdcache(&cache, &i);
return (-1);
}
}
if (errno != 0) {
clear_pwdcache(&cache, &i);
return (-1);
}
/* ignore error when closing passwd database */
endpwent();
/* reclaim some memory */
newcache = realloc(cache, i * sizeof *cache);
if (newcache != NULL)
cache = newcache;
qsort(cache, i, sizeof *cache, &sort_by_userid);
pwdcache = cache;
pwdcachesize = i;
errno = saved_errno;
return (0);
}
/*
* Lookup a user name from a uid. It's unspecified which user name is
* returned if multiple passwd entries exist for a given uid. Calling
* uid_to_user() without a succesful call to fill_pwdcache() beforehand
* is not an error, in this case an attempt is made to lookup the
* desired information using getpwuid(). NULL is returned if the uid
* could not be found or if an error occured. Check errno to tell these
* two cases apart. The string returned by this function is only
* valid until the next call to this function or fill_pwdcache().
* No attempt is made to keep data up to date.
*/
extern const char*
uid_to_user(uid_t userid)
{
struct passwd *passwd;
struct pwentry *match, key;
if (pwdcache == NULL) {
passwd = getpwuid(userid);
if (passwd == NULL)
return (NULL);
else
return (passwd->pw_name);
}
key.userid = userid;
match = bsearch(&key, pwdcache, pwdcachesize, sizeof *pwdcache, &sort_by_userid);
if (match == NULL)
return (NULL);
else
return (match->username);
}
/*
* Clear out a pwd cache and free() its contents.
*/
static void
clear_pwdcache(struct pwentry **cachevar, size_t *cachesizevar)
{
size_t i, n = *cachesizevar;
struct pwentry *cache = *cachevar;
*cachesizevar = 0;
*cachevar = NULL;
if (cache == NULL)
return;
for (i = 0; i < n; i++)
free(cache[i].username);
free(cache);
}
/*
* Sort struct pwentry by userid.
*/
static int
sort_by_userid(const void *a, const void *b)
{
return (((const struct pwentry*)a)->userid - ((const struct pwentry*)b)->userid);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment