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.

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