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 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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. #define CRLF "\r\n"
  23. enum command_type {
  24. COMMAND_PASSWORD,
  25. COMMAND_QUIT,
  26. COMMAND_RELOAD,
  27. COMMAND_STAT,
  28. COMMAND_SHUTDOWN,
  29. COMMAND_UPTIME,
  30. };
  31. struct controller_command {
  32. char *command;
  33. int privilleged;
  34. enum command_type type;
  35. };
  36. static struct controller_command commands[] = {
  37. {"password", 0, COMMAND_PASSWORD},
  38. {"quit", 0, COMMAND_QUIT},
  39. {"reload", 1, COMMAND_RELOAD},
  40. {"stat", 0, COMMAND_STAT},
  41. {"shutdown", 1, COMMAND_SHUTDOWN},
  42. {"uptime", 0, COMMAND_UPTIME},
  43. };
  44. static GCompletion *comp;
  45. static time_t start_time;
  46. static
  47. void sig_handler (int signo)
  48. {
  49. switch (signo) {
  50. case SIGINT:
  51. case SIGTERM:
  52. _exit (1);
  53. break;
  54. }
  55. }
  56. static void
  57. sigusr_handler (int fd, short what, void *arg)
  58. {
  59. struct rspamd_worker *worker = (struct rspamd_worker *)arg;
  60. /* Do not accept new connections, preparing to end worker's process */
  61. struct timeval tv;
  62. tv.tv_sec = SOFT_SHUTDOWN_TIME;
  63. tv.tv_usec = 0;
  64. event_del (&worker->sig_ev);
  65. event_del (&worker->bind_ev);
  66. msg_info ("controller's shutdown is pending in %d sec", SOFT_SHUTDOWN_TIME);
  67. event_loopexit (&tv);
  68. return;
  69. }
  70. static gchar*
  71. completion_func (gpointer elem)
  72. {
  73. struct controller_command *cmd = (struct controller_command *)elem;
  74. return cmd->command;
  75. }
  76. static void
  77. free_session (struct controller_session *session)
  78. {
  79. bufferevent_disable (session->bev, EV_READ | EV_WRITE);
  80. memory_pool_delete (session->session_pool);
  81. g_free (session);
  82. }
  83. static int
  84. check_auth (struct controller_command *cmd, struct controller_session *session)
  85. {
  86. char out_buf[128];
  87. int r;
  88. if (cmd->privilleged && !session->authorized) {
  89. r = snprintf (out_buf, sizeof (out_buf), "not authorized" CRLF);
  90. bufferevent_write (session->bev, out_buf, r);
  91. return 0;
  92. }
  93. return 1;
  94. }
  95. static void
  96. process_command (struct controller_command *cmd, char **cmd_args, struct controller_session *session)
  97. {
  98. char out_buf[512], *arg;
  99. int r = 0, days, hours, minutes;
  100. time_t uptime;
  101. switch (cmd->type) {
  102. case COMMAND_PASSWORD:
  103. arg = *cmd_args;
  104. if (*arg == '\0') {
  105. msg_debug ("process_command: empty password passed");
  106. r = snprintf (out_buf, sizeof (out_buf), "password command requires one argument" CRLF);
  107. bufferevent_write (session->bev, out_buf, r);
  108. return;
  109. }
  110. if (strncmp (arg, session->cfg->control_password, strlen (arg)) == 0) {
  111. session->authorized = 1;
  112. r = snprintf (out_buf, sizeof (out_buf), "password accepted" CRLF);
  113. bufferevent_write (session->bev, out_buf, r);
  114. }
  115. else {
  116. session->authorized = 0;
  117. r = snprintf (out_buf, sizeof (out_buf), "password NOT accepted" CRLF);
  118. bufferevent_write (session->bev, out_buf, r);
  119. }
  120. break;
  121. case COMMAND_QUIT:
  122. free_session (session);
  123. break;
  124. case COMMAND_RELOAD:
  125. if (check_auth (cmd, session)) {
  126. r = snprintf (out_buf, sizeof (out_buf), "reload request sent" CRLF);
  127. bufferevent_write (session->bev, out_buf, r);
  128. kill (getppid (), SIGHUP);
  129. }
  130. break;
  131. case COMMAND_STAT:
  132. /* XXX need to implement stat */
  133. if (check_auth (cmd, session)) {
  134. r = snprintf (out_buf, sizeof (out_buf), "-- end of stats report" CRLF);
  135. bufferevent_write (session->bev, out_buf, r);
  136. }
  137. case COMMAND_SHUTDOWN:
  138. if (check_auth (cmd, session)) {
  139. r = snprintf (out_buf, sizeof (out_buf), "shutdown request sent" CRLF);
  140. bufferevent_write (session->bev, out_buf, r);
  141. kill (getppid (), SIGTERM);
  142. }
  143. break;
  144. case COMMAND_UPTIME:
  145. if (check_auth (cmd, session)) {
  146. uptime = time (NULL) - start_time;
  147. /* If uptime more than 2 hours, print as a number of days. */
  148. if (uptime >= 2 * 3600) {
  149. days = uptime / 86400;
  150. hours = (uptime % 3600) / 60;
  151. minutes = (uptime % 60) / 60;
  152. r = snprintf (out_buf, sizeof (out_buf), "%dday%s %dhour%s %dminute%s" CRLF,
  153. days, days > 1 ? "s" : " ",
  154. hours, hours > 1 ? "s" : " ",
  155. minutes, minutes > 1 ? "s" : " ");
  156. }
  157. /* If uptime is less than 1 minute print only seconds */
  158. else if (uptime / 60 == 0) {
  159. r = snprintf (out_buf, sizeof (out_buf), "%dsecond%s", uptime, uptime > 1 ? "s" : " ");
  160. }
  161. /* Else print the minutes and seconds. */
  162. else {
  163. hours = uptime / 3600;
  164. minutes = (uptime % 60) / 60;
  165. r = snprintf (out_buf, sizeof (out_buf), "%dhour%s %dminite%s %dsecond%s",
  166. hours, hours > 1 ? "s" : " ",
  167. minutes, minutes > 1 ? "s" : " ",
  168. (int)uptime, uptime > 1 ? "s" : " ");
  169. }
  170. }
  171. bufferevent_write (session->bev, out_buf, r);
  172. break;
  173. }
  174. }
  175. static void
  176. read_socket (struct bufferevent *bev, void *arg)
  177. {
  178. struct controller_session *session = (struct controller_session *)arg;
  179. int len, i;
  180. char *s, **params, *cmd, out_buf[128];
  181. GList *comp_list;
  182. s = evbuffer_readline (EVBUFFER_INPUT (bev));
  183. if (s != NULL && *s != 0) {
  184. len = strlen (s);
  185. /* Remove end of line characters from string */
  186. if (s[len - 1] == '\n') {
  187. if (s[len - 2] == '\r') {
  188. s[len - 2] = 0;
  189. }
  190. s[len - 1] = 0;
  191. }
  192. params = g_strsplit (s, " ", -1);
  193. len = g_strv_length (params);
  194. if (len > 0) {
  195. cmd = g_strstrip (params[0]);
  196. comp_list = g_completion_complete (comp, cmd, NULL);
  197. switch (g_list_length (comp_list)) {
  198. case 1:
  199. process_command ((struct controller_command *)comp_list->data, &params[1], session);
  200. break;
  201. case 0:
  202. i = snprintf (out_buf, sizeof (out_buf), "Unknown command" CRLF);
  203. bufferevent_write (bev, out_buf, i);
  204. break;
  205. default:
  206. i = snprintf (out_buf, sizeof (out_buf), "Ambigious command" CRLF);
  207. bufferevent_write (bev, out_buf, i);
  208. break;
  209. }
  210. }
  211. g_strfreev (params);
  212. }
  213. if (s != NULL) {
  214. free (s);
  215. }
  216. }
  217. static void
  218. write_socket (struct bufferevent *bev, void *arg)
  219. {
  220. char buf[1024], hostbuf[256];
  221. gethostname (hostbuf, sizeof (hostbuf - 1));
  222. hostbuf[sizeof (hostbuf) - 1] = '\0';
  223. snprintf (buf, sizeof (buf), "Rspamd version %s is running on %s" CRLF, RVERSION, hostbuf);
  224. bufferevent_disable (bev, EV_WRITE);
  225. bufferevent_enable (bev, EV_READ);
  226. }
  227. static void
  228. err_socket (struct bufferevent *bev, short what, void *arg)
  229. {
  230. struct controller_session *session = (struct controller_session *)arg;
  231. msg_info ("closing connection");
  232. /* Free buffers */
  233. free_session (session);
  234. }
  235. static void
  236. accept_socket (int fd, short what, void *arg)
  237. {
  238. struct rspamd_worker *worker = (struct rspamd_worker *)arg;
  239. struct sockaddr_storage ss;
  240. struct controller_session *new_session;
  241. socklen_t addrlen = sizeof(ss);
  242. int nfd;
  243. if ((nfd = accept (fd, (struct sockaddr *)&ss, &addrlen)) == -1) {
  244. return;
  245. }
  246. if (event_make_socket_nonblocking(fd) < 0) {
  247. return;
  248. }
  249. new_session = g_malloc (sizeof (struct controller_session));
  250. if (new_session == NULL) {
  251. msg_err ("accept_socket: cannot allocate memory for task, %m");
  252. return;
  253. }
  254. bzero (new_session, sizeof (struct controller_session));
  255. new_session->worker = worker;
  256. new_session->sock = nfd;
  257. new_session->cfg = worker->srv->cfg;
  258. new_session->session_pool = memory_pool_new (memory_pool_get_size () - 1);
  259. memory_pool_add_destructor (new_session->session_pool, (pool_destruct_func)bufferevent_free, new_session->bev);
  260. worker->srv->stat->control_connections_count ++;
  261. /* Read event */
  262. new_session->bev = bufferevent_new (nfd, read_socket, write_socket, err_socket, (void *)new_session);
  263. bufferevent_enable (new_session->bev, EV_WRITE);
  264. }
  265. void
  266. start_controller (struct rspamd_worker *worker)
  267. {
  268. struct sigaction signals;
  269. int listen_sock, i;
  270. struct sockaddr_un *un_addr;
  271. GList *comp_list = NULL;
  272. worker->srv->pid = getpid ();
  273. worker->srv->type = TYPE_CONTROLLER;
  274. event_init ();
  275. g_mime_init (0);
  276. init_signals (&signals, sig_handler);
  277. sigprocmask (SIG_UNBLOCK, &signals.sa_mask, NULL);
  278. /* SIGUSR2 handler */
  279. signal_set (&worker->sig_ev, SIGUSR2, sigusr_handler, (void *) worker);
  280. signal_add (&worker->sig_ev, NULL);
  281. if (worker->srv->cfg->control_family == AF_INET) {
  282. if ((listen_sock = make_socket (worker->srv->cfg->control_host, worker->srv->cfg->control_port)) == -1) {
  283. msg_err ("start_controller: cannot create tcp listen socket. %m");
  284. exit(-errno);
  285. }
  286. }
  287. else {
  288. un_addr = (struct sockaddr_un *) alloca (sizeof (struct sockaddr_un));
  289. if (!un_addr || (listen_sock = make_unix_socket (worker->srv->cfg->bind_host, un_addr)) == -1) {
  290. msg_err ("start_controller: cannot create unix listen socket. %m");
  291. exit(-errno);
  292. }
  293. }
  294. start_time = time (NULL);
  295. /* Init command completion */
  296. for (i = 0; i < sizeof (commands) / sizeof (commands[0]) - 1; i ++) {
  297. comp_list = g_list_prepend (comp_list, &commands[i]);
  298. }
  299. comp = g_completion_new (completion_func);
  300. g_completion_add_items (comp, comp_list);
  301. /* Accept event */
  302. event_set(&worker->bind_ev, listen_sock, EV_READ | EV_PERSIST, accept_socket, (void *)worker);
  303. event_add(&worker->bind_ev, NULL);
  304. /* Send SIGUSR2 to parent */
  305. kill (getppid (), SIGUSR2);
  306. event_loop (0);
  307. }
  308. /*
  309. * vi:ts=4
  310. */