LCOV - code coverage report
Current view: top level - shared - bus-wait-for-units.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 198 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 17 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 154 0.0 %

           Branch data     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