(linenum→info "unix/slp.c:2238")

dbus/1.0.2/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.1
    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-shell.h>
   34: #include <dbus/dbus-spawn.h>
   35: #include <dbus/dbus-timeout.h>
   36: #include <dbus/dbus-sysdeps.h>
   37: #include <dirent.h>
   38: #include <errno.h>
   39: 
   40: #define DBUS_SERVICE_SECTION "D-BUS Service"
   41: #define DBUS_SERVICE_NAME "Name"
   42: #define DBUS_SERVICE_EXEC "Exec"
   43: 
   44: struct BusActivation
   45: {
   46:   int refcount;
   47:   DBusHashTable *entries;
   48:   DBusHashTable *pending_activations;
   49:   char *server_address;
   50:   BusContext *context;
   51:   int n_pending_activations; /**< This is in fact the number of BusPendingActivationEntry,
   52:                               * i.e. number of pending activation requests, not pending
   53:                               * activations per se
   54:                               */
   55:   DBusHashTable *directories;
   56: };
   57: 
   58: typedef struct
   59: {
   60:   int refcount;
   61:   char *dir_c;
   62:   DBusHashTable *entries;
   63: } BusServiceDirectory;
   64: 
   65: typedef struct
   66: {
   67:   int refcount;
   68:   char *name;
   69:   char *exec;
   70:   unsigned long mtime;
   71:   BusServiceDirectory *s_dir;
   72:   char *filename;
   73: } BusActivationEntry;
   74: 
   75: typedef struct BusPendingActivationEntry BusPendingActivationEntry;
   76: 
   77: struct BusPendingActivationEntry
   78: {
   79:   DBusMessage *activation_message;
   80:   DBusConnection *connection;
   81: 
   82:   dbus_bool_t auto_activation;
   83: };
   84: 
   85: typedef struct
   86: {
   87:   int refcount;
   88:   BusActivation *activation;
   89:   char *service_name;
   90:   char *exec;
   91:   DBusList *entries;
   92:   int n_entries;
   93:   DBusBabysitter *babysitter;
   94:   DBusTimeout *timeout;
   95:   unsigned int timeout_added : 1;
   96: } BusPendingActivation;
   97: 
   98: #if 0
   99: static BusServiceDirectory *
  100: bus_service_directory_ref (BusServiceDirectory *dir)
  101: {
  102:   _dbus_assert (dir->refcount);
  103:   
  104:   dir->refcount++;
  105: 
  106:   return dir;
  107: }
  108: #endif
  109: 
  110: static void
  111: bus_service_directory_unref (BusServiceDirectory *dir)
  112: {
  113:   if (dir == NULL) 
  114:     return; 
  115: 
  116:   _dbus_assert (dir->refcount > 0);
  117:   dir->refcount--;
  118: 
  119:   if (dir->refcount > 0)
  120:     return;
  121: 
  122:   if (dir->entries)
  123:     _dbus_hash_table_unref (dir->entries);
  124: 
  125:   dbus_free (dir->dir_c);
  126:   dbus_free (dir);
  127: }
  128: 
  129: static void
  130: bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
  131: {
  132:   if (entry->activation_message)
  133:     dbus_message_unref (entry->activation_message);
  134:   
  135:   if (entry->connection)
  136:     dbus_connection_unref (entry->connection);
  137:   
  138:   dbus_free (entry);
  139: }
  140: 
  141: static void
  142: handle_timeout_callback (DBusTimeout   *timeout,
  143:                          void          *data)
  144: {
  145:   BusPendingActivation *pending_activation = data;
  146: 
  147:   while (!dbus_timeout_handle (pending_activation->timeout))
  148:     _dbus_wait_for_memory ();
  149: }
  150: 
  151: static BusPendingActivation * 
  152: bus_pending_activation_ref (BusPendingActivation *pending_activation)
  153: {
  154:   _dbus_assert (pending_activation->refcount > 0);
  155:   pending_activation->refcount += 1;
  156: 
  157:   return pending_activation;
  158: }
  159: 
  160: static void
  161: bus_pending_activation_unref (BusPendingActivation *pending_activation)
  162: {
  163:   DBusList *link;
  164:   
  165:   if (pending_activation == NULL) /* hash table requires this */
  166:     return;
  167: 
  168:   _dbus_assert (pending_activation->refcount > 0);
  169:   pending_activation->refcount -= 1;
  170: 
  171:   if (pending_activation->refcount > 0)
  172:     return;
  173:   
  174:   if (pending_activation->timeout_added)
  175:     {
  176:       _dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
  177:                                  pending_activation->timeout,
  178:                                  handle_timeout_callback, pending_activation);
  179:       pending_activation->timeout_added = FALSE;
  180:     }
  181: 
  182:   if (pending_activation->timeout)
  183:     _dbus_timeout_unref (pending_activation->timeout);
  184:   
  185:   if (pending_activation->babysitter)
  186:     {
  187:       if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
  188:                                                  NULL, NULL, NULL,
  189:                                                  pending_activation->babysitter,
  190:                                                  NULL))
  191:         _dbus_assert_not_reached ("setting watch functions to NULL failed");
  192:       
  193:       _dbus_babysitter_unref (pending_activation->babysitter);
  194:     }
  195:   
  196:   dbus_free (pending_activation->service_name);
  197:   dbus_free (pending_activation->exec);
  198: 
  199:   link = _dbus_list_get_first_link (&pending_activation->entries);
  200: 
  201:   while (link != NULL)
  202:     {
  203:       BusPendingActivationEntry *entry = link->data;
  204: 
  205:       bus_pending_activation_entry_free (entry);
  206: 
  207:       link = _dbus_list_get_next_link (&pending_activation->entries, link);
  208:     }
  209:   _dbus_list_clear (&pending_activation->entries);
  210: 
  211:   pending_activation->activation->n_pending_activations -=
  212:     pending_activation->n_entries;
  213: 
  214:   _dbus_assert (pending_activation->activation->n_pending_activations >= 0);
  215:   
  216:   dbus_free (pending_activation);
  217: }
  218: 
  219: static BusActivationEntry *
  220: bus_activation_entry_ref (BusActivationEntry *entry)
  221: {
  222:   _dbus_assert (entry->refcount > 0);
  223:   entry->refcount++;
  224: 
  225:   return entry;
  226: }
  227: 
  228: static void
  229: bus_activation_entry_unref (BusActivationEntry *entry)
  230: {
  231:   if (entry == NULL) /* hash table requires this */
  232:     return;
  233:   
  234:   _dbus_assert (entry->refcount > 0);
  235:   entry->refcount--;
  236:   
  237:   if (entry->refcount > 0) 
  238:     return;
  239:   
  240:   dbus_free (entry->name);
  241:   dbus_free (entry->exec);
  242:   dbus_free (entry->filename);
  243: 
  244:   dbus_free (entry);
  245: }
  246: 
  247: static dbus_bool_t
  248: update_desktop_file_entry (BusActivation       *activation,
  249:                            BusServiceDirectory *s_dir,
  250:                            DBusString          *filename,
  251:                            BusDesktopFile      *desktop_file,
  252:                            DBusError           *error)
  253: {
  254:   char *name, *exec;
  255:   BusActivationEntry *entry;
  256:   DBusStat stat_buf;
  257:   DBusString file_path;
  258: 
  259:   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
  260:   
  261:   name = NULL;
  262:   exec = NULL;
  263:   entry = NULL;
  264:   
  265:   if (!_dbus_string_init (&file_path))
  266:     {
  267:       BUS_SET_OOM (error);
  268:       return FALSE;
  269:     }
  270:  
  271:   if (!_dbus_string_append (&file_path, s_dir->dir_c) ||
  272:       !_dbus_concat_dir_and_file (&file_path, filename))
  273:     {
  274:       BUS_SET_OOM (error);
  275:       goto failed;
  276:     }
  277:  
  278:   if (!_dbus_stat (&file_path, &stat_buf, NULL)) 
  279:     {
  280:       dbus_set_error (error, DBUS_ERROR_FAILED,
  281:                       "Can't stat the service file\n");
  282:       goto failed;
  283:     }
  284:  
  285:   if (!bus_desktop_file_get_string (desktop_file,
  286:                                     DBUS_SERVICE_SECTION,
  287:                                     DBUS_SERVICE_NAME,
  288:                                     &name,
  289:                                     error))
  290:     goto failed;
  291: 
  292:   if (!bus_desktop_file_get_string (desktop_file,
  293:                                     DBUS_SERVICE_SECTION,
  294:                                     DBUS_SERVICE_EXEC,
  295:                                     &exec,
  296:                                     error))
  297:     goto failed;
  298: 
  299:   entry = _dbus_hash_table_lookup_string (s_dir->entries, 
  300:                                           _dbus_string_get_const_data (filename));
  301:   if (entry == NULL) /* New file */
  302:     { 
  303:       /* FIXME we need a better-defined algorithm for which service file to
  304:        * pick than "whichever one is first in the directory listing"
  305:        */
  306:       if (_dbus_hash_table_lookup_string (activation->entries, name))
  307:         {
  308:           dbus_set_error (error, DBUS_ERROR_FAILED,
  309:                           "Service %s already exists in activation entry list\n", name);
  310:           goto failed;
  311:         }
  312:       
  313:       entry = dbus_new0 (BusActivationEntry, 1);
  314:       if (entry == NULL)
  315:         {
  316:           BUS_SET_OOM (error);
  317:           goto failed;
  318:         }
  319:      
  320:       entry->name = name;
  321:       entry->exec = exec;
  322:       entry->refcount = 1;
  323:     
  324:       entry->s_dir = s_dir;
  325:       entry->filename = _dbus_strdup (_dbus_string_get_const_data (filename));
  326:       if (!entry->filename)
  327:         {
  328:           BUS_SET_OOM (error);
  329:           goto failed;
  330:         }
  331: 
  332:       if (!_dbus_hash_table_insert_string (activation->entries, entry->name, bus_activation_entry_ref (entry)))
  333:         {
  334:           BUS_SET_OOM (error);
  335:           goto failed;
  336:         }
  337:      
  338:       if (!_dbus_hash_table_insert_string (s_dir->entries, entry->filename, bus_activation_entry_ref (entry)))
  339:         {
  340:           /* Revert the insertion in the entries table */
  341:           _dbus_hash_table_remove_string (activation->entries, entry->name);
  342:           BUS_SET_OOM (error);
  343:           goto failed;
  344:         }
  345: 
  346:       _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
  347:     }
  348:   else /* Just update the entry */
  349:     {
  350:       bus_activation_entry_ref (entry);
  351:       _dbus_hash_table_remove_string (activation->entries, entry->name);
  352: 
  353:       if (_dbus_hash_table_lookup_string (activation->entries, name))
  354:         {
  355:           _dbus_verbose ("The new service name \"%s\" of service file \"%s\" already in cache, ignoring\n",
  356:                          name, _dbus_string_get_const_data (&file_path));
  357:           goto failed;
  358:         }
  359:  
  360:       dbus_free (entry->name);
  361:       dbus_free (entry->exec);
  362:       entry->name = name;
  363:       entry->exec = exec;
  364:       if (!_dbus_hash_table_insert_string (activation->entries,
  365:                                            entry->name, bus_activation_entry_ref(entry)))
  366:         {
  367:           BUS_SET_OOM (error);
  368:           /* Also remove path to entries hash since we want this in sync with
  369:            * the entries hash table */
  370:           _dbus_hash_table_remove_string (entry->s_dir->entries, 
  371:                                           entry->filename);
  372:           bus_activation_entry_unref (entry);
  373:           return FALSE;
  374:         }
  375:     }
  376:   
  377:   entry->mtime = stat_buf.mtime;
  378:   
  379:   _dbus_string_free (&file_path);
  380:   bus_activation_entry_unref (entry);
  381: 
  382:   return TRUE;
  383: 
  384: failed:
  385:   dbus_free (name);
  386:   dbus_free (exec);
  387:   _dbus_string_free (&file_path);
  388: 
  389:   if (entry)
  390:     bus_activation_entry_unref (entry);
  391:   
  392:   return FALSE;
  393: }
  394: 
  395: static dbus_bool_t
  396: check_service_file (BusActivation       *activation,
  397:                     BusActivationEntry  *entry,
  398:                     BusActivationEntry **updated_entry,
  399:                     DBusError           *error)
  400: {
  401:   DBusStat stat_buf;
  402:   dbus_bool_t retval;
  403:   BusActivationEntry *tmp_entry;
  404:   DBusString file_path;
  405:   DBusString filename;
  406: 
  407:   retval = TRUE;
  408:   tmp_entry = entry;
  409:   
  410:   _dbus_string_init_const (&filename, entry->filename);
  411:   
  412:   if (!_dbus_string_init (&file_path))
  413:     {
  414:       BUS_SET_OOM (error);
  415:       return FALSE;
  416:     }
  417:  
  418:   if (!_dbus_string_append (&file_path, entry->s_dir->dir_c) ||
  419:       !_dbus_concat_dir_and_file (&file_path, &filename))
  420:     {
  421:       BUS_SET_OOM (error);
  422:       retval = FALSE;
  423:       goto out;
  424:     }
  425:   
  426:   if (!_dbus_stat (&file_path, &stat_buf, NULL))
  427:     {
  428:       _dbus_verbose ("****** Can't stat file \"%s\", removing from cache\n",
  429:                      _dbus_string_get_const_data (&file_path));
  430: 
  431:       _dbus_hash_table_remove_string (activation->entries, entry->name);
  432:       _dbus_hash_table_remove_string (entry->s_dir->entries, entry->filename);
  433: 
  434:       tmp_entry = NULL;
  435:       retval = TRUE;
  436:       goto out;
  437:     }
  438:   else 
  439:     {
  440:       if (stat_buf.mtime > entry->mtime) 
  441:         {
  442:           BusDesktopFile *desktop_file;
  443:           DBusError tmp_error;
  444:           
  445:           dbus_error_init (&tmp_error);
  446:           
  447:           desktop_file = bus_desktop_file_load (&file_path, &tmp_error);
  448:           if (desktop_file == NULL)
  449:             {
  450:               _dbus_verbose ("Could not load %s: %s\n",
  451:                              _dbus_string_get_const_data (&file_path), 
  452:                              tmp_error.message);
  453:               if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
  454:                 {
  455:                   dbus_move_error (&tmp_error, error);
  456:                   retval = FALSE;
  457:                   goto out;
  458:                 }
  459:               dbus_error_free (&tmp_error);
  460:               retval = TRUE;
  461:               goto out;
  462:             }
  463:          
  464:           /* @todo We can return OOM or a DBUS_ERROR_FAILED error 
  465:            *       Handle these both better
  466:            */ 
  467:           if (!update_desktop_file_entry (activation, entry->s_dir, &filename, desktop_file, &tmp_error))
  468:             {
  469:               bus_desktop_file_free (desktop_file);
  470:               if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
  471:                 {
  472:                   dbus_move_error (&tmp_error, error);
  473:                   retval = FALSE;
  474:                   goto out;
  475:                 }
  476:               dbus_error_free (&tmp_error);
  477:               retval = TRUE;
  478:               goto out;
  479:             }
  480:          
  481:           bus_desktop_file_free (desktop_file);
  482:           retval = TRUE;
  483:         }
  484:     }
  485:   
  486: out:
  487:   _dbus_string_free (&file_path);
  488: 
  489:   if (updated_entry != NULL)
  490:     *updated_entry = tmp_entry;
  491:   return retval;
  492: }
  493: 
  494: 
  495: /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
  496:  * hash entries it already added.
  497:  */
  498: static dbus_bool_t
  499: update_directory (BusActivation       *activation,
  500:                   BusServiceDirectory *s_dir,
  501:                   DBusError           *error)
  502: {
  503:   DBusDirIter *iter;
  504:   DBusString dir, filename;
  505:   BusDesktopFile *desktop_file;
  506:   DBusError tmp_error;
  507:   dbus_bool_t retval;
  508:   BusActivationEntry *entry;
  509:   DBusString full_path;
  510:   
  511:   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
  512:   
  513:   iter = NULL;
  514:   desktop_file = NULL;
  515:   
  516:   _dbus_string_init_const (&dir, s_dir->dir_c);
  517:   
  518:   if (!_dbus_string_init (&filename))
  519:     {
  520:       BUS_SET_OOM (error);
  521:       return FALSE;
  522:     }
  523: 
  524:   if (!_dbus_string_init (&full_path))
  525:     {
  526:       BUS_SET_OOM (error);
  527:       _dbus_string_free (&filename);
  528:       return FALSE;
  529:     }
  530: 
  531:   retval = FALSE;
  532: 
  533:   /* from this point it's safe to "goto out" */
  534:   
  535:   iter = _dbus_directory_open (&dir, error);
  536:   if (iter == NULL)
  537:     {
  538:       _dbus_verbose ("Failed to open directory %s: %s\n",
  539:                      s_dir->dir_c, 
  540:                      error ? error->message : "unknown");
  541:       goto out;
  542:     }
  543:   
  544:   /* Now read the files */
  545:   dbus_error_init (&tmp_error);
  546:   while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
  547:     {
  548:       _dbus_assert (!dbus_error_is_set (&tmp_error));
  549:       
  550:       _dbus_string_set_length (&full_path, 0);
  551:       
  552:       if (!_dbus_string_ends_with_c_str (&filename, ".service"))
  553:         {
  554:           _dbus_verbose ("Skipping non-.service file %s\n",
  555:                          _dbus_string_get_const_data (&filename));
  556:           continue;
  557:         }
  558: 
  559:       entry = _dbus_hash_table_lookup_string (s_dir->entries, _dbus_string_get_const_data (&filename));
  560:       if (entry) /* Already has this service file in the cache */ 
  561:         {
  562:           if (!check_service_file (activation, entry, NULL, error))
  563:             goto out;
  564: 
  565:           continue;
  566:         }
  567:       
  568:       if (!_dbus_string_append (&full_path, s_dir->dir_c) ||
  569:           !_dbus_concat_dir_and_file (&full_path, &filename))
  570:         {
  571:           BUS_SET_OOM (error);
  572:           goto out;
  573:         }
  574:           
  575:       /* New file */
  576:       desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
  577:       if (desktop_file == NULL)
  578:         {
  579:           _dbus_verbose ("Could not load %s: %s\n",
  580:                          _dbus_string_get_const_data (&full_path),
  581:                          tmp_error.message);
  582: 
  583:           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
  584:             {
  585:               dbus_move_error (&tmp_error, error);
  586:               goto out;
  587:             }
  588:           
  589:           dbus_error_free (&tmp_error);
  590:           continue;
  591:         }
  592: 
  593:       /* @todo We can return OOM or a DBUS_ERROR_FAILED error 
  594:        *       Handle these both better
  595:        */ 
  596:       if (!update_desktop_file_entry (activation, s_dir, &filename, desktop_file, &tmp_error))
  597:         {
  598:           bus_desktop_file_free (desktop_file);
  599:           desktop_file = NULL;
  600:           
  601:           _dbus_verbose ("Could not add %s to activation entry list: %s\n",
  602:                          _dbus_string_get_const_data (&full_path), tmp_error.message);
  603: 
  604:           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
  605:             {
  606:               dbus_move_error (&tmp_error, error);
  607:               goto out;
  608:             }
  609: 
  610:           dbus_error_free (&tmp_error);
  611:           continue;
  612:         }
  613:       else
  614:         {
  615:           bus_desktop_file_free (desktop_file);
  616:           desktop_file = NULL;
  617:           continue;
  618:         }
  619:     }
  620: 
  621:   if (dbus_error_is_set (&tmp_error))
  622:     {
  623:       dbus_move_error (&tmp_error, error);