repo_shell
+acl_test
+mystrtok_test
version.c
*.swp
.svn
-PROGRAM := repo_shell
+MAIN := repo_shell
# Git derives its version "prefix" from tags
# Add TMI's mkversion to render files xxx.in -> xxx, updating __appVersion__
INFILES := $(INFILES:%.in=%)
#$(warning INFILES $(INFILES))
-repo_shell: repo_shell.c inih/ini.c version.c
+PROGRAMS = $(MAIN) mystrtok_test acl_test
+
+all: $(MAIN)
+
+repo_shell: repo_shell.c inih/ini.c mystrtok.c stra.c utility.c version.c
+ $(CC) -I inih $^ -o $@
+
+mystrtok_test: mystrtok_test.c mystrtok.c utility.c
+ $(CC) -g $^ -o $@
+
+acl_test: acl_test.c git_acl.c inih/ini.c mystrtok.c stra.c utility.c
$(CC) -I inih $^ -o $@
$(INFILES):%: %.in
@diff -q $@-tmp $@ >/dev/null 2>&1 && rm -f $@-tmp || mv $@-tmp $@
clean:
- @rm -rf $(PROGRAM) $(INFILES)
+ @rm -rf $(PROGRAMS) $(INFILES)
.PHONY: all clean $(INFILES)
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * Test parsing of the git acl config file
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#include <stdio.h>
+#include "git_acl.h"
+
+int main(int argc, char **argv)
+{
+ char *file = "git_acl.cfg.example";
+ perms_t p;
+
+ if (argc != 3)
+ die("usage: %s <username> <reponame>\n", argv[0]);
+
+ p = git_acl(argv[1], argv[2], file); /* file=NULL for default */
+
+ printf("config file %s\n"
+ "GITACL(%s@%s) = %s(%u)\n via repoid='%s', userid='%s'\n",
+ file, argv[1], argv[2], git_acl_perms_as_str(p), p,
+ git_acl_last_repoid(), git_acl_last_userid());
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * Parse the git_acl.cfg file to find the first permissions therein matching the
+ * requested user and repo names.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#include <stdbool.h>
+//#include <stdio.h>
+//#include <errno.h>
+//#include <stdlib.h>
+//#include <sys/types.h>
+//#include <fcntl.h>
+//#include <unistd.h>
+//#include <pwd.h>
+#include <string.h>
+#include "ini.h"
+#include "utility.h"
+#include "mystrtok.h"
+#include "stra.h"
+#include "git_acl.h"
+
+enum {
+ DFLT_IDS_SZ = 32
+};
+
+typedef struct {
+ char *repo;
+ char *user;
+ stra_t repoids;
+ stra_t userids;
+ perms_t perms;
+} acl_t;
+
+const char* perm_str[PERMS_COUNT] = {
+ "NOTFOUND", "NONE", "READ", "READ_WRITE"
+};
+
+const char* lm_none = "<none>";
+static char *lm_repoid = NULL;
+static char *lm_userid = NULL;
+static char lm_perms = PERMS_NOTFOUND;
+
+static char *get_lm_repoid()
+{
+ return (lm_repoid) ? lm_repoid : (char*)lm_none;
+}
+
+static void set_lm_repoid(const char* newstr)
+{
+ if (lm_repoid)
+ free(lm_repoid);
+ if (newstr)
+ lm_repoid = xstrdup(newstr);
+}
+
+static char *get_lm_userid()
+{
+ return (lm_userid) ? lm_userid : (char*)lm_none;
+}
+
+static void set_lm_userid(const char* newstr)
+{
+ if (lm_userid)
+ free(lm_userid);
+ if (newstr)
+ lm_userid = xstrdup(newstr);
+}
+
+static const char *perms_as_str(perms_t p)
+{
+ if (p < PERMS_NOTFOUND || p >= PERMS_COUNT)
+ die("perms_as_str: invalid perm %u", p);
+ return perm_str[p];
+}
+
+static perms_t perms_from_str(const char *str)
+{
+ perms_t p = PERMS_NOTFOUND;
+
+ if (!str)
+ return PERMS_NOTFOUND;
+ else if (!*str)
+ return PERMS_NONE;
+ else if (!strcmp(str, "r"))
+ return PERMS_READ;
+ else if (!strcmp(str, "rw"))
+ return PERMS_READ_WRITE;
+ else
+ die("Invalid perms value '%s'", str);
+}
+
+static acl_init(acl_t *acl)
+{
+ acl->repo = acl->user = NULL;
+ stra_init(&acl->repoids, DFLT_IDS_SZ);
+ stra_init(&acl->userids, DFLT_IDS_SZ);
+ acl->perms = PERMS_NOTFOUND;
+}
+
+static acl_clear(acl_t *acl)
+{
+ stra_destroy(&acl->repoids);
+ stra_destroy(&acl->userids);
+}
+
+static int str_has_word(const char* string, const char* word)
+{
+ char *_s = xstrdup(string);
+ char *s = _s;
+ char *p = my_strtok(&s, " \t\n");
+
+ while (p) {
+ if (!strcmp(p, word)) {
+ free(_s);
+ return true;
+ }
+ p = my_strtok(&s, " \t\n");
+ }
+ free(_s);
+ return false;
+}
+
+static int acl_handler(void* user, const char* section, const char* name,
+ const char* value)
+{
+ acl_t* acl = (acl_t*)user;
+
+ if (acl->perms)
+ return 1; /* Already matched an ACL entry for repo+user */
+
+ //debug("section='%s', name='%s', value='%s'", section, name, value);
+
+ if (!strcmp(section, "user_groups")) {
+ if (str_has_word(value, acl->user)) {
+ //debug("userids += '%s'", name);
+ stra_add(&acl->userids, name);
+ }
+ } else if (!strcmp(section, "repo_groups")) {
+ if (str_has_word(value, acl->repo)) {
+ //debug("repoids += '%s'", name);
+ stra_add(&acl->repoids, name);
+ }
+ } else if (!strncmp(section, "repo", 4)) {
+ char *_p = xstrdup(section + 4);
+ char *p = _p;
+ char *repo = my_strtok(&p, " \t\n");
+
+ if (!repo || my_strtok(&p, " \t\n"))
+ die("acl_handler: badly formatted section '%s'", section);
+ /* repo is repo name, name is userid, value is permission */
+ if (stra_find(&acl->repoids, repo) >= 0 &&
+ stra_find(&acl->userids, name) >= 0) {
+ acl->perms = perms_from_str(value);
+ set_lm_repoid(repo);
+ set_lm_userid(name);
+ lm_perms = acl->perms;
+ //debug("match: repoid='%s', userid='%s', perms='%s'(%u)", repo, name,
+ // value, acl->perms);
+ }
+ free(_p);
+ } else
+ die("acl_handler: unknown section='%s' name='%s'", section, name);
+ return 1;
+}
+
+int git_acl(char *user, char *repo, char *file)
+{
+ acl_t acl;
+
+ if (!file || !*file)
+ file = GIT_ACL_FILE;
+ if (!user || !*user || !repo || !*repo) {
+ die("git_acl: invalid args user='%s', repo='%s', file='%s'", user, repo,
+ file);
+ }
+
+ set_lm_repoid(NULL);
+ set_lm_userid(NULL);
+ lm_perms = PERMS_NOTFOUND;
+ acl_init(&acl);
+ acl.user = user;
+ acl.repo = repo;
+ stra_add(&acl.userids, acl.user);
+ stra_add(&acl.repoids, acl.repo);
+
+ //debug("Searching for '%s'@'%s'", acl.user, acl.repo);
+ if (ini_parse(file, acl_handler, &acl) < 0)
+ die("cannot read acl file %s", file);
+ acl_clear(&acl); /* acl.perms remains unchanged */
+ return acl.perms;
+}
+
+char *git_acl_last_repoid() { return get_lm_repoid(); }
+
+char *git_acl_last_userid() { return get_lm_userid(); }
+
+int git_acl_last_perms() { return lm_perms; }
+
+const char *git_acl_perms_as_str(perms_t p) { return perms_as_str(p); }
--- /dev/null
+# git_acl.cfg for use with repo_shell
+#
+# The [user_groups] section contains lines of the form:
+# <ugroup> = user1 user2 ... userN
+#
+# The [repo_groups] section contains lines of the form:
+# <rgroup> = repo1 repo2 ... repoN
+#
+# Nested user and repo groups are not supported. Therefore, user group names
+# (ugroup or rgroup) used as an element defining another group will treat the
+# former as a user or a repo, respectively.
+#
+# The [repo <repoid>] section contains lines of the form:
+# <userid> = <perms>
+# These lines define the permissions (<perms>) for one or more <userid>'s
+# (users or user groups) for the repository (or repository group) <repoid>.
+#
+# The first <userid> line in the first [repo <repoid>] section matching the user
+# and repo for which permissions are requested will satisfy the request. Any
+# other possible matches later in the configuration file are irrelevant.
+#
+# A user/repo combination that has no match in the git_acl.cfg file is reported
+# as the separate NOTFOUND permission, but is effectively treated the same as
+# the NONE permission.
+
+[user_groups]
+devs = smckown dlotton jobu
+losers = frank bill ted mike
+
+[repo_groups]
+mirrors = mirrors/tinyos.git mirrors/chibios.git
+public = oss-web.git repo_shell.git cp210x.git
+private = redmine.git nesc.git
+
+[repo nesc.git]
+smckown =
+ted = rw
+losers = r
+
+[repo oss-web.git]
+smckown = rw
+devs = r
+
+[repo mirrors/tinyos.git]
+jobu = rw
+smckown = r
+devs = -
+losers = r
+
+[repo public]
+dlotton = rw
+devs = -
+losers = r
+
+[repo private]
+smckown = rw
+dlotton = rw
+devs =
+
+[repo mirrors]
+jobu = r
+devs = rw
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * Parse the git_acl.cfg file to find the first permissions therein matching the
+ * requested user and repo names.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#ifndef GIT_ACL_H
+#define GIT_ACL_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <string.h>
+#include "ini.h"
+#include "utility.h"
+#include "mystrtok.h"
+#include "stra.h"
+
+#define GIT_ACL_FILE "git_acl.cfg"
+
+typedef enum {
+ PERMS_NOTFOUND = 0,
+ PERMS_NONE,
+ PERMS_READ,
+ PERMS_READ_WRITE,
+ PERMS_COUNT
+} perms_t;
+
+int git_acl(char *user, char *repo, char *file);
+char *git_acl_last_repoid();
+char *git_acl_last_userid();
+int git_acl_last_perms();
+const char *git_acl_perms_as_str(perms_t p);
+
+#endif /* end of include guard: GIT_ACL_H */
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * A custom strtok. This version skips any number and any combination of
+ * delimiter characters between tokens.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#include <stddef.h>
+#include <strings.h>
+
+char *my_strtok(char **begin, char* delims)
+{
+ char *p;
+ char *q;
+
+ if (!begin)
+ return NULL;
+ if (!delims || !*delims || !*begin)
+ return (*begin = NULL);
+
+ q = *begin;
+ while (*q && index(delims, *q)) q++;
+ if (!*q)
+ return (*begin = NULL);
+ p = q;
+ while (*q && !index(delims, *q)) q++;
+ if (*q)
+ *q++ = 0;
+ *begin = q;
+ return p;
+}
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * A custom strtok. This version skips any number and any combination of
+ * delimiter characters between tokens.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#ifndef MYSTRTOK_H
+#define MYSTRTOK_H
+
+char *my_strtok(char **begin, char* delims);
+
+#endif /* end of include guard: MYSTRTOK_H */
--- /dev/null
+#include <stdio.h>
+#include "utility.h"
+#include "mystrtok.h"
+
+int main(int argc, char **argv)
+{
+ char *text;
+ char *p;
+
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s \"A string to parse\" \"delimiters\"\n", argv[0]);
+ return 0;
+ }
+
+ text = argv[1];
+ putchar('|');
+ p = my_strtok(&text, argv[2]);
+ while (p) {
+ printf("%s|", p);
+ p = my_strtok(&text, argv[2]);
+ }
+ putchar('\n');
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * A dynamic string array implementation.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#include <stdlib.h>
+#include "utility.h"
+#include "stra.h"
+
+void stra_init(stra_t *stra, size_t size)
+{
+ if (!stra)
+ die("stra_destroy: stra NULL reference");
+ stra->items = xmalloc(sizeof(char*) * size);
+ stra->count = 0;
+ stra->size = size;
+}
+
+void stra_destroy(stra_t *stra)
+{
+ unsigned i;
+
+ if (!stra)
+ die("stra_destroy: stra NULL reference");
+ for (i = 0; i < stra->count; i++)
+ free(stra->items[i]);
+ if (stra->size) {
+ free(stra->items);
+ stra->size = 0;
+ }
+}
+
+int stra_add(stra_t *stra, const char *item)
+{
+ if (!stra)
+ die("stra_add: stra NULL reference");
+ if (!item)
+ die("stra_in: item is NULL");
+ if (stra->count > stra->size)
+ die("str_add: bad count=%u, size=%u\n", stra->count, stra->size);
+ else if (stra->count == stra->size) {
+ /* Expand items[] */
+ if (stra->size)
+ stra->size *= 2;
+ else
+ stra->size = 1;
+ stra->items = xrealloc(stra->items, stra->size);
+ }
+ stra->items[stra->count] = xstrdup(item);
+ return stra->count++;
+}
+
+int stra_find(stra_t *stra, const char *item)
+{
+ unsigned i;
+
+ if (!stra)
+ die("stra_in: stra NULL reference");
+ if (!item)
+ die("stra_in: item is NULL");
+ for (i = 0; i < stra->count; i++) {
+ if (!strcmp(stra->items[i], item))
+ return i;
+ }
+ return -1;
+}
+
+size_t stra_count(stra_t *stra)
+{
+ if (!stra)
+ die("stra_count: stra NULL reference");
+ return stra->count;
+}
+
+size_t stra_size(stra_t *stra)
+{
+ if (!stra)
+ die("stra_size: stra NULL reference");
+ return stra->size;
+}
+
+char *stra_item(stra_t *stra, size_t ele)
+{
+ if (!stra)
+ die("stra_item: stra NULL reference");
+ if (ele > stra->count)
+ die("stra_item: ele out of range (%u > %u)", ele, stra->count);
+ return stra->items[ele];
+}
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * A dynamic string array implementation.
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+
+#ifndef STRA_H
+#define STRA_H
+
+#include <stddef.h>
+
+typedef int (*stra_fneq_t)(void *item1, void *item2);
+
+typedef struct {
+ size_t count;
+ size_t size;
+ char **items;
+} stra_t;
+
+/* Initialize an stra structure, to hold up to size strings */
+void stra_init(stra_t *stra, size_t size);
+
+/* Destroy all dynamic memory associated with the stra. Use init to reuse */
+void stra_destroy(stra_t *stra);
+
+/* Add an item to the end of the string array, returning its ele# */
+int stra_add(stra_t *stra, const char *item);
+
+/* Return the ele# of the first string matching item */
+int stra_find(stra_t *stra, const char *item);
+
+/* Return the count of items in the stra */
+size_t stra_count(stra_t *stra);
+
+/* Return the current size of the stra */
+size_t stra_size(stra_t *stra);
+
+/* Return a pointer to the item at ele# ele */
+char *stra_item(stra_t *stra, size_t ele);
+
+#endif /* end of include guard: STRA_H */
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * Common functions for command line utility programs
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utility.h"
+
+void debug(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "debug: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n" );
+ va_end(ap);
+}
+
+void die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n" );
+ va_end(ap);
+ exit(1);
+}
+
+void *xmalloc(size_t size)
+{
+ void *ret;
+
+ if (!size)
+ die("xmalloc: size cannot be zero");
+ ret = malloc(size);
+ if (!ret)
+ die("out of memory (malloc)");
+ return ret;
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+ void *ret;
+
+ if (!size)
+ die("xrealloc: size cannot be zero");
+ ret = realloc(ptr, size);
+ if (!ret)
+ die("out of memory (realloc)");
+ return ret;
+}
+
+char *xstrdup(const char *str)
+{
+ char *ret = strdup(str);
+ if (!ret)
+ die("out of memory (strdup)");
+ return ret;
+}
+
+char *xstrndup(const char *str, size_t n)
+{
+ char *ret = strndup(str, n);
+ if (!ret)
+ die("out of memory (strndup)");
+ return ret;
+}
--- /dev/null
+/*
+ * Copyright © 2012, Titanium Mirror, Inc.
+ * All Rights Reserved.
+ *
+ * This document is the proprietary and confidential property of
+ * Titanium Mirror, Inc. All use, distribution, reproduction or re-distribution
+ * is disallowed without the prior express written consent of
+ * Titanium Mirror, Inc.
+ */
+
+/*
+ * Common functions for command line utility programs
+ *
+ * @author R. Steve McKown <rsmckown@gmail.com>
+ */
+
+#ifndef UTILITY_H
+#define UTILITY_H
+
+#include <stddef.h>
+#include <stdarg.h>
+
+void debug(const char *fmt, ...);
+void die(const char *fmt, ...);
+void *xmalloc(size_t size);
+void *xrealloc(void *ptr, size_t size);
+char *xstrdup(const char *str);
+char *xstrndup(const char *str, size_t n);
+
+#endif /* end of include guard: UTILITY_H */