pdbt.c

00001 /*
00002  * Copyright (C) 2009 1&1 Internet AG
00003  *
00004  * This file is part of sip-router, a free SIP server.
00005  *
00006  * sip-router is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version
00010  *
00011  * sip-router is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License 
00017  * along with this program; if not, write to the Free Software 
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 #define _GNU_SOURCE
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <unistd.h>
00025 #include <string.h>
00026 #include <fcntl.h>
00027 #include <sys/time.h>
00028 #include <poll.h>
00029 #include <ctype.h>
00030 #include <sys/socket.h>
00031 #include <netinet/in.h>
00032 #include <arpa/inet.h>
00033 #include <netdb.h>
00034 #include <errno.h>
00035 #include <limits.h>
00036 #include "dt.h"
00037 #include "dtm.h"
00038 #include "carrier.h"
00039 #include "log.h"
00040 
00041 
00042 
00043 
00044 #define NETBUFSIZE 200
00045 
00046 
00047 
00048 
00049 typedef void (*query_func_t)(char *number, char *comment, void *data);
00050 
00051 
00052 
00053 
00054 void print_usage(char *program) {
00055         set_log_level(LOG_INFO);
00056         LINFO("Usage: %s [<option>...] <command> [<param>...]\n", program);
00057         LINFO("  %s -s <csv file> -m <mmap file> [-k <ids>] [-o] [-u <tree file>] [-l <log level>] build\n", program);
00058         LINFO("  %s (-m <mmap file>|-r <host>:<port>) [-q <timeout>] [-f <query file>] [-t <carrier text file>] [-l <log level>] query <number>...\n", program);
00059         LINFO("\n");
00060         LINFO("  Commands:\n");
00061         LINFO("    build: Build a mmap image from a csv list.\n");
00062         LINFO("    query: Query a mmap image or pdb_server.\n");
00063         LINFO("           Uses Numbers given on commandline or in a file (-f).\n");
00064         LINFO("\n");
00065         LINFO("  Options:\n");
00066         LINFO("    -s <file>: Specifies the csv list.\n");
00067         LINFO("               Use '-' as filename to read from stdin.\n");
00068         LINFO("               Line format: <number prefix>;<carrier id>\n");
00069         LINFO("               Format of carrier id: [0-9][0-9][0-9]\n");
00070         LINFO("    -m <file>: Specifies the mmap image.\n");
00071         LINFO("    -f <file>: Specifies the query file.\n");
00072         LINFO("               Use '-' as filename to read from stdin.\n");
00073         LINFO("               Each number must be in a separate line.\n");
00074         LINFO("               Numbers on the command line will not be processed.\n");
00075         LINFO("    -t <file>: Specifies the file containing carrier names.\n");
00076         LINFO("               In addition to the carrier code these names will be shown\n");
00077         LINFO("               when querying numbers.\n");
00078         LINFO("               Each carrier id and name must be in a separate line.\n");
00079         LINFO("               Format: D[0-9][0-9][0-9] <name>\n");
00080         LINFO("    -k <ids>: Keep these carrier ids.\n");
00081         LINFO("              Merge all other carrier ids into a new id.\n");
00082         LINFO("              This will save some memory.\n");
00083         LINFO("              Format: <id>[,<id>...]\n");
00084         LINFO("    -r <host>:<port>: Host and port to be used for remote server queries.\n");
00085         LINFO("    -q <timeout>: Timeout for remote server queries in milliseconds.\n");
00086         LINFO("                  Default is 500 ms.\n");
00087         LINFO("    -o: Try to optimize the data structure when building a mmap image.\n");
00088         LINFO("    -u: Write (possibly optimized) tree structure in human-readable format to the given file.\n");
00089         LINFO("    -l <debug level>: %ld for debug level.\n", LOG_DEBUG);
00090         LINFO("                      %ld for info level.\n", LOG_INFO);
00091         LINFO("                      %ld for notice level.\n", LOG_NOTICE);
00092         LINFO("                      %ld for warning level.\n", LOG_WARNING);
00093         LINFO("                      %ld for error level.\n", LOG_ERR);
00094         LINFO("                      %ld for critical level.\n", LOG_CRIT);
00095         LINFO("                      %ld for alert level.\n", LOG_ALERT);
00096         LINFO("                      %ld for emergency level.\n", LOG_EMERG);
00097         LINFO("                      %ld to disable all messages.\n", LOG_EMERG-1);
00098         LINFO("                      Default is info level.\n");
00099         LINFO("    -h: Print this help.\n");
00100 }
00101 
00102 
00103 
00104 
00105 void print_stats(struct dt_node_t *root) {
00106         int s;
00107         int l;
00108         int c;
00109 
00110         LINFO("+----------------------------------------\n");
00111         s = dt_size(root);
00112         LINFO("| %ld nodes in tree (%ld bytes, %ld KB, %ld MB)\n", (long int)s, (long int)s*sizeof(struct dt_node_t), (long int)s*sizeof(struct dt_node_t)/1024, (long int)s*sizeof(struct dt_node_t)/1024/1024);
00113         l = dt_leaves(root);
00114         LINFO("| %ld nodes are leaves (%ld bytes, %ld KB, %ld MB)\n", (long int)l, (long int)l*sizeof(struct dt_node_t), (long int)l*sizeof(struct dt_node_t)/1024, (long int)l*sizeof(struct dt_node_t)/1024/1024);
00115         c = dt_loaded_nodes(root);
00116         LINFO("| %ld carrier nodes in tree\n", (long int)c);
00117         LINFO("| \n");
00118         LINFO("| After saving with leaf node compression:\n");
00119         LINFO("| %ld nodes in tree (%ld bytes, %ld KB, %ld MB)\n", (long int)s-l, (long int)(s-l)*sizeof(struct dtm_node_t), (long int)(s-l)*sizeof(struct dtm_node_t)/1024, (long int)(s-l)*sizeof(struct dtm_node_t)/1024/1024);
00120         LINFO("+----------------------------------------\n");
00121 }
00122 
00123 
00124 
00125 
00126 int file_query(char *filename, query_func_t query_func, void *data) {
00127         char * p;
00128         char * comment;
00129         FILE * fp;
00130         char * line = NULL;
00131         size_t len = 0;
00132         ssize_t read;
00133 
00134         LINFO("\nprocessing query file '%s'...\n", filename);
00135   if (strcmp(filename, "-")==0) fp=stdin;
00136         else fp = fopen(filename, "r");
00137         if (fp == NULL) {
00138                 LERR("cannot open file '%s'\n", filename);
00139                 return -1;
00140         }
00141         while ((read = getline(&line, &len, fp)) != -1) {
00142                 p=line;
00143                 while ((*p >= '0') && (*p <= '9') && (p < line+len)) p++;
00144                 *p='\0';
00145                 p++;
00146                 comment=p;
00147                 while ((*p >= 32) && (p < line+len)) p++;
00148                 *p='\0';
00149                 query_func(line, comment, data);
00150         }
00151         if (line) free(line);
00152         fclose(fp);
00153         return 0;
00154 }
00155 
00156 
00157 
00158 
00159 /*
00160  Read a csv list from the given file and build a dtree structure.
00161  Format of lines in csv file: "<number prefix>;<carrier id>".
00162  Format of carrier id: "[0-9][0-9][0-9]".
00163  Returns the number of lines imported or -1 on error.
00164 */
00165 int import_csv(struct dt_node_t *root, char *filename) {
00166         char *prefix;
00167         char *carrier_str;
00168         carrier_t carrier;
00169         long int ret;
00170 
00171         FILE * fp;
00172         char * line = NULL;
00173         size_t len = 0;
00174         ssize_t read;
00175         int i=0;
00176         int n=1;
00177 
00178   if (strcmp(filename, "-")==0) fp=stdin;
00179         else fp = fopen(filename, "r");
00180         if (fp == NULL) {
00181                 LERR("cannot open file '%s'\n", filename);
00182                 return -1;
00183         }
00184         while ((read = getline(&line, &len, fp)) != -1) {
00185                 carrier_str=line;
00186                 prefix=strsep(&carrier_str, ";");
00187                 ret=strtol(carrier_str, NULL, 10);
00188                 if (!IS_VALID_PDB_CARRIERID(ret)) {
00189                         LWARNING("invalid carrier '%s' in line %ld.\n", carrier_str, (long int)n);
00190                         if (line) free(line);
00191                         fclose(fp);
00192                         return -1;
00193                 }
00194                 else {
00195                         carrier=ret;
00196                         i++;
00197                         dt_insert(root, prefix, strlen(prefix), carrier);
00198                 }
00199                 n++;
00200         }
00201         if (line) free(line);
00202         fclose(fp);
00203         return i;
00204 }
00205 
00206 
00207 
00208 
00209 /*
00210  Returns 1 if the given node is a leaf node, 0 otherwise.
00211 */
00212 inline int dt_is_leaf(struct dt_node_t *root)
00213 {
00214         int i;
00215 
00216         for (i=0; i<10; i++) {
00217                 if (root->child[i]) return 0;
00218         }
00219 
00220         return 1;
00221 }
00222 
00223 
00224 
00225 /*
00226  Recursively writes sequences of digits (i.e., telephone numbers/prefixes) and mapped
00227  carrier ids to the given file descriptor for the entire subtree starting at the
00228  given node. Each written line matches one such sequence and its mapped carried id.
00229  Returns 1 on success, -1 otherwise.
00230  */
00231 int dt_write_tree_recursor(const struct dt_node_t *node, const int fd, char* number)
00232 {
00233         int i;
00234         int ret;
00235         int slen;
00236         char *buf;
00237         char *p;
00238         int bufsize;
00239 
00240         if (node == NULL) return 0;
00241 
00242         slen = strlen(number);
00243         if (slen > 0) {
00244                 
00245                 bufsize = slen + 1 + 1 + 3 + 1 + 1;                 // line buffer (telephone number + colon + white space + carrier ID + newline + \0)
00246                 buf = (char *)malloc(bufsize);      
00247                 if (buf == NULL) {
00248                         LERR("could not allocate line output buffer of size %d\n", bufsize);
00249                         return -1;
00250                 }
00251 
00252                 /* construct outline line */
00253                 p = strncpy(buf, number, slen);
00254                 p += slen;
00255                 strncpy(p, ": ", 2);
00256                 p += 2;
00257                 ret = snprintf(p, 5, "%d\n", node->carrier);
00258                 if (ret < 1 || ret > 4) {
00259                         LERR("snprintf failed to write correct number of characters\n");
00260                         return -1;
00261                 }
00262 
00263                 /* write line to file */
00264                 ret = write(fd, (void *)buf, strlen(buf));
00265                 if (ret != strlen(buf)) {
00266                         LERR("could not write (complete) line output '%s' to file\n", number);
00267                         return -1;
00268                 }
00269                 free(buf);
00270         }
00271 
00272         for (i=0;i<10;i++) {
00273                 /* extend number by single digit and adjust terminating null byte */
00274                 number[slen] = i + '0';
00275                 number[slen+1] = '\0';      /* must always be done because other recursive invocations operate on `number' too */
00276                 ret = dt_write_tree_recursor(node->child[i], fd, number);
00277                 if (ret < 0) {
00278                         LERR("could not write node\n");
00279                         return -1;
00280                 }
00281         }
00282 
00283         return 1;
00284 }
00285 
00286 
00287 
00288 /*
00289  Writes tree to a file in human-readable format, i.e., ASCII.
00290  Returns 1 on success, -1 otherwise.
00291  */
00292 int dt_write_tree(const struct dt_node_t *root, const char* filename)
00293 {
00294         int fd;
00295         char number[25];
00296         number[0] = '\0';
00297 
00298         fd = creat(filename, S_IRWXU);
00299         if (fd < 0) {
00300                 LERR("cannot create file '%s'\n", filename);
00301                 return -1;
00302         }
00303         
00304         if (dt_write_tree_recursor(root, fd, (char *)&number) < 0) {
00305                 LERR("writing tree to file '%s' failed\n", filename);
00306                 return -1;
00307         }
00308 
00309         close(fd);
00310         return 1;
00311 }
00312 
00313 
00314 
00315 /*
00316   Saves the given node and all sub-nodes to an mmappable file recursively.
00317   Pointers from parent to child nodes will be represented by positive numbers indexing
00318   file offsets. The "pointee" address within the file will be linearly proportional to
00319   this number.
00320   Pointers to leaf nodes will be replaced by leaf node carrier ids encoded in negative
00321   numbers, thereby discarding the leaves layer in the tree. Note that this optimization
00322   accounts for the majority of space saving.
00323   Returns the index of the next free node, -1 on error.
00324  */
00325 dtm_node_index_t save_recursor(struct dt_node_t *root, int fd, dtm_node_index_t n) {
00326         dtm_node_index_t i;
00327         dtm_node_index_t nn=n+1; /* next free node */
00328         struct dtm_node_t node;
00329         int offset;
00330 
00331         node.carrier=root->carrier;
00332         for (i=0; i<10; i++) {
00333                 if (root->child[i]) {
00334                         if (dt_is_leaf(root->child[i])) {
00335                                 node.child[i]=-root->child[i]->carrier;
00336                         }
00337                         else {
00338                                 node.child[i]=nn;
00339                                 nn=save_recursor(root->child[i], fd, nn);
00340                         }
00341                 }
00342                 else {
00343                         node.child[i]=NULL_CARRIERID;
00344                 }
00345         }
00346 
00347         offset=lseek(fd, n*sizeof(struct dtm_node_t), SEEK_SET);
00348         if (offset < 0) {
00349                 LERR("could not position file offset to address %d: errno=%d (%s)\n", n*sizeof(struct dtm_node_t), errno, strerror(errno));
00350                 return -1;
00351         }
00352         if (write(fd, &node, sizeof(struct dtm_node_t)) != sizeof(struct dtm_node_t)) {
00353                 LERR("could not write %d bytes of node data at file address %d: errno=%d (%s)\n", sizeof(struct dtm_node_t), offset, errno, strerror(errno));
00354                 return -1;
00355         }
00356 
00357         return nn;
00358 }
00359 
00360 
00361 
00362 
00363 /*
00364         Saves the given tree in a mmappable file.
00365         Returns the number of nodes saved or -1 on error.
00366 */
00367 int save_mmap(struct dt_node_t *root, char *filename) {
00368         int fd;
00369         int n;
00370 
00371         fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU);
00372         if (fd < 0) {
00373                 LERR("cannot create file '%s'\n", filename);
00374                 return -1;
00375         }
00376 
00377         n=save_recursor(root, fd, 0);
00378 
00379         close(fd);
00380 
00381         return n;
00382 }
00383 
00384 
00385 
00386 
00387 /*
00388  Returns 1 if carrier is found in keep_carriers, 0 otherwise.
00389 */
00390 int keep_carrier_func(carrier_t carrier, int keep_carriers_num, carrier_t keep_carriers[])
00391 {
00392         int i;
00393 
00394         for (i=0; i<keep_carriers_num; i++) {
00395                 if (keep_carriers[i]==carrier) return 1;
00396         }
00397 
00398         return 0;
00399 }
00400 
00401 
00402 
00403 
00404 int merge_carrier_recursor(struct dt_node_t *node, int keep_carriers_num, carrier_t keep_carriers[], carrier_t lastcarrier)
00405 {
00406   carrier_t currentcarrier;
00407         int i;
00408         int sum=0;
00409 
00410         if (node==NULL) return 0;
00411 
00412         if (node->carrier>0) {
00413                 if (!keep_carrier_func(node->carrier, keep_carriers_num, keep_carriers)) {
00414                         sum++;
00415                         if (lastcarrier==0) node->carrier=0; /* first carrier we encountered. we can remove it since we are not interested in it. */
00416                         else {
00417                                 node->carrier=OTHER_CARRIERID; /* we already have a carrier we are interested in. this is an exception, set it to a special carrier id. */
00418                         }
00419                 }
00420         }
00421 
00422         if (node->carrier>0) currentcarrier=node->carrier;
00423         else currentcarrier=lastcarrier;
00424 
00425         /* merge children carriers */
00426         for (i=0; i<10; i++) {
00427                 sum+=merge_carrier_recursor(node->child[i], keep_carriers_num, keep_carriers, currentcarrier);
00428         }
00429 
00430         return sum;
00431 }
00432 
00433 
00434 
00435 
00436 /*
00437  Merge all carriers not in keep_carriers into one new carrier id.
00438  This will save some memory.
00439  Returns the number of nodes modified.
00440 */
00441 int merge_carrier(struct dt_node_t *root, int keep_carriers_num, carrier_t keep_carriers[])
00442 {
00443         return merge_carrier_recursor(root, keep_carriers_num, keep_carriers, 0);
00444 }
00445 
00446 
00447 
00448 
00452 int query_udp(char *number, int timeout, struct pollfd *pfds, struct sockaddr_in *dstaddr, socklen_t dstaddrlen)
00453 {
00454         struct timeval tstart, tnow;
00455         short int carrierid;
00456         char buf[NETBUFSIZE+1+sizeof(carrierid)];
00457         size_t reqlen;
00458         int ret, nflush;
00459         long int td;
00460 
00461         if (gettimeofday(&tstart, NULL) != 0) {
00462                 LERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
00463                 return -1;
00464         }
00465 
00466         /* clear recv buffer */
00467         nflush = 0;
00468         while (recv(pfds->fd, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) {
00469                 nflush++;
00470                 if (gettimeofday(&tnow, NULL) != 0) {
00471                         LERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
00472                         return -1;
00473                 }
00474                 td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
00475                 if (td > timeout) {
00476                         LWARNING("exceeded timeout while flushing recv buffer.\n");
00477                         return -1;
00478                 }
00479         }
00480         
00481         /* prepare request */
00482         reqlen = strlen(number) + 1; /* include null termination */
00483         if (reqlen > NETBUFSIZE) {
00484                 LERR("number too long '%s'.\n", number);
00485                 return -1;
00486         }
00487         strcpy(buf, number);
00488 
00489         /* send request to all servers */
00490         ret=sendto(pfds->fd, buf, reqlen, MSG_DONTWAIT, (struct sockaddr *)dstaddr, dstaddrlen);
00491         if (ret < 0) {
00492                 LERR("sendto() failed with errno=%d (%s)\n", errno, strerror(errno));
00493                 return -1;
00494         }
00495                 
00496         /* wait for response */
00497         for (;;) {
00498                 if (gettimeofday(&tnow, NULL) != 0) {
00499                         LERR("gettimeofday() failed with errno=%d (%s)\n", errno, strerror(errno));
00500                         return -1;
00501                 }
00502                 td=(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000) / 1000;
00503                 if (td > timeout) {
00504                         LWARNING("exceeded timeout while waiting for response.\n");
00505                         return -1;
00506                 }
00507                 
00508                 ret=poll(pfds, 1, timeout-td);
00509                 if (pfds->revents & POLLIN) {
00510                         if (recv(pfds->fd, buf, NETBUFSIZE, MSG_DONTWAIT) > 0) { /* do not block - just in case select/poll was wrong */
00511                                 buf[NETBUFSIZE] = '\0';
00512                                 if (strcmp(buf, number) == 0) {
00513                                         carrierid=ntohs(*((short int *)&(buf[reqlen]))); /* convert to host byte order */
00514                                         goto found;
00515                                 }
00516                         }
00517                 }
00518                 pfds->revents = 0;
00519         }
00520 
00521         found:
00522         if (gettimeofday(&tnow, NULL) == 0) {
00523                 LINFO("got an answer in %f ms\n", ((double)(tnow.tv_usec-tstart.tv_usec+(tnow.tv_sec-tstart.tv_sec)*1000000))/1000);
00524         }
00525         return carrierid;
00526 }
00527 
00528 
00529 
00530 
00531 struct server_query_data_t {
00532         int timeout;
00533         struct sockaddr_in dstaddr;
00534         socklen_t dstaddrlen;
00535         struct pollfd pfds;
00536 };
00537 
00538 
00539 
00540 
00541 void query_mmap(char *number, char *comment, void *data) {
00542         int nmatch;
00543         carrier_t carrierid;
00544         struct dtm_node_t *mroot = (struct dtm_node_t *)data;
00545 
00546         nmatch=dtm_longest_match(mroot, number, strlen(number), &carrierid);
00547 
00548         if (nmatch<=0) {
00549                 LINFO("%s:%s:%ld:%s\n", number, comment, (long int)carrierid, "not allocated, probably old");
00550         }
00551         else {
00552                 LINFO("%s:%s:%ld:%s\n", number, comment, (long int)carrierid, carrierid2name(carrierid));
00553                 /* LINFO("%s: found: carrier_id=%ld, carrier_name='%s', nmatch=%ld, comment='%s'\n", number, (long int)carrierid, carrierid2name(carrierid), (long int)nmatch, comment);
00554                 */
00555         }
00556 }
00557 
00558 
00559 
00560 
00561 void query_server(char *number, char *comment, void *data) {
00562         carrier_t carrierid;
00563         struct server_query_data_t *sdata = (struct server_query_data_t *)data;
00564 
00565         carrierid = query_udp(number, sdata->timeout, &(sdata->pfds), &(sdata->dstaddr), sdata->dstaddrlen);
00566 
00567         if (carrierid<=0) {
00568                 LINFO("%s: not_found: comment='%s'\n", number, comment);
00569         }
00570         else {
00571                 LINFO("%s:%ld:%s\n", number, (long int)carrierid, carrierid2name(carrierid));
00572                 /* LINFO("%s: found: carrier_id=%ld, carrier_name='%s', comment='%s'\n", number, (long int)carrierid, carrierid2name(carrierid), comment);
00573                 */
00574         }
00575 }
00576 
00577 
00578 
00579 
00580 int main(int argc, char *argv[]) {
00581         int n;
00582         struct dt_node_t root;
00583         memset(&root, 0, sizeof(root));
00584         struct dtm_node_t *mroot;
00585 
00586         int opt;
00587         char *csv_file = NULL;
00588         char *mmap_file = NULL;
00589         char *query_file = NULL;
00590         char *tree_file = NULL;
00591         int optimize = 0;
00592         int keep_carriers_num = 0;
00593         carrier_t keep_carriers[MAX_PDB_CARRIERID+1];
00594         char *host_str = NULL;
00595         char *port_str = NULL;
00596         unsigned short int port = 0;
00597         char *tmp;
00598         int log_level = LOG_INFO;
00599 
00600         struct hostent *hp;
00601         int sockfd;
00602 
00603         struct server_query_data_t sdata;
00604 
00605         char *id_str;
00606         long int ret;
00607 
00608         sdata.timeout=500;
00609 
00610         init_carrier_names();
00611 
00612         init_log("pdbt", 0);
00613 
00614         while ((opt = getopt(argc, argv, "s:m:f:u:t:r:q:k:ol:h")) != -1) {
00615                 switch (opt) {
00616                 case 's':
00617                         csv_file = optarg;
00618                         break;
00619                 case 'm':
00620                         mmap_file = optarg;
00621                         break;
00622                 case 'f':
00623                         query_file = optarg;
00624                         break;
00625                 case 'u':
00626                         tree_file = optarg;
00627                         break;
00628                 case 'k':
00629                         while ((id_str=strsep(&optarg, ","))) {
00630                                 ret=strtol(id_str, NULL, 10);
00631                                 if (!IS_VALID_PDB_CARRIERID(ret)) {
00632                                         LERR("invalid carrier id '%s' specified.\n", id_str);
00633                                         return -1;
00634                                 }
00635                                 if (keep_carriers_num>MAX_PDB_CARRIERID) {
00636                                         LERR("too many carrier ids specified.\n");
00637                                         return -1;
00638                                 }
00639                                 keep_carriers[keep_carriers_num]=ret;
00640                                 keep_carriers_num++;
00641                         }
00642                         break;
00643                 case 't':
00644                         if (load_carrier_names(optarg)<0) {
00645                                 LERR("cannot load carrier names from '%s'.\n", optarg);
00646                                 return -1;
00647                         }
00648                         break;
00649                 case 'r':
00650                         host_str=optarg;
00651 
00652                         tmp = strchr(host_str, ':');
00653                         if (tmp == NULL) {
00654                                 LERR("syntax error in remote host:port specification '%s'.\n", host_str);
00655                                 return -1;
00656                         }
00657                         *tmp = '\0';
00658                         port_str = tmp + 1;
00659 
00660                         ret=strtol(port_str, NULL, 10);
00661                         if ((ret<0) || (ret==LONG_MAX)) {
00662                                 LERR("invalid timeout '%s'\n", optarg);
00663                                 return -1;
00664                         }
00665                         port = ret;
00666 
00667                         break;
00668                 case 'q':
00669                         ret=strtol(optarg, NULL, 10);
00670                         if ((ret<0) || (ret>65535)) {
00671                                 LERR("invalid port '%s'\n", port_str);
00672                                 return -1;
00673                         }
00674                         sdata.timeout = ret;
00675 
00676                         break;
00677                 case 'o':
00678                         optimize=1;
00679                         break;
00680                 case 'l':
00681                         ret=strtol(optarg, NULL, 10);
00682                         if ((ret<LOG_EMERG-1) || (ret>LOG_DEBUG)) {
00683                                 LERR("invalid log level '%s' specified.\n", optarg);
00684                                 return -1;
00685                         }
00686                         log_level=ret;
00687                         break;
00688                 case 'h':
00689                         print_usage(argv[0]);
00690                         return 0;
00691                         break;
00692                 default:
00693                         LERR("invalid option '%c'.\n", opt);
00694                         print_usage(argv[0]);
00695                         return 1;
00696                 }
00697         }
00698 
00699         set_log_level(log_level);
00700 
00701         if (optind>=argc) {
00702                 LERR("no command specified.\n");
00703                 return 1;
00704         }
00705 
00706         if (strcmp(argv[optind], "build")==0) {
00707                 if (csv_file==NULL) {
00708                         LERR("no csv file specified.\n");
00709                         return 1;
00710                 }
00711 
00712                 if (mmap_file==NULL) {
00713                         LERR("no mmap file specified.\n");
00714                         return 1;
00715                 }
00716 
00717                 LINFO("loading '%s'...\n", csv_file);
00718                 n = import_csv(&root, csv_file);
00719                 if (n < 0) {
00720                         LERR("cannot import '%s'\n", csv_file);
00721                         return -1;
00722                 }
00723                 LINFO("done.\n");
00724                 LINFO("%ld lines imported\n", (long int)n);
00725 
00726                 LINFO("Node size is %ld bytes (%ld for dtm)\n", (long int)sizeof(struct dt_node_t), (long int)sizeof(struct dtm_node_t));
00727                 print_stats(&root);
00728 
00729                 if (keep_carriers_num) {
00730                         LINFO("merging carriers...\n");
00731                         n=merge_carrier(&root, keep_carriers_num, keep_carriers);
00732                         LINFO("done (modified %ld nodes).\n", (long int)n);
00733                 }
00734 
00735                 if (optimize) {
00736                         LINFO("optimizing...\n");
00737                         dt_optimize(&root);
00738                         LINFO("done.\n");
00739                         print_stats(&root);
00740                 }
00741 
00742                 if (tree_file != NULL) {
00743                         LINFO("writing human-readable tree...\n");
00744                         if (dt_write_tree(&root, tree_file) < 0) {
00745                                 LERR("cannot write tree\n");
00746                                 return -1;
00747                     }
00748                         LINFO("done.\n");
00749                 }
00750 
00751                 LINFO("saving to '%s'...\n", mmap_file);
00752                 n = save_mmap(&root, mmap_file);
00753                 if (n < 0) {
00754                         LERR("cannot save '%s'\n", mmap_file);
00755                         return -1;
00756                 }
00757                 LINFO("done.\n");
00758                 LINFO("%ld nodes saved\n", (long int)n);
00759         }
00760         else if (strcmp(argv[optind], "query")==0) {
00761                 if ((mmap_file!=NULL) && (host_str!=NULL)) {
00762                         LERR("you cannot query a pdb_server and a mmap file at the same time.\n");
00763                         return 1;
00764                 }
00765 
00766                 if ((mmap_file==NULL) && (host_str==NULL)) {
00767                         LERR("neither a mmap file nor a remote host specified.\n");
00768                         return 1;
00769                 }
00770 
00771                 if (mmap_file==NULL) {
00772                         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
00773                         if (sockfd<0) {
00774                                 LERR("socket() failed with errno=%d (%s).\n", errno, strerror(errno));
00775                                 return -1;
00776                         }
00777 
00778                         memset(&sdata.dstaddr, 0, sizeof(sdata.dstaddr));
00779                         sdata.dstaddr.sin_family = AF_INET;
00780                         sdata.dstaddr.sin_port = htons(port);
00781                         hp = gethostbyname(host_str);
00782                         if (hp == NULL) {
00783                                 LERR("gethostbyname(%s) failed with h_errno=%d.\n", host_str, h_errno);
00784                                 close(sockfd);
00785                                 return -1;
00786                         }
00787                         memcpy(&sdata.dstaddr.sin_addr.s_addr, hp->h_addr, hp->h_length);
00788                         sdata.dstaddrlen=sizeof(sdata.dstaddr);
00789 
00790                         sdata.pfds.fd=sockfd;
00791                         sdata.pfds.events=POLLIN;
00792 
00793                         if (query_file==NULL) {
00794                                 LINFO("\nprocessing command line parameters...\n");
00795                                 for (n=optind+1; n<argc; n++) {
00796                                         query_server(argv[n], "", &sdata);
00797                                 }
00798                         }
00799                         else {
00800                                 file_query(query_file, query_server, &sdata);
00801                         }
00802                 }
00803                 else {
00804                         mroot=dtm_load(mmap_file);
00805                         if (mroot == NULL) {
00806                                 LERR("cannot load '%s'.\n", mmap_file);
00807                                 return -1;
00808                         }
00809                         
00810                         if (query_file==NULL) {
00811                                 LINFO("\nprocessing command line parameters...\n");
00812                                 for (n=optind+1; n<argc; n++) {
00813                                         query_mmap(argv[n], "", mroot);
00814                                 }
00815                         }
00816                         else {
00817                                 file_query(query_file, query_mmap, mroot);
00818                         }
00819                 }
00820         }
00821         else {
00822                 LERR("invalid command '%s'.\n", argv[optind]);
00823                 return 1;
00824         }
00825 
00826         return 0;
00827 }