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;
67 BusActivation *activation;
70 DBusBabysitter *babysitter;
72 unsigned int timeout_added : 1;
73 } BusPendingActivation;
76 bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
78 if (entry->activation_message)
79 dbus_message_unref (entry->activation_message);
81 if (entry->connection)
82 dbus_connection_unref (entry->connection);
88 handle_timeout_callback (DBusTimeout *timeout,
91 BusPendingActivation *pending_activation = data;
93 while (!dbus_timeout_handle (pending_activation->timeout))
94 _dbus_wait_for_memory ();
98 bus_pending_activation_ref (BusPendingActivation *pending_activation)
100 _dbus_assert (pending_activation->refcount > 0);
101 pending_activation->refcount += 1;
105 bus_pending_activation_unref (BusPendingActivation *pending_activation)
109 if (pending_activation == NULL) /* hash table requires this */
112 _dbus_assert (pending_activation->refcount > 0);
113 pending_activation->refcount -= 1;
115 if (pending_activation->refcount > 0)
118 if (pending_activation->timeout_added)
120 _dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
121 pending_activation->timeout,
122 handle_timeout_callback, pending_activation);
123 pending_activation->timeout_added = FALSE;
126 if (pending_activation->timeout)
127 _dbus_timeout_unref (pending_activation->timeout);
129 if (pending_activation->babysitter)
131 if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
133 pending_activation->babysitter,
135 _dbus_assert_not_reached ("setting watch functions to NULL failed");
137 _dbus_babysitter_unref (pending_activation->babysitter);
140 dbus_free (pending_activation->service_name);
142 link = _dbus_list_get_first_link (&pending_activation->entries);
146 BusPendingActivationEntry *entry = link->data;
148 bus_pending_activation_entry_free (entry);
150 link = _dbus_list_get_next_link (&pending_activation->entries, link);
152 _dbus_list_clear (&pending_activation->entries);
154 dbus_free (pending_activation);
158 bus_activation_entry_free (BusActivationEntry *entry)
163 dbus_free (entry->name);
164 dbus_free (entry->exec);
170 add_desktop_file_entry (BusActivation *activation,
171 BusDesktopFile *desktop_file,
175 BusActivationEntry *entry;
177 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
183 if (!bus_desktop_file_get_string (desktop_file,
184 DBUS_SERVICE_SECTION,
188 dbus_set_error (error, DBUS_ERROR_FAILED,
189 "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
193 if (!bus_desktop_file_get_string (desktop_file,
194 DBUS_SERVICE_SECTION,
198 dbus_set_error (error, DBUS_ERROR_FAILED,
199 "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
203 /* FIXME we need a better-defined algorithm for which service file to
204 * pick than "whichever one is first in the directory listing"
206 if (_dbus_hash_table_lookup_string (activation->entries, name))
208 dbus_set_error (error, DBUS_ERROR_FAILED,
209 "Service %s already exists in activation entry list\n", name);
213 entry = dbus_new0 (BusActivationEntry, 1);
223 if (!_dbus_hash_table_insert_string (activation->entries, entry->name, entry))
229 _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
241 /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
242 * hash entries it already added.
245 load_directory (BusActivation *activation,
246 const char *directory,
250 DBusString dir, filename;
251 DBusString full_path;
252 BusDesktopFile *desktop_file;
256 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
258 _dbus_string_init_const (&dir, directory);
263 if (!_dbus_string_init (&filename))
269 if (!_dbus_string_init (&full_path))
272 _dbus_string_free (&filename);
278 /* from this point it's safe to "goto out" */
280 iter = _dbus_directory_open (&dir, error);
283 _dbus_verbose ("Failed to open directory %s: %s\n",
284 directory, error ? error->message : "unknown");
288 /* Now read the files */
289 dbus_error_init (&tmp_error);
290 while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
292 _dbus_assert (!dbus_error_is_set (&tmp_error));
294 _dbus_string_set_length (&full_path, 0);
296 if (!_dbus_string_append (&full_path, directory) ||
297 !_dbus_concat_dir_and_file (&full_path, &filename))
303 if (!_dbus_string_ends_with_c_str (&filename, ".service"))
305 _dbus_verbose ("Skipping non-.service file %s\n",
306 _dbus_string_get_const_data (&filename));
310 desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
312 if (desktop_file == NULL)
314 _dbus_verbose ("Could not load %s: %s\n",
315 _dbus_string_get_const_data (&full_path),
318 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
320 dbus_move_error (&tmp_error, error);
324 dbus_error_free (&tmp_error);
328 if (!add_desktop_file_entry (activation, desktop_file, &tmp_error))
330 bus_desktop_file_free (desktop_file);
333 _dbus_verbose ("Could not add %s to activation entry list: %s\n",
334 _dbus_string_get_const_data (&full_path), tmp_error.message);
336 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
338 dbus_move_error (&tmp_error, error);
342 dbus_error_free (&tmp_error);
347 bus_desktop_file_free (desktop_file);
353 if (dbus_error_is_set (&tmp_error))
355 dbus_move_error (&tmp_error, error);
363 _DBUS_ASSERT_ERROR_IS_SET (error);
365 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
368 _dbus_directory_close (iter);
370 bus_desktop_file_free (desktop_file);
371 _dbus_string_free (&filename);
372 _dbus_string_free (&full_path);
378 bus_activation_new (BusContext *context,
379 const DBusString *address,
380 DBusList **directories,
383 BusActivation *activation;
386 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
388 activation = dbus_new0 (BusActivation, 1);
389 if (activation == NULL)
395 activation->refcount = 1;
396 activation->context = context;
398 if (!_dbus_string_copy_data (address, &activation->server_address))
404 activation->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
405 (DBusFreeFunction)bus_activation_entry_free);
406 if (activation->entries == NULL)
412 activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
413 (DBusFreeFunction)bus_pending_activation_unref);
415 if (activation->pending_activations == NULL)
421 /* Load service files */
422 link = _dbus_list_get_first_link (directories);
425 if (!load_directory (activation, link->data, error))
427 link = _dbus_list_get_next_link (directories, link);
433 bus_activation_unref (activation);
438 bus_activation_ref (BusActivation *activation)
440 _dbus_assert (activation->refcount > 0);
442 activation->refcount += 1;
446 bus_activation_unref (BusActivation *activation)
448 _dbus_assert (activation->refcount > 0);
450 activation->refcount -= 1;
452 if (activation->refcount == 0)
454 dbus_free (activation->server_address);
455 if (activation->entries)
456 _dbus_hash_table_unref (activation->entries);
457 if (activation->pending_activations)
458 _dbus_hash_table_unref (activation->pending_activations);
459 dbus_free (activation);
464 child_setup (void *data)
466 BusActivation *activation = data;
469 /* If no memory, we simply have the child exit, so it won't try
470 * to connect to the wrong thing.
472 if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", activation->server_address))
475 type = bus_context_get_type (activation->context);
478 if (!_dbus_setenv ("DBUS_BUS_TYPE", type))
485 BusPendingActivation *pending_activation;
486 DBusPreallocatedHash *hash_entry;
487 } RestorePendingData;
490 restore_pending (void *data)
492 RestorePendingData *d = data;
494 _dbus_assert (d->pending_activation != NULL);
495 _dbus_assert (d->hash_entry != NULL);
497 _dbus_verbose ("Restoring pending activation for service %s, has timeout = %d\n",
498 d->pending_activation->service_name,
499 d->pending_activation->timeout_added);
501 _dbus_hash_table_insert_string_preallocated (d->pending_activation->activation->pending_activations,
503 d->pending_activation->service_name, d->pending_activation);
505 bus_pending_activation_ref (d->pending_activation);
507 d->hash_entry = NULL;
511 free_pending_restore_data (void *data)
513 RestorePendingData *d = data;
516 _dbus_hash_table_free_preallocated_entry (d->pending_activation->activation->pending_activations,
519 bus_pending_activation_unref (d->pending_activation);
525 add_restore_pending_to_transaction (BusTransaction *transaction,
526 BusPendingActivation *pending_activation)
528 RestorePendingData *d;
530 d = dbus_new (RestorePendingData, 1);
534 d->pending_activation = pending_activation;
535 d->hash_entry = _dbus_hash_table_preallocate_entry (d->pending_activation->activation->pending_activations);
537 bus_pending_activation_ref (d->pending_activation);
539 if (d->hash_entry == NULL ||
540 !bus_transaction_add_cancel_hook (transaction, restore_pending, d,
541 free_pending_restore_data))
543 free_pending_restore_data (d);
547 _dbus_verbose ("Saved pending activation to be restored if the transaction fails\n");
553 bus_activation_service_created (BusActivation *activation,
554 const char *service_name,
555 BusTransaction *transaction,
558 BusPendingActivation *pending_activation;
559 DBusMessage *message;
562 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
564 /* Check if it's a pending activation */
565 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
567 if (!pending_activation)
570 link = _dbus_list_get_first_link (&pending_activation->entries);
573 BusPendingActivationEntry *entry = link->data;
574 DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
576 if (dbus_connection_get_is_connected (entry->connection))
578 message = dbus_message_new_reply (entry->activation_message);
585 if (!dbus_message_append_args (message,
586 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
589 dbus_message_unref (message);
594 if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
596 dbus_message_unref (message);
601 dbus_message_unref (message);
607 if (!add_restore_pending_to_transaction (transaction, pending_activation))
609 _dbus_verbose ("Could not add cancel hook to transaction to revert removing pending activation\n");
614 _dbus_hash_table_remove_string (activation->pending_activations, service_name);
623 * FIXME @todo the error messages here would ideally be preallocated
624 * so we don't need to allocate memory to send them.
625 * Using the usual tactic, prealloc an OOM message, then
626 * if we can't alloc the real error send the OOM error instead.
629 try_send_activation_failure (BusPendingActivation *pending_activation,
630 const DBusError *how)
632 BusActivation *activation;
633 DBusMessage *message;
635 BusTransaction *transaction;
637 activation = pending_activation->activation;
639 transaction = bus_transaction_new (activation->context);
640 if (transaction == NULL)
643 link = _dbus_list_get_first_link (&pending_activation->entries);
646 BusPendingActivationEntry *entry = link->data;
647 DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
649 if (dbus_connection_get_is_connected (entry->connection))
651 message = dbus_message_new_error_reply (entry->activation_message,
657 if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
659 dbus_message_unref (message);
663 dbus_message_unref (message);
669 bus_transaction_execute_and_free (transaction);
675 bus_transaction_cancel_and_free (transaction);
680 * Free the pending activation and send an error message to all the
681 * connections that were waiting for it.
684 pending_activation_failed (BusPendingActivation *pending_activation,
685 const DBusError *how)
687 /* FIXME use preallocated OOM messages instead of bus_wait_for_memory() */
688 while (!try_send_activation_failure (pending_activation, how))
689 _dbus_wait_for_memory ();
691 /* Destroy this pending activation */
692 _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
693 pending_activation->service_name);
697 babysitter_watch_callback (DBusWatch *watch,
698 unsigned int condition,
701 BusPendingActivation *pending_activation = data;
703 DBusBabysitter *babysitter;
705 babysitter = pending_activation->babysitter;
707 _dbus_babysitter_ref (babysitter);
709 retval = dbus_watch_handle (watch, condition);
711 /* FIXME this is broken in the same way that
712 * connection watches used to be; there should be
713 * a separate callback for status change, instead
714 * of doing "if we handled a watch status might
717 * Fixing this lets us move dbus_watch_handle
718 * calls into dbus-mainloop.c
721 if (_dbus_babysitter_get_child_exited (babysitter))
725 dbus_error_init (&error);
726 _dbus_babysitter_set_child_exit_error (babysitter, &error);
728 /* Destroys the pending activation */
729 pending_activation_failed (pending_activation, &error);
731 dbus_error_free (&error);
734 _dbus_babysitter_unref (babysitter);
740 add_babysitter_watch (DBusWatch *watch,
743 BusPendingActivation *pending_activation = data;
745 return _dbus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
746 watch, babysitter_watch_callback, pending_activation,
751 remove_babysitter_watch (DBusWatch *watch,
754 BusPendingActivation *pending_activation = data;
756 _dbus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
757 watch, babysitter_watch_callback, pending_activation);
761 pending_activation_timed_out (void *data)
763 BusPendingActivation *pending_activation = data;
766 /* Kill the spawned process, since it sucks
767 * (not sure this is what we want to do, but
768 * may as well try it for now)
770 _dbus_babysitter_kill_child (pending_activation->babysitter);
772 dbus_error_init (&error);
774 dbus_set_error (&error, DBUS_ERROR_TIMED_OUT,
775 "Activation of %s timed out",
776 pending_activation->service_name);
778 pending_activation_failed (pending_activation, &error);
780 dbus_error_free (&error);
786 cancel_pending (void *data)
788 BusPendingActivation *pending_activation = data;
790 _dbus_verbose ("Canceling pending activation of %s\n",
791 pending_activation->service_name);
793 if (pending_activation->babysitter)
794 _dbus_babysitter_kill_child (pending_activation->babysitter);
796 _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
797 pending_activation->service_name);
801 free_pending_cancel_data (void *data)
803 BusPendingActivation *pending_activation = data;
805 bus_pending_activation_unref (pending_activation);
809 add_cancel_pending_to_transaction (BusTransaction *transaction,
810 BusPendingActivation *pending_activation)
812 if (!bus_transaction_add_cancel_hook (transaction, cancel_pending,
814 free_pending_cancel_data))
817 bus_pending_activation_ref (pending_activation);
819 _dbus_verbose ("Saved pending activation to be canceled if the transaction fails\n");
825 bus_activation_activate_service (BusActivation *activation,
826 DBusConnection *connection,
827 BusTransaction *transaction,
828 DBusMessage *activation_message,
829 const char *service_name,
832 BusActivationEntry *entry;
833 BusPendingActivation *pending_activation;
834 BusPendingActivationEntry *pending_activation_entry;
835 DBusMessage *message;
836 DBusString service_str;
840 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
842 entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
846 dbus_set_error (error, DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND,
847 "The service %s was not found in the activation entry list",
852 /* Check if the service is active */
853 _dbus_string_init_const (&service_str, service_name);
854 if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
856 _dbus_verbose ("Service \"%s\" is already active\n", service_name);
858 message = dbus_message_new_reply (activation_message);
862 _dbus_verbose ("No memory to create reply to activate message\n");
867 if (!dbus_message_append_args (message,
868 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE,
871 _dbus_verbose ("No memory to set args of reply to activate message\n");
873 dbus_message_unref (message);
877 retval = bus_transaction_send_from_driver (transaction, connection, message);
878 dbus_message_unref (message);
881 _dbus_verbose ("Failed to send reply\n");
888 pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
889 if (!pending_activation_entry)
891 _dbus_verbose ("Failed to create pending activation entry\n");
896 pending_activation_entry->activation_message = activation_message;
897 dbus_message_ref (activation_message);
898 pending_activation_entry->connection = connection;
899 dbus_connection_ref (connection);
901 /* Check if the service is being activated */
902 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
903 if (pending_activation)
905 /* FIXME security - a client could keep sending activations over and
906 * over, growing this queue.
908 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
910 _dbus_verbose ("Failed to append a new entry to pending activation\n");
913 bus_pending_activation_entry_free (pending_activation_entry);
919 pending_activation = dbus_new0 (BusPendingActivation, 1);
920 if (!pending_activation)
922 _dbus_verbose ("Failed to create pending activation\n");
925 bus_pending_activation_entry_free (pending_activation_entry);
929 pending_activation->activation = activation;
930 pending_activation->refcount = 1;
932 pending_activation->service_name = _dbus_strdup (service_name);
933 if (!pending_activation->service_name)
935 _dbus_verbose ("Failed to copy service name for pending activation\n");
938 bus_pending_activation_unref (pending_activation);
939 bus_pending_activation_entry_free (pending_activation_entry);
943 pending_activation->timeout =
944 _dbus_timeout_new (bus_context_get_activation_timeout (activation->context),
945 pending_activation_timed_out,
948 if (!pending_activation->timeout)
950 _dbus_verbose ("Failed to create timeout for pending activation\n");
953 bus_pending_activation_unref (pending_activation);
954 bus_pending_activation_entry_free (pending_activation_entry);
958 if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
959 pending_activation->timeout,
960 handle_timeout_callback,
964 _dbus_verbose ("Failed to add timeout for pending activation\n");
967 bus_pending_activation_unref (pending_activation);
968 bus_pending_activation_entry_free (pending_activation_entry);
972 pending_activation->timeout_added = TRUE;
974 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
976 _dbus_verbose ("Failed to add entry to just-created pending activation\n");
979 bus_pending_activation_unref (pending_activation);
980 bus_pending_activation_entry_free (pending_activation_entry);
984 if (!_dbus_hash_table_insert_string (activation->pending_activations,
985 pending_activation->service_name,
988 _dbus_verbose ("Failed to put pending activation in hash table\n");
991 bus_pending_activation_unref (pending_activation);
996 if (!add_cancel_pending_to_transaction (transaction, pending_activation))
998 _dbus_verbose ("Failed to add pending activation cancel hook to transaction\n");
1000 _dbus_hash_table_remove_string (activation->pending_activations,
1001 pending_activation->service_name);
1005 /* FIXME we need to support a full command line, not just a single
1009 /* Now try to spawn the process */
1010 argv[0] = entry->exec;
1013 if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv,
1014 child_setup, activation,
1017 _dbus_verbose ("Failed to spawn child\n");
1018 _DBUS_ASSERT_ERROR_IS_SET (error);
1022 _dbus_assert (pending_activation->babysitter != NULL);
1024 if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
1025 add_babysitter_watch,
1026 remove_babysitter_watch,
1031 BUS_SET_OOM (error);
1032 _dbus_verbose ("Failed to set babysitter watch functions\n");