OpenDNSSEC-signer 1.3.0
/build/buildd/opendnssec-1.3.0/signer/src/daemon/cmdhandler.c
Go to the documentation of this file.
00001 /*
00002  * $Id: cmdhandler.c 5320 2011-07-12 10:42:26Z jakob $
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/locks.h"
00041 #include "shared/log.h"
00042 #include "shared/status.h"
00043 
00044 #include <errno.h>
00045 #include <fcntl.h>
00046 #include <ldns/ldns.h>
00047 #include <stdio.h>
00048 #include <stdlib.h>
00049 #include <string.h>
00050 #include <strings.h>
00051 #include <sys/select.h>
00052 #include <sys/socket.h>
00053 #ifdef HAVE_SYS_TYPES_H
00054 # include <sys/types.h>
00055 #endif
00056 #include <unistd.h>
00057 /* According to earlier standards: select() sys/time.h sys/types.h unistd.h */
00058 #include <sys/time.h>
00059 #include <sys/types.h>
00060 
00061 #define SE_CMDH_CMDLEN 7
00062 
00063 #ifndef SUN_LEN
00064 #define SUN_LEN(su)  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
00065 #endif
00066 
00067 static int count = 0;
00068 static char* cmdh_str = "cmdhandler";
00069 
00070 
00075 static void
00076 cmdhandler_handle_cmd_help(int sockfd)
00077 {
00078     char buf[ODS_SE_MAXLINE];
00079 
00080     (void) snprintf(buf, ODS_SE_MAXLINE,
00081         "Commands:\n"
00082         "zones           show the currently known zones.\n"
00083         "sign <zone>     read zone and schedule for immediate (re-)sign.\n"
00084         "sign --all      read all zones and schedule all for immediate "
00085                          "(re-)sign.\n"
00086         "clear <zone>    delete the internal storage of this zone.\n"
00087         "                All signatures will be regenerated on the next "
00088                          "re-sign.\n"
00089         "queue           show the current task queue.\n"
00090     );
00091     ods_writen(sockfd, buf, strlen(buf));
00092 
00093     (void) snprintf(buf, ODS_SE_MAXLINE,
00094         "flush           execute all scheduled tasks immediately.\n"
00095         "update <zone>   update this zone signer configurations.\n"
00096         "update [--all]  update zone list and all signer configurations.\n"
00097         "start           start the engine.\n"
00098         "running         check if the engine is running.\n"
00099         "reload          reload the engine.\n"
00100         "stop            stop the engine.\n"
00101         "verbosity <nr>  set verbosity.\n"
00102     );
00103     ods_writen(sockfd, buf, strlen(buf));
00104     return;
00105 }
00106 
00107 
00112 static void
00113 cmdhandler_handle_cmd_zones(int sockfd, cmdhandler_type* cmdc)
00114 {
00115     char buf[ODS_SE_MAXLINE];
00116     size_t i;
00117     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
00118     zone_type* zone = NULL;
00119 
00120     ods_log_assert(cmdc);
00121     ods_log_assert(cmdc->engine);
00122     if (!cmdc->engine->zonelist || !cmdc->engine->zonelist->zones) {
00123         (void)snprintf(buf, ODS_SE_MAXLINE, "I have no zones configured\n");
00124         ods_writen(sockfd, buf, strlen(buf));
00125         return;
00126     }
00127 
00128     lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00129     /* how many zones */
00130     /* [LOCK] zonelist */
00131     (void)snprintf(buf, ODS_SE_MAXLINE, "I have %i zones configured\n",
00132         (int) cmdc->engine->zonelist->zones->count);
00133     ods_writen(sockfd, buf, strlen(buf));
00134 
00135     /* list zones */
00136     node = ldns_rbtree_first(cmdc->engine->zonelist->zones);
00137     while (node && node != LDNS_RBTREE_NULL) {
00138         zone = (zone_type*) node->data;
00139         for (i=0; i < ODS_SE_MAXLINE; i++) {
00140             buf[i] = 0;
00141         }
00142         (void)snprintf(buf, ODS_SE_MAXLINE, "- %s\n", zone->name);
00143         ods_writen(sockfd, buf, strlen(buf));
00144         node = ldns_rbtree_next(node);
00145     }
00146     /* [UNLOCK] zonelist */
00147     lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00148     return;
00149 }
00150 
00151 
00156 static void
00157 cmdhandler_handle_cmd_update(int sockfd, cmdhandler_type* cmdc,
00158     const char* tbd)
00159 {
00160     char buf[ODS_SE_MAXLINE];
00161     ods_status status = ODS_STATUS_OK;
00162     zone_type* zone = NULL;
00163     task_type* task = NULL;
00164     int zl_changed = 0;
00165 
00166     ods_log_assert(tbd);
00167     ods_log_assert(cmdc);
00168     ods_log_assert(cmdc->engine);
00169     ods_log_assert(cmdc->engine->taskq);
00170 
00171     if (ods_strcmp(tbd, "--all") == 0) {
00172         /* [LOCK] zonelist */
00173         lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00174         zl_changed = zonelist_update(cmdc->engine->zonelist,
00175             cmdc->engine->config->zonelist_filename);
00176         /* [UNLOCK] zonelist */
00177         if (zl_changed == ODS_STATUS_UNCHANGED) {
00178             lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00179             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has not changed."
00180                 "\n");
00181             ods_writen(sockfd, buf, strlen(buf));
00182         } else if (zl_changed == ODS_STATUS_OK) {
00183             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list updated: %i "
00184             "removed, %i added, %i updated.\n",
00185                 cmdc->engine->zonelist->just_removed,
00186                 cmdc->engine->zonelist->just_added,
00187                 cmdc->engine->zonelist->just_updated);
00188             ods_writen(sockfd, buf, strlen(buf));
00189 
00190             cmdc->engine->zonelist->just_removed = 0;
00191             cmdc->engine->zonelist->just_added = 0;
00192             cmdc->engine->zonelist->just_updated = 0;
00193             lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00194 
00195             ods_log_debug("[%s] commit zone list changes", cmdh_str);
00196             engine_update_zones(cmdc->engine);
00197             ods_log_debug("[%s] signer configurations updated", cmdh_str);
00198         } else {
00199             lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00200             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone list has errors.\n");
00201             ods_writen(sockfd, buf, strlen(buf));
00202         }
00203         return;
00204     } else {
00205         /* look up zone */
00206         lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00207         /* [LOCK] zonelist */
00208         zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
00209             LDNS_RR_CLASS_IN);
00210         if (zone && zone->just_added) {
00211             zone = NULL;
00212         }
00213         /* [UNLOCK] zonelist */
00214         lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00215         if (!zone) {
00216             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
00217                 tbd);
00218             ods_writen(sockfd, buf, strlen(buf));
00219             /* update all */
00220             cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
00221             return;
00222         }
00223 
00224         lock_basic_lock(&zone->zone_lock);
00225         ods_log_assert(zone->task);
00226 
00227         lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00228         /* [LOCK] schedule */
00229         task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
00230         if (task != NULL) {
00231             ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
00232                 zone->name);
00233             if (task->what != TASK_SIGNCONF) {
00234                 task->halted = task->what;
00235                 task->interrupt = TASK_SIGNCONF;
00236             }
00237             task->what = TASK_SIGNCONF;
00238             task->when = time_now();
00239             status = schedule_task(cmdc->engine->taskq, task, 0);
00240         } else {
00241             /* task not queued, being worked on? */
00242             ods_log_verbose("[%s] worker busy with zone %s, will update "
00243                 "signconf as soon as possible", cmdh_str, zone->name);
00244             task = (task_type*) zone->task;
00245             task->interrupt = TASK_SIGNCONF;
00246             /* task->halted set by worker */
00247         }
00248         /* [UNLOCK] schedule */
00249         lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00250 
00251         zone->task = task;
00252         lock_basic_unlock(&zone->zone_lock);
00253 
00254         if (status != ODS_STATUS_OK) {
00255             ods_log_crit("[%s] cannot schedule task for zone %s: %s",
00256                 cmdh_str, zone->name, ods_status2str(status));
00257             task_cleanup(task);
00258             zone->task = NULL;
00259         } else {
00260             engine_wakeup_workers(cmdc->engine);
00261         }
00262 
00263         (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s config being updated.\n",
00264             tbd);
00265         ods_writen(sockfd, buf, strlen(buf));
00266     }
00267     return;
00268 }
00269 
00270 
00275 static void
00276 cmdhandler_handle_cmd_sign(int sockfd, cmdhandler_type* cmdc, const char* tbd)
00277 {
00278     zone_type* zone = NULL;
00279     task_type* task = NULL;
00280     ods_status status = ODS_STATUS_OK;
00281     char buf[ODS_SE_MAXLINE];
00282 
00283     ods_log_assert(tbd);
00284     ods_log_assert(cmdc);
00285     ods_log_assert(cmdc->engine);
00286     ods_log_assert(cmdc->engine->taskq);
00287 
00288     if (ods_strcmp(tbd, "--all") == 0) {
00289         lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00290         /* [LOCK] schedule */
00291         schedule_flush(cmdc->engine->taskq, TASK_READ);
00292         /* [UNLOCK] schedule */
00293         lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00294         engine_wakeup_workers(cmdc->engine);
00295         (void)snprintf(buf, ODS_SE_MAXLINE, "All zones scheduled for "
00296             "immediate re-sign.\n");
00297         ods_writen(sockfd, buf, strlen(buf));
00298         ods_log_verbose("[%s] all zones scheduled for immediate re-sign",
00299             cmdh_str);
00300         return;
00301     } else {
00302         lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00303         /* [LOCK] zonelist */
00304         zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
00305             LDNS_RR_CLASS_IN);
00306         if (zone && zone->just_added) {
00307             zone = NULL;
00308         }
00309         /* [UNLOCK] zonelist */
00310         lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00311 
00312         if (!zone) {
00313             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not found.\n",
00314                 tbd);
00315             ods_writen(sockfd, buf, strlen(buf));
00316             return;
00317         }
00318 
00319         lock_basic_lock(&zone->zone_lock);
00320         ods_log_assert(zone->task);
00321 
00322         lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00323         /* [LOCK] schedule */
00324         task = unschedule_task(cmdc->engine->taskq, (task_type*) zone->task);
00325         if (task != NULL) {
00326             ods_log_debug("[%s] reschedule task for zone %s", cmdh_str,
00327                 zone->name);
00328             if (task->what != TASK_READ) {
00329                 task->halted = task->what;
00330                 task->interrupt = TASK_READ;
00331             }
00332             task->what = TASK_READ;
00333             task->when = time_now();
00334             status = schedule_task(cmdc->engine->taskq, task, 0);
00335         } else {
00336             /* task now queued, being worked on? */
00337             ods_log_verbose("[%s] worker busy with zone %s, will read "
00338                 "zone input as soon as possible", cmdh_str, zone->name);
00339             task = (task_type*) zone->task;
00340             task->interrupt = TASK_READ;
00341             /* task->halted set by worker */
00342         }
00343         /* [UNLOCK] schedule */
00344         lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00345 
00346         zone->task = task;
00347         lock_basic_unlock(&zone->zone_lock);
00348 
00349         if (status != ODS_STATUS_OK) {
00350             (void)snprintf(buf, ODS_SE_MAXLINE, "Error: Cannot schedule task for "
00351                 "zone %s.\n", tbd);
00352             ods_writen(sockfd, buf, strlen(buf));
00353             ods_log_crit("[%s] cannot schedule task for zone %s: %s",
00354                 cmdh_str, zone->name, ods_status2str(status));
00355             task_cleanup(task);
00356             zone->task = NULL;
00357         } else {
00358             (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s scheduled for immediate "
00359                 "re-sign.\n", tbd);
00360             ods_writen(sockfd, buf, strlen(buf));
00361             ods_log_verbose("[%s] zone %s scheduled for immediate re-sign",
00362                 cmdh_str, tbd);
00363             engine_wakeup_workers(cmdc->engine);
00364         }
00365     }
00366     return;
00367 }
00368 
00369 
00374 static void
00375 unlink_backup_file(const char* filename, const char* extension)
00376 {
00377     char* tmpname = ods_build_path(filename, extension, 0);
00378     ods_log_debug("[%s] unlink file %s", cmdh_str, tmpname);
00379     unlink(tmpname);
00380     free((void*)tmpname);
00381     return;
00382 }
00383 
00388 static void
00389 cmdhandler_handle_cmd_clear(int sockfd, cmdhandler_type* cmdc, const char* tbd)
00390 {
00391     char buf[ODS_SE_MAXLINE];
00392     zone_type* zone = NULL;
00393     task_type* task = NULL;
00394     uint32_t inbound_serial = 0;
00395     uint32_t internal_serial = 0;
00396     uint32_t outbound_serial = 0;
00397     ods_status status = ODS_STATUS_OK;
00398 
00399     ods_log_assert(tbd);
00400     ods_log_assert(cmdc);
00401     ods_log_assert(cmdc->engine);
00402 
00403     unlink_backup_file(tbd, ".inbound");
00404     unlink_backup_file(tbd, ".backup");
00405 
00406     lock_basic_lock(&cmdc->engine->zonelist->zl_lock);
00407     /* [LOCK] zonelist */
00408     zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd,
00409         LDNS_RR_CLASS_IN);
00410     /* [UNLOCK] zonelist */
00411     lock_basic_unlock(&cmdc->engine->zonelist->zl_lock);
00412     if (zone) {
00413         /* [LOCK] zone */
00414         lock_basic_lock(&zone->zone_lock);
00415         inbound_serial = zone->zonedata->inbound_serial;
00416         internal_serial = zone->zonedata->internal_serial;
00417         outbound_serial = zone->zonedata->outbound_serial;
00418         zonedata_cleanup(zone->zonedata);
00419         zone->zonedata = NULL;
00420         zone->zonedata = zonedata_create(zone->allocator);
00421         zone->zonedata->initialized = 1;
00422         zone->zonedata->inbound_serial = inbound_serial;
00423         zone->zonedata->internal_serial = internal_serial;
00424         zone->zonedata->outbound_serial = outbound_serial;
00425 
00426         status = zone_publish_dnskeys(zone, 1);
00427         if (status == ODS_STATUS_OK) {
00428             status = zone_prepare_nsec3(zone, 1);
00429         } else {
00430             ods_log_warning("[%s] unable to restore DNSKEY RRset for zone %s,"
00431                 " reloading signconf", cmdh_str, zone->name);
00432         }
00433         if (status == ODS_STATUS_OK) {
00434             status = zonedata_commit(zone->zonedata);
00435         } else {
00436             ods_log_warning("[%s] unable to restore NSEC3PARAM RRset for "
00437                 " zone %s d1reloading signconf", cmdh_str, zone->name);
00438         }
00439 
00440         task = (task_type*) zone->task;
00441         task->what = TASK_READ;
00442         if (status != ODS_STATUS_OK) {
00443             ods_log_warning("[%s] unable to restore DNSKEY/NSEC3PARAM RRset "
00444                 " for zone %s d1reloading signconf", cmdh_str, zone->name);
00445             task->what = TASK_SIGNCONF;
00446         }
00447         /* [UNLOCK] zone */
00448         lock_basic_unlock(&zone->zone_lock);
00449 
00450         (void)snprintf(buf, ODS_SE_MAXLINE, "Internal zone information about "
00451             "%s cleared", tbd?tbd:"(null)");
00452         ods_log_info("[%s] internal zone information about %s cleared",
00453             cmdh_str, tbd?tbd:"(null)");
00454     } else {
00455         (void)snprintf(buf, ODS_SE_MAXLINE, "Cannot clear zone %s, zone not "
00456             "found", tbd?tbd:"(null)");
00457         ods_log_warning("[%s] cannot clear zone %s, zone not found",
00458             cmdh_str, tbd?tbd:"(null)");
00459     }
00460 
00461     ods_writen(sockfd, buf, strlen(buf));
00462     return;
00463 }
00464 
00465 
00470 static void
00471 cmdhandler_handle_cmd_queue(int sockfd, cmdhandler_type* cmdc)
00472 {
00473     char* strtime = NULL;
00474     char buf[ODS_SE_MAXLINE];
00475     size_t i = 0;
00476     time_t now = 0;
00477     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
00478     task_type* task = NULL;
00479 
00480     ods_log_assert(cmdc);
00481     ods_log_assert(cmdc->engine);
00482     if (!cmdc->engine->taskq || !cmdc->engine->taskq->tasks) {
00483         (void)snprintf(buf, ODS_SE_MAXLINE, "I have no tasks scheduled.\n");
00484         ods_writen(sockfd, buf, strlen(buf));
00485         return;
00486     }
00487 
00488     lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00489     /* [LOCK] schedule */
00490 
00491     /* time */
00492     now = time_now();
00493     strtime = ctime(&now);
00494     (void)snprintf(buf, ODS_SE_MAXLINE, "It is now %s",
00495         strtime?strtime:"(null)");
00496     ods_writen(sockfd, buf, strlen(buf));
00497 
00498     /* current work */
00499     for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
00500         task = cmdc->engine->workers[i]->task;
00501         if (task) {
00502             (void)snprintf(buf, ODS_SE_MAXLINE, "Working with task %s on "
00503                 "zone %s\n",
00504                 task_what2str(cmdc->engine->workers[i]->working_with),
00505                 task_who2str(task->who));
00506             ods_writen(sockfd, buf, strlen(buf));
00507         }
00508     }
00509 
00510     /* how many tasks */
00511     (void)snprintf(buf, ODS_SE_MAXLINE, "\nI have %i tasks scheduled.\n",
00512         (int) cmdc->engine->taskq->tasks->count);
00513     ods_writen(sockfd, buf, strlen(buf));
00514 
00515     /* list tasks */
00516     node = ldns_rbtree_first(cmdc->engine->taskq->tasks);
00517     while (node && node != LDNS_RBTREE_NULL) {
00518         task = (task_type*) node->data;
00519         for (i=0; i < ODS_SE_MAXLINE; i++) {
00520             buf[i] = 0;
00521         }
00522         (void)task2str(task, (char*) &buf[0]);
00523         ods_writen(sockfd, buf, strlen(buf));
00524         node = ldns_rbtree_next(node);
00525     }
00526 
00527     /* [UNLOCK] schedule */
00528     lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00529     return;
00530 }
00531 
00532 
00537 static void
00538 cmdhandler_handle_cmd_flush(int sockfd, cmdhandler_type* cmdc)
00539 {
00540     char buf[ODS_SE_MAXLINE];
00541 
00542     ods_log_assert(cmdc);
00543     ods_log_assert(cmdc->engine);
00544     ods_log_assert(cmdc->engine->taskq);
00545 
00546     lock_basic_lock(&cmdc->engine->taskq->schedule_lock);
00547     /* [LOCK] schedule */
00548     schedule_flush(cmdc->engine->taskq, TASK_NONE);
00549     /* [UNLOCK] schedule */
00550     lock_basic_unlock(&cmdc->engine->taskq->schedule_lock);
00551 
00552     engine_wakeup_workers(cmdc->engine);
00553 
00554     (void)snprintf(buf, ODS_SE_MAXLINE, "All tasks scheduled immediately.\n");
00555     ods_writen(sockfd, buf, strlen(buf));
00556     ods_log_verbose("[%s] all tasks scheduled immediately", cmdh_str);
00557     return;
00558 }
00559 
00560 
00565 static void
00566 cmdhandler_handle_cmd_reload(int sockfd, cmdhandler_type* cmdc)
00567 {
00568     char buf[ODS_SE_MAXLINE];
00569 
00570     ods_log_assert(cmdc);
00571     ods_log_assert(cmdc->engine);
00572 
00573     cmdc->engine->need_to_reload = 1;
00574 
00575     lock_basic_lock(&cmdc->engine->signal_lock);
00576     /* [LOCK] signal */
00577     lock_basic_alarm(&cmdc->engine->signal_cond);
00578     /* [UNLOCK] signal */
00579     lock_basic_unlock(&cmdc->engine->signal_lock);
00580 
00581     (void)snprintf(buf, ODS_SE_MAXLINE, "Reloading engine.\n");
00582     ods_writen(sockfd, buf, strlen(buf));
00583     return;
00584 }
00585 
00586 
00591 static void
00592 cmdhandler_handle_cmd_stop(int sockfd, cmdhandler_type* cmdc)
00593 {
00594     char buf[ODS_SE_MAXLINE];
00595 
00596     ods_log_assert(cmdc);
00597     ods_log_assert(cmdc->engine);
00598 
00599     cmdc->engine->need_to_exit = 1;
00600 
00601     lock_basic_lock(&cmdc->engine->signal_lock);
00602     /* [LOCK] signal */
00603     lock_basic_alarm(&cmdc->engine->signal_cond);
00604     /* [UNLOCK] signal */
00605     lock_basic_unlock(&cmdc->engine->signal_lock);
00606 
00607     (void)snprintf(buf, ODS_SE_MAXLINE, ODS_SE_STOP_RESPONSE);
00608     ods_writen(sockfd, buf, strlen(buf));
00609     return;
00610 }
00611 
00612 
00617 static void
00618 cmdhandler_handle_cmd_start(int sockfd)
00619 {
00620     char buf[ODS_SE_MAXLINE];
00621 
00622     (void)snprintf(buf, ODS_SE_MAXLINE, "Engine already running.\n");
00623     ods_writen(sockfd, buf, strlen(buf));
00624     return;
00625 }
00626 
00627 
00632 static void
00633 cmdhandler_handle_cmd_running(int sockfd)
00634 {
00635     char buf[ODS_SE_MAXLINE];
00636 
00637     (void)snprintf(buf, ODS_SE_MAXLINE, "Engine running.\n");
00638     ods_writen(sockfd, buf, strlen(buf));
00639     return;
00640 }
00641 
00642 
00647 static void
00648 cmdhandler_handle_cmd_verbosity(int sockfd, cmdhandler_type* cmdc, int val)
00649 {
00650     char buf[ODS_SE_MAXLINE];
00651 
00652     ods_log_assert(cmdc);
00653     ods_log_assert(cmdc->engine);
00654     ods_log_assert(cmdc->engine->config);
00655 
00656     ods_log_init(cmdc->engine->config->log_filename,
00657         cmdc->engine->config->use_syslog, val);
00658 
00659     (void)snprintf(buf, ODS_SE_MAXLINE, "Verbosity level set to %i.\n", val);
00660     ods_writen(sockfd, buf, strlen(buf));
00661 }
00662 
00663 
00668 static void
00669 cmdhandler_handle_cmd_error(int sockfd, const char* str)
00670 {
00671     char buf[ODS_SE_MAXLINE];
00672     (void)snprintf(buf, ODS_SE_MAXLINE, "Error: %s.\n", str?str:"(null)");
00673     ods_writen(sockfd, buf, strlen(buf));
00674     return;
00675 }
00676 
00677 
00682 static void
00683 cmdhandler_handle_cmd_unknown(int sockfd, const char* str)
00684 {
00685     char buf[ODS_SE_MAXLINE];
00686     (void)snprintf(buf, ODS_SE_MAXLINE, "Unknown command %s.\n",
00687         str?str:"(null)");
00688     ods_writen(sockfd, buf, strlen(buf));
00689     return;
00690 }
00691 
00692 
00711 static void
00712 cmdhandler_handle_cmd(cmdhandler_type* cmdc)
00713 {
00714     ssize_t n = 0;
00715     int sockfd = 0;
00716     char buf[ODS_SE_MAXLINE];
00717 
00718     ods_log_assert(cmdc);
00719     sockfd = cmdc->client_fd;
00720 
00721 again:
00722     while ((n = read(sockfd, buf, ODS_SE_MAXLINE)) > 0) {
00723         buf[n-1] = '\0';
00724         n--;
00725         if (n <= 0) {
00726             return;
00727         }
00728         ods_log_verbose("[%s] received command %s[%i]", cmdh_str, buf, n);
00729 
00730         if (n == 4 && strncmp(buf, "help", n) == 0) {
00731             ods_log_debug("[%s] help command", cmdh_str);
00732             cmdhandler_handle_cmd_help(sockfd);
00733         } else if (n == 5 && strncmp(buf, "zones", n) == 0) {
00734             ods_log_debug("[%s] list zones command", cmdh_str);
00735             cmdhandler_handle_cmd_zones(sockfd, cmdc);
00736         } else if (n >= 4 && strncmp(buf, "sign", 4) == 0) {
00737             ods_log_debug("[%s] sign zone command", cmdh_str);
00738             if (buf[4] == '\0') {
00739                 /* NOTE: wouldn't it be nice that we default to --all? */
00740                 cmdhandler_handle_cmd_error(sockfd, "sign command needs "
00741                     "an argument (either '--all' or a zone name)");
00742             } else if (buf[4] != ' ') {
00743                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00744             } else {
00745                 cmdhandler_handle_cmd_sign(sockfd, cmdc, &buf[5]);
00746             }
00747         } else if (n >= 5 && strncmp(buf, "clear", 5) == 0) {
00748             ods_log_debug("[%s] clear zone command", cmdh_str);
00749             if (buf[5] == '\0') {
00750                 cmdhandler_handle_cmd_error(sockfd, "clear command needs "
00751                     "a zone name");
00752             } else if (buf[5] != ' ') {
00753                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00754             } else {
00755                 cmdhandler_handle_cmd_clear(sockfd, cmdc, &buf[6]);
00756             }
00757         } else if (n == 5 && strncmp(buf, "queue", n) == 0) {
00758             ods_log_debug("[%s] list tasks command", cmdh_str);
00759             cmdhandler_handle_cmd_queue(sockfd, cmdc);
00760         } else if (n == 5 && strncmp(buf, "flush", n) == 0) {
00761             ods_log_debug("[%s] flush tasks command", cmdh_str);
00762             cmdhandler_handle_cmd_flush(sockfd, cmdc);
00763         } else if (n >= 6 && strncmp(buf, "update", 6) == 0) {
00764             ods_log_debug("[%s] update command", cmdh_str);
00765             if (buf[6] == '\0') {
00766                 cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
00767             } else if (buf[6] != ' ') {
00768                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00769             } else {
00770                 cmdhandler_handle_cmd_update(sockfd, cmdc, &buf[7]);
00771             }
00772         } else if (n == 4 && strncmp(buf, "stop", n) == 0) {
00773             ods_log_debug("[%s] shutdown command", cmdh_str);
00774             cmdhandler_handle_cmd_stop(sockfd, cmdc);
00775             return;
00776         } else if (n == 5 && strncmp(buf, "start", n) == 0) {
00777             ods_log_debug("[%s] start command", cmdh_str);
00778             cmdhandler_handle_cmd_start(sockfd);
00779         } else if (n == 6 && strncmp(buf, "reload", n) == 0) {
00780             ods_log_debug("[%s] reload command", cmdh_str);
00781             cmdhandler_handle_cmd_reload(sockfd, cmdc);
00782         } else if (n == 7 && strncmp(buf, "running", n) == 0) {
00783             ods_log_debug("[%s] running command", cmdh_str);
00784             cmdhandler_handle_cmd_running(sockfd);
00785         } else if (n >= 9 && strncmp(buf, "verbosity", 9) == 0) {
00786             ods_log_debug("[%s] verbosity command", cmdh_str);
00787             if (buf[9] == '\0') {
00788                 cmdhandler_handle_cmd_error(sockfd, "verbosity command "
00789                     "an argument (verbosity level)");
00790             } else if (buf[9] != ' ') {
00791                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00792             } else {
00793                 cmdhandler_handle_cmd_verbosity(sockfd, cmdc, atoi(&buf[10]));
00794             }
00795         } else {
00796             ods_log_debug("[%s] unknown command", cmdh_str);
00797             cmdhandler_handle_cmd_unknown(sockfd, buf);
00798         }
00799 
00800         ods_log_debug("[%s] done handling command %s[%i]", cmdh_str, buf, n);
00801         (void)snprintf(buf, SE_CMDH_CMDLEN, "\ncmd> ");
00802         ods_writen(sockfd, buf, strlen(buf));
00803     }
00804 
00805     if (n < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) ) {
00806         goto again;
00807     } else if (n < 0 && errno == ECONNRESET) {
00808         ods_log_debug("[%s] done handling client: %s", cmdh_str,
00809             strerror(errno));
00810     } else if (n < 0 ) {
00811         ods_log_error("[%s] read error: %s", cmdh_str, strerror(errno));
00812     }
00813     return;
00814 }
00815 
00816 
00821 static void*
00822 cmdhandler_accept_client(void* arg)
00823 {
00824     cmdhandler_type* cmdc = (cmdhandler_type*) arg;
00825 
00826     ods_thread_blocksigs();
00827     ods_thread_detach(cmdc->thread_id);
00828 
00829     ods_log_debug("[%s] accept client %i", cmdh_str, cmdc->client_fd);
00830     cmdhandler_handle_cmd(cmdc);
00831     if (cmdc->client_fd) {
00832         close(cmdc->client_fd);
00833     }
00834     free(cmdc);
00835     count--;
00836     return NULL;
00837 }
00838 
00839 
00844 cmdhandler_type*
00845 cmdhandler_create(allocator_type* allocator, const char* filename)
00846 {
00847     cmdhandler_type* cmdh = NULL;
00848     struct sockaddr_un servaddr;
00849     int listenfd = 0;
00850     int flags = 0;
00851     int ret = 0;
00852 
00853     if (!allocator) {
00854         ods_log_error("[%s] unable to create: no allocator", cmdh_str);
00855         return NULL;
00856     }
00857     ods_log_assert(allocator);
00858 
00859     if (!filename) {
00860         ods_log_error("[%s] unable to create: no socket filename", cmdh_str);
00861         return NULL;
00862     }
00863     ods_log_assert(filename);
00864     ods_log_debug("[%s] create socket %s", cmdh_str, filename);
00865 
00866     /* new socket */
00867     listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
00868     if (listenfd <= 0) {
00869         ods_log_error("[%s] unable to create, socket() failed: %s", cmdh_str,
00870             strerror(errno));
00871         return NULL;
00872     }
00873     /* set it to non-blocking */
00874     flags = fcntl(listenfd, F_GETFL, 0);
00875     if (flags < 0) {
00876         ods_log_error("[%s] unable to create, fcntl(F_GETFL) failed: %s",
00877             cmdh_str, strerror(errno));
00878         close(listenfd);
00879         return NULL;
00880     }
00881     flags |= O_NONBLOCK;
00882     if (fcntl(listenfd, F_SETFL, flags) < 0) {
00883         ods_log_error("[%s] unable to create, fcntl(F_SETFL) failed: %s",
00884             cmdh_str, strerror(errno));
00885         close(listenfd);
00886         return NULL;
00887     }
00888 
00889     /* no surprises */
00890     if (filename) {
00891         unlink(filename);
00892     }
00893     bzero(&servaddr, sizeof(servaddr));
00894     servaddr.sun_family = AF_UNIX;
00895     strncpy(servaddr.sun_path, filename, sizeof(servaddr.sun_path) - 1);
00896 
00897     /* bind and listen... */
00898     ret = bind(listenfd, (const struct sockaddr*) &servaddr,
00899         SUN_LEN(&servaddr));
00900     if (ret != 0) {
00901         ods_log_error("[%s] unable to create, bind() failed: %s", cmdh_str,
00902             strerror(errno));
00903         close(listenfd);
00904         return NULL;
00905     }
00906     ret = listen(listenfd, ODS_SE_MAX_HANDLERS);
00907     if (ret != 0) {
00908         ods_log_error("[%s] unable to create, listen() failed: %s", cmdh_str,
00909             strerror(errno));
00910         close(listenfd);
00911         return NULL;
00912     }
00913 
00914     /* all ok */
00915     cmdh = (cmdhandler_type*) allocator_alloc(allocator,
00916         sizeof(cmdhandler_type));
00917     if (!cmdh) {
00918         close(listenfd);
00919         return NULL;
00920     }
00921     cmdh->allocator = allocator;
00922     cmdh->listen_fd = listenfd;
00923     cmdh->listen_addr = servaddr;
00924     cmdh->need_to_exit = 0;
00925     return cmdh;
00926 }
00927 
00928 
00933 void
00934 cmdhandler_start(cmdhandler_type* cmdhandler)
00935 {
00936     struct sockaddr_un cliaddr;
00937     socklen_t clilen;
00938     cmdhandler_type* cmdc = NULL;
00939     engine_type* engine = NULL;
00940     fd_set rset;
00941     int connfd = 0;
00942     int ret = 0;
00943 
00944     ods_log_assert(cmdhandler);
00945     ods_log_assert(cmdhandler->engine);
00946     ods_log_debug("[%s] start", cmdh_str);
00947 
00948     engine = cmdhandler->engine;
00949     ods_thread_detach(cmdhandler->thread_id);
00950     FD_ZERO(&rset);
00951     while (cmdhandler->need_to_exit == 0) {
00952         clilen = sizeof(cliaddr);
00953         FD_SET(cmdhandler->listen_fd, &rset);
00954         ret = select(ODS_SE_MAX_HANDLERS+1, &rset, NULL, NULL, NULL);
00955         if (ret < 0) {
00956             if (errno != EINTR && errno != EWOULDBLOCK) {
00957                 ods_log_warning("[%s] select() error: %s", cmdh_str,
00958                    strerror(errno));
00959             }
00960             continue;
00961         }
00962         if (FD_ISSET(cmdhandler->listen_fd, &rset)) {
00963             connfd = accept(cmdhandler->listen_fd,
00964                 (struct sockaddr *) &cliaddr, &clilen);
00965             if (connfd < 0) {
00966                 if (errno != EINTR && errno != EWOULDBLOCK) {
00967                     ods_log_warning("[%s] accept error: %s", cmdh_str,
00968                         strerror(errno));
00969                 }
00970                 continue;
00971             }
00972             /* client accepted, create new thread */
00973             cmdc = (cmdhandler_type*) malloc(sizeof(cmdhandler_type));
00974             if (!cmdc) {
00975                 ods_log_crit("[%s] unable to create thread for client: "
00976                     "malloc failed", cmdh_str);
00977                 cmdhandler->need_to_exit = 1;
00978                 break;
00979             }
00980             cmdc->listen_fd = cmdhandler->listen_fd;
00981             cmdc->client_fd = connfd;
00982             cmdc->listen_addr = cmdhandler->listen_addr;
00983             cmdc->engine = cmdhandler->engine;
00984             cmdc->need_to_exit = cmdhandler->need_to_exit;
00985             ods_thread_create(&cmdc->thread_id, &cmdhandler_accept_client,
00986                 (void*) cmdc);
00987             count++;
00988             ods_log_debug("[%s] %i clients in progress...", cmdh_str, count);
00989         }
00990     }
00991 
00992     ods_log_debug("[%s] done", cmdh_str);
00993     engine = cmdhandler->engine;
00994     engine->cmdhandler_done = 1;
00995     return;
00996 }
00997 
00998 
01003 void
01004 cmdhandler_cleanup(cmdhandler_type* cmdhandler)
01005 {
01006     allocator_type* allocator;
01007     if (!cmdhandler) {
01008         return;
01009     }
01010     allocator = cmdhandler->allocator;
01011     allocator_deallocate(allocator, (void*) cmdhandler);
01012     return;
01013 }
01014