1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* activation.c Activation of services
4 * Copyright (C) 2003 CodeFactory AB
5 * Copyright (C) 2003 Red Hat, Inc.
7 * Licensed under the Academic Free License version 1.2
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "activation.h"
25 #include "desktop-file.h"
28 #include <dbus/dbus-internals.h>
29 #include <dbus/dbus-hash.h>
30 #include <dbus/dbus-list.h>
31 #include <dbus/dbus-spawn.h>
32 #include <dbus/dbus-timeout.h>
33 #include <sys/types.h>
37 #define DBUS_SERVICE_SECTION "D-BUS Service"
38 #define DBUS_SERVICE_NAME "Name"
39 #define DBUS_SERVICE_EXEC "Exec"
44 DBusHashTable *entries;
45 DBusHashTable *pending_activations;
56 typedef struct BusPendingActivationEntry BusPendingActivationEntry;
58 struct BusPendingActivationEntry
60 DBusMessage *activation_message;
61 DBusConnection *connection;
66 BusActivation *activation;
69 DBusBabysitter *babysitter;
71 unsigned int timeout_added : 1;
72 } BusPendingActivation;
75 bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
77 if (entry->activation_message)
78 dbus_message_unref (entry->activation_message);
80 if (entry->connection)
81 dbus_connection_unref (entry->connection);
87 handle_timeout_callback (DBusTimeout *timeout,
90 BusPendingActivation *pending_activation = data;
92 while (!dbus_timeout_handle (pending_activation->timeout))
93 _dbus_wait_for_memory ();
97 bus_pending_activation_free (BusPendingActivation *pending_activation)
101 if (pending_activation == NULL) /* hash table requires this */
104 if (pending_activation->timeout_added)
106 _dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
107 pending_activation->timeout,
108 handle_timeout_callback, pending_activation);
109 pending_activation->timeout_added = FALSE;
112 if (pending_activation->timeout)
113 _dbus_timeout_unref (pending_activation->timeout);
115 if (pending_activation->babysitter)
117 if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
119 pending_activation->babysitter,
121 _dbus_assert_not_reached ("setting watch functions to NULL failed");
123 _dbus_babysitter_unref (pending_activation->babysitter);
126 dbus_free (pending_activation->service_name);
128 link = _dbus_list_get_first_link (&pending_activation->entries);
132 BusPendingActivationEntry *entry = link->data;
134 bus_pending_activation_entry_free (entry);
136 link = _dbus_list_get_next_link (&pending_activation->entries, link);
138 _dbus_list_clear (&pending_activation->entries);
140 dbus_free (pending_activation);
144 bus_activation_entry_free (BusActivationEntry *entry)
149 dbus_free (entry->name);
150 dbus_free (entry->exec);
156 add_desktop_file_entry (BusActivation *activation,
157 BusDesktopFile *desktop_file,
161 BusActivationEntry *entry;
163 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
169 if (!bus_desktop_file_get_string (desktop_file,
170 DBUS_SERVICE_SECTION,
174 dbus_set_error (error, DBUS_ERROR_FAILED,
175 "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
179 if (!bus_desktop_file_get_string (desktop_file,
180 DBUS_SERVICE_SECTION,
184 dbus_set_error (error, DBUS_ERROR_FAILED,
185 "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
189 /* FIXME we need a better-defined algorithm for which service file to
190 * pick than "whichever one is first in the directory listing"
192 if (_dbus_hash_table_lookup_string (activation->entries, name))
194 dbus_set_error (error, DBUS_ERROR_FAILED,
195 "Service %s already exists in activation entry list\n", name);
199 entry = dbus_new0 (BusActivationEntry, 1);
209 if (!_dbus_hash_table_insert_string (activation->entries, entry->name, entry))
215 _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
227 /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
228 * hash entries it already added.
231 load_directory (BusActivation *activation,
232 const char *directory,
236 DBusString dir, filename;
237 DBusString full_path;
238 BusDesktopFile *desktop_file;
242 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
244 _dbus_string_init_const (&dir, directory);
249 if (!_dbus_string_init (&filename))
255 if (!_dbus_string_init (&full_path))
258 _dbus_string_free (&filename);
264 /* from this point it's safe to "goto out" */
266 iter = _dbus_directory_open (&dir, error);
269 _dbus_verbose ("Failed to open directory %s: %s\n",
270 directory, error ? error->message : "unknown");
274 /* Now read the files */
275 dbus_error_init (&tmp_error);
276 while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
278 _dbus_assert (!dbus_error_is_set (&tmp_error));
280 _dbus_string_set_length (&full_path, 0);
282 if (!_dbus_string_append (&full_path, directory) ||
283 !_dbus_concat_dir_and_file (&full_path, &filename))
289 if (!_dbus_string_ends_with_c_str (&filename, ".service"))
291 _dbus_verbose ("Skipping non-.service file %s\n",
292 _dbus_string_get_const_data (&filename));
296 desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
298 if (desktop_file == NULL)
300 _dbus_verbose ("Could not load %s: %s\n",
301 _dbus_string_get_const_data (&full_path),
304 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
306 dbus_move_error (&tmp_error, error);
310 dbus_error_free (&tmp_error);
314 if (!add_desktop_file_entry (activation, desktop_file, &tmp_error))
316 bus_desktop_file_free (desktop_file);
319 _dbus_verbose ("Could not add %s to activation entry list: %s\n",
320 _dbus_string_get_const_data (&full_path), tmp_error.message);
322 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
324 dbus_move_error (&tmp_error, error);
328 dbus_error_free (&tmp_error);
333 bus_desktop_file_free (desktop_file);
339 if (dbus_error_is_set (&tmp_error))
341 dbus_move_error (&tmp_error, error);
349 _DBUS_ASSERT_ERROR_IS_SET (error);
351 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
354 _dbus_directory_close (iter);
356 bus_desktop_file_free (desktop_file);
357 _dbus_string_free (&filename);
358 _dbus_string_free (&full_path);
364 bus_activation_new (BusContext *context,
365 const DBusString *address,
366 DBusList **directories,
369 BusActivation *activation;
372 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
374 activation = dbus_new0 (BusActivation, 1);
375 if (activation == NULL)
381 activation->refcount = 1;
382 activation->context = context;
384 if (!_dbus_string_copy_data (address, &activation->server_address))
390 activation->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
391 (DBusFreeFunction)bus_activation_entry_free);
392 if (activation->entries == NULL)
398 activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
399 (DBusFreeFunction)bus_pending_activation_free);
401 if (activation->pending_activations == NULL)
407 /* Load service files */
408 link = _dbus_list_get_first_link (directories);
411 if (!load_directory (activation, link->data, error))
413 link = _dbus_list_get_next_link (directories, link);
419 bus_activation_unref (activation);
424 bus_activation_ref (BusActivation *activation)
426 _dbus_assert (activation->refcount > 0);
428 activation->refcount += 1;
432 bus_activation_unref (BusActivation *activation)
434 _dbus_assert (activation->refcount > 0);
436 activation->refcount -= 1;
438 if (activation->refcount == 0)
440 dbus_free (activation->server_address);
441 if (activation->entries)
442 _dbus_hash_table_unref (activation->entries);
443 if (activation->pending_activations)
444 _dbus_hash_table_unref (activation->pending_activations);
445 dbus_free (activation);
450 child_setup (void *data)
452 BusActivation *activation = data;
455 /* If no memory, we simply have the child exit, so it won't try
456 * to connect to the wrong thing.
458 if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", activation->server_address))
461 type = bus_context_get_type (activation->context);
464 if (!_dbus_setenv ("DBUS_BUS_TYPE", type))
470 bus_activation_service_created (BusActivation *activation,
471 const char *service_name,
472 BusTransaction *transaction,
475 BusPendingActivation *pending_activation;
476 DBusMessage *message;
479 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
481 /* Check if it's a pending activation */
482 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
484 if (!pending_activation)
487 link = _dbus_list_get_first_link (&pending_activation->entries);
490 BusPendingActivationEntry *entry = link->data;
491 DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
493 if (dbus_connection_get_is_connected (entry->connection))
495 message = dbus_message_new_reply (entry->activation_message);
502 if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
503 !dbus_message_append_args (message,
504 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
507 dbus_message_unref (message);
512 if (!bus_transaction_send_message (transaction, entry->connection, message))
514 dbus_message_unref (message);
519 dbus_message_unref (message);
525 _dbus_hash_table_remove_string (activation->pending_activations, service_name);
530 _dbus_hash_table_remove_string (activation->pending_activations, service_name);
535 * FIXME @todo the error messages here would ideally be preallocated
536 * so we don't need to allocate memory to send them.
537 * Using the usual tactic, prealloc an OOM message, then
538 * if we can't alloc the real error send the OOM error instead.
541 try_send_activation_failure (BusPendingActivation *pending_activation,
542 const DBusError *how)
544 BusActivation *activation;
545 DBusMessage *message;
547 BusTransaction *transaction;
549 activation = pending_activation->activation;
551 transaction = bus_transaction_new (activation->context);
552 if (transaction == NULL)
555 link = _dbus_list_get_first_link (&pending_activation->entries);
558 BusPendingActivationEntry *entry = link->data;
559 DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
561 if (dbus_connection_get_is_connected (entry->connection))
563 message = dbus_message_new_error_reply (entry->activation_message,
569 if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
571 dbus_message_unref (message);
575 if (!bus_transaction_send_message (transaction, entry->connection, message))
577 dbus_message_unref (message);
581 dbus_message_unref (message);
587 bus_transaction_execute_and_free (transaction);
593 bus_transaction_cancel_and_free (transaction);
598 * Free the pending activation and send an error message to all the
599 * connections that were waiting for it.
602 pending_activation_failed (BusPendingActivation *pending_activation,
603 const DBusError *how)
605 /* FIXME use preallocated OOM messages instead of bus_wait_for_memory() */
606 while (!try_send_activation_failure (pending_activation, how))
607 _dbus_wait_for_memory ();
609 /* Destroy this pending activation */
610 _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
611 pending_activation->service_name);
615 babysitter_watch_callback (DBusWatch *watch,
616 unsigned int condition,
619 BusPendingActivation *pending_activation = data;
621 DBusBabysitter *babysitter;
623 babysitter = pending_activation->babysitter;
625 _dbus_babysitter_ref (babysitter);
627 retval = _dbus_babysitter_handle_watch (babysitter, watch, condition);
629 if (_dbus_babysitter_get_child_exited (babysitter))
633 dbus_error_init (&error);
634 _dbus_babysitter_set_child_exit_error (babysitter, &error);
636 /* Destroys the pending activation */
637 pending_activation_failed (pending_activation, &error);
639 dbus_error_free (&error);
642 _dbus_babysitter_unref (babysitter);
648 add_babysitter_watch (DBusWatch *watch,
651 BusPendingActivation *pending_activation = data;
653 return _dbus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
654 watch, babysitter_watch_callback, pending_activation,
659 remove_babysitter_watch (DBusWatch *watch,
662 BusPendingActivation *pending_activation = data;
664 _dbus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
665 watch, babysitter_watch_callback, pending_activation);
669 pending_activation_timed_out (void *data)
671 BusPendingActivation *pending_activation = data;
674 /* Kill the spawned process, since it sucks
675 * (not sure this is what we want to do, but
676 * may as well try it for now)
678 _dbus_babysitter_kill_child (pending_activation->babysitter);
680 dbus_error_init (&error);
682 dbus_set_error (&error, DBUS_ERROR_TIMED_OUT,
683 "Activation of %s timed out",
684 pending_activation->service_name);
686 pending_activation_failed (pending_activation, &error);
688 dbus_error_free (&error);
694 bus_activation_activate_service (BusActivation *activation,
695 DBusConnection *connection,
696 BusTransaction *transaction,
697 DBusMessage *activation_message,
698 const char *service_name,
701 BusActivationEntry *entry;
702 BusPendingActivation *pending_activation;
703 BusPendingActivationEntry *pending_activation_entry;
704 DBusMessage *message;
705 DBusString service_str;
709 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
711 entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
715 dbus_set_error (error, DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND,
716 "The service %s was not found in the activation entry list",
721 /* Check if the service is active */
722 _dbus_string_init_const (&service_str, service_name);
723 if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
725 message = dbus_message_new_reply (activation_message);
733 if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
734 !dbus_message_append_args (message,
735 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE,
739 dbus_message_unref (message);
743 retval = bus_transaction_send_message (transaction, connection, message);
744 dbus_message_unref (message);
751 pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
752 if (!pending_activation_entry)
758 pending_activation_entry->activation_message = activation_message;
759 dbus_message_ref (activation_message);
760 pending_activation_entry->connection = connection;
761 dbus_connection_ref (connection);
763 /* Check if the service is being activated */
764 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
765 if (pending_activation)
767 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
770 bus_pending_activation_entry_free (pending_activation_entry);
777 pending_activation = dbus_new0 (BusPendingActivation, 1);
778 if (!pending_activation)
781 bus_pending_activation_entry_free (pending_activation_entry);
785 pending_activation->activation = activation;
787 pending_activation->service_name = _dbus_strdup (service_name);
788 if (!pending_activation->service_name)
791 bus_pending_activation_free (pending_activation);
792 bus_pending_activation_entry_free (pending_activation_entry);
796 pending_activation->timeout =
797 _dbus_timeout_new (bus_context_get_activation_timeout (activation->context),
798 pending_activation_timed_out,
801 if (!pending_activation->timeout)
804 bus_pending_activation_free (pending_activation);
805 bus_pending_activation_entry_free (pending_activation_entry);
809 if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
810 pending_activation->timeout,
811 handle_timeout_callback,
816 bus_pending_activation_free (pending_activation);
817 bus_pending_activation_entry_free (pending_activation_entry);
821 pending_activation->timeout_added = TRUE;
823 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
826 bus_pending_activation_free (pending_activation);
827 bus_pending_activation_entry_free (pending_activation_entry);
831 if (!_dbus_hash_table_insert_string (activation->pending_activations,
832 pending_activation->service_name, pending_activation))
835 bus_pending_activation_free (pending_activation);
840 /* FIXME we need to support a full command line, not just a single
844 /* Now try to spawn the process */
845 argv[0] = entry->exec;
848 if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv,
849 child_setup, activation,
852 _dbus_hash_table_remove_string (activation->pending_activations,
853 pending_activation->service_name);
857 _dbus_assert (pending_activation->babysitter != NULL);
859 if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
860 add_babysitter_watch,
861 remove_babysitter_watch,
868 _dbus_hash_table_remove_string (activation->pending_activations,
869 pending_activation->service_name);