OpenDNSSEC-signer 1.2.1

/build/buildd-opendnssec_1.2.1.dfsg-1-ia64-j6OroR/opendnssec-1.2.1.dfsg/signer/src/daemon/cmdhandler.c

Go to the documentation of this file.
00001 /*
00002  * $Id: cmdhandler.c 4294 2011-01-13 19:58:29Z 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/locks.h"
00037 #include "util/file.h"
00038 #include "util/log.h"
00039 #include "util/se_malloc.h"
00040 
00041 #include <errno.h>
00042 #include <fcntl.h> /* fcntl() */
00043 #include <ldns/ldns.h> /* ldns_rbtree_*() */
00044 #include <stdio.h> /* snprintf() */
00045 #include <stdlib.h> /* atoi() */
00046 #include <string.h> /* strncpy(), strerror(), strlen(), strncmp() */
00047 #include <strings.h> /* bzero() */
00048 #include <sys/select.h> /* select(), FD_ZERO(), FD_SET(), FD_ISSET() */
00049 #include <sys/socket.h> /* socket(), listen(), bind(), accept() */
00050 #ifdef HAVE_SYS_TYPES_H
00051 # include <sys/types.h>
00052 #endif
00053 #include <unistd.h> /* fcntl(), close(), unlink(), read() */
00054 
00055 /* According to earlier standards: select() sys/time.h sys/types.h unistd.h */
00056 #include <sys/time.h>
00057 #include <sys/types.h>
00058 
00059 #define SE_CMDH_CMDLEN 7
00060 
00061 #ifndef SUN_LEN
00062 #define SUN_LEN(su)  (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
00063 #endif
00064 
00065 static int count = 0;
00066 
00067 
00072 static void
00073 cmdhandler_handle_cmd_help(int sockfd)
00074 {
00075     char buf[ODS_SE_MAXLINE];
00076 
00077     (void) snprintf(buf, ODS_SE_MAXLINE,
00078         "Commands:\n"
00079         "zones           show the currently known zones.\n"
00080         "sign <zone>     read zone and schedule for immediate (re-)sign.\n"
00081         "sign --all      read all zones and schedule all for immediate (re-)sign.\n"
00082         "clear <zone>    delete the internal storage of this zone.\n"
00083         "                All signatures will be regenerated on the next re-sign.\n"
00084         "queue           show the current task queue.\n"
00085     );
00086     se_writen(sockfd, buf, strlen(buf));
00087 
00088     (void) snprintf(buf, ODS_SE_MAXLINE,
00089         "flush           execute all scheduled tasks immediately.\n"
00090         "update <zone>   update this zone signer configurations.\n"
00091         "update [--all]  update zone list and all signer configurations.\n"
00092         "start           start the engine.\n"
00093         "reload          reload the engine.\n"
00094         "stop            stop the engine.\n"
00095         "verbosity <nr>  set verbosity.\n"
00096     );
00097     se_writen(sockfd, buf, strlen(buf));
00098     return;
00099 }
00100 
00101 
00106 static void
00107 cmdhandler_handle_cmd_zones(int sockfd, cmdhandler_type* cmdc)
00108 {
00109     char buf[ODS_SE_MAXLINE];
00110     size_t i;
00111     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
00112     zone_type* zone = NULL;
00113 
00114     se_log_assert(cmdc);
00115     se_log_assert(cmdc->engine);
00116     se_log_assert(cmdc->engine->zonelist);
00117 
00118     /* lock zonelist */
00119     zonelist_lock(cmdc->engine->zonelist);
00120 
00121     /* how many zones */
00122     (void)snprintf(buf, ODS_SE_MAXLINE, "I have %i zones configured\n",
00123         (int) cmdc->engine->zonelist->zones->count);
00124     se_writen(sockfd, buf, strlen(buf));
00125 
00126     /* list zones */
00127     node = ldns_rbtree_first(cmdc->engine->zonelist->zones);
00128     while (node && node != LDNS_RBTREE_NULL) {
00129         zone = (zone_type*) node->key;
00130         for (i=0; i < ODS_SE_MAXLINE; i++) {
00131             buf[i] = 0;
00132         }
00133         (void)snprintf(buf, ODS_SE_MAXLINE, "- %s\n",
00134             zone->name?zone->name:"(null)");
00135         se_writen(sockfd, buf, strlen(buf));
00136         node = ldns_rbtree_next(node);
00137     }
00138 
00139     /* unlock zonelist */
00140     zonelist_unlock(cmdc->engine->zonelist);
00141     return;
00142 }
00143 
00144 
00149 static void
00150 cmdhandler_handle_cmd_update(int sockfd, cmdhandler_type* cmdc, const char* tbd)
00151 {
00152     char buf[ODS_SE_MAXLINE];
00153     size_t i = 0;
00154     int ret = 0;
00155 
00156     se_log_assert(tbd);
00157     se_log_assert(cmdc);
00158     se_log_assert(cmdc->engine);
00159     se_log_assert(cmdc->engine->config);
00160     se_log_assert(cmdc->engine->tasklist);
00161 
00162     if (se_strcmp(tbd, "--all") == 0) {
00163         se_log_info("cmdhandler: updating zone list");
00164         ret = engine_update_zonelist(cmdc->engine, buf);
00165         se_writen(sockfd, buf, strlen(buf));
00166         tbd = NULL;
00167     }
00168     se_log_info("cmdhandler: updating signer configuration (%s)",
00169         tbd?tbd:"--all");
00170     ret = engine_update_zones(cmdc->engine, tbd, buf, 1);
00171     se_writen(sockfd, buf, strlen(buf));
00172 
00173     if (tbd && ret != 0) {
00174         /* zone was not found */
00175         ret = engine_update_zonelist(cmdc->engine, buf);
00176         se_writen(sockfd, buf, strlen(buf));
00177 
00178         /* try again */
00179         ret = engine_update_zones(cmdc->engine, tbd, buf, 0);
00180         se_writen(sockfd, buf, strlen(buf));
00181     }
00182 
00183     /* wake up sleeping workers */
00184     for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
00185         worker_wakeup(cmdc->engine->workers[i]);
00186     }
00187     return;
00188 }
00189 
00190 
00195 static void
00196 cmdhandler_handle_cmd_sign(int sockfd, cmdhandler_type* cmdc, const char* tbd)
00197 {
00198     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
00199     task_type* task = NULL;
00200     time_t now = time_now();
00201     int found = 0, scheduled = 0;
00202     char buf[ODS_SE_MAXLINE];
00203     size_t i = 0;
00204 
00205     se_log_assert(tbd);
00206     se_log_assert(cmdc);
00207     se_log_assert(cmdc->engine);
00208     se_log_assert(cmdc->engine->config);
00209     se_log_assert(cmdc->engine->tasklist);
00210 
00211     /* lock tasklist */
00212     lock_basic_lock(&cmdc->engine->tasklist->tasklist_lock);
00213 
00214     if (se_strcmp(tbd, "--all") == 0) {
00215         tasklist_flush(cmdc->engine->tasklist, TASK_READ);
00216     } else {
00217         node = ldns_rbtree_first(cmdc->engine->tasklist->tasks);
00218         while (node && node != LDNS_RBTREE_NULL) {
00219             task = (task_type*) node->key;
00220             if (se_strcmp(tbd, task->who) == 0) {
00221                 found = 1;
00222                 task = tasklist_delete_task(cmdc->engine->tasklist, task);
00223                 if (!task) {
00224                     se_log_error("cannot immediate sign zone %s: delete old "
00225                         "task failed", tbd);
00226                     (void)snprintf(buf, ODS_SE_MAXLINE, "Sign zone %s "
00227                         "failed.\n", tbd);
00228                     se_writen(sockfd, buf, strlen(buf));
00229                     lock_basic_unlock(&cmdc->engine->tasklist->tasklist_lock);
00230                     return;
00231                 } else {
00232                     task->when = now;
00233                     task->what = TASK_READ;
00234                     task = tasklist_schedule_task(cmdc->engine->tasklist,
00235                         task, 0);
00236                     if (task) {
00237                        scheduled = 1;
00238                     }
00239                 }
00240                 break;
00241             }
00242             node = ldns_rbtree_next(node);
00243         }
00244     }
00245 
00246     /* unlock tasklist */
00247     lock_basic_unlock(&cmdc->engine->tasklist->tasklist_lock);
00248 
00249     if (se_strcmp(tbd, "--all") == 0) {
00250         (void)snprintf(buf, ODS_SE_MAXLINE, "All zones scheduled for "
00251             "immediate re-sign.\n");
00252         se_writen(sockfd, buf, strlen(buf));
00253         se_log_info("cmdhandler: all zones scheduled for immediate re-sign");
00254 
00255         /* wake up sleeping workers */
00256         for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
00257             worker_wakeup(cmdc->engine->workers[i]);
00258         }
00259     } else if (found && scheduled) {
00260         (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s scheduled for "
00261             "immediate re-sign.\n", tbd?tbd:"(null)");
00262         se_writen(sockfd, buf, strlen(buf));
00263         se_log_info("cmdhandler: zone %s scheduled for immediate re-sign",
00264             tbd?tbd:"(null)");
00265 
00266         /* wake up sleeping workers */
00267         for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
00268             worker_wakeup(cmdc->engine->workers[i]);
00269         }
00270     } else if (found && !scheduled) {
00271         (void)snprintf(buf, ODS_SE_MAXLINE, "Failed to schedule signing for "
00272              "zone %s\n", tbd?tbd:"(null)");
00273         se_writen(sockfd, buf, strlen(buf));
00274         se_log_error("cmdhandler: zone %s was found in task queue but "
00275             "rescheduling failed", tbd?tbd:"(null)");
00276     } else if (engine_search_workers(cmdc->engine, tbd)) {
00277         se_log_warning("cmdhandler: not working on zone %s, updating "
00278             "zone list", tbd?tbd:"(null");
00279         (void)snprintf(buf, ODS_SE_MAXLINE, "Zone %s not not being signed "
00280             "yet, updating sign configuration\n", tbd?tbd:"(null)");
00281         se_writen(sockfd, buf, strlen(buf));
00282         cmdhandler_handle_cmd_update(sockfd, cmdc, tbd);
00283     } else {
00284         se_log_warning("cmdhandler: already performing task for zone %s",
00285             tbd?tbd:"(null");
00286         (void)snprintf(buf, ODS_SE_MAXLINE, "Signer is already working on "
00287             "zone %s, sign command ignored\n", tbd?tbd:"(null)");
00288         se_writen(sockfd, buf, strlen(buf));
00289     }
00290     return;
00291 }
00292 
00293 
00298 static void
00299 unlink_backup_file(const char* filename, const char* extension)
00300 {
00301     char* tmpname = se_build_path(filename, extension, 0);
00302     se_log_debug("unlink file %s", tmpname);
00303     unlink(tmpname);
00304     se_free((void*)tmpname);
00305     return;
00306 }
00307 
00312 static void
00313 cmdhandler_handle_cmd_clear(int sockfd, cmdhandler_type* cmdc, const char* tbd)
00314 {
00315     char buf[ODS_SE_MAXLINE];
00316     zone_type* zone = NULL;
00317     uint32_t inbound_serial = 0;
00318     uint32_t internal_serial = 0;
00319     uint32_t outbound_serial = 0;
00320 
00321     se_log_assert(tbd);
00322     se_log_assert(cmdc);
00323     se_log_assert(cmdc->engine);
00324 
00325     /* unlink_backup_file(tbd,, ".sc"); */
00326     /* unlink_backup_file(tbd,, ".state"); */
00327     /* unlink_backup_file(tbd,, ".task"); */
00328     unlink_backup_file(tbd, ".inbound");
00329     unlink_backup_file(tbd, ".unsorted");
00330     unlink_backup_file(tbd, ".dnskeys");
00331     unlink_backup_file(tbd, ".denial");
00332     unlink_backup_file(tbd, ".rrsigs");
00333     unlink_backup_file(tbd, ".finalized");
00334 
00335     zone = zonelist_lookup_zone_by_name(cmdc->engine->zonelist, tbd);
00336     if (zone) {
00337         inbound_serial = zone->zonedata->inbound_serial;
00338         internal_serial = zone->zonedata->internal_serial;
00339         outbound_serial = zone->zonedata->outbound_serial;
00340         zonedata_cleanup(zone->zonedata);
00341         zone->zonedata = NULL;
00342         zone->zonedata = zonedata_create();
00343         zone->zonedata->initialized = 1;
00344         zone->zonedata->inbound_serial = inbound_serial;
00345         zone->zonedata->internal_serial = internal_serial;
00346         zone->zonedata->outbound_serial = outbound_serial;
00347         zone->task->what = TASK_READ;
00348 
00349         (void)snprintf(buf, ODS_SE_MAXLINE, "Internal zone information about "
00350             "%s cleared", tbd?tbd:"(null)");
00351         se_log_info("cmdhandler: internal zone information about %s cleared",
00352             tbd?tbd:"(null)");
00353     } else {
00354         (void)snprintf(buf, ODS_SE_MAXLINE, "Cannot clear zone %s, zone not "
00355             "found", tbd?tbd:"(null)");
00356         se_log_warning("cmdhandler: cannot clear zone %s, zone not found",
00357             tbd?tbd:"(null)");
00358     }
00359 
00360     se_writen(sockfd, buf, strlen(buf));
00361     return;
00362 }
00363 
00364 
00369 static void
00370 cmdhandler_handle_cmd_queue(int sockfd, cmdhandler_type* cmdc)
00371 {
00372     char* strtime = NULL;
00373     char buf[ODS_SE_MAXLINE];
00374     size_t i = 0;
00375     time_t now = 0;
00376     ldns_rbnode_t* node = LDNS_RBTREE_NULL;
00377     task_type* task = NULL;
00378 
00379     se_log_assert(cmdc);
00380     se_log_assert(cmdc->engine);
00381     se_log_assert(cmdc->engine->tasklist);
00382 
00383     /* lock tasklist */
00384     lock_basic_lock(&cmdc->engine->tasklist->tasklist_lock);
00385 
00386     /* how many tasks scheduled */
00387     now = time_now();
00388     strtime = ctime(&now);
00389     (void)snprintf(buf, ODS_SE_MAXLINE, "I have %i tasks scheduled\nIt is "
00390         "now %s", (int) cmdc->engine->tasklist->tasks->count,
00391         strtime?strtime:"(null)");
00392     se_writen(sockfd, buf, strlen(buf));
00393 
00394     /* list tasks */
00395     node = ldns_rbtree_first(cmdc->engine->tasklist->tasks);
00396     while (node && node != LDNS_RBTREE_NULL) {
00397         task = (task_type*) node->key;
00398         for (i=0; i < ODS_SE_MAXLINE; i++) {
00399             buf[i] = 0;
00400         }
00401         (void)task2str(task, (char*) &buf[0]);
00402         se_writen(sockfd, buf, strlen(buf));
00403         node = ldns_rbtree_next(node);
00404     }
00405 
00406     /* unlock tasklist */
00407     lock_basic_unlock(&cmdc->engine->tasklist->tasklist_lock);
00408     return;
00409 }
00410 
00411 
00416 static void
00417 cmdhandler_handle_cmd_flush(int sockfd, cmdhandler_type* cmdc)
00418 {
00419     char buf[ODS_SE_MAXLINE];
00420     size_t i = 0;
00421 
00422     se_log_assert(cmdc);
00423     se_log_assert(cmdc->engine);
00424     se_log_assert(cmdc->engine->config);
00425     se_log_assert(cmdc->engine->tasklist);
00426 
00427     lock_basic_lock(&cmdc->engine->tasklist->tasklist_lock);
00428     tasklist_flush(cmdc->engine->tasklist, TASK_NONE);
00429     lock_basic_unlock(&cmdc->engine->tasklist->tasklist_lock);
00430 
00431     /* wake up sleeping workers */
00432     for (i=0; i < (size_t) cmdc->engine->config->num_worker_threads; i++) {
00433         worker_wakeup(cmdc->engine->workers[i]);
00434     }
00435 
00436     (void)snprintf(buf, ODS_SE_MAXLINE, "All tasks scheduled immediately.\n");
00437     se_writen(sockfd, buf, strlen(buf));
00438     se_log_info("cmdhandler: all tasks being flushed");
00439     return;
00440 }
00441 
00442 
00447 static void
00448 cmdhandler_handle_cmd_reload(int sockfd, cmdhandler_type* cmdc)
00449 {
00450     char buf[ODS_SE_MAXLINE];
00451 
00452     se_log_assert(cmdc);
00453     se_log_assert(cmdc->engine);
00454 
00455     cmdc->engine->need_to_reload = 1;
00456 
00457     lock_basic_lock(&cmdc->engine->signal_lock);
00458     lock_basic_alarm(&cmdc->engine->signal_cond);
00459     lock_basic_unlock(&cmdc->engine->signal_lock);
00460 
00461     (void)snprintf(buf, ODS_SE_MAXLINE, "Reloading engine.\n");
00462     se_writen(sockfd, buf, strlen(buf));
00463     return;
00464 }
00465 
00466 
00471 static void
00472 cmdhandler_handle_cmd_stop(int sockfd, cmdhandler_type* cmdc)
00473 {
00474     char buf[ODS_SE_MAXLINE];
00475 
00476     se_log_assert(cmdc);
00477     se_log_assert(cmdc->engine);
00478 
00479     cmdc->engine->need_to_exit = 1;
00480 
00481     lock_basic_lock(&cmdc->engine->signal_lock);
00482     lock_basic_alarm(&cmdc->engine->signal_cond);
00483     lock_basic_unlock(&cmdc->engine->signal_lock);
00484 
00485     (void)snprintf(buf, ODS_SE_MAXLINE, ODS_SE_STOP_RESPONSE);
00486     se_writen(sockfd, buf, strlen(buf));
00487     return;
00488 }
00489 
00490 
00495 static void
00496 cmdhandler_handle_cmd_start(int sockfd)
00497 {
00498     char buf[ODS_SE_MAXLINE];
00499 
00500     (void)snprintf(buf, ODS_SE_MAXLINE, "Engine already running.\n");
00501     se_writen(sockfd, buf, strlen(buf));
00502     return;
00503 }
00504 
00505 
00510 static void
00511 cmdhandler_handle_cmd_running(int sockfd)
00512 {
00513     char buf[ODS_SE_MAXLINE];
00514 
00515     (void)snprintf(buf, ODS_SE_MAXLINE, "Engine running.\n");
00516     se_writen(sockfd, buf, strlen(buf));
00517     return;
00518 }
00519 
00520 
00525 static void
00526 cmdhandler_handle_cmd_verbosity(int sockfd, cmdhandler_type* cmdc, int val)
00527 {
00528     char buf[ODS_SE_MAXLINE];
00529 
00530     se_log_assert(cmdc);
00531     se_log_assert(cmdc->engine);
00532     se_log_assert(cmdc->engine->config);
00533 
00534     se_log_init(cmdc->engine->config->log_filename,
00535         cmdc->engine->config->use_syslog, val);
00536 
00537     (void)snprintf(buf, ODS_SE_MAXLINE, "Verbosity level set to %i.\n", val);
00538     se_writen(sockfd, buf, strlen(buf));
00539 }
00540 
00541 
00546 static void
00547 cmdhandler_handle_cmd_error(int sockfd, const char* str)
00548 {
00549     char buf[ODS_SE_MAXLINE];
00550     (void)snprintf(buf, ODS_SE_MAXLINE, "Error: %s.\n", str?str:"(null)");
00551     se_writen(sockfd, buf, strlen(buf));
00552     return;
00553 }
00554 
00555 
00560 static void
00561 cmdhandler_handle_cmd_unknown(int sockfd, const char* str)
00562 {
00563     char buf[ODS_SE_MAXLINE];
00564     (void)snprintf(buf, ODS_SE_MAXLINE, "Unknown command %s.\n",
00565         str?str:"(null)");
00566     se_writen(sockfd, buf, strlen(buf));
00567     return;
00568 }
00569 
00570 
00589 static void
00590 cmdhandler_handle_cmd(cmdhandler_type* cmdc)
00591 {
00592     ssize_t n = 0;
00593     int sockfd = 0;
00594     char buf[ODS_SE_MAXLINE];
00595 
00596     se_log_assert(cmdc);
00597 
00598     sockfd = cmdc->client_fd;
00599 
00600 again:
00601     while ((n = read(sockfd, buf, ODS_SE_MAXLINE)) > 0) {
00602         buf[n-1] = '\0';
00603         n--;
00604         if (n <= 0) {
00605             return;
00606         }
00607         se_log_verbose("received command %s[%i]", buf, n);
00608 
00609         if (n == 4 && strncmp(buf, "help", n) == 0) {
00610             se_log_debug("help command");
00611             cmdhandler_handle_cmd_help(sockfd);
00612         } else if (n == 5 && strncmp(buf, "zones", n) == 0) {
00613             se_log_debug("list zones command");
00614             cmdhandler_handle_cmd_zones(sockfd, cmdc);
00615         } else if (n >= 4 && strncmp(buf, "sign", 4) == 0) {
00616             se_log_debug("sign zone command");
00617             if (buf[4] == '\0') {
00618                 /* NOTE: wouldn't it be nice that we default to --all? */
00619                 cmdhandler_handle_cmd_error(sockfd, "sign command needs "
00620                     "an argument (either '--all' or a zone name)");
00621             } else if (buf[4] != ' ') {
00622                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00623             } else {
00624                 cmdhandler_handle_cmd_sign(sockfd, cmdc, &buf[5]);
00625             }
00626         } else if (n >= 5 && strncmp(buf, "clear", 5) == 0) {
00627             se_log_debug("clear zone command");
00628             if (buf[5] == '\0') {
00629                 cmdhandler_handle_cmd_error(sockfd, "clear command needs "
00630                     "a zone name");
00631             } else if (buf[5] != ' ') {
00632                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00633             } else {
00634                 cmdhandler_handle_cmd_clear(sockfd, cmdc, &buf[6]);
00635             }
00636         } else if (n == 5 && strncmp(buf, "queue", n) == 0) {
00637             se_log_debug("list tasks command");
00638             cmdhandler_handle_cmd_queue(sockfd, cmdc);
00639         } else if (n == 5 && strncmp(buf, "flush", n) == 0) {
00640             se_log_debug("flush tasks command");
00641             cmdhandler_handle_cmd_flush(sockfd, cmdc);
00642         } else if (n >= 6 && strncmp(buf, "update", 6) == 0) {
00643             se_log_debug("update command");
00644             if (buf[6] == '\0') {
00645                 cmdhandler_handle_cmd_update(sockfd, cmdc, "--all");
00646             } else if (buf[6] != ' ') {
00647                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00648             } else {
00649                 cmdhandler_handle_cmd_update(sockfd, cmdc, &buf[7]);
00650             }
00651         } else if (n == 4 && strncmp(buf, "stop", n) == 0) {
00652             se_log_debug("shutdown command");
00653             cmdhandler_handle_cmd_stop(sockfd, cmdc);
00654             return;
00655         } else if (n == 5 && strncmp(buf, "start", n) == 0) {
00656             se_log_debug("start command");
00657             cmdhandler_handle_cmd_start(sockfd);
00658         } else if (n == 6 && strncmp(buf, "reload", n) == 0) {
00659             se_log_debug("reload command");
00660             cmdhandler_handle_cmd_reload(sockfd, cmdc);
00661         } else if (n == 7 && strncmp(buf, "running", n) == 0) {
00662             se_log_debug("running command");
00663             cmdhandler_handle_cmd_running(sockfd);
00664         } else if (n >= 9 && strncmp(buf, "verbosity", 9) == 0) {
00665             se_log_debug("verbosity command");
00666             if (buf[9] == '\0') {
00667                 cmdhandler_handle_cmd_error(sockfd, "verbosity command "
00668                     "an argument (verbosity level)");
00669             } else if (buf[9] != ' ') {
00670                 cmdhandler_handle_cmd_unknown(sockfd, buf);
00671             } else {
00672                 cmdhandler_handle_cmd_verbosity(sockfd, cmdc, atoi(&buf[10]));
00673             }
00674         } else {
00675             se_log_debug("unknown command");
00676             cmdhandler_handle_cmd_unknown(sockfd, buf);
00677         }
00678 
00679         se_log_debug("done handling command %s[%i]", buf, n);
00680         (void)snprintf(buf, SE_CMDH_CMDLEN, "\ncmd> ");
00681         se_writen(sockfd, buf, strlen(buf));
00682     }
00683 
00684     if (n < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) ) {
00685         goto again;
00686     } else if (n < 0 && errno == ECONNRESET) {
00687         se_log_debug("done handling client: %s", strerror(errno));
00688     } else if (n < 0 ) {
00689         se_log_error("command handler read error[%i]: %s", errno, strerror(errno));
00690     }
00691     return;
00692 }
00693 
00694 
00699 static void*
00700 cmdhandler_accept_client(void* arg)
00701 {
00702     cmdhandler_type* cmdc = (cmdhandler_type*) arg;
00703 
00704     se_thread_blocksigs();
00705     se_thread_detach(cmdc->thread_id);
00706 
00707     se_log_debug("command handler accept client %i", cmdc->client_fd);
00708     cmdhandler_handle_cmd(cmdc);
00709     if (cmdc->client_fd) {
00710         close(cmdc->client_fd);
00711     }
00712     cmdhandler_cleanup(cmdc);
00713     count--;
00714     return NULL;
00715 }
00716 
00717 
00722 cmdhandler_type*
00723 cmdhandler_create(const char* filename)
00724 {
00725     cmdhandler_type* cmdh = NULL;
00726     struct sockaddr_un servaddr;
00727     int listenfd = 0;
00728     int flags = 0;
00729     int ret = 0;
00730 
00731     if (!filename) {
00732         se_log_error("unable to create command handler, no filename");
00733         return NULL;
00734     }
00735     se_log_debug("create command handler to socket %s", filename);
00736 
00737     /* new socket */
00738     listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
00739     if (listenfd <= 0) {
00740         se_log_error("unable to create command handler, socket() failed: %s",
00741             strerror(errno));
00742         return NULL;
00743     }
00744     /* set it to non-blocking */
00745     flags = fcntl(listenfd, F_GETFL, 0);
00746     if (flags < 0) {
00747         se_log_error("unable to create command handler, fcntl(F_GETFL) "
00748             "failed: %s", strerror(errno));
00749         close(listenfd);
00750         return NULL;
00751     }
00752     flags |= O_NONBLOCK;
00753     if (fcntl(listenfd, F_SETFL, flags) < 0) {
00754         se_log_error("unable to create command handler, fcntl(F_SETFL) "
00755             "failed: %s", strerror(errno));
00756         close(listenfd);
00757         return NULL;
00758     }
00759 
00760     /* no suprises */
00761     unlink(filename);
00762     bzero(&servaddr, sizeof(servaddr));
00763     servaddr.sun_family = AF_UNIX;
00764     strncpy(servaddr.sun_path, filename, sizeof(servaddr.sun_path) - 1);
00765 
00766     /* bind and listen... */
00767     ret = bind(listenfd, (const struct sockaddr*) &servaddr,
00768         SUN_LEN(&servaddr));
00769     if (ret != 0) {
00770         se_log_error("unable to create command handler, bind() failed: %s",
00771             strerror(errno));
00772         close(listenfd);
00773         return NULL;
00774     }
00775     ret = listen(listenfd, ODS_SE_MAX_HANDLERS);
00776     if (ret != 0) {
00777         se_log_error("unable to create command handler, listen() failed: %s",
00778             strerror(errno));
00779         close(listenfd);
00780         return NULL;
00781     }
00782 
00783     /* all ok */
00784     cmdh = (cmdhandler_type*) se_malloc(sizeof(cmdhandler_type));
00785     cmdh->listen_fd = listenfd;
00786     cmdh->listen_addr = servaddr;
00787     cmdh->need_to_exit = 0;
00788     return cmdh;
00789 }
00790 
00791 
00796 void
00797 cmdhandler_start(cmdhandler_type* cmdhandler)
00798 {
00799     struct sockaddr_un cliaddr;
00800     socklen_t clilen;
00801     cmdhandler_type* cmdc = NULL;
00802     engine_type* engine = NULL;
00803     fd_set rset;
00804     int connfd = 0;
00805     int ret = 0;
00806 
00807     se_log_assert(cmdhandler);
00808     se_log_assert(cmdhandler->engine);
00809     se_log_debug("command handler start");
00810 
00811     engine = cmdhandler->engine;
00812     se_thread_detach(cmdhandler->thread_id);
00813     FD_ZERO(&rset);
00814     while (cmdhandler->need_to_exit == 0) {
00815         clilen = sizeof(cliaddr);
00816         FD_SET(cmdhandler->listen_fd, &rset);
00817         ret = select(ODS_SE_MAX_HANDLERS+1, &rset, NULL, NULL, NULL);
00818         if (ret < 0) {
00819             if (errno != EINTR && errno != EWOULDBLOCK) {
00820                 se_log_warning("cmdhandler select() error: %s",
00821                    strerror(errno));
00822             }
00823             continue;
00824         }
00825         if (FD_ISSET(cmdhandler->listen_fd, &rset)) {
00826             connfd = accept(cmdhandler->listen_fd,
00827                 (struct sockaddr *) &cliaddr, &clilen);
00828             if (connfd < 0) {
00829                 if (errno != EINTR && errno != EWOULDBLOCK) {
00830                     se_log_warning("command handler accept error: %s",
00831                         strerror(errno));
00832                 }
00833                 continue;
00834             }
00835             /* client accepted, create new thread */
00836             cmdc = (cmdhandler_type*) se_malloc(sizeof(cmdhandler_type));
00837             cmdc->listen_fd = cmdhandler->listen_fd;
00838             cmdc->client_fd = connfd;
00839             cmdc->listen_addr = cmdhandler->listen_addr;
00840             cmdc->engine = cmdhandler->engine;
00841             cmdc->need_to_exit = cmdhandler->need_to_exit;
00842             se_thread_create(&cmdc->thread_id, &cmdhandler_accept_client,
00843                 (void*) cmdc);
00844             count++;
00845             se_log_debug("command handler %i clients in progress...", count);
00846         }
00847     }
00848 
00849     se_log_debug("command handler done");
00850     engine = cmdhandler->engine;
00851     cmdhandler_cleanup(cmdhandler);
00852     engine->cmdhandler_done = 1;
00853     return;
00854 }
00855 
00856 
00861 void
00862 cmdhandler_cleanup(cmdhandler_type* cmdhandler)
00863 {
00864     if (cmdhandler) {
00865         se_free((void*)cmdhandler);
00866     } else {
00867         se_log_warning("clean up empty command handler");
00868     }
00869     return;
00870 }