OpenDNSSEC-signer  1.3.14
cmdhandler.c
Go to the documentation of this file.
1 /*
2  * $Id: cmdhandler.c 7127 2013-05-07 07:52:18Z matthijs $
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  "debug-locks show locking information (for debugging purposes).\n"
92  );
93  ods_writen(sockfd, buf, strlen(buf));
94 
95  (void) snprintf(buf, ODS_SE_MAXLINE,
96  "flush execute all scheduled tasks immediately.\n"
97  "update <zone> update this zone signer configurations.\n"
98  "update [--all] update zone list and all signer configurations.\n"
99  "start start the engine.\n"
100  "running check if the engine is running.\n"
101  "reload reload the engine.\n"
102  "stop stop the engine.\n"
103  "verbosity <nr> set verbosity.\n"
104  );
105  ods_writen(sockfd, buf, strlen(buf));
106  return;
107 }
108 
109 
114 static void
115 cmdhandler_handle_cmd_zones(int sockfd, cmdhandler_type* cmdc)
116 {
117  char buf[ODS_SE_MAXLINE];
118  size_t i;
119  ldns_rbnode_t* node = LDNS_RBTREE_NULL;
120  zone_type* zone = NULL;
121 
122  ods_log_assert(cmdc);
123  ods_log_assert(cmdc->engine);
124  if (!cmdc->engine->zonelist || !cmdc->engine->zonelist->zones) {
125  (void)snprintf(buf, ODS_SE_MAXLINE, "I have no zones configured\n");
126  ods_writen(sockfd, buf, strlen(buf));
127  return;
128  }
129 
132  /* how many zones */
133  (void)snprintf(buf, ODS_SE_MAXLINE, "I have %i zones configured\n",
134  (int) cmdc->engine->zonelist->zones->count);
135  ods_writen(sockfd, buf, strlen(buf));
136 
137  /* list zones */
138  node = ldns_rbtree_first(cmdc->engine->zonelist->zones);
139  while (node && node != LDNS_RBTREE_NULL) {
140  zone = (zone_type*) node->data;
141  for (i=0; i < ODS_SE_MAXLINE; i++) {
142  buf[i] = 0;
143  }
144  (void)snprintf(buf, ODS_SE_MAXLINE, "- %s\n", zone->name);
145  ods_writen(sockfd, buf, strlen(buf));
146  node = ldns_rbtree_next(node);
147  }
149  cmdc->engine->zonelist->zl_locked = 0;
150  return;
151 }
152 
153 
158 static void
159 cmdhandler_handle_cmd_update(int sockfd, cmdhandler_type* cmdc,
160  const char* tbd)
161 {
162  char buf[ODS_SE_MAXLINE];
163  ods_status status = ODS_STATUS_OK;
164  zone_type* zone = NULL;
165  task_type* task = NULL;
166  int zl_changed = 0;
167 
168  ods_log_assert(tbd);
169  ods_log_assert(cmdc);
170  ods_log_assert(cmdc->engine);
171  ods_log_assert(cmdc->engine->taskq);
172 
173  if (ods_strcmp(tbd, "--all") == 0) {
176  zl_changed = zonelist_update(cmdc->engine->zonelist,
178  if (zl_changed == ODS_STATUS_UNCHANGED) {
180  cmdc->engine->zonelist->zl_locked = 0;
181  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has not changed."
182  " Signer configurations updated.\n");
183  ods_writen(sockfd, buf, strlen(buf));
184 
186  ods_log_debug("[%s] signer configurations updated", cmdh_str);
187  } else if (zl_changed == ODS_STATUS_OK) {
188  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list updated: %i "
189  "removed, %i added, %i updated.\n",
190  cmdc->engine->zonelist->just_removed,
191  cmdc->engine->zonelist->just_added,
192  cmdc->engine->zonelist->just_updated);
193  ods_writen(sockfd, buf, strlen(buf));
194 
195  cmdc->engine->zonelist->just_removed = 0;
196  cmdc->engine->zonelist->just_added = 0;
197  cmdc->engine->zonelist->just_updated = 0;
199  cmdc->engine->zonelist->zl_locked = 0;
200 
201  ods_log_debug("[%s] commit zone list changes", cmdh_str);
203  ods_log_debug("[%s] signer configurations updated", cmdh_str);
204  } else {
206  cmdc->engine->zonelist->zl_locked = 0;
207  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has errors.\n");
208  ods_writen(sockfd, buf, strlen(buf));
209  }
210  return;
211  } else {
212  /* look up zone */
215  zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
216  LDNS_RR_CLASS_IN);
217  /* If this zone is just added, don't update (it might not have a
218  * task yet) */
219  if (zone && zone->just_added) {
220  zone = NULL;
221  }
223  cmdc->engine->zonelist->zl_locked = 0;
224  if (!zone) {
225  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
226  tbd);
227  ods_writen(sockfd, buf, strlen(buf));
228  /* update all */
229  cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
230  return;
231  }
232 
233  lock_basic_lock(&zone->zone_lock);
235  ods_log_assert(zone->task);
236 
239  task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
240  if (task != NULL) {
241  ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
242  zone->name);
243  if (task->what != TASK_SIGNCONF) {
244  task->halted = task->what;
245  task->interrupt = TASK_SIGNCONF;
246  }
247  task->what = TASK_SIGNCONF;
248  task->when = time_now();
249  status = schedule_task(cmdc->engine->taskq, task, 0);
250  zone->task = task;
251  } else {
252  /* task not queued, being worked on? */
253  ods_log_verbose("[%s] worker busy with zone %s, will update "
254  "signconf as soon as possible", cmdh_str, zone->name);
255  task = (task_type*) zone->task;
256  task->interrupt = TASK_SIGNCONF;
257  /* task->halted set by worker */
258  }
260  cmdc->engine->taskq->schedule_locked = 0;
261 
263  zone->zone_locked = 0;
264 
265  if (status != ODS_STATUS_OK) {
266  ods_log_crit("[%s] cannot schedule task for zone %s: %s",
267  cmdh_str, zone->name, ods_status2str(status));
268  task_cleanup(task);
269  zone->task = NULL;
270  } else {
272  }
273 
274  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s config being updated.\n",
275  tbd);
276  ods_writen(sockfd, buf, strlen(buf));
277  }
278  return;
279 }
280 
281 
286 static void
287 cmdhandler_handle_cmd_sign(int sockfd, cmdhandler_type* cmdc, const char* tbd)
288 {
289  zone_type* zone = NULL;
290  task_type* task = NULL;
291  ods_status status = ODS_STATUS_OK;
292  char buf[ODS_SE_MAXLINE];
293 
294  ods_log_assert(tbd);
295  ods_log_assert(cmdc);
296  ods_log_assert(cmdc->engine);
297  ods_log_assert(cmdc->engine->taskq);
298 
299  if (ods_strcmp(tbd, "--all") == 0) {
304  cmdc->engine->taskq->schedule_locked = 0;
306  (void)snprintf(buf, ODS_SE_MAXLINE, "All zones scheduled for "
307  "immediate re-sign.\n");
308  ods_writen(sockfd, buf, strlen(buf));
309  ods_log_verbose("[%s] all zones scheduled for immediate re-sign",
310  cmdh_str);
311  return;
312  } else {
315  zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
316  LDNS_RR_CLASS_IN);
317  /* If this zone is just added, don't update (it might not have a task yet) */
318  if (zone && zone->just_added) {
319  zone = NULL;
320  }
322  cmdc->engine->zonelist->zl_locked = 0;
323 
324  if (!zone) {
325  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
326  tbd);
327  ods_writen(sockfd, buf, strlen(buf));
328  return;
329  }
330 
331  lock_basic_lock(&zone->zone_lock);
333  ods_log_assert(zone->task);
334 
337  task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
338  if (task != NULL) {
339  ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
340  zone->name);
341  if (task->what != TASK_READ) {
342  task->halted = task->what;
343  task->interrupt = TASK_READ;
344  }
345  task->what = TASK_READ;
346  task->when = time_now();
347  status = schedule_task(cmdc->engine->taskq, task, 0);
348  } else {
349  /* task now queued, being worked on? */
350  ods_log_verbose("[%s] worker busy with zone %s, will read "
351  "zone input as soon as possible", cmdh_str, zone->name);
352  task = (task_type*) zone->task;
353  task->interrupt = TASK_READ;
354  /* task->halted set by worker */
355  }
357  cmdc->engine->taskq->schedule_locked = 0;
358 
359  zone->task = task;
361  zone->zone_locked = 0;
362 
363  if (status != ODS_STATUS_OK) {
364  (void)snprintf(buf, ODS_SE_MAXLINE, "Error: Cannot schedule task for "
365  "zone %s.\n", tbd);
366  ods_writen(sockfd, buf, strlen(buf));
367  ods_log_crit("[%s] cannot schedule task for zone %s: %s",
368  cmdh_str, zone->name, ods_status2str(status));
369  task_cleanup(task);
370  zone->task = NULL;
371  } else {
372  (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s scheduled for immediate "
373  "re-sign.\n", tbd);
374  ods_writen(sockfd, buf, strlen(buf));
375  ods_log_verbose("[%s] zone %s scheduled for immediate re-sign",
376  cmdh_str, tbd);
378  }
379  }
380  return;
381 }
382 
383 
388 static void
389 unlink_backup_file(const char* filename, const char* extension)
390 {
391  char* tmpname = ods_build_path(filename, extension, 0, 1);
392  ods_log_debug("[%s] unlink file %s", cmdh_str, tmpname);
393  unlink(tmpname);
394  free((void*)tmpname);
395  return;
396 }
397 
402 static void
403 cmdhandler_handle_cmd_clear(int sockfd, cmdhandler_type* cmdc, const char* tbd)
404 {
405  char buf[ODS_SE_MAXLINE];
406  zone_type* zone = NULL;
407  task_type* task = NULL;
408  uint32_t inbound_serial = 0;
409  uint32_t internal_serial = 0;
410  uint32_t outbound_serial = 0;
411  ods_status status = ODS_STATUS_OK;
412 
413  ods_log_assert(tbd);
414  ods_log_assert(cmdc);
415  ods_log_assert(cmdc->engine);
416 
417  unlink_backup_file(tbd, ".inbound");
418  unlink_backup_file(tbd, ".backup");
419 
422  zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
423  LDNS_RR_CLASS_IN);
425  cmdc->engine->zonelist->zl_locked = 0;
426  if (zone) {
427  lock_basic_lock(&zone->zone_lock);
429  inbound_serial = zone->zonedata->inbound_serial;
430  internal_serial = zone->zonedata->internal_serial;
431  outbound_serial = zone->zonedata->outbound_serial;
432  zonedata_cleanup(zone->zonedata);
433  zone->zonedata = NULL;
434  zone->zonedata = zonedata_create(zone->allocator);
435  zone->zonedata->initialized = 1;
436  zone->zonedata->inbound_serial = inbound_serial;
437  zone->zonedata->internal_serial = internal_serial;
438  zone->zonedata->outbound_serial = outbound_serial;
439 
444  lhsm_check_connection((void*)cmdc->engine);
445  status = zone_publish_dnskeys(zone, 1);
446  if (status == ODS_STATUS_OK) {
447  status = zone_prepare_nsec3(zone, 1);
448  } else {
449  ods_log_warning("[%s] unable to restore DNSKEY RRset for zone %s,"
450  " reloading signconf", cmdh_str, zone->name);
451  }
452  if (status == ODS_STATUS_OK) {
453  status = zonedata_commit(zone->zonedata);
454  } else {
455  ods_log_warning("[%s] unable to restore NSEC3PARAM RRset for "
456  " zone %s, reloading signconf", cmdh_str, zone->name);
457  }
458 
459  task = (task_type*) zone->task;
460  task->what = TASK_READ;
461  if (status != ODS_STATUS_OK) {
462  ods_log_warning("[%s] unable to restore DNSKEY/NSEC3PARAM RRset "
463  " for zone %s, reloading signconf", cmdh_str, zone->name);
464  task->what = TASK_SIGNCONF;
465  }
467  zone->zone_locked = 0;
468 
469  (void)snprintf(buf, ODS_SE_MAXLINE, "Internal zone information about "
470  "%s cleared", tbd?tbd:"(null)");
471  ods_log_info("[%s] internal zone information about %s cleared",
472  cmdh_str, tbd?tbd:"(null)");
473  } else {
474  (void)snprintf(buf, ODS_SE_MAXLINE, "Cannot clear zone %s, zone not "
475  "found", tbd?tbd:"(null)");
476  ods_log_warning("[%s] cannot clear zone %s, zone not found",
477  cmdh_str, tbd?tbd:"(null)");
478  }
479 
480  ods_writen(sockfd, buf, strlen(buf));
481  return;
482 }
483 
484 
489 static void
490 cmdhandler_handle_cmd_queue(int sockfd, cmdhandler_type* cmdc)
491 {
492  char* strtime = NULL;
493  char buf[ODS_SE_MAXLINE];
494  size_t i = 0;
495  time_t now = 0;
496  ldns_rbnode_t* node = LDNS_RBTREE_NULL;
497  task_type* task = NULL;
498 
499  ods_log_assert(cmdc);
500  ods_log_assert(cmdc->engine);
501  if (!cmdc->engine->taskq || !cmdc->engine->taskq->tasks) {
502  (void)snprintf(buf, ODS_SE_MAXLINE, "I have no tasks scheduled.\n");
503  ods_writen(sockfd, buf, strlen(buf));
504  return;
505  }
506 
509  /* time */
510  now = time_now();
511  strtime = ctime(&now);
512  (void)snprintf(buf, ODS_SE_MAXLINE, "It is now %s",
513  strtime?strtime:"(null)");
514  ods_writen(sockfd, buf, strlen(buf));
515 
516  /* current work */
517  for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
518  task = cmdc->engine->workers[i]->task;
519  if (task) {
520  (void)snprintf(buf, ODS_SE_MAXLINE, "Working with task %s on "
521  "zone %s\n",
523  task_who2str(task->who));
524  ods_writen(sockfd, buf, strlen(buf));
525  }
526  }
527 
528  /* how many tasks */
529  (void)snprintf(buf, ODS_SE_MAXLINE, "\nI have %i tasks scheduled.\n",
530  (int) cmdc->engine->taskq->tasks->count);
531  ods_writen(sockfd, buf, strlen(buf));
532 
533  /* list tasks */
534  node = ldns_rbtree_first(cmdc->engine->taskq->tasks);
535  while (node && node != LDNS_RBTREE_NULL) {
536  task = (task_type*) node->data;
537  for (i=0; i < ODS_SE_MAXLINE; i++) {
538  buf[i] = 0;
539  }
540  (void)task2str(task, (char*) &buf[0]);
541  ods_writen(sockfd, buf, strlen(buf));
542  node = ldns_rbtree_next(node);
543  }
545  cmdc->engine->taskq->schedule_locked = 0;
546  return;
547 }
548 
549 
554 static void
555 cmdhandler_handle_cmd_debuglocks(int sockfd, cmdhandler_type* cmdc)
556 {
557  char* strtime = NULL;
558  char buf[ODS_SE_MAXLINE];
559  size_t i = 0;
560  time_t now = 0;
561  ldns_rbnode_t* node = LDNS_RBTREE_NULL;
562  zone_type* zone = NULL;
563 
564  ods_log_assert(cmdc);
565  ods_log_assert(cmdc->engine);
566  ods_log_assert(cmdc->engine->taskq);
567  /* time */
568  now = time_now();
569  strtime = ctime(&now);
570  (void)snprintf(buf, ODS_SE_MAXLINE, "It is now %s",
571  strtime?strtime:"(null)");
572  ods_writen(sockfd, buf, strlen(buf));
573  /* general */
574  (void)snprintf(buf, ODS_SE_MAXLINE, "- signal is %s[%i]\n",
575  cmdc->engine->signal_locked?"locked":"unlocked",
576  cmdc->engine->signal_locked);
577  ods_writen(sockfd, buf, strlen(buf));
578  (void)snprintf(buf, ODS_SE_MAXLINE, "- zone list is %s[%i]\n",
579  cmdc->engine->zonelist->zl_locked?"locked":"unlocked",
580  cmdc->engine->zonelist->zl_locked);
581  ods_writen(sockfd, buf, strlen(buf));
582  (void)snprintf(buf, ODS_SE_MAXLINE, "- task schedule is %s[%i]\n",
583  cmdc->engine->taskq->schedule_locked?"locked":"unlocked",
584  cmdc->engine->taskq->schedule_locked);
585  ods_writen(sockfd, buf, strlen(buf));
586  (void)snprintf(buf, ODS_SE_MAXLINE, "- rrset queue is %s[%i]\n",
587  cmdc->engine->signq->q_locked?"locked":"unlocked",
588  cmdc->engine->signq->q_locked);
589  ods_writen(sockfd, buf, strlen(buf));
590  /* workers */
591  for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
592  (void)snprintf(buf, ODS_SE_MAXLINE, "- worker[%i] is %s[%i]\n",
593  cmdc->engine->workers[i]->thread_num,
594  cmdc->engine->workers[i]->worker_locked?"locked":"unlocked",
595  cmdc->engine->workers[i]->worker_locked);
596  ods_writen(sockfd, buf, strlen(buf));
597  }
598  /* drudgers */
599  for (i=0; i < (size_t) cmdc->engine->config->num_signer_threads; i++) {
600  (void)snprintf(buf, ODS_SE_MAXLINE, "- drudger[%i] is %s[%i]\n",
601  cmdc->engine->drudgers[i]->thread_num,
602  cmdc->engine->drudgers[i]->worker_locked?"locked":"unlocked",
603  cmdc->engine->drudgers[i]->worker_locked);
604  ods_writen(sockfd, buf, strlen(buf));
605  }
606  /* zones */
608  node = ldns_rbtree_first(cmdc->engine->zonelist->zones);
609  while (node && node != LDNS_RBTREE_NULL) {
610  zone = (zone_type*) node->data;
611  memset(buf, 0, ODS_SE_MAXLINE);
612  (void)snprintf(buf, ODS_SE_MAXLINE, "- %s is %s[%i], stats is %s[%i]\n",
613  zone->name,
614  zone->zone_locked?"locked":"unlocked",
615  zone->zone_locked,
616  zone->stats->stats_locked?"locked":"unlocked",
617  zone->stats->stats_locked);
618  ods_writen(sockfd, buf, strlen(buf));
619  node = ldns_rbtree_next(node);
620  }
622  return;
623 }
624 
625 
630 static void
631 cmdhandler_handle_cmd_flush(int sockfd, cmdhandler_type* cmdc)
632 {
633  char buf[ODS_SE_MAXLINE];
634 
635  ods_log_assert(cmdc);
636  ods_log_assert(cmdc->engine);
637  ods_log_assert(cmdc->engine->taskq);
638 
643  cmdc->engine->taskq->schedule_locked = 0;
644 
646 
647  (void)snprintf(buf, ODS_SE_MAXLINE, "All tasks scheduled immediately.\n");
648  ods_writen(sockfd, buf, strlen(buf));
649  ods_log_verbose("[%s] all tasks scheduled immediately", cmdh_str);
650  return;
651 }
652 
653 
658 static void
659 cmdhandler_handle_cmd_reload(int sockfd, cmdhandler_type* cmdc)
660 {
661  char buf[ODS_SE_MAXLINE];
662 
663  ods_log_assert(cmdc);
664  ods_log_assert(cmdc->engine);
665 
666  cmdc->engine->need_to_reload = 1;
667 
672  cmdc->engine->signal_locked = 0;
673 
674  (void)snprintf(buf, ODS_SE_MAXLINE, "Reloading engine.\n");
675  ods_writen(sockfd, buf, strlen(buf));
676  return;
677 }
678 
679 
684 static void
685 cmdhandler_handle_cmd_stop(int sockfd, cmdhandler_type* cmdc)
686 {
687  char buf[ODS_SE_MAXLINE];
688 
689  ods_log_assert(cmdc);
690  ods_log_assert(cmdc->engine);
691 
692  cmdc->engine->need_to_exit = 1;
693 
698  cmdc->engine->signal_locked = 0;
699 
700  (void)snprintf(buf, ODS_SE_MAXLINE, ODS_SE_STOP_RESPONSE);
701  ods_writen(sockfd, buf, strlen(buf));
702  return;
703 }
704 
705 
710 static void
711 cmdhandler_handle_cmd_start(int sockfd)
712 {
713  char buf[ODS_SE_MAXLINE];
714 
715  (void)snprintf(buf, ODS_SE_MAXLINE, "Engine already running.\n");
716  ods_writen(sockfd, buf, strlen(buf));
717  return;
718 }
719 
720 
725 static void
726 cmdhandler_handle_cmd_running(int sockfd)
727 {
728  char buf[ODS_SE_MAXLINE];
729 
730  (void)snprintf(buf, ODS_SE_MAXLINE, "Engine running.\n");
731  ods_writen(sockfd, buf, strlen(buf));
732  return;
733 }
734 
735 
740 static void
741 cmdhandler_handle_cmd_verbosity(int sockfd, cmdhandler_type* cmdc, int val)
742 {
743  char buf[ODS_SE_MAXLINE];
744 
745  ods_log_assert(cmdc);
746  ods_log_assert(cmdc->engine);
747  ods_log_assert(cmdc->engine->config);
748 
750  cmdc->engine->config->use_syslog, val);
751 
752  (void)snprintf(buf, ODS_SE_MAXLINE, "Verbosity level set to %i.\n", val);
753  ods_writen(sockfd, buf, strlen(buf));
754 }
755 
756 
761 static void
762 cmdhandler_handle_cmd_error(int sockfd, const char* str)
763 {
764  char buf[ODS_SE_MAXLINE];
765  (void)snprintf(buf, ODS_SE_MAXLINE, "Error: %s.\n", str?str:"(null)");
766  ods_writen(sockfd, buf, strlen(buf));
767  return;
768 }
769 
770 
775 static void
776 cmdhandler_handle_cmd_unknown(int sockfd, const char* str)
777 {
778  char buf[ODS_SE_MAXLINE];
779  (void)snprintf(buf, ODS_SE_MAXLINE, "Unknown command %s.\n",
780  str?str:"(null)");
781  ods_writen(sockfd, buf, strlen(buf));
782  return;
783 }
784 
785 
804 static void
805 cmdhandler_handle_cmd(cmdhandler_type* cmdc)
806 {
807  ssize_t n = 0;
808  int sockfd = 0;
809  char buf[ODS_SE_MAXLINE];
810 
811  ods_log_assert(cmdc);
812  sockfd = cmdc->client_fd;
813 
814 again:
815  while ((n = read(sockfd, buf, ODS_SE_MAXLINE)) > 0) {
816  buf[n-1] = '\0';
817  n--;
818  if (n <= 0) {
819  return;
820  }
821  ods_log_verbose("[%s] received command %s[%i]", cmdh_str, buf, n);
822 
823  if (n == 4 && strncmp(buf, "help", n) == 0) {
824  ods_log_debug("[%s] help command", cmdh_str);
825  cmdhandler_handle_cmd_help(sockfd);
826  } else if (n == 5 && strncmp(buf, "zones", n) == 0) {
827  ods_log_debug("[%s] list zones command", cmdh_str);
828  cmdhandler_handle_cmd_zones(sockfd, cmdc);
829  } else if (n >= 4 && strncmp(buf, "sign", 4) == 0) {
830  ods_log_debug("[%s] sign zone command", cmdh_str);
831  if (buf[4] == '\0') {
832  /* NOTE: wouldn't it be nice that we default to --all? */
833  cmdhandler_handle_cmd_error(sockfd, "sign command needs "
834  "an argument (either '--all' or a zone name)");
835  } else if (buf[4] != ' ') {
836  cmdhandler_handle_cmd_unknown(sockfd, buf);
837  } else {
838  cmdhandler_handle_cmd_sign(sockfd, cmdc, &buf[5]);
839  }
840  } else if (n >= 5 && strncmp(buf, "clear", 5) == 0) {
841  ods_log_debug("[%s] clear zone command", cmdh_str);
842  if (buf[5] == '\0') {
843  cmdhandler_handle_cmd_error(sockfd, "clear command needs "
844  "a zone name");
845  } else if (buf[5] != ' ') {
846  cmdhandler_handle_cmd_unknown(sockfd, buf);
847  } else {
848  cmdhandler_handle_cmd_clear(sockfd, cmdc, &buf[6]);
849  }
850  } else if (n == 5 && strncmp(buf, "queue", n) == 0) {
851  ods_log_debug("[%s] list tasks command", cmdh_str);
852  cmdhandler_handle_cmd_queue(sockfd, cmdc);
853  } else if (n == 11 && strncmp(buf, "debug-locks", n) == 0) {
854  ods_log_debug("[%s] debug locks command", cmdh_str);
855  cmdhandler_handle_cmd_debuglocks(sockfd, cmdc);
856  } else if (n == 5 && strncmp(buf, "flush", n) == 0) {
857  ods_log_debug("[%s] flush tasks command", cmdh_str);
858  cmdhandler_handle_cmd_flush(sockfd, cmdc);
859  } else if (n >= 6 && strncmp(buf, "update", 6) == 0) {
860  ods_log_debug("[%s] update command", cmdh_str);
861  if (buf[6] == '\0') {
862  cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
863  } else if (buf[6] != ' ') {
864  cmdhandler_handle_cmd_unknown(sockfd, buf);
865  } else {
866  cmdhandler_handle_cmd_update(sockfd, cmdc, &buf[7]);
867  }
868  } else if (n == 4 && strncmp(buf, "stop", n) == 0) {
869  ods_log_debug("[%s] shutdown command", cmdh_str);
870  cmdhandler_handle_cmd_stop(sockfd, cmdc);
871  return;
872  } else if (n == 5 && strncmp(buf, "start", n) == 0) {
873  ods_log_debug("[%s] start command", cmdh_str);
874  cmdhandler_handle_cmd_start(sockfd);
875  } else if (n == 6 && strncmp(buf, "reload", n) == 0) {
876  ods_log_debug("[%s] reload command", cmdh_str);
877  cmdhandler_handle_cmd_reload(sockfd, cmdc);
878  } else if (n == 7 && strncmp(buf, "running", n) == 0) {
879  ods_log_debug("[%s] running command", cmdh_str);
880  cmdhandler_handle_cmd_running(sockfd);
881  } else if (n >= 9 && strncmp(buf, "verbosity", 9) == 0) {
882  ods_log_debug("[%s] verbosity command", cmdh_str);
883  if (buf[9] == '\0') {
884  cmdhandler_handle_cmd_error(sockfd, "verbosity command "
885  "an argument (verbosity level)");
886  } else if (buf[9] != ' ') {
887  cmdhandler_handle_cmd_unknown(sockfd, buf);
888  } else {
889  cmdhandler_handle_cmd_verbosity(sockfd, cmdc, atoi(&buf[10]));
890  }
891  } else {
892  ods_log_debug("[%s] unknown command", cmdh_str);
893  cmdhandler_handle_cmd_unknown(sockfd, buf);
894  }
895 
896  ods_log_debug("[%s] done handling command %s[%i]", cmdh_str, buf, n);
897  (void)snprintf(buf, SE_CMDH_CMDLEN, "\ncmd> ");
898  ods_writen(sockfd, buf, strlen(buf));
899  }
900 
901  if (n < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) ) {
902  goto again;
903  } else if (n < 0 && errno == ECONNRESET) {
904  ods_log_debug("[%s] done handling client: %s", cmdh_str,
905  strerror(errno));
906  } else if (n < 0 ) {
907  ods_log_error("[%s] read error: %s", cmdh_str, strerror(errno));
908  }
909  return;
910 }
911 
912 
917 static void*
918 cmdhandler_accept_client(void* arg)
919 {
920  cmdhandler_type* cmdc = (cmdhandler_type*) arg;
921 
924 
925  ods_log_debug("[%s] accept client %i", cmdh_str, cmdc->client_fd);
926  cmdhandler_handle_cmd(cmdc);
927  if (cmdc->client_fd) {
928  shutdown(cmdc->client_fd, SHUT_RDWR);
929  close(cmdc->client_fd);
930  }
931  free(cmdc);
932  count--;
933  return NULL;
934 }
935 
936 
942 cmdhandler_create(allocator_type* allocator, const char* filename)
943 {
944  cmdhandler_type* cmdh = NULL;
945  struct sockaddr_un servaddr;
946  int listenfd = 0;
947  int flags = 0;
948  int ret = 0;
949 
950  if (!allocator) {
951  ods_log_error("[%s] unable to create: no allocator", cmdh_str);
952  return NULL;
953  }
954  ods_log_assert(allocator);
955 
956  if (!filename) {
957  ods_log_error("[%s] unable to create: no socket filename", cmdh_str);
958  return NULL;
959  }
960  ods_log_assert(filename);
961  ods_log_debug("[%s] create socket %s", cmdh_str, filename);
962 
963  /* new socket */
964  listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
965  if (listenfd <= 0) {
966  ods_log_error("[%s] unable to create, socket() failed: %s", cmdh_str,
967  strerror(errno));
968  return NULL;
969  }
970  /* set it to non-blocking */
971  flags = fcntl(listenfd, F_GETFL, 0);
972  if (flags < 0) {
973  ods_log_error("[%s] unable to create, fcntl(F_GETFL) failed: %s",
974  cmdh_str, strerror(errno));
975  close(listenfd);
976  return NULL;
977  }
978  flags |= O_NONBLOCK;
979  if (fcntl(listenfd, F_SETFL, flags) < 0) {
980  ods_log_error("[%s] unable to create, fcntl(F_SETFL) failed: %s",
981  cmdh_str, strerror(errno));
982  close(listenfd);
983  return NULL;
984  }
985 
986  /* no surprises */
987  if (filename) {
988  unlink(filename);
989  }
990  bzero(&servaddr, sizeof(servaddr));
991  servaddr.sun_family = AF_UNIX;
992  strncpy(servaddr.sun_path, filename, sizeof(servaddr.sun_path) - 1);
993 
994  /* bind and listen... */
995  ret = bind(listenfd, (const struct sockaddr*) &servaddr,
996  SUN_LEN(&servaddr));
997  if (ret != 0) {
998  ods_log_error("[%s] unable to create, bind() failed: %s", cmdh_str,
999  strerror(errno));
1000  close(listenfd);
1001  return NULL;
1002  }
1003  ret = listen(listenfd, ODS_SE_MAX_HANDLERS);
1004  if (ret != 0) {
1005  ods_log_error("[%s] unable to create, listen() failed: %s", cmdh_str,
1006  strerror(errno));
1007  close(listenfd);
1008  return NULL;
1009  }
1010 
1011  /* all ok */
1012  cmdh = (cmdhandler_type*) allocator_alloc(allocator,
1013  sizeof(cmdhandler_type));
1014  if (!cmdh) {
1015  close(listenfd);
1016  return NULL;
1017  }
1018  cmdh->allocator = allocator;
1019  cmdh->listen_fd = listenfd;
1020  cmdh->listen_addr = servaddr;
1021  cmdh->need_to_exit = 0;
1022  return cmdh;
1023 }
1024 
1025 
1030 void
1032 {
1033  struct sockaddr_un cliaddr;
1034  socklen_t clilen;
1035  cmdhandler_type* cmdc = NULL;
1036  engine_type* engine = NULL;
1037  fd_set rset;
1038  int connfd = 0;
1039  int ret = 0;
1040 
1041  ods_log_assert(cmdhandler);
1042  ods_log_assert(cmdhandler->engine);
1043  ods_log_debug("[%s] start", cmdh_str);
1044 
1045  engine = cmdhandler->engine;
1046  ods_thread_detach(cmdhandler->thread_id);
1047  FD_ZERO(&rset);
1048  while (cmdhandler->need_to_exit == 0) {
1049  clilen = sizeof(cliaddr);
1050  FD_SET(cmdhandler->listen_fd, &rset);
1051  ret = select(cmdhandler->listen_fd+1, &rset, NULL, NULL, NULL);
1052  if (ret < 0) {
1053  if (errno != EINTR && errno != EWOULDBLOCK) {
1054  ods_log_warning("[%s] select() error: %s", cmdh_str,
1055  strerror(errno));
1056  }
1057  continue;
1058  }
1059  if (FD_ISSET(cmdhandler->listen_fd, &rset)) {
1060  connfd = accept(cmdhandler->listen_fd,
1061  (struct sockaddr *) &cliaddr, &clilen);
1062  if (connfd < 0) {
1063  if (errno != EINTR && errno != EWOULDBLOCK) {
1064  ods_log_warning("[%s] accept error: %s", cmdh_str,
1065  strerror(errno));
1066  }
1067  continue;
1068  }
1069  /* client accepted, create new thread */
1070  cmdc = (cmdhandler_type*) malloc(sizeof(cmdhandler_type));
1071  if (!cmdc) {
1072  ods_log_crit("[%s] unable to create thread for client: "
1073  "malloc failed", cmdh_str);
1074  cmdhandler->need_to_exit = 1;
1075  break;
1076  }
1077  cmdc->listen_fd = cmdhandler->listen_fd;
1078  cmdc->client_fd = connfd;
1079  cmdc->listen_addr = cmdhandler->listen_addr;
1080  cmdc->engine = cmdhandler->engine;
1081  cmdc->need_to_exit = cmdhandler->need_to_exit;
1082  ods_thread_create(&cmdc->thread_id, &cmdhandler_accept_client,
1083  (void*) cmdc);
1084  count++;
1085  ods_log_debug("[%s] %i clients in progress...", cmdh_str, count);
1086  }
1087  }
1088 
1089  ods_log_debug("[%s] done", cmdh_str);
1090  engine = cmdhandler->engine;
1091  engine->cmdhandler_done = 1;
1092  return;
1093 }
1094 
1095 
1100 void
1102 {
1103  allocator_type* allocator;
1104  if (!cmdhandler) {
1105  return;
1106  }
1107  allocator = cmdhandler->allocator;
1108  allocator_deallocate(allocator, (void*) cmdhandler);
1109  return;
1110 }
1111