Initialize Tizen 2.3
[framework/multimedia/gst-plugins-base0.10.git] / wearable / gst-libs / gst / pbutils / install-plugins.c
1 /* GStreamer base utils library plugin install support for applications
2  * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
3  * Copyright (C) 2006 Ryan Lortie <desrt desrt ca>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:gstpbutilsinstallplugins
23  * @short_description: Missing plugin installation support for applications
24  *
25  * <refsect2>
26  * <title>Overview</title>
27  * <para>
28  * Using this API, applications can request the installation of missing
29  * GStreamer plugins. These may be missing decoders/demuxers or encoders/muxers
30  * for a certain format, sources or sinks for a certain URI protocol
31  * (e.g. 'http'), or certain elements known by their element factory name
32  * ('audioresample').
33  * </para>
34  * <para>
35  * Whether plugin installation is supported or not depends on the operating
36  * system and/or distribution in question. The vendor of the operating system
37  * needs to make sure the necessary hooks and mechanisms are in place for
38  * plugin installation to work. See below for more detailed information.
39  * </para>
40  * <para>
41  * From the application perspective, plugin installation is usually triggered
42  * either
43  * <itemizedlist>
44  * <listitem><para>
45  * when the application itself has found that it wants or needs to install a
46  * certain element
47  * </para></listitem>
48  * <listitem><para>
49  * when the application has been notified by an element (such as playbin or
50  * decodebin) that one or more plugins are missing <emphasis>and</emphasis>
51  * the application has decided that it wants to install one or more of those
52  * missing plugins
53  * </para></listitem>
54  * </itemizedlist>
55  * </para>
56  * <title>Detail Strings</title>
57  * <para>
58  * The install functions in this section all take one or more 'detail strings'.
59  * These detail strings contain information about the type of plugin that
60  * needs to be installed (decoder, encoder, source, sink, or named element),
61  * and some additional information such GStreamer version used and a
62  * human-readable description of the component to install for user dialogs.
63  * </para>
64  * <para>
65  * Applications should not concern themselves with the composition of the
66  * string itself. They should regard the string as if it was a shared secret
67  * between GStreamer and the plugin installer application.
68  * </para>
69  * <para>
70  * Detail strings can be obtained using the function
71  * gst_missing_plugin_message_get_installer_detail() on a missing-plugin
72  * message. Such a message will either have been found by the application on
73  * a pipeline's #GstBus, or the application will have created it itself using
74  * gst_missing_element_message_new(), gst_missing_decoder_message_new(),
75  * gst_missing_encoder_message_new(), gst_missing_uri_sink_message_new(), or
76  * gst_missing_uri_source_message_new().
77  * </para>
78  * <title>Plugin Installation from the Application Perspective</title>
79  * <para>
80  * For each GStreamer element/plugin/component that should be installed, the
81  * application needs one of those 'installer detail' string mentioned in the
82  * previous section. This string can be obtained, as already mentioned above,
83  * from a missing-plugin message using the function
84  * gst_missing_plugin_message_get_installer_detail(). The missing-plugin
85  * message is either posted by another element and then found on the bus
86  * by the application, or the application has created it itself as described
87  * above.
88  * </para>
89  * <para>
90  * The application will then call gst_install_plugins_async(), passing a
91  * NULL-terminated array of installer detail strings, and a function that
92  * should be called when the installation of the plugins has finished
93  * (successfully or not). Optionally, a #GstInstallPluginsContext created
94  * with gst_install_plugins_context_new() may be passed as well. This way
95  * additional optional arguments like the application window's XID can be
96  * passed to the external installer application.
97  * </para>
98  * <para>
99  * gst_install_plugins_async() will return almost immediately, with the
100  * return code indicating whether plugin installation was started or not.
101  * If the necessary hooks for plugin installation are in place and an
102  * external installer application has in fact been called, the passed in
103  * function will be called with a result code as soon as the external installer
104  * has finished. If the result code indicates that new plugins have been
105  * installed, the application will want to call gst_update_registry() so the
106  * run-time plugin registry is updated and the new plugins are made available
107  * to the application.
108  * <note>
109  * A Gtk/GLib main loop must be running in order for the result function to
110  * be called when the external installer has finished. If this is not the case,
111  * make sure to regularly call
112  * <programlisting>
113  * g_main_context_iteration (NULL,FALSE);
114  * </programlisting>
115  * from your code.
116  * </note>
117  * </para>
118  * <title>Plugin Installation from the Vendor/Distribution Perspective</title>
119  * <para>
120  * <emphasis>1. Installer hook</emphasis>
121  * </para>
122  * <para>
123  * When GStreamer applications initiate plugin installation via
124  * gst_install_plugins_async() or gst_install_plugins_sync(), a pre-defined
125  * helper application will be called.
126  * </para>
127  * <para>
128  * The exact path of the helper application to be called is set at compile
129  * time, usually by the <literal>./configure</literal> script based on the
130  * install prefix. For a normal package build into the <literal>/usr</literal>
131  * prefix, this will usually default to
132  * <filename>/usr/libexec/gst-install-plugins-helper</filename> or
133  * <filename>/usr/lib/gst-install-plugins-helper</filename>.
134  * </para>
135  * <para>
136  * Vendors/distros who want to support GStreamer plugin installation should
137  * either provide such a helper script/application or use the
138  * <literal>./configure</literal> option
139  * <literal>--with-install-plugins-helper=/path/to/installer</literal> to
140  * make GStreamer call an installer of their own directly.
141  * </para>
142  * <para>
143  * It is strongly recommended that vendors provide a small helper application
144  * as interlocutor to the real installer though, even more so if command line
145  * argument munging is required to transform the command line arguments
146  * passed by GStreamer to the helper application into arguments that are
147  * understood by the real installer.
148  * </para>
149  * <para>
150  * The helper application path defined at compile time can be overriden at
151  * runtime by setting the <envar>GST_INSTALL_PLUGINS_HELPER</envar>
152  * environment variable. This can be useful for testing/debugging purposes.
153  * </para>
154  * <para>
155  * <emphasis>2. Arguments passed to the install helper</emphasis>
156  * </para>
157  * <para>
158  * GStreamer will pass the following arguments to the install helper (this is
159  * in addition to the path of the executable itself, which is by convention
160  * argv[0]):
161  * <itemizedlist>
162  *  <listitem><para>
163  *    none to many optional arguments in the form of
164  *    <literal>--foo-bar=val</literal>. Example:
165  *    <literal>--transient-for=XID</literal> where XID is the X Window ID of
166  *    the main window of the calling application (so the installer can make
167  *    itself transient to that window). Unknown optional arguments should
168  *    be ignored by the installer.
169  *  </para></listitem>
170  *  <listitem><para>
171  *    one 'installer detail string' argument for each plugin to be installed;
172  *    these strings will have a <literal>gstreamer</literal> prefix; the
173  *    exact format of the detail string is explained below
174  *  </para></listitem>
175  * </itemizedlist>
176  * </para>
177  * <para>
178  * <emphasis>3. Detail string describing the missing plugin</emphasis>
179  * </para>
180  * <para>
181  * The string is in UTF-8 encoding and is made up of several fields, separated
182  * by '|' characters (but neither the first nor the last character is a '|').
183  * The fields are:
184  * <itemizedlist>
185  *   <listitem><para>
186  *    plugin system identifier, ie. "gstreamer"
187  *   </para><para>
188  *    This identifier determines the format of the rest of the detail string.
189  *    Automatic plugin installers should not process detail strings with
190  *    unknown identifiers. This allows other plugin-based libraries to use
191  *    the same mechanism for their automatic plugin installation needs, or
192  *    for the format to be changed should it turn out to be insufficient.
193  *   </para></listitem>
194  *   <listitem><para>
195  *    plugin system version, e.g. "0.10"
196  *   </para><para>
197  *    This is required so that when there is a GStreamer-0.12 or GStreamer-1.0
198  *    at some point in future, the different major versions can still co-exist
199  *    and use the same plugin install mechanism in the same way.
200  *   </para></listitem>
201  *   <listitem><para>
202  *    application identifier, e.g. "totem"
203  *   </para><para>
204  *    This may also be in the form of "pid/12345" if the program name can't
205  *    be obtained for some reason.
206  *   </para></listitem>
207  *   <listitem><para>
208  *    human-readable localised description of the required component,
209  *    e.g. "Vorbis audio decoder"
210  *   </para></listitem>
211  *   <listitem><para>
212  *    identifier string for the required component (see below for details about
213  *    how to map this to the package/plugin that needs installing), e.g.
214  *    <itemizedlist>
215  *     <listitem><para>
216  *       urisource-$(PROTOCOL_REQUIRED), e.g. urisource-http or urisource-mms
217  *     </para></listitem>
218  *     <listitem><para>
219  *       element-$(ELEMENT_REQUIRED), e.g. element-ffmpegcolorspace
220  *     </para></listitem>
221  *     <listitem><para>
222  *       decoder-$(CAPS_REQUIRED), e.g. (do read below for more details!):
223  *       <itemizedlist>
224  *         <listitem><para>decoder-audio/x-vorbis</para></listitem>
225  *         <listitem><para>decoder-application/ogg</para></listitem>
226  *         <listitem><para>decoder-audio/mpeg, mpegversion=(int)4</para></listitem>
227  *         <listitem><para>decoder-video/mpeg, systemstream=(boolean)true, mpegversion=(int)2</para></listitem>
228          </itemizedlist>
229  *     </para></listitem>
230  *     <listitem><para>
231  *       encoder-$(CAPS_REQUIRED), e.g. encoder-audio/x-vorbis
232  *     </para></listitem>
233  *    </itemizedlist>
234  *   </para></listitem>
235  *   <listitem><para>
236  *     optional further fields not yet specified
237  *   </para></listitem>
238  * </itemizedlist>
239  * </para>
240  * <para>
241  * An entire ID string might then look like this, for example:
242  * <literal>
243  * gstreamer|0.10|totem|Vorbis audio decoder|decoder-audio/x-vorbis
244  * </literal>
245  * </para>
246  * <para>
247  * Plugin installers parsing this ID string should expect further fields also
248  * separated by '|' symbols and either ignore them, warn the user, or error
249  * out when encountering them.
250  * </para>
251  * <para>
252  * Those unfamiliar with the GStreamer 'caps' system should note a few things
253  * about the caps string used in the above decoder/encoder case:
254  *   <itemizedlist>
255  *     <listitem><para>
256  *       the first part ("video/mpeg") of the caps string is a GStreamer media
257  *       type and <emphasis>not</emphasis> a MIME type. Wherever possible, the
258  *       GStreamer media type will be the same as the corresponding MIME type,
259  *       but often it is not.
260  *     </para></listitem>
261  *     <listitem><para>
262  *       a caps string may or may not have additional comma-separated fields
263  *       of various types (as seen in the examples above)
264  *     </para></listitem>
265  *     <listitem><para>
266  *       the caps string of a 'required' component (as above) will always have
267  *       fields with fixed values, whereas an introspected string (see below)
268  *       may have fields with non-fixed values. Compare for example:
269  *       <itemizedlist>
270  *         <listitem><para>
271  *           <literal>audio/mpeg, mpegversion=(int)4</literal> vs.
272  *           <literal>audio/mpeg, mpegversion=(int){2, 4}</literal>
273  *         </para></listitem>
274  *         <listitem><para>
275  *           <literal>video/mpeg, mpegversion=(int)2</literal> vs.
276  *           <literal>video/mpeg, systemstream=(boolean){ true, false}, mpegversion=(int)[1, 2]</literal>
277  *         </para></listitem>
278  *       </itemizedlist>
279  *     </para></listitem>
280  *   </itemizedlist>
281  * </para>
282  * <para>
283  * <emphasis>4. Exit codes the installer should return</emphasis>
284  * </para>
285  * <para>
286  * The installer should return one of the following exit codes when it exits:
287  * <itemizedlist>
288  *   <listitem><para>
289  *     0 if all of the requested plugins could be installed
290  *     (#GST_INSTALL_PLUGINS_SUCCESS)
291  *   </para></listitem>
292  *   <listitem><para>
293  *     1 if no appropriate installation candidate for any of the requested
294  *     plugins could be found. Only return this if nothing has been installed
295  *     (#GST_INSTALL_PLUGINS_NOT_FOUND)
296  *   </para></listitem>
297  *   <listitem><para>
298  *     2 if an error occured during the installation. The application will
299  *     assume that the user will already have seen an error message by the
300  *     installer in this case and will usually not show another one
301  *     (#GST_INSTALL_PLUGINS_ERROR)
302  *   </para></listitem>
303  *   <listitem><para>
304  *     3 if some of the requested plugins could be installed, but not all
305  *     (#GST_INSTALL_PLUGINS_PARTIAL_SUCCESS)
306  *   </para></listitem>
307  *   <listitem><para>
308  *     4 if the user aborted the installation (#GST_INSTALL_PLUGINS_USER_ABORT)
309  *   </para></listitem>
310  * </itemizedlist>
311  * </para>
312  * <para>
313  * <emphasis>5. How to map the required detail string to packages</emphasis>
314  * </para>
315  * <para>
316  * It is up to the vendor to find mechanism to map required components from
317  * the detail string to the actual packages/plugins to install. This could
318  * be a hardcoded list of mappings, for example, or be part of the packaging
319  * system metadata.
320  * </para>
321  * <para>
322  * GStreamer plugin files can be introspected for this information. The
323  * <literal>gst-inspect</literal> utility has a special command line option
324  * that will output information similar to what is required. For example
325  * <command>
326  * $ gst-inspect-0.10 --print-plugin-auto-install-info /path/to/libgstvorbis.so
327  * </command>
328  * should output something along the lines of
329  * <computeroutput>
330  * decoder-audio/x-vorbis
331  * element-vorbisdec
332  * element-vorbisenc
333  * element-vorbisparse
334  * element-vorbistag
335  * encoder-audio/x-vorbis
336  * </computeroutput>
337  * Note that in the encoder and decoder case the introspected caps can be more
338  * complex with additional fields, e.g.
339  * <literal>audio/mpeg,mpegversion=(int){2,4}</literal>, so they will not
340  * always exactly match the caps wanted by the application. It is up to the
341  * installer to deal with this (either by doing proper caps intersection using
342  * the GStreamer #GstCaps API, or by only taking into account the media type).
343  * </para>
344  * <para>
345  * Another potential source of problems are plugins such as ladspa or
346  * libvisual where the list of elements depends on the installed
347  * ladspa/libvisual plugins at the time. This is also up to the distribution
348  * to handle (but usually not relevant for playback applications).
349  * </para>
350  * </refsect2>
351  */
352
353 #ifdef HAVE_CONFIG_H
354 #include "config.h"
355 #endif
356
357 #include "install-plugins.h"
358
359 #include <gst/gstinfo.h>
360
361 #ifdef HAVE_SYS_TYPES_H
362 #include <sys/types.h>
363 #endif
364
365 #ifdef HAVE_SYS_WAIT_H
366 #include <sys/wait.h>
367 #endif
368
369 #include <string.h>
370
371 /* best effort to make things compile and possibly even work on win32 */
372 #ifndef WEXITSTATUS
373 # define WEXITSTATUS(status) ((((guint)(status)) & 0xff00) >> 8)
374 #endif
375 #ifndef WIFEXITED
376 # define WIFEXITED(status) ((((guint)(status)) & 0x7f) == 0)
377 #endif
378
379 static gboolean install_in_progress;    /* FALSE */
380
381 /* private struct */
382 struct _GstInstallPluginsContext
383 {
384   guint xid;
385 };
386
387 /**
388  * gst_install_plugins_context_set_xid:
389  * @ctx: a #GstInstallPluginsContext
390  * @xid: the XWindow ID (XID) of the top-level application
391  *
392  * This function is for X11-based applications (such as most Gtk/Qt
393  * applications on linux/unix) only. You can use it to tell the external
394  * installer the XID of your main application window. That way the installer
395  * can make its own window transient to your application window during the
396  * installation.
397  *
398  * If set, the XID will be passed to the installer via a --transient-for=XID
399  * command line option.
400  *
401  * Gtk+/Gnome application should be able to obtain the XID of the top-level
402  * window like this:
403  * <programlisting>
404  * ##include &lt;gtk/gtk.h&gt;
405  * ##ifdef GDK_WINDOWING_X11
406  * ##include &lt;gdk/gdkx.h&gt;
407  * ##endif
408  * ...
409  * ##ifdef GDK_WINDOWING_X11
410  *   xid = GDK_WINDOW_XWINDOW (GTK_WIDGET (application_window)-&gt;window);
411  * ##endif
412  * ...
413  * </programlisting>
414  *
415  * Since: 0.10.12
416  */
417 void
418 gst_install_plugins_context_set_xid (GstInstallPluginsContext * ctx, guint xid)
419 {
420   g_return_if_fail (ctx != NULL);
421
422   ctx->xid = xid;
423 }
424
425 /**
426  * gst_install_plugins_context_new:
427  *
428  * Creates a new #GstInstallPluginsContext.
429  *
430  * Returns: a new #GstInstallPluginsContext. Free with
431  * gst_install_plugins_context_free() when no longer needed
432  *
433  * Since: 0.10.12
434  */
435 GstInstallPluginsContext *
436 gst_install_plugins_context_new (void)
437 {
438   return g_new0 (GstInstallPluginsContext, 1);
439 }
440
441 /**
442  * gst_install_plugins_context_free:
443  * @ctx: a #GstInstallPluginsContext
444  *
445  * Frees a #GstInstallPluginsContext.
446  *
447  * Since: 0.10.12
448  */
449 void
450 gst_install_plugins_context_free (GstInstallPluginsContext * ctx)
451 {
452   g_return_if_fail (ctx != NULL);
453
454   g_free (ctx);
455 }
456
457 static GstInstallPluginsContext *
458 gst_install_plugins_context_copy (GstInstallPluginsContext * ctx)
459 {
460   GstInstallPluginsContext *ret;
461
462   ret = gst_install_plugins_context_new ();
463   ret->xid = ctx->xid;
464
465   return ret;
466 }
467
468 GType
469 gst_install_plugins_context_get_type (void)
470 {
471   static GType gst_ipc_type = 0;
472
473   if (G_UNLIKELY (gst_ipc_type == 0)) {
474     gst_ipc_type = g_boxed_type_register_static ("GstInstallPluginsContext",
475         (GBoxedCopyFunc) gst_install_plugins_context_copy,
476         (GBoxedFreeFunc) gst_install_plugins_context_free);
477   }
478   return gst_ipc_type;
479 }
480
481 static const gchar *
482 gst_install_plugins_get_helper (void)
483 {
484   const gchar *helper;
485
486   helper = g_getenv ("GST_INSTALL_PLUGINS_HELPER");
487   if (helper == NULL)
488     helper = GST_INSTALL_PLUGINS_HELPER;
489
490   GST_LOG ("Using plugin install helper '%s'", helper);
491   return helper;
492 }
493
494 static gboolean
495 ptr_array_contains_string (GPtrArray * arr, const gchar * s)
496 {
497   gint i;
498
499   for (i = 0; i < arr->len; ++i) {
500     if (strcmp ((const char *) g_ptr_array_index (arr, i), s) == 0)
501       return TRUE;
502   }
503   return FALSE;
504 }
505
506 static gboolean
507 gst_install_plugins_spawn_child (gchar ** details,
508     GstInstallPluginsContext * ctx, GPid * child_pid, gint * exit_status)
509 {
510   GPtrArray *arr;
511   gboolean ret;
512   GError *err = NULL;
513   gchar **argv, xid_str[64] = { 0, };
514
515   arr = g_ptr_array_new ();
516
517   /* argv[0] = helper path */
518   g_ptr_array_add (arr, (gchar *) gst_install_plugins_get_helper ());
519
520   /* add any additional command line args from the context */
521   if (ctx != NULL && ctx->xid != 0) {
522     g_snprintf (xid_str, sizeof (xid_str), "--transient-for=%u", ctx->xid);
523     g_ptr_array_add (arr, xid_str);
524   }
525
526   /* finally, add the detail strings, but without duplicates */
527   while (details != NULL && details[0] != NULL) {
528     if (!ptr_array_contains_string (arr, details[0]))
529       g_ptr_array_add (arr, details[0]);
530     ++details;
531   }
532
533   /* and NULL-terminate */
534   g_ptr_array_add (arr, NULL);
535
536   argv = (gchar **) arr->pdata;
537
538   if (child_pid == NULL && exit_status != NULL) {
539     install_in_progress = TRUE;
540     ret = g_spawn_sync (NULL, argv, NULL, (GSpawnFlags) 0, NULL, NULL,
541         NULL, NULL, exit_status, &err);
542     install_in_progress = FALSE;
543   } else if (child_pid != NULL && exit_status == NULL) {
544     install_in_progress = TRUE;
545     ret = g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL,
546         NULL, child_pid, &err);
547   } else {
548     g_return_val_if_reached (FALSE);
549   }
550
551   if (!ret) {
552     GST_ERROR ("Error spawning plugin install helper: %s", err->message);
553     g_error_free (err);
554   }
555
556   g_ptr_array_free (arr, TRUE);
557   return ret;
558 }
559
560 static GstInstallPluginsReturn
561 gst_install_plugins_return_from_status (gint status)
562 {
563   GstInstallPluginsReturn ret;
564
565   /* did we exit cleanly? */
566   if (!WIFEXITED (status)) {
567     ret = GST_INSTALL_PLUGINS_CRASHED;
568   } else {
569     ret = (GstInstallPluginsReturn) WEXITSTATUS (status);
570
571     /* did the helper return an invalid status code? */
572     if ((ret < 0 || ret >= GST_INSTALL_PLUGINS_STARTED_OK) &&
573         ret != GST_INSTALL_PLUGINS_INTERNAL_FAILURE) {
574       ret = GST_INSTALL_PLUGINS_INVALID;
575     }
576   }
577
578   GST_LOG ("plugin installer exited with status 0x%04x = %s", status,
579       gst_install_plugins_return_get_name (ret));
580
581   return ret;
582 }
583
584 typedef struct
585 {
586   GstInstallPluginsResultFunc func;
587   gpointer user_data;
588 } GstInstallPluginsAsyncHelper;
589
590 static void
591 gst_install_plugins_installer_exited (GPid pid, gint status, gpointer data)
592 {
593   GstInstallPluginsAsyncHelper *helper;
594   GstInstallPluginsReturn ret;
595
596   install_in_progress = FALSE;
597
598   helper = (GstInstallPluginsAsyncHelper *) data;
599   ret = gst_install_plugins_return_from_status (status);
600
601   GST_LOG ("calling plugin install result function %p", helper->func);
602   helper->func (ret, helper->user_data);
603
604   g_free (helper);
605 }
606
607 /**
608  * gst_install_plugins_async:
609  * @details: NULL-terminated array of installer string details (see below)
610  * @ctx: a #GstInstallPluginsContext, or NULL
611  * @func: (scope async): the function to call when the installer program returns
612  * @user_data: (closure): the user data to pass to @func when called, or NULL
613  * 
614  * Requests plugin installation without blocking. Once the plugins have been
615  * installed or installation has failed, @func will be called with the result
616  * of the installation and your provided @user_data pointer.
617  *
618  * This function requires a running GLib/Gtk main loop. If you are not
619  * running a GLib/Gtk main loop, make sure to regularly call
620  * g_main_context_iteration(NULL,FALSE).
621  *
622  * The installer strings that make up @detail are typically obtained by
623  * calling gst_missing_plugin_message_get_installer_detail() on missing-plugin
624  * messages that have been caught on a pipeline's bus or created by the
625  * application via the provided API, such as gst_missing_element_message_new().
626  *
627  * It is possible to request the installation of multiple missing plugins in
628  * one go (as might be required if there is a demuxer for a certain format
629  * installed but no suitable video decoder and no suitable audio decoder).
630  *
631  * Returns: result code whether an external installer could be started
632  *
633  * Since: 0.10.12
634  */
635
636 GstInstallPluginsReturn
637 gst_install_plugins_async (gchar ** details, GstInstallPluginsContext * ctx,
638     GstInstallPluginsResultFunc func, gpointer user_data)
639 {
640   GstInstallPluginsAsyncHelper *helper;
641   GPid pid;
642
643   g_return_val_if_fail (details != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE);
644   g_return_val_if_fail (func != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE);
645
646   if (install_in_progress)
647     return GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS;
648
649   /* if we can't access our helper, don't bother */
650   if (!g_file_test (gst_install_plugins_get_helper (),
651           G_FILE_TEST_IS_EXECUTABLE))
652     return GST_INSTALL_PLUGINS_HELPER_MISSING;
653
654   if (!gst_install_plugins_spawn_child (details, ctx, &pid, NULL))
655     return GST_INSTALL_PLUGINS_INTERNAL_FAILURE;
656
657   helper = g_new (GstInstallPluginsAsyncHelper, 1);
658   helper->func = func;
659   helper->user_data = user_data;
660
661   g_child_watch_add (pid, gst_install_plugins_installer_exited, helper);
662
663   return GST_INSTALL_PLUGINS_STARTED_OK;
664 }
665
666 /**
667  * gst_install_plugins_sync:
668  * @details: NULL-terminated array of installer string details
669  * @ctx: a #GstInstallPluginsContext, or NULL
670  * 
671  * Requests plugin installation and block until the plugins have been
672  * installed or installation has failed.
673  *
674  * This function should almost never be used, it only exists for cases where
675  * a non-GLib main loop is running and the user wants to run it in a separate
676  * thread and marshal the result back asynchronously into the main thread
677  * using the other non-GLib main loop. You should almost always use
678  * gst_install_plugins_async() instead of this function.
679  *
680  * Returns: the result of the installation.
681  *
682  * Since: 0.10.12
683  */
684 GstInstallPluginsReturn
685 gst_install_plugins_sync (gchar ** details, GstInstallPluginsContext * ctx)
686 {
687   gint status;
688
689   g_return_val_if_fail (details != NULL, GST_INSTALL_PLUGINS_INTERNAL_FAILURE);
690
691   if (install_in_progress)
692     return GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS;
693
694   /* if we can't access our helper, don't bother */
695   if (!g_file_test (gst_install_plugins_get_helper (),
696           G_FILE_TEST_IS_EXECUTABLE))
697     return GST_INSTALL_PLUGINS_HELPER_MISSING;
698
699   if (!gst_install_plugins_spawn_child (details, ctx, NULL, &status))
700     return GST_INSTALL_PLUGINS_INTERNAL_FAILURE;
701
702   return gst_install_plugins_return_from_status (status);
703 }
704
705 /**
706  * gst_install_plugins_return_get_name:
707  * @ret: the return status code
708  * 
709  * Convenience function to return the descriptive string associated
710  * with a status code.  This function returns English strings and
711  * should not be used for user messages. It is here only to assist
712  * in debugging.
713  *
714  * Returns: a descriptive string for the status code in @ret
715  *
716  * Since: 0.10.12
717  */
718 const gchar *
719 gst_install_plugins_return_get_name (GstInstallPluginsReturn ret)
720 {
721   switch (ret) {
722     case GST_INSTALL_PLUGINS_SUCCESS:
723       return "success";
724     case GST_INSTALL_PLUGINS_NOT_FOUND:
725       return "not-found";
726     case GST_INSTALL_PLUGINS_ERROR:
727       return "install-error";
728     case GST_INSTALL_PLUGINS_CRASHED:
729       return "installer-exit-unclean";
730     case GST_INSTALL_PLUGINS_PARTIAL_SUCCESS:
731       return "partial-success";
732     case GST_INSTALL_PLUGINS_USER_ABORT:
733       return "user-abort";
734     case GST_INSTALL_PLUGINS_STARTED_OK:
735       return "started-ok";
736     case GST_INSTALL_PLUGINS_INTERNAL_FAILURE:
737       return "internal-failure";
738     case GST_INSTALL_PLUGINS_HELPER_MISSING:
739       return "helper-missing";
740     case GST_INSTALL_PLUGINS_INSTALL_IN_PROGRESS:
741       return "install-in-progress";
742     case GST_INSTALL_PLUGINS_INVALID:
743       return "invalid";
744     default:
745       break;
746   }
747   return "(UNKNOWN)";
748 }
749
750 /**
751  * gst_install_plugins_installation_in_progress:
752  * 
753  * Checks whether plugin installation (initiated by this application only)
754  * is currently in progress.
755  *
756  * Returns: TRUE if plugin installation is in progress, otherwise FALSE
757  *
758  * Since: 0.10.12
759  */
760 gboolean
761 gst_install_plugins_installation_in_progress (void)
762 {
763   return install_in_progress;
764 }
765
766 /**
767  * gst_install_plugins_supported:
768  * 
769  * Checks whether plugin installation is likely to be supported by the
770  * current environment. This currently only checks whether the helper script
771  * that is to be provided by the distribution or operating system vendor
772  * exists.
773  *
774  * Returns: TRUE if plugin installation is likely to be supported.
775  *
776  * Since: 0.10.15
777  */
778 gboolean
779 gst_install_plugins_supported (void)
780 {
781   return g_file_test (gst_install_plugins_get_helper (),
782       G_FILE_TEST_IS_EXECUTABLE);
783 }