2 * Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com>
4 * gstpluginloader.c: GstPluginLoader helper for loading plugin files
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.
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.
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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
28 #include <sys/types.h>
34 #include <gst/gst_private.h>
35 #include <gst/gstconfig.h>
37 #include <gst/gstpoll.h>
38 #include <gst/gstutils.h>
40 #include <gst/gstpluginloader.h>
41 #include <gst/gstregistrychunks.h>
43 #define GST_CAT_DEFAULT GST_CAT_PLUGIN_LOADING
45 static GstPluginLoader *plugin_loader_new (GstRegistry * registry);
46 static gboolean plugin_loader_free (GstPluginLoader * loader);
47 static gboolean plugin_loader_load (GstPluginLoader * loader,
48 const gchar * filename, off_t file_size, time_t file_mtime);
50 const GstPluginLoaderFuncs _priv_gst_plugin_loader_funcs = {
51 plugin_loader_new, plugin_loader_free, plugin_loader_load
54 typedef struct _PendingPluginEntry
62 struct _GstPluginLoader
64 GstRegistry *registry;
67 gboolean child_running;
73 gboolean got_plugin_details;
88 /* Head and tail of the pending plugins list. List of
89 PendingPluginEntry structs */
90 GList *pending_plugins;
91 GList *pending_plugins_tail;
95 #define PACKET_LOAD_PLUGIN 2
97 #define PACKET_PLUGIN_DETAILS 4
99 #define BUF_INIT_SIZE 512
100 #define BUF_GROW_EXTRA 512
101 #define HEADER_SIZE 8
102 #define ALIGNMENT (sizeof (void *))
105 static gboolean gst_plugin_loader_spawn (GstPluginLoader * loader);
106 static void put_packet (GstPluginLoader * loader, guint type, guint32 tag,
107 const guint8 * payload, guint32 payload_len);
108 static gboolean exchange_packets (GstPluginLoader * l);
109 static gboolean plugin_loader_replay_pending (GstPluginLoader * l);
110 static gboolean plugin_loader_load_and_sync (GstPluginLoader * l,
111 PendingPluginEntry * entry);
112 static void plugin_loader_create_blacklist_plugin (GstPluginLoader * l,
113 PendingPluginEntry * entry);
114 static void plugin_loader_cleanup_child (GstPluginLoader * loader);
116 static GstPluginLoader *
117 plugin_loader_new (GstRegistry * registry)
119 GstPluginLoader *l = g_new0 (GstPluginLoader, 1);
122 l->registry = gst_object_ref (registry);
123 l->fdset = gst_poll_new (FALSE);
124 gst_poll_fd_init (&l->fd_w);
125 gst_poll_fd_init (&l->fd_r);
127 l->tx_buf_size = BUF_INIT_SIZE;
128 l->tx_buf = g_malloc (BUF_INIT_SIZE);
132 l->rx_buf_size = BUF_INIT_SIZE;
133 l->rx_buf = g_malloc (BUF_INIT_SIZE);
139 plugin_loader_free (GstPluginLoader * loader)
142 gboolean got_plugin_details;
144 fsync (loader->fd_w.fd);
146 if (loader->child_running) {
147 put_packet (loader, PACKET_EXIT, 0, NULL, 0);
149 /* Swap packets with the child until it exits cleanly */
150 while (!loader->rx_done) {
151 if (exchange_packets (loader) || loader->rx_done)
154 if (!plugin_loader_replay_pending (loader))
156 put_packet (loader, PACKET_EXIT, 0, NULL, 0);
159 plugin_loader_cleanup_child (loader);
161 close (loader->fd_w.fd);
162 close (loader->fd_r.fd);
165 gst_poll_free (loader->fdset);
167 g_free (loader->rx_buf);
168 g_free (loader->tx_buf);
170 if (loader->registry)
171 gst_object_unref (loader->registry);
173 got_plugin_details = loader->got_plugin_details;
175 /* Free any pending plugin entries */
176 cur = loader->pending_plugins;
178 PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
179 g_free (entry->filename);
182 cur = g_list_delete_link (cur, cur);
187 return got_plugin_details;
191 plugin_loader_load (GstPluginLoader * loader, const gchar * filename,
192 off_t file_size, time_t file_mtime)
195 PendingPluginEntry *entry;
197 if (!gst_plugin_loader_spawn (loader))
200 /* Send a packet to the child requesting that it load the given file */
201 GST_LOG_OBJECT (loader->registry,
202 "Sending file %s to child. tag %u", filename, loader->next_tag);
204 entry = g_new (PendingPluginEntry, 1);
205 entry->tag = loader->next_tag++;
206 entry->filename = g_strdup (filename);
207 entry->file_size = file_size;
208 entry->file_mtime = file_mtime;
209 loader->pending_plugins_tail =
210 g_list_append (loader->pending_plugins_tail, entry);
212 if (loader->pending_plugins == NULL)
213 loader->pending_plugins = loader->pending_plugins_tail;
215 loader->pending_plugins_tail = g_list_next (loader->pending_plugins_tail);
217 len = strlen (filename);
218 put_packet (loader, PACKET_LOAD_PLUGIN, entry->tag,
219 (guint8 *) filename, len + 1);
221 if (!exchange_packets (loader)) {
222 if (!plugin_loader_replay_pending (loader))
230 plugin_loader_replay_pending (GstPluginLoader * l)
235 if (!gst_plugin_loader_spawn (l))
238 /* Load each plugin one by one synchronously until we find the
240 while ((cur = l->pending_plugins)) {
241 PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
243 if (!plugin_loader_load_and_sync (l, entry)) {
244 GST_INFO ("AHA! %s crashes on loading", entry->filename);
245 /* Create dummy plugin entry to block re-scanning this file */
246 plugin_loader_create_blacklist_plugin (l, entry);
247 l->got_plugin_details = TRUE;
248 /* Now remove this crashy plugin from the head of the list */
249 l->pending_plugins = g_list_delete_link (cur, cur);
250 if (l->pending_plugins == NULL)
251 l->pending_plugins_tail = NULL;
252 if (!gst_plugin_loader_spawn (l))
258 /* We exited after finding the crashing one. If there's any more pending,
259 * dispatch them post-haste, but don't wait */
260 for (cur = l->pending_plugins; cur != NULL; cur = next) {
261 PendingPluginEntry *entry = (PendingPluginEntry *) (cur->data);
263 next = g_list_next (cur);
265 put_packet (l, PACKET_LOAD_PLUGIN, entry->tag,
266 (guint8 *) entry->filename, strlen (entry->filename) + 1);
268 /* This might invalidate cur, which is why we grabbed 'next' above */
269 if (!exchange_packets (l))
277 plugin_loader_load_and_sync (GstPluginLoader * l, PendingPluginEntry * entry)
281 GST_DEBUG_OBJECT (l->registry, "Synchronously loading plugin file %s",
284 len = strlen (entry->filename);
285 put_packet (l, PACKET_LOAD_PLUGIN, entry->tag,
286 (guint8 *) entry->filename, len + 1);
287 put_packet (l, PACKET_SYNC, 0, NULL, 0);
289 l->rx_got_sync = FALSE;
290 while (!l->rx_got_sync) {
291 if (!exchange_packets (l))
299 plugin_loader_create_blacklist_plugin (GstPluginLoader * l,
300 PendingPluginEntry * entry)
302 GstPlugin *plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
304 plugin->filename = g_strdup (entry->filename);
305 plugin->file_mtime = entry->file_mtime;
306 plugin->file_size = entry->file_size;
307 plugin->flags |= GST_PLUGIN_FLAG_BLACKLISTED;
309 plugin->basename = g_path_get_basename (plugin->filename);
310 plugin->desc.name = g_intern_string (plugin->basename);
311 plugin->desc.description = g_strdup_printf ("Plugin for blacklisted file");
312 plugin->desc.version = g_intern_string ("0.0.0");
313 plugin->desc.license = g_intern_string ("BLACKLIST");
314 plugin->desc.source = plugin->desc.license;
315 plugin->desc.package = plugin->desc.license;
316 plugin->desc.origin = plugin->desc.license;
318 GST_DEBUG ("Adding blacklist plugin '%s'", plugin->desc.name);
319 gst_registry_add_plugin (l->registry, plugin);
323 gst_plugin_loader_spawn (GstPluginLoader * loader)
325 if (loader->child_running)
329 /* FIXME: Find the plugin-scanner! */
331 "/home/jan/devel/gstreamer/head/gstreamer/libs/gst/helpers/plugin-scanner";
332 char *argv[] = { helper_bin, "-l", NULL };
334 if (!g_spawn_async_with_pipes (NULL, argv, NULL,
335 G_SPAWN_DO_NOT_REAP_CHILD /* | G_SPAWN_STDERR_TO_DEV_NULL */ ,
336 NULL, NULL, &loader->child_pid, &loader->fd_w.fd, &loader->fd_r.fd,
341 gst_poll_add_fd (loader->fdset, &loader->fd_w);
342 gst_poll_add_fd (loader->fdset, &loader->fd_r);
344 gst_poll_fd_ctl_read (loader->fdset, &loader->fd_r, TRUE);
346 loader->tx_buf_write = loader->tx_buf_read = 0;
348 loader->child_running = TRUE;
354 plugin_loader_cleanup_child (GstPluginLoader * l)
356 if (!l->child_running || l->is_child)
359 gst_poll_remove_fd (l->fdset, &l->fd_w);
360 gst_poll_remove_fd (l->fdset, &l->fd_r);
366 GST_LOG ("waiting for child process to exit");
367 waitpid (l->child_pid, NULL, 0);
369 g_warning ("FIXME: Implement child process shutdown for Win32");
371 g_spawn_close_pid (l->child_pid);
373 l->child_running = FALSE;
377 _gst_plugin_loader_client_run ()
381 l = plugin_loader_new (NULL);
385 l->fd_w.fd = 1; /* STDOUT */
386 gst_poll_add_fd (l->fdset, &l->fd_w);
388 l->fd_r.fd = 0; /* STDIN */
389 gst_poll_add_fd (l->fdset, &l->fd_r);
390 gst_poll_fd_ctl_read (l->fdset, &l->fd_r, TRUE);
394 GST_DEBUG ("Plugin scanner child running. Waiting for instructions");
396 /* Loop, listening for incoming packets on the fd and writing responses */
397 while (!l->rx_done && exchange_packets (l));
399 plugin_loader_free (l);
405 put_packet (GstPluginLoader * l, guint type, guint32 tag,
406 const guint8 * payload, guint32 payload_len)
409 guint len = payload_len + HEADER_SIZE;
411 if (l->tx_buf_write + len >= l->tx_buf_size) {
412 l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
413 l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
416 out = l->tx_buf + l->tx_buf_write;
419 GST_WRITE_UINT24_BE (out + 1, tag);
420 GST_WRITE_UINT32_BE (out + 4, payload_len);
421 memcpy (out + HEADER_SIZE, payload, payload_len);
423 l->tx_buf_write += len;
424 gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
428 put_chunk (GstPluginLoader * l, GstRegistryChunk * chunk, guint * pos)
434 /* Might need to align the chunk */
435 if (chunk->align && ((*pos) % ALIGNMENT) != 0)
436 padsize = ALIGNMENT - ((*pos) % ALIGNMENT);
438 len = padsize + chunk->size;
440 if (l->tx_buf_write + len >= l->tx_buf_size) {
441 l->tx_buf_size = l->tx_buf_write + len + BUF_GROW_EXTRA;
442 l->tx_buf = g_realloc (l->tx_buf, l->tx_buf_size);
445 out = l->tx_buf + l->tx_buf_write;
446 memcpy (out + padsize, chunk->data, chunk->size);
448 l->tx_buf_write += len;
451 gst_poll_fd_ctl_write (l->fdset, &l->fd_w, TRUE);
455 write_one (GstPluginLoader * l)
461 if (l->tx_buf_read + HEADER_SIZE > l->tx_buf_write)
464 out = l->tx_buf + l->tx_buf_read;
465 to_write = GST_READ_UINT32_BE (out + 4) + HEADER_SIZE;
466 l->tx_buf_read += to_write;
468 GST_LOG ("Writing packet of size %d bytes to fd %d", to_write, l->fd_w.fd);
471 res = write (l->fd_w.fd, out, to_write);
476 } while (to_write > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
478 /* Failed to write -> child died */
479 plugin_loader_cleanup_child (l);
483 if (l->tx_buf_read == l->tx_buf_write) {
484 gst_poll_fd_ctl_write (l->fdset, &l->fd_w, FALSE);
485 l->tx_buf_read = l->tx_buf_write = 0;
491 do_plugin_load (GstPluginLoader * l, const gchar * filename, guint tag)
493 GstPlugin *newplugin;
494 GList *chunks = NULL;
496 GST_DEBUG ("Plugin scanner loading file %s. tag %u\n", filename, tag);
498 #if 0 /* Test code - crash based on an env var */
499 if (strstr (filename, "coreelements") == NULL) {
500 g_printerr ("Crashing on file %s\n", filename);
501 g_printerr ("%d", *(gint *) (NULL));
506 newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
511 /* Now serialise the plugin details and send */
512 if (!_priv_gst_registry_chunks_save_plugin (&chunks,
513 gst_registry_get_default (), newplugin))
516 /* Store where the header is, write an empty one, then write
517 * all the payload chunks, then fix up the header size */
518 hdr_pos = l->tx_buf_write;
519 offset = HEADER_SIZE;
520 put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
524 for (walk = chunks; walk; walk = g_list_next (walk)) {
525 GstRegistryChunk *cur = walk->data;
526 put_chunk (l, cur, &offset);
528 if (!(cur->flags & GST_REGISTRY_CHUNK_FLAG_CONST))
533 g_list_free (chunks);
535 /* Store the size of the written payload */
536 GST_WRITE_UINT32_BE (l->tx_buf + hdr_pos + 4, offset - HEADER_SIZE);
539 gst_object_unref (newplugin);
541 put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
546 put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
549 for (walk = chunks; walk; walk = g_list_next (walk)) {
550 GstRegistryChunk *cur = walk->data;
552 if (!(cur->flags & GST_REGISTRY_CHUNK_FLAG_CONST))
557 g_list_free (chunks);
564 handle_rx_packet (GstPluginLoader * l,
565 guint pack_type, guint32 tag, guint8 * payload, guint payload_len)
571 gst_poll_fd_ctl_read (l->fdset, &l->fd_r, FALSE);
573 /* Respond, then we keep looping until the parent closes the fd */
574 put_packet (l, PACKET_EXIT, 0, NULL, 0);
576 l->rx_done = TRUE; /* All done reading from child */
579 case PACKET_LOAD_PLUGIN:{
583 /* Payload is the filename to load */
584 res = do_plugin_load (l, (gchar *) payload, tag);
588 case PACKET_PLUGIN_DETAILS:{
589 gchar *tmp = (gchar *) payload;
590 PendingPluginEntry *entry = NULL;
593 GST_DEBUG_OBJECT (l->registry,
594 "Received plugin details from child w/ tag %u. %d bytes info",
597 /* Assume that tagged details come back in the order
598 * we requested, and delete anything before this one */
599 cur = l->pending_plugins;
601 PendingPluginEntry *e = (PendingPluginEntry *) (cur->data);
606 cur = g_list_delete_link (cur, cur);
612 g_free (e->filename);
616 l->pending_plugins = cur;
618 l->pending_plugins_tail = NULL;
620 if (payload_len > 0) {
621 GstPlugin *newplugin;
622 _priv_gst_registry_chunks_load_plugin (l->registry, &tmp,
623 tmp + payload_len, &newplugin);
624 newplugin->flags &= ~GST_PLUGIN_FLAG_CACHED;
625 GST_LOG_OBJECT (l->registry,
626 "marking plugin %p as registered as %s", newplugin,
627 newplugin->filename);
628 newplugin->registered = TRUE;
630 /* We got a set of plugin details - remember it for later */
631 l->got_plugin_details = TRUE;
632 } else if (entry != NULL) {
633 /* Create a blacklist entry for this file to prevent scanning every time */
634 plugin_loader_create_blacklist_plugin (l, entry);
635 l->got_plugin_details = TRUE;
639 g_free (entry->filename);
647 /* Respond with our reply - also a sync */
648 put_packet (l, PACKET_SYNC, 0, NULL, 0);
649 GST_LOG ("Got SYNC in child - replying");
651 l->rx_got_sync = TRUE;
654 return FALSE; /* Invalid packet -> something is wrong */
661 read_one (GstPluginLoader * l)
663 guint32 to_read, packet_len, tag;
667 to_read = HEADER_SIZE;
670 res = read (l->fd_r.fd, in, to_read);
675 } while (to_read > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
678 GST_LOG ("Failed reading packet header");
682 packet_len = GST_READ_UINT32_BE (l->rx_buf + 4);
684 if (packet_len + HEADER_SIZE >= l->rx_buf_size) {
685 l->rx_buf_size = packet_len + HEADER_SIZE + BUF_GROW_EXTRA;
686 l->rx_buf = g_realloc (l->rx_buf, l->rx_buf_size);
689 in = l->rx_buf + HEADER_SIZE;
690 to_read = packet_len;
692 res = read (l->fd_r.fd, in, to_read);
697 } while (to_read > 0 && res < 0 && (errno == EAGAIN || errno == EINTR));
700 GST_ERROR ("Packet payload read failed");
704 tag = GST_READ_UINT24_BE (l->rx_buf + 1);
706 return handle_rx_packet (l, l->rx_buf[0], tag,
707 l->rx_buf + HEADER_SIZE, packet_len);
711 exchange_packets (GstPluginLoader * l)
715 /* Wait for activity on our FDs */
718 res = gst_poll_wait (l->fdset, GST_CLOCK_TIME_NONE);
719 } while (res == -1 && (errno == EINTR || errno == EAGAIN));
724 GST_LOG ("Poll res = %d. %d bytes pending for write", res,
725 l->tx_buf_write - l->tx_buf_read);
728 if (gst_poll_fd_has_error (l->fdset, &l->fd_r) ||
729 gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
730 GST_LOG ("read fd %d closed/errored", l->fd_r.fd);
731 plugin_loader_cleanup_child (l);
735 if (gst_poll_fd_can_read (l->fdset, &l->fd_r)) {
741 if (l->tx_buf_read < l->tx_buf_write) {
742 if (gst_poll_fd_has_error (l->fdset, &l->fd_w) ||
743 gst_poll_fd_has_closed (l->fdset, &l->fd_r)) {
744 GST_ERROR ("write fd %d closed/errored", l->fd_w.fd);
745 plugin_loader_cleanup_child (l);
748 if (gst_poll_fd_can_write (l->fdset, &l->fd_w)) {
753 } while (l->tx_buf_read < l->tx_buf_write);