LCOV - code coverage report
Current view: directory - src/shared/srpc - rpc_service.c (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 270 220 81.5 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : /*
       8                 :  * Service descriptions.
       9                 :  */
      10                 : 
      11                 : #include <stdio.h>
      12                 : #include <stdlib.h>
      13                 : #include <string.h>
      14                 : 
      15                 : #include "native_client/src/shared/srpc/nacl_srpc.h"
      16                 : #include "native_client/src/shared/srpc/nacl_srpc_internal.h"
      17                 : 
      18                 : #ifndef SIZE_T_MAX
      19                 : # define SIZE_T_MAX (~((size_t) 0))
      20                 : #endif
      21                 : 
      22                 : 
      23                 : /*
      24                 :  * The struct used to describe methods within services.
      25                 :  */
      26                 : struct NaClSrpcMethodDesc {
      27                 :   char const  *name;
      28                 :   char const  *input_types;
      29                 :   char const  *output_types;
      30                 :   /**
      31                 :    * function pointer used to process calls to the named method
      32                 :    */
      33                 :   NaClSrpcMethod handler;
      34                 : };
      35                 : typedef struct NaClSrpcMethodDesc NaClSrpcMethodDesc;
      36                 : 
      37                 : /*
      38                 :  * Forward declarations for static "built-in" methods.
      39                 :  */
      40                 : static void ServiceDiscovery(NaClSrpcRpc* rpc,
      41                 :                              NaClSrpcArg** in_args,
      42                 :                              NaClSrpcArg** out_args,
      43                 :                              NaClSrpcClosure* done);
      44                 : 
      45                 : /*
      46                 :  * Get the next text element (name, input types, or output types).  These
      47                 :  * elements are delimited by ':', '\n', or '\0'.
      48                 :  */
      49            1302 : static char* GetOneElement(const char* string) {
      50            1302 :   size_t len = 0;
      51            1302 :   char* buf;
      52                 : 
      53           22468 :   while (':' != string[len] && '\n' != string[len] && '\0' != string[len]) {
      54            6408 :     len++;
      55            6408 :   }
      56            1302 :   buf = malloc(len + 1);
      57            1302 :   if (NULL == buf) {
      58               0 :     return NULL;
      59                 :   }
      60            3906 :   strncpy(buf, string, len);
      61            1302 :   buf[len] = '\0';
      62            1302 :   return buf;
      63            1302 : }
      64                 : 
      65                 : /* * Get the components of one method description.  Returns a pointer to
      66                 :  * the delimiter if successful or NULL if not.
      67                 :  */
      68             434 : static const char* ParseOneEntry(const char* entry_fmt,
      69             434 :                                  char** name,
      70             434 :                                  char** input_types,
      71             434 :                                  char** output_types) {
      72             434 :   char* namebuf = NULL;
      73             434 :   char* insbuf = NULL;
      74             434 :   char* outsbuf = NULL;
      75                 : 
      76             434 :   *name = NULL;
      77             434 :   *input_types = NULL;
      78             434 :   *output_types = NULL;
      79                 : 
      80                 :   /* Don't try to parse NULL strings. */
      81             434 :   if (NULL == entry_fmt) {
      82               0 :     goto cleanup;
      83                 :   }
      84                 :   /* Get the name into a buffer, and ensure it is followed by ':'. */
      85             434 :   namebuf = GetOneElement(entry_fmt);
      86             434 :   if (NULL == namebuf) {
      87               0 :     goto cleanup;
      88                 :   }
      89             434 :   entry_fmt += strlen(namebuf);
      90             434 :   if (':' != *entry_fmt) {
      91               0 :     goto cleanup;
      92                 :   }
      93             434 :   entry_fmt++;
      94                 :   /* Get the input types into a buffer, and ensure it is followed by ':'. */
      95             434 :   insbuf = GetOneElement(entry_fmt);
      96             434 :   if (NULL == insbuf) {
      97               0 :     goto cleanup;
      98                 :   }
      99             434 :   entry_fmt += strlen(insbuf);
     100             434 :   if (':' != *entry_fmt) {
     101               0 :     goto cleanup;
     102                 :   }
     103             434 :   entry_fmt++;
     104                 :   /* Get the output types into a buffer. */
     105             434 :   outsbuf = GetOneElement(entry_fmt);
     106             434 :   if (NULL == outsbuf) {
     107               0 :     goto cleanup;
     108                 :   }
     109             434 :   entry_fmt += strlen(outsbuf);
     110                 :   /* Copy the result strings out. */
     111             434 :   *name = namebuf;
     112             434 :   *input_types = insbuf;
     113             434 :   *output_types = outsbuf;
     114                 :   /* Return a pointer to the next character after the elements. */
     115             434 :   return entry_fmt;
     116                 : 
     117                 :  cleanup:
     118               0 :   free(insbuf);
     119               0 :   free(namebuf);
     120               0 :   return NULL;
     121             434 : }
     122                 : 
     123             104 : static void FreeMethods(NaClSrpcMethodDesc* methods, uint32_t method_count) {
     124             104 :   uint32_t i;
     125                 : 
     126             104 :   if (NULL == methods) {
     127               0 :     return;
     128                 :   }
     129             854 :   for (i = 0; i < method_count; ++i) {
     130             323 :     if (NULL == methods[i].name) {
     131                 :       /* We have reached the end of the portion set by ParseOneEntry calls. */
     132               0 :       break;
     133                 :     }
     134             323 :     free((char*) methods[i].name);
     135             323 :     free((char*) methods[i].input_types);
     136             323 :     free((char*) methods[i].output_types);
     137             323 :   }
     138             104 :   free(methods);
     139             208 : }
     140                 : 
     141                 : /*
     142                 :  * The method tables passed to construction do not contain "intrinsic" methods
     143                 :  * such as service discovery and shutdown.  Build a complete table including
     144                 :  * those from a given input.
     145                 :  */
     146                 : static NaClSrpcMethodDesc* BuildMethods(
     147              74 :     const struct NaClSrpcHandlerDesc* methods,
     148              74 :     uint32_t* method_count) {
     149                 :   static const char* kSDDescString = "service_discovery::C";
     150              74 :   NaClSrpcMethodDesc* complete_methods = NULL;
     151              74 :   uint32_t i;
     152              74 :   const char* nul_loc;
     153                 : 
     154                 :   /* Compute the number of methods to export. */
     155              74 :   *method_count = 0;
     156             280 :   while (NULL != methods[*method_count].entry_fmt)
     157             132 :     ++*method_count;
     158                 :   /* Add one extra method for service discovery. */
     159              74 :   ++*method_count;
     160                 :   /* Allocate the method descriptors. One extra for NULL termination. */
     161                 :   complete_methods = (NaClSrpcMethodDesc*)
     162              74 :       calloc(*method_count + 1, sizeof(*complete_methods));
     163              74 :   if (NULL == complete_methods) {
     164               0 :     return NULL;
     165                 :   }
     166                 :   /* Copy the methods passed in, adding service discovery as element zero. */
     167              74 :   nul_loc = ParseOneEntry(kSDDescString,
     168                 :                           (char**) &complete_methods[0].name,
     169                 :                           (char**) &complete_methods[0].input_types,
     170                 :                           (char**) &complete_methods[0].output_types);
     171              74 :   if (nul_loc == NULL) {
     172               0 :     goto cleanup;
     173                 :   }
     174              74 :   complete_methods[0].handler = ServiceDiscovery;
     175             412 :   for (i = 0; i < *method_count - 1; ++i) {
     176             132 :     nul_loc = ParseOneEntry(methods[i].entry_fmt,
     177                 :                             (char**) &complete_methods[i + 1].name,
     178                 :                             (char**) &complete_methods[i + 1].input_types,
     179                 :                             (char**) &complete_methods[i + 1].output_types);
     180             132 :     if (nul_loc == NULL) {
     181               0 :       goto cleanup;
     182                 :     }
     183             132 :     complete_methods[i + 1].handler = methods[i].handler;
     184             132 :   }
     185                 :   /* Add the NULL terminator */
     186              74 :   complete_methods[*method_count].name = NULL;
     187              74 :   complete_methods[*method_count].input_types = NULL;
     188              74 :   complete_methods[*method_count].output_types = NULL;
     189              74 :   complete_methods[*method_count].handler = NULL;
     190                 :   /* Return the array */
     191              74 :   return complete_methods;
     192                 : 
     193                 :  cleanup:
     194               0 :   FreeMethods(complete_methods, *method_count);
     195               0 :   return NULL;
     196              74 : }
     197                 : 
     198                 : /*
     199                 :  * Builds a service discovery string from an array of method descriptions.
     200                 :  */
     201              74 : static char* BuildSDString(const NaClSrpcMethodDesc* methods,
     202              74 :                            uint32_t method_count,
     203              74 :                            nacl_abi_size_t *length) {
     204              74 :   uint32_t i;
     205              74 :   char* p;
     206              74 :   char* str;
     207                 : 
     208                 :   /* Find the total length of the method description strings.  */
     209              74 :   *length = 1;
     210             560 :   for (i = 0; i < method_count; ++i) {
     211             206 :     *length += nacl_abi_size_t_saturate(
     212             206 :                  strlen(methods[i].name) + 1 +
     213             206 :                  strlen(methods[i].input_types) + 1 +
     214             206 :                  strlen(methods[i].output_types) + 1);
     215             206 :   }
     216                 :   /* Allocate the string. */
     217              74 :   str = (char*) malloc(*length + 1);
     218              74 :   if (NULL == str) {
     219               0 :     return NULL;
     220                 :   }
     221                 :   /* Concatenate the method description strings into the string. */
     222              74 :   p = str;
     223             560 :   for (i = 0; i < method_count; ++i) {
     224             206 :     size_t buf_limit = str + *length - p;
     225                 :     /* TODO(robertm): explore use portability_io.h */
     226                 : #if NACL_WINDOWS
     227                 :     p += _snprintf(p, buf_limit, "%s:%s:%s\n",
     228                 :                    methods[i].name,
     229                 :                    methods[i].input_types,
     230                 :                    methods[i].output_types);
     231                 : #else
     232             206 :     p += snprintf(p, buf_limit, "%s:%s:%s\n",
     233                 :                   methods[i].name,
     234                 :                   methods[i].input_types,
     235                 :                   methods[i].output_types);
     236                 : #endif
     237             206 :   }
     238              74 :   *p = '\0';
     239                 :   /* Return the resulting string. */
     240              74 :   return str;
     241              74 : }
     242                 : 
     243                 : /*
     244                 :  * Create a service descriptor from an array of methods.
     245                 :  */
     246              74 : int NaClSrpcServiceHandlerCtor(NaClSrpcService* service,
     247              74 :                                const NaClSrpcHandlerDesc* handler_desc) {
     248              74 :   char*       service_str;
     249              74 :   nacl_abi_size_t  str_length;
     250              74 :   NaClSrpcMethodDesc* methods = NULL;
     251              74 :   uint32_t method_count = 0;
     252                 : 
     253                 :   /* Initialize the struct, so that failures can be cleaned up properly. */
     254              74 :   service->service_string = NULL;
     255              74 :   service->service_string_length = 0;
     256              74 :   service->rpc_descr = NULL;
     257              74 :   service->rpc_count = 0;
     258                 :   /* Add the service_discovery method to the table. */
     259              74 :   methods = BuildMethods(handler_desc, &method_count);
     260              74 :   if (NULL == methods) {
     261               0 :     goto cleanup;
     262                 :   }
     263              74 :   service_str = BuildSDString(methods, method_count, &str_length);
     264              74 :   if (NULL == service_str) {
     265               0 :     goto cleanup;
     266                 :   }
     267              74 :   service->service_string = service_str;
     268              74 :   service->service_string_length = str_length;
     269              74 :   service->rpc_descr = methods;
     270              74 :   service->rpc_count = method_count;
     271              74 :   return 1;
     272                 :  cleanup:
     273               0 :   FreeMethods(methods, method_count);
     274               0 :   return 0;
     275              74 : }
     276                 : 
     277              48 : int NaClSrpcServiceStringCtor(NaClSrpcService* service, const char* str) {
     278              48 :   NaClSrpcMethodDesc* methods = NULL;
     279              48 :   const char* p;
     280              48 :   uint32_t i;
     281              48 :   uint32_t rpc_count;
     282              48 :   size_t rpc_count_size_t;
     283                 : 
     284                 :   /* Initialize the struct, so that failures can be cleaned up properly. */
     285              48 :   service->service_string = NULL;
     286              48 :   service->service_string_length = 0;
     287              48 :   service->rpc_descr = NULL;
     288              48 :   service->rpc_count = 0;
     289                 :   /* Count the number of rpc methods */
     290              48 :   rpc_count = 0;
     291             324 :   for (p = str; *p != '\0'; ) {
     292             228 :     char* next_p = strchr(p, '\n');
     293             228 :     if (NULL == next_p) {
     294                 :       /* malformed input -- no remaining \n character was found */
     295               0 :       goto cleanup;
     296                 :     }
     297             228 :     p = next_p + 1;
     298             228 :     ++rpc_count;
     299             228 :     if (0 == rpc_count) {
     300                 :       /* uint32_t overflow detected. */
     301               0 :       goto cleanup;
     302                 :     }
     303             228 :   }
     304                 :   /*
     305                 :    * The front end knows the next comparison is useless due to the range of
     306                 :    * uint32_t.  And furthermore at least one version of gcc knows that a
     307                 :    * cast doesn't really change that fact.  Hence, assign to a new variable
     308                 :    * of size_t type.
     309                 :    */
     310              48 :   rpc_count_size_t = (size_t) rpc_count;
     311                 :   /* Allocate and clear the descriptor array */
     312              48 :   if (rpc_count_size_t >= SIZE_T_MAX / sizeof(*methods)) {
     313               0 :     goto cleanup;
     314                 :   }
     315              48 :   methods = (NaClSrpcMethodDesc*) malloc(rpc_count_size_t * sizeof(*methods));
     316              48 :   if (NULL == methods) {
     317               0 :     goto cleanup;
     318                 :   }
     319             144 :   memset(methods, 0, rpc_count_size_t * sizeof(*methods));
     320                 :   /* Parse the list of method descriptions */
     321              48 :   p = str;
     322             552 :   for (i = 0; i < rpc_count; ++i) {
     323             228 :     const char* newline_loc;
     324                 : 
     325             228 :     newline_loc = ParseOneEntry(p,
     326                 :                                 (char**) &methods[i].name,
     327                 :                                 (char**) &methods[i].input_types,
     328                 :                                 (char**) &methods[i].output_types);
     329             456 :     if (NULL == newline_loc || '\n' != *newline_loc) {
     330               0 :       goto cleanup;
     331                 :     }
     332             228 :     p = newline_loc + 1;
     333                 :     /* The handler is not set from service_discovery strings */
     334             228 :     methods[i].handler = NULL;
     335             228 :   }
     336              48 :   service->service_string = strdup(str);
     337              48 :   service->service_string_length = nacl_abi_size_t_saturate(strlen(str));
     338              48 :   service->rpc_descr = methods;
     339              48 :   service->rpc_count = rpc_count;
     340              48 :   return 1;
     341                 : 
     342                 :  cleanup:
     343               0 :   FreeMethods(methods, rpc_count);
     344               0 :   return 0;
     345              48 : }
     346                 : 
     347                 : /*
     348                 :  * Destroy a service descriptor.
     349                 :  */
     350             214 : void NaClSrpcServiceDtor(NaClSrpcService* service) {
     351             214 :   if (NULL == service) {
     352             110 :     return;
     353                 :   }
     354                 :   /* Free the method descriptors. */
     355             104 :   FreeMethods((NaClSrpcMethodDesc*) service->rpc_descr, service->rpc_count);
     356                 :   /* Free the service discovery string. */
     357             104 :   free((char*) service->service_string);
     358             318 : }
     359                 : 
     360               0 : void NaClSrpcServicePrint(const NaClSrpcService *service) {
     361               0 :   uint32_t i;
     362                 : 
     363               0 :   if (NULL == service) {
     364               0 :     printf("NULL service\n");
     365               0 :     return;
     366                 :   }
     367               0 :   printf("RPC %-20s %-10s %-10s\n", "Name", "Input args", "Output args");
     368               0 :   for (i = 0; i < service->rpc_count; ++i) {
     369               0 :     printf("%3u %-20s %-10s %-10s\n",
     370                 :            (unsigned int) i,
     371                 :            service->rpc_descr[i].name,
     372                 :            service->rpc_descr[i].input_types,
     373                 :            service->rpc_descr[i].output_types);
     374               0 :   }
     375               0 : }
     376                 : 
     377               0 : uint32_t NaClSrpcServiceMethodCount(const NaClSrpcService *service) {
     378               0 :   if (NULL == service) {
     379               0 :     return 0;
     380                 :   }
     381               0 :   return service->rpc_count;
     382               0 : }
     383                 : 
     384             463 : static int SignatureMatches(NaClSrpcMethodDesc* method_desc,
     385             463 :                             char const* signature) {
     386                 :   struct {
     387                 :     char const* field;
     388                 :     char terminator;
     389             463 :   } matcher[3];
     390             463 :   size_t ix;
     391                 : 
     392             463 :   matcher[0].field = method_desc->name;
     393             463 :   matcher[0].terminator = ':';
     394             463 :   matcher[1].field = method_desc->input_types;
     395             463 :   matcher[1].terminator = ':';
     396             463 :   matcher[2].field = method_desc->output_types;
     397             463 :   matcher[2].terminator = '\0';
     398                 : 
     399            1664 :   for (ix = 0; ix < sizeof(matcher) / sizeof(matcher[0]); ++ix) {
     400             709 :     size_t length = strlen(matcher[ix].field);
     401            1085 :     if (strncmp(matcher[ix].field, signature, length) ||
     402                 :         matcher[ix].terminator != signature[length]) {
     403             340 :       return 0;
     404                 :     }
     405             369 :     signature += length + 1;
     406             369 :   }
     407             123 :   return 1;
     408             463 : }
     409                 : 
     410             123 : uint32_t NaClSrpcServiceMethodIndex(const NaClSrpcService* service,
     411             123 :                                     char const* signature) {
     412             123 :   uint32_t i;
     413                 : 
     414             123 :   if (NULL == service) {
     415               0 :     return kNaClSrpcInvalidMethodIndex;
     416                 :   }
     417             926 :   for (i = 0; i < service->rpc_count;  ++i) {
     418             463 :     if (SignatureMatches(&(service->rpc_descr[i]), signature)) {
     419             123 :       return i;
     420                 :     }
     421             340 :   }
     422               0 :   return kNaClSrpcInvalidMethodIndex;
     423             123 : }
     424                 : 
     425             443 : int NaClSrpcServiceMethodNameAndTypes(const NaClSrpcService* service,
     426             443 :                                       uint32_t rpc_number,
     427             443 :                                       const char** name,
     428             443 :                                       const char** input_types,
     429             443 :                                       const char** output_types) {
     430             443 :   if (rpc_number >= service->rpc_count) {
     431                 :     /* This ensures that the method is in the user-defined set. */
     432               0 :     return 0;
     433                 :   } else {
     434             443 :     *name = service->rpc_descr[rpc_number].name;
     435             443 :     *input_types = service->rpc_descr[rpc_number].input_types;
     436             443 :     *output_types = service->rpc_descr[rpc_number].output_types;
     437                 :   }
     438             443 :   return 1;
     439             443 : }
     440                 : 
     441              65 : NaClSrpcMethod NaClSrpcServiceMethod(const NaClSrpcService* service,
     442              65 :                                      uint32_t rpc_number) {
     443             130 :   if (NULL == service || rpc_number >= service->rpc_count) {
     444               0 :     return NULL;
     445                 :   } else {
     446              65 :     return service->rpc_descr[rpc_number].handler;
     447                 :   }
     448              65 : }
     449                 : 
     450              25 : static void ServiceDiscovery(NaClSrpcRpc* rpc,
     451              25 :                              NaClSrpcArg** in_args,
     452              25 :                              NaClSrpcArg** out_args,
     453              25 :                              NaClSrpcClosure* done) {
     454              50 :   UNREFERENCED_PARAMETER(in_args);
     455              25 :   rpc->result = NACL_SRPC_RESULT_APP_ERROR;
     456              50 :   if (NULL == rpc->channel || NULL == rpc->channel->server) {
     457               0 :     done->Run(done);
     458               0 :     return;
     459                 :   }
     460              25 :   if (out_args[0]->u.count >= rpc->channel->server->service_string_length) {
     461              75 :     strncpy(out_args[0]->arrays.carr,
     462                 :             rpc->channel->server->service_string,
     463                 :             rpc->channel->server->service_string_length);
     464                 :     /* Set the length of the string actually returned. */
     465              25 :     out_args[0]->u.count = rpc->channel->server->service_string_length;
     466              25 :     rpc->result = NACL_SRPC_RESULT_OK;
     467              25 :   }
     468              25 :   done->Run(done);
     469              50 : }

Generated by: LCOV version 1.7