4e428bdebe7a69ddf25c646a3a20055f37b55697
[platform/upstream/dbus.git] / bus / activation.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* activation.c  Activation of services
3  *
4  * Copyright (C) 2003  CodeFactory AB
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "activation.h"
24 #include "desktop-file.h"
25 #include "utils.h"
26 #include <dbus/dbus-internals.h>
27 #include <dbus/dbus-hash.h>
28 #include <sys/types.h>
29 #include <dirent.h>
30 #include <errno.h>
31
32 #define DBUS_SERVICE_SECTION "D-BUS Service"
33 #define DBUS_SERVICE_NAME "Name"
34 #define DBUS_SERVICE_EXEC "Exec"
35
36 static DBusHashTable *activation_entries = NULL;
37 static char *server_address = NULL;
38
39 typedef struct
40 {
41   char *name;
42   char *exec;
43 } BusActivationEntry;
44
45 static DBusHashTable *pending_activations = NULL;
46 typedef struct
47 {
48   char *service;
49 } BusPendingActivation;
50
51 static void
52 bus_activation_entry_free (BusActivationEntry *entry)
53 {
54   if (!entry)
55     return;
56   
57   dbus_free (entry->name);
58   dbus_free (entry->exec);
59 }
60
61 static dbus_bool_t
62 add_desktop_file_entry (BusDesktopFile *desktop_file,
63                         DBusError      *error)
64 {
65   char *name, *exec;
66   BusActivationEntry *entry;
67
68   name = NULL;
69   exec = NULL;
70   entry = NULL;
71   
72   if (!bus_desktop_file_get_string (desktop_file,
73                                     DBUS_SERVICE_SECTION,
74                                     DBUS_SERVICE_NAME,
75                                     &name))
76     {
77       dbus_set_error (error, DBUS_ERROR_FAILED,
78                       "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
79       goto failed;
80     }
81
82   if (!bus_desktop_file_get_string (desktop_file,
83                                     DBUS_SERVICE_SECTION,
84                                     DBUS_SERVICE_EXEC,
85                                     &exec))
86     {
87       dbus_set_error (error, DBUS_ERROR_FAILED,
88                       "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
89       goto failed;
90     }
91
92   /* FIXME we need a better-defined algorithm for which service file to
93    * pick than "whichever one is first in the directory listing"
94    */
95   if (_dbus_hash_table_lookup_string (activation_entries, name))
96     {
97       dbus_set_error (error, DBUS_ERROR_FAILED,
98                       "Service %s already exists in activation entry list\n", name);
99       goto failed;
100     }
101   
102   entry = dbus_new0 (BusActivationEntry, 1);
103   if (entry == NULL)
104     {
105       BUS_SET_OOM (error);
106       goto failed;
107     }
108   
109   entry->name = name;
110   entry->exec = exec;
111
112   if (!_dbus_hash_table_insert_string (activation_entries, entry->name, entry))
113     {
114       BUS_SET_OOM (error);
115       goto failed;
116     }
117
118   _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
119   
120   return TRUE;
121
122  failed:
123   dbus_free (name);
124   dbus_free (exec);
125   dbus_free (entry);
126   
127   return FALSE;
128 }
129
130 /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
131  * hash entries it already added.
132  */
133 static dbus_bool_t
134 load_directory (const char *directory,
135                 DBusError  *error)
136 {
137   DBusDirIter *iter;
138   DBusString dir, filename;
139   DBusString full_path;
140   BusDesktopFile *desktop_file;
141   DBusError tmp_error;
142   
143   _dbus_string_init_const (&dir, directory);
144
145   iter = NULL;
146   desktop_file = NULL;
147   
148   if (!_dbus_string_init (&filename, _DBUS_INT_MAX))
149     {
150       BUS_SET_OOM (error);
151       return FALSE;
152     }
153
154   if (!_dbus_string_init (&full_path, _DBUS_INT_MAX))
155     {
156       BUS_SET_OOM (error);
157       _dbus_string_free (&filename);
158       return FALSE;
159     }
160
161   /* from this point it's safe to "goto failed" */
162   
163   iter = _dbus_directory_open (&dir, error);
164   if (iter == NULL)
165     {
166       _dbus_verbose ("Failed to open directory %s: %s\n",
167                      directory, error ? error->message : "unknown");
168       goto failed;
169     }
170   
171   /* Now read the files */
172   dbus_error_init (&tmp_error);
173   while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
174     {
175       _dbus_assert (!dbus_error_is_set (&tmp_error));
176       
177       _dbus_string_set_length (&full_path, 0);
178       
179       if (!_dbus_string_append (&full_path, directory) ||
180           !_dbus_concat_dir_and_file (&full_path, &filename))
181         {
182           BUS_SET_OOM (error);
183           goto failed;
184         }
185       
186       if (!_dbus_string_ends_with_c_str (&filename, ".service"))
187         {
188           const char *filename_c;
189           _dbus_string_get_const_data (&filename, &filename_c);
190           _dbus_verbose ("Skipping non-.service file %s\n",
191                          filename_c);
192           continue;
193         }
194       
195       desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
196
197       if (desktop_file == NULL)
198         {
199           const char *full_path_c;
200
201           _dbus_string_get_const_data (&full_path, &full_path_c);
202           
203           _dbus_verbose ("Could not load %s: %s\n", full_path_c,
204                          tmp_error.message);
205
206           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
207             {
208               dbus_move_error (&tmp_error, error);
209               goto failed;
210             }
211           
212           dbus_error_free (&tmp_error);
213           continue;
214         }
215
216       if (!add_desktop_file_entry (desktop_file, &tmp_error))
217         {
218           const char *full_path_c;
219
220           bus_desktop_file_free (desktop_file);
221           desktop_file = NULL;
222           
223           _dbus_string_get_const_data (&full_path, &full_path_c);
224           
225           _dbus_verbose ("Could not add %s to activation entry list: %s\n",
226                          full_path_c, tmp_error.message);
227
228           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
229             {
230               dbus_move_error (&tmp_error, error);
231               goto failed;
232             }
233
234           dbus_error_free (&tmp_error);
235           continue;
236         }
237       else
238         {
239           bus_desktop_file_free (desktop_file);
240           desktop_file = NULL;
241           continue;
242         }
243     }
244
245   if (dbus_error_is_set (&tmp_error))
246     {
247       dbus_move_error (&tmp_error, error);
248       goto failed;
249     }
250   
251   return TRUE;
252   
253  failed:
254   _DBUS_ASSERT_ERROR_IS_SET (error);
255   
256   if (iter != NULL)
257     _dbus_directory_close (iter);
258   if (desktop_file)
259     bus_desktop_file_free (desktop_file);
260   _dbus_string_free (&filename);
261   _dbus_string_free (&full_path);
262   
263   return FALSE;
264 }
265
266 dbus_bool_t
267 bus_activation_init (const char  *address,
268                      const char **directories,
269                      DBusError   *error)
270 {
271   int i;
272
273   _dbus_assert (server_address == NULL);
274   _dbus_assert (activation_entries == NULL);
275   
276   /* FIXME: We should split up the server addresses. */
277   server_address = _dbus_strdup (address);
278   if (server_address == NULL)
279     {
280       BUS_SET_OOM (error);
281       goto failed;
282     }
283   
284   activation_entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
285                                              (DBusFreeFunction)bus_activation_entry_free);
286   if (activation_entries == NULL)
287     {      
288       BUS_SET_OOM (error);
289       goto failed;
290     }
291
292   /* Load service files */
293   i = 0;
294   while (directories[i] != NULL)
295     {
296       if (!load_directory (directories[i], error))
297         goto failed;
298       ++i;
299     }
300
301   return TRUE;
302   
303  failed:
304   dbus_free (server_address);
305   if (activation_entries)
306     _dbus_hash_table_unref (activation_entries);
307   
308   return FALSE;
309 }
310
311 static void
312 child_setup (void *data)
313 {
314   /* If no memory, we simply have the child exit, so it won't try
315    * to connect to the wrong thing.
316    */
317   if (!_dbus_setenv ("DBUS_ADDRESS", server_address))
318     _dbus_exit (1);
319 }
320
321 dbus_bool_t
322 bus_activation_activate_service (const char  *service_name,
323                                  DBusError   *error)
324 {
325   BusActivationEntry *entry;
326   char *argv[2];
327   
328   entry = _dbus_hash_table_lookup_string (activation_entries, service_name);
329
330   if (!entry)
331     {
332       dbus_set_error (error, DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND,
333                       "The service %s was not found in the activation entry list",
334                       service_name);
335       return FALSE;
336     }
337
338   /* FIXME we need to support a full command line, not just a single
339    * argv[0]
340    */
341   
342   /* Now try to spawn the process */
343   argv[0] = entry->exec;
344   argv[1] = NULL;
345
346   if (!_dbus_spawn_async (argv,
347                           child_setup, NULL, 
348                           error))
349     return FALSE;
350
351   return TRUE;
352 }