# HG changeset patch # User David Demelier # Date 1612270299 -3600 # Node ID 8becffd7617a3d8ff74387f5ff74a6c5cf534c97 # Parent 8172399babb7d9b86dc8d73e6aab17da829ceb64 irccd: initial import of configuration file diff -r 8172399babb7 -r 8becffd7617a CMakeLists.txt --- a/CMakeLists.txt Sun Jan 31 22:01:37 2021 +0100 +++ b/CMakeLists.txt Tue Feb 02 13:51:39 2021 +0100 @@ -37,6 +37,9 @@ include(GNUInstallDirs) +find_package(FLEX REQUIRED) +find_package(BISON REQUIRED) + if (IRCCD_WITH_SSL) find_package(OpenSSL REQUIRED) endif () diff -r 8172399babb7 -r 8becffd7617a irccd/CMakeLists.txt --- a/irccd/CMakeLists.txt Sun Jan 31 22:01:37 2021 +0100 +++ b/irccd/CMakeLists.txt Tue Feb 02 13:51:39 2021 +0100 @@ -63,13 +63,29 @@ # This is required for unit tests. add_library(irccd-fe OBJECT ${SOURCES}) target_link_libraries(irccd-fe libirccd) -target_include_directories(irccd-fe PUBLIC $) +target_include_directories( + irccd-fe + PUBLIC + $ + $ +) if (IRCCD_WITH_JS) target_link_libraries(irccd-fe libirccd-duktape) endif () -add_executable(irccd ${irccd_SOURCE_DIR}/main.c) +flex_target(irccd-lex ${irccd_SOURCE_DIR}/lex.l ${irccd_BINARY_DIR}/lex.c) +bison_target(irccd-conf ${irccd_SOURCE_DIR}/conf.y ${irccd_BINARY_DIR}/conf.c) +add_flex_bison_dependency(irccd-lex irccd-conf) + +add_executable( + irccd + ${irccd_SOURCE_DIR}/main.c + ${irccd_SOURCE_DIR}/lex.l + ${irccd_SOURCE_DIR}/conf.y + ${BISON_irccd-conf_OUTPUTS} + ${FLEX_irccd-lex_OUTPUTS} +) target_link_libraries(irccd irccd-fe) source_group(TREE ${irccd_SOURCE_DIR} FILES ${SOURCES}) diff -r 8172399babb7 -r 8becffd7617a irccd/conf.y --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/irccd/conf.y Tue Feb 02 13:51:39 2021 +0100 @@ -0,0 +1,561 @@ +/* + * conf.y -- config parser + * + * Copyright (c) 2013-2021 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "transport.h" + +extern FILE *yyin; + +struct pair { + char *key; + char *value; + SLIST_ENTRY(pair) link; +}; + +SLIST_HEAD(pair_list, pair); + +struct string { + char *value; + SLIST_ENTRY(string) link; +}; + +SLIST_HEAD(string_list, string); + +struct rule_params { + struct string_list *servers; + struct string_list *channels; + struct string_list *origins; + struct string_list *plugins; + struct string_list *events; +}; + +struct server_params { + char *hostname; + int port; + int flags; + char *nickname; + char *username; + char *realname; + struct string_list *channels; +}; + +struct plugin_params { + char *location; + struct pair_list *config; + struct pair_list *paths; + struct pair_list *templates; +}; + +static void +pair_list_finish(struct pair_list *list) +{ + struct pair *pair, *tmp; + + if (!list) + return; + + SLIST_FOREACH_SAFE(pair, list, link, tmp) { + free(pair->key); + free(pair->value); + free(pair); + } + + free(list); +} + +static void +string_list_finish(struct string_list *list) +{ + struct string *string, *tmp; + + if (!list) + return; + + SLIST_FOREACH_SAFE(string, list, link, tmp) { + free(string->value); + free(string); + } + + free(list); +} + +int +yylex(void); + +void +yyerror(const char *); + +%} + +%union { + int ival; + char *sval; + + struct pair *pair; + struct pair_list *pair_list; + + struct string *string; + struct string_list *string_list; + + struct server_params *server; + struct plugin_params *plugin; + struct rule_params *rule; +}; + +%token T_NUMBER T_LOG_VERBOSITY T_RULE_ACTION +%token T_STRING T_LOG_TYPE + +%token T_BRACE_CLOSE +%token T_BRACE_OPEN +%token T_CHANNELS +%token T_COMMA +%token T_CONFIG +%token T_EVENTS +%token T_HOSTNAME +%token T_IDENT +%token T_LOCATION +%token T_LOGS +%token T_OPTIONS +%token T_ORIGINS +%token T_PATHS +%token T_PLUGIN +%token T_PLUGINS +%token T_PORT +%token T_RULE +%token T_SEMICOLON +%token T_SERVER +%token T_SERVERS +%token T_SSL +%token T_TEMPLATES +%token T_TO +%token T_TRANSPORT + +%type log_verbosity +%type plugin_location +%type plugin_params plugin_params_opt +%type rule_params rule_params_opt +%type server_params + +%type pair +%type pair_list plugin_templates plugin_config plugin_paths + +%type string_list +%type string + +%% + +config_list + : config + | config_list config + ; + +config + : server + | logs + | transport + | rule + | plugin + ; + +log_verbosity + : T_LOG_VERBOSITY + { + $$ = $1; + } + | + { + $$ = 0; + } + ; + +logs + : T_LOGS log_verbosity + { + irc_log_set_verbose($2); + } + | T_LOGS log_verbosity T_TO T_LOG_TYPE + { + irc_log_set_verbose($2); + + if (strcmp($4, "console") == 0) + irc_log_to_console(); + else if (strcmp($4, "syslog") == 0) + irc_log_to_syslog(); + else + errx(1, "missing log file path"); + + free($4); + } + | T_LOGS log_verbosity T_TO T_LOG_TYPE T_STRING + { + irc_log_to_file($4); + free($4); + } + ; + +transport + : T_TRANSPORT T_TO T_STRING + { + transport_bind($3); + free($3); + } + ; + +string + : T_STRING + { + $$ = irc_util_calloc(1, sizeof (*$$)); + $$->value = $1; + } + +string_list + : string + { + $$ = irc_util_calloc(1, sizeof (*$$)); + SLIST_INIT($$); + SLIST_INSERT_HEAD($$, $1, link); + } + | string T_COMMA string_list + { + $$ = $3; + SLIST_INSERT_HEAD($$, $1, link); + } + ; + +pair + : T_STRING T_STRING + { + $$ = irc_util_calloc(1, sizeof (*$$)); + $$->key = $1; + $$->value = $2; + } + ; + +pair_list + : pair T_SEMICOLON + { + $$ = irc_util_calloc(1, sizeof (*$$)); + SLIST_INIT($$); + SLIST_INSERT_HEAD($$, $1, link); + } + | pair T_SEMICOLON pair_list + { + $$ = $3; + SLIST_INSERT_HEAD($$, $1, link); + } + ; + +rule_params + : T_SERVERS string_list T_SEMICOLON rule_params + { + $$ = $4; + $$->servers = $2; + } + | T_CHANNELS string_list T_SEMICOLON rule_params + { + $$ = $4; + $$->channels = $2; + } + | T_ORIGINS string_list T_SEMICOLON rule_params + { + $$ = $4; + $$->origins = $2; + } + | T_EVENTS string_list T_SEMICOLON rule_params + { + $$ = $4; + $$->events = $2; + } + | T_PLUGINS string_list T_SEMICOLON rule_params + { + $$ = $4; + $$->plugins = $2; + } + | + { + $$ = irc_util_calloc(1, sizeof (*$$)); + } + ; + +rule_params_opt + : T_BRACE_OPEN rule_params T_BRACE_CLOSE + { + $$ = $2; + } + | + { + $$ = NULL; + } + ; + +rule + : T_RULE T_RULE_ACTION rule_params_opt + { + struct irc_rule *rule; + struct string *string; + + rule = irc_rule_new($2); + + if ($3) { + if ($3->servers) + SLIST_FOREACH(string, $3->servers, link) + irc_rule_add(rule->servers, string->value); + if ($3->channels) + SLIST_FOREACH(string, $3->channels, link) + irc_rule_add(rule->channels, string->value); + if ($3->origins) + SLIST_FOREACH(string, $3->origins, link) + irc_rule_add(rule->origins, string->value); + if ($3->plugins) + SLIST_FOREACH(string, $3->plugins, link) + irc_rule_add(rule->plugins, string->value); + if ($3->events) + SLIST_FOREACH(string, $3->events, link) + irc_rule_add(rule->events, string->value); + + string_list_finish($3->servers); + string_list_finish($3->channels); + string_list_finish($3->origins); + string_list_finish($3->plugins); + string_list_finish($3->events); + } + + free($3); + } + ; + +server_params + : T_HOSTNAME T_STRING T_SEMICOLON server_params + { + $$ = $4; + $$->hostname = $2; + } + | T_PORT T_NUMBER T_SEMICOLON server_params + { + $$ = $4; + $$->port = $2; + } + | T_IDENT T_STRING T_STRING T_STRING T_SEMICOLON server_params + { + $$ = $6; + $$->nickname = $2; + $$->username = $3; + $$->realname = $4; + } + | T_CHANNELS string_list T_SEMICOLON server_params + { + $$ = $4; + $$->channels = $2; + } + | T_SSL T_SEMICOLON server_params + { + $$ = $3; + $$->flags |= IRC_SERVER_FLAGS_SSL; + } + | T_OPTIONS string_list T_SEMICOLON server_params + { + struct string *s; + + $$ = $4; + + SLIST_FOREACH(s, $2, link) { + if (strcmp(s->value, "AUTO-REJOIN") == 0) + $$->flags |= IRC_SERVER_FLAGS_AUTO_REJOIN; + else if (strcmp(s->value, "JOIN-INVITE") == 0) + $$->flags |= IRC_SERVER_FLAGS_JOIN_INVITE; + else + errx(1, "invalid server option: %s", s->value); + } + + string_list_finish($2); + } + | + { + $$ = irc_util_calloc(1, sizeof (*$$)); + } + ; + +server + : T_SERVER T_STRING T_BRACE_OPEN server_params T_BRACE_CLOSE + { + struct irc_server *s; + struct string *str; + char *at; + + if (!$4->hostname) + errx(1, "missing server hostname"); + if (!$4->nickname) + errx(1, "missing server nickname"); + if (!$4->username) + errx(1, "missing server username"); + if (!$4->realname) + errx(1, "missing server realname"); + + s = irc_server_new($2, $4->nickname, $4->username, $4->realname, + $4->hostname, $4->port); + + if ($4->channels) { + SLIST_FOREACH(str, $4->channels, link) { + if ((at = strchr(str->value, '@'))) { + *at = 0; + irc_server_join(s, at + 1, str->value); + } else + irc_server_join(s, str->value, NULL); + } + + string_list_finish($4->channels); + } + + irc_bot_server_add(s); + + free($2); + free($4->hostname); + free($4->nickname); + free($4->username); + free($4->realname); + free($4); + } + ; + +plugin_location + : T_LOCATION T_STRING T_SEMICOLON + { + $$ = $2; + } + ; + +plugin_templates + : T_TEMPLATES T_BRACE_OPEN pair_list T_BRACE_CLOSE + { + $$ = $3; + } + ; + +plugin_paths + : T_PATHS T_BRACE_OPEN pair_list T_BRACE_CLOSE + { + $$ = $3; + } + ; + +plugin_config + : T_CONFIG T_BRACE_OPEN pair_list T_BRACE_CLOSE + { + $$ = $3; + } + ; + +plugin_params + : plugin_location plugin_params + { + $$ = $2; + $$->location = $1; + } + | plugin_templates plugin_params + { + $$ = $2; + $$->templates = $1; + } + | plugin_paths plugin_params + { + $$ = $2; + $$->paths = $1; + } + | plugin_config plugin_params + { + $$ = $2; + $$->config = $1; + } + | + { + $$ = irc_util_calloc(1, sizeof (*$$)); + } + ; + +plugin_params_opt + : T_BRACE_OPEN plugin_params T_BRACE_CLOSE + { + $$ = $2; + } + | + { + $$ = NULL; + } + ; + +plugin + : T_PLUGIN T_STRING plugin_params_opt + { + struct irc_plugin *p; + struct pair *kv; + + if (!(p = irc_bot_plugin_find($2))) + goto cleanup; + + if ($3) { + SLIST_FOREACH(kv, $3->templates, link) + irc_plugin_set_template(p, kv->key, kv->value); + SLIST_FOREACH(kv, $3->config, link) + irc_plugin_set_option(p, kv->key, kv->value); + SLIST_FOREACH(kv, $3->paths, link) + irc_plugin_set_path(p, kv->key, kv->value); + } + + irc_bot_plugin_add(p); + + cleanup: + if ($3) { + free($3->location); + pair_list_finish($3->templates); + pair_list_finish($3->config); + pair_list_finish($3->paths); + } + + free($2); + free($3); + } + ; + +%% + +void +yyerror(const char *err) +{ + errx(1, "%s", err); +} + +void +config_open(const char *path) +{ + if (!(yyin = fopen(path, "r"))) + err(1, "%s", path); + + yyparse(); + fclose(yyin); +} diff -r 8172399babb7 -r 8becffd7617a irccd/lex.l --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/irccd/lex.l Tue Feb 02 13:51:39 2021 +0100 @@ -0,0 +1,136 @@ +/* + * lex.l -- config lexer + * + * Copyright (c) 2013-2021 David Demelier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%option noyywrap +%option nounput +%option noinput + +%{ + +#include +#include + +#include "conf.h" + +%} + +channels channels +config config +events events +hostname hostname +ident ident +location location +log_type console|syslog|file +log_verbosity quiet|verbose +logs logs +options options +origins origins +paths paths +plugin plugin +plugins plugins +port port +rule rule +rule_action accept|drop +server server +servers servers +ssl ssl +templates templates +to to +transport transport + +brace_open \{ +brace_close \} +comma , +semicolon ; + +ws [ \t\n]+ +comment #.*$ +number [0-9]+ +word [A-Za-z_][A-Za-z0-9_-]+ +string \"([^\\\"]|\\.)*\" + +%% + +{ws} ; +{comment} ; + +{brace_close} return T_BRACE_CLOSE; +{brace_open} return T_BRACE_OPEN; +{channels} return T_CHANNELS; +{comma} return T_COMMA; +{config} return T_CONFIG; +{events} return T_EVENTS; +{hostname} return T_HOSTNAME; +{ident} return T_IDENT; +{location} return T_LOCATION; +{logs} return T_LOGS; +{options} return T_OPTIONS; +{origins} return T_ORIGINS; +{paths} return T_PATHS; +{plugins} return T_PLUGINS; +{plugin} return T_PLUGIN; +{port} return T_PORT; +{semicolon} return T_SEMICOLON; +{servers} return T_SERVERS; +{server} return T_SERVER; +{ssl} return T_SSL; +{templates} return T_TEMPLATES; +{to} return T_TO; + +{log_verbosity} { + if (strcmp(yytext, "quiet") == 0) + yylval.ival = 0; + else + yylval.ival = 1; + + return T_LOG_VERBOSITY; + } +{log_type} { + yylval.sval = strdup(yytext); + return T_LOG_TYPE; + } + +{transport} return T_TRANSPORT; + +{rule} return T_RULE; +{rule_action} { + if (strcmp(yytext, "accept") == 0) + yylval.ival = IRC_RULE_ACCEPT; + else + yylval.ival = IRC_RULE_DROP; + + return T_RULE_ACTION; + } + +{word} { + yylval.sval = strdup(yytext); + return T_STRING; + } + +{string} { + yylval.sval = strdup(yytext + 1); + yylval.sval[yyleng - 2] = '\0'; + return T_STRING; + } + +{number} { + yylval.ival = atoi(yytext); + return T_NUMBER; + } + +%% diff -r 8172399babb7 -r 8becffd7617a irccd/main.c --- a/irccd/main.c Sun Jan 31 22:01:37 2021 +0100 +++ b/irccd/main.c Tue Feb 02 13:51:39 2021 +0100 @@ -16,9 +16,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include + +#include #include #include -#include +#include +#include #include #include @@ -32,10 +37,15 @@ #include "js-plugin.h" #include "peer.h" +static const char *config = IRCCD_SYSCONFDIR "/irccd.conf"; static struct peers peers; static int running = 1; static int has_transport; +/* conf.y */ +void +config_open(const char *); + static void broadcast(const struct irc_event *ev) { @@ -80,6 +90,11 @@ init(void) { irc_bot_init(); + irc_bot_plugin_loader_add(dl_plugin_loader_new()); + +#if defined(IRCCD_WITH_JS) + irc_bot_plugin_loader_add(js_plugin_loader_new()); +#endif } static void @@ -114,9 +129,16 @@ } } +static inline void +load(void) +{ + config_open(config); +} + static void loop(void) { +#if 0 struct irc_event ev; struct pollfd *fds; size_t botcount, owncount; @@ -133,6 +155,8 @@ irc_bot_prepare(fds); prepare(&fds[botcount]); + if (poll(fds, + irc_bot_flush(fds); flush(&fds[botcount]); @@ -141,6 +165,7 @@ free(fds); } +#endif } static inline void @@ -154,16 +179,40 @@ transport_finish(); } +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-c config]\n", getprogname()); + exit(1); +} + int main(int argc, char **argv) { - --argc; - ++argv; + int ch; + + setprogname(argv[0]); + + while ((ch = getopt(argc, argv, "c:")) != -1) { + switch (ch) { + case 'c': + config = optarg; + break; + default: + usage(); + break; + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; if (argc > 0) return run(argc, argv); init(); + load(); loop(); finish(); } diff -r 8172399babb7 -r 8becffd7617a lib/irccd/config.h.in --- a/lib/irccd/config.h.in Sun Jan 31 22:01:37 2021 +0100 +++ b/lib/irccd/config.h.in Tue Feb 02 13:51:39 2021 +0100 @@ -24,6 +24,7 @@ #define IRCCD_VERSION_PATCH @IRCCD_VERSION_PATCH@ #define IRCCD_VERSION "@IRCCD_VERSION_MAJOR@.@IRCCD_VERSION_MINOR@.@IRCCD_VERSION_PATCH@" +#define IRCCD_SYSCONFDIR "@CMAKE_INSTALL_FULL_SYSCONFDIR@" #define IRCCD_LIBDIR "@CMAKE_INSTALL_FULL_LIBDIR@" #cmakedefine IRCCD_WITH_JS diff -r 8172399babb7 -r 8becffd7617a lib/irccd/irccd.c --- a/lib/irccd/irccd.c Sun Jan 31 22:01:37 2021 +0100 +++ b/lib/irccd/irccd.c Tue Feb 02 13:51:39 2021 +0100 @@ -338,7 +338,7 @@ return NULL; } -void +struct irc_plugin * irc_bot_plugin_find(const char *name) { char buf[IRC_PATHS_LEN], *t, *token; @@ -361,10 +361,10 @@ } } - if (p) - irc_bot_plugin_add(p); - else + if (!p) irc_log_warn("irccd: could not find plugin %s", name); + + return p; } struct irc_plugin * diff -r 8172399babb7 -r 8becffd7617a lib/irccd/irccd.h --- a/lib/irccd/irccd.h Sun Jan 31 22:01:37 2021 +0100 +++ b/lib/irccd/irccd.h Tue Feb 02 13:51:39 2021 +0100 @@ -50,7 +50,7 @@ void irc_bot_plugin_add(struct irc_plugin *); -void +struct irc_plugin * irc_bot_plugin_find(const char *); struct irc_plugin * diff -r 8172399babb7 -r 8becffd7617a lib/irccd/log.c --- a/lib/irccd/log.c Sun Jan 31 22:01:37 2021 +0100 +++ b/lib/irccd/log.c Tue Feb 02 13:51:39 2021 +0100 @@ -106,14 +106,12 @@ } void -irc_log_to_files(const char *stdout, const char *stderr) +irc_log_to_file(const char *file) { irc_log_finish(); - if (!(out = fopen(stdout, "a"))) - irc_log_warn("%s: %s", stdout, strerror(errno)); - if (!(err = fopen(stderr, "a"))) - irc_log_warn("%s: %s", stdout, strerror(errno)); + if (!(out = err = fopen(file, "a"))) + irc_log_warn("%s: %s", file, strerror(errno)); handler = handler_files; finalizer = finalizer_files; diff -r 8172399babb7 -r 8becffd7617a lib/irccd/log.h --- a/lib/irccd/log.h Sun Jan 31 22:01:37 2021 +0100 +++ b/lib/irccd/log.h Tue Feb 02 13:51:39 2021 +0100 @@ -28,7 +28,7 @@ irc_log_to_console(void); void -irc_log_to_files(const char *, const char *); +irc_log_to_file(const char *); void irc_log_to_null(void);