2 * Copyright (C) 2006 Jonathan Matthew <jonathan@kaolin.hn.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Client for out-of-process metadata reader communicating via D-BUS.
24 * - spawn rb-metadata process, with pipes
25 * - child process sets up its dbus server or whatever
26 * - if successful, child writes dbus server address to stdout; otherwise, dies.
27 * - parent opens dbus connection
29 * For each request, the parent checks if the dbus connection is still alive,
30 * and pings the child to see if it's still responding. If the child has
31 * exited or is not responding, the parent starts a new metadata helper as
34 * The child process exits after a certain period of inactivity (30s
35 * currently), so the ping message serves two purposes - it checks that the
36 * child is still capable of handling messages, and it ensures the child
37 * doesn't time out between when we check the child is still running and when
38 * we actually send it the request.
43 #include "rb-metadata.h"
44 #include "rb-metadata-dbus.h"
48 #include <dbus/dbus.h>
49 #include <dbus/dbus-glib.h>
50 #include <dbus/dbus-glib-lowlevel.h>
51 #include <glib/gi18n.h>
54 #include <sys/types.h>
55 #include <sys/signal.h>
60 static void rb_metadata_class_init (RBMetaDataClass
*klass
);
61 static void rb_metadata_init (RBMetaData
*md
);
62 static void rb_metadata_finalize (GObject
*object
);
64 static gboolean tried_env_address
= FALSE
;
65 static DBusConnection
*dbus_connection
= NULL
;
66 static GPid metadata_child
= 0;
67 static GMainContext
*main_context
= NULL
;
68 static GStaticMutex conn_mutex
= G_STATIC_MUTEX_INIT
;
70 struct RBMetaDataPrivate
77 G_DEFINE_TYPE (RBMetaData
, rb_metadata
, G_TYPE_OBJECT
)
80 rb_metadata_class_init (RBMetaDataClass
*klass
)
82 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
83 object_class
->finalize
= rb_metadata_finalize
;
85 g_type_class_add_private (object_class
, sizeof (RBMetaDataPrivate
));
87 main_context
= g_main_context_new (); /* maybe not needed? */
91 rb_metadata_init (RBMetaData
*md
)
93 md
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (md
, RB_TYPE_METADATA
, RBMetaDataPrivate
);
97 rb_metadata_finalize (GObject
*object
)
101 md
= RB_METADATA (object
);
102 g_free (md
->priv
->uri
);
103 g_free (md
->priv
->mimetype
);
104 if (md
->priv
->metadata
)
105 g_hash_table_destroy (md
->priv
->metadata
);
107 G_OBJECT_CLASS (rb_metadata_parent_class
)->finalize (object
);
111 rb_metadata_new (void)
113 return RB_METADATA (g_object_new (RB_TYPE_METADATA
, NULL
));
117 kill_metadata_service (void)
119 if (dbus_connection
) {
120 if (dbus_connection_get_is_connected (dbus_connection
)) {
121 rb_debug ("closing dbus connection");
123 dbus_connection_disconnect (dbus_connection
);
125 dbus_connection_close (dbus_connection
);
128 rb_debug ("dbus connection already closed");
130 dbus_connection_unref (dbus_connection
);
131 dbus_connection
= NULL
;
134 if (metadata_child
) {
135 rb_debug ("killing child process");
136 kill (metadata_child
, SIGINT
);
137 g_spawn_close_pid (metadata_child
);
143 ping_metadata_service (GError
**error
)
145 DBusMessage
*message
, *response
;
146 DBusError dbus_error
= {0,};
148 if (!dbus_connection_get_is_connected (dbus_connection
))
151 message
= dbus_message_new_method_call (RB_METADATA_DBUS_NAME
,
152 RB_METADATA_DBUS_OBJECT_PATH
,
153 RB_METADATA_DBUS_INTERFACE
,
158 response
= dbus_connection_send_with_reply_and_block (dbus_connection
,
160 RB_METADATA_DBUS_TIMEOUT
,
162 dbus_message_unref (message
);
163 if (dbus_error_is_set (&dbus_error
)) {
164 /* ignore 'no reply': just means the service is dead */
165 if (strcmp (dbus_error
.name
, DBUS_ERROR_NO_REPLY
)) {
166 dbus_set_g_error (error
, &dbus_error
);
168 dbus_error_free (&dbus_error
);
171 dbus_message_unref (response
);
176 start_metadata_service (GError
**error
)
179 * Normally, we find the metadata helper in the libexec dir,
180 * but when --enable-uninstalled-build is specified, we look
181 * in the directory it's built in.
184 #ifdef METADATA_UNINSTALLED_DIR
185 METADATA_UNINSTALLED_DIR
"/rhythmbox-metadata",
187 LIBEXEC_DIR
"/rhythmbox-metadata",
189 "unix:tmpdir=/tmp", NULL
191 DBusError dbus_error
= {0,};
192 GIOChannel
*stdout_channel
;
194 gchar
*dbus_address
= NULL
;
196 if (dbus_connection
) {
197 if (ping_metadata_service (error
))
200 /* Metadata service is broken. Kill it, and if we haven't run
201 * into any errors yet, we can try to restart it.
203 kill_metadata_service ();
209 if (!tried_env_address
) {
210 const char *addr
= g_getenv ("RB_DBUS_METADATA_ADDRESS");
211 tried_env_address
= TRUE
;
213 rb_debug ("trying metadata service address %s (from environment)", addr
);
214 dbus_address
= g_strdup (addr
);
219 if (dbus_address
== NULL
) {
220 gint metadata_stdout
;
222 if (!g_spawn_async_with_pipes (NULL
,
235 stdout_channel
= g_io_channel_unix_new (metadata_stdout
);
236 status
= g_io_channel_read_line (stdout_channel
, &dbus_address
, NULL
, NULL
, error
);
237 g_io_channel_unref (stdout_channel
);
238 if (status
!= G_IO_STATUS_NORMAL
) {
239 kill_metadata_service ();
243 g_strchomp (dbus_address
);
244 rb_debug ("Got metadata helper D-BUS address %s", dbus_address
);
247 dbus_connection
= dbus_connection_open_private (dbus_address
, &dbus_error
);
248 g_free (dbus_address
);
249 if (!dbus_connection
) {
250 kill_metadata_service ();
252 dbus_set_g_error (error
, &dbus_error
);
253 dbus_error_free (&dbus_error
);
256 dbus_connection_set_exit_on_disconnect (dbus_connection
, FALSE
);
258 dbus_connection_setup_with_g_main (dbus_connection
, main_context
);
260 rb_debug ("Metadata process %d started", metadata_child
);
265 handle_dbus_error (RBMetaData
*md
, DBusError
*dbus_error
, GError
**error
)
268 * If the error is 'no reply within the specified time',
269 * then we assume that either the metadata process died, or
270 * it's stuck in a loop and needs to be killed.
272 if (strcmp (dbus_error
->name
, DBUS_ERROR_NO_REPLY
) == 0) {
273 kill_metadata_service ();
277 RB_METADATA_ERROR_INTERNAL
,
278 _("Internal GStreamer problem; file a bug"));
280 dbus_set_g_error (error
, dbus_error
);
281 dbus_error_free (dbus_error
);
286 read_error_from_message (RBMetaData
*md
, DBusMessageIter
*iter
, GError
**error
)
289 gchar
*error_message
;
291 if (!rb_metadata_dbus_get_uint32 (iter
, &error_code
) ||
292 !rb_metadata_dbus_get_string (iter
, &error_message
)) {
295 RB_METADATA_ERROR_INTERNAL
,
296 _("D-BUS communication error"));
300 g_set_error (error
, RB_METADATA_ERROR
,
302 "%s", error_message
);
303 g_free (error_message
);
307 rb_metadata_load (RBMetaData
*md
,
311 DBusMessage
*message
= NULL
;
312 DBusMessage
*response
= NULL
;
313 DBusMessageIter iter
;
314 DBusError dbus_error
= {0,};
316 GError
*fake_error
= NULL
;
321 g_free (md
->priv
->mimetype
);
322 md
->priv
->mimetype
= NULL
;
324 g_free (md
->priv
->uri
);
325 md
->priv
->uri
= g_strdup (uri
);
329 if (md
->priv
->metadata
)
330 g_hash_table_destroy (md
->priv
->metadata
);
331 md
->priv
->metadata
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, (GDestroyNotify
)rb_value_free
);
333 g_static_mutex_lock (&conn_mutex
);
335 start_metadata_service (error
);
337 if (*error
== NULL
) {
338 message
= dbus_message_new_method_call (RB_METADATA_DBUS_NAME
,
339 RB_METADATA_DBUS_OBJECT_PATH
,
340 RB_METADATA_DBUS_INTERFACE
,
345 RB_METADATA_ERROR_INTERNAL
,
346 _("D-BUS communication error"));
347 } else if (!dbus_message_append_args (message
, DBUS_TYPE_STRING
, &uri
, DBUS_TYPE_INVALID
)) {
350 RB_METADATA_ERROR_INTERNAL
,
351 _("D-BUS communication error"));
355 if (*error
== NULL
) {
356 rb_debug ("sending metadata load request");
357 response
= dbus_connection_send_with_reply_and_block (dbus_connection
,
359 RB_METADATA_DBUS_TIMEOUT
,
363 handle_dbus_error (md
, &dbus_error
, error
);
366 if (*error
== NULL
) {
367 if (!dbus_message_iter_init (response
, &iter
)) {
370 RB_METADATA_ERROR_INTERNAL
,
371 _("D-BUS communication error"));
372 rb_debug ("couldn't read response message");
376 if (*error
== NULL
) {
377 if (!rb_metadata_dbus_get_boolean (&iter
, &ok
)) {
380 RB_METADATA_ERROR_INTERNAL
,
381 _("D-BUS communication error"));
382 rb_debug ("couldn't get success flag from response message");
385 if (!rb_metadata_dbus_get_string (&iter
, &md
->priv
->mimetype
)) {
388 RB_METADATA_ERROR_INTERNAL
,
389 _("D-BUS communication error"));
392 rb_debug ("got mimetype: %s", md
->priv
->mimetype
);
393 rb_metadata_dbus_read_from_message (md
, md
->priv
->metadata
, &iter
);
396 read_error_from_message (md
, &iter
, error
);
401 dbus_message_unref (message
);
403 dbus_message_unref (response
);
405 g_error_free (fake_error
);
407 g_static_mutex_unlock (&conn_mutex
);
411 rb_metadata_get_mime (RBMetaData
*md
)
413 return md
->priv
->mimetype
;
417 rb_metadata_get (RBMetaData
*md
, RBMetaDataField field
,
421 if (!md
->priv
->metadata
)
424 if ((val
= g_hash_table_lookup (md
->priv
->metadata
,
425 GINT_TO_POINTER (field
)))) {
426 g_value_init (ret
, G_VALUE_TYPE (val
));
427 g_value_copy (val
, ret
);
434 rb_metadata_set (RBMetaData
*md
, RBMetaDataField field
,
440 type
= rb_metadata_get_field_type (field
);
441 g_return_val_if_fail (type
== G_VALUE_TYPE (val
), FALSE
);
443 newval
= g_new0 (GValue
, 1);
444 g_value_init (newval
, type
);
445 g_value_copy (val
, newval
);
447 g_hash_table_insert (md
->priv
->metadata
, GINT_TO_POINTER (field
),
453 rb_metadata_can_save (RBMetaData
*md
, const char *mimetype
)
455 GError
*error
= NULL
;
456 DBusMessage
*message
= NULL
;
457 DBusMessage
*response
= NULL
;
458 gboolean can_save
= FALSE
;
459 DBusError dbus_error
= {0,};
460 DBusMessageIter iter
;
463 g_static_mutex_lock (&conn_mutex
);
465 if (start_metadata_service (&error
) == FALSE
) {
466 g_error_free (error
);
471 message
= dbus_message_new_method_call (RB_METADATA_DBUS_NAME
,
472 RB_METADATA_DBUS_OBJECT_PATH
,
473 RB_METADATA_DBUS_INTERFACE
,
477 } else if (!dbus_message_append_args (message
, DBUS_TYPE_STRING
, &mimetype
, DBUS_TYPE_INVALID
)) {
483 response
= dbus_connection_send_with_reply_and_block (dbus_connection
,
485 RB_METADATA_DBUS_TIMEOUT
,
488 dbus_error_free (&dbus_error
);
490 } else if (dbus_message_iter_init (response
, &iter
)) {
491 rb_metadata_dbus_get_boolean (&iter
, &can_save
);
496 dbus_message_unref (message
);
498 dbus_message_unref (response
);
499 g_static_mutex_unlock (&conn_mutex
);
505 rb_metadata_save (RBMetaData
*md
, GError
**error
)
507 GError
*fake_error
= NULL
;
508 DBusMessage
*message
= NULL
;
509 DBusMessage
*response
= NULL
;
510 DBusError dbus_error
= {0,};
511 DBusMessageIter iter
;
516 g_static_mutex_lock (&conn_mutex
);
518 start_metadata_service (error
);
520 if (*error
== NULL
) {
521 message
= dbus_message_new_method_call (RB_METADATA_DBUS_NAME
,
522 RB_METADATA_DBUS_OBJECT_PATH
,
523 RB_METADATA_DBUS_INTERFACE
,
528 RB_METADATA_ERROR_INTERNAL
,
529 _("D-BUS communication error"));
533 if (*error
== NULL
) {
534 dbus_message_iter_init_append (message
, &iter
);
535 if (!rb_metadata_dbus_add_to_message (md
, &iter
)) {
538 RB_METADATA_ERROR_INTERNAL
,
539 _("D-BUS communication error"));
543 if (*error
== NULL
) {
544 response
= dbus_connection_send_with_reply_and_block (dbus_connection
,
546 RB_METADATA_DBUS_TIMEOUT
,
549 handle_dbus_error (md
, &dbus_error
, error
);
550 } else if (dbus_message_iter_init (response
, &iter
)) {
551 /* if there's any return data at all, it'll be an error */
552 read_error_from_message (md
, &iter
, error
);
557 dbus_message_unref (message
);
559 dbus_message_unref (response
);
561 g_error_free (fake_error
);
563 g_static_mutex_unlock (&conn_mutex
);