From 6bd476632311503105e0ce33bf1288b3a4ba919f Mon Sep 17 00:00:00 2001 From: Jose Bollo Date: Wed, 9 Oct 2019 17:34:23 +0200 Subject: Rename cynagoradm to cynagoracli Signed-off-by: Jose Bollo --- src/CMakeLists.txt | 8 +- src/main-cynagoracli.c | 830 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main-cynagoradm.c | 830 ------------------------------------------------- 3 files changed, 834 insertions(+), 834 deletions(-) create mode 100644 src/main-cynagoracli.c delete mode 100644 src/main-cynagoradm.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b69c15f..bde17f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,10 +101,10 @@ install(TARGETS cynagorad RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) ########################################### -# build and install cynagoradm +# build and install cynagoracli ########################################### -add_executable(cynagoradm main-cynagoradm.c expire.c) -target_link_libraries(cynagoradm cynagora) -install(TARGETS cynagoradm +add_executable(cynagoracli main-cynagoracli.c expire.c) +target_link_libraries(cynagoracli cynagora) +install(TARGETS cynagoracli RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) diff --git a/src/main-cynagoracli.c b/src/main-cynagoracli.c new file mode 100644 index 0000000..db36243 --- /dev/null +++ b/src/main-cynagoracli.c @@ -0,0 +1,830 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/******************************************************************************/ +/******************************************************************************/ +/* IMPLEMENTATION OF CYNAGORA ADMINISTRATION TOOL */ +/******************************************************************************/ +/******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cynagora.h" +#include "cyn-protocol.h" +#include "expire.h" + +#define DEFAULT_CACHE_SIZE 5000 + +#define _CACHE_ 'c' +#define _ECHO_ 'e' +#define _HELP_ 'h' +#define _SOCKET_ 's' +#define _VERSION_ 'v' + +static +const char +shortopts[] = "c:ehs:v"; + +static +const struct option +longopts[] = { + { "cache", 1, NULL, _CACHE_ }, + { "echo", 0, NULL, _ECHO_ }, + { "help", 0, NULL, _HELP_ }, + { "socket", 1, NULL, _SOCKET_ }, + { "version", 0, NULL, _VERSION_ }, + { NULL, 0, NULL, 0 } +}; + +static +const char +helptxt[] = + "\n" + "usage: cynagoracli [options]... [action [arguments]]\n" + "\n" + "otpions:\n" + " -s, --socket xxx set the base xxx for sockets\n" + " -e, --echo print the evaluated command\n" + " -c, --cache xxx set the cache size to xxx bytes\n" + " (default: %d)\n" + " -h, --help print this help and exit\n" + " -v, --version print the version and exit\n" + "\n" + "When action is given, cynagoracli performs the action and exits.\n" + "Otherwise cynagoracli continuously read its input to get the actions.\n" + "For a list of actions type 'cynagoracli help'.\n" + "\n" +; + +static +const char +versiontxt[] = + "cynagoracli version 1.99.99\n" +; + +static +const char +help__text[] = + "\n" + "Commands are: list, set, drop, check, test, cache, clear, quit, log, help\n" + "Type 'help command' to get help on the command\n" + "Type 'help expiration' to get help on expirations\n" + "\n" +; + +static +const char +help_list_text[] = + "\n" + "Command: list [client [session [user [permission]]]]\n" + "\n" + "List the rules matching the optionally given 'client', 'session',\n" + "'user', 'permission'.\n" + "\n" + "This command requires to be connected to the administrator socket.\n" + "\n" + "The value given can be '#' (sharp) to match any value. When no value\n" + "is given, it is implied as being '#'.\n" + "\n" + "Examples:\n" + "\n" + " list list all rules\n" + " list # # 1001 list the rules of the user 1001\n" + "\n" +; + +static +const char +help_set_text[] = + "\n" + "Command: set client session user permission value expiration\n" + "\n" + "Set the rule associating the given 'client', 'session', 'user'\n" + "permission' with the 'value' for a time given by 'expiration'.\n" + "\n" + "Type 'help expiration' to get help on expirations\n" + "\n" + "This command requires to be connected to the administrator socket.\n" + "\n" + "Examples:\n" + "\n" + " set * * 0 * yes * set forever the value yes for user 0 and any\n" + " permission, client or session.\n" + "\n" + " set wrt * * X no 1d set for one day the value no for client xrt and\n" + " permission X of any user and session.\n" + "\n" +; + +static +const char +help_drop_text[] = + "\n" + "Command: drop [client [session [user [permission]]]]\n" + "\n" + "Removes the rules matching the optionally given 'client', 'session',\n" + "'user', 'permission'.\n" + "\n" + "This command requires to be connected to the administrator socket.\n" + "\n" + "The value given can be '#' (sharp) to match any value. When no value\n" + "is given, it is implied as being '#'.\n" + "\n" + "Examples:\n" + "\n" + " drop drop all rules\n" + " drop # # 1001 drop the rules of the user 1001\n" + "\n" +; + +static +const char +help_check_text[] = + "\n" + "Command: check client session user permission\n" + "\n" + "Check authorization for the given 'client', 'session', 'user', 'permission'.\n" + "\n" + "Examples:\n" + "\n" + " check wrt W3llcomE 1001 audio check that client 'wrt' of session\n" + " 'W3llcomE' for user '1001' has the\n" + " 'audio' permission\n" + "\n" +; + +static +const char +help_acheck_text[] = + "\n" + "Command: acheck client session user permission\n" + "\n" + "Check asynchronously authorization for the given 'client', 'session', 'user', 'permission'.\n" + "Same as check but don't wait the answer.\n" + "\n" +; + +static +const char +help_test_text[] = + "\n" + "Command: test client session user permission\n" + "\n" + "Test authorization for the given 'client', 'session', 'user', 'permission'.\n" + "Same as command 'check' except that it doesn't use query agent if it were\n" + "needed to avoid asynchronous timely unlimited queries.\n" + "\n" + "Examples:\n" + "\n" + " test wrt W3llcomE 1001 audio check that client 'wrt' of session\n" + " 'W3llcomE' for user '1001' has the\n" + " 'audio' permission\n" + "\n" +; + +static +const char +help_atest_text[] = + "\n" + "Command: atest client session user permission\n" + "\n" + "Test asynchronously authorization for the given 'client', 'session', 'user', 'permission'.\n" + "Same as test but don't wait the answer.\n" + "\n" +; + +static +const char +help_log_text[] = + "\n" + "Command: log [on|off]\n" + "\n" + "With the 'on' or 'off' arguments, set the logging state to what required.\n" + "In all cases, prints the logging state.\n" + "\n" + "Examples:\n" + "\n" + " log on activates the logging\n" + "\n" +; + +static +const char +help_cache_text[] = + "\n" + "Command: cache client session user permission\n" + "\n" + "Test cache for authorization for the given 'client', 'session', 'user', 'permission'.\n" + "\n" + "Examples:\n" + "\n" + " cache wrt W3llcomE 1001 audio check that client 'wrt' of session\n" + " 'W3llcomE' for user '1001' has the\n" + " 'audio' permission\n" + "\n" +; + +static +const char +help_clear_text[] = + "\n" + "Command: clear\n" + "\n" + "Clear the current cache.\n" + "\n" +; + +static +const char +help_quit_text[] = + "\n" + "Command: quit\n" + "\n" + "Quit the program\n" + "\n" +; + +static +const char +help_help_text[] = + "\n" + "Command: help [command | topic]\n" + "\n" + "Gives help on the command or on the topic.\n" + "\n" + "Available commands: list, set, drop, check, test, cache, clear, quit, help\n" + "Available topics: expiration\n" + "\n" +; + +static +const char +help_expiration_text[] = + "\n" + "Expirations limited in the time are expressed using the scheme NyNdNhNmNs\n" + "where N are numeric values and ydhms are unit specifications.\n" + "Almost all part of the scheme are optional. The default unit is second.\n" + "\n" + "Unlimited expirations can be expressed using: 0, *, always or forever.\n" + "\n" + "Examples:\n" + "\n" + " 6y5d 6 years and 5 days\n" + " 1d6h 1 day and 6 hours\n" + " 56 56 seconds\n" + " forever unlimited, no expiration\n" + "\n" +; + +static cynagora_t *cynagora; +static char buffer[4000]; +static size_t bufill; +static char *str[40]; +static int nstr; +static int pending; +static int echo; + +cynagora_key_t key; +cynagora_value_t value; + +int plink(int ac, char **av, int *used, int maxi) +{ + int r = 0; + + if (maxi < ac) + ac = maxi; + while (r < ac && strcmp(av[r], ";")) + r++; + + *used = r + (r < ac); + return r; +} + +int get_csupve(int ac, char **av, int *used, const char *def) +{ + int n = plink(ac, av, used, 7); + + key.client = n > 1 ? av[1] : def; + key.session = n > 2 ? av[2] : def; + key.user = n > 3 ? av[3] : def; + key.permission = n > 4 ? av[4] : def; + value.value = n > 5 ? av[5] : "no"; + value.expire = n > 6 ? txt2exp(av[6]) : 0; + + return key.client && key.session && key.user && key.permission && value.value && value.expire >= 0 ? 0 : -EINVAL; +} + +int get_csup(int ac, char **av, int *used, const char *def) +{ + int n = plink(ac, av, used, 5); + + key.client = n > 1 ? av[1] : def; + key.session = n > 2 ? av[2] : def; + key.user = n > 3 ? av[3] : def; + key.permission = n > 4 ? av[4] : def; + + return key.client && key.session && key.user && key.permission ? 0 : -EINVAL; +} + + +struct listresult { + int count; + size_t lengths[6]; + struct listitem { + struct listitem *next; + const char *items[6]; + } *head; +}; + +struct listitem *listresult_sort(int count, struct listitem *head) +{ + int n1, n2, i, r, c1, c2; + struct listitem *i1, *i2, **pp; + + if (count == 1) + head->next = 0; + else { + i2 = head; + n2 = count >> 1; + n1 = 0; + while (n1 < n2) { + n1++; + i2 = i2->next; + } + n2 = count - n1; + i1 = listresult_sort(n1, head); + i2 = listresult_sort(n2, i2); + pp = &head; + while(n1 || n2) { + c1 = !n2; + c2 = !n1; + for (i = 0 ; !c1 && !c2 && i < 4 ; i++) { + r = strcmp(i1->items[i], i2->items[i]); + c1 = r < 0; + c2 = r > 0; + } + if (c1) { + *pp = i1; + i1 = i1->next; + n1--; + } else { + *pp = i2; + i2 = i2->next; + n2--; + } + pp = &(*pp)->next; + } + *pp = 0; + } + return head; +} + +void listcb(void *closure, const cynagora_key_t *k, const cynagora_value_t *v) +{ + struct listresult *lr = closure; + char buffer[100], *p; + const char *items[6]; + struct listitem *it; + size_t s, as; + int i; + + exp2txt(v->expire, buffer, sizeof buffer); + items[0] = k->client; + items[1] = k->session; + items[2] = k->user; + items[3] = k->permission; + items[4] = v->value; + items[5] = buffer; + + as = 0; + for (i = 0 ; i < 6 ; i++) { + s = strlen(items[i]); + as += s; + if (s > lr->lengths[i]) + lr->lengths[i] = s; + } + it = malloc(sizeof *it + as + 6); + if (!it) + fprintf(stdout, "%s\t%s\t%s\t%s\t%s\t%s\n", + k->client, k->session, k->user, k->permission, + v->value, buffer); + else { + p = (char*)(it + 1); + for (i = 0 ; i < 6 ; i++) { + it->items[i] = p; + p = 1 + stpcpy(p, items[i]); + } + it->next = lr->head; + lr->head = it; + } + lr->count++; +} + +int do_list(int ac, char **av) +{ + int uc, rc; + struct listresult lr; + struct listitem *it; + + rc = get_csup(ac, av, &uc, "#"); + if (rc == 0) { + memset(&lr, 0, sizeof lr); + rc = cynagora_get(cynagora, &key, listcb, &lr); + if (rc < 0) + fprintf(stderr, "error %s\n", strerror(-rc)); + else { + if (lr.count) { + it = lr.head = listresult_sort(lr.count, lr.head); + while(it) { + fprintf(stdout, "%-*s %-*s %-*s %-*s %-*s %-*s\n", + (int)lr.lengths[0], it->items[0], + (int)lr.lengths[1], it->items[1], + (int)lr.lengths[2], it->items[2], + (int)lr.lengths[3], it->items[3], + (int)lr.lengths[4], it->items[4], + (int)lr.lengths[5], it->items[5]); + it = it->next; + } + } + fprintf(stdout, "%d entries found\n", lr.count); + } + /* free list */ + while(lr.head) { + it = lr.head; + lr.head = it->next; + free(it); + } + } + return uc; +} + +int do_set(int ac, char **av) +{ + int uc, rc; + + rc = get_csupve(ac, av, &uc, "*"); + if (rc == 0) + rc = cynagora_enter(cynagora); + if (rc == 0) { + rc = cynagora_set(cynagora, &key, &value); + cynagora_leave(cynagora, !rc); + } + if (rc < 0) + fprintf(stderr, "error %s\n", strerror(-rc)); + return uc; +} + +int do_drop(int ac, char **av) +{ + int uc, rc; + + rc = get_csup(ac, av, &uc, "#"); + if (rc == 0) + rc = cynagora_enter(cynagora); + if (rc == 0) { + rc = cynagora_drop(cynagora, &key); + cynagora_leave(cynagora, !rc); + } + if (rc < 0) + fprintf(stderr, "error %s\n", strerror(-rc)); + return uc; +} + +int do_check(int ac, char **av, int (*f)(cynagora_t*,const cynagora_key_t*)) +{ + int uc, rc; + + rc = get_csup(ac, av, &uc, NULL); + if (rc == 0) { + rc = f(cynagora, &key); + if (rc > 0) + fprintf(stdout, "allowed\n"); + else if (rc == 0) + fprintf(stdout, "denied\n"); + else if (rc == -ENOENT && f == cynagora_cache_check) + fprintf(stdout, "not in cache!\n"); + else if (rc == -EEXIST) + fprintf(stderr, "denied but an entry exist\n"); + else + fprintf(stderr, "error %s\n", strerror(-rc)); + } + return uc; +} + +void acheck_cb(void *closure, int status) +{ + if (status > 0) + fprintf(stdout, "allowed\n"); + else if (status == 0) + fprintf(stdout, "denied\n"); + else + fprintf(stderr, "error %s\n", strerror(-status)); + pending--; +} + +int do_acheck(int ac, char **av, bool simple) +{ + int uc, rc; + + rc = get_csup(ac, av, &uc, NULL); + if (rc == 0) { + pending++; + rc = cynagora_async_check(cynagora, &key, simple, acheck_cb, NULL); + if (rc < 0) { + fprintf(stderr, "error %s\n", strerror(-rc)); + pending--; + } + } + return uc; +} + +int do_log(int ac, char **av) +{ + int uc, rc; + int on = 0, off = 0; + int n = plink(ac, av, &uc, 2); + + if (n > 1) { + on = !strcmp(av[1], "on"); + off = !strcmp(av[1], "off"); + if (!on && !off) { + fprintf(stderr, "bad argument '%s'\n", av[1]); + return uc; + } + } + rc = cynagora_log(cynagora, on, off); + if (rc < 0) + fprintf(stderr, "error %s\n", strerror(-rc)); + else + fprintf(stdout, "logging %s\n", rc ? "on" : "off"); + return uc; +} + +int do_help(int ac, char **av) +{ + if (ac > 1 && !strcmp(av[1], "list")) + fprintf(stdout, "%s", help_list_text); + else if (ac > 1 && !strcmp(av[1], "set")) + fprintf(stdout, "%s", help_set_text); + else if (ac > 1 && !strcmp(av[1], "drop")) + fprintf(stdout, "%s", help_drop_text); + else if (ac > 1 && !strcmp(av[1], "check")) + fprintf(stdout, "%s", help_check_text); + else if (ac > 1 && !strcmp(av[1], "acheck")) + fprintf(stdout, "%s", help_acheck_text); + else if (ac > 1 && !strcmp(av[1], "test")) + fprintf(stdout, "%s", help_test_text); + else if (ac > 1 && !strcmp(av[1], "atest")) + fprintf(stdout, "%s", help_atest_text); + else if (ac > 1 && !strcmp(av[1], "cache")) + fprintf(stdout, "%s", help_cache_text); + else if (ac > 1 && !strcmp(av[1], "clear")) + fprintf(stdout, "%s", help_clear_text); + else if (ac > 1 && !strcmp(av[1], "log")) + fprintf(stdout, "%s", help_log_text); + else if (ac > 1 && !strcmp(av[1], "quit")) + fprintf(stdout, "%s", help_quit_text); + else if (ac > 1 && !strcmp(av[1], "help")) + fprintf(stdout, "%s", help_help_text); + else if (ac > 1 && !strcmp(av[1], "expiration")) + fprintf(stdout, "%s", help_expiration_text); + else { + fprintf(stdout, "%s", help__text); + return 1; + } + return 2; +} + +int do_any(int ac, char **av) +{ + if (!ac) + return 0; + + if (!strcmp(av[0], "list")) + return do_list(ac, av); + + if (!strcmp(av[0], "set")) + return do_set(ac, av); + + if (!strcmp(av[0], "drop")) + return do_drop(ac, av); + + if (!strcmp(av[0], "check")) + return do_check(ac, av, cynagora_check); + + if (!strcmp(av[0], "acheck")) + return do_acheck(ac, av, 0); + + if (!strcmp(av[0], "test")) + return do_check(ac, av, cynagora_test); + + if (!strcmp(av[0], "atest")) + return do_acheck(ac, av, 1); + + if (!strcmp(av[0], "cache")) + return do_check(ac, av, cynagora_cache_check); + + if (!strcmp(av[0], "log")) + return do_log(ac, av); + + if (!strcmp(av[0], "clear")) { + cynagora_cache_clear(cynagora); + return 1; + } + + if (!strcmp(av[0], "quit")) + return 0; + + if (!strcmp(av[0], "help") || !strcmp(av[0], "?")) + return do_help(ac, av); + + fprintf(stderr, "unknown command %s\n", av[0]); + return 0; +} + +void do_all(int ac, char **av) +{ + int rc; + + if (echo) { + for (rc = 0 ; rc < ac ; rc++) + fprintf(stdout, "%s%s", rc ? " " : "", av[rc]); + fprintf(stdout, "\n"); + } + while(ac) { + rc = do_any(ac, av); + if (rc <= 0) + exit(1); + ac -= rc; + av += rc; + } +} + +int async_ctl(void *closure, int op, int fd, uint32_t events) +{ + int *pfd = closure; + + switch(op) { + case EPOLL_CTL_ADD: + case EPOLL_CTL_MOD: + *pfd = fd; + break; + case EPOLL_CTL_DEL: + *pfd = -1; + break; + } + return 0; +} + +int main(int ac, char **av) +{ + int opt; + int rc; + int help = 0; + int version = 0; + int error = 0; + uint32_t cachesize = DEFAULT_CACHE_SIZE; + unsigned long ul; + char *socket = NULL; + char *cache = NULL; + struct pollfd fds[2]; + char *p; + + setlinebuf(stdout); + + /* scan arguments */ + for (;;) { + opt = getopt_long(ac, av, shortopts, longopts, NULL); + if (opt == -1) + break; + + switch(opt) { + case _CACHE_: + cache = optarg; + ul = strtoul(optarg, &cache, 0); + if (*cache || ul > UINT32_MAX) + error = 1; + else + cachesize = (uint32_t)ul; + break; + case _ECHO_: + echo = 1; + break; + case _HELP_: + help = 1; + break; + case _SOCKET_: + socket = optarg; + break; + case _VERSION_: + version = 1; + break; + default: + error = 1; + break; + } + } + + /* handles help, version, error */ + if (help) { + fprintf(stdout, helptxt, DEFAULT_CACHE_SIZE); + return 0; + } + if (version) { + fprintf(stdout, versiontxt); + return 0; + } + if (error) + return 1; + + /* initialize server */ + signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */ + rc = cynagora_create(&cynagora, cynagora_Admin, cachesize, socket); + if (rc < 0) { + fprintf(stderr, "initialization failed: %s\n", strerror(-rc)); + return 1; + } + + fds[1].fd = -1; + rc = cynagora_async_setup(cynagora, async_ctl, &fds[1].fd); + if (rc < 0) { + fprintf(stderr, "asynchronous setup failed: %s\n", strerror(-rc)); + return 1; + } + + if (optind < ac) { + do_all(ac - optind, av + optind); + return 0; + } + + fcntl(0, F_SETFL, O_NONBLOCK); + bufill = 0; + fds[0].fd = 0; + fds[0].events = fds[1].events = POLLIN; + for(;;) { + rc = poll(fds, 2, -1); + if (fds[0].revents & POLLIN) { + rc = (int)read(0, &buffer[bufill], sizeof buffer - bufill); + if (rc == 0) + break; + if (rc > 0) { + bufill += (size_t)rc; + while((p = memchr(buffer, '\n', bufill))) { + /* process one line */ + *p++ = 0; + str[nstr = 0] = strtok(buffer, " \t"); + while(str[nstr]) + str[++nstr] = strtok(NULL, " \t"); + do_all(nstr, str); + bufill -= (size_t)(p - buffer); + if (!bufill) + break; + memmove(buffer, p, bufill); + } + } + } + if (fds[0].revents & POLLHUP) { + if (!pending) + break; + fds[0].fd = -1; + } + if (fds[1].revents & POLLIN) { + rc = cynagora_async_process(cynagora); + if (rc < 0) + fprintf(stderr, "asynchronous processing failed: %s\n", strerror(-rc)); + if (fds[0].fd < 0 && !pending) + break; + } + if (fds[1].revents & POLLHUP) { + if (fds[0].fd < 0) + break; + fds[1].fd = -1; + } + } + return 0; +} diff --git a/src/main-cynagoradm.c b/src/main-cynagoradm.c deleted file mode 100644 index b668abb..0000000 --- a/src/main-cynagoradm.c +++ /dev/null @@ -1,830 +0,0 @@ -/* - * Copyright (C) 2018 "IoT.bzh" - * Author José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/******************************************************************************/ -/******************************************************************************/ -/* IMPLEMENTATION OF CYNAGORA ADMINISTRATION TOOL */ -/******************************************************************************/ -/******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cynagora.h" -#include "cyn-protocol.h" -#include "expire.h" - -#define DEFAULT_CACHE_SIZE 5000 - -#define _CACHE_ 'c' -#define _ECHO_ 'e' -#define _HELP_ 'h' -#define _SOCKET_ 's' -#define _VERSION_ 'v' - -static -const char -shortopts[] = "c:ehs:v"; - -static -const struct option -longopts[] = { - { "cache", 1, NULL, _CACHE_ }, - { "echo", 0, NULL, _ECHO_ }, - { "help", 0, NULL, _HELP_ }, - { "socket", 1, NULL, _SOCKET_ }, - { "version", 0, NULL, _VERSION_ }, - { NULL, 0, NULL, 0 } -}; - -static -const char -helptxt[] = - "\n" - "usage: cynadm [options]... [action [arguments]]\n" - "\n" - "otpions:\n" - " -s, --socket xxx set the base xxx for sockets\n" - " -e, --echo print the evaluated command\n" - " -c, --cache xxx set the cache size to xxx bytes\n" - " (default: %d)\n" - " -h, --help print this help and exit\n" - " -v, --version print the version and exit\n" - "\n" - "When action is given, cynadm performs the action and exits.\n" - "Otherwise cynadm continuously read its input to get the actions.\n" - "For a list of actions tpe 'cynadm help'.\n" - "\n" -; - -static -const char -versiontxt[] = - "cynadm version 1.99.99\n" -; - -static -const char -help__text[] = - "\n" - "Commands are: list, set, drop, check, test, cache, clear, quit, log, help\n" - "Type 'help command' to get help on the command\n" - "Type 'help expiration' to get help on expirations\n" - "\n" -; - -static -const char -help_list_text[] = - "\n" - "Command: list [client [session [user [permission]]]]\n" - "\n" - "List the rules matching the optionally given 'client', 'session',\n" - "'user', 'permission'.\n" - "\n" - "This command requires to be connected to the administrator socket.\n" - "\n" - "The value given can be '#' (sharp) to match any value. When no value\n" - "is given, it is implied as being '#'.\n" - "\n" - "Examples:\n" - "\n" - " list list all rules\n" - " list # # 1001 list the rules of the user 1001\n" - "\n" -; - -static -const char -help_set_text[] = - "\n" - "Command: set client session user permission value expiration\n" - "\n" - "Set the rule associating the given 'client', 'session', 'user'\n" - "permission' with the 'value' for a time given by 'expiration'.\n" - "\n" - "Type 'help expiration' to get help on expirations\n" - "\n" - "This command requires to be connected to the administrator socket.\n" - "\n" - "Examples:\n" - "\n" - " set * * 0 * yes * set forever the value yes for user 0 and any\n" - " permission, client or session.\n" - "\n" - " set wrt * * X no 1d set for one day the value no for client xrt and\n" - " permission X of any user and session.\n" - "\n" -; - -static -const char -help_drop_text[] = - "\n" - "Command: drop [client [session [user [permission]]]]\n" - "\n" - "Removes the rules matching the optionally given 'client', 'session',\n" - "'user', 'permission'.\n" - "\n" - "This command requires to be connected to the administrator socket.\n" - "\n" - "The value given can be '#' (sharp) to match any value. When no value\n" - "is given, it is implied as being '#'.\n" - "\n" - "Examples:\n" - "\n" - " drop drop all rules\n" - " drop # # 1001 drop the rules of the user 1001\n" - "\n" -; - -static -const char -help_check_text[] = - "\n" - "Command: check client session user permission\n" - "\n" - "Check authorization for the given 'client', 'session', 'user', 'permission'.\n" - "\n" - "Examples:\n" - "\n" - " check wrt W3llcomE 1001 audio check that client 'wrt' of session\n" - " 'W3llcomE' for user '1001' has the\n" - " 'audio' permission\n" - "\n" -; - -static -const char -help_acheck_text[] = - "\n" - "Command: acheck client session user permission\n" - "\n" - "Check asynchronously authorization for the given 'client', 'session', 'user', 'permission'.\n" - "Same as check but don't wait the answer.\n" - "\n" -; - -static -const char -help_test_text[] = - "\n" - "Command: test client session user permission\n" - "\n" - "Test authorization for the given 'client', 'session', 'user', 'permission'.\n" - "Same as command 'check' except that it doesn't use query agent if it were\n" - "needed to avoid asynchronous timely unlimited queries.\n" - "\n" - "Examples:\n" - "\n" - " test wrt W3llcomE 1001 audio check that client 'wrt' of session\n" - " 'W3llcomE' for user '1001' has the\n" - " 'audio' permission\n" - "\n" -; - -static -const char -help_atest_text[] = - "\n" - "Command: atest client session user permission\n" - "\n" - "Test asynchronously authorization for the given 'client', 'session', 'user', 'permission'.\n" - "Same as test but don't wait the answer.\n" - "\n" -; - -static -const char -help_log_text[] = - "\n" - "Command: log [on|off]\n" - "\n" - "With the 'on' or 'off' arguments, set the logging state to what required.\n" - "In all cases, prints the logging state.\n" - "\n" - "Examples:\n" - "\n" - " log on activates the logging\n" - "\n" -; - -static -const char -help_cache_text[] = - "\n" - "Command: cache client session user permission\n" - "\n" - "Test cache for authorization for the given 'client', 'session', 'user', 'permission'.\n" - "\n" - "Examples:\n" - "\n" - " cache wrt W3llcomE 1001 audio check that client 'wrt' of session\n" - " 'W3llcomE' for user '1001' has the\n" - " 'audio' permission\n" - "\n" -; - -static -const char -help_clear_text[] = - "\n" - "Command: clear\n" - "\n" - "Clear the current cache.\n" - "\n" -; - -static -const char -help_quit_text[] = - "\n" - "Command: quit\n" - "\n" - "Quit the program\n" - "\n" -; - -static -const char -help_help_text[] = - "\n" - "Command: help [command | topic]\n" - "\n" - "Gives help on the command or on the topic.\n" - "\n" - "Available commands: list, set, drop, check, test, cache, clear, quit, help\n" - "Available topics: expiration\n" - "\n" -; - -static -const char -help_expiration_text[] = - "\n" - "Expirations limited in the time are expressed using the scheme NyNdNhNmNs\n" - "where N are numeric values and ydhms are unit specifications.\n" - "Almost all part of the scheme are optional. The default unit is second.\n" - "\n" - "Unlimited expirations can be expressed using: 0, *, always or forever.\n" - "\n" - "Examples:\n" - "\n" - " 6y5d 6 years and 5 days\n" - " 1d6h 1 day and 6 hours\n" - " 56 56 seconds\n" - " forever unlimited, no expiration\n" - "\n" -; - -static cynagora_t *cynagora; -static char buffer[4000]; -static size_t bufill; -static char *str[40]; -static int nstr; -static int pending; -static int echo; - -cynagora_key_t key; -cynagora_value_t value; - -int plink(int ac, char **av, int *used, int maxi) -{ - int r = 0; - - if (maxi < ac) - ac = maxi; - while (r < ac && strcmp(av[r], ";")) - r++; - - *used = r + (r < ac); - return r; -} - -int get_csupve(int ac, char **av, int *used, const char *def) -{ - int n = plink(ac, av, used, 7); - - key.client = n > 1 ? av[1] : def; - key.session = n > 2 ? av[2] : def; - key.user = n > 3 ? av[3] : def; - key.permission = n > 4 ? av[4] : def; - value.value = n > 5 ? av[5] : "no"; - value.expire = n > 6 ? txt2exp(av[6]) : 0; - - return key.client && key.session && key.user && key.permission && value.value && value.expire >= 0 ? 0 : -EINVAL; -} - -int get_csup(int ac, char **av, int *used, const char *def) -{ - int n = plink(ac, av, used, 5); - - key.client = n > 1 ? av[1] : def; - key.session = n > 2 ? av[2] : def; - key.user = n > 3 ? av[3] : def; - key.permission = n > 4 ? av[4] : def; - - return key.client && key.session && key.user && key.permission ? 0 : -EINVAL; -} - - -struct listresult { - int count; - size_t lengths[6]; - struct listitem { - struct listitem *next; - const char *items[6]; - } *head; -}; - -struct listitem *listresult_sort(int count, struct listitem *head) -{ - int n1, n2, i, r, c1, c2; - struct listitem *i1, *i2, **pp; - - if (count == 1) - head->next = 0; - else { - i2 = head; - n2 = count >> 1; - n1 = 0; - while (n1 < n2) { - n1++; - i2 = i2->next; - } - n2 = count - n1; - i1 = listresult_sort(n1, head); - i2 = listresult_sort(n2, i2); - pp = &head; - while(n1 || n2) { - c1 = !n2; - c2 = !n1; - for (i = 0 ; !c1 && !c2 && i < 4 ; i++) { - r = strcmp(i1->items[i], i2->items[i]); - c1 = r < 0; - c2 = r > 0; - } - if (c1) { - *pp = i1; - i1 = i1->next; - n1--; - } else { - *pp = i2; - i2 = i2->next; - n2--; - } - pp = &(*pp)->next; - } - *pp = 0; - } - return head; -} - -void listcb(void *closure, const cynagora_key_t *k, const cynagora_value_t *v) -{ - struct listresult *lr = closure; - char buffer[100], *p; - const char *items[6]; - struct listitem *it; - size_t s, as; - int i; - - exp2txt(v->expire, buffer, sizeof buffer); - items[0] = k->client; - items[1] = k->session; - items[2] = k->user; - items[3] = k->permission; - items[4] = v->value; - items[5] = buffer; - - as = 0; - for (i = 0 ; i < 6 ; i++) { - s = strlen(items[i]); - as += s; - if (s > lr->lengths[i]) - lr->lengths[i] = s; - } - it = malloc(sizeof *it + as + 6); - if (!it) - fprintf(stdout, "%s\t%s\t%s\t%s\t%s\t%s\n", - k->client, k->session, k->user, k->permission, - v->value, buffer); - else { - p = (char*)(it + 1); - for (i = 0 ; i < 6 ; i++) { - it->items[i] = p; - p = 1 + stpcpy(p, items[i]); - } - it->next = lr->head; - lr->head = it; - } - lr->count++; -} - -int do_list(int ac, char **av) -{ - int uc, rc; - struct listresult lr; - struct listitem *it; - - rc = get_csup(ac, av, &uc, "#"); - if (rc == 0) { - memset(&lr, 0, sizeof lr); - rc = cynagora_get(cynagora, &key, listcb, &lr); - if (rc < 0) - fprintf(stderr, "error %s\n", strerror(-rc)); - else { - if (lr.count) { - it = lr.head = listresult_sort(lr.count, lr.head); - while(it) { - fprintf(stdout, "%-*s %-*s %-*s %-*s %-*s %-*s\n", - (int)lr.lengths[0], it->items[0], - (int)lr.lengths[1], it->items[1], - (int)lr.lengths[2], it->items[2], - (int)lr.lengths[3], it->items[3], - (int)lr.lengths[4], it->items[4], - (int)lr.lengths[5], it->items[5]); - it = it->next; - } - } - fprintf(stdout, "%d entries found\n", lr.count); - } - /* free list */ - while(lr.head) { - it = lr.head; - lr.head = it->next; - free(it); - } - } - return uc; -} - -int do_set(int ac, char **av) -{ - int uc, rc; - - rc = get_csupve(ac, av, &uc, "*"); - if (rc == 0) - rc = cynagora_enter(cynagora); - if (rc == 0) { - rc = cynagora_set(cynagora, &key, &value); - cynagora_leave(cynagora, !rc); - } - if (rc < 0) - fprintf(stderr, "error %s\n", strerror(-rc)); - return uc; -} - -int do_drop(int ac, char **av) -{ - int uc, rc; - - rc = get_csup(ac, av, &uc, "#"); - if (rc == 0) - rc = cynagora_enter(cynagora); - if (rc == 0) { - rc = cynagora_drop(cynagora, &key); - cynagora_leave(cynagora, !rc); - } - if (rc < 0) - fprintf(stderr, "error %s\n", strerror(-rc)); - return uc; -} - -int do_check(int ac, char **av, int (*f)(cynagora_t*,const cynagora_key_t*)) -{ - int uc, rc; - - rc = get_csup(ac, av, &uc, NULL); - if (rc == 0) { - rc = f(cynagora, &key); - if (rc > 0) - fprintf(stdout, "allowed\n"); - else if (rc == 0) - fprintf(stdout, "denied\n"); - else if (rc == -ENOENT && f == cynagora_cache_check) - fprintf(stdout, "not in cache!\n"); - else if (rc == -EEXIST) - fprintf(stderr, "denied but an entry exist\n"); - else - fprintf(stderr, "error %s\n", strerror(-rc)); - } - return uc; -} - -void acheck_cb(void *closure, int status) -{ - if (status > 0) - fprintf(stdout, "allowed\n"); - else if (status == 0) - fprintf(stdout, "denied\n"); - else - fprintf(stderr, "error %s\n", strerror(-status)); - pending--; -} - -int do_acheck(int ac, char **av, bool simple) -{ - int uc, rc; - - rc = get_csup(ac, av, &uc, NULL); - if (rc == 0) { - pending++; - rc = cynagora_async_check(cynagora, &key, simple, acheck_cb, NULL); - if (rc < 0) { - fprintf(stderr, "error %s\n", strerror(-rc)); - pending--; - } - } - return uc; -} - -int do_log(int ac, char **av) -{ - int uc, rc; - int on = 0, off = 0; - int n = plink(ac, av, &uc, 2); - - if (n > 1) { - on = !strcmp(av[1], "on"); - off = !strcmp(av[1], "off"); - if (!on && !off) { - fprintf(stderr, "bad argument '%s'\n", av[1]); - return uc; - } - } - rc = cynagora_log(cynagora, on, off); - if (rc < 0) - fprintf(stderr, "error %s\n", strerror(-rc)); - else - fprintf(stdout, "logging %s\n", rc ? "on" : "off"); - return uc; -} - -int do_help(int ac, char **av) -{ - if (ac > 1 && !strcmp(av[1], "list")) - fprintf(stdout, "%s", help_list_text); - else if (ac > 1 && !strcmp(av[1], "set")) - fprintf(stdout, "%s", help_set_text); - else if (ac > 1 && !strcmp(av[1], "drop")) - fprintf(stdout, "%s", help_drop_text); - else if (ac > 1 && !strcmp(av[1], "check")) - fprintf(stdout, "%s", help_check_text); - else if (ac > 1 && !strcmp(av[1], "acheck")) - fprintf(stdout, "%s", help_acheck_text); - else if (ac > 1 && !strcmp(av[1], "test")) - fprintf(stdout, "%s", help_test_text); - else if (ac > 1 && !strcmp(av[1], "atest")) - fprintf(stdout, "%s", help_atest_text); - else if (ac > 1 && !strcmp(av[1], "cache")) - fprintf(stdout, "%s", help_cache_text); - else if (ac > 1 && !strcmp(av[1], "clear")) - fprintf(stdout, "%s", help_clear_text); - else if (ac > 1 && !strcmp(av[1], "log")) - fprintf(stdout, "%s", help_log_text); - else if (ac > 1 && !strcmp(av[1], "quit")) - fprintf(stdout, "%s", help_quit_text); - else if (ac > 1 && !strcmp(av[1], "help")) - fprintf(stdout, "%s", help_help_text); - else if (ac > 1 && !strcmp(av[1], "expiration")) - fprintf(stdout, "%s", help_expiration_text); - else { - fprintf(stdout, "%s", help__text); - return 1; - } - return 2; -} - -int do_any(int ac, char **av) -{ - if (!ac) - return 0; - - if (!strcmp(av[0], "list")) - return do_list(ac, av); - - if (!strcmp(av[0], "set")) - return do_set(ac, av); - - if (!strcmp(av[0], "drop")) - return do_drop(ac, av); - - if (!strcmp(av[0], "check")) - return do_check(ac, av, cynagora_check); - - if (!strcmp(av[0], "acheck")) - return do_acheck(ac, av, 0); - - if (!strcmp(av[0], "test")) - return do_check(ac, av, cynagora_test); - - if (!strcmp(av[0], "atest")) - return do_acheck(ac, av, 1); - - if (!strcmp(av[0], "cache")) - return do_check(ac, av, cynagora_cache_check); - - if (!strcmp(av[0], "log")) - return do_log(ac, av); - - if (!strcmp(av[0], "clear")) { - cynagora_cache_clear(cynagora); - return 1; - } - - if (!strcmp(av[0], "quit")) - return 0; - - if (!strcmp(av[0], "help") || !strcmp(av[0], "?")) - return do_help(ac, av); - - fprintf(stderr, "unknown command %s\n", av[0]); - return 0; -} - -void do_all(int ac, char **av) -{ - int rc; - - if (echo) { - for (rc = 0 ; rc < ac ; rc++) - fprintf(stdout, "%s%s", rc ? " " : "", av[rc]); - fprintf(stdout, "\n"); - } - while(ac) { - rc = do_any(ac, av); - if (rc <= 0) - exit(1); - ac -= rc; - av += rc; - } -} - -int async_ctl(void *closure, int op, int fd, uint32_t events) -{ - int *pfd = closure; - - switch(op) { - case EPOLL_CTL_ADD: - case EPOLL_CTL_MOD: - *pfd = fd; - break; - case EPOLL_CTL_DEL: - *pfd = -1; - break; - } - return 0; -} - -int main(int ac, char **av) -{ - int opt; - int rc; - int help = 0; - int version = 0; - int error = 0; - uint32_t cachesize = DEFAULT_CACHE_SIZE; - unsigned long ul; - char *socket = NULL; - char *cache = NULL; - struct pollfd fds[2]; - char *p; - - setlinebuf(stdout); - - /* scan arguments */ - for (;;) { - opt = getopt_long(ac, av, shortopts, longopts, NULL); - if (opt == -1) - break; - - switch(opt) { - case _CACHE_: - cache = optarg; - ul = strtoul(optarg, &cache, 0); - if (*cache || ul > UINT32_MAX) - error = 1; - else - cachesize = (uint32_t)ul; - break; - case _ECHO_: - echo = 1; - break; - case _HELP_: - help = 1; - break; - case _SOCKET_: - socket = optarg; - break; - case _VERSION_: - version = 1; - break; - default: - error = 1; - break; - } - } - - /* handles help, version, error */ - if (help) { - fprintf(stdout, helptxt, DEFAULT_CACHE_SIZE); - return 0; - } - if (version) { - fprintf(stdout, versiontxt); - return 0; - } - if (error) - return 1; - - /* initialize server */ - signal(SIGPIPE, SIG_IGN); /* avoid SIGPIPE! */ - rc = cynagora_create(&cynagora, cynagora_Admin, cachesize, socket); - if (rc < 0) { - fprintf(stderr, "initialization failed: %s\n", strerror(-rc)); - return 1; - } - - fds[1].fd = -1; - rc = cynagora_async_setup(cynagora, async_ctl, &fds[1].fd); - if (rc < 0) { - fprintf(stderr, "asynchronous setup failed: %s\n", strerror(-rc)); - return 1; - } - - if (optind < ac) { - do_all(ac - optind, av + optind); - return 0; - } - - fcntl(0, F_SETFL, O_NONBLOCK); - bufill = 0; - fds[0].fd = 0; - fds[0].events = fds[1].events = POLLIN; - for(;;) { - rc = poll(fds, 2, -1); - if (fds[0].revents & POLLIN) { - rc = (int)read(0, buffer, sizeof buffer - bufill); - if (rc == 0) - break; - if (rc > 0) { - bufill += (size_t)rc; - while((p = memchr(buffer, '\n', bufill))) { - /* process one line */ - *p++ = 0; - str[nstr = 0] = strtok(buffer, " \t"); - while(str[nstr]) - str[++nstr] = strtok(NULL, " \t"); - do_all(nstr, str); - bufill -= (size_t)(p - buffer); - if (!bufill) - break; - memmove(buffer, p, bufill); - } - } - } - if (fds[0].revents & POLLHUP) { - if (!pending) - break; - fds[0].fd = -1; - } - if (fds[1].revents & POLLIN) { - rc = cynagora_async_process(cynagora); - if (rc < 0) - fprintf(stderr, "asynchronous processing failed: %s\n", strerror(-rc)); - if (fds[0].fd < 0 && !pending) - break; - } - if (fds[1].revents & POLLHUP) { - if (fds[0].fd < 0) - break; - fds[1].fd = -1; - } - } - return 0; -} -- cgit 1.2.3-korg