2004-03-16 Richard Hult <richard@imendio.com>
[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  * Copyright (C) 2003  Red Hat, Inc.
6  * Copyright (C) 2004  Imendio HB
7  *
8  * Licensed under the Academic Free License version 2.0
9  * 
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25 #include "activation.h"
26 #include "desktop-file.h"
27 #include "services.h"
28 #include "test.h"
29 #include "utils.h"
30 #include <dbus/dbus-internals.h>
31 #include <dbus/dbus-hash.h>
32 #include <dbus/dbus-list.h>
33 #include <dbus/dbus-spawn.h>
34 #include <dbus/dbus-timeout.h>
35 #include <dirent.h>
36 #include <errno.h>
37
38 #define DBUS_SERVICE_SECTION "D-BUS Service"
39 #define DBUS_SERVICE_NAME "Name"
40 #define DBUS_SERVICE_EXEC "Exec"
41
42 struct BusActivation
43 {
44   int refcount;
45   DBusHashTable *entries;
46   DBusHashTable *pending_activations;
47  char *server_address;
48   BusContext *context;
49   int n_pending_activations; /**< This is in fact the number of BusPendingActivationEntry,
50                               * i.e. number of pending activation requests, not pending
51                               * activations per se
52                               */
53   DBusHashTable *directories;
54 };
55
56 typedef struct
57 {
58   int refcount;
59   char *dir_c;
60   DBusHashTable *entries;
61 } BusServiceDirectory;
62
63 typedef struct
64 {
65   int refcount;
66   char *name;
67   char *exec;
68   unsigned long mtime;
69   BusServiceDirectory *s_dir;
70   char *filename;
71 } BusActivationEntry;
72
73 typedef struct BusPendingActivationEntry BusPendingActivationEntry;
74
75 struct BusPendingActivationEntry
76 {
77   DBusMessage *activation_message;
78   DBusConnection *connection;
79
80   dbus_bool_t auto_activation;
81 };
82
83 typedef struct
84 {
85   int refcount;
86   BusActivation *activation;
87   char *service_name;
88   char *exec;
89   DBusList *entries;
90   int n_entries;
91   DBusBabysitter *babysitter;
92   DBusTimeout *timeout;
93   unsigned int timeout_added : 1;
94 } BusPendingActivation;
95
96 static BusServiceDirectory *
97 bus_service_directory_ref (BusServiceDirectory *dir)
98 {
99   _dbus_assert (dir->refcount);
100   
101   dir->refcount++;
102
103   return dir;
104 }
105
106 static void
107 bus_service_directory_unref (BusServiceDirectory *dir)
108 {
109   if (dir == NULL) 
110     return; 
111
112   _dbus_assert (dir->refcount > 0);
113   dir->refcount--;
114
115   if (dir->refcount > 0)
116     return;
117
118   if (dir->entries)
119     _dbus_hash_table_unref (dir->entries);
120
121   dbus_free (dir->dir_c);
122   dbus_free (dir);
123 }
124
125 static void
126 bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
127 {
128   if (entry->activation_message)
129     dbus_message_unref (entry->activation_message);
130   
131   if (entry->connection)
132     dbus_connection_unref (entry->connection);
133   
134   dbus_free (entry);
135 }
136
137 static void
138 handle_timeout_callback (DBusTimeout   *timeout,
139                          void          *data)
140 {
141   BusPendingActivation *pending_activation = data;
142
143   while (!dbus_timeout_handle (pending_activation->timeout))
144     _dbus_wait_for_memory ();
145 }
146
147 static BusPendingActivation * 
148 bus_pending_activation_ref (BusPendingActivation *pending_activation)
149 {
150   _dbus_assert (pending_activation->refcount > 0);
151   pending_activation->refcount += 1;
152
153   return pending_activation;
154 }
155
156 static void
157 bus_pending_activation_unref (BusPendingActivation *pending_activation)
158 {
159   DBusList *link;
160   
161   if (pending_activation == NULL) /* hash table requires this */
162     return;
163
164   _dbus_assert (pending_activation->refcount > 0);
165   pending_activation->refcount -= 1;
166
167   if (pending_activation->refcount > 0)
168     return;
169   
170   if (pending_activation->timeout_added)
171     {
172       _dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
173                                  pending_activation->timeout,
174                                  handle_timeout_callback, pending_activation);
175       pending_activation->timeout_added = FALSE;
176     }
177
178   if (pending_activation->timeout)
179     _dbus_timeout_unref (pending_activation->timeout);
180   
181   if (pending_activation->babysitter)
182     {
183       if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
184                                                  NULL, NULL, NULL,
185                                                  pending_activation->babysitter,
186                                                  NULL))
187         _dbus_assert_not_reached ("setting watch functions to NULL failed");
188       
189       _dbus_babysitter_unref (pending_activation->babysitter);
190     }
191   
192   dbus_free (pending_activation->service_name);
193   dbus_free (pending_activation->exec);
194
195   link = _dbus_list_get_first_link (&pending_activation->entries);
196
197   while (link != NULL)
198     {
199       BusPendingActivationEntry *entry = link->data;
200
201       bus_pending_activation_entry_free (entry);
202
203       link = _dbus_list_get_next_link (&pending_activation->entries, link);
204     }
205   _dbus_list_clear (&pending_activation->entries);
206
207   pending_activation->activation->n_pending_activations -=
208     pending_activation->n_entries;
209
210   _dbus_assert (pending_activation->activation->n_pending_activations >= 0);
211   
212   dbus_free (pending_activation);
213 }
214
215 static BusActivationEntry *
216 bus_activation_entry_ref (BusActivationEntry *entry)
217 {
218   _dbus_assert (entry->refcount > 0);
219   entry->refcount++;
220
221   return entry;
222 }
223
224 static void
225 bus_activation_entry_unref (BusActivationEntry *entry)
226 {
227   if (entry == NULL) /* hash table requires this */
228     return;
229   
230   _dbus_assert (entry->refcount > 0);
231   entry->refcount--;
232   
233   if (entry->refcount > 0) 
234     return;
235   
236   dbus_free (entry->name);
237   dbus_free (entry->exec);
238   dbus_free (entry->filename);
239
240   dbus_free (entry);
241 }
242
243 static dbus_bool_t
244 update_desktop_file_entry (BusActivation       *activation,
245                            BusServiceDirectory *s_dir,
246                            DBusString          *filename,
247                            BusDesktopFile      *desktop_file,
248                            DBusError           *error)
249 {
250   char *name, *exec;
251   BusActivationEntry *entry;
252   DBusStat stat_buf;
253   DBusString file_path;
254
255   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
256   
257   name = NULL;
258   exec = NULL;
259   entry = NULL;
260   
261   if (!_dbus_string_init (&file_path))
262     {
263       BUS_SET_OOM (error);
264       return FALSE;
265     }
266  
267   if (!_dbus_string_append (&file_path, s_dir->dir_c) ||
268       !_dbus_concat_dir_and_file (&file_path, filename))
269     {
270       BUS_SET_OOM (error);
271       goto failed;
272     }
273  
274   if (!_dbus_stat (&file_path, &stat_buf, NULL)) 
275     {
276       dbus_set_error (error, DBUS_ERROR_FAILED,
277                       "Can't stat the service file\n");
278       goto failed;
279     }
280  
281   if (!bus_desktop_file_get_string (desktop_file,
282                                     DBUS_SERVICE_SECTION,
283                                     DBUS_SERVICE_NAME,
284                                     &name))
285     {
286       dbus_set_error (error, DBUS_ERROR_FAILED,
287                       "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
288       goto failed;
289     }
290
291   if (!bus_desktop_file_get_string (desktop_file,
292                                     DBUS_SERVICE_SECTION,
293                                     DBUS_SERVICE_EXEC,
294                                     &exec))
295     {
296       dbus_set_error (error, DBUS_ERROR_FAILED,
297                       "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
298       goto failed;
299     }
300
301   entry = _dbus_hash_table_lookup_string (s_dir->entries, 
302                                           _dbus_string_get_const_data (filename));
303   if (entry == NULL) /* New file */
304     { 
305       /* FIXME we need a better-defined algorithm for which service file to
306        * pick than "whichever one is first in the directory listing"
307        */
308       if (_dbus_hash_table_lookup_string (activation->entries, name))
309         {
310           dbus_set_error (error, DBUS_ERROR_FAILED,
311                           "Service %s already exists in activation entry list\n", name);
312           goto failed;
313         }
314       
315       entry = dbus_new0 (BusActivationEntry, 1);
316       if (entry == NULL)
317         {
318           BUS_SET_OOM (error);
319           goto failed;
320         }
321      
322       entry->name = name;
323       entry->exec = exec;
324       entry->refcount = 1;
325     
326       entry->s_dir = s_dir;
327       entry->filename = _dbus_strdup (_dbus_string_get_const_data (filename));
328       if (!entry->filename)
329         {
330           BUS_SET_OOM (error);
331           goto failed;
332         }
333
334       if (!_dbus_hash_table_insert_string (activation->entries, entry->name, bus_activation_entry_ref (entry)))
335         {
336           BUS_SET_OOM (error);
337           goto failed;
338         }
339      
340       if (!_dbus_hash_table_insert_string (s_dir->entries, entry->filename, bus_activation_entry_ref (entry)))
341         {
342           /* Revert the insertion in the entries table */
343           _dbus_hash_table_remove_string (activation->entries, entry->name);
344           BUS_SET_OOM (error);
345           goto failed;
346         }
347
348       _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
349     }
350   else /* Just update the entry */
351     {
352       bus_activation_entry_ref (entry);
353       _dbus_hash_table_remove_string (activation->entries, entry->name);
354
355       if (_dbus_hash_table_lookup_string (activation->entries, name))
356         {
357           _dbus_verbose ("The new service name \"%s\" of service file \"%s\" already in cache, ignoring\n",
358                          name, _dbus_string_get_const_data (&file_path));
359           goto failed;
360         }
361  
362       dbus_free (entry->name);
363       dbus_free (entry->exec);
364       entry->name = name;
365       entry->exec = exec;
366       if (!_dbus_hash_table_insert_string (activation->entries,
367                                            entry->name, bus_activation_entry_ref(entry)))
368         {
369           BUS_SET_OOM (error);
370           /* Also remove path to entries hash since we want this in sync with
371            * the entries hash table */
372           _dbus_hash_table_remove_string (entry->s_dir->entries, 
373                                           entry->filename);
374           bus_activation_entry_unref (entry);
375           return FALSE;
376         }
377     }
378   
379   entry->mtime = stat_buf.mtime;
380   
381   _dbus_string_free (&file_path);
382   bus_activation_entry_unref (entry);
383
384   return TRUE;
385
386 failed:
387   dbus_free (name);
388   dbus_free (exec);
389   _dbus_string_free (&file_path);
390
391   if (entry)
392     bus_activation_entry_unref (entry);
393   
394   return FALSE;
395 }
396
397 static dbus_bool_t
398 check_service_file (BusActivation       *activation,
399                     BusActivationEntry  *entry,
400                     BusActivationEntry **updated_entry,
401                     DBusError           *error)
402 {
403   DBusStat stat_buf;
404   dbus_bool_t retval;
405   BusActivationEntry *tmp_entry;
406   DBusString file_path;
407   DBusString filename;
408
409   retval = TRUE;
410   tmp_entry = entry;
411   
412   _dbus_string_init_const (&filename, entry->filename);
413   
414   if (!_dbus_string_init (&file_path))
415     {
416       BUS_SET_OOM (error);
417       return FALSE;
418     }
419  
420   if (!_dbus_string_append (&file_path, entry->s_dir->dir_c) ||
421       !_dbus_concat_dir_and_file (&file_path, &filename))
422     {
423       BUS_SET_OOM (error);
424       retval = FALSE;
425       goto out;
426     }
427   
428   if (!_dbus_stat (&file_path, &stat_buf, NULL))
429     {
430       _dbus_verbose ("****** Can't stat file \"%s\", removing from cache\n",
431                      _dbus_string_get_const_data (&file_path));
432
433       _dbus_hash_table_remove_string (activation->entries, entry->name);
434       _dbus_hash_table_remove_string (entry->s_dir->entries, entry->filename);
435
436       tmp_entry = NULL;
437       retval = TRUE;
438       goto out;
439     }
440   else 
441     {
442       if (stat_buf.mtime > entry->mtime) 
443         {
444           BusDesktopFile *desktop_file;
445           DBusError tmp_error;
446           
447           dbus_error_init (&tmp_error);
448           
449           desktop_file = bus_desktop_file_load (&file_path, &tmp_error);
450           if (desktop_file == NULL)
451             {
452               _dbus_verbose ("Could not load %s: %s\n",
453                              _dbus_string_get_const_data (&file_path), 
454                              tmp_error.message);
455               if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
456                 {
457                   dbus_move_error (&tmp_error, error);
458                   retval = FALSE;
459                   goto out;
460                 }
461               dbus_error_free (&tmp_error);
462               retval = TRUE;
463               goto out;
464             }
465           
466           if (!update_desktop_file_entry (activation, entry->s_dir, &filename, desktop_file, &tmp_error))
467             {
468               bus_desktop_file_free (desktop_file);
469               if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
470                 {
471                   dbus_move_error (&tmp_error, error);
472                   retval = FALSE;
473                   goto out;
474                 }
475               dbus_error_free (&tmp_error);
476               retval = TRUE;
477               goto out;
478             }
479          
480           bus_desktop_file_free (desktop_file);
481           retval = TRUE;
482         }
483     }
484   
485 out:
486   _dbus_string_free (&file_path);
487
488   if (updated_entry != NULL)
489     *updated_entry = tmp_entry;
490   return retval;
491 }
492
493
494 /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
495  * hash entries it already added.
496  */
497 static dbus_bool_t
498 update_directory (BusActivation       *activation,
499                   BusServiceDirectory *s_dir,
500                   DBusError           *error)
501 {
502   DBusDirIter *iter;
503   DBusString dir, filename;
504   BusDesktopFile *desktop_file;
505   DBusError tmp_error;
506   dbus_bool_t retval;
507   BusActivationEntry *entry;
508   DBusString full_path;
509   
510   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
511   
512   iter = NULL;
513   desktop_file = NULL;
514   
515   _dbus_string_init_const (&dir, s_dir->dir_c);
516   
517   if (!_dbus_string_init (&filename))
518     {
519       BUS_SET_OOM (error);
520       return FALSE;
521     }
522
523   if (!_dbus_string_init (&full_path))
524     {
525       BUS_SET_OOM (error);
526       _dbus_string_free (&filename);
527       return FALSE;
528     }
529
530   retval = FALSE;
531
532   /* from this point it's safe to "goto out" */
533   
534   iter = _dbus_directory_open (&dir, error);
535   if (iter == NULL)
536     {
537       _dbus_verbose ("Failed to open directory %s: %s\n",
538                      s_dir->dir_c, 
539                      error ? error->message : "unknown");
540       goto out;
541     }
542   
543   /* Now read the files */
544   dbus_error_init (&tmp_error);
545   while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
546     {
547       _dbus_assert (!dbus_error_is_set (&tmp_error));
548       
549       _dbus_string_set_length (&full_path, 0);
550       
551       if (!_dbus_string_ends_with_c_str (&filename, ".service"))
552         {
553           _dbus_verbose ("Skipping non-.service file %s\n",
554                          _dbus_string_get_const_data (&filename));
555           continue;
556         }
557
558       entry = _dbus_hash_table_lookup_string (s_dir->entries, _dbus_string_get_const_data (&filename));
559       if (entry) /* Already has this service file in the cache */ 
560         {
561           if (!check_service_file (activation, entry, NULL, error))
562             goto out;
563
564           continue;
565         }
566       
567       if (!_dbus_string_append (&full_path, s_dir->dir_c) ||
568           !_dbus_concat_dir_and_file (&full_path, &filename))
569         {
570           BUS_SET_OOM (error);
571           goto out;
572         }
573           
574       /* New file */
575       desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
576       if (desktop_file == NULL)
577         {
578           _dbus_verbose ("Could not load %s: %s\n",
579                          _dbus_string_get_const_data (&full_path),
580                          tmp_error.message);
581
582           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
583             {
584               dbus_move_error (&tmp_error, error);
585               goto out;
586             }
587           
588           dbus_error_free (&tmp_error);
589           continue;
590         }
591
592       if (!update_desktop_file_entry (activation, s_dir, &filename, desktop_file, &tmp_error))
593         {
594           bus_desktop_file_free (desktop_file);
595           desktop_file = NULL;
596           
597           _dbus_verbose ("Could not add %s to activation entry list: %s\n",
598                          _dbus_string_get_const_data (&full_path), tmp_error.message);
599
600           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
601             {
602               dbus_move_error (&tmp_error, error);
603               goto out;
604             }
605
606           dbus_error_free (&tmp_error);
607           continue;
608         }
609       else
610         {
611           bus_desktop_file_free (desktop_file);
612           desktop_file = NULL;
613           continue;
614         }
615     }
616
617   if (dbus_error_is_set (&tmp_error))
618     {
619       dbus_move_error (&tmp_error, error);
620       goto out;
621     }
622   
623   retval = TRUE;
624
625  out:
626   if (!retval)
627     _DBUS_ASSERT_ERROR_IS_SET (error);
628   else
629     _DBUS_ASSERT_ERROR_IS_CLEAR (error);
630   
631   if (iter != NULL)
632     _dbus_directory_close (iter);
633   if (desktop_file)
634     bus_desktop_file_free (desktop_file);
635   _dbus_string_free (&filename);
636   _dbus_string_free (&full_path);
637   
638   return retval;
639 }
640
641 BusActivation*
642 bus_activation_new (BusContext        *context,
643                     const DBusString  *address,
644                     DBusList         **directories,
645                     DBusError         *error)
646 {
647   BusActivation *activation;
648   DBusList      *link;
649   char          *dir;
650   
651   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
652   
653   activation = dbus_new0 (BusActivation, 1);
654   if (activation == NULL)
655     {
656       BUS_SET_OOM (error);
657       return NULL;
658     }
659   
660   activation->refcount = 1;
661   activation->context = context;
662   activation->n_pending_activations = 0;
663   
664   if (!_dbus_string_copy_data (address, &activation->server_address))
665     {
666       BUS_SET_OOM (error);
667       goto failed;
668     }
669   
670   activation->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
671                                              (DBusFreeFunction)bus_activation_entry_unref);
672   if (activation->entries == NULL)
673     {      
674       BUS_SET_OOM (error);
675       goto failed;
676     }
677
678   activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
679                                                           (DBusFreeFunction)bus_pending_activation_unref);
680
681   if (activation->pending_activations == NULL)
682     {
683       BUS_SET_OOM (error);
684       goto failed;
685     }
686
687   activation->directories = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
688                                                   (DBusFreeFunction)bus_service_directory_unref);
689   
690   if (activation->directories == NULL) 
691     {
692       BUS_SET_OOM (error);
693       goto failed;
694     }
695  
696   /* Load service files */
697   link = _dbus_list_get_first_link (directories);
698   while (link != NULL)
699     {
700       BusServiceDirectory *s_dir;
701       
702       dir = _dbus_strdup ((const char *) link->data);
703       if (!dir)
704         {
705           BUS_SET_OOM (error);
706           goto failed;
707         }
708       
709       s_dir = dbus_new0 (BusServiceDirectory, 1);
710       if (!s_dir)
711         {
712           dbus_free (dir);
713           BUS_SET_OOM (error);
714           goto failed;
715         }
716
717       s_dir->refcount = 1;
718       s_dir->dir_c = dir;
719       
720       s_dir->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
721                                              (DBusFreeFunction)bus_activation_entry_unref);
722
723       if (!s_dir->entries)
724         {
725           bus_service_directory_unref (s_dir);
726           BUS_SET_OOM (error);
727           goto failed;
728         }
729
730       if (!_dbus_hash_table_insert_string (activation->directories, s_dir->dir_c, s_dir))
731         {
732           bus_service_directory_unref (s_dir);
733           BUS_SET_OOM (error);
734           goto failed;
735         }
736
737       if (!update_directory (activation, s_dir, error))
738         goto failed;
739       
740       link = _dbus_list_get_next_link (directories, link);
741     }
742
743   return activation;
744   
745  failed:
746   bus_activation_unref (activation);  
747   return NULL;
748 }
749
750 BusActivation *
751 bus_activation_ref (BusActivation *activation)
752 {
753   _dbus_assert (activation->refcount > 0);
754   
755   activation->refcount += 1;
756
757   return activation;
758 }
759
760 void
761 bus_activation_unref (BusActivation *activation)
762 {
763   _dbus_assert (activation->refcount > 0);
764
765   activation->refcount -= 1;
766
767   if (activation->refcount > 0)
768     return;
769   
770   dbus_free (activation->server_address);
771   if (activation->entries)
772     _dbus_hash_table_unref (activation->entries);
773   if (activation->pending_activations)
774     _dbus_hash_table_unref (activation->pending_activations);
775   if (activation->directories)  
776     _dbus_hash_table_unref (activation->directories);
777   
778   dbus_free (activation);
779 }
780
781 static void
782 child_setup (void *data)
783 {
784   BusActivation *activation = data;
785   const char *type;
786   
787   /* If no memory, we simply have the child exit, so it won't try
788    * to connect to the wrong thing.
789    */
790   if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", activation->server_address))
791     _dbus_exit (1);
792   
793   type = bus_context_get_type (activation->context);
794   if (type != NULL)
795     {
796       if (!_dbus_setenv ("DBUS_BUS_TYPE", type))
797         _dbus_exit (1);
798
799       if (strcmp (type, "session") == 0)
800         {
801           if (!_dbus_setenv ("DBUS_SESSION_BUS_ADDRESS",
802                              activation->server_address))
803             _dbus_exit (1);
804         }
805       else if (strcmp (type, "system") == 0)
806         {
807           if (!_dbus_setenv ("DBUS_SYSTEM_BUS_ADDRESS",
808                              activation->server_address))
809             _dbus_exit (1);
810         }
811     }
812 }
813
814 typedef struct
815 {
816   BusPendingActivation *pending_activation;
817   DBusPreallocatedHash *hash_entry;
818 } RestorePendingData;
819
820 static void
821 restore_pending (void *data)
822 {
823   RestorePendingData *d = data;
824
825   _dbus_assert (d->pending_activation != NULL);
826   _dbus_assert (d->hash_entry != NULL);
827
828   _dbus_verbose ("Restoring pending activation for service %s, has timeout = %d\n",
829                  d->pending_activation->service_name,
830                  d->pending_activation->timeout_added);
831   
832   _dbus_hash_table_insert_string_preallocated (d->pending_activation->activation->pending_activations,
833                                                d->hash_entry,
834                                                d->pending_activation->service_name, d->pending_activation);
835
836   bus_pending_activation_ref (d->pending_activation);
837   
838   d->hash_entry = NULL;
839 }
840
841 static void
842 free_pending_restore_data (void *data)
843 {
844   RestorePendingData *d = data;
845
846   if (d->hash_entry)
847     _dbus_hash_table_free_preallocated_entry (d->pending_activation->activation->pending_activations,
848                                               d->hash_entry);
849
850   bus_pending_activation_unref (d->pending_activation);
851   
852   dbus_free (d);
853 }
854
855 static dbus_bool_t
856 add_restore_pending_to_transaction (BusTransaction       *transaction,
857                                     BusPendingActivation *pending_activation)
858 {
859   RestorePendingData *d;
860
861   d = dbus_new (RestorePendingData, 1);
862   if (d == NULL)
863     return FALSE;
864   
865   d->pending_activation = pending_activation;
866   d->hash_entry = _dbus_hash_table_preallocate_entry (d->pending_activation->activation->pending_activations);
867   
868   bus_pending_activation_ref (d->pending_activation);
869   
870   if (d->hash_entry == NULL ||
871       !bus_transaction_add_cancel_hook (transaction, restore_pending, d,
872                                         free_pending_restore_data))
873     {
874       free_pending_restore_data (d);
875       return FALSE;
876     }
877
878   _dbus_verbose ("Saved pending activation to be restored if the transaction fails\n");
879   
880   return TRUE;
881 }
882
883 dbus_bool_t
884 bus_activation_service_created (BusActivation  *activation,
885                                 const char     *service_name,
886                                 BusTransaction *transaction,
887                                 DBusError      *error)
888 {
889   BusPendingActivation *pending_activation;
890   DBusMessage *message;
891   DBusList *link;
892
893   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
894   
895   /* Check if it's a pending activation */
896   pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
897
898   if (!pending_activation)
899     return TRUE;
900
901   link = _dbus_list_get_first_link (&pending_activation->entries);
902   while (link != NULL)
903     {
904       BusPendingActivationEntry *entry = link->data;
905       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
906       
907       if (dbus_connection_get_is_connected (entry->connection))
908         {
909           /* Only send activation replies to regular activation requests. */
910           if (!entry->auto_activation)
911             {
912               message = dbus_message_new_method_return (entry->activation_message);
913               if (!message)
914                 {
915                   BUS_SET_OOM (error);
916                   goto error;
917                 }
918               
919               if (!dbus_message_append_args (message,
920                                              DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
921                                              DBUS_TYPE_INVALID))
922                 {
923                   dbus_message_unref (message);
924                   BUS_SET_OOM (error);
925                   goto error;
926                 }
927               
928               if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
929                 {
930                   dbus_message_unref (message);
931                   BUS_SET_OOM (error);
932                   goto error;
933                 }
934               
935               dbus_message_unref (message);
936             }
937         }
938       
939       link = next;
940     }
941
942   return TRUE;
943
944  error:
945   return FALSE;
946 }
947
948 dbus_bool_t
949 bus_activation_send_pending_auto_activation_messages (BusActivation  *activation,
950                                                       BusService     *service,
951                                                       BusTransaction *transaction,
952                                                       DBusError      *error)
953 {
954   BusPendingActivation *pending_activation;
955   DBusList *link;
956
957   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
958   
959   /* Check if it's a pending activation */
960   pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations,
961                                                        bus_service_get_name (service));
962
963   if (!pending_activation)
964     return TRUE;
965
966   link = _dbus_list_get_first_link (&pending_activation->entries);
967   while (link != NULL)
968     {
969       BusPendingActivationEntry *entry = link->data;
970       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
971
972       if (entry->auto_activation && dbus_connection_get_is_connected (entry->connection))
973         {
974           DBusConnection *addressed_recipient;
975           
976           addressed_recipient = bus_service_get_primary_owner (service);
977
978           /* Check the security policy, which has the side-effect of adding an
979            * expected pending reply.
980            */
981           if (!bus_context_check_security_policy (activation->context, transaction,
982                                                   entry->connection,
983                                                   addressed_recipient,
984                                                   addressed_recipient,
985                                                   entry->activation_message, error))
986             goto error;
987
988           if (!bus_transaction_send (transaction, addressed_recipient, entry->activation_message))
989             {
990               BUS_SET_OOM (error);
991               goto error;
992             }
993         }
994
995       link = next;
996     }
997
998   if (!add_restore_pending_to_transaction (transaction, pending_activation))
999     {
1000       _dbus_verbose ("Could not add cancel hook to transaction to revert removing pending activation\n");
1001       BUS_SET_OOM (error);
1002       goto error;
1003     }
1004   
1005   _dbus_hash_table_remove_string (activation->pending_activations, bus_service_get_name (service));
1006
1007   return TRUE;
1008
1009  error:
1010   return FALSE;
1011 }
1012
1013 /**
1014  * FIXME @todo the error messages here would ideally be preallocated
1015  * so we don't need to allocate memory to send them.
1016  * Using the usual tactic, prealloc an OOM message, then
1017  * if we can't alloc the real error send the OOM error instead.
1018  */
1019 static dbus_bool_t
1020 try_send_activation_failure (BusPendingActivation *pending_activation,
1021                              const DBusError      *how)
1022 {
1023   BusActivation *activation;
1024   DBusList *link;
1025   BusTransaction *transaction;
1026   
1027   activation = pending_activation->activation;
1028
1029   transaction = bus_transaction_new (activation->context);
1030   if (transaction == NULL)
1031     return FALSE;
1032   
1033   link = _dbus_list_get_first_link (&pending_activation->entries);
1034   while (link != NULL)
1035     {
1036       BusPendingActivationEntry *entry = link->data;
1037       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
1038       
1039       if (dbus_connection_get_is_connected (entry->connection))
1040         {
1041           if (!bus_transaction_send_error_reply (transaction,
1042                                                  entry->connection,
1043                                                  how,
1044                                                  entry->activation_message))
1045             goto error;
1046         }
1047       
1048       link = next;
1049     }
1050
1051   bus_transaction_execute_and_free (transaction);
1052   
1053   return TRUE;
1054
1055  error:
1056   if (transaction)
1057     bus_transaction_cancel_and_free (transaction);
1058   return FALSE;
1059 }
1060
1061 /**
1062  * Free the pending activation and send an error message to all the
1063  * connections that were waiting for it.
1064  */
1065 static void
1066 pending_activation_failed (BusPendingActivation *pending_activation,
1067                            const DBusError      *how)
1068 {
1069   /* FIXME use preallocated OOM messages instead of bus_wait_for_memory() */
1070   while (!try_send_activation_failure (pending_activation, how))
1071     _dbus_wait_for_memory ();
1072
1073   /* Destroy this pending activation */
1074   _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
1075                                   pending_activation->service_name);
1076 }
1077
1078 static dbus_bool_t
1079 babysitter_watch_callback (DBusWatch     *watch,
1080                            unsigned int   condition,
1081                            void          *data)
1082 {
1083   BusPendingActivation *pending_activation = data;
1084   dbus_bool_t retval;
1085   DBusBabysitter *babysitter;
1086
1087   babysitter = pending_activation->babysitter;
1088   
1089   _dbus_babysitter_ref (babysitter);
1090   
1091   retval = dbus_watch_handle (watch, condition);
1092
1093   /* FIXME this is broken in the same way that
1094    * connection watches used to be; there should be
1095    * a separate callback for status change, instead
1096    * of doing "if we handled a watch status might
1097    * have changed"
1098    *
1099    * Fixing this lets us move dbus_watch_handle
1100    * calls into dbus-mainloop.c
1101    */
1102   
1103   if (_dbus_babysitter_get_child_exited (babysitter))
1104     {
1105       DBusError error;
1106       DBusHashIter iter;
1107       
1108       dbus_error_init (&error);
1109       _dbus_babysitter_set_child_exit_error (babysitter, &error);
1110
1111       /* Destroy all pending activations with the same exec */
1112       _dbus_hash_iter_init (pending_activation->activation->pending_activations,
1113                             &iter);
1114       while (_dbus_hash_iter_next (&iter))
1115         {
1116           BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
1117          
1118           if (p != pending_activation && strcmp (p->exec, pending_activation->exec) == 0)
1119             pending_activation_failed (p, &error);
1120         }
1121       
1122       /* Destroys the pending activation */
1123       pending_activation_failed (pending_activation, &error);
1124
1125       dbus_error_free (&error);
1126     }
1127   
1128   _dbus_babysitter_unref (babysitter);
1129
1130   return retval;
1131 }
1132
1133 static dbus_bool_t
1134 add_babysitter_watch (DBusWatch      *watch,
1135                       void           *data)
1136 {
1137   BusPendingActivation *pending_activation = data;
1138
1139   return _dbus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
1140                                watch, babysitter_watch_callback, pending_activation,
1141                                NULL);
1142 }
1143
1144 static void
1145 remove_babysitter_watch (DBusWatch      *watch,
1146                          void           *data)
1147 {
1148   BusPendingActivation *pending_activation = data;
1149   
1150   _dbus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
1151                            watch, babysitter_watch_callback, pending_activation);
1152 }
1153
1154 static dbus_bool_t
1155 pending_activation_timed_out (void *data)
1156 {
1157   BusPendingActivation *pending_activation = data;
1158   DBusError error;
1159   
1160   /* Kill the spawned process, since it sucks
1161    * (not sure this is what we want to do, but
1162    * may as well try it for now)
1163    */
1164   if (pending_activation->babysitter) 
1165     _dbus_babysitter_kill_child (pending_activation->babysitter);
1166
1167   dbus_error_init (&error);
1168
1169   dbus_set_error (&error, DBUS_ERROR_TIMED_OUT,
1170                   "Activation of %s timed out",
1171                   pending_activation->service_name);
1172
1173   pending_activation_failed (pending_activation, &error);
1174
1175   dbus_error_free (&error);
1176
1177   return TRUE;
1178 }
1179
1180 static void
1181 cancel_pending (void *data)
1182 {
1183   BusPendingActivation *pending_activation = data;
1184
1185   _dbus_verbose ("Canceling pending activation of %s\n",
1186                  pending_activation->service_name);
1187
1188   if (pending_activation->babysitter)
1189     _dbus_babysitter_kill_child (pending_activation->babysitter);
1190   
1191   _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
1192                                   pending_activation->service_name);
1193 }
1194
1195 static void
1196 free_pending_cancel_data (void *data)
1197 {
1198   BusPendingActivation *pending_activation = data;
1199   
1200   bus_pending_activation_unref (pending_activation);
1201 }
1202
1203 static dbus_bool_t
1204 add_cancel_pending_to_transaction (BusTransaction       *transaction,
1205                                    BusPendingActivation *pending_activation)
1206 {  
1207   if (!bus_transaction_add_cancel_hook (transaction, cancel_pending,
1208                                         pending_activation,
1209                                         free_pending_cancel_data))
1210     return FALSE;
1211
1212   bus_pending_activation_ref (pending_activation); 
1213   
1214   _dbus_verbose ("Saved pending activation to be canceled if the transaction fails\n");
1215   
1216   return TRUE;
1217 }
1218
1219 static dbus_bool_t 
1220 update_service_cache (BusActivation *activation, DBusError *error)
1221 {
1222   DBusHashIter iter;
1223  
1224   _dbus_hash_iter_init (activation->directories, &iter);
1225   while (_dbus_hash_iter_next (&iter))
1226     {
1227       DBusError tmp_error;
1228       BusServiceDirectory *s_dir;
1229
1230       s_dir = _dbus_hash_iter_get_value (&iter);
1231
1232       dbus_error_init (&tmp_error);
1233       if (!update_directory (activation, s_dir, &tmp_error))
1234         {
1235           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
1236             {
1237               dbus_move_error (&tmp_error, error);
1238               return FALSE;
1239             }
1240
1241           dbus_error_free (&tmp_error);
1242           continue;
1243         }
1244     }
1245   
1246   return TRUE;
1247 }
1248
1249 static BusActivationEntry *
1250 activation_find_entry (BusActivation *activation, 
1251                        const char    *service_name,
1252                        DBusError     *error)
1253 {
1254   BusActivationEntry *entry;
1255   
1256   entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
1257   if (!entry)
1258     { 
1259       if (!update_service_cache (activation, error)) 
1260         return NULL;
1261
1262       entry = _dbus_hash_table_lookup_string (activation->entries,
1263                                               service_name);
1264     }
1265   else 
1266     {
1267       BusActivationEntry *updated_entry;
1268
1269       if (!check_service_file (activation, entry, &updated_entry, error)) 
1270         return NULL;
1271
1272       entry = updated_entry;
1273     }
1274
1275   if (!entry) 
1276     {
1277       dbus_set_error (error, DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND,
1278                       "The service %s was not found in the activation entry list",
1279                       service_name);
1280       return NULL;
1281     }
1282
1283   return entry;
1284 }
1285
1286 dbus_bool_t
1287 bus_activation_activate_service (BusActivation  *activation,
1288                                  DBusConnection *connection,
1289                                  BusTransaction *transaction,
1290                                  dbus_bool_t     auto_activation,
1291                                  DBusMessage    *activation_message,
1292                                  const char     *service_name,
1293                                  DBusError      *error)
1294 {
1295   BusActivationEntry *entry;
1296   BusPendingActivation *pending_activation;
1297   BusPendingActivationEntry *pending_activation_entry;
1298   DBusMessage *message;
1299   DBusString service_str;
1300   char *argv[2];
1301   dbus_bool_t retval;
1302   DBusHashIter iter;
1303   dbus_bool_t activated;
1304   
1305   activated = TRUE;
1306
1307   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1308
1309   if (activation->n_pending_activations >=
1310       bus_context_get_max_pending_activations (activation->context))
1311     {
1312       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
1313                       "The maximum number of pending activations has been reached, activation of %s failed",
1314                       service_name);
1315       return FALSE;
1316     }
1317
1318   entry = activation_find_entry (activation, service_name, error);
1319   if (!entry) 
1320     return FALSE;
1321
1322   /* Check if the service is active */
1323   _dbus_string_init_const (&service_str, service_name);
1324   if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
1325     {
1326       _dbus_verbose ("Service \"%s\" is already active\n", service_name);
1327       
1328       message = dbus_message_new_method_return (activation_message);
1329
1330       if (!message)
1331         {
1332           _dbus_verbose ("No memory to create reply to activate message\n");
1333           BUS_SET_OOM (error);
1334           return FALSE;
1335         }
1336
1337       if (!dbus_message_append_args (message,
1338                                      DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE, 
1339                                      DBUS_TYPE_INVALID))
1340         {
1341           _dbus_verbose ("No memory to set args of reply to activate message\n");
1342           BUS_SET_OOM (error);
1343           dbus_message_unref (message);
1344           return FALSE;
1345         }
1346
1347       retval = bus_transaction_send_from_driver (transaction, connection, message);
1348       dbus_message_unref (message);
1349       if (!retval)
1350         {
1351           _dbus_verbose ("Failed to send reply\n");
1352           BUS_SET_OOM (error);
1353         }
1354
1355       return retval;
1356     }
1357
1358   pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
1359   if (!pending_activation_entry)
1360     {
1361       _dbus_verbose ("Failed to create pending activation entry\n");
1362       BUS_SET_OOM (error);
1363       return FALSE;
1364     }
1365
1366   pending_activation_entry->auto_activation = auto_activation;
1367
1368   pending_activation_entry->activation_message = activation_message;
1369   dbus_message_ref (activation_message);
1370   pending_activation_entry->connection = connection;
1371   dbus_connection_ref (connection);
1372   
1373   /* Check if the service is being activated */
1374   pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
1375   if (pending_activation)
1376     {
1377       if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
1378         {
1379           _dbus_verbose ("Failed to append a new entry to pending activation\n");
1380           
1381           BUS_SET_OOM (error);
1382           bus_pending_activation_entry_free (pending_activation_entry);
1383           return FALSE;
1384         }
1385
1386       pending_activation->n_entries += 1;
1387       pending_activation->activation->n_pending_activations += 1;
1388     }
1389   else
1390     {
1391       pending_activation = dbus_new0 (BusPendingActivation, 1);
1392       if (!pending_activation)
1393         {
1394           _dbus_verbose ("Failed to create pending activation\n");
1395           
1396           BUS_SET_OOM (error);
1397           bus_pending_activation_entry_free (pending_activation_entry);   
1398           return FALSE;
1399         }
1400
1401       pending_activation->activation = activation;
1402       pending_activation->refcount = 1;
1403       
1404       pending_activation->service_name = _dbus_strdup (service_name);
1405       if (!pending_activation->service_name)
1406         {
1407           _dbus_verbose ("Failed to copy service name for pending activation\n");
1408           
1409           BUS_SET_OOM (error);
1410           bus_pending_activation_unref (pending_activation);
1411           bus_pending_activation_entry_free (pending_activation_entry);   
1412           return FALSE;
1413         }
1414
1415       pending_activation->exec = _dbus_strdup (entry->exec);
1416       if (!pending_activation->exec)
1417         {
1418           _dbus_verbose ("Failed to copy service exec for pending activation\n");
1419           BUS_SET_OOM (error);
1420           bus_pending_activation_unref (pending_activation);
1421           bus_pending_activation_entry_free (pending_activation_entry);
1422           return FALSE;
1423         }
1424
1425       pending_activation->timeout =
1426         _dbus_timeout_new (bus_context_get_activation_timeout (activation->context),
1427                            pending_activation_timed_out,
1428                            pending_activation,
1429                            NULL);
1430       if (!pending_activation->timeout)
1431         {
1432           _dbus_verbose ("Failed to create timeout for pending activation\n");
1433           
1434           BUS_SET_OOM (error);
1435           bus_pending_activation_unref (pending_activation);
1436           bus_pending_activation_entry_free (pending_activation_entry);   
1437           return FALSE;
1438         }
1439
1440       if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
1441                                    pending_activation->timeout,
1442                                    handle_timeout_callback,
1443                                    pending_activation,
1444                                    NULL))
1445         {
1446           _dbus_verbose ("Failed to add timeout for pending activation\n");
1447           
1448           BUS_SET_OOM (error);
1449           bus_pending_activation_unref (pending_activation);
1450           bus_pending_activation_entry_free (pending_activation_entry);   
1451           return FALSE;
1452         }
1453
1454       pending_activation->timeout_added = TRUE;
1455       
1456       if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
1457         {
1458           _dbus_verbose ("Failed to add entry to just-created pending activation\n");
1459           
1460           BUS_SET_OOM (error);
1461           bus_pending_activation_unref (pending_activation);
1462           bus_pending_activation_entry_free (pending_activation_entry);   
1463           return FALSE;
1464         }
1465
1466       pending_activation->n_entries += 1;
1467       pending_activation->activation->n_pending_activations += 1;
1468     
1469       activated = FALSE;
1470       _dbus_hash_iter_init (activation->pending_activations, &iter);
1471       while (_dbus_hash_iter_next (&iter))
1472         {
1473           BusPendingActivation *p = _dbus_hash_iter_get_value (&iter);
1474           
1475           if (strcmp (p->exec, entry->exec) == 0) 
1476             {
1477               activated = TRUE;
1478               break;
1479             }
1480         }
1481      
1482       if (!_dbus_hash_table_insert_string (activation->pending_activations,
1483                                            pending_activation->service_name,
1484                                            pending_activation))
1485         {
1486           _dbus_verbose ("Failed to put pending activation in hash table\n");
1487           
1488           BUS_SET_OOM (error);
1489           bus_pending_activation_unref (pending_activation);
1490           return FALSE;
1491         }
1492     }
1493   
1494   if (!add_cancel_pending_to_transaction (transaction, pending_activation))
1495     {
1496       _dbus_verbose ("Failed to add pending activation cancel hook to transaction\n");
1497       BUS_SET_OOM (error);
1498       _dbus_hash_table_remove_string (activation->pending_activations,
1499                                       pending_activation->service_name);
1500       return FALSE;
1501     }
1502   
1503   /* FIXME we need to support a full command line, not just a single
1504    * argv[0]
1505    */
1506
1507   if (activated == TRUE) 
1508     {
1509       pending_activation->babysitter = NULL;
1510       return TRUE;
1511     }
1512
1513   /* Now try to spawn the process */
1514   argv[0] = entry->exec;
1515   argv[1] = NULL;
1516
1517   if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv,
1518                                           child_setup, activation, 
1519                                           error))
1520     {
1521       _dbus_verbose ("Failed to spawn child\n");
1522       _DBUS_ASSERT_ERROR_IS_SET (error);
1523       return FALSE;
1524     }
1525
1526   _dbus_assert (pending_activation->babysitter != NULL);
1527   
1528   if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
1529                                              add_babysitter_watch,
1530                                              remove_babysitter_watch,
1531                                              NULL,
1532                                              pending_activation,
1533                                              NULL))
1534     {
1535       BUS_SET_OOM (error);
1536       _dbus_verbose ("Failed to set babysitter watch functions\n");
1537       return FALSE;
1538     }
1539   
1540   return TRUE;
1541 }
1542
1543 #ifdef DBUS_BUILD_TESTS
1544
1545 #include <stdio.h>
1546
1547 #define SERVICE_NAME_1 "MyService1"
1548 #define SERVICE_NAME_2 "MyService2"
1549 #define SERVICE_NAME_3 "MyService3"
1550
1551 #define SERVICE_FILE_1 "service-1.service"
1552 #define SERVICE_FILE_2 "service-2.service"
1553 #define SERVICE_FILE_3 "service-3.service"
1554
1555 static dbus_bool_t
1556 test_create_service_file (DBusString *dir,
1557                           const char *filename, 
1558                           const char *name, 
1559                           const char *exec)
1560 {
1561   DBusString  file_name, full_path;
1562   FILE        *file;
1563   dbus_bool_t  ret_val;
1564
1565   ret_val = TRUE;
1566   _dbus_string_init_const (&file_name, filename);
1567
1568   if (!_dbus_string_init (&full_path))
1569     return FALSE;
1570
1571   if (!_dbus_string_append (&full_path, _dbus_string_get_const_data (dir)) ||
1572       !_dbus_concat_dir_and_file (&full_path, &file_name))
1573     {
1574       ret_val = FALSE;
1575       goto out;
1576     }
1577   
1578   file = fopen (_dbus_string_get_const_data (&full_path), "w");
1579   if (!file)
1580     {
1581       ret_val = FALSE;
1582       goto out;
1583     }
1584
1585   fprintf (file, "[D-BUS Service]\nName=%s\nExec=%s\n", name, exec);
1586   fclose (file);
1587
1588 out:
1589   _dbus_string_free (&full_path);
1590   return ret_val;
1591 }
1592
1593 static dbus_bool_t
1594 test_remove_service_file (DBusString *dir, const char *filename)
1595 {
1596   DBusString  file_name, full_path;
1597   dbus_bool_t ret_val;
1598   
1599   ret_val = TRUE;
1600  
1601   _dbus_string_init_const (&file_name, filename);
1602
1603   if (!_dbus_string_init (&full_path))
1604     return FALSE;
1605
1606   if (!_dbus_string_append (&full_path, _dbus_string_get_const_data (dir)) ||
1607       !_dbus_concat_dir_and_file (&full_path, &file_name))
1608     {
1609       ret_val = FALSE;
1610       goto out;
1611     }
1612
1613   if (!_dbus_delete_file (&full_path, NULL))
1614     {
1615       ret_val = FALSE;
1616       goto out;
1617     }
1618
1619 out:
1620   _dbus_string_free (&full_path);
1621   return ret_val;
1622 }
1623
1624 static dbus_bool_t
1625 test_remove_directory (DBusString *dir)
1626 {
1627   DBusDirIter *iter;
1628   DBusString   filename, full_path;
1629   dbus_bool_t  ret_val;
1630   
1631   ret_val = TRUE;
1632   
1633   if (!_dbus_string_init (&filename))
1634     return FALSE;
1635
1636   if (!_dbus_string_init (&full_path))
1637     {
1638       _dbus_string_free (&filename);
1639       return FALSE;
1640     }
1641     
1642   iter = _dbus_directory_open (dir, NULL);
1643   if (iter == NULL)
1644     {
1645       ret_val = FALSE;
1646       goto out;
1647     }
1648   
1649   while (_dbus_directory_get_next_file (iter, &filename, NULL)) 
1650     {
1651       if (!test_remove_service_file (dir, _dbus_string_get_const_data (&filename)))
1652         {
1653           ret_val = FALSE;
1654           goto out;
1655         }
1656     }
1657   _dbus_directory_close (iter);
1658
1659   if (!_dbus_delete_directory (dir, NULL))
1660     {
1661       ret_val = FALSE;
1662       goto out;
1663     }
1664
1665 out:
1666   _dbus_string_free (&filename);
1667   _dbus_string_free (&full_path);
1668
1669   return ret_val;
1670 }
1671
1672 static dbus_bool_t
1673 init_service_reload_test (DBusString *dir)
1674 {
1675   DBusStat stat_buf;
1676  
1677   if (!_dbus_stat (dir, &stat_buf, NULL))
1678     {
1679       if (!_dbus_create_directory (dir, NULL))
1680         return FALSE;
1681     }
1682   else 
1683     {
1684       if (!test_remove_directory (dir))
1685         return FALSE;
1686
1687       if (!_dbus_create_directory (dir, NULL))
1688         return FALSE;
1689     }
1690
1691   /* Create one initial file */
1692   if (!test_create_service_file (dir, SERVICE_FILE_1, SERVICE_NAME_1, "exec-1"))
1693     return FALSE;
1694
1695   return TRUE;
1696 }
1697
1698 static dbus_bool_t
1699 cleanup_service_reload_test (DBusString *dir)
1700 {
1701   if (!test_remove_directory (dir))
1702     return FALSE;
1703
1704   return TRUE;
1705 }
1706
1707 typedef struct 
1708 {
1709   BusActivation *activation;
1710   const char    *service_name;
1711   dbus_bool_t    expecting_find;
1712 } CheckData;
1713
1714 static dbus_bool_t
1715 check_func (void *data)
1716 {
1717   CheckData          *d;
1718   BusActivationEntry *entry;
1719   DBusError           error;
1720   dbus_bool_t         ret_val;
1721   
1722   ret_val = TRUE;
1723   d = data;
1724   
1725   dbus_error_init (&error);
1726  
1727   entry = activation_find_entry (d->activation, d->service_name, &error);
1728   if (entry == NULL)
1729     {
1730       if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) 
1731         {
1732           ret_val = TRUE;
1733         }
1734       else
1735         {
1736           if (d->expecting_find)
1737             ret_val = FALSE;
1738         }
1739       
1740       dbus_error_free (&error);
1741     }
1742   else 
1743     {
1744       if (!d->expecting_find)
1745         ret_val = FALSE;
1746     }
1747
1748   return ret_val;
1749 }
1750
1751 static dbus_bool_t
1752 do_test (const char *description, dbus_bool_t oom_test, CheckData *data)
1753 {
1754   dbus_bool_t err;
1755
1756   if (oom_test)
1757     err = !_dbus_test_oom_handling (description, check_func, data);
1758   else
1759     err = !check_func (data);
1760
1761   if (err) 
1762     _dbus_assert_not_reached ("Test failed");
1763
1764   return TRUE;
1765 }
1766
1767 static dbus_bool_t
1768 do_service_reload_test (DBusString *dir, dbus_bool_t oom_test)
1769 {
1770   BusActivation *activation;
1771   DBusString     address;
1772   DBusList      *directories;
1773   CheckData      d;
1774   
1775   directories = NULL;
1776   _dbus_string_init_const (&address, "");
1777  
1778   if (!_dbus_list_append (&directories, _dbus_string_get_data (dir)))
1779     return FALSE; 
1780
1781   activation = bus_activation_new (NULL, &address, &directories, NULL);
1782   if (!activation)
1783     return FALSE;
1784
1785   d.activation = activation;
1786   
1787   /* Check for existing service file */
1788   d.expecting_find = TRUE;
1789   d.service_name = SERVICE_NAME_1;
1790
1791   if (!do_test ("Existing service file", oom_test, &d))
1792     return FALSE;
1793
1794   /* Check for non-existing service file */
1795   d.expecting_find = FALSE;
1796   d.service_name = SERVICE_NAME_3;
1797
1798   if (!do_test ("Nonexisting service file", oom_test, &d))
1799     return FALSE;
1800
1801   /* Check for added service file */
1802   if (!test_create_service_file (dir, SERVICE_FILE_2, SERVICE_NAME_2, "exec-2"))
1803     return FALSE;
1804
1805   d.expecting_find = TRUE;
1806   d.service_name = SERVICE_NAME_2;
1807   
1808   if (!do_test ("Added service file", oom_test, &d))
1809     return FALSE;
1810   
1811   /* Check for removed service file */
1812   if (!test_remove_service_file (dir, SERVICE_FILE_2))
1813     return FALSE;
1814
1815   d.expecting_find = FALSE;
1816   d.service_name = SERVICE_FILE_2;
1817
1818   if (!do_test ("Removed service file", oom_test, &d))
1819     return FALSE;
1820   
1821   /* Check for updated service file */
1822   
1823   _dbus_sleep_milliseconds (1000); /* Sleep a second to make sure the mtime is updated */
1824
1825   if (!test_create_service_file (dir, SERVICE_FILE_1, SERVICE_NAME_3, "exec-3"))
1826     return FALSE;
1827
1828   d.expecting_find = TRUE;
1829   d.service_name = SERVICE_NAME_3;
1830
1831   if (!do_test ("Updated service file, part 1", oom_test, &d))
1832     return FALSE;
1833
1834   d.expecting_find = FALSE;
1835   d.service_name = SERVICE_NAME_1;
1836
1837   if (!do_test ("Updated service file, part 2", oom_test, &d))
1838     return FALSE; 
1839
1840   bus_activation_unref (activation);
1841   _dbus_list_clear (&directories);
1842
1843   return TRUE;
1844 }
1845
1846 dbus_bool_t
1847 bus_activation_service_reload_test (const DBusString *test_data_dir)
1848 {
1849   DBusString directory;
1850
1851   if (!_dbus_string_init (&directory))
1852     return FALSE;
1853
1854   if (!_dbus_string_append (&directory, "/tmp/dbus-reload-test-") ||
1855       !_dbus_generate_random_ascii (&directory, 6))
1856      {
1857        return FALSE;
1858      }
1859    
1860   /* Do normal tests */
1861   if (!init_service_reload_test (&directory))
1862     _dbus_assert_not_reached ("could not initiate service reload test");
1863  
1864   if (!do_service_reload_test (&directory, FALSE));
1865   
1866   /* Do OOM tests */
1867   if (!init_service_reload_test (&directory))
1868     _dbus_assert_not_reached ("could not initiate service reload test");
1869  
1870   if (!do_service_reload_test (&directory, TRUE));
1871  
1872   /* Cleanup test directory */
1873   if (!cleanup_service_reload_test (&directory))
1874     return FALSE;
1875   
1876   _dbus_string_free (&directory);
1877   
1878   return TRUE;
1879 }
1880
1881 #endif /* DBUS_BUILD_TESTS */