OpenDNSSEC-signer  1.3.9
cmdhandler.c
Go to the documentation of this file.
1 /*
2  * $Id: cmdhandler.c 6312 2012-05-08 09:54:27Z jerry $
3  *
4  * Copyright (c) 2009 NLNet Labs. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
34 #include "daemon/cmdhandler.h"
35 #include "daemon/engine.h"
36 #include "scheduler/schedule.h"
37 #include "scheduler/task.h"
38 #include "shared/allocator.h"
39 #include "shared/file.h"
40 #include "shared/hsm.h"
41 #include "shared/locks.h"
42 #include "shared/log.h"
43 #include "shared/status.h"
44 
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <ldns/ldns.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <strings.h>
52 #include <sys/select.h>
53 #include <sys/socket.h>
54 #ifdef HAVE_SYS_TYPES_H
55 # include <sys/types.h>
56 #endif
57 #include <unistd.h>
58 /* According to earlier standards: select() sys/time.h sys/types.h unistd.h */
59 #include <sys/time.h>
60 #include <sys/types.h>
61 
62 #define SE_CMDH_CMDLEN 7
63 
64 #ifndef SUN_LEN
65 #define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
66 #endif
67 
68 static int count = 0;
69 static char* cmdh_str = "cmdhandler";
70 
71 
76 static void
77 cmdhandler_handle_cmd_help(int sockfd)
78 {
79  char buf[ODS_SE_MAXLINE];
80 
81  (void) snprintf(buf, ODS_SE_MAXLINE,
82  "Commands:\n"
83  "zones show the currently known zones.\n"
84  "sign <zone> read zone and schedule for immediate (re-)sign.\n"
85  "sign --all read all zones and schedule all for immediate "
86  "(re-)sign.\n"
87  "clear <zone> delete the internal storage of this zone.\n"
88  " All signatures will be regenerated on the next "
89  "re-sign.\n"
90  "queue show the current task queue.\n"
91  );
92  ods_writen(sockfd, buf, strlen(buf));
93 
94  (void) snprintf(buf, ODS_SE_MAXLINE,
95  "flush execute all scheduled tasks immediately.\n"
96  "update <zone> update this zone signer configurations.\n"
97  "update [--all] update zone list and all signer configurations.\n"
98  "start start the engine.\n"
99  "running check if the engine is running.\n"
100  "reload reload the engine.\n"
101  "stop stop the engine.\n"
102  "verbosity <nr> set verbosity.\n"
103  );
104  ods_writen(sockfd, buf, strlen(buf));
105  return;
106 }
107 
108 
113 static void
114 cmdhandler_handle_cmd_zones(int sockfd, cmdhandler_type* cmdc)
115 {
116  char buf[ODS_SE_MAXLINE];
117  size_t i;
118  ldns_rbnode_t* node = LDNS_RBTREE_NULL;
119  zone_type* zone = NULL;
120 
121  ods_log_assert(cmdc);
122  ods_log_assert(cmdc->engine);
123  if (!cmdc->engine->zonelist || !cmdc->engine->zonelist->zones) {
124  (void)snprintf(buf, ODS_SE_MAXLINE, "I have no zones configured\n");
125  ods_writen(sockfd, buf, strlen(buf));
126  return;
127  }
128 
130  /* how many zones */
131  /* [LOCK] zonelist */
132  (void)snprintf(buf, ODS_SE_MAXLINE, "I have %i zones configured\n",
133  (int) cmdc->engine->zonelist->zones->count);
134  ods_writen(sockfd, buf, strlen(buf));
135 
136  /* list zones */
137  node = ldns_rbtree_first(cmdc->engine->zonelist->zones);
138  while (node && node != LDNS_RBTREE_NULL) {
139  zone = (zone_type*) node->data;
140  for (i=0; i < ODS_SE_MAXLINE; i++) {
141  buf[i] = 0;
142  }
143  (void)snprintf(buf, ODS_SE_MAXLINE, "- %s\n", zone->name);
144  ods_writen(sockfd, buf, strlen(buf));
145  node = ldns_rbtree_next(node);
146  }
147  /* [UNLOCK] zonelist */
149  return;
150 }
151 
152 
157 static void
158 cmdhandler_handle_cmd_update(int sockfd, cmdhandler_type* cmdc,
159  const char* tbd)
160 {
161  char buf[ODS_SE_MAXLINE];
162  ods_status status = ODS_STATUS_OK;
163  zone_type* zone = NULL;
164  task_type* task = NULL;
165  int zl_changed = 0;
166 
167  ods_log_assert(tbd);
168  ods_log_assert(cmdc);
169  ods_log_assert(cmdc->engine);
170  ods_log_assert(cmdc->engine->taskq);
171 
172  if (ods_strcmp(tbd, "--all") == 0) {
173  /* [LOCK] zonelist */
175  zl_changed = zonelist_update(cmdc->engine->zonelist,
177  /* [UNLOCK] zonelist */
178  if (zl_changed == ODS_STATUS_UNCHANGED) {
180  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has not changed."
181  " Signer configurations updated.\n");
182  ods_writen(sockfd, buf, strlen(buf));
183 
185  ods_log_debug("[%s] signer configurations updated", cmdh_str);
186  } else if (zl_changed == ODS_STATUS_OK) {
187  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list updated: %i "
188  "removed, %i added, %i updated.\n",
189  cmdc->engine->zonelist->just_removed,
190  cmdc->engine->zonelist->just_added,
191  cmdc->engine->zonelist->just_updated);
192  ods_writen(sockfd, buf, strlen(buf));
193 
194  cmdc->engine->zonelist->just_removed = 0;
195  cmdc->engine->zonelist->just_added = 0;
196  cmdc->engine->zonelist->just_updated = 0;
198 
199  ods_log_debug("[%s] commit zone list changes", cmdh_str);
201  ods_log_debug("[%s] signer configurations updated", cmdh_str);
202  } else {
204  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has errors.\n");
205  ods_writen(sockfd, buf, strlen(buf));
206  }
207  return;
208  } else {
209  /* look up zone */
211  /* [LOCK] zonelist */
212  zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
213  LDNS_RR_CLASS_IN);
214  /* If this zone is just added, don't update (it might not have a
215  * task yet) */
216  if (zone && zone->just_added) {
217  zone = NULL;
218  }
219  /* [UNLOCK] zonelist */
221  if (!zone) {
222  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
223  tbd);
224  ods_writen(sockfd, buf, strlen(buf));
225  /* update all */
226  cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
227  return;
228  }
229 
230  lock_basic_lock(&zone->zone_lock);
231  ods_log_assert(zone->task);
232 
234  /* [LOCK] schedule */
235  task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
236  if (task != NULL) {
237  ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
238  zone->name);
239  if (task->what != TASK_SIGNCONF) {
240  task->halted = task->what;
241  task->interrupt = TASK_SIGNCONF;
242  }
243  task->what = TASK_SIGNCONF;
244  task->when = time_now();
245  status = schedule_task(cmdc->engine->taskq, task, 0);
246  zone->task = task;
247  } else {
248  /* task not queued, being worked on? */
249  ods_log_verbose("[%s] worker busy with zone %s, will update "
250  "signconf as soon as possible", cmdh_str, zone->name);
251  task = (task_type*) zone->task;
252  task->interrupt = TASK_SIGNCONF;
253  /* task->halted set by worker */
254  }
255  /* [UNLOCK] schedule */
257 
259 
260  if (status != ODS_STATUS_OK) {
261  ods_log_crit("[%s] cannot schedule task for zone %s: %s",
262  cmdh_str, zone->name, ods_status2str(status));
263  task_cleanup(task);
264  zone->task = NULL;
265  } else {
267  }
268 
269  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s config being updated.\n",
270  tbd);
271  ods_writen(sockfd, buf, strlen(buf));
272  }
273  return;
274 }
275 
276 
281 static void
282 cmdhandler_handle_cmd_sign(int sockfd, cmdhandler_type* cmdc, const char* tbd)
283 {
284  zone_type* zone = NULL;
285  task_type* task = NULL;
286  ods_status status = ODS_STATUS_OK;
287  char buf[ODS_SE_MAXLINE];
288 
289  ods_log_assert(tbd);
290  ods_log_assert(cmdc);
291  ods_log_assert(cmdc->engine);
292  ods_log_assert(cmdc->engine->taskq);
293 
294  if (ods_strcmp(tbd, "--all") == 0) {
296  /* [LOCK] schedule */
298  /* [UNLOCK] schedule */
301  (void)snprintf(buf, ODS_SE_MAXLINE, "All zones scheduled for "
302  "immediate re-sign.\n");
303  ods_writen(sockfd, buf, strlen(buf));
304  ods_log_verbose("[%s] all zones scheduled for immediate re-sign",
305  cmdh_str);
306  return;
307  } else {
309  /* [LOCK] zonelist */
310  zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
311  LDNS_RR_CLASS_IN);
312  /* If this zone is just added, don't update (it might not have a task yet) */
313  if (zone && zone->just_added) {
314  zone = NULL;
315  }
316  /* [UNLOCK] zonelist */
318 
319  if (!zone) {
320  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
321  tbd);
322  ods_writen(sockfd, buf, strlen(buf));
323  return;
324  }
325 
326  lock_basic_lock(&zone->zone_lock);
327  ods_log_assert(zone->task);
328 
330  /* [LOCK] schedule */
331  task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
332  if (task != NULL) {
333  ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
334  zone->name);
335  if (task->what != TASK_READ) {
336  task->halted = task->what;
337  task->interrupt = TASK_READ;
338  }
339  task->what = TASK_READ;
340  task->when = time_now();
341  status = schedule_task(cmdc->engine->taskq, task, 0);
342  } else {
343  /* task now queued, being worked on? */
344  ods_log_verbose("[%s] worker busy with zone %s, will read "
345  "zone input as soon as possible", cmdh_str, zone->name);
346  task = (task_type*) zone->task;
347  task->interrupt = TASK_READ;
348  /* task->halted set by worker */
349  }
350  /* [UNLOCK] schedule */
352 
353  zone->task = task;
355 
356  if (status != ODS_STATUS_OK) {
357  (void)snprintf(buf, ODS_SE_MAXLINE, "Error: Cannot schedule task for "
358  "zone %s.\n", tbd);
359  ods_writen(sockfd, buf, strlen(buf));
360  ods_log_crit("[%s] cannot schedule task for zone %s: %s",
361  cmdh_str, zone->name, ods_status2str(status));
362  task_cleanup(task);
363  zone->task = NULL;
364  } else {
365  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s scheduled for immediate "
366  "re-sign.\n", tbd);
367  ods_writen(sockfd, buf, strlen(buf));
368  ods_log_verbose("[%s] zone %s scheduled for immediate re-sign",
369  cmdh_str, tbd);
371  }
372  }
373  return;
374 }
375 
376 
381 static void
382 unlink_backup_file(const char* filename, const char* extension)
383 {
384  char* tmpname = ods_build_path(filename, extension, 0, 1);
385  ods_log_debug("[%s] unlink file %s", cmdh_str, tmpname);
386  unlink(tmpname);
387  free((void*)tmpname);
388  return;
389 }
390 
395 static void
396 cmdhandler_handle_cmd_clear(int sockfd, cmdhandler_type* cmdc, const char* tbd)
397 {
398  char buf[ODS_SE_MAXLINE];
399  zone_type* zone = NULL;
400  task_type* task = NULL;
401  uint32_t inbound_serial = 0;
402  uint32_t internal_serial = 0;
403  uint32_t outbound_serial = 0;
404  ods_status status = ODS_STATUS_OK;
405 
406  ods_log_assert(tbd);
407  ods_log_assert(cmdc);
408  ods_log_assert(cmdc->engine);
409 
410  unlink_backup_file(tbd, ".inbound");
411  unlink_backup_file(tbd, ".backup");
412 
414  /* [LOCK] zonelist */
415  zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
416  LDNS_RR_CLASS_IN);
417  /* [UNLOCK] zonelist */
419  if (zone) {
420  /* [LOCK] zone */
421  lock_basic_lock(&zone->zone_lock);
422  inbound_serial = zone->zonedata->inbound_serial;
423  internal_serial = zone->zonedata->internal_serial;
424  outbound_serial = zone->zonedata->outbound_serial;
425  zonedata_cleanup(zone->zonedata);
426  zone->zonedata = NULL;
427  zone->zonedata = zonedata_create(zone->allocator);
428  zone->zonedata->initialized = 1;
429  zone->zonedata->inbound_serial = inbound_serial;
430  zone->zonedata->internal_serial = internal_serial;
431  zone->zonedata->outbound_serial = outbound_serial;
432 
437  lhsm_check_connection((void*)cmdc->engine);
438  status = zone_publish_dnskeys(zone, 1);
439  if (status == ODS_STATUS_OK) {
440  status = zone_prepare_nsec3(zone, 1);
441  } else {
442  ods_log_warning("[%s] unable to restore DNSKEY RRset for zone %s,"
443  " reloading signconf", cmdh_str, zone->name);
444  }
445  if (status == ODS_STATUS_OK) {
446  status = zonedata_commit(zone->zonedata);
447  } else {
448  ods_log_warning("[%s] unable to restore NSEC3PARAM RRset for "
449  " zone %s, reloading signconf", cmdh_str, zone->name);
450  }
451 
452  task = (task_type*) zone->task;
453  task->what = TASK_READ;
454  if (status != ODS_STATUS_OK) {
455  ods_log_warning("[%s] unable to restore DNSKEY/NSEC3PARAM RRset "
456  " for zone %s, reloading signconf", cmdh_str, zone->name);
457  task->what = TASK_SIGNCONF;
458  }
459  /* [UNLOCK] zone */
461 
462  (void)snprintf(buf, ODS_SE_MAXLINE, "Internal zone information about "
463  "%s cleared", tbd?tbd:"(null)");
464  ods_log_info("[%s] internal zone information about %s cleared",
465  cmdh_str, tbd?tbd:"(null)");
466  } else {
467  (void)snprintf(buf, ODS_SE_MAXLINE, "Cannot clear zone %s, zone not "
468  "found", tbd?tbd:"(null)");
469  ods_log_warning("[%s] cannot clear zone %s, zone not found",
470  cmdh_str, tbd?tbd:"(null)");
471  }
472 
473  ods_writen(sockfd, buf, strlen(buf));
474  return;
475 }
476 
477 
482 static void
483 cmdhandler_handle_cmd_queue(int sockfd, cmdhandler_type* cmdc)
484 {
485  char* strtime = NULL;
486  char buf[ODS_SE_MAXLINE];
487  size_t i = 0;
488  time_t now = 0;
489  ldns_rbnode_t* node = LDNS_RBTREE_NULL;
490  task_type* task = NULL;
491 
492  ods_log_assert(cmdc);
493  ods_log_assert(cmdc->engine);
494  if (!cmdc->engine->taskq || !cmdc->engine->taskq->tasks) {
495  (void)snprintf(buf, ODS_SE_MAXLINE, "I have no tasks scheduled.\n");
496  ods_writen(sockfd, buf, strlen(buf));
497  return;
498  }
499 
501  /* [LOCK] schedule */
502 
503  /* time */
504  now = time_now();
505  strtime = ctime(&now);
506  (void)snprintf(buf, ODS_SE_MAXLINE, "It is now %s",
507  strtime?strtime:"(null)");
508  ods_writen(sockfd, buf, strlen(buf));
509 
510  /* current work */
511  for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
512  task = cmdc->engine->workers[i]->task;
513  if (task) {
514  (void)snprintf(buf, ODS_SE_MAXLINE, "Working with task %s on "
515  "zone %s\n",
517  task_who2str(task->who));
518  ods_writen(sockfd, buf, strlen(buf));
519  }
520  }
521 
522  /* how many tasks */
523  (void)snprintf(buf, ODS_SE_MAXLINE, "\nI have %i tasks scheduled.\n",
524  (int) cmdc->engine->taskq->tasks->count);
525  ods_writen(sockfd, buf, strlen(buf));
526 
527  /* list tasks */
528  node = ldns_rbtree_first(cmdc->engine->taskq->tasks);
529  while (node && node != LDNS_RBTREE_NULL) {
530  task = (task_type*) node->data;
531  for (i=0; i < ODS_SE_MAXLINE; i++) {
532  buf[i] = 0;
533  }
534  (void)task2str(task, (char*) &buf[0]);
535  ods_writen(sockfd, buf, strlen(buf));
536  node = ldns_rbtree_next(node);
537  }
538 
539  /* [UNLOCK] schedule */
541  return;
542 }
543 
544 
549 static void
550 cmdhandler_handle_cmd_flush(int sockfd, cmdhandler_type* cmdc)
551 {
552  char buf[ODS_SE_MAXLINE];
553 
554  ods_log_assert(cmdc);
555  ods_log_assert(cmdc->engine);
556  ods_log_assert(cmdc->engine->taskq);
557 
559  /* [LOCK] schedule */
561  /* [UNLOCK] schedule */
563 
565 
566  (void)snprintf(buf, ODS_SE_MAXLINE, "All tasks scheduled immediately.\n");
567  ods_writen(sockfd, buf, strlen(buf));
568  ods_log_verbose("[%s] all tasks scheduled immediately", cmdh_str);
569  return;
570 }
571 
572 
577 static void
578 cmdhandler_handle_cmd_reload(int sockfd, cmdhandler_type* cmdc)
579 {
580  char buf[ODS_SE_MAXLINE];
581 
582  ods_log_assert(cmdc);
583  ods_log_assert(cmdc->engine);
584 
585  cmdc->engine->need_to_reload = 1;
586 
588  /* [LOCK] signal */
590  /* [UNLOCK] signal */
592 
593  (void)snprintf(buf, ODS_SE_MAXLINE, "Reloading engine.\n");
594  ods_writen(sockfd, buf, strlen(buf));
595  return;
596 }
597 
598 
603 static void
604 cmdhandler_handle_cmd_stop(int sockfd, cmdhandler_type* cmdc)
605 {
606  char buf[ODS_SE_MAXLINE];
607 
608  ods_log_assert(cmdc);
609  ods_log_assert(cmdc->engine);
610 
611  cmdc->engine->need_to_exit = 1;
612 
614  /* [LOCK] signal */
616  /* [UNLOCK] signal */
618 
619  (void)snprintf(buf, ODS_SE_MAXLINE, ODS_SE_STOP_RESPONSE);
620  ods_writen(sockfd, buf, strlen(buf));
621  return;
622 }
623 
624 
629 static void
630 cmdhandler_handle_cmd_start(int sockfd)
631 {
632  char buf[ODS_SE_MAXLINE];
633 
634  (void)snprintf(buf, ODS_SE_MAXLINE, "Engine already running.\n");
635  ods_writen(sockfd, buf, strlen(buf));
636  return;
637 }
638 
639 
644 static void
645 cmdhandler_handle_cmd_running(int sockfd)
646 {
647  char buf[ODS_SE_MAXLINE];
648 
649  (void)snprintf(buf, ODS_SE_MAXLINE, "Engine running.\n");
650  ods_writen(sockfd, buf, strlen(buf));
651  return;
652 }
653 
654 
659 static void
660 cmdhandler_handle_cmd_verbosity(int sockfd, cmdhandler_type* cmdc, int val)
661 {
662  char buf[ODS_SE_MAXLINE];
663 
664  ods_log_assert(cmdc);
665  ods_log_assert(cmdc->engine);
666  ods_log_assert(cmdc->engine->config);
667 
669  cmdc->engine->config->use_syslog, val);
670 
671  (void)snprintf(buf, ODS_SE_MAXLINE, "Verbosity level set to %i.\n", val);
672  ods_writen(sockfd, buf, strlen(buf));
673 }
674 
675 
680 static void
681 cmdhandler_handle_cmd_error(int sockfd, const char* str)
682 {
683  char buf[ODS_SE_MAXLINE];
684  (void)snprintf(buf, ODS_SE_MAXLINE, "Error: %s.\n", str?str:"(null)");
685  ods_writen(sockfd, buf, strlen(buf));
686  return;
687 }
688 
689 
694 static void
695 cmdhandler_handle_cmd_unknown(int sockfd, const char* str)
696 {
697  char buf[ODS_SE_MAXLINE];
698  (void)snprintf(buf, ODS_SE_MAXLINE, "Unknown command %s.\n",
699  str?str:"(null)");
700  ods_writen(sockfd, buf, strlen(buf));
701  return;
702 }
703 
704 
723 static void
724 cmdhandler_handle_cmd(cmdhandler_type* cmdc)
725 {
726  ssize_t n = 0;
727  int sockfd = 0;
728  char buf[ODS_SE_MAXLINE];
729 
730  ods_log_assert(cmdc);
731  sockfd = cmdc->client_fd;
732 
733 again:
734  while ((n = read(sockfd, buf, ODS_SE_MAXLINE)) > 0) {
735  buf[n-1] = '\0';
736  n--;
737  if (n <= 0) {
738  return;
739  }
740  ods_log_verbose("[%s] received command %s[%i]", cmdh_str, buf, n);
741 
742  if (n == 4 && strncmp(buf, "help", n) == 0) {
743  ods_log_debug("[%s] help command", cmdh_str);
744  cmdhandler_handle_cmd_help(sockfd);
745  } else if (n == 5 && strncmp(buf, "zones", n) == 0) {
746  ods_log_debug("[%s] list zones command", cmdh_str);
747  cmdhandler_handle_cmd_zones(sockfd, cmdc);
748  } else if (n >= 4 && strncmp(buf, "sign", 4) == 0) {
749  ods_log_debug("[%s] sign zone command", cmdh_str);
750  if (buf[4] == '\0') {
751  /* NOTE: wouldn't it be nice that we default to --all? */
752  cmdhandler_handle_cmd_error(sockfd, "sign command needs "
753  "an argument (either '--all' or a zone name)");
754  } else if (buf[4] != ' ') {
755  cmdhandler_handle_cmd_unknown(sockfd, buf);
756  } else {
757  cmdhandler_handle_cmd_sign(sockfd, cmdc, &buf[5]);
758  }
759  } else if (n >= 5 && strncmp(buf, "clear", 5) == 0) {
760  ods_log_debug("[%s] clear zone command", cmdh_str);
761  if (buf[5] == '\0') {
762  cmdhandler_handle_cmd_error(sockfd, "clear command needs "
763  "a zone name");
764  } else if (buf[5] != ' ') {
765  cmdhandler_handle_cmd_unknown(sockfd, buf);
766  } else {
767  cmdhandler_handle_cmd_clear(sockfd, cmdc, &buf[6]);
768  }
769  } else if (n == 5 && strncmp(buf, "queue", n) == 0) {
770  ods_log_debug("[%s] list tasks command", cmdh_str);
771  cmdhandler_handle_cmd_queue(sockfd, cmdc);
772  } else if (n == 5 && strncmp(buf, "flush", n) == 0) {
773  ods_log_debug("[%s] flush tasks command", cmdh_str);
774  cmdhandler_handle_cmd_flush(sockfd, cmdc);
775  } else if (n >= 6 && strncmp(buf, "update", 6) == 0) {
776  ods_log_debug("[%s] update command", cmdh_str);
777  if (buf[6] == '\0') {
778  cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
779  } else if (buf[6] != ' ') {
780  cmdhandler_handle_cmd_unknown(sockfd, buf);
781  } else {
782  cmdhandler_handle_cmd_update(sockfd, cmdc, &buf[7]);
783  }
784  } else if (n == 4 && strncmp(buf, "stop", n) == 0) {
785  ods_log_debug("[%s] shutdown command", cmdh_str);
786  cmdhandler_handle_cmd_stop(sockfd, cmdc);
787  return;
788  } else if (n == 5 && strncmp(buf, "start", n) == 0) {
789  ods_log_debug("[%s] start command", cmdh_str);
790  cmdhandler_handle_cmd_start(sockfd);
791  } else if (n == 6 && strncmp(buf, "reload", n) == 0) {
792  ods_log_debug("[%s] reload command", cmdh_str);
793  cmdhandler_handle_cmd_reload(sockfd, cmdc);
794  } else if (n == 7 && strncmp(buf, "running", n) == 0) {
795  ods_log_debug("[%s] running command", cmdh_str);
796  cmdhandler_handle_cmd_running(sockfd);
797  } else if (n >= 9 && strncmp(buf, "verbosity", 9) == 0) {
798  ods_log_debug("[%s] verbosity command", cmdh_str);
799  if (buf[9] == '\0') {
800  cmdhandler_handle_cmd_error(sockfd, "verbosity command "
801  "an argument (verbosity level)");
802  } else if (buf[9] != ' ') {
803  cmdhandler_handle_cmd_unknown(sockfd, buf);
804  } else {
805  cmdhandler_handle_cmd_verbosity(sockfd, cmdc, atoi(&buf[10]));
806  }
807  } else {
808  ods_log_debug("[%s] unknown command", cmdh_str);
809  cmdhandler_handle_cmd_unknown(sockfd, buf);
810  }
811 
812  ods_log_debug("[%s] done handling command %s[%i]", cmdh_str, buf, n);
813  (void)snprintf(buf, SE_CMDH_CMDLEN, "\ncmd> ");
814  ods_writen(sockfd, buf, strlen(buf));
815  }
816 
817  if (n < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) ) {
818  goto again;
819  } else if (n < 0 && errno == ECONNRESET) {
820  ods_log_debug("[%s] done handling client: %s", cmdh_str,
821  strerror(errno));
822  } else if (n < 0 ) {
823  ods_log_error("[%s] read error: %s", cmdh_str, strerror(errno));
824  }
825  return;
826 }
827 
828 
833 static void*
834 cmdhandler_accept_client(void* arg)
835 {
836  cmdhandler_type* cmdc = (cmdhandler_type*) arg;
837 
840 
841  ods_log_debug("[%s] accept client %i", cmdh_str, cmdc->client_fd);
842  cmdhandler_handle_cmd(cmdc);
843  if (cmdc->client_fd) {
844  close(cmdc->client_fd);
845  }
846  free(cmdc);
847  count--;
848  return NULL;
849 }
850 
851 
857 cmdhandler_create(allocator_type* allocator, const char* filename)
858 {
859  cmdhandler_type* cmdh = NULL;
860  struct sockaddr_un servaddr;
861  int listenfd = 0;
862  int flags = 0;
863  int ret = 0;
864 
865  if (!allocator) {
866  ods_log_error("[%s] unable to create: no allocator", cmdh_str);
867  return NULL;
868  }
869  ods_log_assert(allocator);
870 
871  if (!filename) {
872  ods_log_error("[%s] unable to create: no socket filename", cmdh_str);
873  return NULL;
874  }
875  ods_log_assert(filename);
876  ods_log_debug("[%s] create socket %s", cmdh_str, filename);
877 
878  /* new socket */
879  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
880  if (listenfd <= 0) {
881  ods_log_error("[%s] unable to create, socket() failed: %s", cmdh_str,
882  strerror(errno));
883  return NULL;
884  }
885  /* set it to non-blocking */
886  flags = fcntl(listenfd, F_GETFL, 0);
887  if (flags < 0) {
888  ods_log_error("[%s] unable to create, fcntl(F_GETFL) failed: %s",
889  cmdh_str, strerror(errno));
890  close(listenfd);
891  return NULL;
892  }
893  flags |= O_NONBLOCK;
894  if (fcntl(listenfd, F_SETFL, flags) < 0) {
895  ods_log_error("[%s] unable to create, fcntl(F_SETFL) failed: %s",
896  cmdh_str, strerror(errno));
897  close(listenfd);
898  return NULL;
899  }
900 
901  /* no surprises */
902  if (filename) {
903  unlink(filename);
904  }
905  bzero(&servaddr, sizeof(servaddr));
906  servaddr.sun_family = AF_UNIX;
907  strncpy(servaddr.sun_path, filename, sizeof(servaddr.sun_path) - 1);
908 
909  /* bind and listen... */
910  ret = bind(listenfd, (const struct sockaddr*) &servaddr,
911  SUN_LEN(&servaddr));
912  if (ret != 0) {
913  ods_log_error("[%s] unable to create, bind() failed: %s", cmdh_str,
914  strerror(errno));
915  close(listenfd);
916  return NULL;
917  }
918  ret = listen(listenfd, ODS_SE_MAX_HANDLERS);
919  if (ret != 0) {
920  ods_log_error("[%s] unable to create, listen() failed: %s", cmdh_str,
921  strerror(errno));
922  close(listenfd);
923  return NULL;
924  }
925 
926  /* all ok */
927  cmdh = (cmdhandler_type*) allocator_alloc(allocator,
928  sizeof(cmdhandler_type));
929  if (!cmdh) {
930  close(listenfd);
931  return NULL;
932  }
933  cmdh->allocator = allocator;
934  cmdh->listen_fd = listenfd;
935  cmdh->listen_addr = servaddr;
936  cmdh->need_to_exit = 0;
937  return cmdh;
938 }
939 
940 
945 void
947 {
948  struct sockaddr_un cliaddr;
949  socklen_t clilen;
950  cmdhandler_type* cmdc = NULL;
951  engine_type* engine = NULL;
952  fd_set rset;
953  int connfd = 0;
954  int ret = 0;
955 
956  ods_log_assert(cmdhandler);
957  ods_log_assert(cmdhandler->engine);
958  ods_log_debug("[%s] start", cmdh_str);
959 
960  engine = cmdhandler->engine;
961  ods_thread_detach(cmdhandler->thread_id);
962  FD_ZERO(&rset);
963  while (cmdhandler->need_to_exit == 0) {
964  clilen = sizeof(cliaddr);
965  FD_SET(cmdhandler->listen_fd, &rset);
966  ret = select(ODS_SE_MAX_HANDLERS+1, &rset, NULL, NULL, NULL);
967  if (ret < 0) {
968  if (errno != EINTR && errno != EWOULDBLOCK) {
969  ods_log_warning("[%s] select() error: %s", cmdh_str,
970  strerror(errno));
971  }
972  continue;
973  }
974  if (FD_ISSET(cmdhandler->listen_fd, &rset)) {
975  connfd = accept(cmdhandler->listen_fd,
976  (struct sockaddr *) &cliaddr, &clilen);
977  if (connfd < 0) {
978  if (errno != EINTR && errno != EWOULDBLOCK) {
979  ods_log_warning("[%s] accept error: %s", cmdh_str,
980  strerror(errno));
981  }
982  continue;
983  }
984  /* client accepted, create new thread */
985  cmdc = (cmdhandler_type*) malloc(sizeof(cmdhandler_type));
986  if (!cmdc) {
987  ods_log_crit("[%s] unable to create thread for client: "
988  "malloc failed", cmdh_str);
989  cmdhandler->need_to_exit = 1;
990  break;
991  }
992  cmdc->listen_fd = cmdhandler->listen_fd;
993  cmdc->client_fd = connfd;
994  cmdc->listen_addr = cmdhandler->listen_addr;
995  cmdc->engine = cmdhandler->engine;
996  cmdc->need_to_exit = cmdhandler->need_to_exit;
997  ods_thread_create(&cmdc->thread_id, &cmdhandler_accept_client,
998  (void*) cmdc);
999  count++;
1000  ods_log_debug("[%s] %i clients in progress...", cmdh_str, count);
1001  }
1002  }
1003 
1004  ods_log_debug("[%s] done", cmdh_str);
1005  engine = cmdhandler->engine;
1006  engine->cmdhandler_done = 1;
1007  return;
1008 }
1009 
1010 
1015 void
1017 {
1018  allocator_type* allocator;
1019  if (!cmdhandler) {
1020  return;
1021  }
1022  allocator = cmdhandler->allocator;
1023  allocator_deallocate(allocator, (void*) cmdhandler);
1024  return;
1025 }
1026