ccnr_sync.c

Go to the documentation of this file.
00001 /**
00002  * @file ccnr_sync.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 #include <stddef.h>
00024 #include <stdint.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 #include <time.h>
00028 #include <unistd.h>
00029 #include <sys/types.h>
00030 
00031 #include <ccn/btree.h>
00032 #include <ccn/btree_content.h>
00033 #include <ccn/ccn.h>
00034 #include <ccn/charbuf.h>
00035 #include <ccn/indexbuf.h>
00036 #include <ccn/schedule.h>
00037 
00038 #include <sync/SyncBase.h>
00039 
00040 #include "ccnr_private.h"
00041 
00042 #include "ccnr_dispatch.h"
00043 #include "ccnr_io.h"
00044 #include "ccnr_link.h"
00045 #include "ccnr_msg.h"
00046 #include "ccnr_proto.h"
00047 #include "ccnr_store.h"
00048 #include "ccnr_sync.h"
00049 #include "ccnr_util.h"
00050 
00051 #ifndef CCNLINT
00052 
00053 /* Preliminary implementation - algorithm may change */
00054 
00055 PUBLIC uintmax_t
00056 ccnr_accession_encode(struct ccnr_handle *ccnr, ccnr_accession a)
00057 {
00058     return(a);
00059 }
00060 
00061 PUBLIC ccnr_accession
00062 ccnr_accession_decode(struct ccnr_handle *ccnr, uintmax_t encoded)
00063 {
00064     return(encoded);
00065 }
00066 
00067 PUBLIC int
00068 ccnr_accession_compare(struct ccnr_handle *ccnr, ccnr_accession x, ccnr_accession y)
00069 {
00070     if (x > y) return 1;
00071     if (x == y) return 0;
00072     if (x < y) return -1;
00073     return CCNR_NOT_COMPARABLE;
00074 }
00075 
00076 PUBLIC uintmax_t
00077 ccnr_hwm_encode(struct ccnr_handle *ccnr, ccnr_hwm hwm)
00078 {
00079     return(hwm);
00080 }
00081 
00082 PUBLIC ccnr_hwm
00083 ccnr_hwm_decode(struct ccnr_handle *ccnr, uintmax_t encoded)
00084 {
00085     return(encoded);
00086 }
00087 
00088 PUBLIC int
00089 ccnr_acc_in_hwm(struct ccnr_handle *ccnr, ccnr_accession a, ccnr_hwm hwm)
00090 {
00091     return(a <= hwm);
00092 }
00093 
00094 PUBLIC ccnr_hwm
00095 ccnr_hwm_update(struct ccnr_handle *ccnr, ccnr_hwm hwm, ccnr_accession a)
00096 {
00097     return(a <= hwm ? hwm : a);
00098 }
00099 
00100 PUBLIC ccnr_hwm
00101 ccnr_hwm_merge(struct ccnr_handle *ccnr, ccnr_hwm x, ccnr_hwm y)
00102 {
00103     return(x < y ? y : x);
00104 }
00105 
00106 PUBLIC int
00107 ccnr_hwm_compare(struct ccnr_handle *ccnr, ccnr_hwm x, ccnr_hwm y)
00108 {
00109     if (x > y) return 1;
00110     if (x == y) return 0;
00111     if (x < y) return -1;
00112     return CCNR_NOT_COMPARABLE;
00113 }
00114 #endif
00115 
00116 PUBLIC void
00117 r_sync_notify_after(struct ccnr_handle *ccnr, ccnr_hwm item)
00118 {
00119     /* XXX - if ccnr_hwm becomes multi-dimensional then this code has to become
00120      * more sophisticated about restarting the enumeration
00121      */
00122     ccnr->notify_after = (ccnr_accession) item;
00123 }
00124 
00125 /**
00126  * A wrapper for SyncNotifyContent that takes a content entry.
00127  */
00128 PUBLIC int
00129 r_sync_notify_content(struct ccnr_handle *ccnr, int e, struct content_entry *content)
00130 {
00131     int res;
00132     ccnr_accession acc = CCNR_NULL_ACCESSION;
00133     
00134     if (content == NULL) {
00135         res = SyncNotifyContent(ccnr->sync_handle, e, CCNR_NULL_ACCESSION, NULL);
00136         if (res != -1)
00137             ccnr_msg(ccnr, "SyncNotifyContent returned %d, expected -1",
00138                      e, res);
00139     }
00140     else {
00141         struct ccn_charbuf *cb = r_util_charbuf_obtain(ccnr);
00142 
00143         acc = r_store_content_accession(ccnr, content);
00144         if (acc == CCNR_NULL_ACCESSION) {
00145             ccnr_debug_content(ccnr, __LINE__, "r_sync_notify_content - not yet stable", NULL, content);
00146             return(0);
00147         }
00148         /* This must get the full name, including digest. */
00149         ccn_name_init(cb);
00150         res = r_store_name_append_components(cb, ccnr, content, 0, -1);
00151         if (res < 0) abort();
00152         if (CCNSHOULDLOG(ccnr, r_sync_notify_content, CCNL_FINEST))
00153             ccnr_debug_content(ccnr, __LINE__, "r_sync_notify_content", NULL, content);
00154         res = SyncNotifyContent(ccnr->sync_handle, e, acc, cb);
00155         r_util_charbuf_release(ccnr, cb);
00156     }
00157     if (CCNSHOULDLOG(ccnr, r_sync_notify_content, CCNL_FINEST))
00158         ccnr_msg(ccnr, "SyncNotifyContent(..., %d, 0x%jx, ...) returned %d",
00159                  e, ccnr_accession_encode(ccnr, acc), res);
00160     if (e == 0 && res == -1)
00161         r_sync_notify_after(ccnr, CCNR_MAX_ACCESSION); // XXXXXX should be hwm
00162     return(res);
00163 }
00164 
00165 /**
00166  *  State for an ongoing sync enumeration.
00167  */
00168 struct sync_enumeration_state {
00169     int magic; /**< for sanity check - should be se_cookie */
00170     int index; /**< Index into ccnr->active_enum */
00171     ccnr_cookie cookie; /**< Resumption point */
00172     struct ccn_parsed_interest parsed_interest;
00173     struct ccn_charbuf *interest;
00174     struct ccn_indexbuf *comps;
00175 };
00176 static const int se_cookie = __LINE__;
00177 
00178 static struct sync_enumeration_state *
00179 cleanup_se(struct ccnr_handle *ccnr, struct sync_enumeration_state *md)
00180 {
00181     if (md != NULL && md->magic == se_cookie) {
00182         int i = md->index;
00183         if (CCNSHOULDLOG(ccnr, cleanup_se, CCNL_FINEST))
00184             ccnr_msg(ccnr, "sync_enum_cleanup %d", i);
00185         if (0 < i && i < CCNR_MAX_ENUM)
00186             ccnr->active_enum[i] = CCNR_NULL_ACCESSION;
00187         ccn_indexbuf_destroy(&md->comps);
00188         ccn_charbuf_destroy(&md->interest);
00189         free(md);
00190     }
00191     return(NULL);
00192 }
00193 
00194 static int
00195 r_sync_enumerate_action(struct ccn_schedule *sched,
00196     void *clienth,
00197     struct ccn_scheduled_event *ev,
00198     int flags)
00199 {
00200     struct ccnr_handle *ccnr = clienth;
00201     struct sync_enumeration_state *md = NULL;
00202     struct content_entry *content = NULL;
00203     struct ccn_btree_node *leaf = NULL;
00204     struct ccn_charbuf *interest = NULL;
00205     struct ccn_indexbuf *comps = NULL;
00206     struct ccn_parsed_interest *pi = NULL;
00207     struct ccn_charbuf *scratch = NULL;
00208     struct ccn_charbuf *flat = NULL;
00209     int ndx;
00210     int res;
00211     int try;
00212     int matches;
00213     
00214     md = ev->evdata;
00215     if (md->magic != se_cookie || md->index >= CCNR_MAX_ENUM) abort();
00216     if ((flags & CCN_SCHEDULE_CANCEL) != 0) {
00217         ev->evdata = cleanup_se(ccnr, md);
00218         return(0);
00219     }
00220     pi = &md->parsed_interest;
00221     interest = md->interest;
00222     comps = md->comps;
00223     /*
00224      * Recover starting point from either cookie or accession.
00225      *
00226      * The accession number might not be available yet (but we try to avoid
00227      * suspending in such a case).
00228      * The cookie might go away, but only if the content has been accessioned.
00229      */
00230     content = r_store_content_from_cookie(ccnr, md->cookie);
00231     if (content == NULL && md->cookie != 0)
00232         content = r_store_content_from_accession(ccnr, ccnr->active_enum[md->index]);
00233     for (try = 0, matches = 0; content != NULL; try++) {
00234         if (scratch == NULL)
00235             scratch = ccn_charbuf_create();
00236         flat = r_store_content_flatname(ccnr, content);
00237         res = ccn_btree_lookup(ccnr->btree, flat->buf, flat->length, &leaf);
00238         if (CCN_BT_SRCH_FOUND(res) == 0) {
00239             ccnr_debug_content(ccnr, __LINE__, "impossible", NULL, content);
00240             break;
00241         }
00242         ndx = CCN_BT_SRCH_INDEX(res);
00243         res = ccn_btree_match_interest(leaf, ndx, interest->buf, pi, scratch);
00244         if (res == -1) {
00245             ccnr_debug_content(ccnr, __LINE__, "impossible", NULL, content);
00246             break;
00247         }
00248         if (res == 1) {
00249             res = r_sync_notify_content(ccnr, md->index, content);
00250             matches++;
00251             if (res == -1) {
00252                 if (CCNSHOULDLOG(ccnr, r_sync_enumerate_action, CCNL_FINEST))
00253                     ccnr_debug_content(ccnr, __LINE__, "r_sync_enumerate_action", NULL,
00254                                        content);
00255                 ev->evdata = cleanup_se(ccnr, md);
00256                 ccn_charbuf_destroy(&scratch);
00257                 return(0);
00258             }
00259         }
00260         content = r_store_content_next(ccnr, content);
00261         if (content != NULL &&
00262             !r_store_content_matches_interest_prefix(ccnr, content,
00263                                                      interest->buf,
00264                                                      interest->length))
00265             content = NULL;
00266         if (content != NULL) {
00267             md->cookie = r_store_content_cookie(ccnr, content);
00268             ccnr->active_enum[md->index] = r_store_content_accession(ccnr, content);
00269             if (ccnr->active_enum[md->index] != CCNR_NULL_ACCESSION && 
00270                 (matches >= 8 || try >= 200)) { // XXX - these numbers need tuning
00271                 ccn_charbuf_destroy(&scratch);
00272                 return(300);
00273             }
00274         }
00275     }
00276     r_sync_notify_content(ccnr, md->index, NULL);
00277     ev->evdata = cleanup_se(ccnr, md);
00278     ccn_charbuf_destroy(&scratch);
00279     return(0);
00280 }
00281 
00282 /**
00283  * Request that a SyncNotifyContent call be made for each content object
00284  *  in the repository that matches the interest.
00285  *
00286  * If SyncNotifyContent returns -1 the active enumeration will be cancelled.
00287  *
00288  * When there are no more matching objects, SyncNotifyContent will be called
00289  *  passing NULL for name.
00290  *
00291  * Content objects that arrive during an enumeration may or may not be included
00292  *  in that enumeration.
00293  *
00294  *  @returns -1 for error, or an enumeration number which will also be passed
00295  *      in the SyncNotifyContent
00296  */
00297 PUBLIC int
00298 r_sync_enumerate(struct ccnr_handle *ccnr,
00299                  struct ccn_charbuf *interest)
00300 {
00301     int ans = -1;
00302     int i;
00303     int res;
00304     struct ccn_indexbuf *comps = NULL;
00305     struct ccn_parsed_interest parsed_interest = {0};
00306     struct ccn_parsed_interest *pi = &parsed_interest;
00307     struct content_entry *content = NULL;
00308     struct sync_enumeration_state *md = NULL;
00309     
00310     if (CCNSHOULDLOG(ccnr, r_sync_enumerate, CCNL_FINEST))
00311         ccnr_debug_ccnb(ccnr, __LINE__, "sync_enum_start", NULL,
00312                         interest->buf, interest->length);
00313     comps = ccn_indexbuf_create();
00314     res = ccn_parse_interest(interest->buf, interest->length, pi, comps);
00315     if (res < 0) {
00316         ccnr_debug_ccnb(ccnr, __LINE__, "bogus r_sync_enumerate request", NULL,
00317                         interest->buf, interest->length);
00318         if (CCNSHOULDLOG(ccnr, r_sync_enumerate, CCNL_FINEST)) {
00319             struct ccn_charbuf *temp = ccn_charbuf_create();
00320             ccn_charbuf_putf(temp, "interest_dump ");
00321             for (i = 0; i < interest->length; i++)
00322                 ccn_charbuf_putf(temp, "%02X", interest->buf[i]);
00323             ccnr_msg(ccnr, ccn_charbuf_as_string(temp));
00324             ccn_charbuf_destroy(&temp);
00325         }
00326         goto Bail;
00327     }
00328     /* 0 is for notify_after - don't allocate it here. */
00329     for (i = 1; i < CCNR_MAX_ENUM; i++) {
00330         if (ccnr->active_enum[i] == CCNR_NULL_ACCESSION) {
00331             ans = i;
00332             ccnr->active_enum[ans] = CCNR_MAX_ACCESSION; /* for no-match case */
00333             break;
00334         }
00335     }
00336     if (ans < 0) {
00337         if (CCNSHOULDLOG(ccnr, r_sync_enumerate, CCNL_WARNING))
00338             ccnr_msg(ccnr, "sync_enum - Too many active enumerations!", ans);
00339         goto Bail;
00340     }
00341     content = r_store_find_first_match_candidate(ccnr, interest->buf, pi);
00342     if (content == NULL) {
00343         if (CCNSHOULDLOG(ccnr, r_sync_enumerate, CCNL_FINE))
00344             ccnr_debug_ccnb(ccnr, __LINE__, "sync_enum_nomatch", NULL,
00345                         interest->buf, interest->length);
00346     }
00347     else if (r_store_content_matches_interest_prefix(ccnr,
00348            content, interest->buf, interest->length)) {
00349         ccnr->active_enum[ans] = r_store_content_accession(ccnr, content);
00350         if (CCNSHOULDLOG(ccnr, r_sync_enumerate, CCNL_FINEST))
00351             ccnr_msg(ccnr, "sync_enum id=%d starting accession=0x%jx",
00352                      ans, ccnr_accession_encode(ccnr, ccnr->active_enum[ans]));
00353     }
00354     
00355     /* Set up the state for r_sync_enumerate_action */
00356     md = calloc(1, sizeof(*md));
00357     if (md == NULL) { ccnr->active_enum[ans] = CCNR_NULL_ACCESSION; ans = -1; goto Bail; }
00358     md->magic = se_cookie;
00359     md->cookie = content == NULL ? 0 : r_store_content_cookie(ccnr, content);
00360     md->index = ans;
00361     md->interest = ccn_charbuf_create();
00362     if (md->interest == NULL) goto Bail;
00363     ccn_charbuf_append(md->interest, interest->buf, interest->length);
00364     md->parsed_interest = parsed_interest;
00365     md->comps = comps;
00366     comps = NULL;
00367 
00368     /* All the upcalls happen in r_sync_enumerate_action. */
00369     
00370     if (NULL != ccn_schedule_event(ccnr->sched, 123, r_sync_enumerate_action, md, 0))
00371         md = NULL;
00372     
00373 Bail:
00374     if (md != NULL) {
00375         ans = -1;
00376         md = cleanup_se(ccnr, md);
00377     }
00378     ccn_indexbuf_destroy(&comps);
00379     if (CCNSHOULDLOG(ccnr, r_sync_enumerate, CCNL_FINEST))
00380         ccnr_msg(ccnr, "sync_enum %d", ans);
00381     return(ans);
00382 }
00383 
00384 PUBLIC int
00385 r_sync_lookup(struct ccnr_handle *ccnr,
00386               struct ccn_charbuf *interest,
00387               struct ccn_charbuf *content_ccnb)
00388 {
00389     int ans = -1;
00390     struct ccn_indexbuf *comps = r_util_indexbuf_obtain(ccnr);
00391     struct ccn_parsed_interest parsed_interest = {0};
00392     struct ccn_parsed_interest *pi = &parsed_interest;
00393     struct content_entry *content = NULL;
00394 
00395     if (NULL == comps || (ccn_parse_interest(interest->buf, interest->length, pi, comps) < 0))
00396         abort();
00397     content = r_store_lookup(ccnr, interest->buf, pi, comps);
00398     if (content != NULL) {
00399         ans = 0;
00400         if (content_ccnb != NULL) {
00401             ccn_charbuf_append(content_ccnb,
00402                                r_store_content_base(ccnr, content),
00403                                r_store_content_size(ccnr, content));
00404         }
00405     }
00406     r_util_indexbuf_release(ccnr, comps);
00407 
00408     return(ans);
00409 }
00410 
00411 /**
00412  * Called when a content object is received by sync and needs to be
00413  * committed to stable storage by the repo.
00414  */
00415 PUBLIC enum ccn_upcall_res
00416 r_sync_upcall_store(struct ccnr_handle *ccnr,
00417                     enum ccn_upcall_kind kind,
00418                     struct ccn_upcall_info *info)
00419 {
00420     enum ccn_upcall_res ans = CCN_UPCALL_RESULT_OK;
00421     const unsigned char *ccnb = NULL;
00422     size_t ccnb_size = 0;
00423     struct content_entry *content;
00424     
00425     if (kind != CCN_UPCALL_CONTENT)
00426         return(CCN_UPCALL_RESULT_ERR);
00427     
00428     ccnb = info->content_ccnb;
00429     ccnb_size = info->pco->offset[CCN_PCO_E];
00430     
00431     content = process_incoming_content(ccnr, r_io_fdholder_from_fd(ccnr, ccn_get_connection_fd(info->h)),
00432                                        (void *)ccnb, ccnb_size);
00433     if (content == NULL) {
00434         ccnr_msg(ccnr, "r_sync_upcall_store: failed to process incoming content");
00435         return(CCN_UPCALL_RESULT_ERR);
00436     }
00437     // XXX - here we need to check if this is something we *should* be storing, according to our policy
00438     if ((r_store_content_flags(content) & CCN_CONTENT_ENTRY_STABLE) == 0) {
00439         r_store_commit_content(ccnr, content);
00440         if (CCNSHOULDLOG(ccnr, r_sync_upcall_store, CCNL_FINE))
00441             ccnr_debug_content(ccnr, __LINE__, "content_stored",
00442                                r_io_fdholder_from_fd(ccnr, ccnr->active_out_fd),
00443                                content);
00444     }        
00445     r_proto_initiate_key_fetch(ccnr, ccnb, info->pco, 0,
00446                                r_store_content_cookie(ccnr, content));
00447 
00448     return(ans);
00449 }
00450 
00451 /**
00452  * Called when a content object has been constructed locally by sync
00453  * and needs to be committed to stable storage by the repo.
00454  * returns 0 for success, -1 for error.
00455  */
00456 
00457 PUBLIC int
00458 r_sync_local_store(struct ccnr_handle *ccnr,
00459                    struct ccn_charbuf *content_cb)
00460 {
00461     struct content_entry *content = NULL;
00462     
00463     // pretend it came from the internal client, for statistics gathering purposes
00464     content = process_incoming_content(ccnr, ccnr->face0,
00465                                        (void *)content_cb->buf, content_cb->length);
00466     if (content == NULL) {
00467         ccnr_msg(ccnr, "r_sync_local_store: failed to process content");
00468         return(-1);
00469     }
00470     // XXX - we assume we must store things from sync independent of policy
00471     // XXX - sync may want notification, or not, at least for now.
00472     if ((r_store_content_flags(content) & CCN_CONTENT_ENTRY_STABLE) == 0) {
00473         r_store_commit_content(ccnr, content);
00474         if (CCNSHOULDLOG(ccnr, r_sync_local_store, CCNL_FINE))
00475             ccnr_debug_content(ccnr, __LINE__, "content_stored_local",
00476                                r_io_fdholder_from_fd(ccnr, ccnr->active_out_fd),
00477                                content);
00478     }
00479     return(0);
00480 }

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