apps/dtnrecv/dtnrecv.c
changeset 0 2b3e5ec03512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnrecv/dtnrecv.c	Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,583 @@
+/*
+ *    Copyright 2004-2006 Intel Corporation
+ * 
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "dtn_api.h"
+
+#define BUFSIZE 16
+#define BLOCKSIZE 8192
+#define COUNTER_MAX_DIGITS 9
+
+// Find the maximum commandline length
+#ifdef __FreeBSD__
+/* Needed for PATH_MAX, Linux doesn't need it */
+#include <sys/syslimits.h>
+#endif
+
+#ifndef PATH_MAX
+/* A conservative fallback */
+#define PATH_MAX 1024
+#endif
+
+const char *progname;
+
+int   verbose           = 0;    	// verbose output
+int   quiet             = 0;    	// quiet output
+char* endpoint		= NULL; 	// endpoint for registration
+dtn_reg_id_t regid	= DTN_REGID_NONE;// registration id
+int   expiration	= 30; 		// registration expiration time
+int   count             = 0;            // exit after count bundles received
+int   failure_action	= DTN_REG_DEFER;// registration delivery failure action
+char* failure_script	= "";	 	// script to exec
+int   register_only	= 0;    	// register and quit
+int   change		= 0;    	// change existing registration 
+int   unregister	= 0;    	// remove existing registration 
+int   recv_timeout	= -1;    	// timeout to dtn_recv call
+int   no_find_reg	= 0;    	// omit call to dtn_find_registration
+char filename[PATH_MAX];		// Destination filename for file xfers
+dtn_bundle_payload_location_t bundletype = DTN_PAYLOAD_MEM;
+
+void
+usage()
+{
+    fprintf(stderr, "usage: %s [opts] <endpoint> \n", progname);
+    fprintf(stderr, "options:\n");
+    fprintf(stderr, " -v verbose\n");
+    fprintf(stderr, " -q quiet\n");
+    fprintf(stderr, " -h help\n");
+    fprintf(stderr, " -d <eid|demux_string> endpoint id\n");
+    fprintf(stderr, " -r <regid> use existing registration regid\n");
+    fprintf(stderr, " -n <count> exit after count bundles received\n");
+    fprintf(stderr, " -e <time> registration expiration time in seconds "
+            "(default: one hour)\n");
+    fprintf(stderr, " -f <defer|drop|exec> failure action\n");
+    fprintf(stderr, " -F <script> failure script for exec action\n");
+    fprintf(stderr, " -x call dtn_register and immediately exit\n");
+    fprintf(stderr, " -c call dtn_change_registration and immediately exit\n");
+    fprintf(stderr, " -u call dtn_unregister and immediately exit\n");
+    fprintf(stderr, " -N don't try to find an existing registration\n");
+    fprintf(stderr, " -t <timeout> timeout value for call to dtn_recv\n");
+    fprintf(stderr, " -o <template> Write out transfers to files using this template (# chars are\n"
+            "replaced with a counter). Example: f##.bin goes to f00.bin, f01.bin, etc...\n");
+}
+
+void
+parse_options(int argc, char**argv)
+{
+    int c, done = 0;
+
+    progname = argv[0];
+
+    memset(filename, 0, sizeof(char) * PATH_MAX);
+
+    while (!done)
+    {
+        c = getopt(argc, argv, "vqhHd:r:e:f:F:xn:cuNt:o:");
+        switch (c)
+        {
+        case 'v':
+            verbose = 1;
+            break;
+        case 'q':
+            quiet = 1;
+            break;
+        case 'h':
+        case 'H':
+            usage();
+            exit(0);
+            return;
+        case 'r':
+            regid = atoi(optarg);
+            break;
+        case 'e':
+            expiration = atoi(optarg);
+            break;
+        case 'f':
+            if (!strcasecmp(optarg, "defer")) {
+                failure_action = DTN_REG_DEFER;
+
+            } else if (!strcasecmp(optarg, "drop")) {
+                failure_action = DTN_REG_DROP;
+
+            } else if (!strcasecmp(optarg, "exec")) {
+                failure_action = DTN_REG_EXEC;
+
+            } else {
+                fprintf(stderr, "invalid failure action '%s'\n", optarg);
+                usage();
+                exit(1);
+            }
+        case 'F':
+            failure_script = optarg;
+            break;
+        case 'x':
+            register_only = 1;
+            break;
+        case 'n':
+            count = atoi(optarg);
+            break;
+        case 'c':
+            change = 1;
+            break;
+        case 'u':
+            unregister = 1;
+            break;
+        case 'N':
+            no_find_reg = 1;
+            break;
+        case 't':
+            recv_timeout = atoi(optarg);
+            break;
+        case 'o':
+            strncpy(filename, optarg, PATH_MAX);
+            break;
+        case -1:
+            done = 1;
+            break;
+        default:
+            // getopt already prints an error message for unknown
+            // option characters
+            usage();
+            exit(1);
+        }
+    }
+
+    endpoint = argv[optind];
+    if (!endpoint && (regid == DTN_REGID_NONE)) {
+        fprintf(stderr, "must specify either an endpoint or a regid\n");
+        usage();
+        exit(1);
+    }
+
+    if ((change || unregister) && (regid == DTN_REGID_NONE)) {
+        fprintf(stderr, "must specify regid when using -%c option\n",
+                change ? 'c' : 'u');
+        usage();
+        exit(1);
+    }
+
+    if (failure_action == DTN_REG_EXEC && failure_script == NULL) {
+        fprintf(stderr, "failure action EXEC must supply script\n");
+        usage();
+        exit(1);
+    }
+
+    // the default is to use memory transfer mode, but if someone specifies a
+    // filename then we need to tell the API to expect a file
+    if ( filename[0] != '\0' )
+        bundletype = DTN_PAYLOAD_FILE;
+
+}
+
+
+static void
+print_data(char* buffer, u_int length)
+{
+    u_int k;
+    char s_buffer[BUFSIZE];
+    for (k=0; k < length; k++)
+    {
+        if (buffer[k] >= ' ' && buffer[k] <= '~')
+            s_buffer[k%BUFSIZE] = buffer[k];
+        else
+            s_buffer[k%BUFSIZE] = '.';
+
+        if (k%BUFSIZE == 0) // new line every 16 bytes
+        {
+            printf("%07x ", k);
+        }
+        else if (k%2 == 0)
+        {
+            printf(" "); // space every 2 bytes
+        }
+                    
+        printf("%02x", buffer[k] & 0xff);
+                    
+            // print character summary (a la emacs hexl-mode)
+        if (k%BUFSIZE == BUFSIZE-1)
+        {
+            printf(" |  %.*s\n", BUFSIZE, s_buffer);
+        }
+    }
+    
+    // handle leftovers
+    if ((length % BUFSIZE) != 0) {
+        while (k%BUFSIZE != BUFSIZE-1) {
+            if (k%2 == 0) {
+                printf(" ");
+            }
+            printf("  ");
+            k++;
+        }
+        printf("   |  %.*s\n",
+               (int)length%BUFSIZE, 
+               s_buffer);
+    }
+    printf("\n");
+}
+
+/* ----------------------------------------------------------------------- */
+/* Builds the temporary file based on the template given and an integer
+ * counter value
+ */
+int buildfilename(char* template, char* newfilename, int counter )
+{
+    char counterasstring[COUNTER_MAX_DIGITS];
+    char formatstring[10];
+    char* startloc;
+    char* endloc;
+    int templatelen;
+
+    strcpy(newfilename, template);
+
+    endloc = rindex(newfilename, '#');
+    /* No template in the filename, just copy it as is */
+    if ( endloc == NULL )
+        return 0;
+        
+    /* Search backwards for the start of the template */
+    for ( startloc = endloc; *startloc == '#' && startloc != template; 
+          startloc -= 1 );
+
+    startloc += 1;
+
+    templatelen = endloc - startloc + 1;
+    if ( templatelen > COUNTER_MAX_DIGITS )
+        templatelen = COUNTER_MAX_DIGITS;
+
+    sprintf(formatstring, "%%0%dd", templatelen);
+    sprintf(counterasstring, formatstring, counter);
+
+    if ( strlen(counterasstring) > (unsigned int)templatelen )
+        fprintf(stderr, "Warning: Filename template not large enough "
+                "to support counter value %d\n", counter);
+
+    memcpy(startloc, counterasstring, sizeof(char) * templatelen);
+
+    return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* File transfers suffer considerably from the inability to safely send 
+ * metadata on the same channel as the file transfer in DTN.  Perhaps we 
+ * should work around this?
+ */
+int
+handle_file_transfer(dtn_bundle_spec_t spec, dtn_bundle_payload_t payload,
+                     int* total_bytes, int counter)
+{
+    int tempdes;
+    int destdes;
+    char block[BLOCKSIZE];
+    ssize_t bytesread;
+    struct stat fileinfo;
+    char currentfile[PATH_MAX];
+
+    // Create the filename by searching for ### characters in the given
+    // filename and replacing that with an incrementing counter.  
+    buildfilename(filename, currentfile, counter);
+
+    // Try to rename the old file to the new name to avoid unnecessary copying
+    if (rename(filename, currentfile) == 0) {
+        // success!
+
+    } else {
+        // Copy the file into place
+        tempdes = open(payload.filename.filename_val, O_RDONLY);
+        
+        if ( tempdes < 0 )
+        {
+            fprintf(stderr, "While opening the temporary file for reading '%s': %s\n",
+                    payload.filename.filename_val, strerror(errno));
+            exit(1);
+        }
+
+        destdes = creat(currentfile, 0644);
+        
+        if ( destdes < 0 )
+        {
+            fprintf(stderr, "While opening output file for writing '%s': %s\n",
+                    filename, strerror(errno));
+            exit(1);
+        }
+
+        // Duplicate the file
+        while ( (bytesread = read(tempdes, block, sizeof(block))) > 0 )
+            write(destdes, block, bytesread);
+
+        close(tempdes);
+        close(destdes);
+        
+        unlink(payload.filename.filename_val);
+    }
+
+    if ( stat(currentfile, &fileinfo) == -1 )
+    {
+        fprintf(stderr, "Unable to stat destination file '%s': %s\n",
+                currentfile, strerror(errno));
+        return 1;
+    }
+
+    printf("%d byte file from [%s]: transit time=%d ms, written to '%s'\n",
+           (int)fileinfo.st_size, spec.source.uri, 0, currentfile);        
+
+    *total_bytes += fileinfo.st_size;
+
+    return 0;
+}
+
+int
+main(int argc, char** argv)
+{
+    int i;
+    u_int k;
+    int ret;
+    int total_bytes = 0;
+    dtn_handle_t handle;
+    dtn_endpoint_id_t local_eid;
+    dtn_reg_info_t reginfo;
+    dtn_bundle_spec_t spec;
+    dtn_bundle_payload_t payload;
+    int call_bind;
+
+    // force stdout to always be line buffered, even if output is
+    // redirected to a pipe or file
+    setvbuf(stdout, (char *)NULL, _IOLBF, 0);
+    
+    progname = argv[0];
+
+    parse_options(argc, argv);
+
+    if (count == 0) { 
+        printf("dtnrecv (pid %d) starting up\n", getpid());
+    } else {
+        printf("dtnrecv (pid %d) starting up -- count %u\n", getpid(), count);
+    }
+
+    // open the ipc handle
+    if (verbose) printf("opening connection to dtn router...\n");
+    int err = dtn_open(&handle);
+    if (err != DTN_SUCCESS) {
+        fprintf(stderr, "fatal error opening dtn handle: %s\n",
+                dtn_strerror(err));
+        exit(1);
+    }
+    if (verbose) printf("opened connection to dtn router...\n");
+
+    // if we're not given a regid, or we're in change mode, we need to
+    // build up the reginfo
+    memset(&reginfo, 0, sizeof(reginfo));
+
+    if ((regid == DTN_REGID_NONE) || change)
+    {
+        // if the specified eid starts with '/', then build a local
+        // eid based on the configuration of our dtn router plus the
+        // demux string. otherwise make sure it's a valid one
+        if (endpoint[0] == '/') {
+            if (verbose) printf("calling dtn_build_local_eid.\n");
+            dtn_build_local_eid(handle, &local_eid, (char *) endpoint);
+            if (verbose) printf("local_eid [%s]\n", local_eid.uri);
+        } else {
+            if (verbose) printf("calling parse_eid_string\n");
+            if (dtn_parse_eid_string(&local_eid, endpoint)) {
+                fprintf(stderr, "invalid destination endpoint '%s'\n",
+                        endpoint);
+                goto err;
+            }
+        }
+
+        // create a new registration based on this eid
+        dtn_copy_eid(&reginfo.endpoint, &local_eid);
+        reginfo.regid             = regid;
+        reginfo.expiration        = expiration;
+        reginfo.flags             = failure_action;
+        reginfo.script.script_val = failure_script;
+        reginfo.script.script_len = strlen(failure_script) + 1;
+    }
+
+    if (change) {
+        if ((ret = dtn_change_registration(handle, regid, &reginfo)) != 0) {
+            fprintf(stderr, "error changing registration: %d (%s)\n",
+                    ret, dtn_strerror(dtn_errno(handle)));
+            goto err;
+        }
+        printf("change registration succeeded, regid %d\n", regid);
+        goto done;
+    }
+    
+    if (unregister) {
+        if (dtn_unregister(handle, regid) != 0) {
+            fprintf(stderr, "error in unregister regid %d: %s\n",
+                    regid, dtn_strerror(dtn_errno(handle)));
+            goto err;
+        }
+        
+        printf("unregister succeeded, regid %d\n", regid);
+        goto done;
+    }
+    
+    // try to see if there is an existing registration that matches
+    // the given endpoint, in which case we'll use that one.
+    if (regid == DTN_REGID_NONE && ! no_find_reg) {
+        if (dtn_find_registration(handle, &local_eid, &regid) != 0) {
+            if (dtn_errno(handle) != DTN_ENOTFOUND) {
+                fprintf(stderr, "error in find_registration: %s\n",
+                        dtn_strerror(dtn_errno(handle)));
+                goto err;
+            }
+        }
+        printf("find registration succeeded, regid %d\n", regid);
+        call_bind = 1;
+    }
+    
+    // if the user didn't give us a registration to use, get a new one
+    if (regid == DTN_REGID_NONE) {
+        if ((ret = dtn_register(handle, &reginfo, &regid)) != 0) {
+            fprintf(stderr, "error creating registration: %d (%s)\n",
+                    ret, dtn_strerror(dtn_errno(handle)));
+            goto err;
+        }
+
+        printf("register succeeded, regid %d\n", regid);
+        call_bind = 0;
+    } else {
+        call_bind = 1;
+    }
+    
+    if (register_only) {
+        goto done;
+    }
+
+    if (call_bind) {
+        // bind the current handle to the found registration
+        printf("binding to regid %d\n", regid);
+        if (dtn_bind(handle, regid) != 0) {
+            fprintf(stderr, "error binding to registration: %s\n",
+                    dtn_strerror(dtn_errno(handle)));
+            goto err;
+        }
+    }
+
+    // loop waiting for bundles
+    if (count == 0) {
+        printf("looping forever to receive bundles\n");
+    } else {
+        printf("looping to receive %d bundles\n", count);
+    }
+    for (i = 0; (count == 0) || (i < count); ++i) {
+        memset(&spec, 0, sizeof(spec));
+        memset(&payload, 0, sizeof(payload));
+
+        if (!quiet) {
+            printf("dtn_recv [%s]...\n", local_eid.uri);
+        }
+    
+        if ((ret = dtn_recv(handle, &spec, bundletype, &payload, 
+                                                        recv_timeout)) < 0)
+        {
+            fprintf(stderr, "error getting recv reply: %d (%s)\n",
+                    ret, dtn_strerror(dtn_errno(handle)));
+            goto err;
+        }
+
+        // Files need to be handled differently than memory transfers
+        if ( bundletype == DTN_PAYLOAD_FILE )
+        {
+                handle_file_transfer(spec, payload, &total_bytes, i);
+                dtn_free_payload(&payload);
+                continue;
+        }
+
+        total_bytes += payload.buf.buf_len;
+
+        if (quiet) {
+            dtn_free_payload(&payload);
+            if (spec.blocks.blocks_len > 0) {
+                free(spec.blocks.blocks_val);
+                spec.blocks.blocks_val = NULL;
+                spec.blocks.blocks_len = 0;
+            }
+            if (spec.metadata.metadata_len > 0) {
+                free(spec.metadata.metadata_val);
+                spec.metadata.metadata_val = NULL;
+                spec.metadata.metadata_len = 0;
+            }
+            continue;
+        }
+
+        printf("\n%d extension blocks from [%s]: transit time=%d ms\n",
+               spec.blocks.blocks_len, spec.source.uri, 0);
+        dtn_extension_block_t *block = spec.blocks.blocks_val;
+        for (k = 0; k < spec.blocks.blocks_len; k++) {
+            printf("Extension Block %i:\n", k);
+            printf("\ttype = %i\n\tflags = %i\n",
+                   block[k].type, block[k].flags);
+            print_data(block[k].data.data_val, block[k].data.data_len);
+        }
+
+        printf("\n%d metadata blocks from [%s]: transit time=%d ms\n",
+               spec.metadata.metadata_len, spec.source.uri, 0);
+        dtn_extension_block_t *meta = spec.metadata.metadata_val;
+        for (k = 0; k < spec.metadata.metadata_len; k++) {
+            printf("Metadata Extension Block %i:\n", k);
+            printf("\ttype = %i\n\tflags = %i\n",
+                   meta[k].type, meta[k].flags);
+            print_data(meta[k].data.data_val, meta[k].data.data_len);
+        }
+
+        printf("%d payload bytes from [%s]: transit time=%d ms\n",
+               payload.buf.buf_len,
+               spec.source.uri, 0);
+        
+        print_data(payload.buf.buf_val, payload.buf.buf_len);
+        printf("\n");
+
+        dtn_free_payload(&payload);
+        if (spec.blocks.blocks_len > 0) {
+            free(spec.blocks.blocks_val);
+            spec.blocks.blocks_val = NULL;
+            spec.blocks.blocks_len = 0;
+        }
+        if (spec.metadata.metadata_len > 0) {
+            free(spec.metadata.metadata_val);
+            spec.metadata.metadata_val = NULL;
+            spec.metadata.metadata_len = 0;
+        }
+    }
+
+    printf("dtnrecv (pid %d) exiting: %d bundles received, %d total bytes\n\n",
+           getpid(), i, total_bytes);
+
+done:
+    dtn_close(handle);
+    return 0;
+
+err:
+    dtn_close(handle);
+    return 1;
+}