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.

rspamd_control.c 32KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "config.h"
  17. #include "rspamd.h"
  18. #include "rspamd_control.h"
  19. #include "worker_util.h"
  20. #include "libserver/http/http_connection.h"
  21. #include "libserver/http/http_private.h"
  22. #include "libutil/libev_helper.h"
  23. #include "unix-std.h"
  24. #include "utlist.h"
  25. #ifdef HAVE_SYS_RESOURCE_H
  26. #include <sys/resource.h>
  27. #endif
  28. static ev_tstamp io_timeout = 30.0;
  29. static ev_tstamp worker_io_timeout = 0.5;
  30. struct rspamd_control_session;
  31. struct rspamd_control_reply_elt {
  32. struct rspamd_control_reply reply;
  33. struct rspamd_io_ev ev;
  34. struct ev_loop *event_loop;
  35. GQuark wrk_type;
  36. pid_t wrk_pid;
  37. gpointer ud;
  38. gint attached_fd;
  39. GHashTable *pending_elts;
  40. struct rspamd_control_reply_elt *prev, *next;
  41. };
  42. struct rspamd_control_session {
  43. gint fd;
  44. struct ev_loop *event_loop;
  45. struct rspamd_main *rspamd_main;
  46. struct rspamd_http_connection *conn;
  47. struct rspamd_control_command cmd;
  48. struct rspamd_control_reply_elt *replies;
  49. rspamd_inet_addr_t *addr;
  50. guint replies_remain;
  51. gboolean is_reply;
  52. };
  53. static const struct rspamd_control_cmd_match {
  54. rspamd_ftok_t name;
  55. enum rspamd_control_type type;
  56. } cmd_matches[] = {
  57. {
  58. .name = {
  59. .begin = "/stat",
  60. .len = sizeof ("/stat") - 1
  61. },
  62. .type = RSPAMD_CONTROL_STAT
  63. },
  64. {
  65. .name = {
  66. .begin = "/reload",
  67. .len = sizeof ("/reload") - 1
  68. },
  69. .type = RSPAMD_CONTROL_RELOAD
  70. },
  71. {
  72. .name = {
  73. .begin = "/reresolve",
  74. .len = sizeof ("/reresolve") - 1
  75. },
  76. .type = RSPAMD_CONTROL_RERESOLVE
  77. },
  78. {
  79. .name = {
  80. .begin = "/recompile",
  81. .len = sizeof ("/recompile") - 1
  82. },
  83. .type = RSPAMD_CONTROL_RECOMPILE
  84. },
  85. {
  86. .name = {
  87. .begin = "/fuzzystat",
  88. .len = sizeof ("/fuzzystat") - 1
  89. },
  90. .type = RSPAMD_CONTROL_FUZZY_STAT
  91. },
  92. {
  93. .name = {
  94. .begin = "/fuzzysync",
  95. .len = sizeof ("/fuzzysync") - 1
  96. },
  97. .type = RSPAMD_CONTROL_FUZZY_SYNC
  98. },
  99. };
  100. static void rspamd_control_ignore_io_handler (int fd, short what, void *ud);
  101. static void
  102. rspamd_control_stop_pending (struct rspamd_control_reply_elt *elt)
  103. {
  104. GHashTable *htb;
  105. /* It stops event and frees hash */
  106. htb = elt->pending_elts;
  107. g_hash_table_remove (elt->pending_elts, elt);
  108. /* Release hash reference */
  109. g_hash_table_unref (htb);
  110. }
  111. void
  112. rspamd_control_send_error (struct rspamd_control_session *session,
  113. gint code, const gchar *error_msg, ...)
  114. {
  115. struct rspamd_http_message *msg;
  116. rspamd_fstring_t *reply;
  117. va_list args;
  118. msg = rspamd_http_new_message (HTTP_RESPONSE);
  119. va_start (args, error_msg);
  120. msg->status = rspamd_fstring_new ();
  121. rspamd_vprintf_fstring (&msg->status, error_msg, args);
  122. va_end (args);
  123. msg->date = time (NULL);
  124. msg->code = code;
  125. reply = rspamd_fstring_sized_new (msg->status->len + 16);
  126. rspamd_printf_fstring (&reply, "{\"error\":\"%V\"}", msg->status);
  127. rspamd_http_message_set_body_from_fstring_steal (msg, reply);
  128. rspamd_http_connection_reset (session->conn);
  129. rspamd_http_connection_write_message (session->conn,
  130. msg,
  131. NULL,
  132. "application/json",
  133. session,
  134. io_timeout);
  135. }
  136. static void
  137. rspamd_control_send_ucl (struct rspamd_control_session *session,
  138. ucl_object_t *obj)
  139. {
  140. struct rspamd_http_message *msg;
  141. rspamd_fstring_t *reply;
  142. msg = rspamd_http_new_message (HTTP_RESPONSE);
  143. msg->date = time (NULL);
  144. msg->code = 200;
  145. msg->status = rspamd_fstring_new_init ("OK", 2);
  146. reply = rspamd_fstring_sized_new (BUFSIZ);
  147. rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON_COMPACT, &reply);
  148. rspamd_http_message_set_body_from_fstring_steal (msg, reply);
  149. rspamd_http_connection_reset (session->conn);
  150. rspamd_http_connection_write_message (session->conn,
  151. msg,
  152. NULL,
  153. "application/json",
  154. session,
  155. io_timeout);
  156. }
  157. static void
  158. rspamd_control_connection_close (struct rspamd_control_session *session)
  159. {
  160. struct rspamd_control_reply_elt *elt, *telt;
  161. struct rspamd_main *rspamd_main;
  162. rspamd_main = session->rspamd_main;
  163. msg_info_main ("finished connection from %s",
  164. rspamd_inet_address_to_string (session->addr));
  165. DL_FOREACH_SAFE (session->replies, elt, telt) {
  166. rspamd_control_stop_pending (elt);
  167. }
  168. rspamd_inet_address_free (session->addr);
  169. rspamd_http_connection_unref (session->conn);
  170. close (session->fd);
  171. g_free (session);
  172. }
  173. static void
  174. rspamd_control_write_reply (struct rspamd_control_session *session)
  175. {
  176. ucl_object_t *rep, *cur, *workers;
  177. struct rspamd_control_reply_elt *elt;
  178. gchar tmpbuf[64];
  179. gdouble total_utime = 0, total_systime = 0;
  180. struct ucl_parser *parser;
  181. guint total_conns = 0;
  182. rep = ucl_object_typed_new (UCL_OBJECT);
  183. workers = ucl_object_typed_new (UCL_OBJECT);
  184. DL_FOREACH (session->replies, elt) {
  185. /* Skip incompatible worker for fuzzy_stat */
  186. if ((session->cmd.type == RSPAMD_CONTROL_FUZZY_STAT ||
  187. session->cmd.type == RSPAMD_CONTROL_FUZZY_SYNC) &&
  188. elt->wrk_type != g_quark_from_static_string ("fuzzy")) {
  189. continue;
  190. }
  191. rspamd_snprintf (tmpbuf, sizeof (tmpbuf), "%P", elt->wrk_pid);
  192. cur = ucl_object_typed_new (UCL_OBJECT);
  193. ucl_object_insert_key (cur, ucl_object_fromstring (g_quark_to_string (
  194. elt->wrk_type)), "type", 0, false);
  195. switch (session->cmd.type) {
  196. case RSPAMD_CONTROL_STAT:
  197. ucl_object_insert_key (cur, ucl_object_fromint (
  198. elt->reply.reply.stat.conns), "conns", 0, false);
  199. ucl_object_insert_key (cur, ucl_object_fromdouble (
  200. elt->reply.reply.stat.utime), "utime", 0, false);
  201. ucl_object_insert_key (cur, ucl_object_fromdouble (
  202. elt->reply.reply.stat.systime), "systime", 0, false);
  203. ucl_object_insert_key (cur, ucl_object_fromdouble (
  204. elt->reply.reply.stat.uptime), "uptime", 0, false);
  205. ucl_object_insert_key (cur, ucl_object_fromint (
  206. elt->reply.reply.stat.maxrss), "maxrss", 0, false);
  207. total_utime += elt->reply.reply.stat.utime;
  208. total_systime += elt->reply.reply.stat.systime;
  209. total_conns += elt->reply.reply.stat.conns;
  210. break;
  211. case RSPAMD_CONTROL_RELOAD:
  212. ucl_object_insert_key (cur, ucl_object_fromint (
  213. elt->reply.reply.reload.status), "status", 0, false);
  214. break;
  215. case RSPAMD_CONTROL_RECOMPILE:
  216. ucl_object_insert_key (cur, ucl_object_fromint (
  217. elt->reply.reply.recompile.status), "status", 0, false);
  218. break;
  219. case RSPAMD_CONTROL_RERESOLVE:
  220. ucl_object_insert_key (cur, ucl_object_fromint (
  221. elt->reply.reply.reresolve.status), "status", 0, false);
  222. break;
  223. case RSPAMD_CONTROL_FUZZY_STAT:
  224. if (elt->attached_fd != -1) {
  225. /* We have some data to parse */
  226. parser = ucl_parser_new (0);
  227. ucl_object_insert_key (cur,
  228. ucl_object_fromint (
  229. elt->reply.reply.fuzzy_stat.status),
  230. "status",
  231. 0,
  232. false);
  233. if (ucl_parser_add_fd (parser, elt->attached_fd)) {
  234. ucl_object_insert_key (cur, ucl_parser_get_object (parser),
  235. "data", 0, false);
  236. ucl_parser_free (parser);
  237. }
  238. else {
  239. ucl_object_insert_key (cur, ucl_object_fromstring (
  240. ucl_parser_get_error (parser)), "error", 0, false);
  241. ucl_parser_free (parser);
  242. }
  243. ucl_object_insert_key (cur,
  244. ucl_object_fromlstring (
  245. elt->reply.reply.fuzzy_stat.storage_id,
  246. MEMPOOL_UID_LEN - 1),
  247. "id",
  248. 0,
  249. false);
  250. }
  251. else {
  252. ucl_object_insert_key (cur,
  253. ucl_object_fromstring ("missing file"),
  254. "error",
  255. 0,
  256. false);
  257. ucl_object_insert_key (cur,
  258. ucl_object_fromint (
  259. elt->reply.reply.fuzzy_stat.status),
  260. "status",
  261. 0,
  262. false);
  263. }
  264. break;
  265. case RSPAMD_CONTROL_FUZZY_SYNC:
  266. ucl_object_insert_key (cur, ucl_object_fromint (
  267. elt->reply.reply.fuzzy_sync.status), "status", 0, false);
  268. break;
  269. default:
  270. break;
  271. }
  272. if (elt->attached_fd != -1) {
  273. close (elt->attached_fd);
  274. elt->attached_fd = -1;
  275. }
  276. ucl_object_insert_key (workers, cur, tmpbuf, 0, true);
  277. }
  278. ucl_object_insert_key (rep, workers, "workers", 0, false);
  279. if (session->cmd.type == RSPAMD_CONTROL_STAT) {
  280. /* Total stats */
  281. cur = ucl_object_typed_new (UCL_OBJECT);
  282. ucl_object_insert_key (cur, ucl_object_fromint (
  283. total_conns), "conns", 0, false);
  284. ucl_object_insert_key (cur, ucl_object_fromdouble (
  285. total_utime), "utime", 0, false);
  286. ucl_object_insert_key (cur, ucl_object_fromdouble (
  287. total_systime), "systime", 0, false);
  288. ucl_object_insert_key (rep, cur, "total", 0, false);
  289. }
  290. rspamd_control_send_ucl (session, rep);
  291. ucl_object_unref (rep);
  292. }
  293. static void
  294. rspamd_control_wrk_io (gint fd, short what, gpointer ud)
  295. {
  296. struct rspamd_control_reply_elt *elt = ud;
  297. struct rspamd_control_session *session;
  298. guchar fdspace[CMSG_SPACE(sizeof (int))];
  299. struct iovec iov;
  300. struct msghdr msg;
  301. gssize r;
  302. session = elt->ud;
  303. elt->attached_fd = -1;
  304. if (what == EV_READ) {
  305. iov.iov_base = &elt->reply;
  306. iov.iov_len = sizeof (elt->reply);
  307. memset (&msg, 0, sizeof (msg));
  308. msg.msg_control = fdspace;
  309. msg.msg_controllen = sizeof (fdspace);
  310. msg.msg_iov = &iov;
  311. msg.msg_iovlen = 1;
  312. r = recvmsg (fd, &msg, 0);
  313. if (r == -1) {
  314. msg_err ("cannot read reply from the worker %P (%s): %s",
  315. elt->wrk_pid, g_quark_to_string (elt->wrk_type),
  316. strerror (errno));
  317. }
  318. else if (r >= (gssize)sizeof (elt->reply)) {
  319. if (msg.msg_controllen >= CMSG_LEN (sizeof (int))) {
  320. elt->attached_fd = *(int *) CMSG_DATA(CMSG_FIRSTHDR (&msg));
  321. }
  322. }
  323. }
  324. else {
  325. /* Timeout waiting */
  326. msg_warn ("timeout waiting reply from %P (%s)",
  327. elt->wrk_pid, g_quark_to_string (elt->wrk_type));
  328. }
  329. session->replies_remain --;
  330. rspamd_ev_watcher_stop (session->event_loop,
  331. &elt->ev);
  332. if (session->replies_remain == 0) {
  333. rspamd_control_write_reply (session);
  334. }
  335. }
  336. static void
  337. rspamd_control_error_handler (struct rspamd_http_connection *conn, GError *err)
  338. {
  339. struct rspamd_control_session *session = conn->ud;
  340. struct rspamd_main *rspamd_main;
  341. rspamd_main = session->rspamd_main;
  342. if (!session->is_reply) {
  343. msg_info_main ("abnormally closing control connection: %e", err);
  344. session->is_reply = TRUE;
  345. rspamd_control_send_error (session, err->code, "%s", err->message);
  346. }
  347. else {
  348. rspamd_control_connection_close (session);
  349. }
  350. }
  351. void
  352. rspamd_pending_control_free (gpointer p)
  353. {
  354. struct rspamd_control_reply_elt *rep_elt = (struct rspamd_control_reply_elt *)p;
  355. rspamd_ev_watcher_stop (rep_elt->event_loop, &rep_elt->ev);
  356. g_free (rep_elt);
  357. }
  358. static struct rspamd_control_reply_elt *
  359. rspamd_control_broadcast_cmd (struct rspamd_main *rspamd_main,
  360. struct rspamd_control_command *cmd,
  361. gint attached_fd,
  362. rspamd_ev_cb handler,
  363. gpointer ud,
  364. pid_t except_pid)
  365. {
  366. GHashTableIter it;
  367. struct rspamd_worker *wrk;
  368. struct rspamd_control_reply_elt *rep_elt, *res = NULL;
  369. gpointer k, v;
  370. struct msghdr msg;
  371. struct cmsghdr *cmsg;
  372. struct iovec iov;
  373. guchar fdspace[CMSG_SPACE(sizeof (int))];
  374. gssize r;
  375. g_hash_table_iter_init (&it, rspamd_main->workers);
  376. while (g_hash_table_iter_next (&it, &k, &v)) {
  377. wrk = v;
  378. /* No control pipe */
  379. if (wrk->control_pipe[0] == -1) {
  380. continue;
  381. }
  382. if (except_pid != 0 && wrk->pid == except_pid) {
  383. continue;
  384. }
  385. /* Worker is terminating, do not bother sending stuff */
  386. if (wrk->state == rspamd_worker_state_terminating) {
  387. continue;
  388. }
  389. memset (&msg, 0, sizeof (msg));
  390. /* Attach fd to the message */
  391. if (attached_fd != -1) {
  392. memset (fdspace, 0, sizeof (fdspace));
  393. msg.msg_control = fdspace;
  394. msg.msg_controllen = sizeof (fdspace);
  395. cmsg = CMSG_FIRSTHDR (&msg);
  396. cmsg->cmsg_level = SOL_SOCKET;
  397. cmsg->cmsg_type = SCM_RIGHTS;
  398. cmsg->cmsg_len = CMSG_LEN (sizeof (int));
  399. memcpy (CMSG_DATA (cmsg), &attached_fd, sizeof (int));
  400. }
  401. iov.iov_base = cmd;
  402. iov.iov_len = sizeof (*cmd);
  403. msg.msg_iov = &iov;
  404. msg.msg_iovlen = 1;
  405. r = sendmsg (wrk->control_pipe[0], &msg, 0);
  406. if (r == sizeof (*cmd)) {
  407. rep_elt = g_malloc0 (sizeof (*rep_elt));
  408. rep_elt->wrk_pid = wrk->pid;
  409. rep_elt->wrk_type = wrk->type;
  410. rep_elt->event_loop = rspamd_main->event_loop;
  411. rep_elt->ud = ud;
  412. rep_elt->pending_elts = g_hash_table_ref (wrk->control_events_pending);
  413. rspamd_ev_watcher_init (&rep_elt->ev,
  414. wrk->control_pipe[0],
  415. EV_READ, handler,
  416. rep_elt);
  417. rspamd_ev_watcher_start (rspamd_main->event_loop,
  418. &rep_elt->ev, worker_io_timeout);
  419. g_hash_table_insert (wrk->control_events_pending, rep_elt, rep_elt);
  420. DL_APPEND (res, rep_elt);
  421. }
  422. else {
  423. msg_err_main ("cannot write command %d(%z) to the worker %P(%s), fd: %d: %s",
  424. (int)cmd->type, iov.iov_len,
  425. wrk->pid,
  426. g_quark_to_string (wrk->type),
  427. wrk->control_pipe[0],
  428. strerror (errno));
  429. }
  430. }
  431. return res;
  432. }
  433. void
  434. rspamd_control_broadcast_srv_cmd (struct rspamd_main *rspamd_main,
  435. struct rspamd_control_command *cmd,
  436. pid_t except_pid)
  437. {
  438. rspamd_control_broadcast_cmd (rspamd_main, cmd, -1,
  439. rspamd_control_ignore_io_handler, NULL, except_pid);
  440. }
  441. static gint
  442. rspamd_control_finish_handler (struct rspamd_http_connection *conn,
  443. struct rspamd_http_message *msg)
  444. {
  445. struct rspamd_control_session *session = conn->ud;
  446. rspamd_ftok_t srch;
  447. guint i;
  448. gboolean found = FALSE;
  449. struct rspamd_control_reply_elt *cur;
  450. if (!session->is_reply) {
  451. if (msg->url == NULL) {
  452. rspamd_control_connection_close (session);
  453. return 0;
  454. }
  455. srch.begin = msg->url->str;
  456. srch.len = msg->url->len;
  457. session->is_reply = TRUE;
  458. for (i = 0; i < G_N_ELEMENTS (cmd_matches); i++) {
  459. if (rspamd_ftok_casecmp (&srch, &cmd_matches[i].name) == 0) {
  460. session->cmd.type = cmd_matches[i].type;
  461. found = TRUE;
  462. break;
  463. }
  464. }
  465. if (!found) {
  466. rspamd_control_send_error (session, 404, "Command not defined");
  467. }
  468. else {
  469. /* Send command to all workers */
  470. session->replies = rspamd_control_broadcast_cmd (
  471. session->rspamd_main, &session->cmd, -1,
  472. rspamd_control_wrk_io, session, 0);
  473. DL_FOREACH (session->replies, cur) {
  474. session->replies_remain ++;
  475. }
  476. }
  477. }
  478. else {
  479. rspamd_control_connection_close (session);
  480. }
  481. return 0;
  482. }
  483. void
  484. rspamd_control_process_client_socket (struct rspamd_main *rspamd_main,
  485. gint fd, rspamd_inet_addr_t *addr)
  486. {
  487. struct rspamd_control_session *session;
  488. session = g_malloc0 (sizeof (*session));
  489. session->fd = fd;
  490. session->conn = rspamd_http_connection_new_server (rspamd_main->http_ctx,
  491. fd,
  492. NULL,
  493. rspamd_control_error_handler,
  494. rspamd_control_finish_handler,
  495. 0);
  496. session->rspamd_main = rspamd_main;
  497. session->addr = addr;
  498. session->event_loop = rspamd_main->event_loop;
  499. rspamd_http_connection_read_message (session->conn, session,
  500. io_timeout);
  501. }
  502. struct rspamd_worker_control_data {
  503. ev_io io_ev;
  504. struct rspamd_worker *worker;
  505. struct ev_loop *ev_base;
  506. struct {
  507. rspamd_worker_control_handler handler;
  508. gpointer ud;
  509. } handlers[RSPAMD_CONTROL_MAX];
  510. };
  511. static void
  512. rspamd_control_default_cmd_handler (gint fd,
  513. gint attached_fd,
  514. struct rspamd_worker_control_data *cd,
  515. struct rspamd_control_command *cmd)
  516. {
  517. struct rspamd_control_reply rep;
  518. gssize r;
  519. struct rusage rusg;
  520. struct rspamd_config *cfg;
  521. struct rspamd_main *rspamd_main;
  522. memset (&rep, 0, sizeof (rep));
  523. rep.type = cmd->type;
  524. rspamd_main = cd->worker->srv;
  525. switch (cmd->type) {
  526. case RSPAMD_CONTROL_STAT:
  527. if (getrusage (RUSAGE_SELF, &rusg) == -1) {
  528. msg_err_main ("cannot get rusage stats: %s",
  529. strerror (errno));
  530. }
  531. else {
  532. rep.reply.stat.utime = tv_to_double (&rusg.ru_utime);
  533. rep.reply.stat.systime = tv_to_double (&rusg.ru_stime);
  534. rep.reply.stat.maxrss = rusg.ru_maxrss;
  535. }
  536. rep.reply.stat.conns = cd->worker->nconns;
  537. rep.reply.stat.uptime = rspamd_get_calendar_ticks () - cd->worker->start_time;
  538. break;
  539. case RSPAMD_CONTROL_RELOAD:
  540. case RSPAMD_CONTROL_RECOMPILE:
  541. case RSPAMD_CONTROL_HYPERSCAN_LOADED:
  542. case RSPAMD_CONTROL_MONITORED_CHANGE:
  543. case RSPAMD_CONTROL_FUZZY_STAT:
  544. case RSPAMD_CONTROL_FUZZY_SYNC:
  545. case RSPAMD_CONTROL_LOG_PIPE:
  546. case RSPAMD_CONTROL_CHILD_CHANGE:
  547. break;
  548. case RSPAMD_CONTROL_RERESOLVE:
  549. if (cd->worker->srv->cfg) {
  550. REF_RETAIN (cd->worker->srv->cfg);
  551. cfg = cd->worker->srv->cfg;
  552. if (cfg->ups_ctx) {
  553. msg_info_config ("reresolving upstreams");
  554. rspamd_upstream_reresolve (cfg->ups_ctx);
  555. }
  556. rep.reply.reresolve.status = 0;
  557. REF_RELEASE (cfg);
  558. }
  559. else {
  560. rep.reply.reresolve.status = EINVAL;
  561. }
  562. break;
  563. default:
  564. break;
  565. }
  566. r = write (fd, &rep, sizeof (rep));
  567. if (r != sizeof (rep)) {
  568. msg_err_main ("cannot write reply to the control socket: %s",
  569. strerror (errno));
  570. }
  571. if (attached_fd != -1) {
  572. close (attached_fd);
  573. }
  574. }
  575. static void
  576. rspamd_control_default_worker_handler (EV_P_ ev_io *w, int revents)
  577. {
  578. struct rspamd_worker_control_data *cd =
  579. (struct rspamd_worker_control_data *)w->data;
  580. static struct rspamd_control_command cmd;
  581. static struct msghdr msg;
  582. static struct iovec iov;
  583. static guchar fdspace[CMSG_SPACE(sizeof (int))];
  584. gint rfd = -1;
  585. gssize r;
  586. iov.iov_base = &cmd;
  587. iov.iov_len = sizeof (cmd);
  588. memset (&msg, 0, sizeof (msg));
  589. msg.msg_control = fdspace;
  590. msg.msg_controllen = sizeof (fdspace);
  591. msg.msg_iov = &iov;
  592. msg.msg_iovlen = 1;
  593. r = recvmsg (w->fd, &msg, 0);
  594. if (r == -1) {
  595. if (errno != EAGAIN && errno != EINTR) {
  596. if (errno != ECONNRESET) {
  597. /*
  598. * In case of connection reset it means that main process
  599. * has died, so do not pollute logs
  600. */
  601. msg_err ("cannot read request from the control socket: %s",
  602. strerror (errno));
  603. }
  604. ev_io_stop (cd->ev_base, &cd->io_ev);
  605. close (w->fd);
  606. }
  607. }
  608. else if (r < (gint)sizeof (cmd)) {
  609. msg_err ("short read of control command: %d of %d", (gint)r,
  610. (gint)sizeof (cmd));
  611. if (r == 0) {
  612. ev_io_stop (cd->ev_base, &cd->io_ev);
  613. close (w->fd);
  614. }
  615. }
  616. else if ((gint)cmd.type >= 0 && cmd.type < RSPAMD_CONTROL_MAX) {
  617. if (msg.msg_controllen >= CMSG_LEN (sizeof (int))) {
  618. rfd = *(int *) CMSG_DATA(CMSG_FIRSTHDR (&msg));
  619. }
  620. if (cd->handlers[cmd.type].handler) {
  621. cd->handlers[cmd.type].handler (cd->worker->srv,
  622. cd->worker,
  623. w->fd,
  624. rfd,
  625. &cmd,
  626. cd->handlers[cmd.type].ud);
  627. }
  628. else {
  629. rspamd_control_default_cmd_handler (w->fd, rfd, cd, &cmd);
  630. }
  631. }
  632. else {
  633. msg_err ("unknown command: %d", (gint)cmd.type);
  634. }
  635. }
  636. void
  637. rspamd_control_worker_add_default_cmd_handlers (struct rspamd_worker *worker,
  638. struct ev_loop *ev_base)
  639. {
  640. struct rspamd_worker_control_data *cd;
  641. cd = g_malloc0 (sizeof (*cd));
  642. cd->worker = worker;
  643. cd->ev_base = ev_base;
  644. cd->io_ev.data = cd;
  645. ev_io_init (&cd->io_ev, rspamd_control_default_worker_handler,
  646. worker->control_pipe[1], EV_READ);
  647. ev_io_start (ev_base, &cd->io_ev);
  648. worker->control_data = cd;
  649. }
  650. /**
  651. * Register custom handler for a specific control command for this worker
  652. */
  653. void
  654. rspamd_control_worker_add_cmd_handler (struct rspamd_worker *worker,
  655. enum rspamd_control_type type,
  656. rspamd_worker_control_handler handler,
  657. gpointer ud)
  658. {
  659. struct rspamd_worker_control_data *cd;
  660. g_assert (type >= 0 && type < RSPAMD_CONTROL_MAX);
  661. g_assert (handler != NULL);
  662. g_assert (worker->control_data != NULL);
  663. cd = worker->control_data;
  664. cd->handlers[type].handler = handler;
  665. cd->handlers[type].ud = ud;
  666. }
  667. struct rspamd_srv_reply_data {
  668. struct rspamd_worker *worker;
  669. struct rspamd_main *srv;
  670. gint fd;
  671. struct rspamd_srv_reply rep;
  672. };
  673. static void
  674. rspamd_control_ignore_io_handler (int fd, short what, void *ud)
  675. {
  676. struct rspamd_control_reply_elt *elt =
  677. (struct rspamd_control_reply_elt *)ud;
  678. struct rspamd_control_reply rep;
  679. /* At this point we just ignore replies from the workers */
  680. if (read (fd, &rep, sizeof (rep)) == -1) {
  681. msg_debug("cannot read %d bytes: %s", (int)sizeof(rep), strerror(errno));
  682. }
  683. rspamd_control_stop_pending (elt);
  684. }
  685. static void
  686. rspamd_control_log_pipe_io_handler (int fd, short what, void *ud)
  687. {
  688. struct rspamd_control_reply_elt *elt =
  689. (struct rspamd_control_reply_elt *)ud;
  690. struct rspamd_control_reply rep;
  691. /* At this point we just ignore replies from the workers */
  692. (void) !read (fd, &rep, sizeof (rep));
  693. rspamd_control_stop_pending (elt);
  694. }
  695. static void
  696. rspamd_control_handle_on_fork (struct rspamd_srv_command *cmd,
  697. struct rspamd_main *srv)
  698. {
  699. struct rspamd_worker *parent, *child;
  700. parent = g_hash_table_lookup (srv->workers,
  701. GSIZE_TO_POINTER (cmd->cmd.on_fork.ppid));
  702. if (parent == NULL) {
  703. msg_err ("cannot find parent for a forked process %P (%P child)",
  704. cmd->cmd.on_fork.ppid, cmd->cmd.on_fork.cpid);
  705. return;
  706. }
  707. if (cmd->cmd.on_fork.state == child_dead) {
  708. /* We need to remove stale worker */
  709. child = g_hash_table_lookup (srv->workers,
  710. GSIZE_TO_POINTER (cmd->cmd.on_fork.cpid));
  711. if (child == NULL) {
  712. msg_err ("cannot find child for a forked process %P (%P parent)",
  713. cmd->cmd.on_fork.cpid, cmd->cmd.on_fork.ppid);
  714. return;
  715. }
  716. REF_RELEASE (child->cf);
  717. g_hash_table_remove (srv->workers,
  718. GSIZE_TO_POINTER (cmd->cmd.on_fork.cpid));
  719. g_hash_table_unref (child->control_events_pending);
  720. g_free (child);
  721. }
  722. else {
  723. child = g_malloc0 (sizeof (struct rspamd_worker));
  724. child->srv = srv;
  725. child->type = parent->type;
  726. child->pid = cmd->cmd.on_fork.cpid;
  727. child->srv_pipe[0] = -1;
  728. child->srv_pipe[1] = -1;
  729. child->control_pipe[0] = -1;
  730. child->control_pipe[1] = -1;
  731. child->cf = parent->cf;
  732. child->ppid = parent->pid;
  733. REF_RETAIN (child->cf);
  734. child->control_events_pending = g_hash_table_new_full (g_direct_hash, g_direct_equal,
  735. NULL, rspamd_pending_control_free);
  736. g_hash_table_insert (srv->workers,
  737. GSIZE_TO_POINTER (cmd->cmd.on_fork.cpid), child);
  738. }
  739. }
  740. static void
  741. rspamd_fill_health_reply (struct rspamd_main *srv, struct rspamd_srv_reply *rep)
  742. {
  743. GHashTableIter it;
  744. gpointer k, v;
  745. memset (&rep->reply.health, 0, sizeof (rep->reply));
  746. g_hash_table_iter_init (&it, srv->workers);
  747. while (g_hash_table_iter_next (&it, &k, &v)) {
  748. struct rspamd_worker *wrk = (struct rspamd_worker *)v;
  749. if (wrk->hb.nbeats < 0) {
  750. rep->reply.health.workers_hb_lost ++;
  751. }
  752. else if (rspamd_worker_is_scanner (wrk)) {
  753. rep->reply.health.scanners_count ++;
  754. }
  755. rep->reply.health.workers_count ++;
  756. }
  757. rep->reply.status = (g_hash_table_size (srv->workers) > 0);
  758. }
  759. static void
  760. rspamd_srv_handler (EV_P_ ev_io *w, int revents)
  761. {
  762. struct rspamd_worker *worker;
  763. static struct rspamd_srv_command cmd;
  764. struct rspamd_main *srv;
  765. struct rspamd_srv_reply_data *rdata;
  766. struct msghdr msg;
  767. struct cmsghdr *cmsg;
  768. static struct iovec iov;
  769. static guchar fdspace[CMSG_SPACE(sizeof (int))];
  770. gint *spair, rfd = -1;
  771. gchar *nid;
  772. struct rspamd_control_command wcmd;
  773. gssize r;
  774. if (revents == EV_READ) {
  775. worker = (struct rspamd_worker *)w->data;
  776. srv = worker->srv;
  777. iov.iov_base = &cmd;
  778. iov.iov_len = sizeof (cmd);
  779. memset (&msg, 0, sizeof (msg));
  780. msg.msg_control = fdspace;
  781. msg.msg_controllen = sizeof (fdspace);
  782. msg.msg_iov = &iov;
  783. msg.msg_iovlen = 1;
  784. r = recvmsg (w->fd, &msg, 0);
  785. if (r == -1) {
  786. msg_err ("cannot read from worker's srv pipe: %s",
  787. strerror (errno));
  788. }
  789. else if (r == 0) {
  790. /*
  791. * Usually this means that a worker is dead, so do not try to read
  792. * anything
  793. */
  794. ev_io_stop (EV_A_ w);
  795. }
  796. else if (r != sizeof (cmd)) {
  797. msg_err ("cannot read from worker's srv pipe incomplete command: %d",
  798. (gint) r);
  799. }
  800. else {
  801. rdata = g_malloc0 (sizeof (*rdata));
  802. rdata->worker = worker;
  803. rdata->srv = srv;
  804. rdata->rep.id = cmd.id;
  805. rdata->rep.type = cmd.type;
  806. rdata->fd = -1;
  807. worker->tmp_data = rdata;
  808. if (msg.msg_controllen >= CMSG_LEN (sizeof (int))) {
  809. rfd = *(int *) CMSG_DATA(CMSG_FIRSTHDR (&msg));
  810. }
  811. switch (cmd.type) {
  812. case RSPAMD_SRV_SOCKETPAIR:
  813. spair = g_hash_table_lookup (srv->spairs, cmd.cmd.spair.pair_id);
  814. if (spair == NULL) {
  815. spair = g_malloc (sizeof (gint) * 2);
  816. if (rspamd_socketpair (spair, cmd.cmd.spair.af) == -1) {
  817. rdata->rep.reply.spair.code = errno;
  818. msg_err ("cannot create socket pair: %s", strerror (errno));
  819. }
  820. else {
  821. nid = g_malloc (sizeof (cmd.cmd.spair.pair_id));
  822. memcpy (nid, cmd.cmd.spair.pair_id,
  823. sizeof (cmd.cmd.spair.pair_id));
  824. g_hash_table_insert (srv->spairs, nid, spair);
  825. rdata->rep.reply.spair.code = 0;
  826. rdata->fd = cmd.cmd.spair.pair_num ? spair[1] : spair[0];
  827. }
  828. }
  829. else {
  830. rdata->rep.reply.spair.code = 0;
  831. rdata->fd = cmd.cmd.spair.pair_num ? spair[1] : spair[0];
  832. }
  833. break;
  834. case RSPAMD_SRV_HYPERSCAN_LOADED:
  835. /* Load RE cache to provide it for new forks */
  836. if (rspamd_re_cache_is_hs_loaded (srv->cfg->re_cache) != RSPAMD_HYPERSCAN_LOADED_FULL ||
  837. cmd.cmd.hs_loaded.forced) {
  838. rspamd_re_cache_load_hyperscan (
  839. srv->cfg->re_cache,
  840. cmd.cmd.hs_loaded.cache_dir,
  841. false);
  842. }
  843. /* Broadcast command to all workers */
  844. memset (&wcmd, 0, sizeof (wcmd));
  845. wcmd.type = RSPAMD_CONTROL_HYPERSCAN_LOADED;
  846. rspamd_strlcpy (wcmd.cmd.hs_loaded.cache_dir,
  847. cmd.cmd.hs_loaded.cache_dir,
  848. sizeof (wcmd.cmd.hs_loaded.cache_dir));
  849. wcmd.cmd.hs_loaded.forced = cmd.cmd.hs_loaded.forced;
  850. rspamd_control_broadcast_cmd (srv, &wcmd, rfd,
  851. rspamd_control_ignore_io_handler, NULL, worker->pid);
  852. break;
  853. case RSPAMD_SRV_MONITORED_CHANGE:
  854. /* Broadcast command to all workers */
  855. memset (&wcmd, 0, sizeof (wcmd));
  856. wcmd.type = RSPAMD_CONTROL_MONITORED_CHANGE;
  857. rspamd_strlcpy (wcmd.cmd.monitored_change.tag,
  858. cmd.cmd.monitored_change.tag,
  859. sizeof (wcmd.cmd.monitored_change.tag));
  860. wcmd.cmd.monitored_change.alive = cmd.cmd.monitored_change.alive;
  861. wcmd.cmd.monitored_change.sender = cmd.cmd.monitored_change.sender;
  862. rspamd_control_broadcast_cmd (srv, &wcmd, rfd,
  863. rspamd_control_ignore_io_handler, NULL, 0);
  864. break;
  865. case RSPAMD_SRV_LOG_PIPE:
  866. memset (&wcmd, 0, sizeof (wcmd));
  867. wcmd.type = RSPAMD_CONTROL_LOG_PIPE;
  868. wcmd.cmd.log_pipe.type = cmd.cmd.log_pipe.type;
  869. rspamd_control_broadcast_cmd (srv, &wcmd, rfd,
  870. rspamd_control_log_pipe_io_handler, NULL, 0);
  871. break;
  872. case RSPAMD_SRV_ON_FORK:
  873. rdata->rep.reply.on_fork.status = 0;
  874. rspamd_control_handle_on_fork (&cmd, srv);
  875. break;
  876. case RSPAMD_SRV_HEARTBEAT:
  877. worker->hb.last_event = ev_time ();
  878. rdata->rep.reply.heartbeat.status = 0;
  879. break;
  880. case RSPAMD_SRV_HEALTH:
  881. rspamd_fill_health_reply (srv, &rdata->rep);
  882. break;
  883. default:
  884. msg_err ("unknown command type: %d", cmd.type);
  885. break;
  886. }
  887. if (rfd != -1) {
  888. /* Close our copy to avoid descriptors leak */
  889. close (rfd);
  890. }
  891. /* Now plan write event and send data back */
  892. w->data = rdata;
  893. ev_io_stop (EV_A_ w);
  894. ev_io_set (w, worker->srv_pipe[0], EV_WRITE);
  895. ev_io_start (EV_A_ w);
  896. }
  897. }
  898. else if (revents == EV_WRITE) {
  899. rdata = (struct rspamd_srv_reply_data *)w->data;
  900. worker = rdata->worker;
  901. worker->tmp_data = NULL; /* Avoid race */
  902. srv = rdata->srv;
  903. memset (&msg, 0, sizeof (msg));
  904. /* Attach fd to the message */
  905. if (rdata->fd != -1) {
  906. memset (fdspace, 0, sizeof (fdspace));
  907. msg.msg_control = fdspace;
  908. msg.msg_controllen = sizeof (fdspace);
  909. cmsg = CMSG_FIRSTHDR (&msg);
  910. cmsg->cmsg_level = SOL_SOCKET;
  911. cmsg->cmsg_type = SCM_RIGHTS;
  912. cmsg->cmsg_len = CMSG_LEN (sizeof (int));
  913. memcpy (CMSG_DATA (cmsg), &rdata->fd, sizeof (int));
  914. }
  915. iov.iov_base = &rdata->rep;
  916. iov.iov_len = sizeof (rdata->rep);
  917. msg.msg_iov = &iov;
  918. msg.msg_iovlen = 1;
  919. r = sendmsg (w->fd, &msg, 0);
  920. if (r == -1) {
  921. msg_err ("cannot write to worker's srv pipe: %s",
  922. strerror (errno));
  923. }
  924. g_free (rdata);
  925. w->data = worker;
  926. ev_io_stop (EV_A_ w);
  927. ev_io_set (w, worker->srv_pipe[0], EV_READ);
  928. ev_io_start (EV_A_ w);
  929. }
  930. }
  931. void
  932. rspamd_srv_start_watching (struct rspamd_main *srv,
  933. struct rspamd_worker *worker,
  934. struct ev_loop *ev_base)
  935. {
  936. g_assert (worker != NULL);
  937. worker->tmp_data = NULL;
  938. worker->srv_ev.data = worker;
  939. ev_io_init (&worker->srv_ev, rspamd_srv_handler, worker->srv_pipe[0], EV_READ);
  940. ev_io_start (ev_base, &worker->srv_ev);
  941. }
  942. struct rspamd_srv_request_data {
  943. struct rspamd_worker *worker;
  944. struct rspamd_srv_command cmd;
  945. gint attached_fd;
  946. struct rspamd_srv_reply rep;
  947. rspamd_srv_reply_handler handler;
  948. ev_io io_ev;
  949. gpointer ud;
  950. };
  951. static void
  952. rspamd_srv_request_handler (EV_P_ ev_io *w, int revents)
  953. {
  954. struct rspamd_srv_request_data *rd = (struct rspamd_srv_request_data *)w->data;
  955. struct msghdr msg;
  956. struct iovec iov;
  957. guchar fdspace[CMSG_SPACE(sizeof (int))];
  958. struct cmsghdr *cmsg;
  959. gssize r;
  960. gint rfd = -1;
  961. if (revents == EV_WRITE) {
  962. /* Send request to server */
  963. memset (&msg, 0, sizeof (msg));
  964. /* Attach fd to the message */
  965. if (rd->attached_fd != -1) {
  966. memset (fdspace, 0, sizeof (fdspace));
  967. msg.msg_control = fdspace;
  968. msg.msg_controllen = sizeof (fdspace);
  969. cmsg = CMSG_FIRSTHDR (&msg);
  970. cmsg->cmsg_level = SOL_SOCKET;
  971. cmsg->cmsg_type = SCM_RIGHTS;
  972. cmsg->cmsg_len = CMSG_LEN (sizeof (int));
  973. memcpy (CMSG_DATA (cmsg), &rd->attached_fd, sizeof (int));
  974. }
  975. iov.iov_base = &rd->cmd;
  976. iov.iov_len = sizeof (rd->cmd);
  977. msg.msg_iov = &iov;
  978. msg.msg_iovlen = 1;
  979. r = sendmsg (w->fd, &msg, 0);
  980. if (r == -1) {
  981. msg_err ("cannot write to server pipe: %s", strerror (errno));
  982. goto cleanup;
  983. }
  984. ev_io_stop (EV_A_ w);
  985. ev_io_set (w, rd->worker->srv_pipe[1], EV_READ);
  986. ev_io_start (EV_A_ w);
  987. }
  988. else {
  989. iov.iov_base = &rd->rep;
  990. iov.iov_len = sizeof (rd->rep);
  991. memset (&msg, 0, sizeof (msg));
  992. msg.msg_control = fdspace;
  993. msg.msg_controllen = sizeof (fdspace);
  994. msg.msg_iov = &iov;
  995. msg.msg_iovlen = 1;
  996. r = recvmsg (w->fd, &msg, 0);
  997. if (r == -1) {
  998. msg_err ("cannot read from server pipe: %s", strerror (errno));
  999. goto cleanup;
  1000. }
  1001. if (r < (gint)sizeof (rd->rep)) {
  1002. msg_err ("cannot read from server pipe, invalid length: %d",
  1003. (gint)r);
  1004. goto cleanup;
  1005. }
  1006. if (msg.msg_controllen >= CMSG_LEN (sizeof (int))) {
  1007. rfd = *(int *) CMSG_DATA(CMSG_FIRSTHDR (&msg));
  1008. }
  1009. goto cleanup;
  1010. }
  1011. return;
  1012. cleanup:
  1013. if (rd->handler) {
  1014. rd->handler (rd->worker, &rd->rep, rfd, rd->ud);
  1015. }
  1016. ev_io_stop (EV_A_ w);
  1017. g_free (rd);
  1018. }
  1019. void
  1020. rspamd_srv_send_command (struct rspamd_worker *worker,
  1021. struct ev_loop *ev_base,
  1022. struct rspamd_srv_command *cmd,
  1023. gint attached_fd,
  1024. rspamd_srv_reply_handler handler,
  1025. gpointer ud)
  1026. {
  1027. struct rspamd_srv_request_data *rd;
  1028. g_assert (cmd != NULL);
  1029. g_assert (worker != NULL);
  1030. rd = g_malloc0 (sizeof (*rd));
  1031. cmd->id = ottery_rand_uint64 ();
  1032. memcpy (&rd->cmd, cmd, sizeof (rd->cmd));
  1033. rd->handler = handler;
  1034. rd->ud = ud;
  1035. rd->worker = worker;
  1036. rd->rep.id = cmd->id;
  1037. rd->rep.type = cmd->type;
  1038. rd->attached_fd = attached_fd;
  1039. rd->io_ev.data = rd;
  1040. ev_io_init (&rd->io_ev, rspamd_srv_request_handler,
  1041. rd->worker->srv_pipe[1], EV_WRITE);
  1042. ev_io_start (ev_base, &rd->io_ev);
  1043. }
  1044. enum rspamd_control_type
  1045. rspamd_control_command_from_string (const gchar *str)
  1046. {
  1047. enum rspamd_control_type ret = RSPAMD_CONTROL_MAX;
  1048. if (!str) {
  1049. return ret;
  1050. }
  1051. if (g_ascii_strcasecmp (str, "hyperscan_loaded") == 0) {
  1052. ret = RSPAMD_CONTROL_HYPERSCAN_LOADED;
  1053. }
  1054. else if (g_ascii_strcasecmp (str, "stat") == 0) {
  1055. ret = RSPAMD_CONTROL_STAT;
  1056. }
  1057. else if (g_ascii_strcasecmp (str, "reload") == 0) {
  1058. ret = RSPAMD_CONTROL_RELOAD;
  1059. }
  1060. else if (g_ascii_strcasecmp (str, "reresolve") == 0) {
  1061. ret = RSPAMD_CONTROL_RERESOLVE;
  1062. }
  1063. else if (g_ascii_strcasecmp (str, "recompile") == 0) {
  1064. ret = RSPAMD_CONTROL_RECOMPILE;
  1065. }
  1066. else if (g_ascii_strcasecmp (str, "log_pipe") == 0) {
  1067. ret = RSPAMD_CONTROL_LOG_PIPE;
  1068. }
  1069. else if (g_ascii_strcasecmp (str, "fuzzy_stat") == 0) {
  1070. ret = RSPAMD_CONTROL_FUZZY_STAT;
  1071. }
  1072. else if (g_ascii_strcasecmp (str, "fuzzy_sync") == 0) {
  1073. ret = RSPAMD_CONTROL_FUZZY_SYNC;
  1074. }
  1075. else if (g_ascii_strcasecmp (str, "monitored_change") == 0) {
  1076. ret = RSPAMD_CONTROL_MONITORED_CHANGE;
  1077. }
  1078. else if (g_ascii_strcasecmp (str, "child_change") == 0) {
  1079. ret = RSPAMD_CONTROL_CHILD_CHANGE;
  1080. }
  1081. return ret;
  1082. }
  1083. const gchar *
  1084. rspamd_control_command_to_string (enum rspamd_control_type cmd)
  1085. {
  1086. const gchar *reply = "unknown";
  1087. switch (cmd) {
  1088. case RSPAMD_CONTROL_STAT:
  1089. reply = "stat";
  1090. break;
  1091. case RSPAMD_CONTROL_RELOAD:
  1092. reply = "reload";
  1093. break;
  1094. case RSPAMD_CONTROL_RERESOLVE:
  1095. reply = "reresolve";
  1096. break;
  1097. case RSPAMD_CONTROL_RECOMPILE:
  1098. reply = "recompile";
  1099. break;
  1100. case RSPAMD_CONTROL_HYPERSCAN_LOADED:
  1101. reply = "hyperscan_loaded";
  1102. break;
  1103. case RSPAMD_CONTROL_LOG_PIPE:
  1104. reply = "log_pipe";
  1105. break;
  1106. case RSPAMD_CONTROL_FUZZY_STAT:
  1107. reply = "fuzzy_stat";
  1108. break;
  1109. case RSPAMD_CONTROL_FUZZY_SYNC:
  1110. reply = "fuzzy_sync";
  1111. break;
  1112. case RSPAMD_CONTROL_MONITORED_CHANGE:
  1113. reply = "monitored_change";
  1114. break;
  1115. case RSPAMD_CONTROL_CHILD_CHANGE:
  1116. reply = "child_change";
  1117. break;
  1118. default:
  1119. break;
  1120. }
  1121. return reply;
  1122. }