changeset 967:8becffd7617a

irccd: initial import of configuration file
author David Demelier <markand@malikania.fr>
date Tue, 02 Feb 2021 13:51:39 +0100
parents 8172399babb7
children 5ded2b4994af
files CMakeLists.txt irccd/CMakeLists.txt irccd/conf.y irccd/lex.l irccd/main.c lib/irccd/config.h.in lib/irccd/irccd.c lib/irccd/irccd.h lib/irccd/log.c lib/irccd/log.h
diffstat 10 files changed, 780 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- 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 ()
--- 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 $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>)
+target_include_directories(
+	irccd-fe
+	PUBLIC
+		$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
+		$<BUILD_INTERFACE:${irccd_SOURCE_DIR}>
+)
 
 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})
--- /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 <markand@malikania.fr>
+ *
+ * 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 <err.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <irccd/irccd.h>
+#include <irccd/log.h>
+#include <irccd/rule.h>
+#include <irccd/server.h>
+#include <irccd/util.h>
+
+#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 <ival> T_NUMBER T_LOG_VERBOSITY T_RULE_ACTION
+%token <sval> 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 <ival> log_verbosity
+%type <sval> plugin_location
+%type <plugin> plugin_params plugin_params_opt
+%type <rule> rule_params rule_params_opt
+%type <server> server_params
+
+%type <pair> pair
+%type <pair_list> pair_list plugin_templates plugin_config plugin_paths
+
+%type <string_list> string_list
+%type <string> 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);
+}
--- /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 <markand@malikania.fr>
+ *
+ * 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 <irccd/limits.h>
+#include <irccd/rule.h>
+
+#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;
+                        }
+
+%%
--- 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 <config.h>
+#include <compat.h>
+
+#include <err.h>
 #include <poll.h>
 #include <stdio.h>
-#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
 
 #include <irccd/irccd.h>
 #include <irccd/log.h>
@@ -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();
 }
--- 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
--- 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 *
--- 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 *
--- 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;
--- 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);