ccnr_init.c

Go to the documentation of this file.
00001 /**
00002  * @file ccnr_init.c
00003  * 
00004  * Part of ccnr -  CCNx Repository Daemon.
00005  *
00006  */
00007 
00008 /*
00009  * Copyright (C) 2011 Palo Alto Research Center, Inc.
00010  *
00011  * This work is free software; you can redistribute it and/or modify it under
00012  * the terms of the GNU General Public License version 2 as published by the
00013  * Free Software Foundation.
00014  * This work is distributed in the hope that it will be useful, but WITHOUT ANY
00015  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00016  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
00017  * for more details. You should have received a copy of the GNU General Public
00018  * License along with this program; if not, write to the
00019  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020  * Boston, MA 02110-1301, USA.
00021  */
00022 
00023 
00024 #include <errno.h>
00025 #include <fcntl.h>
00026 #include <inttypes.h>
00027 #include <limits.h>
00028 #include <netdb.h>
00029 #include <poll.h>
00030 #include <signal.h>
00031 #include <stddef.h>
00032 #include <stdint.h>
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <time.h>
00037 #include <unistd.h>
00038 #include <arpa/inet.h>
00039 #include <sys/mman.h>
00040 #include <sys/time.h>
00041 #include <sys/socket.h>
00042 #include <sys/stat.h>
00043 #include <sys/types.h>
00044 #include <sys/un.h>
00045 #include <netinet/in.h>
00046 #include <netinet/tcp.h>
00047 
00048 #include <ccn/bloom.h>
00049 #include <ccn/ccn.h>
00050 #include <ccn/ccn_private.h>
00051 #include <ccn/charbuf.h>
00052 #include <ccn/face_mgmt.h>
00053 #include <ccn/hashtb.h>
00054 #include <ccn/indexbuf.h>
00055 #include <ccn/schedule.h>
00056 #include <ccn/reg_mgmt.h>
00057 #include <ccn/uri.h>
00058 
00059 #include <sync/SyncBase.h>
00060 
00061 #include "ccnr_private.h"
00062 
00063 #include "ccnr_init.h"
00064 
00065 #include "ccnr_dispatch.h"
00066 #include "ccnr_forwarding.h"
00067 #include "ccnr_internal_client.h"
00068 #include "ccnr_io.h"
00069 #include "ccnr_msg.h"
00070 #include "ccnr_net.h"
00071 #include "ccnr_proto.h"
00072 #include "ccnr_sendq.h"
00073 #include "ccnr_store.h"
00074 #include "ccnr_sync.h"
00075 #include "ccnr_util.h"
00076 
00077 static int load_policy(struct ccnr_handle *h);
00078 static int merge_files(struct ccnr_handle *h);
00079 
00080 /**
00081  * Read the contents of the repository config file
00082  *
00083  * Calls r_init_fail and returns NULL in case of error.
00084  * @returns unparsed content of config file in a newly allocated charbuf
00085  */
00086 struct ccn_charbuf *
00087 r_init_read_config(struct ccnr_handle *h)
00088 {
00089     struct ccn_charbuf *path = NULL;
00090     struct ccn_charbuf *contents = NULL;
00091     size_t sz = 800;
00092     ssize_t sres = -1;
00093     int fd;
00094     
00095     h->directory = getenv("CCNR_DIRECTORY");
00096     if (h->directory == NULL || h->directory[0] == 0)
00097         h->directory = ".";
00098     path = ccn_charbuf_create();
00099     contents = ccn_charbuf_create();
00100     if (path == NULL || contents == NULL)
00101         return(NULL);
00102     ccn_charbuf_putf(path, "%s/config", h->directory);
00103     fd = open(ccn_charbuf_as_string(path), O_RDONLY);
00104     if (fd == -1) {
00105         if (errno == ENOENT)
00106             sres = 0;
00107         else
00108             r_init_fail(h, __LINE__, ccn_charbuf_as_string(path), errno);
00109     }
00110     else {
00111         for (;;) {
00112             sres = read(fd, ccn_charbuf_reserve(contents, sz), sz);
00113             if (sres == 0)
00114                 break;
00115             if (sres < 0) {
00116                 r_init_fail(h, __LINE__, "Read failed reading config", errno);
00117                 break;
00118             }
00119             contents->length += sres;
00120             if (contents->length > 999999) {
00121                 r_init_fail(h, __LINE__, "config file too large", 0);
00122                 sres = -1;
00123                 break;
00124             }
00125         }
00126         close(fd);
00127     }
00128     ccn_charbuf_destroy(&path);
00129     if (sres < 0)
00130         ccn_charbuf_destroy(&contents);
00131     return(contents);
00132 }
00133 
00134 static int
00135 r_init_debug_getenv(struct ccnr_handle *h, const char *envname)
00136 {
00137     const char *debugstr;
00138     int debugval;
00139     
00140     debugstr = getenv(envname);
00141     debugval = ccnr_msg_level_from_string(debugstr);
00142     /* Treat 1 and negative specially, for some backward compatibility. */
00143     if (debugval == 1)
00144         debugval = CCNL_WARNING;
00145     if (debugval < 0) {
00146         debugval = CCNL_FINEST;
00147         if (h != NULL)
00148             ccnr_msg(h, "%s='%s' is not valid, using FINEST", envname, debugstr);
00149     }
00150     return(debugval);
00151 }
00152 
00153 /**
00154  * Get the specified numerical config value, subject to limits.
00155  */
00156 intmax_t
00157 r_init_confval(struct ccnr_handle *h, const char *key,
00158                      intmax_t lo, intmax_t hi, intmax_t deflt) {
00159     const char *s;
00160     intmax_t v;
00161     char *ep;
00162     
00163     if (!(lo <= deflt && deflt <= hi))
00164         abort();
00165     s = getenv(key);
00166     if (s != NULL && s[0] != 0) {
00167         ep = "x";
00168         v = strtoimax(s, &ep, 10);
00169         if (v != 0 || ep[0] == 0) {
00170             if (v > hi)
00171                 v = hi;
00172             if (v < lo)
00173                 v = lo;
00174             if (CCNSHOULDLOG(h, mmm, CCNL_FINEST))
00175                 ccnr_msg(h, "Using %s=%jd", key, v);
00176             return(v);
00177         }
00178     }
00179     return (deflt);
00180 }
00181 
00182 #define CCNR_CONFIG_PASSMASK   0x003 /* config pass */
00183 #define CCNR_CONFIG_IGNORELINE 0x100 /* Set if there are prior problems */
00184 #define CCNR_CONFIG_ERR        0x200 /* Report error rather than warning */
00185 /**
00186  * Message helper for r_init_parse_config()
00187  */
00188 static void
00189 r_init_config_msg(struct ccnr_handle *h, int flags,
00190                   int line, int chindex, const char *msg)
00191 {
00192     const char *problem = "Problem";
00193     int log_at = CCNL_WARNING;
00194     
00195     log_at = CCNL_WARNING;
00196     if ((flags & CCNR_CONFIG_ERR) != 0) {
00197         problem = "Error";
00198         log_at = CCNL_ERROR;
00199     }
00200     if ((flags & (CCNR_CONFIG_IGNORELINE|CCNR_CONFIG_PASSMASK)) == 1 &&
00201         CCNSHOULDLOG(h, mmm, log_at)) {
00202         ccnr_msg(h, "%s in config file %s/config - line %d column %d: %s",
00203                  problem, h->directory, line, chindex + 1, msg);
00204     }
00205 }
00206 
00207 /**
00208  * Parse the buffered configuration found in config
00209  *
00210  * The pass argument controls what is done with the result:
00211  *   0 - silent check for syntax errors;
00212  *   1 - check for syntax errors and warnings, logging the results,
00213  *   2 - incorporate settings into environ.
00214  *
00215  * @returns -1 if an error is found, otherwise the count of warnings.
00216  */
00217 int
00218 r_init_parse_config(struct ccnr_handle *h, struct ccn_charbuf *config, int pass)
00219 {
00220     struct ccn_charbuf *key = NULL;
00221     struct ccn_charbuf *value = NULL;
00222     const unsigned char *b;
00223     int line;
00224     size_t i;
00225     size_t sol; /* start of line */
00226     size_t len; /* config->len */
00227     size_t ndx; /* temp for column report*/
00228     int ch;
00229     int warns = 0;
00230     int errors = 0;
00231     int use_it = 0;
00232     static const char pclegal[] = 
00233         "~@%-+=:,./[]"
00234         "abcdefghijklmnopqrstuvwxyz"
00235         "0123456789"
00236         "_"
00237         "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
00238     const char *klegal = strchr(pclegal, 'a');
00239     int flags; /* for reporting */
00240     
00241     b = config->buf;
00242     len = config->length;
00243     if (len == 0)
00244         return(0);
00245     ccn_charbuf_as_string(config);
00246     key = ccn_charbuf_create();
00247     value = ccn_charbuf_create();
00248     if (key == NULL || value == NULL)
00249         return(-1);
00250     /* Ensure there is null termination in the buffered config */
00251     if (ccn_charbuf_as_string(config) == NULL)
00252         return(-1);
00253     for (line = 1, i = 0, ch = b[0], sol = 0; i < len;) {
00254         flags = pass;
00255         use_it = 0;
00256         if (ch > ' ' && ch != '#') {
00257             key->length = value->length = 0;
00258             /* parse key */
00259             while (i < len && ch != '\n' && ch != '=') {
00260                 ccn_charbuf_append_value(key, ch, 1);
00261                 ch = b[++i];
00262             }
00263             if (ch == '=')
00264                 ch = b[++i];
00265             else {
00266                 r_init_config_msg(h, flags, line, key->length, "missing '='");
00267                 flags |= CCNR_CONFIG_IGNORELINE;
00268                 warns++;
00269                 ch = '\n';
00270             }
00271             /* parse value */
00272             while (i < len && ch > ' ') {
00273                 ccn_charbuf_append_value(value, ch, 1);
00274                 ch = b[++i];
00275             }
00276             /* See if it might be one of ours */
00277             if (key->length < 5 || (memcmp(key->buf, "CCNR_", 5) != 0 &&
00278                                     memcmp(key->buf, "CCNS_", 5) != 0)) {
00279                 r_init_config_msg(h, flags, line, 0,
00280                                   "ignoring unrecognized key");
00281                 flags |= CCNR_CONFIG_IGNORELINE;
00282                 warns++;
00283                 use_it = 0;
00284             }
00285             else
00286                 use_it = 1;
00287 
00288             /* Check charset of key */
00289             ndx = strspn(ccn_charbuf_as_string(key), klegal);
00290             if (ndx != key->length) {
00291                 errors += use_it;
00292                 r_init_config_msg(h, (flags | CCNR_CONFIG_ERR), line, ndx,
00293                                   "unexpected character in key");
00294                 flags |= CCNR_CONFIG_IGNORELINE;
00295                 warns++;
00296             }
00297             /* Check charset of value */
00298             ndx = strspn(ccn_charbuf_as_string(value), pclegal);
00299             if (ndx != value->length) {
00300                 errors += use_it;
00301                 r_init_config_msg(h, (flags | CCNR_CONFIG_ERR),
00302                                   line, key->length + 1 + ndx,
00303                                   "unexpected character in value");
00304                 flags |= CCNR_CONFIG_IGNORELINE;
00305                 warns++;
00306             }
00307         }
00308         if (ch == '#') {
00309             /* a comment line or error recovery. */
00310             while (i < len && ch != '\n')
00311                 ch = b[++i];
00312         }
00313         while (i < len && ch <= ' ') {
00314             if (ch == '\n') {
00315                 line++;
00316                 sol = i;
00317                 break;
00318             }
00319             if (memchr("\r\t ", ch, 3) == NULL) {
00320                 r_init_config_msg(h, pass, line, i - sol,
00321                                   "non-whitespace control char at end of line");
00322                 warns++;
00323             } 
00324             ch = b[++i];
00325         }
00326         if (i == len) {
00327             r_init_config_msg(h, flags, line, i - sol,
00328                               "missing newline at end of file");
00329             warns++;
00330             ch = '\n';
00331         }
00332         else if (ch == '\n')
00333             ch = b[++i];
00334         else {
00335             r_init_config_msg(h, flags, line, i - sol, "junk at end of line");
00336             flags |= CCNR_CONFIG_IGNORELINE;
00337             warns++;
00338             ch = '#';
00339         }
00340         if (flags == 0 && strcmp(ccn_charbuf_as_string(key), "CCNR_DEBUG") == 0) {
00341             /* Set this on pass 0 so that it takes effect sooner. */
00342             h->debug = 1;
00343             setenv("CCNR_DEBUG", ccn_charbuf_as_string(value), 1);
00344             h->debug = r_init_debug_getenv(h, "CCNR_DEBUG");
00345         }
00346         if (pass == 2 && use_it) {
00347             if (CCNSHOULDLOG(h, mmm, CCNL_FINEST))
00348                 ccnr_msg(h, "config: %s=%s",
00349                         ccn_charbuf_as_string(key),
00350                         ccn_charbuf_as_string(value));
00351             setenv(ccn_charbuf_as_string(key), ccn_charbuf_as_string(value), 1);
00352         }
00353     }
00354     ccn_charbuf_destroy(&key);
00355     ccn_charbuf_destroy(&value);
00356     return(errors ? -1 : warns);
00357 }
00358 
00359 static int
00360 establish_min_send_bufsize(struct ccnr_handle *h, int fd, int minsize)
00361 {
00362     int res;
00363     int bufsize;
00364     int obufsize;
00365     socklen_t bufsize_sz;
00366 
00367     bufsize_sz = sizeof(bufsize);
00368     res = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsize, &bufsize_sz);
00369     if (res == -1)
00370         return (res);
00371     obufsize = bufsize;
00372     if (bufsize < minsize) {
00373         bufsize = minsize;
00374         res = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
00375         if (res == -1)
00376             return(res);
00377     }
00378     if (CCNSHOULDLOG(h, sdfdsf, CCNL_INFO))
00379         ccnr_msg(h, "SO_SNDBUF for fd %d is %d (was %d)", fd, bufsize, obufsize);
00380     return(bufsize);
00381 }
00382 
00383 /**
00384  * If so configured, replace fd with a tcp socket
00385  * @returns new address family
00386  */
00387 static int
00388 try_tcp_instead(int fd)
00389 {
00390     struct addrinfo hints = {0};
00391     struct addrinfo *ai = NULL;
00392     const char *port = NULL;
00393     const char *proto = NULL;
00394     int res;
00395     int sock;
00396     int ans = AF_UNIX;
00397     int yes = 1;
00398     
00399     proto = getenv("CCNR_PROTO");
00400     if (proto == NULL || strcasecmp(proto, "tcp") != 0)
00401         return(ans);
00402     port = getenv("CCN_LOCAL_PORT");
00403     if (port == NULL || port[0] == 0)
00404         port = "9695";
00405     hints.ai_family = AF_UNSPEC;
00406     hints.ai_socktype = SOCK_STREAM;
00407     hints.ai_flags = 0;
00408     hints.ai_protocol = 0;
00409     res = getaddrinfo(NULL, port, &hints, &ai);
00410     if (res == 0) {
00411         sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
00412         if (sock != -1) {
00413             res = connect(sock, ai->ai_addr, ai->ai_addrlen);
00414             if (res == 0) {
00415                 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
00416                 dup2(sock, fd);
00417                 ans = ai->ai_family;
00418             }
00419             else
00420                 close(sock);
00421         }
00422         freeaddrinfo(ai);
00423     }
00424     return(ans);
00425 }
00426 
00427 PUBLIC struct ccnr_parsed_policy *
00428 ccnr_parsed_policy_create(void)
00429 {
00430     struct ccnr_parsed_policy *pp;
00431     pp = calloc(1, sizeof(struct ccnr_parsed_policy));
00432     pp->store = ccn_charbuf_create();
00433     pp->namespaces = ccn_indexbuf_create();
00434     return(pp);
00435 }
00436 
00437 PUBLIC void
00438 ccnr_parsed_policy_destroy(struct ccnr_parsed_policy **ppp)
00439 {
00440     struct ccnr_parsed_policy *pp;
00441     
00442     if (*ppp == NULL)
00443         return;
00444     pp = *ppp;
00445     ccn_charbuf_destroy(&pp->store);
00446     ccn_indexbuf_destroy(&pp->namespaces);
00447     free(pp);
00448     *ppp = NULL;
00449 }
00450 
00451 /**
00452  * Create a new ccnr instance
00453  * @param progname - name of program binary, used for locating helpers
00454  * @param logger - logger function
00455  * @param loggerdata - data to pass to logger function
00456  */
00457 PUBLIC struct ccnr_handle *
00458 r_init_create(const char *progname, ccnr_logger logger, void *loggerdata)
00459 {
00460     char *sockname = NULL;
00461     const char *portstr = NULL;
00462     const char *listen_on = NULL;
00463     const char *d = NULL;
00464     struct ccnr_handle *h = NULL;
00465     struct hashtb_param param = {0};
00466     struct ccn_charbuf *config = NULL;
00467     int res;
00468     
00469     h = calloc(1, sizeof(*h));
00470     if (h == NULL)
00471         return(h);
00472     h->notify_after = CCNR_MAX_ACCESSION;
00473     h->logger = logger;
00474     h->loggerdata = loggerdata;
00475     h->logpid = (int)getpid();
00476     h->progname = progname;
00477     h->debug = -1;
00478     config = r_init_read_config(h);
00479     if (config == NULL)
00480         goto Bail;
00481     r_init_parse_config(h, config, 0); /* silent pass to pick up CCNR_DEBUG */
00482     h->debug = 1; /* so that we see any complaints */
00483     h->debug = r_init_debug_getenv(h, "CCNR_DEBUG");
00484     res = r_init_parse_config(h, config, 1);
00485     if (res < 0) {
00486         h->running = -1;
00487         goto Bail;
00488     }
00489     r_init_parse_config(h, config, 2);
00490     sockname = r_net_get_local_sockname();
00491     h->skiplinks = ccn_indexbuf_create();
00492     h->face_limit = 10; /* soft limit */
00493     h->fdholder_by_fd = calloc(h->face_limit, sizeof(h->fdholder_by_fd[0]));
00494     param.finalize_data = h;
00495     param.finalize = &r_fwd_finalize_nameprefix;
00496     h->nameprefix_tab = hashtb_create(sizeof(struct nameprefix_entry), &param);
00497     param.finalize = 0; // PRUNED &r_fwd_finalize_propagating;
00498     h->propagating_tab = hashtb_create(sizeof(struct propagating_entry), &param);
00499     param.finalize = 0;
00500     h->enum_state_tab = hashtb_create(sizeof(struct enum_state), NULL); // XXX - do we need finalization? Perhaps
00501     h->min_stale = ~0;
00502     h->max_stale = 0;
00503     h->unsol = ccn_indexbuf_create();
00504     h->ticktock.descr[0] = 'C';
00505     h->ticktock.micros_per_base = 1000000;
00506     h->ticktock.gettime = &r_util_gettime;
00507     h->ticktock.data = h;
00508     h->sched = ccn_schedule_create(h, &h->ticktock);
00509     h->starttime = h->sec;
00510     h->starttime_usec = h->usec;
00511     h->oldformatcontentgrumble = 1;
00512     h->oldformatinterestgrumble = 1;
00513     h->cob_limit = 4201;
00514     h->start_write_scope_limit = r_init_confval(h, "CCNR_START_WRITE_SCOPE_LIMIT", 0, 3, 3);
00515     h->debug = 1; /* so that we see any complaints */
00516     h->debug = r_init_debug_getenv(h, "CCNR_DEBUG");
00517     h->syncdebug = r_init_debug_getenv(h, "CCNS_DEBUG");
00518     portstr = getenv("CCNR_STATUS_PORT");
00519     if (portstr == NULL || portstr[0] == 0 || strlen(portstr) > 10)
00520         portstr = "";
00521     h->portstr = portstr;
00522     ccnr_msg(h, "CCNR_DEBUG=%d CCNR_DIRECTORY=%s CCNR_STATUS_PORT=%s", h->debug, h->directory, h->portstr);
00523     listen_on = getenv("CCNR_LISTEN_ON");
00524     if (listen_on != NULL && listen_on[0] != 0)
00525         ccnr_msg(h, "CCNR_LISTEN_ON=%s", listen_on);
00526     
00527     if (ccnr_init_repo_keystore(h, NULL) < 0) {
00528         h->running = -1;
00529         goto Bail;
00530     }
00531     r_util_reseed(h);
00532     r_store_init(h);
00533     if (h->running == -1) goto Bail;
00534     while (h->active_in_fd >= 0) {
00535         r_dispatch_process_input(h, h->active_in_fd);
00536         r_store_trim(h, h->cob_limit);
00537         ccn_schedule_run(h->sched);
00538     }
00539     ccnr_msg(h, "Repository file is indexed");
00540     if (h->face0 == NULL) {
00541         struct fdholder *fdholder;
00542         fdholder = calloc(1, sizeof(*fdholder));
00543         if (dup2(open("/dev/null", O_RDONLY), 0) == -1)
00544             ccnr_msg(h, "stdin: %s", strerror(errno));
00545         fdholder->filedesc = 0;
00546         fdholder->flags = (CCNR_FACE_GG | CCNR_FACE_NORECV);
00547         r_io_enroll_face(h, fdholder);
00548     }
00549     ccnr_direct_client_start(h);
00550     d = getenv("CCNR_SKIP_VERIFY");
00551 #if (CCN_API_VERSION >= 4004)
00552     if (d != NULL && strcmp(d, "1") == 0) {
00553         ccnr_msg(h, "CCNR_SKIP_VERIFY=%s", d);
00554         ccn_defer_verification(h->direct_client, 1);
00555     }
00556 #endif
00557     if (ccn_connect(h->direct_client, NULL) != -1) {
00558         int af = 0;
00559         int bufsize;
00560         int flags;
00561         int fd;
00562         struct fdholder *fdholder;
00563 
00564         fd = ccn_get_connection_fd(h->direct_client);
00565         // Play a dirty trick here - if this wins, we can fix it right in the c lib later on...
00566         af = try_tcp_instead(fd);  
00567         flags = CCNR_FACE_CCND;
00568         if (af == AF_INET)
00569             flags |= CCNR_FACE_INET;
00570         else if (af == AF_INET6)
00571             flags |= CCNR_FACE_INET6;
00572         else
00573             flags |= CCNR_FACE_LOCAL;
00574         fdholder = r_io_record_fd(h, fd, "CCND", 5, flags);
00575         if (fdholder == NULL) abort();
00576         ccnr_uri_listen(h, h->direct_client, "ccnx:/%C1.M.S.localhost/%C1.M.SRV/repository",
00577                         &ccnr_answer_req, OP_SERVICE);
00578         ccnr_uri_listen(h, h->direct_client, "ccnx:/%C1.M.S.neighborhood/%C1.M.SRV/repository",
00579                         &ccnr_answer_req, OP_SERVICE);
00580         bufsize = r_init_confval(h, "CCNR_MIN_SEND_BUFSIZE", 1, 2097152, 16384);
00581         establish_min_send_bufsize(h, fd, bufsize);
00582     }
00583     else
00584         ccn_disconnect(h->direct_client); // Apparently ccn_connect error case needs work.
00585     h->sync_handle = SyncNewBase(h, h->direct_client, h->sched);
00586     if (-1 == load_policy(h))
00587         goto Bail;
00588     r_net_listen_on(h, listen_on);
00589     ccnr_internal_client_start(h);
00590     r_proto_init(h);
00591     r_proto_activate_policy(h, h->parsed_policy);
00592     if (merge_files(h) == -1)
00593         r_init_fail(h, __LINE__, "Unable to merge additional repository data files.", errno);
00594     if (h->running == -1) goto Bail;
00595     SyncInit(h->sync_handle);
00596 Bail:
00597     if (sockname)
00598         free(sockname);
00599     sockname = NULL;
00600     ccn_charbuf_destroy(&config);
00601     if (h->running == -1)
00602         r_init_destroy(&h);
00603     return(h);
00604 }
00605 
00606 void
00607 r_init_fail(struct ccnr_handle *ccnr, int line, const char *culprit, int err)
00608 {
00609     ccnr_msg(ccnr, "Startup failure %d %s - %s", line, culprit,
00610              (err > 0) ? strerror(err) : "");
00611     ccnr->running = -1;
00612 }
00613 
00614 /**
00615  * Destroy the ccnr instance, releasing all associated resources.
00616  */
00617 PUBLIC void
00618 r_init_destroy(struct ccnr_handle **pccnr)
00619 {
00620     struct ccnr_handle *h = *pccnr;
00621     int stable;
00622     if (h == NULL)
00623         return;
00624     stable = h->active_in_fd == -1 ? 1 : 0;
00625     r_io_shutdown_all(h);
00626     ccnr_direct_client_stop(h);
00627     ccn_schedule_destroy(&h->sched);
00628     hashtb_destroy(&h->propagating_tab);
00629     hashtb_destroy(&h->nameprefix_tab);
00630     hashtb_destroy(&h->content_by_accession_tab);
00631     hashtb_destroy(&h->enum_state_tab);
00632     
00633     SyncFreeBase(&h->sync_handle);
00634     
00635     r_store_final(h, stable);
00636     
00637     if (h->fds != NULL) {
00638         free(h->fds);
00639         h->fds = NULL;
00640         h->nfds = 0;
00641     }
00642     if (h->fdholder_by_fd != NULL) {
00643         free(h->fdholder_by_fd);
00644         h->fdholder_by_fd = NULL;
00645         h->face_limit = h->face_gen = 0;
00646     }
00647     if (h->content_by_cookie != NULL) {
00648         free(h->content_by_cookie);
00649         h->content_by_cookie = NULL;
00650         h->cookie_limit = 1;
00651     }
00652     ccn_charbuf_destroy(&h->scratch_charbuf);
00653     ccn_indexbuf_destroy(&h->skiplinks);
00654     ccn_indexbuf_destroy(&h->scratch_indexbuf);
00655     ccn_indexbuf_destroy(&h->unsol);
00656     if (h->parsed_policy != NULL) {
00657         ccn_indexbuf_destroy(&h->parsed_policy->namespaces);
00658         ccn_charbuf_destroy(&h->parsed_policy->store);
00659         free(h->parsed_policy);
00660         h->parsed_policy = NULL;
00661     }
00662     ccn_charbuf_destroy(&h->policy_link_cob);
00663     free(h);
00664     *pccnr = NULL;
00665 }
00666 
00667 int
00668 r_init_map_and_process_file(struct ccnr_handle *h, struct ccn_charbuf *filename, int add_content)
00669 {
00670     int res = 0;
00671     int dres;
00672     struct stat statbuf;
00673     unsigned char *mapped_file = MAP_FAILED;
00674     unsigned char *msg;
00675     size_t size;
00676     int fd = -1;
00677     struct content_entry *content;
00678     struct ccn_skeleton_decoder *d;
00679     struct fdholder *fdholder;
00680     
00681     fd = r_io_open_repo_data_file(h, ccn_charbuf_as_string(filename), 0);
00682     if (fd == -1)   // Normal exit
00683         return(1);
00684     
00685     res = fstat(fd, &statbuf);
00686     if (res != 0) {
00687         ccnr_msg(h, "stat failed for %s (fd=%d), %s (errno=%d)",
00688                  ccn_charbuf_as_string(filename), fd, strerror(errno), errno);
00689         res = -errno;
00690         goto Bail;
00691     }
00692     if (statbuf.st_size == 0)
00693         goto Bail;
00694     
00695     mapped_file = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
00696     if (mapped_file == MAP_FAILED) {
00697         ccnr_msg(h, "mmap failed for %s (fd=%d), %s (errno=%d)",
00698                  ccn_charbuf_as_string(filename), fd, strerror(errno), errno);
00699         res = -errno;
00700         goto Bail;
00701     }
00702     fdholder = r_io_fdholder_from_fd(h, fd);
00703     d = &fdholder->decoder;
00704     msg = mapped_file;
00705     size = statbuf.st_size;
00706     while (d->index < size) {
00707         dres = ccn_skeleton_decode(d, msg + d->index, size - d->index);
00708         if (!CCN_FINAL_DSTATE(d->state))
00709             break;
00710         if (add_content) {
00711             content = process_incoming_content(h, fdholder, msg + d->index - dres, dres);
00712             if (content != NULL)
00713                 r_store_commit_content(h, content);
00714         }
00715     }
00716     
00717     if (d->index != size || !CCN_FINAL_DSTATE(d->state)) {
00718         ccnr_msg(h, "protocol error on fdholder %u (state %d), discarding %d bytes",
00719                  fdholder->filedesc, d->state, (int)(size - d->index));
00720         res = -1;
00721         goto Bail;
00722     }
00723     
00724 Bail:
00725     if (mapped_file != MAP_FAILED)
00726         munmap(mapped_file, statbuf.st_size);
00727     r_io_shutdown_client_fd(h, fd);
00728     return (res);
00729 }
00730 
00731 static int
00732 merge_files(struct ccnr_handle *h)
00733 {
00734     int i, last_file;
00735     int res;
00736     struct ccn_charbuf *filename = ccn_charbuf_create();
00737     
00738     // first parse the file(s) making sure there are no errors
00739     for (i = 2;; i++) {
00740         filename->length = 0;
00741         ccn_charbuf_putf(filename, "repoFile%d", i);
00742         res = r_init_map_and_process_file(h, filename, 0);
00743         if (res == 1)
00744             break;
00745         if (res < 0) {
00746             ccnr_msg(h, "Error parsing repository file %s", ccn_charbuf_as_string(filename));
00747             return (-1);
00748         }
00749     }
00750     last_file = i - 1;
00751     
00752     for (i = 2; i <= last_file; i++) {
00753         filename->length = 0;
00754         ccn_charbuf_putf(filename, "repoFile%d", i);
00755         res = r_init_map_and_process_file(h, filename, 1);
00756         if (res < 0) {
00757             ccnr_msg(h, "Error in phase 2 incorporating repository file %s", ccn_charbuf_as_string(filename));
00758             return (-1);
00759         }
00760     }
00761     
00762     for (i = last_file; i > 1; --i) {
00763         filename->length = 0;
00764         ccn_charbuf_putf(filename, "%s/repoFile%d", h->directory, i);
00765         if (CCNSHOULDLOG(h, LM_128, CCNL_INFO))
00766             ccnr_msg(h, "unlinking %s", ccn_charbuf_as_string(filename));   
00767         unlink(ccn_charbuf_as_string(filename));
00768     }
00769     ccn_charbuf_destroy(&filename);
00770     return (0);
00771 }
00772 
00773 static struct ccn_charbuf *
00774 ccnr_init_policy_cob(struct ccnr_handle *ccnr, struct ccn *h,
00775                      struct ccn_charbuf *basename,
00776                      int freshness, struct ccn_charbuf *content)
00777 {
00778     struct ccn_signing_params sp = CCN_SIGNING_PARAMS_INIT;
00779     struct ccn_charbuf *name = ccn_charbuf_create();
00780     struct ccn_charbuf *pubid = ccn_charbuf_create();
00781     struct ccn_charbuf *pubkey = ccn_charbuf_create();
00782     struct ccn_charbuf *keyid = ccn_charbuf_create();
00783     struct ccn_charbuf *tcob = ccn_charbuf_create();
00784     struct ccn_charbuf *cob = NULL;          // result
00785     int res;
00786     
00787     res = ccn_get_public_key(h, NULL, pubid, pubkey);
00788     if (res < 0) 
00789         goto Leave;
00790     res = ccn_charbuf_append_charbuf(name, basename);
00791     if (ccn_name_from_uri(name, "%00") < 0)
00792         goto Leave;
00793     sp.sp_flags |= CCN_SP_FINAL_BLOCK;
00794     sp.type = CCN_CONTENT_DATA;
00795     sp.freshness = freshness;
00796     res |= ccn_sign_content(h, tcob, name, &sp, content->buf, content->length);
00797     if (res == 0) {
00798         cob = tcob;
00799         tcob = NULL;
00800     }
00801     
00802 Leave:
00803     ccn_charbuf_destroy(&name);
00804     ccn_charbuf_destroy(&pubid);
00805     ccn_charbuf_destroy(&pubkey);
00806     ccn_charbuf_destroy(&keyid);
00807     ccn_charbuf_destroy(&tcob);
00808     return (cob);
00809 }
00810 /**
00811  * should probably return a new cob, rather than reusing one.
00812  * should publish link as:
00813  *    CCNRID_POLICY_URI("ccnx:/%C1.M.S.localhost/%C1.M.SRV/repository/POLICY)/%C1.M.K<pubid>/<version>/%00
00814  * should have key locator which is the key name of the repository
00815  */
00816 PUBLIC struct ccn_charbuf *
00817 ccnr_init_policy_link_cob(struct ccnr_handle *ccnr, struct ccn *h,
00818                           struct ccn_charbuf *targetname)
00819 {
00820     struct ccn_signing_params sp = CCN_SIGNING_PARAMS_INIT;
00821     struct ccn_charbuf *name = ccn_charbuf_create();
00822     struct ccn_charbuf *pubid = ccn_charbuf_create();
00823     struct ccn_charbuf *pubkey = ccn_charbuf_create();
00824     struct ccn_charbuf *keyid = ccn_charbuf_create();
00825     struct ccn_charbuf *content = ccn_charbuf_create();
00826     struct ccn_charbuf *cob = ccn_charbuf_create();   // result
00827     int res;
00828     
00829     res = ccn_get_public_key(h, NULL, pubid, pubkey);
00830     if (res < 0)
00831         goto Bail;
00832     if (ccn_name_from_uri(name, CCNRID_POLICY_URI) < 0)
00833         goto Bail;
00834     res |= ccn_charbuf_append_value(keyid, CCN_MARKER_CONTROL, 1);
00835     res |= ccn_charbuf_append_string(keyid, ".M.K");
00836     res |= ccn_charbuf_append_value(keyid, 0, 1);
00837     res |= ccn_charbuf_append_charbuf(keyid, pubid);
00838     res |= ccn_name_append(name, keyid->buf, keyid->length);
00839     res |= ccn_create_version(h, name, CCN_V_NOW, 0, 0);
00840     if (ccn_name_from_uri(name, "%00") < 0)
00841         goto Bail;
00842     sp.sp_flags |= CCN_SP_FINAL_BLOCK;
00843     sp.type = CCN_CONTENT_LINK;
00844     res |= ccnb_append_Link(content, targetname, "Repository Policy", NULL);
00845     if (res != 0)
00846         goto Bail;
00847     res |= ccn_sign_content(h, cob, name, &sp, content->buf, content->length);
00848     if (res != 0)
00849         goto Bail;
00850     return (cob);
00851     
00852 Bail:
00853     ccn_charbuf_destroy(&name);
00854     ccn_charbuf_destroy(&pubid);
00855     ccn_charbuf_destroy(&pubkey);
00856     ccn_charbuf_destroy(&keyid);
00857     ccn_charbuf_destroy(&content);
00858     return (NULL);
00859 }
00860 
00861 
00862 /**
00863  * Load a link to the repo policy from the repoPolicy file and load the link
00864  * target to extract the actual policy.
00865  * If a policy file does not exist a new one is created, with a link to a policy
00866  * based either on the environment variable CCNR_GLOBAL_PREFIX or the system
00867  * default value of ccnx:/parc.com/csl/ccn/Repos, plus the system defaults for
00868  * other fields.
00869  * This routine must be called after the btree code is initialized and capable
00870  * of returning content objects.
00871  * Sets the parsed_policy field of the handle to be the new policy.
00872  */
00873 static int
00874 load_policy(struct ccnr_handle *ccnr)
00875 {
00876     int fd;
00877     ssize_t res;
00878     struct content_entry *content = NULL;
00879     const unsigned char *content_msg = NULL;
00880     struct ccn_parsed_ContentObject pco = {0};
00881     struct ccn_parsed_Link pl = {0};
00882     struct ccn_indexbuf *nc = NULL;
00883     struct ccn_charbuf *basename = NULL;
00884     struct ccn_charbuf *policy = NULL;
00885     struct ccn_charbuf *policy_cob = NULL;
00886     struct ccn_charbuf *policyFileName;
00887     const char *global_prefix;
00888     const unsigned char *buf = NULL;
00889     size_t length = 0;
00890     int segment = 0;
00891     int final = 0;
00892     struct ccn_buf_decoder decoder;
00893     struct ccn_buf_decoder *d;
00894     
00895     policyFileName = ccn_charbuf_create();
00896     ccn_charbuf_putf(policyFileName, "%s/repoPolicy", ccnr->directory);
00897     ccnr->parsed_policy = ccnr_parsed_policy_create();
00898     fd = open(ccn_charbuf_as_string(policyFileName), O_RDONLY);
00899     if (fd >= 0) {
00900         ccnr->policy_link_cob = ccn_charbuf_create();
00901         ccn_charbuf_reserve(ccnr->policy_link_cob, 4096);   // limits the size of the policy link
00902         ccnr->policy_link_cob->length = 0;    // clear the buffer
00903         res = read(fd, ccnr->policy_link_cob->buf, ccnr->policy_link_cob->limit - ccnr->policy_link_cob->length);
00904         close(fd);
00905         if (res == -1) {
00906             r_init_fail(ccnr, __LINE__, "Error reading repoPolicy file.", errno);
00907             ccn_charbuf_destroy(&ccnr->policy_link_cob);
00908             ccn_charbuf_destroy(&policyFileName);
00909             return(-1);
00910         }
00911         ccnr->policy_link_cob->length = res;
00912         nc = ccn_indexbuf_create();
00913         res = ccn_parse_ContentObject(ccnr->policy_link_cob->buf,
00914                                       ccnr->policy_link_cob->length, &pco, nc);
00915         res = ccn_ref_tagged_BLOB(CCN_DTAG_Content, ccnr->policy_link_cob->buf,
00916                                   pco.offset[CCN_PCO_B_Content],
00917                                   pco.offset[CCN_PCO_E_Content],
00918                                   &buf, &length);
00919         d = ccn_buf_decoder_start(&decoder, buf, length);
00920         res = ccn_parse_Link(d, &pl, NULL);
00921         if (res <= 0) {
00922             ccnr_msg(ccnr, "Policy link is malformed.");
00923             goto CreateNewPolicy;
00924         }
00925         basename = ccn_charbuf_create();
00926         ccn_charbuf_append(basename, buf + pl.offset[CCN_PL_B_Name],
00927                            pl.offset[CCN_PL_E_Name] - pl.offset[CCN_PL_B_Name]);
00928         ccnr->policy_name = ccn_charbuf_create(); // to detect writes to this name
00929         ccn_charbuf_append_charbuf(ccnr->policy_name, basename); // has version
00930         ccn_name_chop(ccnr->policy_name, NULL, -1); // get rid of version
00931         policy = ccn_charbuf_create();
00932         // if we fail to retrieve the link target, report and then create a new one
00933         do {
00934             ccn_name_append_numeric(basename, CCN_MARKER_SEQNUM, segment++);
00935             content = r_store_lookup_ccnb(ccnr, basename->buf, basename->length);
00936             if (content == NULL) {
00937                 ccnr_debug_ccnb(ccnr, __LINE__, "policy lookup failed for", NULL,
00938                                 basename->buf, basename->length);
00939                 break;
00940             }
00941             ccn_name_chop(basename, NULL, -1);
00942             content_msg = r_store_content_base(ccnr, content);
00943             res = ccn_parse_ContentObject(content_msg, r_store_content_size(ccnr, content), &pco, nc);
00944             res = ccn_ref_tagged_BLOB(CCN_DTAG_Content, content_msg,
00945                                       pco.offset[CCN_PCO_B_Content],
00946                                       pco.offset[CCN_PCO_E_Content],
00947                                       &buf, &length);
00948             ccn_charbuf_append(policy, buf, length);
00949             final = r_util_is_final_pco(content_msg, &pco, nc);
00950         } while (!final && segment < 100);
00951         if (policy->length == 0) {
00952             ccnr_msg(ccnr, "Policy link points to empty or non-existent policy.");
00953             goto CreateNewPolicy;
00954         }
00955         if (segment >= 100) {
00956             r_init_fail(ccnr, __LINE__, "Policy link points to policy with too many segments.", 0);
00957             return(-1);
00958         }
00959         if (r_proto_parse_policy(ccnr, policy->buf, policy->length, ccnr->parsed_policy) < 0) {
00960             ccnr_msg(ccnr, "Policy link points to malformed policy.");
00961             goto CreateNewPolicy;
00962         }
00963         res = ccn_name_comp_get(content_msg, nc, nc->n - 3, &buf, &length);
00964         if (length != 7 || buf[0] != CCN_MARKER_VERSION) {
00965             ccnr_msg(ccnr, "Policy link points to unversioned policy.");
00966             goto CreateNewPolicy;
00967         }
00968         memmove(ccnr->parsed_policy->version, buf, sizeof(ccnr->parsed_policy->version));
00969         ccn_indexbuf_destroy(&nc);
00970         ccn_charbuf_destroy(&basename);
00971         ccn_charbuf_destroy(&policy);
00972         ccn_charbuf_destroy(&policyFileName);
00973         return (0);
00974     }
00975     
00976 CreateNewPolicy:
00977     ccnr_msg(ccnr, "Creating new policy file.");
00978     // construct the policy content object
00979     global_prefix = getenv ("CCNR_GLOBAL_PREFIX");
00980     if (global_prefix != NULL)
00981         ccnr_msg(ccnr, "CCNR_GLOBAL_PREFIX=%s", global_prefix);
00982     else 
00983         global_prefix = "ccnx:/parc.com/csl/ccn/Repos";
00984     policy = ccn_charbuf_create();
00985     r_proto_policy_append_basic(ccnr, policy, "1.5", "Repository", global_prefix);
00986     r_proto_policy_append_namespace(ccnr, policy, "/");
00987     basename = ccn_charbuf_create();
00988     res = ccn_name_from_uri(basename, global_prefix);
00989     res |= ccn_name_from_uri(basename, "data/policy.xml");
00990     if (res < 0) {
00991         r_init_fail(ccnr, __LINE__, "Global prefix is not a valid URI", 0);
00992         return(-1);
00993     }
00994     ccnr->policy_name = ccn_charbuf_create(); // to detect writes to this name
00995     ccn_charbuf_append_charbuf(ccnr->policy_name, basename);
00996     ccn_create_version(ccnr->direct_client, basename, 0,
00997                        ccnr->starttime, ccnr->starttime_usec * 1000);
00998     policy_cob = ccnr_init_policy_cob(ccnr, ccnr->direct_client, basename,
00999                                       600, policy);
01000     // save the policy content object to the repository
01001     r_sync_local_store(ccnr, policy_cob);
01002     ccn_charbuf_destroy(&policy_cob);
01003     // make a link to the policy content object
01004     ccnr->policy_link_cob = ccnr_init_policy_link_cob(ccnr, ccnr->direct_client,
01005                                                       basename);
01006     if (ccnr->policy_link_cob == NULL) {
01007         r_init_fail(ccnr, __LINE__, "Unable to create policy link object", 0);
01008         return(-1);
01009     }
01010     
01011     fd = open(ccn_charbuf_as_string(policyFileName), O_WRONLY | O_CREAT, 0666);
01012     if (fd < 0) {
01013         r_init_fail(ccnr, __LINE__, "Unable to open repoPolicy file for write", errno);
01014         return(-1);
01015     }
01016     lseek(fd, 0, SEEK_SET);
01017     res = write(fd, ccnr->policy_link_cob->buf, ccnr->policy_link_cob->length);
01018     if (res == -1) {
01019         r_init_fail(ccnr, __LINE__, "Unable to write repoPolicy file", errno);
01020         return(-1);
01021     }
01022     ftruncate(fd, ccnr->policy_link_cob->length);
01023     close(fd);
01024     // parse the policy for later use
01025     if (r_proto_parse_policy(ccnr, policy->buf, policy->length, ccnr->parsed_policy) < 0) {
01026         r_init_fail(ccnr, __LINE__, "Unable to parse new repoPolicy file", 0);
01027         return(-1);
01028     }
01029     // get the pp->version from the policy_cob base name .../policy.xml/<ver>
01030     nc = ccn_indexbuf_create();
01031     ccn_name_split(basename, nc);
01032     res = ccn_name_comp_get(basename->buf, nc, nc->n - 2, &buf, &length);
01033     if (length != 7 || buf[0] != CCN_MARKER_VERSION) {
01034         r_init_fail(ccnr, __LINE__, "Unable to get repository policy object version", 0);
01035         return(-1);
01036     }
01037     memmove(ccnr->parsed_policy->version, buf, sizeof(ccnr->parsed_policy->version));
01038     ccn_charbuf_destroy(&basename);
01039     ccn_charbuf_destroy(&policy);
01040     ccn_charbuf_destroy(&policyFileName);
01041     return(0);
01042 }
01043 

Generated on Thu Feb 16 00:43:58 2012 for Content-Centric Networking in C by  doxygen 1.5.6