applib/dtn_ipc.c
changeset 0 2b3e5ec03512
equal deleted inserted replaced
-1:000000000000 0:2b3e5ec03512
       
     1 /*
       
     2  *    Copyright 2004-2006 Intel Corporation
       
     3  * 
       
     4  *    Licensed under the Apache License, Version 2.0 (the "License");
       
     5  *    you may not use this file except in compliance with the License.
       
     6  *    You may obtain a copy of the License at
       
     7  * 
       
     8  *        http://www.apache.org/licenses/LICENSE-2.0
       
     9  * 
       
    10  *    Unless required by applicable law or agreed to in writing, software
       
    11  *    distributed under the License is distributed on an "AS IS" BASIS,
       
    12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    13  *    See the License for the specific language governing permissions and
       
    14  *    limitations under the License.
       
    15  */
       
    16 
       
    17 #ifdef HAVE_CONFIG_H
       
    18 #  include <dtn-config.h>
       
    19 #endif
       
    20 
       
    21 #include <stdio.h>
       
    22 #include <stdlib.h>
       
    23 #include <errno.h>
       
    24 #include <string.h>
       
    25 #include <unistd.h>
       
    26 #include <sys/types.h>
       
    27 #include <sys/socket.h>
       
    28 #include <netinet/in.h>
       
    29 #include <arpa/inet.h>
       
    30 #include <oasys/compat/inet_aton.h>
       
    31 #include <oasys/compat/inttypes.h>
       
    32 
       
    33 #include "dtn_ipc.h"
       
    34 #include "dtn_errno.h"
       
    35 #include "dtn_types.h"
       
    36 
       
    37 /* exposed globally for testing purposes only */
       
    38 int dtnipc_version = DTN_IPC_VERSION;
       
    39 
       
    40 const char*
       
    41 dtnipc_msgtoa(u_int8_t type)
       
    42 {
       
    43 #define CASE(_type) case _type : return #_type; break;
       
    44     
       
    45     switch(type) {
       
    46         CASE(DTN_OPEN);
       
    47         CASE(DTN_CLOSE);
       
    48         CASE(DTN_LOCAL_EID);
       
    49         CASE(DTN_REGISTER);
       
    50         CASE(DTN_UNREGISTER);
       
    51         CASE(DTN_FIND_REGISTRATION);
       
    52         CASE(DTN_CHANGE_REGISTRATION);
       
    53         CASE(DTN_BIND);
       
    54         CASE(DTN_SEND);
       
    55         CASE(DTN_RECV);
       
    56         CASE(DTN_BEGIN_POLL);
       
    57         CASE(DTN_CANCEL_POLL);
       
    58         CASE(DTN_CANCEL);
       
    59         CASE(DTN_SESSION_UPDATE);
       
    60 
       
    61     default:
       
    62         return "(unknown type)";
       
    63     }
       
    64     
       
    65 #undef CASE
       
    66 }
       
    67 
       
    68 /*
       
    69  * Initialize the handle structure.
       
    70  */
       
    71 int
       
    72 dtnipc_open(dtnipc_handle_t* handle)
       
    73 {
       
    74     int remote_version, ret;
       
    75     char *env, *end;
       
    76     struct sockaddr_in sa;
       
    77     in_addr_t ipc_addr;
       
    78     u_int16_t ipc_port;
       
    79     u_int32_t handshake;
       
    80     u_int port;
       
    81 
       
    82     // zero out the handle
       
    83     memset(handle, 0, sizeof(dtnipc_handle_t));
       
    84 
       
    85     // check for debugging
       
    86     if (getenv("DTNAPI_DEBUG") != 0) {
       
    87         handle->debug = 1;
       
    88     }
       
    89     
       
    90     // note that we leave eight bytes free to be used for the framing
       
    91     // -- the type code and length for send (which is only five
       
    92     // bytes), and the return code and length for recv (which is
       
    93     // actually eight bytes)
       
    94     xdrmem_create(&handle->xdr_encode, handle->buf + 8,
       
    95                   DTN_MAX_API_MSG - 8, XDR_ENCODE);
       
    96     
       
    97     xdrmem_create(&handle->xdr_decode, handle->buf + 8,
       
    98                   DTN_MAX_API_MSG - 8, XDR_DECODE);
       
    99 
       
   100     // open the socket
       
   101     handle->sock = socket(PF_INET, SOCK_STREAM, 0);
       
   102     if (handle->sock < 0)
       
   103     {
       
   104         handle->err = DTN_ECOMM;
       
   105         dtnipc_close(handle);
       
   106         return -1;
       
   107     }
       
   108 
       
   109     // check for DTNAPI environment variables overriding the address /
       
   110     // port defaults
       
   111     ipc_addr = htonl(INADDR_LOOPBACK);
       
   112     ipc_port = DTN_IPC_PORT;
       
   113     
       
   114     if ((env = getenv("DTNAPI_ADDR")) != NULL) {
       
   115         if (inet_aton(env, (struct in_addr*)&ipc_addr) == 0)
       
   116         {
       
   117             fprintf(stderr, "DTNAPI_ADDR environment variable (%s) "
       
   118                     "not a valid ip address\n", env);
       
   119             exit(1);
       
   120         }
       
   121     }
       
   122 
       
   123     if ((env = getenv("DTNAPI_PORT")) != NULL) {
       
   124         port = strtoul(env, &end, 10);
       
   125         if (*end != '\0' || port > 0xffff)
       
   126         {
       
   127             fprintf(stderr, "DTNAPI_PORT environment variable (%s) "
       
   128                     "not a valid ip port\n", env);
       
   129             exit(1);
       
   130         }
       
   131         ipc_port = (u_int16_t)port;
       
   132     }
       
   133 
       
   134     // connect to the server
       
   135     memset(&sa, 0, sizeof(sa));
       
   136     sa.sin_family = AF_INET;
       
   137     sa.sin_addr.s_addr = ipc_addr;
       
   138     sa.sin_port = htons(ipc_port);
       
   139     
       
   140     ret = connect(handle->sock, (const struct sockaddr*)&sa, sizeof(sa));
       
   141     if (ret != 0) {
       
   142         if (handle->debug) {
       
   143             fprintf(stderr, "dtn_ipc: error connecting to server: %s\n",
       
   144                     strerror(errno));
       
   145         }
       
   146 
       
   147         handle->err = DTN_ECOMM;
       
   148         dtnipc_close(handle);
       
   149         return -1;
       
   150     }
       
   151 
       
   152     if (handle->debug) {
       
   153         fprintf(stderr, "dtn_ipc: connected to server: fd %d\n", handle->sock);
       
   154     }
       
   155 
       
   156     // send the session initiation to the server on the handshake
       
   157     // port. it consists of DTN_OPEN in the high 16 bits and IPC
       
   158     // version in the low 16 bits
       
   159     handshake = htonl(DTN_OPEN << 16 | dtnipc_version);
       
   160     ret = write(handle->sock, &handshake, sizeof(handshake));
       
   161     if (ret != sizeof(handshake)) {
       
   162         if (handle->debug) {
       
   163             fprintf(stderr, "dtn_ipc: handshake error\n");
       
   164         }
       
   165         handle->err = DTN_ECOMM;
       
   166         dtnipc_close(handle);
       
   167         return -1;
       
   168     }
       
   169     handle->total_sent += ret;
       
   170 
       
   171     // wait for the handshake response
       
   172     handshake = 0;
       
   173     ret = read(handle->sock, &handshake, sizeof(handshake));
       
   174     if (ret != sizeof(handshake) || (ntohl(handshake) >> 16) != DTN_OPEN) {
       
   175         if (handle->debug) {
       
   176             fprintf(stderr, "dtn_ipc: handshake error\n");
       
   177         }
       
   178         dtnipc_close(handle);
       
   179         handle->err = DTN_ECOMM;
       
   180         return -1;
       
   181     }
       
   182 
       
   183     handle->total_rcvd += ret;
       
   184 
       
   185     remote_version = (ntohl(handshake) & 0x0ffff);
       
   186     if (remote_version != dtnipc_version) {
       
   187         if (handle->debug) {
       
   188             fprintf(stderr, "dtn_ipc: version mismatch\n");
       
   189         }
       
   190         dtnipc_close(handle);
       
   191         handle->err = DTN_EVERSION;
       
   192         return -1;
       
   193     }
       
   194     
       
   195     return 0;
       
   196 }
       
   197 
       
   198 /*
       
   199  * Clean up the handle. dtnipc_open must have already been called on
       
   200  * the handle.
       
   201  */
       
   202 int
       
   203 dtnipc_close(dtnipc_handle_t* handle)
       
   204 {
       
   205     int ret;
       
   206     
       
   207     // first send a close over RPC
       
   208     if (handle->err != DTN_ECOMM) {
       
   209         ret = dtnipc_send_recv(handle, DTN_CLOSE);
       
   210     } else {
       
   211         ret = -1;
       
   212     }
       
   213     
       
   214     xdr_destroy(&handle->xdr_encode);
       
   215     xdr_destroy(&handle->xdr_decode);
       
   216 
       
   217     if (handle->sock > 0) {
       
   218         close(handle->sock);
       
   219     }
       
   220 
       
   221     handle->sock = 0;
       
   222 
       
   223     return ret;
       
   224 }
       
   225       
       
   226 
       
   227 /*
       
   228  * Send a message over the dtn ipc protocol.
       
   229  *
       
   230  * Returns 0 on success, -1 on error.
       
   231  */
       
   232 int
       
   233 dtnipc_send(dtnipc_handle_t* handle, dtnapi_message_type_t type)
       
   234 {
       
   235     int ret;
       
   236     u_int32_t len, msglen, origlen;
       
   237     
       
   238     // pack the message code in the fourth byte of the buffer and the
       
   239     // message length into the next four. we don't use xdr routines
       
   240     // for these since we need to be able to decode the length on the
       
   241     // other side to make sure we've read the whole message, and we
       
   242     // need the type to know which xdr decoder to call
       
   243     handle->buf[3] = type;
       
   244 
       
   245     len = xdr_getpos(&handle->xdr_encode);
       
   246     msglen = len + 5;
       
   247     len = htonl(len);
       
   248     memcpy(&handle->buf[4], &len, sizeof(len));
       
   249     
       
   250     // reset the xdr encoder
       
   251     xdr_setpos(&handle->xdr_encode, 0);
       
   252     
       
   253     // send the message, looping until it's all sent
       
   254     origlen = msglen;
       
   255     char* bp = &handle->buf[3];
       
   256     do {
       
   257         ret = write(handle->sock, bp, msglen);
       
   258         handle->total_sent += ret;
       
   259         
       
   260         if (handle->debug) {
       
   261             fprintf(stderr, "dtn_ipc: send(%s) wrote %d/%d bytes (%s) "
       
   262                     "(total sent/rcvd %u/%u)\n",
       
   263                     dtnipc_msgtoa(type), ret, origlen,
       
   264                     ret == -1 ? strerror(errno) : "success",
       
   265                     handle->total_sent, handle->total_rcvd);
       
   266         }
       
   267 
       
   268         if (ret <= 0) {
       
   269             if (errno == EINTR)
       
   270                 continue;
       
   271             
       
   272             handle->err = DTN_ECOMM;
       
   273             dtnipc_close(handle);
       
   274             return -1;
       
   275         }
       
   276 
       
   277         bp     += ret;
       
   278         msglen -= ret;
       
   279         
       
   280     } while (msglen > 0);
       
   281 
       
   282     return 0;
       
   283 }
       
   284 
       
   285 /*
       
   286  * Receive a message on the ipc channel. May block if there is no
       
   287  * pending message.
       
   288  *
       
   289  * Sets status to the server-returned status code and returns the
       
   290  * length of any reply message on success, returns -1 on internal
       
   291  * error.
       
   292  */
       
   293 int
       
   294 dtnipc_recv(dtnipc_handle_t* handle, int* status)
       
   295 {
       
   296     int ret;
       
   297     u_int32_t len, nread;
       
   298     u_int32_t statuscode;
       
   299 
       
   300     // reset the xdr decoder before reading in any data
       
   301     xdr_setpos(&handle->xdr_decode, 0);
       
   302 
       
   303     // read the status code and length
       
   304     ret = read(handle->sock, handle->buf, 8);
       
   305     handle->total_rcvd += ret;
       
   306 
       
   307     // make sure we got it all
       
   308     if (ret != 8) {
       
   309         handle->err = DTN_ECOMM;
       
   310         dtnipc_close(handle);
       
   311         return -1;
       
   312     }
       
   313     
       
   314     memcpy(&statuscode, handle->buf, sizeof(statuscode));
       
   315     statuscode = ntohl(statuscode);
       
   316     *status = statuscode;
       
   317     
       
   318     memcpy(&len, &handle->buf[4], sizeof(len));
       
   319     len = ntohl(len);
       
   320     
       
   321     if (handle->debug) {
       
   322         fprintf(stderr, "dtn_ipc: recv() read %d/8 bytes for status (%s): "
       
   323                 "status %d len %d (total sent/rcvd %u/%u)\n",
       
   324                 ret, ret == -1 ? strerror(errno) : "success",
       
   325                 *status, len, handle->total_sent, handle->total_rcvd);
       
   326     }
       
   327 
       
   328     // read the rest of the message 
       
   329     nread = 8;
       
   330     while (nread < len + 8) {
       
   331         ret = read(handle->sock,
       
   332                    &handle->buf[nread], sizeof(handle->buf) - nread);
       
   333         handle->total_rcvd += ret;
       
   334         
       
   335         if (handle->debug) {
       
   336             fprintf(stderr, "dtn_ipc: recv() read %d/%d bytes (%s)\n",
       
   337                     ret, len, ret == -1 ? strerror(errno) : "success");
       
   338         }
       
   339 
       
   340         if (ret <= 0) {
       
   341             if (errno == EINTR)
       
   342                 continue;
       
   343             
       
   344             handle->err = DTN_ECOMM;
       
   345             dtnipc_close(handle);
       
   346             return -1;
       
   347         }
       
   348 
       
   349         nread += ret;
       
   350     }
       
   351 
       
   352     return len;
       
   353 }
       
   354 
       
   355 
       
   356 /**
       
   357  * Send a message and wait for a response over the dtn ipc protocol.
       
   358  *
       
   359  * Returns 0 on success, -1 on error.
       
   360  */
       
   361 int dtnipc_send_recv(dtnipc_handle_t* handle, dtnapi_message_type_t type)
       
   362 {
       
   363     int status;
       
   364 
       
   365     // send the message
       
   366     if (dtnipc_send(handle, type) < 0) {
       
   367         return -1;
       
   368     }
       
   369 
       
   370     // wait for a response
       
   371     if (dtnipc_recv(handle, &status) < 0) {
       
   372         return -1;
       
   373     }
       
   374 
       
   375     // handle server-side errors
       
   376     if (status != DTN_SUCCESS) {
       
   377         handle->err = status;
       
   378         return -1;
       
   379     }
       
   380 
       
   381     return 0;
       
   382 }