diff options
| author | Ted Percival <ted@midg3t.net> | 2009-08-21 16:02:57 -0600 | 
|---|---|---|
| committer | Lennart Poettering <lennart@poettering.net> | 2009-08-22 00:53:39 +0200 | 
| commit | 15eb03a5b39f8c54328caa7516a7870bf977db40 (patch) | |
| tree | 7e10ffc65f73d73a88d5bd45b62291c9b4f1a6f7 | |
| parent | 9d1cc133f397b6aa37c986dbc42d3aa4262a0847 (diff) | |
core: Add thread-safe group info functions with dynamic buffers
Provides getgrgid, getgrnam, getpwuid & getpwnam replacements that are
thread safe (a la getgrgid_r() and friends) that internally
handle allocating big-enough buffers to avoid ERANGE errors
on large users or groups.
| -rw-r--r-- | src/Makefile.am | 12 | ||||
| -rw-r--r-- | src/pulse/util.c | 52 | ||||
| -rw-r--r-- | src/pulsecore/core-util.c | 106 | ||||
| -rw-r--r-- | src/pulsecore/usergroup.c | 376 | ||||
| -rw-r--r-- | src/pulsecore/usergroup.h | 51 | ||||
| -rw-r--r-- | src/tests/usergroup-test.c | 161 | 
6 files changed, 643 insertions, 115 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index fd440991..73c0db5b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -280,7 +280,8 @@ TESTS = \  		proplist-test \  		lock-autospawn-test \  		prioq-test \ -		sigbus-test +		sigbus-test \ +		usergroup-test  TESTS_BINARIES = \  		mainloop-test \ @@ -318,7 +319,8 @@ TESTS_BINARIES = \  		stripnul \  		lock-autospawn-test \  		prioq-test \ -		sigbus-test +		sigbus-test \ +		usergroup-test  if HAVE_SIGXCPU  #TESTS += \ @@ -557,6 +559,11 @@ alsa_time_test_LDADD = $(AM_LDADD)  alsa_time_test_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)  alsa_time_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(ASOUNDLIB_LIBS) +usergroup_test_SOURCES = tests/usergroup-test.c +usergroup_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la +usergroup_test_CFLAGS = $(AM_CFLAGS) +usergroup_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +  ###################################  #         Common library          #  ################################### @@ -621,6 +628,7 @@ libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \  		pulsecore/tagstruct.c pulsecore/tagstruct.h \  		pulsecore/time-smoother.c pulsecore/time-smoother.h \  		pulsecore/tokenizer.c pulsecore/tokenizer.h \ +		pulsecore/usergroup.c pulsecore/usergroup.h \  		pulsecore/sndfile-util.c pulsecore/sndfile-util.h \  		pulsecore/winsock.h diff --git a/src/pulse/util.c b/src/pulse/util.c index 6f1e40a9..9440f5de 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -61,38 +61,40 @@  #include <pulsecore/log.h>  #include <pulsecore/core-util.h>  #include <pulsecore/macro.h> +#include <pulsecore/usergroup.h>  #include "util.h"  char *pa_get_user_name(char *s, size_t l) {      const char *p; +    char *name = NULL; +#ifdef OS_IS_WIN32      char buf[1024]; +#endif  #ifdef HAVE_PWD_H -    struct passwd pw, *r; +    struct passwd *r;  #endif      pa_assert(s);      pa_assert(l > 0); -    if (!(p = (getuid() == 0 ? "root" : NULL)) && -        !(p = getenv("USER")) && -        !(p = getenv("LOGNAME")) && -        !(p = getenv("USERNAME"))) { +    if ((p = (getuid() == 0 ? "root" : NULL)) || +        (p = getenv("USER")) || +        (p = getenv("LOGNAME")) || +        (p = getenv("USERNAME"))) +    { +        name = pa_strlcpy(s, p, l); +    } else {  #ifdef HAVE_PWD_H -#ifdef HAVE_GETPWUID_R -        if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { -#else -        /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) -            * that do not support getpwuid_r. */ -        if ((r = getpwuid(getuid())) == NULL) { -#endif +        if ((r = pa_getpwuid_malloc(getuid())) == NULL) {              pa_snprintf(s, l, "%lu", (unsigned long) getuid());              return s;          } -        p = r->pw_name; +        name = pa_strlcpy(s, r->pw_name, l); +        pa_getpwuid_free(r);  #elif defined(OS_IS_WIN32) /* HAVE_PWD_H */          DWORD size = sizeof(buf); @@ -102,7 +104,7 @@ char *pa_get_user_name(char *s, size_t l) {              return NULL;          } -        p = buf; +        name = pa_strlcpy(s, buf, l);  #else /* HAVE_PWD_H */ @@ -110,7 +112,7 @@ char *pa_get_user_name(char *s, size_t l) {  #endif /* HAVE_PWD_H */      } -    return pa_strlcpy(s, p, l); +    return name;  }  char *pa_get_host_name(char *s, size_t l) { @@ -126,11 +128,10 @@ char *pa_get_host_name(char *s, size_t l) {  }  char *pa_get_home_dir(char *s, size_t l) { -    char *e; +    char *e, *dir;  #ifdef HAVE_PWD_H -    char buf[1024]; -    struct passwd pw, *r; +    struct passwd *r;  #endif      pa_assert(s); @@ -143,22 +144,19 @@ char *pa_get_home_dir(char *s, size_t l) {          return pa_strlcpy(s, e, l);  #ifdef HAVE_PWD_H -      errno = 0; -#ifdef HAVE_GETPWUID_R -    if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) { -#else -    /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X) -        * that do not support getpwuid_r. */ -    if ((r = getpwuid(getuid())) == NULL) { -#endif +    if ((r = pa_getpwuid_malloc(getuid())) == NULL) {          if (!errno)              errno = ENOENT;          return NULL;      } -    return pa_strlcpy(s, r->pw_dir, l); +    dir = pa_strlcpy(s, r->pw_dir, l); + +    pa_getpwuid_free(r); + +    return dir;  #else /* HAVE_PWD_H */      errno = ENOENT; diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 843c8377..0eb32cc4 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -115,6 +115,7 @@  #include <pulsecore/macro.h>  #include <pulsecore/thread.h>  #include <pulsecore/strbuf.h> +#include <pulsecore/usergroup.h>  #include "core-util.h" @@ -969,42 +970,24 @@ fail:  /* Check whether the specified GID and the group name match */  static int is_group(gid_t gid, const char *name) { -    struct group group, *result = NULL; -    long n; -    void *data; +    struct group *group = NULL;      int r = -1; -#ifdef HAVE_GETGRGID_R - -#ifdef _SC_GETGR_R_SIZE_MAX -    n = sysconf(_SC_GETGR_R_SIZE_MAX); -#else -    n = -1; -#endif -    if (n <= 0) -        n = 512; - -    data = pa_xmalloc((size_t) n); - -    if ((errno = getgrgid_r(gid, &group, data, (size_t) n, &result)) || !result) -#else      errno = 0; -    if (!(result = getgrgid(gid))) -#endif +    if (!(group = pa_getgrgid_malloc(gid)))      {          if (!errno)              errno = ENOENT; -        pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno)); +        pa_log("pa_getgrgid_malloc(%u): %s", gid, pa_cstrerror(errno));          goto finish;      } -    r = strcmp(name, result->gr_name) == 0; +    r = strcmp(name, group->gr_name) == 0;  finish: - -    pa_xfree(data); +    pa_getgrgid_free(group);      return r;  } @@ -1053,69 +1036,37 @@ finish:  /* Check whether the specifc user id is a member of the specified group */  int pa_uid_in_group(uid_t uid, const char *name) { -    char *g_buf = NULL, *p_buf = NULL; -    long g_n, p_n; -    struct group grbuf, *gr = NULL; +    struct group *group = NULL;      char **i;      int r = -1; -#ifdef HAVE_GETGRNAM_R - -#ifdef _SC_GETGR_R_SIZE_MAX -    g_n = sysconf(_SC_GETGR_R_SIZE_MAX); -#else -    g_n = -1; -#endif -    if (g_n <= 0) -        g_n = 512; - -    g_buf = pa_xmalloc((size_t) g_n); - -    if ((errno = getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr)) != 0 || !gr) -#else      errno = 0; -    if (!(gr = getgrnam(name))) -#endif +    if (!(group = pa_getgrnam_malloc(name)))      {          if (!errno)              errno = ENOENT;          goto finish;      } -#ifdef HAVE_GETPWNAM_R - -#ifdef _SC_GETPW_R_SIZE_MAX -    p_n = sysconf(_SC_GETPW_R_SIZE_MAX); -#else -    p_n = -1; -#endif -    if (p_n <= 0) -        p_n = 512; - -    p_buf = pa_xmalloc((size_t) p_n); -#endif -      r = 0; -    for (i = gr->gr_mem; *i; i++) { -        struct passwd pwbuf, *pw = NULL; +    for (i = group->gr_mem; *i; i++) { +        struct passwd *pw = NULL; -#ifdef HAVE_GETPWNAM_R -        if ((errno = getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw)) != 0 || !pw) -#else          errno = 0; -        if (!(pw = getpwnam(*i))) -#endif +        if (!(pw = pa_getpwnam_malloc(*i)))              continue; -        if (pw->pw_uid == uid) { +        if (pw->pw_uid == uid)              r = 1; + +        pa_getpwnam_free(pw); + +        if (r == 1)              break; -        }      }  finish: -    pa_xfree(g_buf); -    pa_xfree(p_buf); +    pa_getgrnam_free(group);      return r;  } @@ -1123,27 +1074,10 @@ finish:  /* Get the GID of a gfiven group, return (gid_t) -1 on failure. */  gid_t pa_get_gid_of_group(const char *name) {      gid_t ret = (gid_t) -1; -    char *g_buf = NULL; -    long g_n; -    struct group grbuf, *gr = NULL; - -#ifdef HAVE_GETGRNAM_R - -#ifdef _SC_GETGR_R_SIZE_MAX -    g_n = sysconf(_SC_GETGR_R_SIZE_MAX); -#else -    g_n = -1; -#endif -    if (g_n <= 0) -        g_n = 512; - -    g_buf = pa_xmalloc((size_t) g_n); +    struct group *gr = NULL; -    if ((errno = getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr)) != 0 || !gr) -#else      errno = 0; -    if (!(gr = getgrnam(name))) -#endif +    if (!(gr = pa_getgrnam_malloc(name)))      {          if (!errno)              errno = ENOENT; @@ -1153,7 +1087,7 @@ gid_t pa_get_gid_of_group(const char *name) {      ret = gr->gr_gid;  finish: -    pa_xfree(g_buf); +    pa_getgrnam_free(gr);      return ret;  } diff --git a/src/pulsecore/usergroup.c b/src/pulsecore/usergroup.c new file mode 100644 index 00000000..bf686b77 --- /dev/null +++ b/src/pulsecore/usergroup.c @@ -0,0 +1,376 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2009 Ted Percival + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <errno.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#ifdef HAVE_GRP_H +#include <grp.h> +#endif + +#include <pulse/xmalloc.h> +#include <pulsecore/macro.h> + +#include "usergroup.h" + +#ifdef HAVE_GRP_H + +/* Returns a suitable starting size for a getgrnam_r() or getgrgid_r() buffer, +   plus the size of a struct group. + */ +static size_t starting_getgr_buflen(void) { +    size_t full_size; +    long n; +#ifdef _SC_GETGR_R_SIZE_MAX +    n = sysconf(_SC_GETGR_R_SIZE_MAX); +#else +    n = -1; +#endif +    if (n <= 0) +        n = 512; + +    full_size = (size_t) n + sizeof(struct group); + +    if (full_size < (size_t) n) /* check for integer overflow */ +        return (size_t) n; + +    return full_size; +} + +/* Returns a suitable starting size for a getpwnam_r() or getpwuid_r() buffer, +   plus the size of a struct passwd. + */ +static size_t starting_getpw_buflen(void) { +    long n; +    size_t full_size; + +#ifdef _SC_GETPW_R_SIZE_MAX +    n = sysconf(_SC_GETPW_R_SIZE_MAX); +#else +    n = -1; +#endif +    if (n <= 0) +        n = 512; + +    full_size = (size_t) n + sizeof(struct passwd); + +    if (full_size < (size_t) n) /* check for integer overflow */ +        return (size_t) n; + +    return full_size; +} + +/* Given a memory allocation (*bufptr) and its length (*buflenptr), +   double the size of the allocation, updating the given buffer and length +   arguments. This function should be used in conjunction with the pa_*alloc +   and pa_xfree functions. + +   Unlike realloc(), this function does *not* retain the original buffer's +   contents. + +   Returns 0 on success, nonzero on error. The error cause is indicated by +   errno. + */ +static int expand_buffer_trashcontents(void **bufptr, size_t *buflenptr) { +    size_t newlen; + +    if (!bufptr || !*bufptr || !buflenptr) { +        errno = EINVAL; +        return -1; +    } + +    newlen = *buflenptr * 2; + +    if (newlen < *buflenptr) { +        errno = EOVERFLOW; +        return -1; +    } + +    /* Don't bother retaining memory contents; free & alloc anew */ +    pa_xfree(*bufptr); + +    *bufptr = pa_xmalloc(newlen); +    *buflenptr = newlen; + +    return 0; +} + +#ifdef HAVE_GETGRGID_R +/* Thread-safe getgrgid() replacement. +   Returned value should be freed using pa_getgrgid_free() when the caller is +   finished with the returned group data. + +   API is the same as getgrgid(), errors are indicated by a NULL return; +   consult errno for the error cause (zero it before calling). +   The returned value must be freed using pa_xfree(). + */ +struct group *pa_getgrgid_malloc(gid_t gid) { +    size_t buflen, getgr_buflen; +    int err; +    void *buf; +    void *getgr_buf; +    struct group *result = NULL; + +    buflen = starting_getgr_buflen(); +    buf = pa_xmalloc(buflen); + +    getgr_buflen = buflen - sizeof(struct group); +    getgr_buf = (char *)buf + sizeof(struct group); + +    while ((err = getgrgid_r(gid, (struct group *)buf, getgr_buf, +                    getgr_buflen, &result)) == ERANGE) +    { +        if (expand_buffer_trashcontents(&buf, &buflen)) +            break; + +        getgr_buflen = buflen - sizeof(struct group); +        getgr_buf = (char *)buf + sizeof(struct group); +    } + +    if (err || !result) { +        result = NULL; +        if (buf) { +            pa_xfree(buf); +            buf = NULL; +        } +    } + +    pa_assert(result == buf || result == NULL); + +    return result; +} + +void pa_getgrgid_free(struct group *grp) { +    pa_xfree(grp); +} + +#else /* !HAVE_GETGRGID_R */ + +struct group *pa_getgrgid_malloc(gid_t gid) { +    return getgrgid(gid); +} + +void pa_getgrgid_free(struct group *grp) { +    /* nothing */ +    return; +} + +#endif /* !HAVE_GETGRGID_R */ + +#ifdef HAVE_GETGRNAM_R +/* Thread-safe getgrnam() function. +   Returned value should be freed using pa_getgrnam_free() when the caller is +   finished with the returned group data. + +   API is the same as getgrnam(), errors are indicated by a NULL return; +   consult errno for the error cause (zero it before calling). +   The returned value must be freed using pa_xfree(). + */ +struct group *pa_getgrnam_malloc(const char *name) { +    size_t buflen, getgr_buflen; +    int err; +    void *buf; +    void *getgr_buf; +    struct group *result = NULL; + +    buflen = starting_getgr_buflen(); +    buf = pa_xmalloc(buflen); + +    getgr_buflen = buflen - sizeof(struct group); +    getgr_buf = (char *)buf + sizeof(struct group); + +    while ((err = getgrnam_r(name, (struct group *)buf, getgr_buf, +                    getgr_buflen, &result)) == ERANGE) +    { +        if (expand_buffer_trashcontents(&buf, &buflen)) +            break; + +        getgr_buflen = buflen - sizeof(struct group); +        getgr_buf = (char *)buf + sizeof(struct group); +    } + +    if (err || !result) { +        result = NULL; +        if (buf) { +            pa_xfree(buf); +            buf = NULL; +        } +    } + +    pa_assert(result == buf || result == NULL); + +    return result; +} + +void pa_getgrnam_free(struct group *group) { +    pa_xfree(group); +} + +#else /* !HAVE_GETGRNAM_R */ + +struct group *pa_getgrnam_malloc(const char *name) { +    return getgrnam(name); +} + +void pa_getgrnam_free(struct group *group) { +    /* nothing */ +    return; +} + +#endif /* HAVE_GETGRNAM_R */ + +#endif /* HAVE_GRP_H */ + +#ifdef HAVE_PWD_H + +#ifdef HAVE_GETPWNAM_R +/* Thread-safe getpwnam() function. +   Returned value should be freed using pa_getpwnam_free() when the caller is +   finished with the returned passwd data. + +   API is the same as getpwnam(), errors are indicated by a NULL return; +   consult errno for the error cause (zero it before calling). +   The returned value must be freed using pa_xfree(). + */ +struct passwd *pa_getpwnam_malloc(const char *name) { +    size_t buflen, getpw_buflen; +    int err; +    void *buf; +    void *getpw_buf; +    struct passwd *result = NULL; + +    buflen = starting_getpw_buflen(); +    buf = pa_xmalloc(buflen); + +    getpw_buflen = buflen - sizeof(struct passwd); +    getpw_buf = (char *)buf + sizeof(struct passwd); + +    while ((err = getpwnam_r(name, (struct passwd *)buf, getpw_buf, +                    getpw_buflen, &result)) == ERANGE) +    { +        if (expand_buffer_trashcontents(&buf, &buflen)) +            break; + +        getpw_buflen = buflen - sizeof(struct passwd); +        getpw_buf = (char *)buf + sizeof(struct passwd); +    } + +    if (err || !result) { +        result = NULL; +        if (buf) { +            pa_xfree(buf); +            buf = NULL; +        } +    } + +    pa_assert(result == buf || result == NULL); + +    return result; +} + +void pa_getpwnam_free(struct passwd *passwd) { +    pa_xfree(passwd); +} + +#else /* !HAVE_GETPWNAM_R */ + +struct passwd *pa_getpwnam_malloc(const char *name) { +    return getpwnam(name); +} + +void pa_getpwnam_free(struct passwd *passwd) { +    /* nothing */ +    return; +} + +#endif /* !HAVE_GETPWNAM_R */ + +#ifdef HAVE_GETPWUID_R +/* Thread-safe getpwuid() function. +   Returned value should be freed using pa_getpwuid_free() when the caller is +   finished with the returned group data. + +   API is the same as getpwuid(), errors are indicated by a NULL return; +   consult errno for the error cause (zero it before calling). +   The returned value must be freed using pa_xfree(). + */ +struct passwd *pa_getpwuid_malloc(uid_t uid) { +    size_t buflen, getpw_buflen; +    int err; +    void *buf; +    void *getpw_buf; +    struct passwd *result = NULL; + +    buflen = starting_getpw_buflen(); +    buf = pa_xmalloc(buflen); + +    getpw_buflen = buflen - sizeof(struct passwd); +    getpw_buf = (char *)buf + sizeof(struct passwd); + +    while ((err = getpwuid_r(uid, (struct passwd *)buf, getpw_buf, +                    getpw_buflen, &result)) == ERANGE) +    { +        if (expand_buffer_trashcontents(&buf, &buflen)) +            break; + +        getpw_buflen = buflen - sizeof(struct passwd); +        getpw_buf = (char *)buf + sizeof(struct passwd); +    } + +    if (err || !result) { +        result = NULL; +        if (buf) { +            pa_xfree(buf); +            buf = NULL; +        } +    } + +    pa_assert(result == buf || result == NULL); + +    return result; +} + +void pa_getpwuid_free(struct passwd *passwd) { +    pa_xfree(passwd); +} + +#else /* !HAVE_GETPWUID_R */ + +struct passwd *pa_getpwuid_malloc(uid_t uid) { +    return getpwuid(uid); +} + +void pa_getpwuid_free(struct passwd *passwd) { +    /* nothing */ +    return; +} + +#endif /* !HAVE_GETPWUID_R */ + +#endif /* HAVE_PWD_H */ diff --git a/src/pulsecore/usergroup.h b/src/pulsecore/usergroup.h new file mode 100644 index 00000000..1c091638 --- /dev/null +++ b/src/pulsecore/usergroup.h @@ -0,0 +1,51 @@ +#ifndef foousergrouphfoo +#define foousergrouphfoo + +/*** +  This file is part of PulseAudio. + +  Copyright 2009 Ted Percival + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <sys/types.h> + +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif + +#ifdef HAVE_GRP_H + +struct group *pa_getgrgid_malloc(gid_t gid); +void pa_getgrgid_free(struct group *grp); + +struct group *pa_getgrnam_malloc(const char *name); +void pa_getgrnam_free(struct group *group); + +#endif /* HAVE_GRP_H */ + +#ifdef HAVE_PWD_H + +struct passwd *pa_getpwuid_malloc(uid_t uid); +void pa_getpwuid_free(struct passwd *passwd); + +struct passwd *pa_getpwnam_malloc(const char *name); +void pa_getpwnam_free(struct passwd *passwd); + +#endif /* HAVE_PWD_H */ + +#endif /* foousergrouphfoo */ diff --git a/src/tests/usergroup-test.c b/src/tests/usergroup-test.c new file mode 100644 index 00000000..a48b016d --- /dev/null +++ b/src/tests/usergroup-test.c @@ -0,0 +1,161 @@ +/*** +  This file is part of PulseAudio. + +  Copyright 2009 Ted Percival + +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. + +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> + +#include <pulsecore/usergroup.h> + +static int load_reference_structs(struct group **gr, struct passwd **pw) { +    setpwent(); +    *pw = getpwent(); +    endpwent(); + +    setgrent(); +    *gr = getgrent(); +    endgrent(); + +    return (*gr && *pw) ? 0 : 1; +} + +static int compare_group(const struct group *a, const struct group *b) { +    char **amem, **bmem; + +    if (strcmp(a->gr_name, b->gr_name)) { +        fprintf(stderr, "Group name mismatch: [%s] [%s]\n", +                a->gr_name, b->gr_name); +        return 1; +    } + +    if (strcmp(a->gr_passwd, b->gr_passwd)) { +        fprintf(stderr, "Group password mismatch: [%s] [%s]\n", +                a->gr_passwd, b->gr_passwd); +        return 1; +    } + +    if (a->gr_gid != b->gr_gid) { +        fprintf(stderr, "Gid mismatch: [%lu] [%lu]\n", +                (unsigned long) a->gr_gid, (unsigned long) b->gr_gid); +        return 1; +    } + +    /* XXX: Assuming the group ordering is identical. */ +    for (amem = a->gr_mem, bmem = b->gr_mem; *amem && *bmem; ++amem, ++bmem) { +        if (strcmp(*amem, *bmem)) { +            fprintf(stderr, "Group member mismatch: [%s] [%s]\n", +                    *amem, *bmem); +            return 1; +        } +    } + +    if (*amem || *bmem) { +        fprintf(stderr, "Mismatched group count\n"); +        return 1; +    } + +    return 0; +} + +static int compare_passwd(const struct passwd *a, const struct passwd *b) { +    if (strcmp(a->pw_name, b->pw_name)) { +        fprintf(stderr, "pw_name mismatch: [%s] [%s]\n", a->pw_name, b->pw_name); +        return 1; +    } + +    if (strcmp(a->pw_passwd, b->pw_passwd)) { +        fprintf(stderr, "pw_passwd mismatch: [%s] [%s]\n", a->pw_passwd, b->pw_passwd); +        return 1; +    } + +    if (a->pw_uid != b->pw_uid) { +        fprintf(stderr, "pw_uid mismatch: [%lu] [%lu]\n", +		(unsigned long) a->pw_uid, (unsigned long) b->pw_uid); +        return 1; +    } + +    if (a->pw_gid != b->pw_gid) { +        fprintf(stderr, "pw_gid mismatch: [%lu] [%lu]\n", +		(unsigned long) a->pw_gid, (unsigned long) b->pw_gid); +        return 1; +    } + +    if (strcmp(a->pw_gecos, b->pw_gecos)) { +        fprintf(stderr, "pw_gecos mismatch: [%s] [%s]\n", a->pw_gecos, b->pw_gecos); +        return 1; +    } + +    if (strcmp(a->pw_dir, b->pw_dir)) { +        fprintf(stderr, "pw_dir mismatch: [%s] [%s]\n", a->pw_dir, b->pw_dir); +        return 1; +    } + +    if (strcmp(a->pw_shell, b->pw_shell)) { +        fprintf(stderr, "pw_shell mismatch: [%s] [%s]\n", a->pw_shell, b->pw_shell); +        return 1; +    } + +    return 0; +} + +int main(int argc, char *argv[]) { +    struct group *gr; +    struct passwd *pw; +    int err; +    struct group *reference_group = NULL; +    struct passwd *reference_passwd = NULL; + +    err = load_reference_structs(&reference_group, &reference_passwd); +    if (err) +        return 77; + +    errno = 0; +    gr = pa_getgrgid_malloc(reference_group->gr_gid); +    if (compare_group(reference_group, gr)) +        return 1; +    pa_getgrgid_free(gr); + +    errno = 0; +    gr = pa_getgrnam_malloc(reference_group->gr_name); +    if (compare_group(reference_group, gr)) +        return 1; +    pa_getgrnam_free(gr); + +    errno = 0; +    pw = pa_getpwuid_malloc(reference_passwd->pw_uid); +    if (compare_passwd(reference_passwd, pw)) +        return 1; +    pa_getpwuid_free(pw); + +    errno = 0; +    pw = pa_getpwnam_malloc(reference_passwd->pw_name); +    if (compare_passwd(reference_passwd, pw)) +        return 1; +    pa_getpwnam_free(pw); + +    return 0; +}  | 
