OpenDNSSEC-enforcer  2.0.4
cmdhandler.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009 NLNet Labs. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
19  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
21  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
23  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  */
26 
32 #include "config.h"
33 
34 #include <sys/un.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <ldns/ldns.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <strings.h>
42 #include <sys/select.h>
43 #include <sys/socket.h>
44 #include <pthread.h>
45 #include <syslog.h>
46 #ifdef HAVE_SYS_TYPES_H
47 # include <sys/types.h>
48 #endif
49 #include <unistd.h>
50 /* According to earlier standards: select() sys/time.h sys/types.h unistd.h */
51 #include <sys/time.h>
52 #include <sys/types.h>
53 
54 #include "daemon/engine.h"
55 #include "clientpipe.h"
56 #include "scheduler/schedule.h"
57 #include "scheduler/task.h"
58 #include "file.h"
59 #include "log.h"
60 #include "status.h"
61 #include "duration.h"
62 #include "str.h"
63 #include "db/db_connection.h"
64 
65 /* commands to handle */
67 #include "policy/policy_list_cmd.h"
68 #include "daemon/help_cmd.h"
69 #include "daemon/time_leap_cmd.h"
70 #include "daemon/queue_cmd.h"
71 #include "daemon/verbosity_cmd.h"
72 #include "daemon/ctrl_cmd.h"
77 #include "enforcer/enforce_cmd.h"
81 #include "keystate/zone_list_cmd.h"
82 #include "keystate/zone_del_cmd.h"
83 #include "keystate/zone_add_cmd.h"
91 #include "keystate/key_purge_cmd.h"
96 #include "signconf/signconf_cmd.h"
99 
100 #include "daemon/cmdhandler.h"
101 
102 #define SE_CMDH_CMDLEN 7
103 #define MAX_CLIENT_CONN 8
104 
105 static char const * module_str = "cmdhandler";
106 
107 typedef struct cmd_func_block* (*fbgetfunctype)(void);
108 
109 static fbgetfunctype*
110 cmd_funcs_avail(void)
111 {
112  static struct cmd_func_block* (*fb[])(void) = {
113  /* Thoughts has gone into the ordering of this list, it affects
114  * the output of the help command */
124 
128 
131 
141 
144 
146 
149 
150 
157  NULL
158  };
159  return fb;
160 }
161 
162 void
164 {
165  fbgetfunctype* fb = cmd_funcs_avail();
166  int cmd_iter = 0;
167  while (fb[cmd_iter]) {
168  if (!fb[cmd_iter]()->handles("time leap", 10))
169  (*fb[cmd_iter])()->usage(sockfd);
170  cmd_iter++;
171  }
172 }
173 
174 struct cmd_func_block*
175 get_funcblock(const char *cmd, ssize_t n)
176 {
177  fbgetfunctype* fb = cmd_funcs_avail();
178  int cmd_iter = 0;
179  while (fb[cmd_iter]) {
180  if (fb[cmd_iter]()->handles(cmd, n))
181  return fb[cmd_iter]();
182  cmd_iter++;
183  }
184  return NULL;
185 }
186 
196 static int
197 cmdhandler_perform_command(cmdhandler_type* cmdc, const char *cmd,
198  ssize_t n)
199 {
200  time_t tstart = time(NULL);
201  struct cmd_func_block* fb;
202  int ret;
203  int sockfd = cmdc->client_fd;
204 
205  ods_log_verbose("received command %s[%ld]", cmd, (long)n);
206  if (n == 0) return 0;
207 
208  /* Find function claiming responsibility */
209  if ((fb = get_funcblock(cmd, n))) {
210  ods_log_debug("[%s] %s command", module_str, fb->cmdname);
211  ret = fb->run(sockfd, cmdc->engine, cmd, n, cmdc->dbconn);
212  if (ret == -1) {
213  /* Syntax error, print usage for cmd */
214  client_printf_err(sockfd, "Error parsing arguments\n",
215  fb->cmdname, time(NULL) - tstart);
216  client_printf(sockfd, "Usage:\n\n");
217  fb->usage(sockfd);
218  } else if (ret == 0) { /* success */
219  client_printf_err(sockfd, "%s completed in %ld seconds.\n",
220  fb->cmdname, time(NULL) - tstart);
221  }
222  ods_log_debug("[%s] done handling command %s[%ld]", module_str, cmd, (long)n);
223  return ret;
224  }
225  /* Unhandled command, print general error */
226  client_printf_err(sockfd, "Unknown command %s.\n", cmd?cmd:"(null)");
227  client_printf(sockfd, "Commands:\n");
228  cmdhandler_get_usage(sockfd);
229  return 1;
230 }
231 
250 static int
251 extract_msg(char* buf, int *pos, int buflen, int *exitcode,
252 cmdhandler_type* cmdc)
253 {
254  char data[ODS_SE_MAXLINE+1], opc;
255  int datalen;
256 
257  assert(exitcode);
258  assert(buf);
259  assert(pos);
260  assert(*pos <= buflen);
261  assert(ODS_SE_MAXLINE >= buflen);
262 
263  while (1) {
264  if (*pos < 3) return 0;
265  opc = buf[0];
266  datalen = (buf[1]<<8) | (buf[2]&0xFF);
267  if (datalen+3 <= *pos) {
268  /* a complete message */
269  memset(data, 0, ODS_SE_MAXLINE+1);
270  memcpy(data, buf+3, datalen);
271  *pos -= datalen+3;
272  memmove(buf, buf+datalen+3, *pos);
273  ods_str_trim(data, 0);
274 
275  if (opc == CLIENT_OPC_STDIN) {
276  *exitcode = cmdhandler_perform_command(cmdc, data, strlen(data));
277  return 1;
278  }
279  } else if (datalen+3 > buflen) {
280  /* Message is not going to fit! Discard the data already recvd */
281  ods_log_error("[%s] Message received to big, truncating.", module_str);
282  datalen -= *pos - 3;
283  buf[1] = datalen >> 8;
284  buf[2] = datalen & 0xFF;
285  *pos = 3;
286  return 0;
287  } else {
288  /* waiting for more data */
289  return 0;
290  }
291  }
292 }
293 
298 static void
299 cmdhandler_handle_client_conversation(cmdhandler_type* cmdc)
300 {
301  /* read blocking */
302  char buf[ODS_SE_MAXLINE+4]; /* enough space for hdr and \0 */
303  int bufpos = 0, r;
304  int exitcode = 0;
305 
306  assert(cmdc);
307 
308  while (1) {
309  int n = read(cmdc->client_fd, buf+bufpos, ODS_SE_MAXLINE-bufpos+3);
310  /* client closed pipe */
311  if (n == 0) return;
312  if (n == -1) { /* an error */
313  if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
314  return;
315  }
316  bufpos += n;
317  r = extract_msg(buf, &bufpos, ODS_SE_MAXLINE, &exitcode, cmdc);
318  if (r == -1) {
319  ods_log_error("[%s] Error receiving message from client.", module_str);
320  break;
321  } else if (r == 1) {
322  if (!client_exit(cmdc->client_fd, exitcode)) {
323  ods_log_error("[%s] Error sending message to client.", module_str);
324  }
325  }
326  }
327 }
328 
333 static void*
334 cmdhandler_accept_client(void* arg)
335 {
336  int err;
337  sigset_t sigset;
338  cmdhandler_type* cmdc = (cmdhandler_type*) arg;
339 
340  sigfillset(&sigset);
341  if((err=pthread_sigmask(SIG_SETMASK, &sigset, NULL)))
342  ods_fatal_exit("[%s] pthread_sigmask: %s", module_str, strerror(err));
343 
344  ods_log_debug("[%s] accept client %i", module_str, cmdc->client_fd);
345 
347  if (!cmdc->dbconn) {
348  client_printf_err(cmdc->client_fd, "Failed to open DB connection.\n");
349  client_exit(cmdc->client_fd, 1);
350  return NULL;
351  }
352 
353  cmdhandler_handle_client_conversation(cmdc);
354  if (cmdc->client_fd) {
355  close(cmdc->client_fd);
356  }
357  db_connection_free(cmdc->dbconn);
358  cmdc->stopped = 1;
359  return NULL;
360 }
361 
367 cmdhandler_create(const char* filename)
368 {
369  cmdhandler_type* cmdh = NULL;
370  struct sockaddr_un servaddr;
371  int listenfd = 0;
372  int flags = 0;
373  int ret = 0;
374 
375  if (!filename) {
376  ods_log_error("[%s] unable to create: no socket filename", module_str);
377  return NULL;
378  }
379  ods_log_assert(filename);
380  ods_log_debug("[%s] create socket %s", module_str, filename);
381 
382  /* new socket */
383  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
384  if (listenfd < 0) {
385  ods_log_error("[%s] unable to create, socket() failed: %s", module_str,
386  strerror(errno));
387  return NULL;
388  }
389  /* set it to non-blocking */
390  flags = fcntl(listenfd, F_GETFL, 0);
391  if (flags < 0) {
392  ods_log_error("[%s] unable to create, fcntl(F_GETFL) failed: %s",
393  module_str, strerror(errno));
394  close(listenfd);
395  return NULL;
396  }
397  flags |= O_NONBLOCK;
398  if (fcntl(listenfd, F_SETFL, flags) < 0) {
399  ods_log_error("[%s] unable to create, fcntl(F_SETFL) failed: %s",
400  module_str, strerror(errno));
401  close(listenfd);
402  return NULL;
403  }
404 
405  /* no surprises */
406  bzero(&servaddr, sizeof(servaddr));
407  servaddr.sun_family = AF_UNIX;
408  strncpy(servaddr.sun_path, filename, sizeof(servaddr.sun_path) - 1);
409  if (filename) {
410  unlink(servaddr.sun_path);
411  }
412 
413  /* bind and listen... */
414  ret = bind(listenfd, (const struct sockaddr*) &servaddr, sizeof(struct sockaddr_un));
415  if (ret != 0) {
416  ods_log_error("[%s] unable to create, bind() failed: %s", module_str,
417  strerror(errno));
418  close(listenfd);
419  return NULL;
420  }
421  ret = listen(listenfd, ODS_SE_MAX_HANDLERS);
422  if (ret != 0) {
423  ods_log_error("[%s] unable to create, listen() failed: %s", module_str,
424  strerror(errno));
425  close(listenfd);
426  return NULL;
427  }
428 
429  /* all ok */
430  cmdh = (cmdhandler_type*)malloc(sizeof (cmdhandler_type));
431  if (!cmdh) {
432  close(listenfd);
433  return NULL;
434  }
435  cmdh->listen_fd = listenfd;
436  cmdh->listen_addr = servaddr;
437  cmdh->need_to_exit = 0;
438  return cmdh;
439 }
440 
445 void
447 {
448  close(cmdhandler->listen_fd);
449  free(cmdhandler);
450 }
451 
456 void
458 {
459  struct sockaddr_un cliaddr;
460  socklen_t clilen;
461  cmdhandler_type* cmdc = NULL;
462  fd_set rset;
463  int flags, connfd = 0, ret = 0;
464  ssize_t thread_index = 0, i;
466 
467  ods_log_assert(cmdhandler);
468  ods_log_assert(cmdhandler->engine);
469  ods_log_debug("[%s] start", module_str);
470 
471 
472  FD_ZERO(&rset);
473  while (cmdhandler->need_to_exit == 0) {
474  clilen = sizeof(cliaddr);
475  FD_SET(cmdhandler->listen_fd, &rset);
476  ret = select(cmdhandler->listen_fd+1, &rset, NULL, NULL, NULL);
477  /* Don't handle new connections when need to exit, this
478  * removes the delay of the self_pipe_trick*/
479 
480  /* Opportunistic join threads LIFO. */
481  for (i = thread_index-1; i>0; i--) {
482  if (!cmdcs[i].stopped) break;
483  if (pthread_join(cmdcs[i].thread_id, NULL)) {
484  break;
485  }
486  thread_index--;
487  }
488 
489  if (cmdhandler->need_to_exit) break;
490  if (ret < 0) {
491  if (errno != EINTR && errno != EWOULDBLOCK) {
492  ods_log_warning("[%s] select() error: %s", module_str,
493  strerror(errno));
494  }
495  continue;
496  }
497  if (FD_ISSET(cmdhandler->listen_fd, &rset) &&
498  thread_index < MAX_CLIENT_CONN)
499  {
500  connfd = accept(cmdhandler->listen_fd,
501  (struct sockaddr *) &cliaddr, &clilen);
502  if (connfd < 0) {
503  if (errno != EINTR && errno != EWOULDBLOCK) {
504  ods_log_warning("[%s] accept error: %s", module_str,
505  strerror(errno));
506  }
507  continue;
508  }
509  /* Explicitely set to blocking, on BSD they would inherit
510  * O_NONBLOCK from parent */
511  flags = fcntl(connfd, F_GETFL, 0);
512  if (flags < 0) {
513  ods_log_error("[%s] unable to create, fcntl(F_GETFL) failed: %s",
514  module_str, strerror(errno));
515  close(connfd);
516  continue;
517  }
518  if (fcntl(connfd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
519  ods_log_error("[%s] unable to create, fcntl(F_SETFL) failed: %s",
520  module_str, strerror(errno));
521  close(connfd);
522  continue;
523  }
524  /* client accepted, create new thread */
525  cmdc = &cmdcs[thread_index];
526  cmdc->stopped = 0;
527  cmdc->listen_fd = cmdhandler->listen_fd;
528  cmdc->client_fd = connfd;
529  cmdc->listen_addr = cmdhandler->listen_addr;
530  cmdc->engine = cmdhandler->engine;
531  cmdc->need_to_exit = cmdhandler->need_to_exit;
532  if (!pthread_create(&(cmdcs[thread_index].thread_id), NULL, &cmdhandler_accept_client,
533  (void*) cmdc))
534  {
535  thread_index++;
536  }
537  ods_log_debug("[%s] %lu clients in progress...", module_str, thread_index);
538  }
539  }
540 
541  /* join threads LIFO. */
542  for (i = thread_index-1; i>0; i--) {
543  if (pthread_join(cmdcs[i].thread_id, NULL)) {
544  break;
545  }
546  }
547 
548  ods_log_debug("[%s] done", module_str);
549  cmdhandler->engine->cmdhandler_done = 1;
550 }
551 
556 static int
557 self_pipe_trick()
558 {
559  int sockfd, ret;
560  struct sockaddr_un servaddr;
561  const char* servsock_filename = OPENDNSSEC_ENFORCER_SOCKETFILE;
562 
563  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
564  if (sockfd < 0) {
565  ods_log_error("[engine] cannot connect to command handler: "
566  "socket() failed: %s\n", strerror(errno));
567  return 1;
568  } else {
569  bzero(&servaddr, sizeof(servaddr));
570  servaddr.sun_family = AF_UNIX;
571  strncpy(servaddr.sun_path, servsock_filename,
572  sizeof(servaddr.sun_path) - 1);
573 
574  ret = connect(sockfd, (const struct sockaddr*) &servaddr,
575  sizeof(servaddr));
576  if (ret != 0) {
577  ods_log_error("[engine] cannot connect to command handler: "
578  "connect() failed: %s\n", strerror(errno));
579  close(sockfd);
580  return 1;
581  } else {
582  /* self-pipe trick */
583  client_printf(sockfd, "");
584  close(sockfd);
585  }
586  }
587  return 0;
588 }
593 void
595 {
596  ods_log_assert(engine);
597  if (!engine->cmdhandler) {
598  return;
599  }
600  ods_log_debug("[engine] stop command handler");
601  engine->cmdhandler->need_to_exit = 1;
602  if (self_pipe_trick() == 0) {
603  while (!engine->cmdhandler_done) {
604  ods_log_debug("[engine] waiting for command handler to exit...");
605  sleep(1);
606  }
607  } else {
608  ods_log_error("[engine] command handler self pipe trick failed, "
609  "unclean shutdown");
610  }
611  (void) pthread_join(engine->cmdhandler->thread_id, NULL);
612 }
struct cmd_func_block * key_ds_seen_funcblock(void)
struct cmd_func_block * update_conf_funcblock(void)
void cmdhandler_stop(struct engine_struct *engine)
Definition: cmdhandler.c:594
void cmdhandler_get_usage(int sockfd)
Definition: cmdhandler.c:163
void ods_log_debug(const char *format,...)
Definition: log.c:41
struct cmd_func_block * signconf_funcblock(void)
Definition: signconf_cmd.c:82
struct cmd_func_block * resalt_funcblock(void)
struct cmd_func_block * zonelist_export_funcblock(void)
struct cmd_func_block * key_ds_gone_funcblock(void)
struct cmd_func_block * verbosity_funcblock(void)
struct engine_struct * engine
Definition: cmdhandler.h:47
void ods_fatal_exit(const char *format,...)
Definition: log.c:94
int(* run)(int sockfd, struct engine_struct *engine, const char *cmd, ssize_t n, db_connection_t *dbconn)
Definition: cmdhandler.h:79
struct cmd_func_block * key_list_funcblock(void)
const char * cmdname
Definition: cmdhandler.h:59
void ods_log_error(const char *format,...)
Definition: log.c:69
struct cmd_func_block * ctrl_funcblock(void)
Definition: ctrl_cmd.c:117
struct cmd_func_block * queue_funcblock(void)
Definition: queue_cmd.c:140
void cmdhandler_start(cmdhandler_type *cmdhandler)
Definition: cmdhandler.c:457
struct sockaddr_un listen_addr
Definition: cmdhandler.h:48
struct cmd_func_block * policy_import_funcblock(void)
struct cmd_func_block * enforce_funcblock(void)
Definition: enforce_cmd.c:109
#define MAX_CLIENT_CONN
Definition: cmdhandler.c:103
db_connection_t * dbconn
Definition: cmdhandler.h:54
#define ODS_SE_MAX_HANDLERS
Definition: cmdhandler.h:40
db_configuration_list_t * dbcfg_list
Definition: engine.h:74
void(* usage)(int sockfd)
Definition: cmdhandler.h:61
struct cmd_func_block * policy_list_funcblock(void)
struct cmd_func_block * key_generate_funcblock(void)
struct cmd_func_block * help_funcblock(void)
Definition: help_cmd.c:111
struct cmd_func_block * key_ds_submit_funcblock(void)
struct cmd_func_block * flush_funcblock(void)
Definition: queue_cmd.c:187
struct cmd_func_block * zonelist_import_funcblock(void)
struct cmd_func_block * update_repositorylist_funcblock(void)
struct cmd_func_block * zone_add_funcblock(void)
Definition: zone_add_cmd.c:382
struct cmd_func_block * key_purge_funcblock(void)
struct cmd_func_block * key_export_funcblock(void)
cmdhandler_type * cmdhandler
Definition: engine.h:56
db_connection_t * get_database_connection(db_configuration_list_t *dbcfg_list)
Definition: engine.c:263
struct cmd_func_block * policy_purge_funcblock(void)
struct cmd_func_block * policy_export_funcblock(void)
struct cmd_func_block * repositorylist_funcblock(void)
pthread_t thread_id
Definition: cmdhandler.h:49
struct cmd_func_block * get_funcblock(const char *cmd, ssize_t n)
Definition: cmdhandler.c:175
void ods_log_verbose(const char *format,...)
Definition: log.c:48
struct cmd_func_block * key_import_funcblock(void)
struct cmd_func_block * time_leap_funcblock(void)
struct cmd_func_block * zone_del_funcblock(void)
Definition: zone_del_cmd.c:296
struct cmd_func_block * key_rollover_funcblock(void)
struct cmd_func_block * backup_funcblock(void)
cmdhandler_type * cmdhandler_create(const char *filename)
Definition: cmdhandler.c:367
void db_connection_free(db_connection_t *connection)
Definition: db_connection.c:45
struct cmd_func_block * key_ds_retract_funcblock(void)
struct cmd_func_block * update_all_funcblock(void)
int(* handles)(const char *cmd, ssize_t n)
Definition: cmdhandler.h:67
struct cmd_func_block * zone_list_funcblock(void)
void ods_log_warning(const char *format,...)
Definition: log.c:62
struct cmd_func_block * rollover_list_funcblock(void)
void cmdhandler_cleanup(cmdhandler_type *cmdhandler)
Definition: cmdhandler.c:446
int cmdhandler_done
Definition: engine.h:57
struct cmd_func_block *(* fbgetfunctype)(void)
Definition: cmdhandler.c:107