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 <sys/types.h>
35 #define DBUS_SERVICE_SECTION "D-BUS Service"
36 #define DBUS_SERVICE_NAME "Name"
37 #define DBUS_SERVICE_EXEC "Exec"
42 DBusHashTable *entries;
43 DBusHashTable *pending_activations;
54 typedef struct BusPendingActivationEntry BusPendingActivationEntry;
56 struct BusPendingActivationEntry
58 DBusMessage *activation_message;
59 DBusConnection *connection;
66 } BusPendingActivation;
69 bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
71 if (entry->activation_message)
72 dbus_message_unref (entry->activation_message);
74 if (entry->connection)
75 dbus_connection_unref (entry->connection);
81 bus_pending_activation_free (BusPendingActivation *activation)
88 dbus_free (activation->service_name);
90 link = _dbus_list_get_first_link (&activation->entries);
94 BusPendingActivationEntry *entry = link->data;
96 bus_pending_activation_entry_free (entry);
98 link = _dbus_list_get_next_link (&activation->entries, link);
100 _dbus_list_clear (&activation->entries);
102 dbus_free (activation);
106 bus_activation_entry_free (BusActivationEntry *entry)
111 dbus_free (entry->name);
112 dbus_free (entry->exec);
118 add_desktop_file_entry (BusActivation *activation,
119 BusDesktopFile *desktop_file,
123 BusActivationEntry *entry;
125 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
131 if (!bus_desktop_file_get_string (desktop_file,
132 DBUS_SERVICE_SECTION,
136 dbus_set_error (error, DBUS_ERROR_FAILED,
137 "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
141 if (!bus_desktop_file_get_string (desktop_file,
142 DBUS_SERVICE_SECTION,
146 dbus_set_error (error, DBUS_ERROR_FAILED,
147 "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
151 /* FIXME we need a better-defined algorithm for which service file to
152 * pick than "whichever one is first in the directory listing"
154 if (_dbus_hash_table_lookup_string (activation->entries, name))
156 dbus_set_error (error, DBUS_ERROR_FAILED,
157 "Service %s already exists in activation entry list\n", name);
161 entry = dbus_new0 (BusActivationEntry, 1);
171 if (!_dbus_hash_table_insert_string (activation->entries, entry->name, entry))
177 _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
189 /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
190 * hash entries it already added.
193 load_directory (BusActivation *activation,
194 const char *directory,
198 DBusString dir, filename;
199 DBusString full_path;
200 BusDesktopFile *desktop_file;
204 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
206 _dbus_string_init_const (&dir, directory);
211 if (!_dbus_string_init (&filename))
217 if (!_dbus_string_init (&full_path))
220 _dbus_string_free (&filename);
226 /* from this point it's safe to "goto out" */
228 iter = _dbus_directory_open (&dir, error);
231 _dbus_verbose ("Failed to open directory %s: %s\n",
232 directory, error ? error->message : "unknown");
236 /* Now read the files */
237 dbus_error_init (&tmp_error);
238 while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
240 _dbus_assert (!dbus_error_is_set (&tmp_error));
242 _dbus_string_set_length (&full_path, 0);
244 if (!_dbus_string_append (&full_path, directory) ||
245 !_dbus_concat_dir_and_file (&full_path, &filename))
251 if (!_dbus_string_ends_with_c_str (&filename, ".service"))
253 _dbus_verbose ("Skipping non-.service file %s\n",
254 _dbus_string_get_const_data (&filename));
258 desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
260 if (desktop_file == NULL)
262 _dbus_verbose ("Could not load %s: %s\n",
263 _dbus_string_get_const_data (&full_path),
266 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
268 dbus_move_error (&tmp_error, error);
272 dbus_error_free (&tmp_error);
276 if (!add_desktop_file_entry (activation, desktop_file, &tmp_error))
278 bus_desktop_file_free (desktop_file);
281 _dbus_verbose ("Could not add %s to activation entry list: %s\n",
282 _dbus_string_get_const_data (&full_path), tmp_error.message);
284 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
286 dbus_move_error (&tmp_error, error);
290 dbus_error_free (&tmp_error);
295 bus_desktop_file_free (desktop_file);
301 if (dbus_error_is_set (&tmp_error))
303 dbus_move_error (&tmp_error, error);
311 _DBUS_ASSERT_ERROR_IS_SET (error);
313 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
316 _dbus_directory_close (iter);
318 bus_desktop_file_free (desktop_file);
319 _dbus_string_free (&filename);
320 _dbus_string_free (&full_path);
326 bus_activation_new (BusContext *context,
327 const DBusString *address,
328 DBusList **directories,
331 BusActivation *activation;
334 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
336 activation = dbus_new0 (BusActivation, 1);
337 if (activation == NULL)
343 activation->refcount = 1;
344 activation->context = context;
346 if (!_dbus_string_copy_data (address, &activation->server_address))
352 activation->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
353 (DBusFreeFunction)bus_activation_entry_free);
354 if (activation->entries == NULL)
360 activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
361 (DBusFreeFunction)bus_pending_activation_free);
363 if (activation->pending_activations == NULL)
369 /* Load service files */
370 link = _dbus_list_get_first_link (directories);
373 if (!load_directory (activation, link->data, error))
375 link = _dbus_list_get_next_link (directories, link);
381 bus_activation_unref (activation);
386 bus_activation_ref (BusActivation *activation)
388 _dbus_assert (activation->refcount > 0);
390 activation->refcount += 1;
394 bus_activation_unref (BusActivation *activation)
396 _dbus_assert (activation->refcount > 0);
398 activation->refcount -= 1;
400 if (activation->refcount == 0)
402 dbus_free (activation->server_address);
403 if (activation->entries)
404 _dbus_hash_table_unref (activation->entries);
405 if (activation->pending_activations)
406 _dbus_hash_table_unref (activation->pending_activations);
407 dbus_free (activation);
412 child_setup (void *data)
414 BusActivation *activation = data;
417 /* If no memory, we simply have the child exit, so it won't try
418 * to connect to the wrong thing.
420 if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", activation->server_address))
423 type = bus_context_get_type (activation->context);
426 if (!_dbus_setenv ("DBUS_BUS_TYPE", type))
432 bus_activation_service_created (BusActivation *activation,
433 const char *service_name,
434 BusTransaction *transaction,
437 BusPendingActivation *pending_activation;
438 DBusMessage *message;
441 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
443 /* Check if it's a pending activation */
444 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
446 if (!pending_activation)
449 link = _dbus_list_get_first_link (&pending_activation->entries);
452 BusPendingActivationEntry *entry = link->data;
453 DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
455 if (dbus_connection_get_is_connected (entry->connection))
457 message = dbus_message_new_reply (entry->activation_message);
464 if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
465 !dbus_message_append_args (message,
466 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
469 dbus_message_unref (message);
474 if (!bus_transaction_send_message (transaction, entry->connection, message))
476 dbus_message_unref (message);
482 bus_pending_activation_entry_free (entry);
484 _dbus_list_remove_link (&pending_activation->entries, link);
488 _dbus_hash_table_remove_string (activation->pending_activations, service_name);
493 _dbus_hash_table_remove_string (activation->pending_activations, service_name);
498 bus_activation_activate_service (BusActivation *activation,
499 DBusConnection *connection,
500 BusTransaction *transaction,
501 DBusMessage *activation_message,
502 const char *service_name,
505 BusActivationEntry *entry;
506 BusPendingActivation *pending_activation;
507 BusPendingActivationEntry *pending_activation_entry;
508 DBusMessage *message;
509 DBusString service_str;
513 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
515 entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
519 dbus_set_error (error, DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND,
520 "The service %s was not found in the activation entry list",
525 /* Check if the service is active */
526 _dbus_string_init_const (&service_str, service_name);
527 if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
529 message = dbus_message_new_reply (activation_message);
537 if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
538 !dbus_message_append_args (message,
539 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE,
543 dbus_message_unref (message);
547 retval = bus_transaction_send_message (transaction, connection, message);
548 dbus_message_unref (message);
555 pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
556 if (!pending_activation_entry)
562 pending_activation_entry->activation_message = activation_message;
563 dbus_message_ref (activation_message);
564 pending_activation_entry->connection = connection;
565 dbus_connection_ref (connection);
567 /* Check if the service is being activated */
568 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
569 if (pending_activation)
571 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
574 bus_pending_activation_entry_free (pending_activation_entry);
581 pending_activation = dbus_new0 (BusPendingActivation, 1);
582 if (!pending_activation)
585 bus_pending_activation_entry_free (pending_activation_entry);
588 pending_activation->service_name = _dbus_strdup (service_name);
589 if (!pending_activation->service_name)
592 bus_pending_activation_free (pending_activation);
593 bus_pending_activation_entry_free (pending_activation_entry);
597 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
600 bus_pending_activation_free (pending_activation);
601 bus_pending_activation_entry_free (pending_activation_entry);
605 if (!_dbus_hash_table_insert_string (activation->pending_activations,
606 pending_activation->service_name, pending_activation))
609 bus_pending_activation_free (pending_activation);
614 /* FIXME we need to support a full command line, not just a single
618 /* Now try to spawn the process */
619 argv[0] = entry->exec;
622 if (!_dbus_spawn_async_with_babysitter (NULL, argv,
623 child_setup, activation,
626 _dbus_hash_table_remove_string (activation->pending_activations,
627 pending_activation->service_name);