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