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 <sys/types.h>
36 #define DBUS_SERVICE_SECTION "D-BUS Service"
37 #define DBUS_SERVICE_NAME "Name"
38 #define DBUS_SERVICE_EXEC "Exec"
43 DBusHashTable *entries;
44 DBusHashTable *pending_activations;
55 typedef struct BusPendingActivationEntry BusPendingActivationEntry;
57 struct BusPendingActivationEntry
59 DBusMessage *activation_message;
60 DBusConnection *connection;
67 } BusPendingActivation;
70 bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
72 if (entry->activation_message)
73 dbus_message_unref (entry->activation_message);
75 if (entry->connection)
76 dbus_connection_unref (entry->connection);
82 bus_pending_activation_free (BusPendingActivation *activation)
89 dbus_free (activation->service_name);
91 link = _dbus_list_get_first_link (&activation->entries);
95 BusPendingActivationEntry *entry = link->data;
97 bus_pending_activation_entry_free (entry);
99 link = _dbus_list_get_next_link (&activation->entries, link);
101 _dbus_list_clear (&activation->entries);
103 dbus_free (activation);
107 bus_activation_entry_free (BusActivationEntry *entry)
112 dbus_free (entry->name);
113 dbus_free (entry->exec);
119 add_desktop_file_entry (BusActivation *activation,
120 BusDesktopFile *desktop_file,
124 BusActivationEntry *entry;
126 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
132 if (!bus_desktop_file_get_string (desktop_file,
133 DBUS_SERVICE_SECTION,
137 dbus_set_error (error, DBUS_ERROR_FAILED,
138 "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
142 if (!bus_desktop_file_get_string (desktop_file,
143 DBUS_SERVICE_SECTION,
147 dbus_set_error (error, DBUS_ERROR_FAILED,
148 "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
152 /* FIXME we need a better-defined algorithm for which service file to
153 * pick than "whichever one is first in the directory listing"
155 if (_dbus_hash_table_lookup_string (activation->entries, name))
157 dbus_set_error (error, DBUS_ERROR_FAILED,
158 "Service %s already exists in activation entry list\n", name);
162 entry = dbus_new0 (BusActivationEntry, 1);
172 if (!_dbus_hash_table_insert_string (activation->entries, entry->name, entry))
178 _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
190 /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
191 * hash entries it already added.
194 load_directory (BusActivation *activation,
195 const char *directory,
199 DBusString dir, filename;
200 DBusString full_path;
201 BusDesktopFile *desktop_file;
205 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
207 _dbus_string_init_const (&dir, directory);
212 if (!_dbus_string_init (&filename))
218 if (!_dbus_string_init (&full_path))
221 _dbus_string_free (&filename);
227 /* from this point it's safe to "goto out" */
229 iter = _dbus_directory_open (&dir, error);
232 _dbus_verbose ("Failed to open directory %s: %s\n",
233 directory, error ? error->message : "unknown");
237 /* Now read the files */
238 dbus_error_init (&tmp_error);
239 while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
241 _dbus_assert (!dbus_error_is_set (&tmp_error));
243 _dbus_string_set_length (&full_path, 0);
245 if (!_dbus_string_append (&full_path, directory) ||
246 !_dbus_concat_dir_and_file (&full_path, &filename))
252 if (!_dbus_string_ends_with_c_str (&filename, ".service"))
254 _dbus_verbose ("Skipping non-.service file %s\n",
255 _dbus_string_get_const_data (&filename));
259 desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
261 if (desktop_file == NULL)
263 _dbus_verbose ("Could not load %s: %s\n",
264 _dbus_string_get_const_data (&full_path),
267 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
269 dbus_move_error (&tmp_error, error);
273 dbus_error_free (&tmp_error);
277 if (!add_desktop_file_entry (activation, desktop_file, &tmp_error))
279 bus_desktop_file_free (desktop_file);
282 _dbus_verbose ("Could not add %s to activation entry list: %s\n",
283 _dbus_string_get_const_data (&full_path), tmp_error.message);
285 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
287 dbus_move_error (&tmp_error, error);
291 dbus_error_free (&tmp_error);
296 bus_desktop_file_free (desktop_file);
302 if (dbus_error_is_set (&tmp_error))
304 dbus_move_error (&tmp_error, error);
312 _DBUS_ASSERT_ERROR_IS_SET (error);
314 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
317 _dbus_directory_close (iter);
319 bus_desktop_file_free (desktop_file);
320 _dbus_string_free (&filename);
321 _dbus_string_free (&full_path);
327 bus_activation_new (BusContext *context,
328 const DBusString *address,
329 DBusList **directories,
332 BusActivation *activation;
335 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
337 activation = dbus_new0 (BusActivation, 1);
338 if (activation == NULL)
344 activation->refcount = 1;
345 activation->context = context;
347 if (!_dbus_string_copy_data (address, &activation->server_address))
353 activation->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
354 (DBusFreeFunction)bus_activation_entry_free);
355 if (activation->entries == NULL)
361 activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
362 (DBusFreeFunction)bus_pending_activation_free);
364 if (activation->pending_activations == NULL)
370 /* Load service files */
371 link = _dbus_list_get_first_link (directories);
374 if (!load_directory (activation, link->data, error))
376 link = _dbus_list_get_next_link (directories, link);
382 bus_activation_unref (activation);
387 bus_activation_ref (BusActivation *activation)
389 _dbus_assert (activation->refcount > 0);
391 activation->refcount += 1;
395 bus_activation_unref (BusActivation *activation)
397 _dbus_assert (activation->refcount > 0);
399 activation->refcount -= 1;
401 if (activation->refcount == 0)
403 dbus_free (activation->server_address);
404 if (activation->entries)
405 _dbus_hash_table_unref (activation->entries);
406 if (activation->pending_activations)
407 _dbus_hash_table_unref (activation->pending_activations);
408 dbus_free (activation);
413 child_setup (void *data)
415 BusActivation *activation = data;
418 /* If no memory, we simply have the child exit, so it won't try
419 * to connect to the wrong thing.
421 if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", activation->server_address))
424 type = bus_context_get_type (activation->context);
427 if (!_dbus_setenv ("DBUS_BUS_TYPE", type))
433 bus_activation_service_created (BusActivation *activation,
434 const char *service_name,
435 BusTransaction *transaction,
438 BusPendingActivation *pending_activation;
439 DBusMessage *message;
442 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
444 /* Check if it's a pending activation */
445 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
447 if (!pending_activation)
450 link = _dbus_list_get_first_link (&pending_activation->entries);
453 BusPendingActivationEntry *entry = link->data;
454 DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
456 if (dbus_connection_get_is_connected (entry->connection))
458 message = dbus_message_new_reply (entry->activation_message);
465 if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
466 !dbus_message_append_args (message,
467 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
470 dbus_message_unref (message);
475 if (!bus_transaction_send_message (transaction, entry->connection, message))
477 dbus_message_unref (message);
483 bus_pending_activation_entry_free (entry);
485 _dbus_list_remove_link (&pending_activation->entries, link);
489 _dbus_hash_table_remove_string (activation->pending_activations, service_name);
494 _dbus_hash_table_remove_string (activation->pending_activations, service_name);
499 bus_activation_activate_service (BusActivation *activation,
500 DBusConnection *connection,
501 BusTransaction *transaction,
502 DBusMessage *activation_message,
503 const char *service_name,
506 BusActivationEntry *entry;
507 BusPendingActivation *pending_activation;
508 BusPendingActivationEntry *pending_activation_entry;
509 DBusMessage *message;
510 DBusString service_str;
514 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
516 entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
520 dbus_set_error (error, DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND,
521 "The service %s was not found in the activation entry list",
526 /* Check if the service is active */
527 _dbus_string_init_const (&service_str, service_name);
528 if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
530 message = dbus_message_new_reply (activation_message);
538 if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS) ||
539 !dbus_message_append_args (message,
540 DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE,
544 dbus_message_unref (message);
548 retval = bus_transaction_send_message (transaction, connection, message);
549 dbus_message_unref (message);
556 pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
557 if (!pending_activation_entry)
563 pending_activation_entry->activation_message = activation_message;
564 dbus_message_ref (activation_message);
565 pending_activation_entry->connection = connection;
566 dbus_connection_ref (connection);
568 /* Check if the service is being activated */
569 pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
570 if (pending_activation)
572 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
575 bus_pending_activation_entry_free (pending_activation_entry);
582 pending_activation = dbus_new0 (BusPendingActivation, 1);
583 if (!pending_activation)
586 bus_pending_activation_entry_free (pending_activation_entry);
589 pending_activation->service_name = _dbus_strdup (service_name);
590 if (!pending_activation->service_name)
593 bus_pending_activation_free (pending_activation);
594 bus_pending_activation_entry_free (pending_activation_entry);
598 if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
601 bus_pending_activation_free (pending_activation);
602 bus_pending_activation_entry_free (pending_activation_entry);
606 if (!_dbus_hash_table_insert_string (activation->pending_activations,
607 pending_activation->service_name, pending_activation))
610 bus_pending_activation_free (pending_activation);
615 /* FIXME we need to support a full command line, not just a single
619 /* Now try to spawn the process */
620 argv[0] = entry->exec;
623 if (!_dbus_spawn_async_with_babysitter (NULL, argv,
624 child_setup, activation,
627 _dbus_hash_table_remove_string (activation->pending_activations,
628 pending_activation->service_name);