2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * NaCl simple RPC over IMC mechanism.
37 #include "nacl_srpc.h"
38 #include "nacl_srpc_internal.h"
43 #include <sys/types.h>
47 * SRPC communicates using two main message types, requests and responses.
48 * Both are encoded with variable sizes, according to the table.
52 * rpc method index - 4 bytes
54 * #args * (arg value) - varying size
56 * #rets * (arg template) - varying size
60 * return code - 4 bytes
62 * #rets * (arg value) - varying size
74 * value - (length) * 1 bytes ('\0' is not sent)
77 * value - (length) * 1 bytes
80 * value - (length) * 4 bytes
83 * value - (length) * 8 bytes
106 * Support for timing the SRPC infrastructure.
108 static NaClSrpcError
GetTimes(NaClSrpcChannel
* channel
,
109 NaClSrpcArg
** in_args
,
110 NaClSrpcArg
** out_args
);
111 static NaClSrpcError
SetTimingEnabled(NaClSrpcChannel
* channel
,
112 NaClSrpcArg
** in_args
,
113 NaClSrpcArg
** out_args
);
117 * The high level APIs provided for external use.
119 NaClSrpcError
NaClSrpcReceiveAndDispatch(NaClSrpcChannel
* channel
) {
120 NaClSrpcArg
* args
[NACL_SRPC_MAX_ARGS
+1];
121 NaClSrpcArg
* rets
[NACL_SRPC_MAX_ARGS
+1];
122 const char* in_types
;
123 const char* out_types
;
125 NaClSrpcError retval
;
126 NaClSrpcError app_error
;
128 int return_break
= 0;
129 double this_start_usec
= 0.0;
130 double this_method_usec
;
133 * If we are timing, get the start time.
135 if (channel
->timing_enabled
) {
136 this_start_usec
= __NaClSrpcGetUsec();
140 * First we receive the parameters for the method
142 dprintf(("SERVER: receiving RPC request\n"));
143 retval
= __NaClSrpcImcRead(&client_protocol
,
144 sizeof(client_protocol
),
148 dprintf(("SERVER: RPC protocol version read failed: %d\n", retval
));
149 return NACL_SRPC_RESULT_INTERNAL
;
151 if (client_protocol
!= kSrpcProtocolVersion
) {
152 dprintf(("SERVER: client protocol %x does not match server %x\n",
153 client_protocol
, kSrpcProtocolVersion
));
154 return NACL_SRPC_RESULT_PROTOCOL_MISMATCH
;
156 dprintf(("SERVER: protocol versions matched\n"));
157 retval
= __NaClSrpcImcRead(&rpc_number
, sizeof(rpc_number
), 1, channel
);
159 dprintf(("SERVER: RPC rpc_number read error: %d %d\n", retval
, errno
));
161 return NACL_SRPC_RESULT_INTERNAL
;
163 dprintf(("SERVER: rpc number %u\n", (unsigned) rpc_number
));
164 if (rpc_number
== NACL_SRPC_SHUTDOWN_METHOD
) {
167 } else if (rpc_number
== NACL_SRPC_GET_TIMES_METHOD
) {
170 } else if (rpc_number
== NACL_SRPC_TOGGLE_CHANNEL_TIMING_METHOD
) {
173 } else if (rpc_number
>= channel
->rpc_count
) {
174 dprintf(("SERVER: RPC bad rpc number: %u not in [0, %u)\n",
175 (unsigned) rpc_number
, (unsigned) channel
->rpc_count
));
176 return NACL_SRPC_RESULT_BAD_RPC_NUMBER
;
178 in_types
= channel
->rpc_descr
[rpc_number
].in_args
;
179 out_types
= channel
->rpc_descr
[rpc_number
].out_args
;
181 if ((retval
= __NaClSrpcArgsGet(channel
, 1, 1, args
, in_types
))
182 != NACL_SRPC_RESULT_OK
) {
183 dprintf(("SERVER: argument vector receive failed: %d\n", retval
));
186 dprintf(("SERVER: argument vector received\n"));
187 if ((retval
= __NaClSrpcArgsGet(channel
, 1, 0, rets
, out_types
))
188 != NACL_SRPC_RESULT_OK
) {
189 dprintf(("SERVER: return descriptor receive failed: %d, %s\n", retval
,
190 NaClSrpcErrorString(retval
)));
193 dprintf(("SERVER: received RPC request\n"));
196 * Then we invoke the method, which computes a return code.
198 dprintf(("SERVER: invoking RPC %u\n", (unsigned) rpc_number
));
199 if (NACL_SRPC_SHUTDOWN_METHOD
== rpc_number
) {
200 app_error
= NACL_SRPC_RESULT_SHUTDOWN
;
201 } else if (NACL_SRPC_GET_TIMES_METHOD
== rpc_number
) {
202 app_error
= GetTimes(channel
, args
, rets
);
203 } else if (NACL_SRPC_TOGGLE_CHANNEL_TIMING_METHOD
== rpc_number
) {
204 app_error
= SetTimingEnabled(channel
, args
, rets
);
206 dprintf(("SERVER: handler %p\n", channel
->rpc_descr
[rpc_number
].handler
));
207 app_error
= (channel
->rpc_descr
[rpc_number
].handler
)(channel
, args
, rets
);
208 if (NACL_SRPC_RESULT_BREAK
== app_error
) {
210 app_error
= NACL_SRPC_RESULT_OK
;
213 dprintf(("SERVER: performed RPC\n"));
216 * Then we return the response
218 dprintf(("SERVER: sending response\n"));
219 __NaClSrpcImcWrite(&kSrpcProtocolVersion
,
220 sizeof(kSrpcProtocolVersion
),
223 dprintf(("SERVER: sent protocol version\n"));
224 __NaClSrpcImcWrite(&app_error
, sizeof(app_error
), 1, channel
);
225 dprintf(("SERVER: RPC returned code %d\n", app_error
));
226 if ((retval
= __NaClSrpcArgsPut(channel
, 1, rets
)) != NACL_SRPC_RESULT_OK
) {
227 dprintf(("SERVER: return value(s) send failed: %d\n", retval
));
230 dprintf(("SERVER: return value(s) sent\n"));
231 __NaClSrpcImcFlush(channel
);
232 dprintf(("SERVER: sent response\n"));
235 * Then we free any memory that might have been allocated for array-typed
236 * arguments or returns.
238 __NaClSrpcArgsFree(args
);
239 __NaClSrpcArgsFree(rets
);
240 dprintf(("SERVER: freed memory\n"));
243 * If we are timing, collect the current time, compute the delta from
244 * the start, and update the cumulative counter.
246 if (channel
->timing_enabled
) {
247 this_method_usec
= __NaClSrpcGetUsec();
248 channel
->receive_usec
+= this_method_usec
;
251 if (NACL_SRPC_SHUTDOWN_METHOD
== rpc_number
||
252 NACL_SRPC_RESULT_SHUTDOWN
== app_error
) {
253 return NACL_SRPC_RESULT_SHUTDOWN
;
254 } else if (return_break
) {
255 return NACL_SRPC_RESULT_BREAK
;
257 return NACL_SRPC_RESULT_OK
;
261 void __NaClSrpcArgsFree(NaClSrpcArg
* argvec
[]) {
262 NaClSrpcArg
** argvecp
;
264 for (argvecp
= argvec
; *argvecp
!= NULL
; ++argvecp
) {
265 switch ((*argvecp
)->tag
) {
266 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
267 free((*argvecp
)->u
.caval
.carr
);
269 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
270 free((*argvecp
)->u
.daval
.darr
);
272 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
273 free((*argvecp
)->u
.iaval
.iarr
);
275 case NACL_SRPC_ARG_TYPE_INVALID
:
276 case NACL_SRPC_ARG_TYPE_BOOL
:
277 case NACL_SRPC_ARG_TYPE_DOUBLE
:
278 case NACL_SRPC_ARG_TYPE_HANDLE
:
279 case NACL_SRPC_ARG_TYPE_INT
:
281 case NACL_SRPC_ARG_TYPE_STRING
:
282 free((*argvecp
)->u
.sval
);
285 * The two cases below are added to avoid warnings, they are only used
288 case NACL_SRPC_ARG_TYPE_OBJECT
:
289 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY
:
297 char* NaClSrpcErrorString(NaClSrpcError error_code
) {
298 switch (error_code
) {
299 case NACL_SRPC_RESULT_OK
:
301 case NACL_SRPC_RESULT_BREAK
:
302 return "Break out of server RPC loop";
303 case NACL_SRPC_RESULT_SHUTDOWN
:
304 return "Connection shut down by other end";
305 case NACL_SRPC_RESULT_INTERNAL
:
306 return "Internal error in rpc method";
307 case NACL_SRPC_RESULT_BAD_SERVICE_DISCOVERY
:
308 return "Service discovery failed";
309 case NACL_SRPC_RESULT_BAD_RPC_NUMBER
:
310 return "No method for the given rpc number";
311 case NACL_SRPC_RESULT_BAD_ARG_TYPE
:
312 return "Bad argument type received";
313 case NACL_SRPC_RESULT_TOO_MANY_ARGS
:
314 return "Too many arguments (more than NACL_SRPC_MAX_ARGS or declared)";
315 case NACL_SRPC_RESULT_TOO_FEW_ARGS
:
316 return "Too few arguments (fewer than declared)";
317 case NACL_SRPC_RESULT_IN_ARG_TYPE_MISMATCH
:
318 return "Input argument type mismatch";
319 case NACL_SRPC_RESULT_OUT_ARG_TYPE_MISMATCH
:
320 return "Output argument type mismatch";
321 case NACL_SRPC_RESULT_NO_MEMORY
:
322 return "Out of memory";
323 case NACL_SRPC_RESULT_APP_ERROR
:
324 return "Rpc application returned an error";
325 case NACL_SRPC_RESULT_STRING_OUTPUT
:
326 return "Strings may not be used as output arguments";
327 case NACL_SRPC_RESULT_EOF_ON_STREAM
:
328 return "End of file encountered";
329 case NACL_SRPC_RESULT_PROTOCOL_MISMATCH
:
330 return "Client and server have different protocol versions";
332 return "Unrecognized NaClSrpcError value";
336 * Static function definitions.
338 NaClSrpcError
__NaClSrpcArgsGet(NaClSrpcChannel
* channel
,
341 NaClSrpcArg
* argvec
[],
342 const char* arg_types
) {
346 int retval
= NACL_SRPC_RESULT_OK
;
347 int i
= 0; /* must be initialized here to get proper error handling. */
349 retval
= __NaClSrpcImcRead(&length
, sizeof(length
), 1, channel
);
351 return NACL_SRPC_RESULT_INTERNAL
;
353 if (length
>= NACL_SRPC_MAX_ARGS
) {
355 * It is an error if the argument length exceeds the length of the
358 return NACL_SRPC_RESULT_TOO_MANY_ARGS
;
361 dprintf(("GET: length %d\n", length
));
363 if (allocate_args
&& length
> 0) {
366 args
= (NaClSrpcArg
*) malloc(length
* sizeof(NaClSrpcArg
));
368 retval
= NACL_SRPC_RESULT_NO_MEMORY
;
371 memset((void*) args
, 0, length
* sizeof(NaClSrpcArg
));
374 * Initialize the arg type tags with those specified in the declaration.
376 for (i
= 0; i
< length
; ++i
) {
377 if (arg_types
[i
] == ':' || arg_types
[i
] == '\0') {
378 return NACL_SRPC_RESULT_TOO_MANY_ARGS
;
380 args
[i
].tag
= arg_types
[i
];
382 if (arg_types
[length
] == ':' && arg_types
[length
] == '\0') {
383 return NACL_SRPC_RESULT_TOO_FEW_ARGS
;
389 for (; i
< length
; ++i
) {
392 retval
= __NaClSrpcImcRead(&read_type
, sizeof(char), 1, channel
);
394 retval
= NACL_SRPC_RESULT_INTERNAL
;
397 if (args
[i
].tag
!= read_type
) {
398 dprintf(("arg[%d]: tag %d, expected %d\n", i
, read_type
, args
[i
].tag
));
399 retval
= NACL_SRPC_RESULT_BAD_ARG_TYPE
;
403 argvec
[i
] = args
+ i
;
405 switch (args
[i
].tag
) {
406 case NACL_SRPC_ARG_TYPE_INVALID
:
407 retval
= NACL_SRPC_RESULT_BAD_ARG_TYPE
;
409 case NACL_SRPC_ARG_TYPE_BOOL
:
411 retval
= __NaClSrpcImcRead(&args
[i
].u
.bval
, sizeof(char), 1, channel
);
413 retval
= NACL_SRPC_RESULT_INTERNAL
;
417 dprintf(("value %d\n", args
[i
].u
.bval
));
419 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
420 retval
= __NaClSrpcImcRead(&dim
, sizeof(dim
), 1, channel
);
422 retval
= NACL_SRPC_RESULT_INTERNAL
;
425 dprintf(("dim %u\n", (unsigned) dim
));
427 args
[i
].u
.caval
.carr
= (char*) malloc(dim
* sizeof(char));
428 if (args
[i
].u
.caval
.carr
== NULL
) {
429 retval
= NACL_SRPC_RESULT_NO_MEMORY
;
432 args
[i
].u
.caval
.count
= dim
;
434 if (args
[i
].u
.caval
.count
< dim
) {
435 return NACL_SRPC_RESULT_TOO_MANY_ARGS
;
439 retval
= __NaClSrpcImcRead(args
[i
].u
.caval
.carr
, 1, dim
, channel
);
441 return NACL_SRPC_RESULT_INTERNAL
;
445 case NACL_SRPC_ARG_TYPE_DOUBLE
:
447 retval
= __NaClSrpcImcRead(&args
[i
].u
.dval
, sizeof(double), 1, channel
);
449 return NACL_SRPC_RESULT_INTERNAL
;
452 dprintf(("value %f\n", args
[i
].u
.dval
));
454 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
455 retval
= __NaClSrpcImcRead(&dim
, sizeof(dim
), 1, channel
);
457 retval
= NACL_SRPC_RESULT_INTERNAL
;
460 dprintf(("dim %u\n", (unsigned) dim
));
462 args
[i
].u
.daval
.darr
= (double*) malloc(dim
* sizeof(double));
463 if (args
[i
].u
.daval
.darr
== NULL
) {
464 retval
= NACL_SRPC_RESULT_NO_MEMORY
;
467 args
[i
].u
.daval
.count
= dim
;
469 if (args
[i
].u
.daval
.count
< dim
) {
470 return NACL_SRPC_RESULT_TOO_MANY_ARGS
;
474 retval
= __NaClSrpcImcRead(args
[i
].u
.daval
.darr
,
479 return NACL_SRPC_RESULT_INTERNAL
;
483 case NACL_SRPC_ARG_TYPE_HANDLE
:
485 args
[i
].u
.hval
= __NaClSrpcImcReadDesc(channel
);
488 case NACL_SRPC_ARG_TYPE_INT
:
490 retval
= __NaClSrpcImcRead(&args
[i
].u
.ival
, sizeof(int), 1, channel
);
492 return NACL_SRPC_RESULT_INTERNAL
;
495 dprintf(("value %d\n", args
[i
].u
.ival
));
497 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
498 retval
= __NaClSrpcImcRead(&dim
, sizeof(dim
), 1, channel
);
500 retval
= NACL_SRPC_RESULT_INTERNAL
;
503 dprintf(("dim %u\n", (unsigned) dim
));
505 args
[i
].u
.iaval
.iarr
= (int*) malloc(dim
* sizeof(int));
506 if (args
[i
].u
.iaval
.iarr
== NULL
) {
507 retval
= NACL_SRPC_RESULT_NO_MEMORY
;
510 args
[i
].u
.iaval
.count
= dim
;
512 if (args
[i
].u
.iaval
.count
< dim
) {
513 return NACL_SRPC_RESULT_TOO_MANY_ARGS
;
517 retval
= __NaClSrpcImcRead(args
[i
].u
.iaval
.iarr
,
522 return NACL_SRPC_RESULT_INTERNAL
;
526 case NACL_SRPC_ARG_TYPE_STRING
:
528 retval
= __NaClSrpcImcRead(&dim
, sizeof(dim
), 1, channel
);
530 retval
= NACL_SRPC_RESULT_INTERNAL
;
533 args
[i
].u
.sval
= (char*) malloc(dim
+ 1);
534 if (args
[i
].u
.sval
== NULL
) {
535 retval
= NACL_SRPC_RESULT_NO_MEMORY
;
538 retval
= __NaClSrpcImcRead(args
[i
].u
.sval
, sizeof(char), dim
, channel
);
540 retval
= NACL_SRPC_RESULT_INTERNAL
;
543 args
[i
].u
.sval
[dim
] = '\0';
547 * The two cases below are added to avoid warnings, they are only used
550 case NACL_SRPC_ARG_TYPE_OBJECT
:
551 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY
:
553 retval
= NACL_SRPC_RESULT_BAD_ARG_TYPE
;
557 argvec
[length
] = NULL
;
558 return NACL_SRPC_RESULT_OK
;
561 __NaClSrpcArgsFree(argvec
);
566 NaClSrpcError
__NaClSrpcArgsPut(NaClSrpcChannel
* channel
,
568 NaClSrpcArg
* argvec
[]) {
574 for (length
= 0; argvec
[length
] != NULL
; ++length
);
575 if (length
>= NACL_SRPC_MAX_ARGS
) {
577 * It is an error if the argument length exceeds the length of the
580 return NACL_SRPC_RESULT_TOO_MANY_ARGS
;
582 __NaClSrpcImcWrite(&length
, sizeof(length
), 1, channel
);
583 dprintf(("PUT: length %d\n", length
));
585 for (i
= 0; i
< length
; ++i
) {
587 __NaClSrpcImcWrite(&arg
->tag
, sizeof(char), 1, channel
);
589 dprintf(("PUT: arg[%d]: tag %d, ", i
, arg
->tag
));
592 case NACL_SRPC_ARG_TYPE_INVALID
:
594 case NACL_SRPC_ARG_TYPE_BOOL
:
595 dprintf(("value %d\n", arg
->u
.bval
));
597 __NaClSrpcImcWrite(&arg
->u
.bval
, sizeof(char), 1, channel
);
600 case NACL_SRPC_ARG_TYPE_CHAR_ARRAY
:
601 dprintf(("dim %u\n", (unsigned) arg
->u
.caval
.count
));
602 __NaClSrpcImcWrite(&arg
->u
.caval
.count
, sizeof(uint32_t), 1, channel
);
603 dprintf(("PUT: sent length, carr = %p\n", arg
->u
.caval
.carr
));
605 __NaClSrpcImcWrite(arg
->u
.caval
.carr
, 1, arg
->u
.caval
.count
, channel
);
608 case NACL_SRPC_ARG_TYPE_DOUBLE
:
609 dprintf(("value %f\n", arg
->u
.dval
));
611 __NaClSrpcImcWrite(&arg
->u
.dval
, sizeof(double), 1, channel
);
614 case NACL_SRPC_ARG_TYPE_DOUBLE_ARRAY
:
615 dprintf(("dim %u\n", (unsigned) arg
->u
.daval
.count
));
616 __NaClSrpcImcWrite(&arg
->u
.daval
.count
, sizeof(uint32_t), 1, channel
);
617 dprintf(("PUT: sent length, darr = %p\n", arg
->u
.daval
.darr
));
619 __NaClSrpcImcWrite(arg
->u
.daval
.darr
,
625 case NACL_SRPC_ARG_TYPE_HANDLE
:
626 dprintf(("value %d\n", (int) arg
->u
.hval
));
628 __NaClSrpcImcWriteDesc(channel
, arg
->u
.hval
);
631 case NACL_SRPC_ARG_TYPE_INT
:
632 dprintf(("value %d\n", arg
->u
.ival
));
634 __NaClSrpcImcWrite(&arg
->u
.ival
, sizeof(int), 1, channel
);
637 case NACL_SRPC_ARG_TYPE_INT_ARRAY
:
638 dprintf(("dim %u\n", (unsigned) arg
->u
.iaval
.count
));
639 __NaClSrpcImcWrite(&arg
->u
.iaval
.count
, sizeof(uint32_t), 1, channel
);
640 dprintf(("PUT: sent length, iarr = %p\n", arg
->u
.iaval
.iarr
));
642 __NaClSrpcImcWrite(arg
->u
.iaval
.iarr
,
648 case NACL_SRPC_ARG_TYPE_STRING
:
650 slen
= strlen(arg
->u
.sval
);
651 dprintf(("strlen %u\n", (unsigned) slen
));
652 __NaClSrpcImcWrite(&slen
, sizeof(slen
), 1, channel
);
653 __NaClSrpcImcWrite(arg
->u
.sval
, 1, strlen(arg
->u
.sval
), channel
);
657 * The two cases below are added to avoid warnings, they are only used
660 case NACL_SRPC_ARG_TYPE_OBJECT
:
661 case NACL_SRPC_ARG_TYPE_VARIANT_ARRAY
:
665 dprintf(("PUT: sent value\n"));
667 return NACL_SRPC_RESULT_OK
;
670 static NaClSrpcError
GetTimes(NaClSrpcChannel
* channel
,
671 NaClSrpcArg
** in_args
,
672 NaClSrpcArg
** out_args
)
674 NaClSrpcGetTimes(channel
,
675 &out_args
[0]->u
.dval
,
676 &out_args
[1]->u
.dval
,
677 &out_args
[2]->u
.dval
,
678 &out_args
[3]->u
.dval
);
679 return NACL_SRPC_RESULT_OK
;
682 static NaClSrpcError
SetTimingEnabled(NaClSrpcChannel
* channel
,
683 NaClSrpcArg
** in_args
,
684 NaClSrpcArg
** out_args
)
686 NaClSrpcToggleChannelTiming(channel
, in_args
[0]->u
.ival
);
687 return NACL_SRPC_RESULT_OK
;
690 void NaClSrpcToggleChannelTiming(NaClSrpcChannel
* channel
, int enable_timing
) {
691 channel
->timing_enabled
= enable_timing
;
694 void NaClSrpcGetTimes(NaClSrpcChannel
* channel
,
696 double* receive_time
,
697 double* imc_read_time
,
698 double* imc_write_time
) {
699 *send_time
= channel
->send_usec
;
700 *receive_time
= channel
->receive_usec
;
701 *imc_read_time
= channel
->imc_read_usec
;
702 *imc_write_time
= channel
->imc_write_usec
;