Experimental IRC client, daemon and bot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1770 lines
48KB

  1. /*
  2. * zyklonb.c: the experimental IRC bot
  3. *
  4. * Copyright (c) 2014, Přemysl Janouch <p.janouch@gmail.com>
  5. * All rights reserved.
  6. *
  7. * Permission to use, copy, modify, and/or distribute this software for any
  8. * purpose with or without fee is hereby granted, provided that the above
  9. * copyright notice and this permission notice appear in all copies.
  10. *
  11. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  12. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  14. * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  16. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  17. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18. *
  19. */
  20. #define PROGRAM_NAME "ZyklonB"
  21. #define PROGRAM_VERSION "alpha"
  22. #include "common.c"
  23. // --- Configuration (application-specific) ------------------------------------
  24. static struct config_item g_config_table[] =
  25. {
  26. { "nickname", "ZyklonB", "IRC nickname" },
  27. { "username", "bot", "IRC user name" },
  28. { "realname", "ZyklonB IRC bot", "IRC real name/e-mail" },
  29. { "irc_host", NULL, "Address of the IRC server" },
  30. { "irc_port", "6667", "Port of the IRC server" },
  31. { "ssl_use", "off", "Whether to use SSL" },
  32. { "ssl_cert", NULL, "Client SSL certificate (PEM)" },
  33. { "autojoin", NULL, "Channels to join on start" },
  34. { "reconnect", "on", "Whether to reconnect on error" },
  35. { "reconnect_delay", "5", "Time between reconnecting" },
  36. { "prefix", ":", "The prefix for bot commands" },
  37. { "admin", NULL, "Host mask for administrators" },
  38. { "plugins", NULL, "The plugins to load on startup" },
  39. { "plugin_dir", NULL, "Where to search for plugins" },
  40. { "recover", "on", "Whether to re-launch on crash" },
  41. { NULL, NULL, NULL }
  42. };
  43. // --- Application data --------------------------------------------------------
  44. struct plugin_data
  45. {
  46. LIST_HEADER (plugin_data)
  47. struct bot_context *ctx; ///< Parent context
  48. char *name; ///< Plugin identifier
  49. pid_t pid; ///< PID of the plugin process
  50. bool is_zombie; ///< Whether the child is a zombie
  51. bool initialized; ///< Ready to exchange IRC messages
  52. struct str queued_output; ///< Output queued up until initialized
  53. // Since we're doing non-blocking I/O, we need to queue up data so that
  54. // we don't stall on plugins unnecessarily.
  55. int read_fd; ///< The read end of the comm. pipe
  56. int write_fd; ///< The write end of the comm. pipe
  57. struct str read_buffer; ///< Unprocessed input
  58. struct str write_buffer; ///< Output yet to be sent out
  59. };
  60. static void
  61. plugin_data_init (struct plugin_data *self)
  62. {
  63. memset (self, 0, sizeof *self);
  64. self->pid = -1;
  65. str_init (&self->queued_output);
  66. self->read_fd = -1;
  67. str_init (&self->read_buffer);
  68. self->write_fd = -1;
  69. str_init (&self->write_buffer);
  70. }
  71. static void
  72. plugin_data_free (struct plugin_data *self)
  73. {
  74. soft_assert (self->pid == -1);
  75. free (self->name);
  76. str_free (&self->read_buffer);
  77. if (!soft_assert (self->read_fd == -1))
  78. xclose (self->read_fd);
  79. str_free (&self->write_buffer);
  80. if (!soft_assert (self->write_fd == -1))
  81. xclose (self->write_fd);
  82. if (!self->initialized)
  83. str_free (&self->queued_output);
  84. }
  85. struct bot_context
  86. {
  87. struct str_map config; ///< User configuration
  88. regex_t *admin_re; ///< Regex to match our administrator
  89. int irc_fd; ///< Socket FD of the server
  90. struct str read_buffer; ///< Input yet to be processed
  91. bool irc_ready; ///< Whether we may send messages now
  92. SSL_CTX *ssl_ctx; ///< SSL context
  93. SSL *ssl; ///< SSL connection
  94. struct plugin_data *plugins; ///< Linked list of plugins
  95. struct str_map plugins_by_name; ///< Indexes @em plugins by their name
  96. struct poller poller; ///< Manages polled descriptors
  97. bool quitting; ///< User requested quitting
  98. bool polling; ///< The event loop is running
  99. };
  100. static void
  101. bot_context_init (struct bot_context *self)
  102. {
  103. str_map_init (&self->config);
  104. self->config.free = free;
  105. load_config_defaults (&self->config, g_config_table);
  106. self->admin_re = NULL;
  107. self->irc_fd = -1;
  108. str_init (&self->read_buffer);
  109. self->irc_ready = false;
  110. self->ssl = NULL;
  111. self->ssl_ctx = NULL;
  112. self->plugins = NULL;
  113. str_map_init (&self->plugins_by_name);
  114. poller_init (&self->poller);
  115. self->quitting = false;
  116. self->polling = false;
  117. }
  118. static void
  119. bot_context_free (struct bot_context *self)
  120. {
  121. str_map_free (&self->config);
  122. if (self->admin_re)
  123. regex_free (self->admin_re);
  124. str_free (&self->read_buffer);
  125. // TODO: terminate the plugins properly before this is called
  126. struct plugin_data *link, *tmp;
  127. for (link = self->plugins; link; link = tmp)
  128. {
  129. tmp = link->next;
  130. plugin_data_free (link);
  131. free (link);
  132. }
  133. if (self->irc_fd != -1)
  134. xclose (self->irc_fd);
  135. if (self->ssl)
  136. SSL_free (self->ssl);
  137. if (self->ssl_ctx)
  138. SSL_CTX_free (self->ssl_ctx);
  139. str_map_free (&self->plugins_by_name);
  140. poller_free (&self->poller);
  141. }
  142. static void
  143. irc_shutdown (struct bot_context *ctx)
  144. {
  145. // Generally non-critical
  146. if (ctx->ssl)
  147. soft_assert (SSL_shutdown (ctx->ssl) != -1);
  148. else
  149. soft_assert (shutdown (ctx->irc_fd, SHUT_WR) == 0);
  150. }
  151. static void
  152. initiate_quit (struct bot_context *ctx)
  153. {
  154. irc_shutdown (ctx);
  155. ctx->quitting = true;
  156. }
  157. static void
  158. try_finish_quit (struct bot_context *ctx)
  159. {
  160. if (!ctx->quitting)
  161. return;
  162. if (ctx->irc_fd == -1 && !ctx->plugins)
  163. ctx->polling = false;
  164. }
  165. static bool irc_send (struct bot_context *ctx,
  166. const char *format, ...) ATTRIBUTE_PRINTF (2, 3);
  167. // XXX: is it okay to just ignore the return value and wait until we receive
  168. // it in on_irc_readable()?
  169. static bool
  170. irc_send (struct bot_context *ctx, const char *format, ...)
  171. {
  172. va_list ap;
  173. if (g_debug_mode)
  174. {
  175. fputs ("[IRC] <== \"", stderr);
  176. va_start (ap, format);
  177. vfprintf (stderr, format, ap);
  178. va_end (ap);
  179. fputs ("\"\n", stderr);
  180. }
  181. if (!soft_assert (ctx->irc_fd != -1))
  182. return false;
  183. va_start (ap, format);
  184. struct str str;
  185. str_init (&str);
  186. str_append_vprintf (&str, format, ap);
  187. str_append (&str, "\r\n");
  188. va_end (ap);
  189. bool result = true;
  190. if (ctx->ssl)
  191. {
  192. // TODO: call SSL_get_error() to detect if a clean shutdown has occured
  193. if (SSL_write (ctx->ssl, str.str, str.len) != (int) str.len)
  194. {
  195. print_debug ("%s: %s: %s", __func__, "SSL_write",
  196. ERR_error_string (ERR_get_error (), NULL));
  197. result = false;
  198. }
  199. }
  200. else if (write (ctx->irc_fd, str.str, str.len) != (ssize_t) str.len)
  201. {
  202. print_debug ("%s: %s: %s", __func__, "write", strerror (errno));
  203. result = false;
  204. }
  205. str_free (&str);
  206. return result;
  207. }
  208. static bool
  209. irc_initialize_ssl (struct bot_context *ctx, struct error **e)
  210. {
  211. ctx->ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
  212. if (!ctx->ssl_ctx)
  213. goto error_ssl_1;
  214. // We don't care; some encryption is always better than no encryption
  215. SSL_CTX_set_verify (ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
  216. // XXX: maybe we should call SSL_CTX_set_options() for some workarounds
  217. ctx->ssl = SSL_new (ctx->ssl_ctx);
  218. if (!ctx->ssl)
  219. goto error_ssl_2;
  220. const char *ssl_cert = str_map_find (&ctx->config, "ssl_cert");
  221. if (ssl_cert)
  222. {
  223. char *path = resolve_config_filename (ssl_cert);
  224. if (!path)
  225. print_error ("%s: %s", "cannot open file", ssl_cert);
  226. // XXX: perhaps we should read the file ourselves for better messages
  227. else if (!SSL_use_certificate_file (ctx->ssl, path, SSL_FILETYPE_PEM))
  228. print_error ("%s: %s", "setting the SSL client certificate failed",
  229. ERR_error_string (ERR_get_error (), NULL));
  230. free (path);
  231. }
  232. SSL_set_connect_state (ctx->ssl);
  233. if (!SSL_set_fd (ctx->ssl, ctx->irc_fd))
  234. goto error_ssl_3;
  235. // Avoid SSL_write() returning SSL_ERROR_WANT_READ
  236. SSL_set_mode (ctx->ssl, SSL_MODE_AUTO_RETRY);
  237. if (SSL_connect (ctx->ssl) > 0)
  238. return true;
  239. error_ssl_3:
  240. SSL_free (ctx->ssl);
  241. ctx->ssl = NULL;
  242. error_ssl_2:
  243. SSL_CTX_free (ctx->ssl_ctx);
  244. ctx->ssl_ctx = NULL;
  245. error_ssl_1:
  246. // XXX: these error strings are really nasty; also there could be
  247. // multiple errors on the OpenSSL stack.
  248. error_set (e, "%s: %s", "could not initialize SSL",
  249. ERR_error_string (ERR_get_error (), NULL));
  250. return false;
  251. }
  252. static bool
  253. irc_establish_connection (struct bot_context *ctx,
  254. const char *host, const char *port, bool use_ssl, struct error **e)
  255. {
  256. struct addrinfo gai_hints, *gai_result, *gai_iter;
  257. memset (&gai_hints, 0, sizeof gai_hints);
  258. // We definitely want TCP.
  259. gai_hints.ai_socktype = SOCK_STREAM;
  260. int err = getaddrinfo (host, port, &gai_hints, &gai_result);
  261. if (err)
  262. {
  263. error_set (e, "%s: %s: %s",
  264. "connection failed", "getaddrinfo", gai_strerror (err));
  265. return false;
  266. }
  267. int sockfd;
  268. for (gai_iter = gai_result; gai_iter; gai_iter = gai_iter->ai_next)
  269. {
  270. sockfd = socket (gai_iter->ai_family,
  271. gai_iter->ai_socktype, gai_iter->ai_protocol);
  272. if (sockfd == -1)
  273. continue;
  274. set_cloexec (sockfd);
  275. int yes = 1;
  276. soft_assert (setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE,
  277. &yes, sizeof yes) != -1);
  278. const char *real_host = host;
  279. // Let's try to resolve the address back into a real hostname;
  280. // we don't really need this, so we can let it quietly fail
  281. char buf[NI_MAXHOST];
  282. err = getnameinfo (gai_iter->ai_addr, gai_iter->ai_addrlen,
  283. buf, sizeof buf, NULL, 0, 0);
  284. if (err)
  285. print_debug ("%s: %s", "getnameinfo", gai_strerror (err));
  286. else
  287. real_host = buf;
  288. // XXX: we shouldn't mix these statuses with `struct error'; choose 1!
  289. print_status ("connecting to `%s:%s'...", real_host, port);
  290. if (!connect (sockfd, gai_iter->ai_addr, gai_iter->ai_addrlen))
  291. break;
  292. xclose (sockfd);
  293. }
  294. freeaddrinfo (gai_result);
  295. if (!gai_iter)
  296. {
  297. error_set (e, "connection failed");
  298. return false;
  299. }
  300. ctx->irc_fd = sockfd;
  301. if (use_ssl && !irc_initialize_ssl (ctx, e))
  302. {
  303. xclose (ctx->irc_fd);
  304. ctx->irc_fd = -1;
  305. return false;
  306. }
  307. print_status ("connection established");
  308. return true;
  309. }
  310. // --- Signals -----------------------------------------------------------------
  311. static int g_signal_pipe[2]; ///< A pipe used to signal... signals
  312. static struct str_vector
  313. g_original_argv, ///< Original program arguments
  314. g_recovery_env; ///< Environment for re-exec recovery
  315. /// Program termination has been requested by a signal
  316. static volatile sig_atomic_t g_termination_requested;
  317. /// Points to startup reason location within `g_recovery_environment'
  318. static char **g_startup_reason_location;
  319. /// The environment variable used to pass the startup reason when re-executing
  320. static const char g_startup_reason_str[] = "STARTUP_REASON";
  321. static void
  322. sigchld_handler (int signum)
  323. {
  324. (void) signum;
  325. int original_errno = errno;
  326. // Just so that the read end of the pipe wakes up the poller.
  327. // NOTE: Linux has signalfd() and eventfd(), and the BSD's have kqueue.
  328. // All of them are better than this approach, although platform-specific.
  329. if (write (g_signal_pipe[1], "c", 1) == -1)
  330. soft_assert (errno == EAGAIN);
  331. errno = original_errno;
  332. }
  333. static void
  334. sigterm_handler (int signum)
  335. {
  336. (void) signum;
  337. g_termination_requested = true;
  338. int original_errno = errno;
  339. if (write (g_signal_pipe[1], "t", 1) == -1)
  340. soft_assert (errno == EAGAIN);
  341. errno = original_errno;
  342. }
  343. static void
  344. setup_signal_handlers (void)
  345. {
  346. if (pipe (g_signal_pipe) == -1)
  347. exit_fatal ("%s: %s", "pipe", strerror (errno));
  348. set_cloexec (g_signal_pipe[0]);
  349. set_cloexec (g_signal_pipe[1]);
  350. // So that the pipe cannot overflow; it would make write() block within
  351. // the signal handler, which is something we really don't want to happen.
  352. // The same holds true for read().
  353. set_blocking (g_signal_pipe[0], false);
  354. set_blocking (g_signal_pipe[1], false);
  355. struct sigaction sa;
  356. sa.sa_flags = SA_RESTART;
  357. sa.sa_handler = sigchld_handler;
  358. sigemptyset (&sa.sa_mask);
  359. if (sigaction (SIGCHLD, &sa, NULL) == -1)
  360. exit_fatal ("sigaction: %s", strerror (errno));
  361. signal (SIGPIPE, SIG_IGN);
  362. sa.sa_handler = sigterm_handler;
  363. if (sigaction (SIGINT, &sa, NULL) == -1
  364. || sigaction (SIGTERM, &sa, NULL) == -1)
  365. exit_fatal ("sigaction: %s", strerror (errno));
  366. }
  367. static void
  368. translate_signal_info (int no, const char **name, int code, const char **reason)
  369. {
  370. if (code == SI_USER) *reason = "signal sent by kill()";
  371. if (code == SI_QUEUE) *reason = "signal sent by sigqueue()";
  372. switch (no)
  373. {
  374. case SIGILL:
  375. *name = "SIGILL";
  376. if (code == ILL_ILLOPC) *reason = "illegal opcode";
  377. if (code == ILL_ILLOPN) *reason = "illegal operand";
  378. if (code == ILL_ILLADR) *reason = "illegal addressing mode";
  379. if (code == ILL_ILLTRP) *reason = "illegal trap";
  380. if (code == ILL_PRVOPC) *reason = "privileged opcode";
  381. if (code == ILL_PRVREG) *reason = "privileged register";
  382. if (code == ILL_COPROC) *reason = "coprocessor error";
  383. if (code == ILL_BADSTK) *reason = "internal stack error";
  384. break;
  385. case SIGFPE:
  386. *name = "SIGFPE";
  387. if (code == FPE_INTDIV) *reason = "integer divide by zero";
  388. if (code == FPE_INTOVF) *reason = "integer overflow";
  389. if (code == FPE_FLTDIV) *reason = "floating-point divide by zero";
  390. if (code == FPE_FLTOVF) *reason = "floating-point overflow";
  391. if (code == FPE_FLTUND) *reason = "floating-point underflow";
  392. if (code == FPE_FLTRES) *reason = "floating-point inexact result";
  393. if (code == FPE_FLTINV) *reason = "invalid floating-point operation";
  394. if (code == FPE_FLTSUB) *reason = "subscript out of range";
  395. break;
  396. case SIGSEGV:
  397. *name = "SIGSEGV";
  398. if (code == SEGV_MAPERR)
  399. *reason = "address not mapped to object";
  400. if (code == SEGV_ACCERR)
  401. *reason = "invalid permissions for mapped object";
  402. break;
  403. case SIGBUS:
  404. *name = "SIGBUS";
  405. if (code == BUS_ADRALN) *reason = "invalid address alignment";
  406. if (code == BUS_ADRERR) *reason = "nonexistent physical address";
  407. if (code == BUS_OBJERR) *reason = "object-specific hardware error";
  408. break;
  409. default:
  410. *name = NULL;
  411. }
  412. }
  413. static void
  414. recovery_handler (int signum, siginfo_t *info, void *context)
  415. {
  416. (void) context;
  417. // TODO: maybe try to force a core dump like this: if (fork() == 0) return;
  418. // TODO: maybe we could even send "\r\nQUIT :reason\r\n" to the server. >_>
  419. // As long as we're not connected via TLS, that is.
  420. const char *signal_name = NULL, *reason = NULL;
  421. translate_signal_info (signum, &signal_name, info->si_code, &reason);
  422. char buf[128], numbuf[8];
  423. if (!signal_name)
  424. {
  425. snprintf (numbuf, sizeof numbuf, "%d", signum);
  426. signal_name = numbuf;
  427. }
  428. if (reason)
  429. snprintf (buf, sizeof buf, "%s=%s: %s: %s", g_startup_reason_str,
  430. "signal received", signal_name, reason);
  431. else
  432. snprintf (buf, sizeof buf, "%s=%s: %s", g_startup_reason_str,
  433. "signal received", signal_name);
  434. *g_startup_reason_location = buf;
  435. // TODO: maybe pregenerate the path, see the following for some other ways
  436. // that would be illegal to do from within a signal handler:
  437. // http://stackoverflow.com/a/1024937
  438. // http://stackoverflow.com/q/799679
  439. // Especially if we change the current working directory in the program.
  440. //
  441. // Note that I can just overwrite g_orig_argv[0].
  442. // NOTE: our children will read EOF on the read ends of their pipes as a
  443. // a result of O_CLOEXEC. That should be enough to make them terminate.
  444. char **argv = g_original_argv.vector, **argp = g_recovery_env.vector;
  445. execve ("/proc/self/exe", argv, argp); // Linux
  446. execve ("/proc/curproc/file", argv, argp); // BSD
  447. execve ("/proc/curproc/exe", argv, argp); // BSD
  448. execve ("/proc/self/path/a.out", argv, argp); // Solaris
  449. execve (argv[0], argv, argp); // unreliable fallback
  450. // Let's just crash
  451. perror ("execve");
  452. signal (signum, SIG_DFL);
  453. raise (signum);
  454. }
  455. static void
  456. prepare_recovery_environment (void)
  457. {
  458. str_vector_init (&g_recovery_env);
  459. str_vector_add_vector (&g_recovery_env, environ);
  460. // Prepare a location within the environment where we will put the startup
  461. // (or maybe rather restart) reason in case of an irrecoverable error.
  462. char **iter;
  463. for (iter = g_recovery_env.vector; *iter; iter++)
  464. {
  465. const size_t len = sizeof g_startup_reason_str - 1;
  466. if (!strncmp (*iter, g_startup_reason_str, len) && (*iter)[len] == '=')
  467. break;
  468. }
  469. if (iter)
  470. g_startup_reason_location = iter;
  471. else
  472. {
  473. str_vector_add (&g_recovery_env, "");
  474. g_startup_reason_location =
  475. g_recovery_env.vector + g_recovery_env.len - 1;
  476. }
  477. }
  478. static void
  479. setup_recovery_handler (struct bot_context *ctx)
  480. {
  481. const char *recover_str = str_map_find (&ctx->config, "recover");
  482. hard_assert (recover_str != NULL); // We have a default value for this
  483. bool recover;
  484. if (!set_boolean_if_valid (&recover, recover_str))
  485. {
  486. print_error ("invalid configuration value for `%s'", "recover");
  487. exit (EXIT_FAILURE);
  488. }
  489. if (!recover)
  490. return;
  491. // Make sure these signals aren't blocked, otherwise we would be unable
  492. // to handle them, making the critical conditions fatal.
  493. sigset_t mask;
  494. sigemptyset (&mask);
  495. sigaddset (&mask, SIGSEGV);
  496. sigaddset (&mask, SIGBUS);
  497. sigaddset (&mask, SIGFPE);
  498. sigaddset (&mask, SIGILL);
  499. sigprocmask (SIG_UNBLOCK, &mask, NULL);
  500. struct sigaction sa;
  501. sa.sa_flags = SA_SIGINFO;
  502. sa.sa_sigaction = recovery_handler;
  503. sigemptyset (&sa.sa_mask);
  504. prepare_recovery_environment ();
  505. // TODO: also handle SIGABRT... or avoid doing abort() in the first place?
  506. if (sigaction (SIGSEGV, &sa, NULL) == -1
  507. || sigaction (SIGBUS, &sa, NULL) == -1
  508. || sigaction (SIGFPE, &sa, NULL) == -1
  509. || sigaction (SIGILL, &sa, NULL) == -1)
  510. print_error ("sigaction: %s", strerror (errno));
  511. }
  512. // --- Plugins -----------------------------------------------------------------
  513. /// The name of the special IRC command for interprocess communication
  514. static const char *plugin_ipc_command = "ZYKLONB";
  515. static struct plugin_data *
  516. plugin_find_by_pid (struct bot_context *ctx, pid_t pid)
  517. {
  518. struct plugin_data *iter;
  519. for (iter = ctx->plugins; iter; iter = iter->next)
  520. if (iter->pid == pid)
  521. return iter;
  522. return NULL;
  523. }
  524. static bool
  525. plugin_zombify (struct plugin_data *plugin)
  526. {
  527. if (plugin->is_zombie)
  528. return false;
  529. // FIXME: make sure that we don't remove entries from the poller while we
  530. // still may have stuff to read; maybe just check that the read pipe is
  531. // empty before closing it... and then on EOF check if `pid == -1' and
  532. // only then dispose of it (it'd be best to simulate that both of these
  533. // cases may happen).
  534. ssize_t poller_idx =
  535. poller_find_by_fd (&plugin->ctx->poller, plugin->write_fd);
  536. if (poller_idx != -1)
  537. poller_remove_at_index (&plugin->ctx->poller, poller_idx);
  538. // TODO: try to flush the write buffer (non-blocking)?
  539. // The plugin should terminate itself after it receives EOF.
  540. xclose (plugin->write_fd);
  541. plugin->write_fd = -1;
  542. // Make it a pseudo-anonymous zombie. In this state we process any
  543. // remaining commands it attempts to send to us before it finally dies.
  544. str_map_set (&plugin->ctx->plugins_by_name, plugin->name, NULL);
  545. plugin->is_zombie = true;
  546. return true;
  547. }
  548. static void
  549. on_plugin_writable (const struct pollfd *fd, struct plugin_data *plugin)
  550. {
  551. struct bot_context *ctx = plugin->ctx;
  552. struct str *buf = &plugin->write_buffer;
  553. size_t written_total = 0;
  554. if (fd->revents & ~(POLLOUT | POLLHUP | POLLERR))
  555. print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
  556. while (written_total != buf->len)
  557. {
  558. ssize_t n_written = write (fd->fd, buf->str + written_total,
  559. buf->len - written_total);
  560. if (n_written < 0)
  561. {
  562. if (errno == EAGAIN)
  563. break;
  564. if (errno == EINTR)
  565. continue;
  566. soft_assert (errno == EPIPE);
  567. // Zombies shouldn't get dispatched for writability
  568. hard_assert (!plugin->is_zombie);
  569. print_debug ("%s: %s", "write", strerror (errno));
  570. print_error ("failure on writing to plugin `%s',"
  571. " therefore I'm unloading it", plugin->name);
  572. plugin_zombify (plugin);
  573. break;
  574. }
  575. // This may be equivalent to EAGAIN on some implementations
  576. if (n_written == 0)
  577. break;
  578. written_total += n_written;
  579. }
  580. if (written_total != 0)
  581. str_remove_slice (buf, 0, written_total);
  582. if (buf->len == 0)
  583. {
  584. // Everything has been written, there's no need to end up in here again
  585. ssize_t index = poller_find_by_fd (&ctx->poller, fd->fd);
  586. if (index != -1)
  587. poller_remove_at_index (&ctx->poller, index);
  588. }
  589. }
  590. static void
  591. plugin_queue_write (struct plugin_data *plugin)
  592. {
  593. if (plugin->is_zombie)
  594. return;
  595. // Don't let the write buffer grow infinitely. If there's a ton of data
  596. // waiting to be processed by the plugin, it usually means there's something
  597. // wrong with it (such as someone stopping the process).
  598. if (plugin->write_buffer.len >= (1 << 20))
  599. {
  600. print_warning ("plugin `%s' does not seem to process messages fast"
  601. " enough, I'm unloading it", plugin->name);
  602. plugin_zombify (plugin);
  603. return;
  604. }
  605. poller_set (&plugin->ctx->poller, plugin->write_fd, POLLOUT,
  606. (poller_dispatcher_func) on_plugin_writable, plugin);
  607. }
  608. static void
  609. plugin_send (struct plugin_data *plugin, const char *format, ...)
  610. ATTRIBUTE_PRINTF (2, 3);
  611. static void
  612. plugin_send (struct plugin_data *plugin, const char *format, ...)
  613. {
  614. va_list ap;
  615. if (g_debug_mode)
  616. {
  617. fprintf (stderr, "[%s] <-- \"", plugin->name);
  618. va_start (ap, format);
  619. vfprintf (stderr, format, ap);
  620. va_end (ap);
  621. fputs ("\"\n", stderr);
  622. }
  623. va_start (ap, format);
  624. str_append_vprintf (&plugin->write_buffer, format, ap);
  625. va_end (ap);
  626. str_append (&plugin->write_buffer, "\r\n");
  627. plugin_queue_write (plugin);
  628. }
  629. static void
  630. plugin_process_message (const struct irc_message *msg,
  631. const char *raw, void *user_data)
  632. {
  633. struct plugin_data *plugin = user_data;
  634. struct bot_context *ctx = plugin->ctx;
  635. if (g_debug_mode)
  636. fprintf (stderr, "[%s] --> \"%s\"\n", plugin->name, raw);
  637. if (!strcasecmp (msg->command, plugin_ipc_command))
  638. {
  639. // Replies are sent in the order in which they came in, so there's
  640. // no need to attach a special identifier to them. It might be
  641. // desirable in some cases, though.
  642. if (msg->params.len < 1)
  643. return;
  644. const char *command = msg->params.vector[0];
  645. if (!plugin->initialized && !strcasecmp (command, "register"))
  646. {
  647. // Register for relaying of IRC traffic
  648. plugin->initialized = true;
  649. // Flush any queued up traffic here. The point of queuing it in
  650. // the first place is so that we don't have to wait for plugin
  651. // initialization during startup.
  652. //
  653. // Note that if we start filtering data coming to the plugins e.g.
  654. // based on what it tells us upon registration, we might need to
  655. // filter `queued_output' as well.
  656. str_append_str (&plugin->write_buffer, &plugin->queued_output);
  657. str_free (&plugin->queued_output);
  658. // NOTE: this may trigger the buffer length check
  659. plugin_queue_write (plugin);
  660. }
  661. else if (!strcasecmp (command, "get_config"))
  662. {
  663. if (msg->params.len < 2)
  664. return;
  665. const char *value =
  666. str_map_find (&ctx->config, msg->params.vector[1]);
  667. // TODO: escape the value (although there's no need to ATM)
  668. plugin_send (plugin, "%s :%s",
  669. plugin_ipc_command, value ? value : "");
  670. }
  671. else if (!strcasecmp (command, "print"))
  672. {
  673. if (msg->params.len < 2)
  674. return;
  675. printf ("%s\n", msg->params.vector[1]);
  676. }
  677. }
  678. else if (plugin->initialized && ctx->irc_ready)
  679. {
  680. // Pass everything else through to the IRC server
  681. // XXX: when the server isn't ready yet, these messages get silently
  682. // discarded, which shouldn't pose a problem most of the time.
  683. // Perhaps we could send a "connected" notification on `register'
  684. // if `irc_ready' is true, or after it becomes true later, so that
  685. // plugins know when to start sending unprovoked IRC messages.
  686. // XXX: another case is when the connection gets interrupted and the
  687. // plugin tries to send something back while we're reconnecting.
  688. // For that we might set up a global buffer that gets flushed out
  689. // after `irc_ready' becomes true. Note that there is always some
  690. // chance of messages getting lost without us even noticing it.
  691. irc_send (ctx, "%s", raw);
  692. }
  693. }
  694. static void
  695. on_plugin_readable (const struct pollfd *fd, struct plugin_data *plugin)
  696. {
  697. if (fd->revents & ~(POLLIN | POLLHUP | POLLERR))
  698. print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
  699. // TODO: see if I can reuse irc_fill_read_buffer()
  700. struct str *buf = &plugin->read_buffer;
  701. while (true)
  702. {
  703. str_ensure_space (buf, 512 + 1);
  704. ssize_t n_read = read (fd->fd, buf->str + buf->len,
  705. buf->alloc - buf->len - 1);
  706. if (n_read < 0)
  707. {
  708. if (errno == EAGAIN)
  709. break;
  710. if (soft_assert (errno == EINTR))
  711. continue;
  712. if (!plugin->is_zombie)
  713. {
  714. print_error ("failure on reading from plugin `%s',"
  715. " therefore I'm unloading it", plugin->name);
  716. plugin_zombify (plugin);
  717. }
  718. return;
  719. }
  720. // EOF; hopefully it will die soon (maybe it already has)
  721. if (n_read == 0)
  722. break;
  723. buf->str[buf->len += n_read] = '\0';
  724. if (buf->len >= (1 << 20))
  725. {
  726. // XXX: this isn't really the best flood prevention mechanism,
  727. // but it wasn't even supposed to be one.
  728. if (plugin->is_zombie)
  729. {
  730. print_error ("a zombie of plugin `%s' is trying to flood us,"
  731. " therefore I'm killing it", plugin->name);
  732. kill (plugin->pid, SIGKILL);
  733. }
  734. else
  735. {
  736. print_error ("plugin `%s' seems to spew out data frantically,"
  737. " therefore I'm unloading it", plugin->name);
  738. plugin_zombify (plugin);
  739. }
  740. return;
  741. }
  742. }
  743. irc_process_buffer (buf, plugin_process_message, plugin);
  744. }
  745. static bool
  746. is_valid_plugin_name (const char *name)
  747. {
  748. if (!*name)
  749. return false;
  750. for (const char *p = name; *p; p++)
  751. if (!isgraph (*p) || *p == '/')
  752. return false;
  753. return true;
  754. }
  755. static bool
  756. plugin_load (struct bot_context *ctx, const char *name, struct error **e)
  757. {
  758. const char *plugin_dir = str_map_find (&ctx->config, "plugin_dir");
  759. if (!plugin_dir)
  760. {
  761. error_set (e, "plugin directory not set");
  762. return false;
  763. }
  764. if (!is_valid_plugin_name (name))
  765. {
  766. error_set (e, "invalid plugin name");
  767. return false;
  768. }
  769. if (str_map_find (&ctx->plugins_by_name, name))
  770. {
  771. error_set (e, "the plugin has already been loaded");
  772. return false;
  773. }
  774. int stdin_pipe[2];
  775. if (pipe (stdin_pipe) == -1)
  776. {
  777. error_set (e, "%s: %s: %s",
  778. "failed to load the plugin", "pipe", strerror (errno));
  779. goto fail_1;
  780. }
  781. int stdout_pipe[2];
  782. if (pipe (stdout_pipe) == -1)
  783. {
  784. error_set (e, "%s: %s: %s",
  785. "failed to load the plugin", "pipe", strerror (errno));
  786. goto fail_2;
  787. }
  788. set_cloexec (stdin_pipe[1]);
  789. set_cloexec (stdout_pipe[0]);
  790. pid_t pid = fork ();
  791. if (pid == -1)
  792. {
  793. error_set (e, "%s: %s: %s",
  794. "failed to load the plugin", "fork", strerror (errno));
  795. goto fail_3;
  796. }
  797. if (pid == 0)
  798. {
  799. // Redirect the child's stdin and stdout to the pipes
  800. hard_assert (dup2 (stdin_pipe[0], STDIN_FILENO) != -1);
  801. hard_assert (dup2 (stdout_pipe[1], STDOUT_FILENO) != -1);
  802. xclose (stdin_pipe[0]);
  803. xclose (stdout_pipe[1]);
  804. struct str pathname;
  805. str_init (&pathname);
  806. str_append (&pathname, plugin_dir);
  807. str_append_c (&pathname, '/');
  808. str_append (&pathname, name);
  809. // Restore some of the signal handling
  810. signal (SIGPIPE, SIG_DFL);
  811. char *const argv[] = { pathname.str, NULL };
  812. execve (argv[0], argv, environ);
  813. // We will collect the failure later via SIGCHLD
  814. print_error ("%s: %s: %s",
  815. "failed to load the plugin", "exec", strerror (errno));
  816. _exit (EXIT_FAILURE);
  817. }
  818. xclose (stdin_pipe[0]);
  819. xclose (stdout_pipe[1]);
  820. set_blocking (stdout_pipe[0], false);
  821. set_blocking (stdin_pipe[1], false);
  822. struct plugin_data *plugin = xmalloc (sizeof *plugin);
  823. plugin_data_init (plugin);
  824. plugin->ctx = ctx;
  825. plugin->pid = pid;
  826. plugin->name = xstrdup (name);
  827. plugin->read_fd = stdout_pipe[0];
  828. plugin->write_fd = stdin_pipe[1];
  829. LIST_PREPEND (ctx->plugins, plugin);
  830. str_map_set (&ctx->plugins_by_name, name, plugin);
  831. poller_set (&ctx->poller, stdout_pipe[0], POLLIN,
  832. (poller_dispatcher_func) on_plugin_readable, plugin);
  833. return true;
  834. fail_3:
  835. xclose (stdout_pipe[0]);
  836. xclose (stdout_pipe[1]);
  837. fail_2:
  838. xclose (stdin_pipe[0]);
  839. xclose (stdin_pipe[1]);
  840. fail_1:
  841. return false;
  842. }
  843. static bool
  844. plugin_unload (struct bot_context *ctx, const char *name, struct error **e)
  845. {
  846. struct plugin_data *plugin = str_map_find (&ctx->plugins_by_name, name);
  847. if (!plugin)
  848. {
  849. error_set (e, "no such plugin is loaded");
  850. return false;
  851. }
  852. plugin_zombify (plugin);
  853. // TODO: add a `kill zombies' command to forcefully get rid of processes
  854. // that do not understand the request.
  855. // TODO: set a timeout before we go for a kill automatically (and if this
  856. // was a reload request, try to bring the plugin back up)
  857. return true;
  858. }
  859. static void
  860. plugin_load_all_from_config (struct bot_context *ctx)
  861. {
  862. const char *plugin_list = str_map_find (&ctx->config, "plugins");
  863. if (!plugin_list)
  864. return;
  865. struct str_vector plugins;
  866. str_vector_init (&plugins);
  867. split_str_ignore_empty (plugin_list, ',', &plugins);
  868. for (size_t i = 0; i < plugins.len; i++)
  869. {
  870. char *name = strip_str_in_place (plugins.vector[i], " ");
  871. struct error *e = NULL;
  872. if (!plugin_load (ctx, name, &e))
  873. {
  874. print_error ("plugin `%s' failed to load: %s", name, e->message);
  875. error_free (e);
  876. }
  877. }
  878. str_vector_free (&plugins);
  879. }
  880. // --- Main program ------------------------------------------------------------
  881. static bool
  882. parse_bot_command (const char *s, const char *command, const char **following)
  883. {
  884. size_t command_len = strlen (command);
  885. if (strncasecmp (s, command, command_len))
  886. return false;
  887. s += command_len;
  888. // Expect a word boundary, so that we don't respond to invalid things
  889. if (isalnum (*s))
  890. return false;
  891. // Ignore any initial spaces; the rest is the command's argument
  892. while (isblank (*s))
  893. s++;
  894. *following = s;
  895. return true;
  896. }
  897. static void
  898. split_bot_command_argument_list (const char *arguments, struct str_vector *out)
  899. {
  900. split_str_ignore_empty (arguments, ',', out);
  901. for (size_t i = 0; i < out->len; )
  902. {
  903. if (!*strip_str_in_place (out->vector[i], " \t"))
  904. str_vector_remove (out, i);
  905. else
  906. i++;
  907. }
  908. }
  909. static bool
  910. is_private_message (const struct irc_message *msg)
  911. {
  912. hard_assert (msg->params.len);
  913. return !strchr ("#&+!", *msg->params.vector[0]);
  914. }
  915. static bool
  916. is_sent_by_admin (struct bot_context *ctx, const struct irc_message *msg)
  917. {
  918. // No administrator set -> everyone is an administrator
  919. if (!ctx->admin_re)
  920. return true;
  921. return regexec (ctx->admin_re, msg->prefix, 0, NULL, 0) != REG_NOMATCH;
  922. }
  923. static void respond_to_user (struct bot_context *ctx, const struct
  924. irc_message *msg, const char *format, ...) ATTRIBUTE_PRINTF (3, 4);
  925. static void
  926. respond_to_user (struct bot_context *ctx, const struct irc_message *msg,
  927. const char *format, ...)
  928. {
  929. if (!soft_assert (msg->prefix && msg->params.len))
  930. return;
  931. char nick[strcspn (msg->prefix, "!") + 1];
  932. strncpy (nick, msg->prefix, sizeof nick - 1);
  933. nick[sizeof nick - 1] = '\0';
  934. struct str text;
  935. va_list ap;
  936. str_init (&text);
  937. va_start (ap, format);
  938. str_append_vprintf (&text, format, ap);
  939. va_end (ap);
  940. if (is_private_message (msg))
  941. irc_send (ctx, "PRIVMSG %s :%s", nick, text.str);
  942. else
  943. irc_send (ctx, "PRIVMSG %s :%s: %s",
  944. msg->params.vector[0], nick, text.str);
  945. str_free (&text);
  946. }
  947. static void
  948. process_plugin_load (struct bot_context *ctx,
  949. const struct irc_message *msg, const char *name)
  950. {
  951. struct error *e = NULL;
  952. if (plugin_load (ctx, name, &e))
  953. respond_to_user (ctx, msg, "plugin `%s' queued for loading", name);
  954. else
  955. {
  956. respond_to_user (ctx, msg, "plugin `%s' could not be loaded: %s",
  957. name, e->message);
  958. error_free (e);
  959. }
  960. }
  961. static void
  962. process_plugin_unload (struct bot_context *ctx,
  963. const struct irc_message *msg, const char *name)
  964. {
  965. struct error *e = NULL;
  966. if (plugin_unload (ctx, name, &e))
  967. respond_to_user (ctx, msg, "plugin `%s' unloaded", name);
  968. else
  969. {
  970. respond_to_user (ctx, msg, "plugin `%s' could not be unloaded: %s",
  971. name, e->message);
  972. error_free (e);
  973. }
  974. }
  975. static void
  976. process_plugin_reload (struct bot_context *ctx,
  977. const struct irc_message *msg, const char *name)
  978. {
  979. // So far the only error that can occur is that the plugin hasn't been
  980. // loaded, which in this case doesn't really matter.
  981. plugin_unload (ctx, name, NULL);
  982. process_plugin_load (ctx, msg, name);
  983. }
  984. static void
  985. process_privmsg (struct bot_context *ctx, const struct irc_message *msg)
  986. {
  987. if (!is_sent_by_admin (ctx, msg))
  988. return;
  989. if (msg->params.len < 2)
  990. return;
  991. const char *prefix = str_map_find (&ctx->config, "prefix");
  992. hard_assert (prefix != NULL); // We have a default value for this
  993. // For us to recognize the command, it has to start with the prefix,
  994. // with the exception of PM's sent directly to us.
  995. const char *text = msg->params.vector[1];
  996. if (!strncmp (text, prefix, strlen (prefix)))
  997. text += strlen (prefix);
  998. else if (!is_private_message (msg))
  999. return;
  1000. const char *following;
  1001. struct str_vector list;
  1002. str_vector_init (&list);
  1003. if (parse_bot_command (text, "quote", &following))
  1004. // This seems to replace tons of random stupid commands
  1005. irc_send (ctx, "%s", following);
  1006. else if (parse_bot_command (text, "quit", &following))
  1007. {
  1008. // We actually need this command (instead of just `quote') because we
  1009. // could try to reconnect to the server automatically otherwise.
  1010. if (*following)
  1011. irc_send (ctx, "QUIT :%s", following);
  1012. else
  1013. irc_send (ctx, "QUIT");
  1014. initiate_quit (ctx);
  1015. }
  1016. else if (parse_bot_command (text, "status", &following))
  1017. {
  1018. struct str report;
  1019. str_init (&report);
  1020. const char *reason = getenv (g_startup_reason_str);
  1021. if (!reason)
  1022. reason = "launched normally";
  1023. str_append_printf (&report, "\x02startup reason:\x0f %s, ", reason);
  1024. str_append (&report, "\x02plugins:\x0f ");
  1025. size_t zombies = 0;
  1026. for (struct plugin_data *plugin = ctx->plugins;
  1027. plugin; plugin = plugin->next)
  1028. {
  1029. if (plugin->is_zombie)
  1030. zombies++;
  1031. else
  1032. str_append_printf (&report, "%s, ", plugin->name);
  1033. }
  1034. if (!ctx->plugins)
  1035. str_append (&report, "\x02none\x0f, ");
  1036. str_append_printf (&report, "\x02zombies:\x0f %zu", zombies);
  1037. respond_to_user (ctx, msg, "%s", report.str);
  1038. str_free (&report);
  1039. }
  1040. else if (parse_bot_command (text, "load", &following))
  1041. {
  1042. split_bot_command_argument_list (following, &list);
  1043. for (size_t i = 0; i < list.len; i++)
  1044. process_plugin_load (ctx, msg, list.vector[i]);
  1045. }
  1046. else if (parse_bot_command (text, "reload", &following))
  1047. {
  1048. split_bot_command_argument_list (following, &list);
  1049. for (size_t i = 0; i < list.len; i++)
  1050. process_plugin_reload (ctx, msg, list.vector[i]);
  1051. }
  1052. else if (parse_bot_command (text, "unload", &following))
  1053. {
  1054. split_bot_command_argument_list (following, &list);
  1055. for (size_t i = 0; i < list.len; i++)
  1056. process_plugin_unload (ctx, msg, list.vector[i]);
  1057. }
  1058. str_vector_free (&list);
  1059. }
  1060. static void
  1061. irc_forward_message_to_plugins (struct bot_context *ctx, const char *raw)
  1062. {
  1063. // For consistency with plugin_process_message()
  1064. if (!ctx->irc_ready)
  1065. return;
  1066. for (struct plugin_data *plugin = ctx->plugins;
  1067. plugin; plugin = plugin->next)
  1068. {
  1069. if (plugin->is_zombie)
  1070. continue;
  1071. if (plugin->initialized)
  1072. plugin_send (plugin, "%s", raw);
  1073. else
  1074. // TODO: make sure that this buffer doesn't get too large either
  1075. str_append_printf (&plugin->queued_output, "%s\r\n", raw);
  1076. }
  1077. }
  1078. static void
  1079. irc_process_message (const struct irc_message *msg,
  1080. const char *raw, void *user_data)
  1081. {
  1082. struct bot_context *ctx = user_data;
  1083. if (g_debug_mode)
  1084. fprintf (stderr, "[%s] ==> \"%s\"\n", "IRC", raw);
  1085. // This should be as minimal as possible, I don't want to have the whole bot
  1086. // written in C, especially when I have this overengineered plugin system.
  1087. // Therefore the very basic functionality only.
  1088. //
  1089. // I should probably even rip out the autojoin...
  1090. irc_forward_message_to_plugins (ctx, raw);
  1091. if (!strcasecmp (msg->command, "PING"))
  1092. {
  1093. if (msg->params.len)
  1094. irc_send (ctx, "PONG :%s", msg->params.vector[0]);
  1095. else
  1096. irc_send (ctx, "PONG");
  1097. }
  1098. else if (!ctx->irc_ready && (!strcasecmp (msg->command, "MODE")
  1099. || !strcasecmp (msg->command, "376") // RPL_ENDOFMOTD
  1100. || !strcasecmp (msg->command, "422"))) // ERR_NOMOTD
  1101. {
  1102. print_status ("successfully connected");
  1103. ctx->irc_ready = true;
  1104. const char *autojoin = str_map_find (&ctx->config, "autojoin");
  1105. if (autojoin)
  1106. irc_send (ctx, "JOIN :%s", autojoin);
  1107. }
  1108. else if (!strcasecmp (msg->command, "PRIVMSG"))
  1109. process_privmsg (ctx, msg);
  1110. }
  1111. enum irc_read_result
  1112. {
  1113. IRC_READ_OK, ///< Some data were read successfully
  1114. IRC_READ_EOF, ///< The server has closed connection
  1115. IRC_READ_AGAIN, ///< No more data at the moment
  1116. IRC_READ_ERROR ///< General connection failure
  1117. };
  1118. static enum irc_read_result
  1119. irc_fill_read_buffer_ssl (struct bot_context *ctx, struct str *buf)
  1120. {
  1121. int n_read;
  1122. start:
  1123. n_read = SSL_read (ctx->ssl, buf->str + buf->len,
  1124. buf->alloc - buf->len - 1 /* null byte */);
  1125. const char *error_info = NULL;
  1126. switch (xssl_get_error (ctx->ssl, n_read, &error_info))
  1127. {
  1128. case SSL_ERROR_NONE:
  1129. buf->str[buf->len += n_read] = '\0';
  1130. return IRC_READ_OK;
  1131. case SSL_ERROR_ZERO_RETURN:
  1132. return IRC_READ_EOF;
  1133. case SSL_ERROR_WANT_READ:
  1134. return IRC_READ_AGAIN;
  1135. case SSL_ERROR_WANT_WRITE:
  1136. {
  1137. // Let it finish the handshake as we don't poll for writability;
  1138. // any errors are to be collected by SSL_read() in the next iteration
  1139. struct pollfd pfd = { .fd = ctx->irc_fd, .events = POLLOUT };
  1140. soft_assert (poll (&pfd, 1, 0) > 0);
  1141. goto start;
  1142. }
  1143. case XSSL_ERROR_TRY_AGAIN:
  1144. goto start;
  1145. default:
  1146. print_debug ("%s: %s: %s", __func__, "SSL_read", error_info);
  1147. return IRC_READ_ERROR;
  1148. }
  1149. }
  1150. static enum irc_read_result
  1151. irc_fill_read_buffer (struct bot_context *ctx, struct str *buf)
  1152. {
  1153. ssize_t n_read;
  1154. start:
  1155. n_read = recv (ctx->irc_fd, buf->str + buf->len,
  1156. buf->alloc - buf->len - 1 /* null byte */, 0);
  1157. if (n_read > 0)
  1158. {
  1159. buf->str[buf->len += n_read] = '\0';
  1160. return IRC_READ_OK;
  1161. }
  1162. if (n_read == 0)
  1163. return IRC_READ_EOF;
  1164. if (errno == EAGAIN)
  1165. return IRC_READ_AGAIN;
  1166. if (errno == EINTR)
  1167. goto start;
  1168. print_debug ("%s: %s: %s", __func__, "recv", strerror (errno));
  1169. return IRC_READ_ERROR;
  1170. }
  1171. static bool irc_connect (struct bot_context *ctx, struct error **e);
  1172. static void
  1173. irc_try_reconnect (struct bot_context *ctx)
  1174. {
  1175. if (!soft_assert (ctx->irc_fd == -1))
  1176. return;
  1177. const char *reconnect_str = str_map_find (&ctx->config, "reconnect");
  1178. hard_assert (reconnect_str != NULL); // We have a default value for this
  1179. bool reconnect;
  1180. if (!set_boolean_if_valid (&reconnect, reconnect_str))
  1181. {
  1182. print_fatal ("invalid configuration value for `%s'", "recover");
  1183. try_finish_quit (ctx);
  1184. return;
  1185. }
  1186. if (!reconnect)
  1187. return;
  1188. const char *delay_str = str_map_find (&ctx->config, "reconnect_delay");
  1189. hard_assert (delay_str != NULL); // We have a default value for this
  1190. unsigned long delay;
  1191. if (!xstrtoul (&delay, delay_str, 10))
  1192. {
  1193. print_error ("invalid configuration value for `%s'",
  1194. "reconnect_delay");
  1195. delay = 0;
  1196. }
  1197. while (true)
  1198. {
  1199. // TODO: this would be better suited by a timeout event;
  1200. // remember to update try_finish_quit() etc. to reflect this
  1201. print_status ("trying to reconnect in %ld seconds...", delay);
  1202. sleep (delay);
  1203. struct error *e = NULL;
  1204. if (irc_connect (ctx, &e))
  1205. break;
  1206. print_error ("%s", e->message);
  1207. error_free (e);
  1208. }
  1209. // TODO: inform plugins about the new connection
  1210. }
  1211. static void
  1212. on_irc_disconnected (struct bot_context *ctx)
  1213. {
  1214. // Get rid of the dead socket etc.
  1215. if (ctx->ssl)
  1216. {
  1217. SSL_free (ctx->ssl);
  1218. ctx->ssl = NULL;
  1219. SSL_CTX_free (ctx->ssl_ctx);
  1220. ctx->ssl_ctx = NULL;
  1221. }
  1222. ssize_t i = poller_find_by_fd (&ctx->poller, ctx->irc_fd);
  1223. if (i != -1)
  1224. poller_remove_at_index (&ctx->poller, i);
  1225. xclose (ctx->irc_fd);
  1226. ctx->irc_fd = -1;
  1227. ctx->irc_ready = false;
  1228. // TODO: inform plugins about the disconnect event
  1229. if (ctx->quitting)
  1230. {
  1231. // Unload all plugins
  1232. // TODO: wait for a few seconds and then send SIGKILL to all plugins
  1233. for (struct plugin_data *plugin = ctx->plugins;
  1234. plugin; plugin = plugin->next)
  1235. plugin_zombify (plugin);
  1236. try_finish_quit (ctx);
  1237. return;
  1238. }
  1239. irc_try_reconnect (ctx);
  1240. }
  1241. static void
  1242. on_irc_readable (const struct pollfd *fd, struct bot_context *ctx)
  1243. {
  1244. if (fd->revents & ~(POLLIN | POLLHUP | POLLERR))
  1245. print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
  1246. (void) set_blocking (ctx->irc_fd, false);
  1247. struct str *buf = &ctx->read_buffer;
  1248. enum irc_read_result (*fill_buffer)(struct bot_context *, struct str *)
  1249. = ctx->ssl
  1250. ? irc_fill_read_buffer_ssl
  1251. : irc_fill_read_buffer;
  1252. bool disconnected = false;
  1253. while (true)
  1254. {
  1255. str_ensure_space (buf, 512);
  1256. switch (fill_buffer (ctx, buf))
  1257. {
  1258. case IRC_READ_AGAIN:
  1259. goto end;
  1260. case IRC_READ_ERROR:
  1261. print_error ("reading from the IRC server failed");
  1262. disconnected = true;
  1263. goto end;
  1264. case IRC_READ_EOF:
  1265. print_status ("the IRC server closed the connection");
  1266. disconnected = true;
  1267. goto end;
  1268. case IRC_READ_OK:
  1269. break;
  1270. }
  1271. if (buf->len >= (1 << 20))
  1272. {
  1273. print_error ("the IRC server seems to spew out data frantically");
  1274. irc_shutdown (ctx);
  1275. goto end;
  1276. }
  1277. }
  1278. end:
  1279. (void) set_blocking (ctx->irc_fd, true);
  1280. irc_process_buffer (buf, irc_process_message, ctx);
  1281. if (disconnected)
  1282. on_irc_disconnected (ctx);
  1283. }
  1284. static bool
  1285. irc_connect (struct bot_context *ctx, struct error **e)
  1286. {
  1287. const char *irc_host = str_map_find (&ctx->config, "irc_host");
  1288. const char *irc_port = str_map_find (&ctx->config, "irc_port");
  1289. const char *ssl_use_str = str_map_find (&ctx->config, "ssl_use");
  1290. const char *nickname = str_map_find (&ctx->config, "nickname");
  1291. const char *username = str_map_find (&ctx->config, "username");
  1292. const char *realname = str_map_find (&ctx->config, "realname");
  1293. // We have a default value for these
  1294. hard_assert (irc_port && ssl_use_str);
  1295. hard_assert (nickname && username && realname);
  1296. // TODO: again, get rid of `struct error' in here. The question is: how
  1297. // do we tell our caller that he should not try to reconnect?
  1298. if (!irc_host)
  1299. {
  1300. error_set (e, "no hostname specified in configuration");
  1301. return false;
  1302. }
  1303. bool use_ssl;
  1304. if (!set_boolean_if_valid (&use_ssl, ssl_use_str))
  1305. {
  1306. error_set (e, "invalid configuration value for `%s'", "use_ssl");
  1307. return false;
  1308. }
  1309. if (!irc_establish_connection (ctx, irc_host, irc_port, use_ssl, e))
  1310. return false;
  1311. // TODO: set a timeout on the socket, something like 30 minutes, then we
  1312. // should ideally send a PING... or just forcefully reconnect.
  1313. //
  1314. // TODO: in exec try: 1/ set blocking, 2/ setsockopt() SO_LINGER,
  1315. // (struct linger) { .l_onoff = true; .l_linger = 1 /* 1s should do */; }
  1316. // 3/ /* O_CLOEXEC */ But only if the QUIT message proves unreliable.
  1317. poller_set (&ctx->poller, ctx->irc_fd, POLLIN,
  1318. (poller_dispatcher_func) on_irc_readable, ctx);
  1319. // TODO: probably check for errors from these calls as well
  1320. irc_send (ctx, "NICK %s", nickname);
  1321. irc_send (ctx, "USER %s 8 * :%s", username, realname);
  1322. return true;
  1323. }
  1324. static bool
  1325. load_admin_regex (struct bot_context *ctx)
  1326. {
  1327. hard_assert (!ctx->admin_re);
  1328. const char *admin = str_map_find (&ctx->config, "admin");
  1329. if (!admin)
  1330. return true;
  1331. struct error *e = NULL;
  1332. ctx->admin_re = regex_compile (admin, REG_EXTENDED | REG_NOSUB, &e);
  1333. if (!e)
  1334. return true;
  1335. print_error ("invalid configuration value for `%s': %s",
  1336. "admin", e->message);
  1337. error_free (e);
  1338. return false;
  1339. }
  1340. static void
  1341. on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx)
  1342. {
  1343. char *dummy;
  1344. (void) read (fd->fd, &dummy, 1);
  1345. // XXX: do we need to check if we have a connection?
  1346. if (g_termination_requested && !ctx->quitting)
  1347. {
  1348. irc_send (ctx, "QUIT :Terminated by signal");
  1349. initiate_quit (ctx);
  1350. }
  1351. // Reap all dead children (since the pipe may overflow, we ask waitpid()
  1352. // to return all the zombies it knows about).
  1353. while (true)
  1354. {
  1355. int status;
  1356. pid_t zombie = waitpid (-1, &status, WNOHANG);
  1357. if (zombie == -1)
  1358. {
  1359. // No children to wait on
  1360. if (errno == ECHILD)
  1361. break;
  1362. hard_assert (errno == EINTR);
  1363. continue;
  1364. }
  1365. if (zombie == 0)
  1366. break;
  1367. struct plugin_data *plugin = plugin_find_by_pid (ctx, zombie);
  1368. // Something has died but we don't recognize it (re-exec?)
  1369. if (!soft_assert (plugin != NULL))
  1370. continue;
  1371. // TODO: callbacks on children death, so that we may tell the user
  1372. // "plugin `name' died like a dirty jewish pig"; use `status'
  1373. if (!plugin->is_zombie && WIFSIGNALED (status))
  1374. {
  1375. const char *notes = "";
  1376. #ifdef WCOREDUMP
  1377. if (WCOREDUMP (status))
  1378. notes = " (core dumped)";
  1379. #endif
  1380. print_warning ("Plugin `%s' died from signal %d%s",
  1381. plugin->name, WTERMSIG (status), notes);
  1382. }
  1383. // Let's go through the zombie state to simplify things a bit
  1384. // TODO: might not be a completely bad idea to restart the plugin
  1385. plugin_zombify (plugin);
  1386. plugin->pid = -1;
  1387. ssize_t poller_idx = poller_find_by_fd (&ctx->poller, plugin->read_fd);
  1388. if (poller_idx != -1)
  1389. poller_remove_at_index (&ctx->poller, poller_idx);
  1390. xclose (plugin->read_fd);
  1391. plugin->read_fd = -1;
  1392. LIST_UNLINK (ctx->plugins, plugin);
  1393. plugin_data_free (plugin);
  1394. free (plugin);
  1395. // Living child processes block us from quitting
  1396. try_finish_quit (ctx);
  1397. }
  1398. }
  1399. static void
  1400. print_usage (const char *program_name)
  1401. {
  1402. fprintf (stderr,
  1403. "Usage: %s [OPTION]...\n"
  1404. "Experimental IRC bot.\n"
  1405. "\n"
  1406. " -d, --debug run in debug mode\n"
  1407. " -h, --help display this help and exit\n"
  1408. " -V, --version output version information and exit\n"
  1409. " --write-default-cfg [filename]\n"
  1410. " write a default configuration file and exit\n",
  1411. program_name);
  1412. }
  1413. int
  1414. main (int argc, char *argv[])
  1415. {
  1416. const char *invocation_name = argv[0];
  1417. str_vector_init (&g_original_argv);
  1418. str_vector_add_vector (&g_original_argv, argv);
  1419. static struct option opts[] =
  1420. {
  1421. { "debug", no_argument, NULL, 'd' },
  1422. { "help", no_argument, NULL, 'h' },
  1423. { "version", no_argument, NULL, 'V' },
  1424. { "write-default-cfg", optional_argument, NULL, 'w' },
  1425. { NULL, 0, NULL, 0 }
  1426. };
  1427. while (1)
  1428. {
  1429. int c, opt_index;
  1430. c = getopt_long (argc, argv, "dhV", opts, &opt_index);
  1431. if (c == -1)
  1432. break;
  1433. switch (c)
  1434. {
  1435. case 'd':
  1436. g_debug_mode = true;
  1437. break;
  1438. case 'h':
  1439. print_usage (invocation_name);
  1440. exit (EXIT_SUCCESS);
  1441. case 'V':
  1442. printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
  1443. exit (EXIT_SUCCESS);
  1444. case 'w':
  1445. call_write_default_config (optarg, g_config_table);
  1446. exit (EXIT_SUCCESS);
  1447. default:
  1448. print_error ("wrong options");
  1449. exit (EXIT_FAILURE);
  1450. }
  1451. }
  1452. print_status (PROGRAM_NAME " " PROGRAM_VERSION " starting");
  1453. setup_signal_handlers ();
  1454. SSL_library_init ();
  1455. atexit (EVP_cleanup);
  1456. SSL_load_error_strings ();
  1457. // XXX: ERR_load_BIO_strings()? Anything else?
  1458. atexit (ERR_free_strings);
  1459. struct bot_context ctx;
  1460. bot_context_init (&ctx);
  1461. struct error *e = NULL;
  1462. if (!read_config_file (&ctx.config, &e))
  1463. {
  1464. print_error ("error loading configuration: %s", e->message);
  1465. error_free (e);
  1466. exit (EXIT_FAILURE);
  1467. }
  1468. setup_recovery_handler (&ctx);
  1469. poller_set (&ctx.poller, g_signal_pipe[0], POLLIN,
  1470. (poller_dispatcher_func) on_signal_pipe_readable, &ctx);
  1471. plugin_load_all_from_config (&ctx);
  1472. if (!load_admin_regex (&ctx))
  1473. exit (EXIT_FAILURE);
  1474. if (!irc_connect (&ctx, &e))
  1475. {
  1476. print_error ("%s", e->message);
  1477. error_free (e);
  1478. exit (EXIT_FAILURE);
  1479. }
  1480. // TODO: clean re-exec support; to save the state I can either use argv,
  1481. // argp, or I can create a temporary file, unlink it and use the FD
  1482. // (mkstemp() on a `struct str' constructed from XDG_RUNTIME_DIR, TMPDIR
  1483. // or /tmp as a last resort + PROGRAM_NAME + ".XXXXXX" -> unlink();
  1484. // remember to use O_CREAT | O_EXCL). The state needs to be versioned.
  1485. // Unfortunately I cannot de/serialize SSL state.
  1486. ctx.polling = true;
  1487. while (ctx.polling)
  1488. poller_run (&ctx.poller);
  1489. bot_context_free (&ctx);
  1490. str_vector_free (&g_original_argv);
  1491. return EXIT_SUCCESS;
  1492. }