imcplugin: Tidy: remove debugging print() statements
[nativeclient.git] / imcplugin / imcplugin.c
blob0b24b02c1dadd913d387532c49d1ca1d73dba7e7
2 #include <stdio.h>
3 #include <string.h>
5 #include <fcntl.h>
6 #include <pthread.h>
7 #include <unistd.h>
9 #include <npapi.h>
10 #include <npupp.h>
11 #include <npruntime.h>
13 #include "native_client/intermodule_comm/nacl_imc_c.h"
14 #include "native_client/service_runtime/include/sys/fcntl.h"
15 #include "native_client/service_runtime/nacl_desc_base.h"
16 #include "native_client/service_runtime/nacl_desc_imc.h"
17 #include "native_client/service_runtime/nacl_desc_io.h"
18 #include "native_client/service_runtime/nrd_xfer_lib/nrd_all_modules.h"
19 #include "native_client/service_runtime/nrd_xfer_lib/nrd_xfer.h"
20 #include "native_client/service_runtime/nrd_xfer_lib/nrd_xfer_effector.h"
23 static NPNetscapeFuncs g_funcs;
24 static NPIdentifier ident_launch;
25 static NPIdentifier ident_get_file;
26 static NPIdentifier ident_send;
27 static NPIdentifier ident_length;
29 #define NPN_CreateObject (g_funcs.createobject)
30 #define NPN_RetainObject (g_funcs.retainobject)
31 #define NPN_ReleaseObject (g_funcs.releaseobject)
32 #define NPN_ReleaseVariantValue (g_funcs.releasevariantvalue)
33 #define NPN_InvokeDefault (g_funcs.invokeDefault)
34 #define NPN_GetProperty (g_funcs.getproperty)
35 #define NPN_UTF8FromIdentifier (g_funcs.utf8fromidentifier)
36 #define NPN_GetStringIdentifier (g_funcs.getstringidentifier)
37 #define NPN_GetIntIdentifier (g_funcs.getintidentifier)
38 #define NPN_GetURLNotify (g_funcs.geturlnotify)
39 #define NPN_PluginThreadAsyncCall (g_funcs.pluginthreadasynccall)
42 static void PrintIdentifier(NPIdentifier ident)
44 char *str = NPN_UTF8FromIdentifier(ident);
45 printf(" ident = \"%s\"\n", str);
46 free(str);
49 static bool
50 IsType(NPObject *npobj, NPClass *npclass)
52 /* The underscore in "_class" suggests this member is not part of
53 the public API, but it could just be to prevent a clash with the
54 C++ keyword. */
55 return npobj->_class == npclass;
59 /* Mozilla does not provide default behaviour for these NoOp_* methods
60 if NULL is provided instead in the NPClass. */
62 static bool
63 NoOp_HasMethod(NPObject* obj, NPIdentifier methodName)
65 return false;
68 static bool
69 NoOp_HasProperty(NPObject *obj, NPIdentifier propertyName)
71 return false;
74 static bool
75 NoOp_GetProperty(NPObject *obj, NPIdentifier propertyName, NPVariant *result)
77 return false;
81 struct FileObj {
82 struct NPObject header;
83 struct NaClDesc *desc;
84 /* We keep a filename to pass to sel_ldr, but there's a chance the
85 file could be deleted from the cache.
86 TODO: pass FDs to sel_ldr instead. */
87 char *filename;
90 static NPObject *
91 FileObj_Allocate(NPP npp, NPClass *npclass)
93 struct FileObj *obj = malloc(sizeof(struct FileObj));
94 return &obj->header;
97 static void
98 FileObj_Deallocate(NPObject *npobj)
100 struct FileObj *obj = (struct FileObj *) npobj;
101 NaClDescUnref(obj->desc);
102 free(obj->filename);
103 free(obj);
106 static NPClass FileObj_Vtable = {
107 .structVersion = NP_CLASS_STRUCT_VERSION,
108 .allocate = FileObj_Allocate,
109 .deallocate = FileObj_Deallocate,
110 .invalidate = NULL,
111 .hasMethod = NoOp_HasMethod,
112 .invoke = NULL,
113 .invokeDefault = NULL,
114 .hasProperty = NoOp_HasProperty,
115 .getProperty = NoOp_GetProperty,
116 .setProperty = NULL,
117 .removeProperty = NULL,
121 struct SendObj {
122 struct NPObject header;
123 NPP plugin;
124 struct NaClDesc *desc;
127 static NPObject *
128 SendObj_Allocate(NPP npp, NPClass *npclass)
130 struct SendObj *obj = malloc(sizeof(struct SendObj));
131 obj->plugin = npp;
132 return &obj->header;
135 static void
136 SendObj_Deallocate(NPObject *npobj)
138 struct SendObj *obj = (struct SendObj *) npobj;
139 NaClDescUnref(obj->desc);
140 free(obj);
143 static bool
144 SendObj_HasMethod(NPObject* npobj, NPIdentifier methodName)
146 return methodName == ident_send;
149 static bool
150 ConvertDescArray(NPP plugin, NPObject *array, struct NaClImcTypedMsgHdr *header)
152 NPVariant length_np;
153 if(!NPN_GetProperty(plugin, array, ident_length, &length_np) ||
154 !NPVARIANT_IS_INT32(length_np)) {
155 NPN_ReleaseVariantValue(&length_np);
156 return false;
158 int length = NPVARIANT_TO_INT32(length_np);
159 NPN_ReleaseVariantValue(&length_np);
161 /* Firstly, there will be a limit to the number of capabilities we
162 can send in a message. Secondly, the array is not necessarily a
163 primitive Javascript array; its .length property could be much
164 larger than the amount of space Javascript is normally allowed to
165 allocate. */
166 if(length > 32)
167 return false;
169 struct NaClDesc **descs = malloc(sizeof(struct NaClDesc *) * length);
170 if(descs == NULL)
171 return false;
172 int i;
173 for(i = 0; i < length; i++) {
174 NPIdentifier index_id = NPN_GetIntIdentifier(i);
175 NPVariant element;
176 if(!NPN_GetProperty(plugin, array, index_id, &element) ||
177 !NPVARIANT_IS_OBJECT(element))
178 return false; /* TODO: clear up */
179 NPObject *file_npobj = NPVARIANT_TO_OBJECT(element);
180 if(!IsType(file_npobj, &FileObj_Vtable))
181 return false; /* TODO: clear up */
182 struct FileObj *file_obj = (struct FileObj *) file_npobj;
183 NaClDescRef(file_obj->desc);
184 descs[i] = file_obj->desc;
185 NPN_ReleaseVariantValue(&element);
187 header->ndescv = descs;
188 header->ndesc_length = length;
189 return true;
192 static bool
193 SendObj_Invoke(NPObject *npobj, NPIdentifier methodName,
194 const NPVariant *args, uint32_t argCount, NPVariant *result)
196 struct SendObj *obj = (struct SendObj *) npobj;
197 if(methodName == ident_send) {
198 if(argCount == 2 &&
199 NPVARIANT_IS_STRING(args[0]) &&
200 NPVARIANT_IS_OBJECT(args[1])) {
201 NPString message_data = NPVARIANT_TO_STRING(args[0]);
202 NPObject *caps_array = NPVARIANT_TO_OBJECT(args[1]);
203 struct NaClNrdXferEffector effector;
204 if(!NaClNrdXferEffectorCtor(&effector, obj->desc))
205 return false;
206 struct NaClImcMsgIoVec iovec;
207 struct NaClImcTypedMsgHdr header;
208 if(!ConvertDescArray(obj->plugin, caps_array, &header))
209 return false;
210 /* TODO: allow sending any data, not just UTF-8 */
211 iovec.base = (char *) message_data.utf8characters;
212 iovec.length = message_data.utf8length;
213 header.iov = &iovec;
214 header.iov_length = 1;
215 header.flags = 0;
216 NaClImcSendTypedMessage(obj->desc, (struct NaClDescEffector *) &effector,
217 &header, 0);
218 int i;
219 for(i = 0; i < header.ndesc_length; i++)
220 NaClDescUnref(header.ndescv[i]);
221 free(header.ndescv);
222 effector.base.vtbl->Dtor(&effector.base);
224 VOID_TO_NPVARIANT(*result);
225 return true;
227 return false;
230 static NPClass SendObj_Vtable = {
231 .structVersion = NP_CLASS_STRUCT_VERSION,
232 .allocate = SendObj_Allocate,
233 .deallocate = SendObj_Deallocate,
234 .invalidate = NULL,
235 .hasMethod = SendObj_HasMethod,
236 .invoke = SendObj_Invoke,
237 .invokeDefault = NULL,
238 .hasProperty = NoOp_HasProperty,
239 .getProperty = NoOp_GetProperty,
240 .setProperty = NULL,
241 .removeProperty = NULL,
245 struct LauncherObj {
246 struct NPObject header;
247 NPP plugin;
250 static NPObject *
251 LauncherObj_Allocate(NPP npp, NPClass *npclass)
253 struct LauncherObj *obj = malloc(sizeof(struct LauncherObj));
254 obj->plugin = npp;
255 return &obj->header;
258 static bool
259 LauncherObj_HasMethod(NPObject* obj, NPIdentifier methodName)
261 return (methodName == ident_launch ||
262 methodName == ident_get_file);
265 struct ReceiveCallbackArgs {
266 NPP plugin;
267 NPObject *callback;
268 char buffer[1024];
269 int size;
272 static void
273 DoReceiveCallback(void *handle)
275 struct ReceiveCallbackArgs *call = handle;
276 NPVariant args[1];
277 NPVariant result;
278 /* TODO: don't assume this data is UTF-8 */
279 STRINGN_TO_NPVARIANT(call->buffer, call->size, args[0]);
280 NPN_InvokeDefault(call->plugin, call->callback, args, 1, &result);
281 NPN_ReleaseVariantValue(&result);
282 free(call);
283 /* We don't do NPN_ReleaseObject() on the callback here because we
284 couldn't do NPN_RetainObject() earlier. */
287 struct ReceiveThreadArgs {
288 NPP plugin;
289 NPObject *callback;
290 struct NaClDesc *desc;
293 static void *
294 ReceiveThread(void *handle)
296 /* We create one thread for every socket we listen on. It would be
297 more efficient to have one thread using poll(). NPAPI doesn't
298 provide a way for us to share the browser's event loop. */
299 struct ReceiveThreadArgs *args = handle;
300 struct NaClNrdXferEffector effector;
301 if(!NaClNrdXferEffectorCtor(&effector, args->desc))
302 return NULL;
303 while(true) {
304 struct ReceiveCallbackArgs *call =
305 malloc(sizeof(struct ReceiveCallbackArgs));
306 if(call == NULL)
307 break;
308 struct NaClImcMsgIoVec iovec;
309 struct NaClImcTypedMsgHdr header;
310 iovec.base = call->buffer;
311 iovec.length = sizeof(call->buffer);
312 header.iov = &iovec;
313 header.iov_length = 1;
314 header.ndescv = NULL;
315 header.ndesc_length = 0;
316 header.flags = 0;
317 int got_bytes =
318 NaClImcRecvTypedMessage(args->desc, (struct NaClDescEffector *) &effector,
319 &header, 0);
320 if(got_bytes < 0) {
321 free(call);
322 break;
324 /* We can't call NPN_RetainObject() here because it is not safe to
325 call it from this thread. We assume that
326 NPN_PluginThreadAsyncCall() schedules functions to be called in
327 order, so that the function is not freed until later. */
328 call->plugin = args->plugin;
329 call->callback = args->callback;
330 call->size = got_bytes;
331 NPN_PluginThreadAsyncCall(args->plugin, DoReceiveCallback, call);
333 effector.base.vtbl->Dtor(&effector.base);
334 NaClDescUnref(args->desc); /* Supposed to be thread safe */
335 NPN_PluginThreadAsyncCall(args->plugin, (void (*)(void *)) NPN_ReleaseObject,
336 args->callback);
337 free(args);
338 return NULL;
341 static bool
342 ConvertArgvArray(NPP plugin, NPObject *array,
343 char ***result_argv, int *result_size)
345 NPVariant length_np;
346 if(!NPN_GetProperty(plugin, array, ident_length, &length_np) ||
347 !NPVARIANT_IS_INT32(length_np)) {
348 NPN_ReleaseVariantValue(&length_np);
349 return false;
351 int length = NPVARIANT_TO_INT32(length_np);
352 NPN_ReleaseVariantValue(&length_np);
354 /* Arbitrary limit */
355 if(length > 100)
356 return false;
358 char **argv = malloc(sizeof(char *) * length);
359 if(argv == NULL)
360 return false;
361 int i;
362 for(i = 0; i < length; i++) {
363 NPIdentifier index_id = NPN_GetIntIdentifier(i);
364 NPVariant element;
365 if(!NPN_GetProperty(plugin, array, index_id, &element) ||
366 !NPVARIANT_IS_STRING(element))
367 return false; /* TODO: clear up */
368 NPString string = NPVARIANT_TO_STRING(element);
369 char *copy = malloc(string.utf8length + 1);
370 if(copy == NULL)
371 return false; /* TODO: clear up */
372 /* TODO: allow sending any data, not just UTF-8 */
373 memcpy(copy, string.utf8characters, string.utf8length);
374 copy[string.utf8length] = 0;
375 argv[i] = copy;
376 NPN_ReleaseVariantValue(&element);
378 *result_argv = argv;
379 *result_size = length;
380 return true;
383 static struct NaClDesc *
384 LaunchProcess(NPP plugin, const char *executable, char **argv, int argv_size,
385 NPObject *receive_callback)
387 int socks[2];
388 if(NaClSocketPair(socks) < 0) {
389 perror("socketpair");
390 return NULL;
392 int pid = fork();
393 if(pid < 0) {
394 NaClClose(socks[0]);
395 NaClClose(socks[1]);
396 perror("fork");
397 return NULL; /* TODO: report error */
399 if(pid == 0) {
400 NaClClose(socks[1]);
401 char fd_buffer[40];
402 snprintf(fd_buffer, sizeof(fd_buffer), "%i:%i", 3, socks[0]);
404 int args_len = 9 + argv_size;
405 const char *args[args_len];
406 int i = 0;
407 args[i++] = "sel_ldr";
408 args[i++] = "-i";
409 args[i++] = fd_buffer;
410 args[i++] = "-E";
411 args[i++] = "NACL_FD=3";
412 args[i++] = "-f";
413 args[i++] = executable;
414 args[i++] = "--";
415 int j;
416 for(j = 0; j < argv_size; j++)
417 args[i++] = argv[j];
418 args[i++] = NULL;
419 assert(i == args_len);
421 execvp("sel_ldr", (char **) args);
422 perror("exec");
423 _exit(1);
425 NaClClose(socks[0]);
426 if(fcntl(socks[1], F_SETFD, FD_CLOEXEC) < 0) {
427 perror("fcntl");
428 return NULL;
431 struct NaClDesc *desc = malloc(sizeof(struct NaClDescImcDesc));
432 if(!NaClDescImcDescCtor((struct NaClDescImcDesc *) desc, socks[1]))
433 return NULL;
435 struct ReceiveThreadArgs *args = malloc(sizeof(struct ReceiveThreadArgs));
436 if(args == NULL)
437 return NULL;
438 NaClDescRef(desc);
439 NPN_RetainObject(receive_callback);
440 args->plugin = plugin;
441 args->desc = desc;
442 args->callback = receive_callback;
443 pthread_t thread_id;
444 pthread_attr_t attr;
445 pthread_attr_init(&attr);
446 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
447 if(pthread_create(&thread_id, &attr, ReceiveThread, args) != 0)
448 return NULL;
450 return desc;
453 static bool
454 LauncherObj_Invoke(NPObject *npobj, NPIdentifier methodName,
455 const NPVariant *args, uint32_t argCount, NPVariant *result)
457 struct LauncherObj *obj = (struct LauncherObj *) npobj;
458 if(methodName == ident_launch) {
459 if(argCount == 3 &&
460 NPVARIANT_IS_OBJECT(args[0]) &&
461 NPVARIANT_IS_OBJECT(args[1]) &&
462 NPVARIANT_IS_OBJECT(args[2])) {
463 NPObject *file_npobj = NPVARIANT_TO_OBJECT(args[0]);
464 NPObject *argv_obj = NPVARIANT_TO_OBJECT(args[1]);
465 NPObject *receive_callback = NPVARIANT_TO_OBJECT(args[2]);
466 char **argv;
467 int argv_size;
468 if(IsType(file_npobj, &FileObj_Vtable) &&
469 ConvertArgvArray(obj->plugin, argv_obj, &argv, &argv_size)) {
470 struct FileObj *file_obj = (struct FileObj *) file_npobj;
471 struct NaClDesc *desc = LaunchProcess(obj->plugin, file_obj->filename,
472 argv, argv_size,
473 receive_callback);
474 int i;
475 for(i = 0; i < argv_size; i++)
476 free(argv[i]);
477 free(argv);
479 if(desc != NULL) {
480 NPObject *send_npobj = NPN_CreateObject(obj->plugin, &SendObj_Vtable);
481 struct SendObj *send_obj = (struct SendObj *) send_npobj;
482 send_obj->desc = desc;
483 OBJECT_TO_NPVARIANT(send_npobj, *result);
484 return true;
488 VOID_TO_NPVARIANT(*result);
489 return true;
491 if(methodName == ident_get_file) {
492 if(argCount == 2 &&
493 NPVARIANT_IS_STRING(args[0]) &&
494 NPVARIANT_IS_OBJECT(args[1])) {
495 /* TODO: do same-origin check (assuming they are useful). */
496 NPString url = NPVARIANT_TO_STRING(args[0]);
497 NPObject *callback = NPVARIANT_TO_OBJECT(args[1]);
498 NPN_RetainObject(callback);
499 /* TODO: I don't think utf8characters is guaranteed to be
500 NULL-terminated. */
501 NPError err = NPN_GetURLNotify(obj->plugin, url.utf8characters,
502 NULL, callback);
504 /* TODO: handle errors */
505 VOID_TO_NPVARIANT(*result);
506 return true;
508 return false;
511 static NPClass LauncherObj_Vtable = {
512 .structVersion = NP_CLASS_STRUCT_VERSION,
513 .allocate = LauncherObj_Allocate,
514 .deallocate = NULL,
515 .invalidate = NULL,
516 .hasMethod = LauncherObj_HasMethod,
517 .invoke = LauncherObj_Invoke,
518 .invokeDefault = NULL,
519 .hasProperty = NoOp_HasProperty,
520 .getProperty = NoOp_GetProperty,
521 .setProperty = NULL,
522 .removeProperty = NULL,
526 NPError
527 NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode,
528 int16 argc, char **argn, char **argv, NPSavedData *saved)
530 return NPERR_NO_ERROR;
533 NPError
534 NPP_Destroy(NPP instance, NPSavedData **save)
536 /* TODO: kill sel_ldr subprocesses, and do waitpid() to avoid a zombie */
537 return NPERR_NO_ERROR;
540 NPError
541 NPP_GetValue(NPP instance, NPPVariable variable, void *result)
543 switch(variable) {
544 case NPPVpluginScriptableNPObject:
546 NPObject *obj = NPN_CreateObject(instance, &LauncherObj_Vtable);
547 *(NPObject **) result = obj;
548 break;
550 default:
551 return NPERR_GENERIC_ERROR;
553 return NPERR_NO_ERROR;
556 NPError
557 NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream,
558 NPBool seekable, uint16 *stype)
560 *stype = NP_ASFILEONLY;
561 return NPERR_NO_ERROR;
564 void
565 NPP_StreamAsFile(NPP instance, NPStream *stream, const char *filename)
567 /* TODO: this function calls LOG_FATAL on error; not good */
568 struct NaClDesc *desc = (struct NaClDesc *)
569 NaClDescIoDescOpen((char *) filename, NACL_ABI_O_RDONLY, 0);
570 if(desc == NULL)
571 return;
573 NPObject *callback = stream->notifyData;
574 NPObject *file_npobj = NPN_CreateObject(instance, &FileObj_Vtable);
575 struct FileObj *file_obj = (struct FileObj *) file_npobj;
576 file_obj->desc = desc;
577 file_obj->filename = strdup(filename);
578 if(file_obj->filename == NULL)
579 return;
580 NPVariant result;
581 NPVariant args[1];
582 OBJECT_TO_NPVARIANT(file_npobj, args[0]);
583 bool success = NPN_InvokeDefault(instance, callback, args, 1, &result);
584 NPN_ReleaseVariantValue(&result);
585 NPN_ReleaseObject(callback);
586 NPN_ReleaseObject(file_npobj);
589 void
590 NPP_URLNotify(NPP instance, const char *url, NPReason reason, void *notifyData)
592 /* TODO: handle file not found errors */
596 NPError
597 NP_Initialize(NPNetscapeFuncs *funcs, NPPluginFuncs *callbacks)
599 g_funcs = *funcs;
600 callbacks->newp = NPP_New;
601 callbacks->destroy = NPP_Destroy;
602 callbacks->getvalue = NPP_GetValue;
603 callbacks->newstream = NPP_NewStream;
604 callbacks->asfile = NPP_StreamAsFile;
605 callbacks->urlnotify = NPP_URLNotify;
607 ident_launch = NPN_GetStringIdentifier("launch");
608 ident_get_file = NPN_GetStringIdentifier("get_file");
609 ident_send = NPN_GetStringIdentifier("send");
610 ident_length = NPN_GetStringIdentifier("length");
612 NaClNrdAllModulesInit();
614 return NPERR_NO_ERROR;
617 char *
618 NP_GetMIMEDescription(void)
620 return "application/x-nacl-imc::Native Client IMC plugin";