LCOV - code coverage report
Current view: top level - shared - bus-wait-for-units.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 198 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 17 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "bus-util.h"
       4             : #include "bus-wait-for-units.h"
       5             : #include "hashmap.h"
       6             : #include "string-util.h"
       7             : #include "strv.h"
       8             : #include "unit-def.h"
       9             : 
      10             : typedef struct WaitForItem {
      11             :         BusWaitForUnits *parent;
      12             : 
      13             :         BusWaitForUnitsFlags flags;
      14             : 
      15             :         char *bus_path;
      16             : 
      17             :         sd_bus_slot *slot_get_all;
      18             :         sd_bus_slot *slot_properties_changed;
      19             : 
      20             :         bus_wait_for_units_unit_callback unit_callback;
      21             :         void *userdata;
      22             : 
      23             :         char *active_state;
      24             :         uint32_t job_id;
      25             :         char *clean_result;
      26             : } WaitForItem;
      27             : 
      28             : typedef struct BusWaitForUnits {
      29             :         sd_bus *bus;
      30             :         sd_bus_slot *slot_disconnected;
      31             : 
      32             :         Hashmap *items;
      33             : 
      34             :         bus_wait_for_units_ready_callback ready_callback;
      35             :         void *userdata;
      36             : 
      37             :         WaitForItem *current;
      38             : 
      39             :         BusWaitForUnitsState state;
      40             :         bool has_failed:1;
      41             : } BusWaitForUnits;
      42             : 
      43           0 : static WaitForItem *wait_for_item_free(WaitForItem *item) {
      44             :         int r;
      45             : 
      46           0 :         if (!item)
      47           0 :                 return NULL;
      48             : 
      49           0 :         if (item->parent) {
      50           0 :                 if (FLAGS_SET(item->flags, BUS_WAIT_REFFED) && item->bus_path && item->parent->bus) {
      51           0 :                         r = sd_bus_call_method_async(
      52           0 :                                         item->parent->bus,
      53             :                                         NULL,
      54             :                                         "org.freedesktop.systemd1",
      55           0 :                                         item->bus_path,
      56             :                                         "org.freedesktop.systemd1.Unit",
      57             :                                         "Unref",
      58             :                                         NULL,
      59             :                                         NULL,
      60             :                                         NULL);
      61           0 :                         if (r < 0)
      62           0 :                                 log_debug_errno(r, "Failed to drop reference to unit %s, ignoring: %m", item->bus_path);
      63             :                 }
      64             : 
      65           0 :                 assert_se(hashmap_remove(item->parent->items, item->bus_path) == item);
      66             : 
      67           0 :                 if (item->parent->current == item)
      68           0 :                         item->parent->current = NULL;
      69             :         }
      70             : 
      71           0 :         sd_bus_slot_unref(item->slot_properties_changed);
      72           0 :         sd_bus_slot_unref(item->slot_get_all);
      73             : 
      74           0 :         free(item->bus_path);
      75           0 :         free(item->active_state);
      76           0 :         free(item->clean_result);
      77             : 
      78           0 :         return mfree(item);
      79             : }
      80             : 
      81           0 : DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem*, wait_for_item_free);
      82             : 
      83           0 : static void bus_wait_for_units_clear(BusWaitForUnits *d) {
      84             :         WaitForItem *item;
      85             : 
      86           0 :         assert(d);
      87             : 
      88           0 :         d->slot_disconnected = sd_bus_slot_unref(d->slot_disconnected);
      89           0 :         d->bus = sd_bus_unref(d->bus);
      90             : 
      91           0 :         while ((item = hashmap_first(d->items))) {
      92           0 :                 d->current = item;
      93             : 
      94           0 :                 item->unit_callback(d, item->bus_path, false, item->userdata);
      95           0 :                 wait_for_item_free(item);
      96             :         }
      97             : 
      98           0 :         d->items = hashmap_free(d->items);
      99           0 : }
     100             : 
     101           0 : static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
     102           0 :         BusWaitForUnits *d = userdata;
     103             : 
     104           0 :         assert(m);
     105           0 :         assert(d);
     106             : 
     107           0 :         log_error("Warning! D-Bus connection terminated.");
     108             : 
     109           0 :         bus_wait_for_units_clear(d);
     110             : 
     111           0 :         if (d->ready_callback)
     112           0 :                 d->ready_callback(d, false, d->userdata);
     113             :         else /* If no ready callback is specified close the connection so that the event loop exits */
     114           0 :                 sd_bus_close(sd_bus_message_get_bus(m));
     115             : 
     116           0 :         return 0;
     117             : }
     118             : 
     119           0 : int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret) {
     120           0 :         _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *d = NULL;
     121             :         int r;
     122             : 
     123           0 :         assert(bus);
     124           0 :         assert(ret);
     125             : 
     126           0 :         d = new(BusWaitForUnits, 1);
     127           0 :         if (!d)
     128           0 :                 return -ENOMEM;
     129             : 
     130           0 :         *d = (BusWaitForUnits) {
     131             :                 .state = BUS_WAIT_SUCCESS,
     132           0 :                 .bus = sd_bus_ref(bus),
     133             :         };
     134             : 
     135           0 :         r = sd_bus_match_signal_async(
     136             :                         bus,
     137           0 :                         &d->slot_disconnected,
     138             :                         "org.freedesktop.DBus.Local",
     139             :                         NULL,
     140             :                         "org.freedesktop.DBus.Local",
     141             :                         "Disconnected",
     142             :                         match_disconnected, NULL, d);
     143           0 :         if (r < 0)
     144           0 :                 return r;
     145             : 
     146           0 :         *ret = TAKE_PTR(d);
     147           0 :         return 0;
     148             : }
     149             : 
     150           0 : BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d) {
     151           0 :         if (!d)
     152           0 :                 return NULL;
     153             : 
     154           0 :         bus_wait_for_units_clear(d);
     155           0 :         sd_bus_slot_unref(d->slot_disconnected);
     156           0 :         sd_bus_unref(d->bus);
     157             : 
     158           0 :         return mfree(d);
     159             : }
     160             : 
     161           0 : static bool bus_wait_for_units_is_ready(BusWaitForUnits *d) {
     162           0 :         assert(d);
     163             : 
     164           0 :         if (!d->bus) /* Disconnected? */
     165           0 :                 return true;
     166             : 
     167           0 :         return hashmap_isempty(d->items);
     168             : }
     169             : 
     170           0 : void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata) {
     171           0 :         assert(d);
     172             : 
     173           0 :         d->ready_callback = callback;
     174           0 :         d->userdata = userdata;
     175           0 : }
     176             : 
     177           0 : static void bus_wait_for_units_check_ready(BusWaitForUnits *d) {
     178           0 :         assert(d);
     179             : 
     180           0 :         if (!bus_wait_for_units_is_ready(d))
     181           0 :                 return;
     182             : 
     183           0 :         d->state = d->has_failed ? BUS_WAIT_FAILURE : BUS_WAIT_SUCCESS;
     184             : 
     185           0 :         if (d->ready_callback)
     186           0 :                 d->ready_callback(d, d->state, d->userdata);
     187             : }
     188             : 
     189           0 : static void wait_for_item_check_ready(WaitForItem *item) {
     190             :         BusWaitForUnits *d;
     191             : 
     192           0 :         assert(item);
     193           0 :         assert_se(d = item->parent);
     194             : 
     195           0 :         if (FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END)) {
     196             : 
     197           0 :                 if (item->clean_result && !streq(item->clean_result, "success"))
     198           0 :                         d->has_failed = true;
     199             : 
     200           0 :                 if (!item->active_state || streq(item->active_state, "maintenance"))
     201           0 :                         return;
     202             :         }
     203             : 
     204           0 :         if (FLAGS_SET(item->flags, BUS_WAIT_NO_JOB) && item->job_id != 0)
     205           0 :                 return;
     206             : 
     207           0 :         if (FLAGS_SET(item->flags, BUS_WAIT_FOR_INACTIVE)) {
     208             : 
     209           0 :                 if (streq_ptr(item->active_state, "failed"))
     210           0 :                         d->has_failed = true;
     211           0 :                 else if (!streq_ptr(item->active_state, "inactive"))
     212           0 :                         return;
     213             :         }
     214             : 
     215           0 :         if (item->unit_callback) {
     216           0 :                 d->current = item;
     217           0 :                 item->unit_callback(d, item->bus_path, true, item->userdata);
     218             :         }
     219             : 
     220           0 :         wait_for_item_free(item);
     221             : 
     222           0 :         bus_wait_for_units_check_ready(d);
     223             : }
     224             : 
     225           0 : static int property_map_job(
     226             :                 sd_bus *bus,
     227             :                 const char *member,
     228             :                 sd_bus_message *m,
     229             :                 sd_bus_error *error,
     230             :                 void *userdata) {
     231             : 
     232           0 :         WaitForItem *item = userdata;
     233             :         const char *path;
     234             :         uint32_t id;
     235             :         int r;
     236             : 
     237           0 :         assert(item);
     238             : 
     239           0 :         r = sd_bus_message_read(m, "(uo)", &id, &path);
     240           0 :         if (r < 0)
     241           0 :                 return r;
     242             : 
     243           0 :         item->job_id = id;
     244           0 :         return 0;
     245             : }
     246             : 
     247           0 : static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) {
     248             : 
     249             :         static const struct bus_properties_map map[] = {
     250             :                 { "ActiveState", "s",    NULL,             offsetof(WaitForItem, active_state) },
     251             :                 { "Job",         "(uo)", property_map_job, 0                                   },
     252             :                 { "CleanResult", "s",    NULL,             offsetof(WaitForItem, clean_result) },
     253             :                 {}
     254             :         };
     255             : 
     256             :         int r;
     257             : 
     258           0 :         assert(item);
     259           0 :         assert(m);
     260             : 
     261           0 :         r = bus_message_map_all_properties(m, map, BUS_MAP_STRDUP, NULL, item);
     262           0 :         if (r < 0)
     263           0 :                 return r;
     264             : 
     265           0 :         wait_for_item_check_ready(item);
     266           0 :         return 0;
     267             : }
     268             : 
     269           0 : static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
     270           0 :         WaitForItem *item = userdata;
     271             :         const char *interface;
     272             :         int r;
     273             : 
     274           0 :         assert(item);
     275             : 
     276           0 :         r = sd_bus_message_read(m, "s", &interface);
     277           0 :         if (r < 0) {
     278           0 :                 log_debug_errno(r, "Failed to parse PropertiesChanged signal: %m");
     279           0 :                 return 0;
     280             :         }
     281             : 
     282           0 :         if (!streq(interface, "org.freedesktop.systemd1.Unit"))
     283           0 :                 return 0;
     284             : 
     285           0 :         r = wait_for_item_parse_properties(item, m);
     286           0 :         if (r < 0)
     287           0 :                 log_debug_errno(r, "Failed to process PropertiesChanged signal: %m");
     288             : 
     289           0 :         return 0;
     290             : }
     291             : 
     292           0 : static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error *error) {
     293           0 :         WaitForItem *item = userdata;
     294             :         int r;
     295             : 
     296           0 :         assert(item);
     297             : 
     298           0 :         if (sd_bus_error_is_set(error)) {
     299           0 :                 BusWaitForUnits *d = item->parent;
     300             : 
     301           0 :                 d->has_failed = true;
     302             : 
     303           0 :                 log_debug_errno(sd_bus_error_get_errno(error), "GetAll() failed for %s: %s",
     304             :                                 item->bus_path, error->message);
     305             : 
     306           0 :                 d->current = item;
     307           0 :                 item->unit_callback(d, item->bus_path, false, item->userdata);
     308           0 :                 wait_for_item_free(item);
     309             : 
     310           0 :                 bus_wait_for_units_check_ready(d);
     311           0 :                 return 0;
     312             :         }
     313             : 
     314           0 :         r = wait_for_item_parse_properties(item, m);
     315           0 :         if (r < 0)
     316           0 :                 log_debug_errno(r, "Failed to process GetAll method reply: %m");
     317             : 
     318           0 :         return 0;
     319             : }
     320             : 
     321           0 : int bus_wait_for_units_add_unit(
     322             :                 BusWaitForUnits *d,
     323             :                 const char *unit,
     324             :                 BusWaitForUnitsFlags flags,
     325             :                 bus_wait_for_units_unit_callback callback,
     326             :                 void *userdata) {
     327             : 
     328           0 :         _cleanup_(wait_for_item_freep) WaitForItem *item = NULL;
     329             :         int r;
     330             : 
     331           0 :         assert(d);
     332           0 :         assert(unit);
     333             : 
     334           0 :         assert(flags != 0);
     335             : 
     336           0 :         r = hashmap_ensure_allocated(&d->items, &string_hash_ops);
     337           0 :         if (r < 0)
     338           0 :                 return r;
     339             : 
     340           0 :         item = new(WaitForItem, 1);
     341           0 :         if (!item)
     342           0 :                 return -ENOMEM;
     343             : 
     344           0 :         *item = (WaitForItem) {
     345             :                 .flags = flags,
     346           0 :                 .bus_path = unit_dbus_path_from_name(unit),
     347             :                 .unit_callback = callback,
     348             :                 .userdata = userdata,
     349             :                 .job_id = UINT32_MAX,
     350             :         };
     351             : 
     352           0 :         if (!item->bus_path)
     353           0 :                 return -ENOMEM;
     354             : 
     355           0 :         if (!FLAGS_SET(item->flags, BUS_WAIT_REFFED)) {
     356           0 :                 r = sd_bus_call_method_async(
     357             :                                 d->bus,
     358             :                                 NULL,
     359             :                                 "org.freedesktop.systemd1",
     360           0 :                                 item->bus_path,
     361             :                                 "org.freedesktop.systemd1.Unit",
     362             :                                 "Ref",
     363             :                                 NULL,
     364             :                                 NULL,
     365             :                                 NULL);
     366           0 :                 if (r < 0)
     367           0 :                         return log_debug_errno(r, "Failed to add reference to unit %s: %m", unit);
     368             : 
     369           0 :                 item->flags |= BUS_WAIT_REFFED;
     370             :         }
     371             : 
     372           0 :         r = sd_bus_match_signal_async(
     373             :                         d->bus,
     374           0 :                         &item->slot_properties_changed,
     375             :                         "org.freedesktop.systemd1",
     376           0 :                         item->bus_path,
     377             :                         "org.freedesktop.DBus.Properties",
     378             :                         "PropertiesChanged",
     379             :                         on_properties_changed,
     380             :                         NULL,
     381             :                         item);
     382           0 :         if (r < 0)
     383           0 :                 return log_debug_errno(r, "Failed to request match for PropertiesChanged signal: %m");
     384             : 
     385           0 :         r = sd_bus_call_method_async(
     386             :                         d->bus,
     387           0 :                         &item->slot_get_all,
     388             :                         "org.freedesktop.systemd1",
     389           0 :                         item->bus_path,
     390             :                         "org.freedesktop.DBus.Properties",
     391             :                         "GetAll",
     392             :                         on_get_all_properties,
     393             :                         item,
     394           0 :                         "s", FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END) ? NULL : "org.freedesktop.systemd1.Unit");
     395           0 :         if (r < 0)
     396           0 :                 return log_debug_errno(r, "Failed to request properties of unit %s: %m", unit);
     397             : 
     398           0 :         r = hashmap_put(d->items, item->bus_path, item);
     399           0 :         if (r < 0)
     400           0 :                 return r;
     401             : 
     402           0 :         d->state = BUS_WAIT_RUNNING;
     403           0 :         item->parent = d;
     404           0 :         TAKE_PTR(item);
     405           0 :         return 0;
     406             : }
     407             : 
     408           0 : int bus_wait_for_units_run(BusWaitForUnits *d) {
     409             :         int r;
     410             : 
     411           0 :         assert(d);
     412             : 
     413           0 :         while (d->state == BUS_WAIT_RUNNING) {
     414             : 
     415           0 :                 r = sd_bus_process(d->bus, NULL);
     416           0 :                 if (r < 0)
     417           0 :                         return r;
     418           0 :                 if (r > 0)
     419           0 :                         continue;
     420             : 
     421           0 :                 r = sd_bus_wait(d->bus, (uint64_t) -1);
     422           0 :                 if (r < 0)
     423           0 :                         return r;
     424             :         }
     425             : 
     426           0 :         return d->state;
     427             : }
     428             : 
     429           0 : BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d) {
     430           0 :         assert(d);
     431             : 
     432           0 :         return d->state;
     433             : }

Generated by: LCOV version 1.14