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.

controller.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. #include <sys/stat.h>
  2. #include <sys/param.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include <errno.h>
  10. #include <signal.h>
  11. #include <netinet/in.h>
  12. #include <syslog.h>
  13. #include <fcntl.h>
  14. #include <netdb.h>
  15. #include <glib.h>
  16. #include "util.h"
  17. #include "main.h"
  18. #include "protocol.h"
  19. #include "upstream.h"
  20. #include "cfg_file.h"
  21. #include "modules.h"
  22. #include "tokenizers/tokenizers.h"
  23. #define CRLF "\r\n"
  24. enum command_type {
  25. COMMAND_PASSWORD,
  26. COMMAND_QUIT,
  27. COMMAND_RELOAD,
  28. COMMAND_STAT,
  29. COMMAND_SHUTDOWN,
  30. COMMAND_UPTIME,
  31. COMMAND_LEARN,
  32. };
  33. struct controller_command {
  34. char *command;
  35. int privilleged;
  36. enum command_type type;
  37. };
  38. static struct controller_command commands[] = {
  39. {"password", 0, COMMAND_PASSWORD},
  40. {"quit", 0, COMMAND_QUIT},
  41. {"reload", 1, COMMAND_RELOAD},
  42. {"stat", 0, COMMAND_STAT},
  43. {"shutdown", 1, COMMAND_SHUTDOWN},
  44. {"uptime", 0, COMMAND_UPTIME},
  45. {"learn", 1, COMMAND_LEARN},
  46. };
  47. static GCompletion *comp;
  48. static time_t start_time;
  49. static
  50. void sig_handler (int signo)
  51. {
  52. switch (signo) {
  53. case SIGINT:
  54. case SIGTERM:
  55. _exit (1);
  56. break;
  57. }
  58. }
  59. static void
  60. sigusr_handler (int fd, short what, void *arg)
  61. {
  62. struct rspamd_worker *worker = (struct rspamd_worker *)arg;
  63. /* Do not accept new connections, preparing to end worker's process */
  64. struct timeval tv;
  65. tv.tv_sec = SOFT_SHUTDOWN_TIME;
  66. tv.tv_usec = 0;
  67. event_del (&worker->sig_ev);
  68. event_del (&worker->bind_ev);
  69. msg_info ("controller's shutdown is pending in %d sec", SOFT_SHUTDOWN_TIME);
  70. event_loopexit (&tv);
  71. return;
  72. }
  73. static gchar*
  74. completion_func (gpointer elem)
  75. {
  76. struct controller_command *cmd = (struct controller_command *)elem;
  77. return cmd->command;
  78. }
  79. static void
  80. free_session (struct controller_session *session)
  81. {
  82. bufferevent_disable (session->bev, EV_READ | EV_WRITE);
  83. memory_pool_delete (session->session_pool);
  84. g_free (session);
  85. }
  86. static int
  87. check_auth (struct controller_command *cmd, struct controller_session *session)
  88. {
  89. char out_buf[128];
  90. int r;
  91. if (cmd->privilleged && !session->authorized) {
  92. r = snprintf (out_buf, sizeof (out_buf), "not authorized" CRLF);
  93. bufferevent_write (session->bev, out_buf, r);
  94. return 0;
  95. }
  96. return 1;
  97. }
  98. static void
  99. process_command (struct controller_command *cmd, char **cmd_args, struct controller_session *session)
  100. {
  101. char out_buf[512], *arg, *err_str;
  102. int r = 0, days, hours, minutes;
  103. time_t uptime;
  104. unsigned long size = 0;
  105. struct statfile *statfile;
  106. switch (cmd->type) {
  107. case COMMAND_PASSWORD:
  108. arg = *cmd_args;
  109. if (!arg || *arg == '\0') {
  110. msg_debug ("process_command: empty password passed");
  111. r = snprintf (out_buf, sizeof (out_buf), "password command requires one argument" CRLF);
  112. bufferevent_write (session->bev, out_buf, r);
  113. return;
  114. }
  115. if (strncmp (arg, session->cfg->control_password, strlen (arg)) == 0) {
  116. session->authorized = 1;
  117. r = snprintf (out_buf, sizeof (out_buf), "password accepted" CRLF);
  118. bufferevent_write (session->bev, out_buf, r);
  119. }
  120. else {
  121. session->authorized = 0;
  122. r = snprintf (out_buf, sizeof (out_buf), "password NOT accepted" CRLF);
  123. bufferevent_write (session->bev, out_buf, r);
  124. }
  125. break;
  126. case COMMAND_QUIT:
  127. free_session (session);
  128. break;
  129. case COMMAND_RELOAD:
  130. if (check_auth (cmd, session)) {
  131. r = snprintf (out_buf, sizeof (out_buf), "reload request sent" CRLF);
  132. bufferevent_write (session->bev, out_buf, r);
  133. kill (getppid (), SIGHUP);
  134. }
  135. break;
  136. case COMMAND_STAT:
  137. /* XXX need to implement stat */
  138. if (check_auth (cmd, session)) {
  139. r = snprintf (out_buf, sizeof (out_buf), "-- end of stats report" CRLF);
  140. bufferevent_write (session->bev, out_buf, r);
  141. }
  142. case COMMAND_SHUTDOWN:
  143. if (check_auth (cmd, session)) {
  144. r = snprintf (out_buf, sizeof (out_buf), "shutdown request sent" CRLF);
  145. bufferevent_write (session->bev, out_buf, r);
  146. kill (getppid (), SIGTERM);
  147. }
  148. break;
  149. case COMMAND_UPTIME:
  150. if (check_auth (cmd, session)) {
  151. uptime = time (NULL) - start_time;
  152. /* If uptime more than 2 hours, print as a number of days. */
  153. if (uptime >= 2 * 3600) {
  154. days = uptime / 86400;
  155. hours = (uptime % 3600) / 60;
  156. minutes = (uptime % 60) / 60;
  157. r = snprintf (out_buf, sizeof (out_buf), "%dday%s %dhour%s %dminute%s" CRLF,
  158. days, days > 1 ? "s" : " ",
  159. hours, hours > 1 ? "s" : " ",
  160. minutes, minutes > 1 ? "s" : " ");
  161. }
  162. /* If uptime is less than 1 minute print only seconds */
  163. else if (uptime / 60 == 0) {
  164. r = snprintf (out_buf, sizeof (out_buf), "%dsecond%s", uptime, uptime > 1 ? "s" : " ");
  165. }
  166. /* Else print the minutes and seconds. */
  167. else {
  168. hours = uptime / 3600;
  169. minutes = (uptime % 60) / 60;
  170. r = snprintf (out_buf, sizeof (out_buf), "%dhour%s %dminite%s %dsecond%s",
  171. hours, hours > 1 ? "s" : " ",
  172. minutes, minutes > 1 ? "s" : " ",
  173. (int)uptime, uptime > 1 ? "s" : " ");
  174. }
  175. bufferevent_write (session->bev, out_buf, r);
  176. }
  177. break;
  178. case COMMAND_LEARN:
  179. if (check_auth (cmd, session)) {
  180. arg = *cmd_args++;
  181. if (!arg || *arg == '\0') {
  182. msg_debug ("process_command: no statfile specified in learn command");
  183. r = snprintf (out_buf, sizeof (out_buf), "learn command requires at least two arguments: stat filename and its size" CRLF);
  184. bufferevent_write (session->bev, out_buf, r);
  185. return;
  186. }
  187. arg = *cmd_args;
  188. if (arg == NULL || *arg == '\0') {
  189. msg_debug ("process_command: no statfile size specified in learn command");
  190. r = snprintf (out_buf, sizeof (out_buf), "learn command requires at least two arguments: stat filename and its size" CRLF);
  191. bufferevent_write (session->bev, out_buf, r);
  192. return;
  193. }
  194. size = strtoul (arg, &err_str, 10);
  195. if (err_str && *err_str != '\0') {
  196. msg_debug ("process_command: statfile size is invalid: %s", arg);
  197. r = snprintf (out_buf, sizeof (out_buf), "learn size is invalid" CRLF);
  198. bufferevent_write (session->bev, out_buf, r);
  199. return;
  200. }
  201. session->learn_buf = memory_pool_alloc (session->session_pool, sizeof (f_str_buf_t));
  202. session->learn_buf->buf = fstralloc (session->session_pool, size);
  203. if (session->learn_buf->buf == NULL) {
  204. r = snprintf (out_buf, sizeof (out_buf), "allocating buffer for learn failed" CRLF);
  205. bufferevent_write (session->bev, out_buf, r);
  206. return;
  207. }
  208. statfile = g_hash_table_lookup (session->cfg->statfiles, arg);
  209. if (statfile == NULL) {
  210. r = snprintf (out_buf, sizeof (out_buf), "statfile %s is not defined" CRLF, arg);
  211. bufferevent_write (session->bev, out_buf, r);
  212. return;
  213. }
  214. session->learn_rcpt = NULL;
  215. session->learn_from = NULL;
  216. session->learn_filename = NULL;
  217. session->learn_tokenizer = get_tokenizer ("osb-text");
  218. /* Get all arguments */
  219. while (*cmd_args++) {
  220. arg = *cmd_args;
  221. if (*arg == '-') {
  222. switch (*(arg + 1)) {
  223. case 'r':
  224. arg = *(cmd_args + 1);
  225. if (!arg || *arg == '\0') {
  226. r = snprintf (out_buf, sizeof (out_buf), "recipient is not defined" CRLF, arg);
  227. bufferevent_write (session->bev, out_buf, r);
  228. return;
  229. }
  230. session->learn_rcpt = memory_pool_strdup (session->session_pool, arg);
  231. break;
  232. case 'f':
  233. arg = *(cmd_args + 1);
  234. if (!arg || *arg == '\0') {
  235. r = snprintf (out_buf, sizeof (out_buf), "from is not defined" CRLF, arg);
  236. bufferevent_write (session->bev, out_buf, r);
  237. return;
  238. }
  239. session->learn_from = memory_pool_strdup (session->session_pool, arg);
  240. break;
  241. case 't':
  242. arg = *(cmd_args + 1);
  243. if (!arg || *arg == '\0' || (session->learn_tokenizer = get_tokenizer (arg)) == NULL) {
  244. r = snprintf (out_buf, sizeof (out_buf), "tokenizer is not defined" CRLF, arg);
  245. bufferevent_write (session->bev, out_buf, r);
  246. return;
  247. }
  248. break;
  249. }
  250. }
  251. }
  252. session->learn_filename = resolve_stat_filename (session->session_pool, statfile->pattern, session->learn_rcpt, session->learn_from);
  253. if (statfile_pool_open (session->worker->srv->statfile_pool, session->learn_filename) == -1) {
  254. /* Try to create statfile */
  255. if (statfile_pool_create (session->worker->srv->statfile_pool, session->learn_filename, statfile->size) == -1) {
  256. r = snprintf (out_buf, sizeof (out_buf), "cannot create statfile %s" CRLF, session->learn_filename);
  257. bufferevent_write (session->bev, out_buf, r);
  258. return;
  259. }
  260. if (statfile_pool_open (session->worker->srv->statfile_pool, session->learn_filename) == -1) {
  261. r = snprintf (out_buf, sizeof (out_buf), "cannot open statfile %s" CRLF, session->learn_filename);
  262. bufferevent_write (session->bev, out_buf, r);
  263. return;
  264. }
  265. }
  266. session->state = STATE_LEARN;
  267. r = snprintf (out_buf, sizeof (out_buf), "ok" CRLF);
  268. bufferevent_write (session->bev, out_buf, r);
  269. break;
  270. }
  271. }
  272. }
  273. static void
  274. read_socket (struct bufferevent *bev, void *arg)
  275. {
  276. struct controller_session *session = (struct controller_session *)arg;
  277. int len, i;
  278. char *s, **params, *cmd, out_buf[128];
  279. GList *comp_list;
  280. switch (session->state) {
  281. case STATE_COMMAND:
  282. s = evbuffer_readline (EVBUFFER_INPUT (bev));
  283. if (s != NULL && *s != 0) {
  284. len = strlen (s);
  285. /* Remove end of line characters from string */
  286. if (s[len - 1] == '\n') {
  287. if (s[len - 2] == '\r') {
  288. s[len - 2] = 0;
  289. }
  290. s[len - 1] = 0;
  291. }
  292. params = g_strsplit (s, " ", -1);
  293. len = g_strv_length (params);
  294. if (len > 0) {
  295. cmd = g_strstrip (params[0]);
  296. comp_list = g_completion_complete (comp, cmd, NULL);
  297. switch (g_list_length (comp_list)) {
  298. case 1:
  299. process_command ((struct controller_command *)comp_list->data, &params[1], session);
  300. break;
  301. case 0:
  302. i = snprintf (out_buf, sizeof (out_buf), "Unknown command" CRLF);
  303. bufferevent_write (bev, out_buf, i);
  304. break;
  305. default:
  306. i = snprintf (out_buf, sizeof (out_buf), "Ambigious command" CRLF);
  307. bufferevent_write (bev, out_buf, i);
  308. break;
  309. }
  310. }
  311. g_strfreev (params);
  312. }
  313. if (s != NULL) {
  314. free (s);
  315. }
  316. break;
  317. case STATE_LEARN:
  318. i = bufferevent_read (bev, session->learn_buf->pos, session->learn_buf->free);
  319. if (i > 0) {
  320. session->learn_buf->pos += i;
  321. update_buf_size (session->learn_buf);
  322. if (session->learn_buf->free == 0) {
  323. /* XXX: require to insert real learning code here */
  324. session->worker->srv->stat->messages_learned ++;
  325. i = snprintf (out_buf, sizeof (out_buf), "learn ok" CRLF);
  326. bufferevent_write (bev, out_buf, i);
  327. session->state = STATE_COMMAND;
  328. break;
  329. }
  330. }
  331. else {
  332. i = snprintf (out_buf, sizeof (out_buf), "read error: %d" CRLF, i);
  333. bufferevent_write (bev, out_buf, i);
  334. bufferevent_disable (bev, EV_READ);
  335. free_session (session);
  336. }
  337. break;
  338. }
  339. }
  340. static void
  341. write_socket (struct bufferevent *bev, void *arg)
  342. {
  343. char buf[1024], hostbuf[256];
  344. gethostname (hostbuf, sizeof (hostbuf - 1));
  345. hostbuf[sizeof (hostbuf) - 1] = '\0';
  346. snprintf (buf, sizeof (buf), "Rspamd version %s is running on %s" CRLF, RVERSION, hostbuf);
  347. bufferevent_disable (bev, EV_WRITE);
  348. bufferevent_enable (bev, EV_READ);
  349. }
  350. static void
  351. err_socket (struct bufferevent *bev, short what, void *arg)
  352. {
  353. struct controller_session *session = (struct controller_session *)arg;
  354. msg_info ("closing control connection");
  355. /* Free buffers */
  356. free_session (session);
  357. }
  358. static void
  359. accept_socket (int fd, short what, void *arg)
  360. {
  361. struct rspamd_worker *worker = (struct rspamd_worker *)arg;
  362. struct sockaddr_storage ss;
  363. struct controller_session *new_session;
  364. socklen_t addrlen = sizeof(ss);
  365. int nfd;
  366. if ((nfd = accept (fd, (struct sockaddr *)&ss, &addrlen)) == -1) {
  367. return;
  368. }
  369. if (event_make_socket_nonblocking(fd) < 0) {
  370. return;
  371. }
  372. new_session = g_malloc (sizeof (struct controller_session));
  373. if (new_session == NULL) {
  374. msg_err ("accept_socket: cannot allocate memory for task, %m");
  375. return;
  376. }
  377. bzero (new_session, sizeof (struct controller_session));
  378. new_session->worker = worker;
  379. new_session->sock = nfd;
  380. new_session->cfg = worker->srv->cfg;
  381. new_session->state = STATE_COMMAND;
  382. new_session->session_pool = memory_pool_new (memory_pool_get_size () - 1);
  383. memory_pool_add_destructor (new_session->session_pool, (pool_destruct_func)bufferevent_free, new_session->bev);
  384. worker->srv->stat->control_connections_count ++;
  385. /* Read event */
  386. new_session->bev = bufferevent_new (nfd, read_socket, write_socket, err_socket, (void *)new_session);
  387. bufferevent_enable (new_session->bev, EV_WRITE);
  388. }
  389. void
  390. start_controller (struct rspamd_worker *worker)
  391. {
  392. struct sigaction signals;
  393. int listen_sock, i;
  394. struct sockaddr_un *un_addr;
  395. GList *comp_list = NULL;
  396. worker->srv->pid = getpid ();
  397. worker->srv->type = TYPE_CONTROLLER;
  398. event_init ();
  399. g_mime_init (0);
  400. init_signals (&signals, sig_handler);
  401. sigprocmask (SIG_UNBLOCK, &signals.sa_mask, NULL);
  402. /* SIGUSR2 handler */
  403. signal_set (&worker->sig_ev, SIGUSR2, sigusr_handler, (void *) worker);
  404. signal_add (&worker->sig_ev, NULL);
  405. if (worker->srv->cfg->control_family == AF_INET) {
  406. if ((listen_sock = make_socket (worker->srv->cfg->control_host, worker->srv->cfg->control_port)) == -1) {
  407. msg_err ("start_controller: cannot create tcp listen socket. %m");
  408. exit(-errno);
  409. }
  410. }
  411. else {
  412. un_addr = (struct sockaddr_un *) alloca (sizeof (struct sockaddr_un));
  413. if (!un_addr || (listen_sock = make_unix_socket (worker->srv->cfg->bind_host, un_addr)) == -1) {
  414. msg_err ("start_controller: cannot create unix listen socket. %m");
  415. exit(-errno);
  416. }
  417. }
  418. start_time = time (NULL);
  419. /* Init command completion */
  420. for (i = 0; i < sizeof (commands) / sizeof (commands[0]) - 1; i ++) {
  421. comp_list = g_list_prepend (comp_list, &commands[i]);
  422. }
  423. comp = g_completion_new (completion_func);
  424. g_completion_add_items (comp, comp_list);
  425. /* Accept event */
  426. event_set(&worker->bind_ev, listen_sock, EV_READ | EV_PERSIST, accept_socket, (void *)worker);
  427. event_add(&worker->bind_ev, NULL);
  428. /* Send SIGUSR2 to parent */
  429. kill (getppid (), SIGUSR2);
  430. event_loop (0);
  431. }
  432. /*
  433. * vi:ts=4
  434. */