OpenDNSSEC-signer  1.3.8
/build/buildd/opendnssec-1.3.8/signer/src/daemon/cmdhandler.c
Go to the documentation of this file.
00001 /*
00002  * $Id: cmdhandler.c 6312 2012-05-08 09:54:27Z jerry $
00003  *
00004  * Copyright (c) 2009 NLNet Labs. All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00016  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00018  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00019  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00020  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00021  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00023  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00024  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00025  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  *
00027  */
00028 
00034 #include "daemon/cmdhandler.h"
00035 #include "daemon/engine.h"
00036 #include "scheduler/schedule.h"
00037 #include "scheduler/task.h"
00038 #include "shared/allocator.h"
00039 #include "shared/file.h"
00040 #include "shared/hsm.h"
00041 #include "shared/locks.h"
00042 #include "shared/log.h"
00043 #include "shared/status.h"
00044 
00045 #include <errno.h>
00046 #include <fcntl.h>
00047 #include <ldns/ldns.h>
00048 #include <stdio.h>
00049 #include <stdlib.h>
00050 #include <string.h>
00051 #include <strings.h>
00052 #include <sys/select.h>
00053 #include <sys/socket.h>
00054 #ifdef HAVE_SYS_TYPES_H
00055 # include <sys/types.h>
00056 #endif
00057 #include <unistd.h>
00058 /* According to earlier standards: select() sys/time.h sys/types.h unistd.h */
00059 #include <sys/time.h>
00060 #include <sys/types.h>
00061 
00062 #define SE_CMDH_CMDLEN 7
00063 
00064 #ifndef SUN_LEN
00065 #define SUN_LEN(su)  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
00066 #endif
00067 
00068 static int count = 0;
00069 static char* cmdh_str = "cmdhandler";
00070 
00071 
00076 static void
00077 cmdhandler_handle_cmd_help(int sockfd)
00078 {
00079     char buf[ODS_SE_MAXLINE];
00080 
00081     (void) snprintf(buf, ODS_SE_MAXLINE,
00082         "Commands:\n"
00083         "zones           show the currently known zones.\n"
00084         "sign <zone>     read zone and schedule for immediate (re-)sign.\n"
00085         "sign --all      read all zones and schedule all for immediate "
00086                          "(re-)sign.\n"
00087         "clear <zone>    delete the internal storage of this zone.\n"
00088         "                All signatures will be regenerated on the next "
00089                          "re-sign.\n"
00090         "queue           show the current task queue.\n"
00091     );
00092     ods_writen(sockfd, buf, strlen(buf));
00093 
00094     (void) snprintf(buf, ODS_SE_MAXLINE,
00095         "flush           execute all scheduled tasks immediately.\n"
00096         "update <zone>   update this zone signer configurations.\n"
00097         "update [--all]  update zone list and all signer configurations.\n"
00098         "start           start the engine.\n"
00099         "running         check if the engine is running.\n"
00100         "reload          reload the engine.\n"
00101         "stop            stop the engine.\n"
00102         "verbosity <nr>  set verbosity.\n"
00103     );
00104     ods_writen(sockfd, buf, strlen(buf));
00105     return;
00106 }
00107 
00108 
00113 static void
00114 cmdhandler_handle_cmd_zones(int sockfd, cmdhandler_type* cmdc)
00115 {
00116     char buf[ODS_SE_MAXLINE];
00117     size_t i;
00118     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
00119     zone_type* zone = NULL;
00120 
00121     ods_log_assert(cmdc);
00122     ods_log_assert(cmdc->engine);
00123     if (!cmdc->engine->zonelist || !cmdc->engine->zonelist->zones) {
00124         (void)snprintf(buf, ODS_SE_MAXLINE, "I have no zones configured\n");
00125         ods_writen(sockfd, buf, strlen(buf));
00126         return;
00127     }
00128 
00129     lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00130     /* how many zones */
00131     /* [LOCK] zonelist */
00132     (void)snprintf(buf, ODS_SE_MAXLINE, "I have %i zones configured\n",
00133         (int) cmdc->engine->zonelist->zones->count);
00134     ods_writen(sockfd, buf, strlen(buf));
00135 
00136     /* list zones */
00137     node = ldns_rbtree_first(cmdc->engine->zonelist->zones);
00138     while (node && node != LDNS_RBTREE_NULL) {
00139         zone = (zone_type*) node->data;
00140         for (i=0; i < ODS_SE_MAXLINE; i++) {
00141             buf[i] = 0;
00142         }
00143         (void)snprintf(buf, ODS_SE_MAXLINE, "- %s\n", zone->name);
00144         ods_writen(sockfd, buf, strlen(buf));
00145         node = ldns_rbtree_next(node);
00146     }
00147     /* [UNLOCK] zonelist */
00148     lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00149     return;
00150 }
00151 
00152 
00157 static void
00158 cmdhandler_handle_cmd_update(int sockfd, cmdhandler_type* cmdc,
00159     const char* tbd)
00160 {
00161     char buf[ODS_SE_MAXLINE];
00162     ods_status status = ODS_STATUS_OK;
00163     zone_type* zone = NULL;
00164     task_type* task = NULL;
00165     int zl_changed = 0;
00166 
00167     ods_log_assert(tbd);
00168     ods_log_assert(cmdc);
00169     ods_log_assert(cmdc->engine);
00170     ods_log_assert(cmdc->engine->taskq);
00171 
00172     if (ods_strcmp(tbd, "--all") == 0) {
00173         /* [LOCK] zonelist */
00174         lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00175         zl_changed = zonelist_update(cmdc->engine->zonelist,
00176             cmdc->engine->config->zonelist_filename);
00177         /* [UNLOCK] zonelist */
00178         if (zl_changed == ODS_STATUS_UNCHANGED) {
00179             lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00180             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has not changed."
00181                 " Signer configurations updated.\n");
00182             ods_writen(sockfd, buf, strlen(buf));
00183 
00184             engine_update_zones(cmdc->engine);
00185             ods_log_debug("[%s] signer configurations updated", cmdh_str);
00186         } else if (zl_changed == ODS_STATUS_OK) {
00187             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list updated: %i "
00188             "removed, %i added, %i updated.\n",
00189                 cmdc->engine->zonelist->just_removed,
00190                 cmdc->engine->zonelist->just_added,
00191                 cmdc->engine->zonelist->just_updated);
00192             ods_writen(sockfd, buf, strlen(buf));
00193 
00194             cmdc->engine->zonelist->just_removed = 0;
00195             cmdc->engine->zonelist->just_added = 0;
00196             cmdc->engine->zonelist->just_updated = 0;
00197             lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00198 
00199             ods_log_debug("[%s] commit zone list changes", cmdh_str);
00200             engine_update_zones(cmdc->engine);
00201             ods_log_debug("[%s] signer configurations updated", cmdh_str);
00202         } else {
00203             lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00204             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has errors.\n");
00205             ods_writen(sockfd, buf, strlen(buf));
00206         }
00207         return;
00208     } else {
00209         /* look up zone */
00210         lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00211         /* [LOCK] zonelist */
00212         zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
00213             LDNS_RR_CLASS_IN);
00214         /* If this zone is just added, don't update (it might not have a
00215          * task yet) */
00216         if (zone && zone->just_added) {
00217             zone = NULL;
00218         }
00219         /* [UNLOCK] zonelist */
00220         lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00221         if (!zone) {
00222             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
00223                 tbd);
00224             ods_writen(sockfd, buf, strlen(buf));
00225             /* update all */
00226             cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
00227             return;
00228         }
00229 
00230         lock_basic_lock(&zone->zone_lock);
00231         ods_log_assert(zone->task);
00232 
00233         lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00234         /* [LOCK] schedule */
00235         task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
00236         if (task != NULL) {
00237             ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
00238                 zone->name);
00239             if (task->what != TASK_SIGNCONF) {
00240                 task->halted = task->what;
00241                 task->interrupt = TASK_SIGNCONF;
00242             }
00243             task->what = TASK_SIGNCONF;
00244             task->when = time_now();
00245             status = schedule_task(cmdc->engine->taskq, task, 0);
00246             zone->task = task;
00247         } else {
00248             /* task not queued, being worked on? */
00249             ods_log_verbose("[%s] worker busy with zone %s, will update "
00250                 "signconf as soon as possible", cmdh_str, zone->name);
00251             task = (task_type*) zone->task;
00252             task->interrupt = TASK_SIGNCONF;
00253             /* task->halted set by worker */
00254         }
00255         /* [UNLOCK] schedule */
00256         lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00257 
00258         lock_basic_unlock(&zone->zone_lock);
00259 
00260         if (status != ODS_STATUS_OK) {
00261             ods_log_crit("[%s] cannot schedule task for zone %s: %s",
00262                 cmdh_str, zone->name, ods_status2str(status));
00263             task_cleanup(task);
00264             zone->task = NULL;
00265         } else {
00266             engine_wakeup_workers(cmdc->engine);
00267         }
00268 
00269         (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s config being updated.\n",
00270             tbd);
00271         ods_writen(sockfd, buf, strlen(buf));
00272     }
00273     return;
00274 }
00275 
00276 
00281 static void
00282 cmdhandler_handle_cmd_sign(int sockfd, cmdhandler_type* cmdc, const char* tbd)
00283 {
00284     zone_type* zone = NULL;
00285     task_type* task = NULL;
00286     ods_status status = ODS_STATUS_OK;
00287     char buf[ODS_SE_MAXLINE];
00288 
00289     ods_log_assert(tbd);
00290     ods_log_assert(cmdc);
00291     ods_log_assert(cmdc->engine);
00292     ods_log_assert(cmdc->engine->taskq);
00293 
00294     if (ods_strcmp(tbd, "--all") == 0) {
00295         lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00296         /* [LOCK] schedule */
00297         schedule_flush(cmdc->engine->taskq, TASK_READ);
00298         /* [UNLOCK] schedule */
00299         lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00300         engine_wakeup_workers(cmdc->engine);
00301         (void)snprintf(buf, ODS_SE_MAXLINE, "All zones scheduled for "
00302             "immediate re-sign.\n");
00303         ods_writen(sockfd, buf, strlen(buf));
00304         ods_log_verbose("[%s] all zones scheduled for immediate re-sign",
00305             cmdh_str);
00306         return;
00307     } else {
00308         lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00309         /* [LOCK] zonelist */
00310         zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
00311             LDNS_RR_CLASS_IN);
00312         /* If this zone is just added, don't update (it might not have a task yet) */
00313         if (zone && zone->just_added) {
00314             zone = NULL;
00315         }
00316         /* [UNLOCK] zonelist */
00317         lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00318 
00319         if (!zone) {
00320             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
00321                 tbd);
00322             ods_writen(sockfd, buf, strlen(buf));
00323             return;
00324         }
00325 
00326         lock_basic_lock(&zone->zone_lock);
00327         ods_log_assert(zone->task);
00328 
00329         lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00330         /* [LOCK] schedule */
00331         task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
00332         if (task != NULL) {
00333             ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
00334                 zone->name);
00335             if (task->what != TASK_READ) {
00336                 task->halted = task->what;
00337                 task->interrupt = TASK_READ;
00338             }
00339             task->what = TASK_READ;
00340             task->when = time_now();
00341             status = schedule_task(cmdc->engine->taskq, task, 0);
00342         } else {
00343             /* task now queued, being worked on? */
00344             ods_log_verbose("[%s] worker busy with zone %s, will read "
00345                 "zone input as soon as possible", cmdh_str, zone->name);
00346             task = (task_type*) zone->task;
00347             task->interrupt = TASK_READ;
00348             /* task->halted set by worker */
00349         }
00350         /* [UNLOCK] schedule */
00351         lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00352 
00353         zone->task = task;
00354         lock_basic_unlock(&zone->zone_lock);
00355 
00356         if (status != ODS_STATUS_OK) {
00357             (void)snprintf(buf, ODS_SE_MAXLINE, "Error: Cannot schedule task for "
00358                 "zone %s.\n", tbd);
00359             ods_writen(sockfd, buf, strlen(buf));
00360             ods_log_crit("[%s] cannot schedule task for zone %s: %s",
00361                 cmdh_str, zone->name, ods_status2str(status));
00362             task_cleanup(task);
00363             zone->task = NULL;
00364         } else {
00365             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s scheduled for immediate "
00366                 "re-sign.\n", tbd);
00367             ods_writen(sockfd, buf, strlen(buf));
00368             ods_log_verbose("[%s] zone %s scheduled for immediate re-sign",
00369                 cmdh_str, tbd);
00370             engine_wakeup_workers(cmdc->engine);
00371         }
00372     }
00373     return;
00374 }
00375 
00376 
00381 static void
00382 unlink_backup_file(const char* filename, const char* extension)
00383 {
00384     char* tmpname = ods_build_path(filename, extension, 0, 1);
00385     ods_log_debug("[%s] unlink file %s", cmdh_str, tmpname);
00386     unlink(tmpname);
00387     free((void*)tmpname);
00388     return;
00389 }
00390 
00395 static void
00396 cmdhandler_handle_cmd_clear(int sockfd, cmdhandler_type* cmdc, const char* tbd)
00397 {
00398     char buf[ODS_SE_MAXLINE];
00399     zone_type* zone = NULL;
00400     task_type* task = NULL;
00401     uint32_t inbound_serial = 0;
00402     uint32_t internal_serial = 0;
00403     uint32_t outbound_serial = 0;
00404     ods_status status = ODS_STATUS_OK;
00405 
00406     ods_log_assert(tbd);
00407     ods_log_assert(cmdc);
00408     ods_log_assert(cmdc->engine);
00409 
00410     unlink_backup_file(tbd, ".inbound");
00411     unlink_backup_file(tbd, ".backup");
00412 
00413     lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00414     /* [LOCK] zonelist */
00415     zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
00416         LDNS_RR_CLASS_IN);
00417     /* [UNLOCK] zonelist */
00418     lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00419     if (zone) {
00420         /* [LOCK] zone */
00421         lock_basic_lock(&zone->zone_lock);
00422         inbound_serial = zone->zonedata->inbound_serial;
00423         internal_serial = zone->zonedata->internal_serial;
00424         outbound_serial = zone->zonedata->outbound_serial;
00425         zonedata_cleanup(zone->zonedata);
00426         zone->zonedata = NULL;
00427         zone->zonedata = zonedata_create(zone->allocator);
00428         zone->zonedata->initialized = 1;
00429         zone->zonedata->inbound_serial = inbound_serial;
00430         zone->zonedata->internal_serial = internal_serial;
00431         zone->zonedata->outbound_serial = outbound_serial;
00432 
00437         lhsm_check_connection((void*)cmdc->engine);
00438         status = zone_publish_dnskeys(zone, 1);
00439         if (status == ODS_STATUS_OK) {
00440             status = zone_prepare_nsec3(zone, 1);
00441         } else {
00442             ods_log_warning("[%s] unable to restore DNSKEY RRset for zone %s,"
00443                 " reloading signconf", cmdh_str, zone->name);
00444         }
00445         if (status == ODS_STATUS_OK) {
00446             status = zonedata_commit(zone->zonedata);
00447         } else {
00448             ods_log_warning("[%s] unable to restore NSEC3PARAM RRset for "
00449                 " zone %s, reloading signconf", cmdh_str, zone->name);
00450         }
00451 
00452         task = (task_type*) zone->task;
00453         task->what = TASK_READ;
00454         if (status != ODS_STATUS_OK) {
00455             ods_log_warning("[%s] unable to restore DNSKEY/NSEC3PARAM RRset "
00456                 " for zone %s, reloading signconf", cmdh_str, zone->name);
00457             task->what = TASK_SIGNCONF;
00458         }
00459         /* [UNLOCK] zone */
00460         lock_basic_unlock(&zone->zone_lock);
00461 
00462         (void)snprintf(buf, ODS_SE_MAXLINE, "Internal zone information about "
00463             "%s cleared", tbd?tbd:"(null)");
00464         ods_log_info("[%s] internal zone information about %s cleared",
00465             cmdh_str, tbd?tbd:"(null)");
00466     } else {
00467         (void)snprintf(buf, ODS_SE_MAXLINE, "Cannot clear zone %s, zone not "
00468             "found", tbd?tbd:"(null)");
00469         ods_log_warning("[%s] cannot clear zone %s, zone not found",
00470             cmdh_str, tbd?tbd:"(null)");
00471     }
00472 
00473     ods_writen(sockfd, buf, strlen(buf));
00474     return;
00475 }
00476 
00477 
00482 static void
00483 cmdhandler_handle_cmd_queue(int sockfd, cmdhandler_type* cmdc)
00484 {
00485     char* strtime = NULL;
00486     char buf[ODS_SE_MAXLINE];
00487     size_t i = 0;
00488     time_t now = 0;
00489     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
00490     task_type* task = NULL;
00491 
00492     ods_log_assert(cmdc);
00493     ods_log_assert(cmdc->engine);
00494     if (!cmdc->engine->taskq || !cmdc->engine->taskq->tasks) {
00495         (void)snprintf(buf, ODS_SE_MAXLINE, "I have no tasks scheduled.\n");
00496         ods_writen(sockfd, buf, strlen(buf));
00497         return;
00498     }
00499 
00500     lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00501     /* [LOCK] schedule */
00502 
00503     /* time */
00504     now = time_now();
00505     strtime = ctime(&now);
00506     (void)snprintf(buf, ODS_SE_MAXLINE, "It is now %s",
00507         strtime?strtime:"(null)");
00508     ods_writen(sockfd, buf, strlen(buf));
00509 
00510     /* current work */
00511     for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
00512         task = cmdc->engine->workers[i]->task;
00513         if (task) {
00514             (void)snprintf(buf, ODS_SE_MAXLINE, "Working with task %s on "
00515                 "zone %s\n",
00516                 task_what2str(cmdc->engine->workers[i]->working_with),
00517                 task_who2str(task->who));
00518             ods_writen(sockfd, buf, strlen(buf));
00519         }
00520     }
00521 
00522     /* how many tasks */
00523     (void)snprintf(buf, ODS_SE_MAXLINE, "\nI have %i tasks scheduled.\n",
00524         (int) cmdc->engine->taskq->tasks->count);
00525     ods_writen(sockfd, buf, strlen(buf));
00526 
00527     /* list tasks */
00528     node = ldns_rbtree_first(cmdc->engine->taskq->tasks);
00529     while (node && node != LDNS_RBTREE_NULL) {
00530         task = (task_type*) node->data;
00531         for (i=0; i < ODS_SE_MAXLINE; i++) {
00532             buf[i] = 0;
00533         }
00534         (void)task2str(task, (char*) &buf[0]);
00535         ods_writen(sockfd, buf, strlen(buf));
00536         node = ldns_rbtree_next(node);
00537     }
00538 
00539     /* [UNLOCK] schedule */
00540     lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00541     return;
00542 }
00543 
00544 
00549 static void
00550 cmdhandler_handle_cmd_flush(int sockfd, cmdhandler_type* cmdc)
00551 {
00552     char buf[ODS_SE_MAXLINE];
00553 
00554     ods_log_assert(cmdc);
00555     ods_log_assert(cmdc->engine);
00556     ods_log_assert(cmdc->engine->taskq);
00557 
00558     lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00559     /* [LOCK] schedule */
00560     schedule_flush(cmdc->engine->taskq, TASK_NONE);
00561     /* [UNLOCK] schedule */
00562     lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00563 
00564     engine_wakeup_workers(cmdc->engine);
00565 
00566     (void)snprintf(buf, ODS_SE_MAXLINE, "All tasks scheduled immediately.\n");
00567     ods_writen(sockfd, buf, strlen(buf));
00568     ods_log_verbose("[%s] all tasks scheduled immediately", cmdh_str);
00569     return;
00570 }
00571 
00572 
00577 static void
00578 cmdhandler_handle_cmd_reload(int sockfd, cmdhandler_type* cmdc)
00579 {
00580     char buf[ODS_SE_MAXLINE];
00581 
00582     ods_log_assert(cmdc);
00583     ods_log_assert(cmdc->engine);
00584 
00585     cmdc->engine->need_to_reload = 1;
00586 
00587     lock_basic_lock(&cmdc->engine->signal_lock);
00588     /* [LOCK] signal */
00589     lock_basic_alarm(&cmdc->engine->signal_cond);
00590     /* [UNLOCK] signal */
00591     lock_basic_unlock(&cmdc->engine->signal_lock);
00592 
00593     (void)snprintf(buf, ODS_SE_MAXLINE, "Reloading engine.\n");
00594     ods_writen(sockfd, buf, strlen(buf));
00595     return;
00596 }
00597 
00598 
00603 static void
00604 cmdhandler_handle_cmd_stop(int sockfd, cmdhandler_type* cmdc)
00605 {
00606     char buf[ODS_SE_MAXLINE];
00607 
00608     ods_log_assert(cmdc);
00609     ods_log_assert(cmdc->engine);
00610 
00611     cmdc->engine->need_to_exit = 1;
00612 
00613     lock_basic_lock(&cmdc->engine->signal_lock);
00614     /* [LOCK] signal */
00615     lock_basic_alarm(&cmdc->engine->signal_cond);
00616     /* [UNLOCK] signal */
00617     lock_basic_unlock(&cmdc->engine->signal_lock);
00618 
00619     (void)snprintf(buf, ODS_SE_MAXLINE, ODS_SE_STOP_RESPONSE);
00620     ods_writen(sockfd, buf, strlen(buf));
00621     return;
00622 }
00623 
00624 
00629 static void
00630 cmdhandler_handle_cmd_start(int sockfd)
00631 {
00632     char buf[ODS_SE_MAXLINE];
00633 
00634     (void)snprintf(buf, ODS_SE_MAXLINE, "Engine already running.\n");
00635     ods_writen(sockfd, buf, strlen(buf));
00636     return;
00637 }
00638 
00639 
00644 static void
00645 cmdhandler_handle_cmd_running(int sockfd)
00646 {
00647     char buf[ODS_SE_MAXLINE];
00648 
00649     (void)snprintf(buf, ODS_SE_MAXLINE, "Engine running.\n");
00650     ods_writen(sockfd, buf, strlen(buf));
00651     return;
00652 }
00653 
00654 
00659 static void
00660 cmdhandler_handle_cmd_verbosity(int sockfd, cmdhandler_type* cmdc, int val)
00661 {
00662     char buf[ODS_SE_MAXLINE];
00663 
00664     ods_log_assert(cmdc);
00665     ods_log_assert(cmdc->engine);
00666     ods_log_assert(cmdc->engine->config);
00667 
00668     ods_log_init(cmdc->engine->config->log_filename,
00669         cmdc->engine->config->use_syslog, val);
00670 
00671     (void)snprintf(buf, ODS_SE_MAXLINE, "Verbosity level set to %i.\n", val);
00672     ods_writen(sockfd, buf, strlen(buf));
00673 }
00674 
00675 
00680 static void
00681 cmdhandler_handle_cmd_error(int sockfd, const char* str)
00682 {
00683     char buf[ODS_SE_MAXLINE];
00684     (void)snprintf(buf, ODS_SE_MAXLINE, "Error: %s.\n", str?str:"(null)");
00685     ods_writen(sockfd, buf, strlen(buf));
00686     return;
00687 }
00688 
00689 
00694 static void
00695 cmdhandler_handle_cmd_unknown(int sockfd, const char* str)
00696 {
00697     char buf[ODS_SE_MAXLINE];
00698     (void)snprintf(buf, ODS_SE_MAXLINE, "Unknown command %s.\n",
00699         str?str:"(null)");
00700     ods_writen(sockfd, buf, strlen(buf));
00701     return;
00702 }
00703 
00704 
00723 static void
00724 cmdhandler_handle_cmd(cmdhandler_type* cmdc)
00725 {
00726     ssize_t n = 0;
00727     int sockfd = 0;
00728     char buf[ODS_SE_MAXLINE];
00729 
00730     ods_log_assert(cmdc);
00731     sockfd = cmdc->client_fd;
00732 
00733 again:
00734     while ((n = read(sockfd, buf, ODS_SE_MAXLINE)) > 0) {
00735         buf[n-1] = '\0';
00736         n--;
00737         if (n <= 0) {
00738             return;
00739         }
00740         ods_log_verbose("[%s] received command %s[%i]", cmdh_str, buf, n);
00741 
00742         if (n == 4 && strncmp(buf, "help", n) == 0) {
00743             ods_log_debug("[%s] help command", cmdh_str);
00744             cmdhandler_handle_cmd_help(sockfd);
00745         } else if (n == 5 && strncmp(buf, "zones", n) == 0) {
00746             ods_log_debug("[%s] list zones command", cmdh_str);
00747             cmdhandler_handle_cmd_zones(sockfd, cmdc);
00748         } else if (n >= 4 && strncmp(buf, "sign", 4) == 0) {
00749             ods_log_debug("[%s] sign zone command", cmdh_str);
00750             if (buf[4] == '\0') {
00751                 /* NOTE: wouldn't it be nice that we default to --all? */
00752                 cmdhandler_handle_cmd_error(sockfd, "sign command needs "
00753                     "an argument (either '--all' or a zone name)");
00754             } else if (buf[4] != ' ') {
00755                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00756             } else {
00757                 cmdhandler_handle_cmd_sign(sockfd, cmdc, &buf[5]);
00758             }
00759         } else if (n >= 5 && strncmp(buf, "clear", 5) == 0) {
00760             ods_log_debug("[%s] clear zone command", cmdh_str);
00761             if (buf[5] == '\0') {
00762                 cmdhandler_handle_cmd_error(sockfd, "clear command needs "
00763                     "a zone name");
00764             } else if (buf[5] != ' ') {
00765                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00766             } else {
00767                 cmdhandler_handle_cmd_clear(sockfd, cmdc, &buf[6]);
00768             }
00769         } else if (n == 5 && strncmp(buf, "queue", n) == 0) {
00770             ods_log_debug("[%s] list tasks command", cmdh_str);
00771             cmdhandler_handle_cmd_queue(sockfd, cmdc);
00772         } else if (n == 5 && strncmp(buf, "flush", n) == 0) {
00773             ods_log_debug("[%s] flush tasks command", cmdh_str);
00774             cmdhandler_handle_cmd_flush(sockfd, cmdc);
00775         } else if (n >= 6 && strncmp(buf, "update", 6) == 0) {
00776             ods_log_debug("[%s] update command", cmdh_str);
00777             if (buf[6] == '\0') {
00778                 cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
00779             } else if (buf[6] != ' ') {
00780                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00781             } else {
00782                 cmdhandler_handle_cmd_update(sockfd, cmdc, &buf[7]);
00783             }
00784         } else if (n == 4 && strncmp(buf, "stop", n) == 0) {
00785             ods_log_debug("[%s] shutdown command", cmdh_str);
00786             cmdhandler_handle_cmd_stop(sockfd, cmdc);
00787             return;
00788         } else if (n == 5 && strncmp(buf, "start", n) == 0) {
00789             ods_log_debug("[%s] start command", cmdh_str);
00790             cmdhandler_handle_cmd_start(sockfd);
00791         } else if (n == 6 && strncmp(buf, "reload", n) == 0) {
00792             ods_log_debug("[%s] reload command", cmdh_str);
00793             cmdhandler_handle_cmd_reload(sockfd, cmdc);
00794         } else if (n == 7 && strncmp(buf, "running", n) == 0) {
00795             ods_log_debug("[%s] running command", cmdh_str);
00796             cmdhandler_handle_cmd_running(sockfd);
00797         } else if (n >= 9 && strncmp(buf, "verbosity", 9) == 0) {
00798             ods_log_debug("[%s] verbosity command", cmdh_str);
00799             if (buf[9] == '\0') {
00800                 cmdhandler_handle_cmd_error(sockfd, "verbosity command "
00801                     "an argument (verbosity level)");
00802             } else if (buf[9] != ' ') {
00803                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00804             } else {
00805                 cmdhandler_handle_cmd_verbosity(sockfd, cmdc, atoi(&buf[10]));
00806             }
00807         } else {
00808             ods_log_debug("[%s] unknown command", cmdh_str);
00809             cmdhandler_handle_cmd_unknown(sockfd, buf);
00810         }
00811 
00812         ods_log_debug("[%s] done handling command %s[%i]", cmdh_str, buf, n);
00813         (void)snprintf(buf, SE_CMDH_CMDLEN, "\ncmd> ");
00814         ods_writen(sockfd, buf, strlen(buf));
00815     }
00816 
00817     if (n < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) ) {
00818         goto again;
00819     } else if (n < 0 && errno == ECONNRESET) {
00820         ods_log_debug("[%s] done handling client: %s", cmdh_str,
00821             strerror(errno));
00822     } else if (n < 0 ) {
00823         ods_log_error("[%s] read error: %s", cmdh_str, strerror(errno));
00824     }
00825     return;
00826 }
00827 
00828 
00833 static void*
00834 cmdhandler_accept_client(void* arg)
00835 {
00836     cmdhandler_type* cmdc = (cmdhandler_type*) arg;
00837 
00838     ods_thread_blocksigs();
00839     ods_thread_detach(cmdc->thread_id);
00840 
00841     ods_log_debug("[%s] accept client %i", cmdh_str, cmdc->client_fd);
00842     cmdhandler_handle_cmd(cmdc);
00843     if (cmdc->client_fd) {
00844         close(cmdc->client_fd);
00845     }
00846     free(cmdc);
00847     count--;
00848     return NULL;
00849 }
00850 
00851 
00856 cmdhandler_type*
00857 cmdhandler_create(allocator_type* allocator, const char* filename)
00858 {
00859     cmdhandler_type* cmdh = NULL;
00860     struct sockaddr_un servaddr;
00861     int listenfd = 0;
00862     int flags = 0;
00863     int ret = 0;
00864 
00865     if (!allocator) {
00866         ods_log_error("[%s] unable to create: no allocator", cmdh_str);
00867         return NULL;
00868     }
00869     ods_log_assert(allocator);
00870 
00871     if (!filename) {
00872         ods_log_error("[%s] unable to create: no socket filename", cmdh_str);
00873         return NULL;
00874     }
00875     ods_log_assert(filename);
00876     ods_log_debug("[%s] create socket %s", cmdh_str, filename);
00877 
00878     /* new socket */
00879     listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
00880     if (listenfd <= 0) {
00881         ods_log_error("[%s] unable to create, socket() failed: %s", cmdh_str,
00882             strerror(errno));
00883         return NULL;
00884     }
00885     /* set it to non-blocking */
00886     flags = fcntl(listenfd, F_GETFL, 0);
00887     if (flags < 0) {
00888         ods_log_error("[%s] unable to create, fcntl(F_GETFL) failed: %s",
00889             cmdh_str, strerror(errno));
00890         close(listenfd);
00891         return NULL;
00892     }
00893     flags |= O_NONBLOCK;
00894     if (fcntl(listenfd, F_SETFL, flags) < 0) {
00895         ods_log_error("[%s] unable to create, fcntl(F_SETFL) failed: %s",
00896             cmdh_str, strerror(errno));
00897         close(listenfd);
00898         return NULL;
00899     }
00900 
00901     /* no surprises */
00902     if (filename) {
00903         unlink(filename);
00904     }
00905     bzero(&servaddr, sizeof(servaddr));
00906     servaddr.sun_family = AF_UNIX;
00907     strncpy(servaddr.sun_path, filename, sizeof(servaddr.sun_path) - 1);
00908 
00909     /* bind and listen... */
00910     ret = bind(listenfd, (const struct sockaddr*) &servaddr,
00911         SUN_LEN(&servaddr));
00912     if (ret != 0) {
00913         ods_log_error("[%s] unable to create, bind() failed: %s", cmdh_str,
00914             strerror(errno));
00915         close(listenfd);
00916         return NULL;
00917     }
00918     ret = listen(listenfd, ODS_SE_MAX_HANDLERS);
00919     if (ret != 0) {
00920         ods_log_error("[%s] unable to create, listen() failed: %s", cmdh_str,
00921             strerror(errno));
00922         close(listenfd);
00923         return NULL;
00924     }
00925 
00926     /* all ok */
00927     cmdh = (cmdhandler_type*) allocator_alloc(allocator,
00928         sizeof(cmdhandler_type));
00929     if (!cmdh) {
00930         close(listenfd);
00931         return NULL;
00932     }
00933     cmdh->allocator = allocator;
00934     cmdh->listen_fd = listenfd;
00935     cmdh->listen_addr = servaddr;
00936     cmdh->need_to_exit = 0;
00937     return cmdh;
00938 }
00939 
00940 
00945 void
00946 cmdhandler_start(cmdhandler_type* cmdhandler)
00947 {
00948     struct sockaddr_un cliaddr;
00949     socklen_t clilen;
00950     cmdhandler_type* cmdc = NULL;
00951     engine_type* engine = NULL;
00952     fd_set rset;
00953     int connfd = 0;
00954     int ret = 0;
00955 
00956     ods_log_assert(cmdhandler);
00957     ods_log_assert(cmdhandler->engine);
00958     ods_log_debug("[%s] start", cmdh_str);
00959 
00960     engine = cmdhandler->engine;
00961     ods_thread_detach(cmdhandler->thread_id);
00962     FD_ZERO(&rset);
00963     while (cmdhandler->need_to_exit == 0) {
00964         clilen = sizeof(cliaddr);
00965         FD_SET(cmdhandler->listen_fd, &rset);
00966         ret = select(ODS_SE_MAX_HANDLERS+1, &rset, NULL, NULL, NULL);
00967         if (ret < 0) {
00968             if (errno != EINTR && errno != EWOULDBLOCK) {
00969                 ods_log_warning("[%s] select() error: %s", cmdh_str,
00970                    strerror(errno));
00971             }
00972             continue;
00973         }
00974         if (FD_ISSET(cmdhandler->listen_fd, &rset)) {
00975             connfd = accept(cmdhandler->listen_fd,
00976                 (struct sockaddr *) &cliaddr, &clilen);
00977             if (connfd < 0) {
00978                 if (errno != EINTR && errno != EWOULDBLOCK) {
00979                     ods_log_warning("[%s] accept error: %s", cmdh_str,
00980                         strerror(errno));
00981                 }
00982                 continue;
00983             }
00984             /* client accepted, create new thread */
00985             cmdc = (cmdhandler_type*) malloc(sizeof(cmdhandler_type));
00986             if (!cmdc) {
00987                 ods_log_crit("[%s] unable to create thread for client: "
00988                     "malloc failed", cmdh_str);
00989                 cmdhandler->need_to_exit = 1;
00990                 break;
00991             }
00992             cmdc->listen_fd = cmdhandler->listen_fd;
00993             cmdc->client_fd = connfd;
00994             cmdc->listen_addr = cmdhandler->listen_addr;
00995             cmdc->engine = cmdhandler->engine;
00996             cmdc->need_to_exit = cmdhandler->need_to_exit;
00997             ods_thread_create(&cmdc->thread_id, &cmdhandler_accept_client,
00998                 (void*) cmdc);
00999             count++;
01000             ods_log_debug("[%s] %i clients in progress...", cmdh_str, count);
01001         }
01002     }
01003 
01004     ods_log_debug("[%s] done", cmdh_str);
01005     engine = cmdhandler->engine;
01006     engine->cmdhandler_done = 1;
01007     return;
01008 }
01009 
01010 
01015 void
01016 cmdhandler_cleanup(cmdhandler_type* cmdhandler)
01017 {
01018     allocator_type* allocator;
01019     if (!cmdhandler) {
01020         return;
01021     }
01022     allocator = cmdhandler->allocator;
01023     allocator_deallocate(allocator, (void*) cmdhandler);
01024     return;
01025 }
01026