pluginloader: check read/write before closed
[platform/upstream/gstreamer.git] / gst / gstpluginloader.c
1 /* GStreamer
2  * Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
3  *
4  * gstpluginloader.c: GstPluginLoader helper for loading plugin files
5  * out of process.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <gst/gst_private.h>
28
29 #ifndef G_OS_WIN32
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #else
36 #define fsync(fd) _commit(fd)
37 #include <io.h>
38 #endif
39
40 #ifdef HAVE_SYS_UTSNAME_H
41 #include <sys/utsname.h>
42 #endif
43
44 #include <errno.h>
45
46 #include <gst/gstconfig.h>
47
48 #include <gst/gstpoll.h>
49 #include <gst/gstutils.h>
50
51 #include <gst/gstpluginloader.h>
52 #include <gst/gstregistrychunks.h>
53 #include <gst/gstregistrybinary.h>
54
55 /* IMPORTANT: Bump the version number if the plugin loader packet protocol
56  * changes. Changes in the binary registry format itself are handled by
57  * bumping the GST_MAGIC_BINARY_VERSION_STR
58  */
59 static const guint32 loader_protocol_version = 3;
60
61 #define GST_CAT_DEFAULT GST_CAT_PLUGIN_LOADING
62
63 static GstPluginLoader *plugin_loader_new (GstRegistry * registry);
64 static gboolean plugin_loader_free (GstPluginLoader * loader);
65 static gboolean plugin_loader_load (GstPluginLoader * loader,
66     const gchar * filename, off_t file_size, time_t file_mtime);
67
68 /* functions used in GstRegistry scanning */
69 const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs = {
70   plugin_loader_new, plugin_loader_free, plugin_loader_load
71 };
72
73 typedef struct _PendingPluginEntry
74 {
75   /* sequence number */
76   guint32 tag;
77   gchar *filename;
78   off_t file_size;
79   time_t file_mtime;
80 } PendingPluginEntry;
81
82 struct _GstPluginLoader
83 {
84   GstRegistry *registry;
85   GstPoll *fdset;
86
87   gboolean child_running;
88   GPid child_pid;
89   GstPollFD fd_w;
90   GstPollFD fd_r;
91
92   gboolean is_child;
93   gboolean got_plugin_details;
94
95   /* Transmit buffer */
96   guint8 *tx_buf;
97   guint tx_buf_size;
98   guint tx_buf_write;
99   guint tx_buf_read;
100
101   /* next sequence number (for PendingPluginEntry) */
102   guint32 next_tag;
103
104   guint8 *rx_buf;
105   guint rx_buf_size;
106   gboolean rx_done;
107   gboolean rx_got_sync;
108
109   /* Head and tail of the pending plugins list. List of
110      PendingPluginEntry structs */
111   GList *pending_plugins;
112   GList *pending_plugins_tail;
113 };
114
115 #define PACKET_EXIT 1
116 #define PACKET_LOAD_PLUGIN 2
117 #define PACKET_SYNC 3
118 #define PACKET_PLUGIN_DETAILS 4
119 #define PACKET_VERSION 5
120
121 #define BUF_INIT_SIZE 512
122 #define BUF_GROW_EXTRA 512
123 #define BUF_MAX_SIZE (32 * 1024 * 1024)
124
125 #define HEADER_SIZE 12
126 /* 4 magic hex bytes to mark each packet */
127 #define HEADER_MAGIC 0xbefec0ae
128 #define ALIGNMENT   (sizeof (void *))
129
130 static gboolean gst_plugin_loader_spawn (GstPluginLoader * loader);
131 static void put_packet (GstPluginLoader * loader, guint type, guint32 tag,
132     const guint8 * payload, guint32 payload_len);
133 static gboolean exchange_packets (GstPluginLoader * l);
134 static gboolean plugin_loader_replay_pending (GstPluginLoader * l);
135 static gboolean plugin_loader_load_and_sync (GstPluginLoader * l,
136     PendingPluginEntry * entry);
137 static void plugin_loader_create_blacklist_plugin (GstPluginLoader * l,
138     PendingPluginEntry * entry);
139 static void plugin_loader_cleanup_child (GstPluginLoader * loader);
140 static gboolean plugin_loader_sync_with_child (GstPluginLoader * l);
141
142 static GstPluginLoader *
143 plugin_loader_new (GstRegistry * registry)
144 {
145   GstPluginLoader *l = g_slice_new0 (GstPluginLoader);
146
147   if (registry)
148     l->registry = gst_object_ref (registry);
149   l->fdset = gst_poll_new (FALSE);
150   gst_poll_fd_init (&l->fd_w);
151   gst_poll_fd_init (&l->fd_r);
152
153   l->tx_buf_size = BUF_INIT_SIZE;
154   l->tx_buf = g_malloc (BUF_INIT_SIZE);
155
156   l->next_tag = 0;
157
158   l->rx_buf_size = BUF_INIT_SIZE;
159   l->rx_buf = g_malloc (BUF_INIT_SIZE);
160
161   return l;
162 }
163
164 static gboolean
165 plugin_loader_free (GstPluginLoader * loader)
166 {
167   GList *cur;
168   gboolean got_plugin_details;
169
170   fsync (loader->fd_w.fd);
171
172   if (loader->child_running) {
173     put_packet (loader, PACKET_EXIT, 0, NULL, 0);
174
175     /* Swap packets with the child until it exits cleanly */
176     while (!loader->rx_done) {
177       if (exchange_packets (loader) || loader->rx_done)
178         continue;
179
180       if (!plugin_loader_replay_pending (loader))
181         break;
182       put_packet (loader, PACKET_EXIT, 0, NULL, 0);
183     }
184
185     plugin_loader_cleanup_child (loader);
186   } else {
187     close (loader->fd_w.fd);
188     close (loader->fd_r.fd);
189   }
190
191   gst_poll_free (loader->fdset);
192
193   g_free (loader->rx_buf);
194   g_free (loader->tx_buf);
195
196   if (loader->registry)
197     gst_object_unref (loader->registry);
198
199   got_plugin_details = loader->got_plugin_details;
200
201   /* Free any pending plugin entries */
202   cur = loader->pending_plugins;
203   while (cur) {
204     PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
205     g_free (entry->filename);
206     g_slice_free (PendingPluginEntry, entry);
207
208     cur = g_list_delete_link (cur, cur);
209   }
210
211   g_slice_free (GstPluginLoader, loader);
212
213   return got_plugin_details;
214 }
215
216 static gboolean
217 plugin_loader_load (GstPluginLoader * loader, const gchar * filename,
218     off_t file_size, time_t file_mtime)
219 {
220   gint len;
221   PendingPluginEntry *entry;
222
223   if (!gst_plugin_loader_spawn (loader))
224     return FALSE;
225
226   /* Send a packet to the child requesting that it load the given file */
227   GST_LOG_OBJECT (loader->registry,
228       "Sending file %s to child. tag %u", filename, loader->next_tag);
229
230   entry = g_slice_new (PendingPluginEntry);
231   entry->tag = loader->next_tag++;
232   entry->filename = g_strdup (filename);
233   entry->file_size = file_size;
234   entry->file_mtime = file_mtime;
235   loader->pending_plugins_tail =
236       g_list_append (loader->pending_plugins_tail, entry);
237
238   if (loader->pending_plugins == NULL)
239     loader->pending_plugins = loader->pending_plugins_tail;
240   else
241     loader->pending_plugins_tail = g_list_next (loader->pending_plugins_tail);
242
243   len = strlen (filename);
244   put_packet (loader, PACKET_LOAD_PLUGIN, entry->tag,
245       (guint8 *) filename, len + 1);
246
247   if (!exchange_packets (loader)) {
248     if (!plugin_loader_replay_pending (loader))
249       return FALSE;
250   }
251
252   return TRUE;
253 }
254
255 static gboolean
256 plugin_loader_replay_pending (GstPluginLoader * l)
257 {
258   GList *cur, *next;
259
260 restart:
261   if (!gst_plugin_loader_spawn (l))
262     return FALSE;
263
264   /* Load each plugin one by one synchronously until we find the
265    * crashing one */
266   while ((cur = l->pending_plugins)) {
267     PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
268
269     if (!plugin_loader_load_and_sync (l, entry)) {
270       /* Create dummy plugin entry to block re-scanning this file */
271       GST_ERROR ("Plugin file %s failed to load. Blacklisting",
272           entry->filename);
273       plugin_loader_create_blacklist_plugin (l, entry);
274       l->got_plugin_details = TRUE;
275       /* Now remove this crashy plugin from the head of the list */
276       l->pending_plugins = g_list_delete_link (cur, cur);
277       g_free (entry->filename);
278       g_slice_free (PendingPluginEntry, entry);
279       if (l->pending_plugins == NULL)
280         l->pending_plugins_tail = NULL;
281       if (!gst_plugin_loader_spawn (l))
282         return FALSE;
283       break;
284     }
285   }
286
287   /* We exited after finding the crashing one. If there's any more pending,
288    * dispatch them post-haste, but don't wait */
289   for (cur = l->pending_plugins; cur != NULL; cur = next) {
290     PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
291
292     next = g_list_next (cur);
293
294     put_packet (l, PACKET_LOAD_PLUGIN, entry->tag,
295         (guint8 *) entry->filename, strlen (entry->filename) + 1);
296
297     /* This might invalidate cur, which is why we grabbed 'next' above */
298     if (!exchange_packets (l))
299       goto restart;
300   }
301
302   return TRUE;
303 }
304
305 static gboolean
306 plugin_loader_sync_with_child (GstPluginLoader * l)
307 {
308   put_packet (l, PACKET_SYNC, 0, NULL, 0);
309
310   l->rx_got_sync = FALSE;
311   while (!l->rx_got_sync) {
312     if (!exchange_packets (l))
313       return FALSE;
314   }
315   return TRUE;
316 }
317
318 static gboolean
319 plugin_loader_load_and_sync (GstPluginLoader * l, PendingPluginEntry * entry)
320 {
321   gint len;
322
323   GST_DEBUG_OBJECT (l->registry, "Synchronously loading plugin file %s",
324       entry->filename);
325
326   len = strlen (entry->filename);
327   put_packet (l, PACKET_LOAD_PLUGIN, entry->tag,
328       (guint8 *) entry->filename, len + 1);
329
330   return plugin_loader_sync_with_child (l);
331 }
332
333 static void
334 plugin_loader_create_blacklist_plugin (GstPluginLoader * l,
335     PendingPluginEntry * entry)
336 {
337   GstPlugin *plugin = g_object_newv (GST_TYPE_PLUGIN, 0, NULL);
338
339   plugin->filename = g_strdup (entry->filename);
340   plugin->file_mtime = entry->file_mtime;
341   plugin->file_size = entry->file_size;
342   GST_OBJECT_FLAG_SET (plugin, GST_PLUGIN_FLAG_BLACKLISTED);
343
344   plugin->basename = g_path_get_basename (plugin->filename);
345   plugin->desc.name = g_intern_string (plugin->basename);
346   plugin->desc.description = "Plugin for blacklisted file";
347   plugin->desc.version = "0.0.0";
348   plugin->desc.license = "BLACKLIST";
349   plugin->desc.source = plugin->desc.license;
350   plugin->desc.package = plugin->desc.license;
351   plugin->desc.origin = plugin->desc.license;
352
353   GST_DEBUG ("Adding blacklist plugin '%s'", plugin->desc.name);
354   gst_registry_add_plugin (l->registry, plugin);
355 }
356
357 #ifdef __APPLE__
358 #if defined(__x86_64__)
359 #define USR_BIN_ARCH_SWITCH "-x86_64"
360 #elif defined(__i386__)
361 #define USR_BIN_ARCH_SWITCH "-i386"
362 #elif defined(__ppc__)
363 #define USR_BIN_ARCH_SWITCH "-ppc"
364 #elif defined(__ppc64__)
365 #define USR_BIN_ARCH_SWITCH "-ppc64"
366 #endif
367 #endif
368
369 #define YES_MULTIARCH 1
370 #define NO_MULTIARCH  2
371
372 #if defined (__APPLE__) && defined (USR_BIN_ARCH_SWITCH)
373 static gboolean
374 gst_plugin_loader_use_usr_bin_arch (void)
375 {
376   static volatile gsize multiarch = 0;
377
378   if (g_once_init_enter (&multiarch)) {
379     gsize res = NO_MULTIARCH;
380
381 #ifdef HAVE_SYS_UTSNAME_H
382     {
383       struct utsname uname_data;
384
385       if (uname (&uname_data) == 0) {
386         /* Check for OS X >= 10.5 (darwin kernel 9.0) */
387         GST_LOG ("%s %s", uname_data.sysname, uname_data.release);
388         if (g_ascii_strcasecmp (uname_data.sysname, "Darwin") == 0 &&
389             g_strtod (uname_data.release, NULL) >= 9.0) {
390           res = YES_MULTIARCH;
391         }
392       }
393     }
394 #endif
395
396     GST_INFO ("multiarch: %s", (res == YES_MULTIARCH) ? "yes" : "no");
397     g_once_init_leave (&multiarch, res);
398   }
399   return (multiarch == YES_MULTIARCH);
400 }
401 #endif /* __APPLE__ && USR_BIN_ARCH_SWITCH */
402
403 static gboolean
404 gst_plugin_loader_try_helper (GstPluginLoader * loader, gchar * location)
405 {
406   char *argv[5] = { NULL, };
407   int c = 0;
408
409 #if defined (__APPLE__) && defined (USR_BIN_ARCH_SWITCH)
410   if (gst_plugin_loader_use_usr_bin_arch ()) {
411     argv[c++] = (char *) "/usr/bin/arch";
412     argv[c++] = (char *) USR_BIN_ARCH_SWITCH;
413   }
414 #endif
415   argv[c++] = location;
416   argv[c++] = (char *) "-l";
417   argv[c++] = NULL;
418
419   if (c > 3) {
420     GST_LOG ("Trying to spawn gst-plugin-scanner helper at %s with arch %s",
421         location, argv[1]);
422   } else {
423     GST_LOG ("Trying to spawn gst-plugin-scanner helper at %s", location);
424   }
425
426   if (!g_spawn_async_with_pipes (NULL, argv, NULL,
427           G_SPAWN_DO_NOT_REAP_CHILD /* | G_SPAWN_STDERR_TO_DEV_NULL */ ,
428           NULL, NULL, &loader->child_pid, &loader->fd_w.fd, &loader->fd_r.fd,
429           NULL, NULL))
430     return FALSE;
431
432   gst_poll_add_fd (loader->fdset, &loader->fd_w);
433   gst_poll_add_fd (loader->fdset, &loader->fd_r);
434
435   gst_poll_fd_ctl_read (loader->fdset, &loader->fd_r, TRUE);
436
437   loader->tx_buf_write = loader->tx_buf_read = 0;
438
439   put_packet (loader, PACKET_VERSION, 0, NULL, 0);
440   if (!plugin_loader_sync_with_child (loader))
441     return FALSE;
442
443   loader->child_running = TRUE;
444
445   return TRUE;
446 }
447
448 static gboolean
449 gst_plugin_loader_spawn (GstPluginLoader * loader)
450 {
451   const gchar *env;
452   char *helper_bin;
453   gboolean res = FALSE;
454
455   if (loader->child_running)
456     return TRUE;
457
458   /* Find the gst-plugin-scanner: first try the env-var if it is set,
459    * otherwise use the installed version */
460   env = g_getenv ("GST_PLUGIN_SCANNER_1_0");
461   if (env == NULL)
462     env = g_getenv ("GST_PLUGIN_SCANNER");
463
464   if (env != NULL && *env != '\0') {
465     GST_LOG ("Trying GST_PLUGIN_SCANNER env var: %s", env);
466     helper_bin = g_strdup (env);
467     res = gst_plugin_loader_try_helper (loader, helper_bin);
468     g_free (helper_bin);
469   }
470
471   if (!res) {
472     GST_LOG ("Trying installed plugin scanner");
473     helper_bin = g_strdup (GST_PLUGIN_SCANNER_INSTALLED);
474     res = gst_plugin_loader_try_helper (loader, helper_bin);
475     g_free (helper_bin);
476
477     if (!res) {
478       GST_INFO ("No gst-plugin-scanner available, or not working");
479     }
480   }
481
482   return loader->child_running;
483 }
484
485 static void
486 plugin_loader_cleanup_child (GstPluginLoader * l)
487 {
488   if (!l->child_running || l->is_child)
489     return;
490
491   gst_poll_remove_fd (l->fdset, &l->fd_w);
492   gst_poll_remove_fd (l->fdset, &l->fd_r);
493
494   close (l->fd_w.fd);
495   close (l->fd_r.fd);
496
497 #ifndef G_OS_WIN32
498   GST_LOG ("waiting for child process to exit");
499   waitpid (l->child_pid, NULL, 0);
500 #else
501   g_warning ("FIXME: Implement child process shutdown for Win32");
502 #endif
503   g_spawn_close_pid (l->child_pid);
504
505   l->child_running = FALSE;
506 }
507
508 gboolean
509 _gst_plugin_loader_client_run (void)
510 {
511   GstPluginLoader *l;
512
513   l = plugin_loader_new (NULL);
514   if (l == NULL)
515     return FALSE;
516
517   /* On entry, the inward pipe is STDIN, and outward is STDOUT.
518    * Dup those somewhere better so that plugins printing things
519    * won't interfere with anything */
520 #ifndef G_OS_WIN32
521   {
522     int dup_fd;
523
524     dup_fd = dup (0);           /* STDIN */
525     if (dup_fd == -1) {
526       GST_ERROR ("Failed to start. Could no dup STDIN, errno %d", errno);
527       return FALSE;
528     }
529     l->fd_r.fd = dup_fd;
530     close (0);
531
532     dup_fd = dup (1);           /* STDOUT */
533     if (dup_fd == -1) {
534       GST_ERROR ("Failed to start. Could no dup STDOUT, errno %d", errno);
535       return FALSE;
536     }
537     l->fd_w.fd = dup_fd;
538     close (1);
539
540     /* Dup stderr down to stdout so things that plugins print are visible,
541      * but don't care if it fails */
542     dup2 (2, 1);
543   }
544 #else
545   /* FIXME: Use DuplicateHandle and friends on win32 */
546   l->fd_w.fd = 1;               /* STDOUT */
547   l->fd_r.fd = 0;               /* STDIN */
548 #endif
549
550   gst_poll_add_fd (l->fdset, &l->fd_w);
551   gst_poll_add_fd (l->fdset, &l->fd_r);
552   gst_poll_fd_ctl_read (l->fdset, &l->fd_r, TRUE);
553
554   l->is_child = TRUE;
555
556   GST_DEBUG ("Plugin scanner child running. Waiting for instructions");
557
558   /* Loop, listening for incoming packets on the fd and writing responses */
559   while (!l->rx_done && exchange_packets (l));
560
561   plugin_loader_free (l);
562
563   return TRUE;
564 }
565
566 static void
567 put_packet (GstPluginLoader * l, guint type, guint32 tag,
568     const guint8 * payload, guint32 payload_len)
569 {
570   guint8 *out;
571   guint len = payload_len + HEADER_SIZE;
572
573   if (l->tx_buf_write + len >= l->tx_buf_size) {
574     GST_LOG ("Expanding tx buf from %d to %d for packet of size %d",
575         l->tx_buf_size, l->tx_buf_write + len + BUF_GROW_EXTRA, len);
576     l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
577     l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
578   }
579
580   out = l->tx_buf + l->tx_buf_write;
581
582   /* one byte packet type */
583   out[0] = type;
584   /* 3 byte packet tag number */
585   GST_WRITE_UINT24_BE (out + 1, tag);
586   /* 4 bytes packet length */
587   GST_WRITE_UINT32_BE (out + 4, payload_len);
588   /* payload */
589   if (payload && payload_len)
590     memcpy (out + HEADER_SIZE, payload, payload_len);
591   /* Write magic into the header */
592   GST_WRITE_UINT32_BE (out + 8, HEADER_MAGIC);
593
594   l->tx_buf_write += len;
595   gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
596 }
597
598 static void
599 put_chunk (GstPluginLoader * l, GstRegistryChunk * chunk, guint * pos)
600 {
601   guint padsize = 0;
602   guint len;
603   guint8 *out;
604
605   /* Might need to align the chunk */
606   if (chunk->align && ((*pos) % ALIGNMENT) != 0)
607     padsize = ALIGNMENT - ((*pos) % ALIGNMENT);
608
609   len = padsize + chunk->size;
610
611   if (G_UNLIKELY (l->tx_buf_write + len >= l->tx_buf_size)) {
612     guint new_size = MAX (l->tx_buf_write + len,
613         l->tx_buf_size + l->tx_buf_size / 4) + BUF_GROW_EXTRA;
614     GST_LOG ("Expanding tx buf from %d to %d for chunk of size %d",
615         l->tx_buf_size, new_size, chunk->size);
616     l->tx_buf_size = new_size;
617     l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
618   }
619
620   out = l->tx_buf + l->tx_buf_write;
621   /* Clear the padding */
622   if (padsize)
623     memset (out, 0, padsize);
624   memcpy (out + padsize, chunk->data, chunk->size);
625
626   l->tx_buf_write += len;
627   *pos += len;
628
629   gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
630 };
631
632 static gboolean
633 write_one (GstPluginLoader * l)
634 {
635   guint8 *out;
636   guint32 to_write, magic;
637   int res;
638
639   if (l->tx_buf_read + HEADER_SIZE > l->tx_buf_write)
640     return FALSE;
641
642   out = l->tx_buf + l->tx_buf_read;
643
644   magic = GST_READ_UINT32_BE (out + 8);
645   if (magic != HEADER_MAGIC) {
646     GST_ERROR ("Packet magic number is missing. Memory corruption detected");
647     goto fail_and_cleanup;
648   }
649
650   to_write = GST_READ_UINT32_BE (out + 4) + HEADER_SIZE;
651   /* Check that the magic is intact, and the size is sensible */
652   if (to_write > l->tx_buf_size) {
653     GST_ERROR ("Indicated packet size is too large. Corruption detected");
654     goto fail_and_cleanup;
655   }
656
657   l->tx_buf_read += to_write;
658
659   GST_LOG ("Writing packet of size %d bytes to fd %d", to_write, l->fd_w.fd);
660
661   do {
662     res = write (l->fd_w.fd, out, to_write);
663     if (G_UNLIKELY (res < 0)) {
664       if (errno == EAGAIN || errno == EINTR)
665         continue;
666       /* Failed to write -> child died */
667       goto fail_and_cleanup;
668     }
669     to_write -= res;
670     out += res;
671   } while (to_write > 0);
672
673   if (l->tx_buf_read == l->tx_buf_write) {
674     gst_poll_fd_ctl_write (l->fdset, &l->fd_w, FALSE);
675     l->tx_buf_read = l->tx_buf_write = 0;
676   }
677
678   return TRUE;
679
680 fail_and_cleanup:
681   plugin_loader_cleanup_child (l);
682   return FALSE;
683 }
684
685 static gboolean
686 do_plugin_load (GstPluginLoader * l, const gchar * filename, guint tag)
687 {
688   GstPlugin *newplugin;
689   GList *chunks = NULL;
690
691   GST_DEBUG ("Plugin scanner loading file %s. tag %u", filename, tag);
692
693 #if 0                           /* Test code - crash based on filename */
694   if (strstr (filename, "coreelements") == NULL) {
695     g_printerr ("Crashing on file %s\n", filename);
696     g_printerr ("%d", *(gint *) (NULL));
697   }
698 #endif
699
700   newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
701   if (newplugin) {
702     guint hdr_pos;
703     guint offset;
704
705     /* Now serialise the plugin details and send */
706     if (!_priv_gst_registry_chunks_save_plugin (&chunks,
707             gst_registry_get (), newplugin))
708       goto fail;
709
710     /* Store where the header is, write an empty one, then write
711      * all the payload chunks, then fix up the header size */
712     hdr_pos = l->tx_buf_write;
713     offset = HEADER_SIZE;
714     put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
715
716     if (chunks) {
717       GList *walk;
718       for (walk = chunks; walk; walk = g_list_next (walk)) {
719         GstRegistryChunk *cur = walk->data;
720         put_chunk (l, cur, &offset);
721
722         _priv_gst_registry_chunk_free (cur);
723       }
724
725       g_list_free (chunks);
726
727       /* Store the size of the written payload */
728       GST_WRITE_UINT32_BE (l->tx_buf + hdr_pos + 4, offset - HEADER_SIZE);
729     }
730 #if 0                           /* Test code - corrupt the tx buffer based on filename */
731     if (strstr (filename, "sink") != NULL) {
732       int fd, res;
733       g_printerr ("Corrupting tx buf on file %s\n", filename);
734       fd = open ("/dev/urandom", O_RDONLY);
735       res = read (fd, l->tx_buf, l->tx_buf_size);
736       close (fd);
737     }
738 #endif
739
740     gst_object_unref (newplugin);
741   } else {
742     put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
743   }
744
745   return TRUE;
746 fail:
747   put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
748   if (chunks) {
749     GList *walk;
750     for (walk = chunks; walk; walk = g_list_next (walk)) {
751       GstRegistryChunk *cur = walk->data;
752
753       _priv_gst_registry_chunk_free (cur);
754     }
755
756     g_list_free (chunks);
757   }
758
759   return FALSE;
760 }
761
762 static gboolean
763 check_protocol_version (GstPluginLoader * l, guint8 * payload,
764     guint payload_len)
765 {
766   guint32 got_version;
767   guint8 *binary_reg_ver;
768
769   if (payload_len < sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN)
770     return FALSE;
771
772   got_version = GST_READ_UINT32_BE (payload);
773   GST_LOG ("Got VERSION %u from child. Ours is %u", got_version,
774       loader_protocol_version);
775   if (got_version != loader_protocol_version)
776     return FALSE;
777
778   binary_reg_ver = payload + sizeof (guint32);
779   if (strcmp ((gchar *) binary_reg_ver, GST_MAGIC_BINARY_VERSION_STR)) {
780     GST_LOG ("Binary chunk format of child is different. Ours: %s, child %s\n",
781         GST_MAGIC_BINARY_VERSION_STR, binary_reg_ver);
782     return FALSE;
783   }
784
785   return TRUE;
786 };
787
788 static gboolean
789 handle_rx_packet (GstPluginLoader * l,
790     guint pack_type, guint32 tag, guint8 * payload, guint payload_len)
791 {
792   gboolean res = TRUE;
793
794   switch (pack_type) {
795     case PACKET_EXIT:
796       gst_poll_fd_ctl_read (l->fdset, &l->fd_r, FALSE);
797       if (l->is_child) {
798         /* Respond */
799         put_packet (l, PACKET_EXIT, 0, NULL, 0);
800       }
801       l->rx_done = TRUE;
802       return TRUE;
803     case PACKET_LOAD_PLUGIN:{
804       if (!l->is_child)
805         return TRUE;
806
807       /* Payload is the filename to load */
808       res = do_plugin_load (l, (gchar *) payload, tag);
809
810       break;
811     }
812     case PACKET_PLUGIN_DETAILS:{
813       gchar *tmp = (gchar *) payload;
814       PendingPluginEntry *entry = NULL;
815       GList *cur;
816
817       GST_DEBUG_OBJECT (l->registry,
818           "Received plugin details from child w/ tag %u. %d bytes info",
819           tag, payload_len);
820
821       /* Assume that tagged details come back in the order
822        * we requested, and delete anything before (but not
823        * including) this one */
824       cur = l->pending_plugins;
825       while (cur) {
826         PendingPluginEntry *e = (PendingPluginEntry *) (cur->data);
827
828         if (e->tag > tag)
829           break;
830
831         if (e->tag == tag) {
832           entry = e;
833           break;
834         } else {
835           cur = g_list_delete_link (cur, cur);
836           g_free (e->filename);
837           g_slice_free (PendingPluginEntry, e);
838         }
839       }
840
841       l->pending_plugins = cur;
842       if (cur == NULL)
843         l->pending_plugins_tail = NULL;
844
845       if (payload_len > 0) {
846         GstPlugin *newplugin = NULL;
847         if (!_priv_gst_registry_chunks_load_plugin (l->registry, &tmp,
848                 tmp + payload_len, &newplugin)) {
849           /* Got garbage from the child, so fail and trigger replay of plugins */
850           GST_ERROR_OBJECT (l->registry,
851               "Problems loading plugin details with tag %u from scanner", tag);
852           return FALSE;
853         }
854
855         GST_OBJECT_FLAG_UNSET (newplugin, GST_PLUGIN_FLAG_CACHED);
856         GST_LOG_OBJECT (l->registry,
857             "marking plugin %p as registered as %s", newplugin,
858             newplugin->filename);
859         newplugin->registered = TRUE;
860
861         /* We got a set of plugin details - remember it for later */
862         l->got_plugin_details = TRUE;
863       } else if (entry != NULL) {
864         /* Create a blacklist entry for this file to prevent scanning every time */
865         plugin_loader_create_blacklist_plugin (l, entry);
866         l->got_plugin_details = TRUE;
867       }
868
869       if (entry != NULL) {
870         g_free (entry->filename);
871         g_slice_free (PendingPluginEntry, entry);
872       }
873
874       /* Remove the plugin entry we just loaded */
875       cur = l->pending_plugins;
876       if (cur != NULL)
877         cur = g_list_delete_link (cur, cur);
878       l->pending_plugins = cur;
879       if (cur == NULL)
880         l->pending_plugins_tail = NULL;
881
882       break;
883     }
884     case PACKET_SYNC:
885       if (l->is_child) {
886         /* Respond with our reply - also a sync */
887         put_packet (l, PACKET_SYNC, tag, NULL, 0);
888         GST_LOG ("Got SYNC in child - replying");
889       } else
890         l->rx_got_sync = TRUE;
891       break;
892     case PACKET_VERSION:
893       if (l->is_child) {
894         /* Respond with our reply - a version packet, with the version */
895         const gint version_len =
896             sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN;
897         guint8 version_info[sizeof (guint32) + GST_MAGIC_BINARY_VERSION_LEN];
898         memset (version_info, 0, version_len);
899         GST_WRITE_UINT32_BE (version_info, loader_protocol_version);
900         memcpy (version_info + sizeof (guint32), GST_MAGIC_BINARY_VERSION_STR,
901             strlen (GST_MAGIC_BINARY_VERSION_STR));
902         put_packet (l, PACKET_VERSION, tag, version_info, version_len);
903         GST_LOG ("Got VERSION in child - replying %u", loader_protocol_version);
904       } else {
905         res = check_protocol_version (l, payload, payload_len);
906       }
907       break;
908     default:
909       return FALSE;             /* Invalid packet -> something is wrong */
910   }
911
912   return res;
913 }
914
915 static gboolean
916 read_one (GstPluginLoader * l)
917 {
918   guint64 magic;
919   guint32 to_read, packet_len, tag;
920   guint8 *in;
921   gint res;
922
923   to_read = HEADER_SIZE;
924   in = l->rx_buf;
925   do {
926     res = read (l->fd_r.fd, in, to_read);
927     if (G_UNLIKELY (res < 0)) {
928       if (errno == EAGAIN || errno == EINTR)
929         continue;
930       GST_LOG ("Failed reading packet header");
931       return FALSE;
932     }
933     to_read -= res;
934     in += res;
935   } while (to_read > 0);
936
937   magic = GST_READ_UINT32_BE (l->rx_buf + 8);
938   if (magic != HEADER_MAGIC) {
939     GST_WARNING
940         ("Invalid packet (bad magic number) received from plugin scanner subprocess");
941     return FALSE;
942   }
943
944   packet_len = GST_READ_UINT32_BE (l->rx_buf + 4);
945   if (packet_len + HEADER_SIZE > BUF_MAX_SIZE) {
946     GST_WARNING
947         ("Received excessively large packet for plugin scanner subprocess");
948     return FALSE;
949   }
950   tag = GST_READ_UINT24_BE (l->rx_buf + 1);
951
952   if (packet_len > 0) {
953     if (packet_len + HEADER_SIZE >= l->rx_buf_size) {
954       GST_LOG ("Expanding rx buf from %d to %d",
955           l->rx_buf_size, packet_len + HEADER_SIZE + BUF_GROW_EXTRA);
956       l->rx_buf_size = packet_len + HEADER_SIZE + BUF_GROW_EXTRA;
957       l->rx_buf = g_realloc (l->rx_buf, l->rx_buf_size);
958     }
959
960     in = l->rx_buf + HEADER_SIZE;
961     to_read = packet_len;
962     do {
963       res = read (l->fd_r.fd, in, to_read);
964       if (G_UNLIKELY (res < 0)) {
965         if (errno == EAGAIN || errno == EINTR)
966           continue;
967         GST_ERROR ("Packet payload read failed");
968         return FALSE;
969       }
970       to_read -= res;
971       in += res;
972     } while (to_read > 0);
973   } else {
974     GST_LOG ("No payload to read for 0 length packet type %d tag %u",
975         l->rx_buf[0], tag);
976   }
977
978   return handle_rx_packet (l, l->rx_buf[0], tag,
979       l->rx_buf + HEADER_SIZE, packet_len);
980 }
981
982 static gboolean
983 exchange_packets (GstPluginLoader * l)
984 {
985   gint res;
986
987   /* Wait for activity on our FDs */
988   do {
989     do {
990       res = gst_poll_wait (l->fdset, GST_SECOND);
991     } while (res == -1 && (errno == EINTR || errno == EAGAIN));
992
993     if (res < 0)
994       return FALSE;
995
996     GST_LOG ("Poll res = %d. %d bytes pending for write", res,
997         l->tx_buf_write - l->tx_buf_read);
998
999     if (!l->rx_done) {
1000       if (gst_poll_fd_has_error (l->fdset, &l->fd_r)) {
1001         GST_LOG ("read fd %d errored", l->fd_r.fd);
1002         goto fail_and_cleanup;
1003       }
1004
1005       if (gst_poll_fd_can_read (l->fdset, &l->fd_r)) {
1006         if (!read_one (l))
1007           goto fail_and_cleanup;
1008       } else if (gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
1009         GST_LOG ("read fd %d closed", l->fd_r.fd);
1010         goto fail_and_cleanup;
1011       }
1012     }
1013
1014     if (l->tx_buf_read < l->tx_buf_write) {
1015       if (gst_poll_fd_has_error (l->fdset, &l->fd_w)) {
1016         GST_ERROR ("write fd %d errored", l->fd_w.fd);
1017         goto fail_and_cleanup;
1018       }
1019       if (gst_poll_fd_can_write (l->fdset, &l->fd_w)) {
1020         if (!write_one (l))
1021           goto fail_and_cleanup;
1022       } else if (gst_poll_fd_has_closed (l->fdset, &l->fd_w)) {
1023         GST_LOG ("write fd %d closed", l->fd_w.fd);
1024         goto fail_and_cleanup;
1025       }
1026     }
1027   } while (l->tx_buf_read < l->tx_buf_write);
1028
1029   return TRUE;
1030 fail_and_cleanup:
1031   plugin_loader_cleanup_child (l);
1032   return FALSE;
1033 }