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

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "alloc-util.h"
       4             : #include "bus-wait-for-jobs.h"
       5             : #include "set.h"
       6             : #include "bus-util.h"
       7             : #include "bus-internal.h"
       8             : #include "unit-def.h"
       9             : #include "escape.h"
      10             : #include "strv.h"
      11             : 
      12             : typedef struct BusWaitForJobs {
      13             :         sd_bus *bus;
      14             : 
      15             :         /* The set of jobs to wait for, as bus object paths */
      16             :         Set *jobs;
      17             : 
      18             :         /* The unit name and job result of the last Job message */
      19             :         char *name;
      20             :         char *result;
      21             : 
      22             :         sd_bus_slot *slot_job_removed;
      23             :         sd_bus_slot *slot_disconnected;
      24             : } BusWaitForJobs;
      25             : 
      26           0 : static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
      27           0 :         assert(m);
      28             : 
      29           0 :         log_error("Warning! D-Bus connection terminated.");
      30           0 :         sd_bus_close(sd_bus_message_get_bus(m));
      31             : 
      32           0 :         return 0;
      33             : }
      34             : 
      35           0 : static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
      36             :         const char *path, *unit, *result;
      37           0 :         BusWaitForJobs *d = userdata;
      38             :         uint32_t id;
      39             :         char *found;
      40             :         int r;
      41             : 
      42           0 :         assert(m);
      43           0 :         assert(d);
      44             : 
      45           0 :         r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
      46           0 :         if (r < 0) {
      47           0 :                 bus_log_parse_error(r);
      48           0 :                 return 0;
      49             :         }
      50             : 
      51           0 :         found = set_remove(d->jobs, (char*) path);
      52           0 :         if (!found)
      53           0 :                 return 0;
      54             : 
      55           0 :         free(found);
      56             : 
      57           0 :         (void) free_and_strdup(&d->result, empty_to_null(result));
      58             : 
      59           0 :         (void) free_and_strdup(&d->name, empty_to_null(unit));
      60             : 
      61           0 :         return 0;
      62             : }
      63             : 
      64           0 : void bus_wait_for_jobs_free(BusWaitForJobs *d) {
      65           0 :         if (!d)
      66           0 :                 return;
      67             : 
      68           0 :         set_free_free(d->jobs);
      69             : 
      70           0 :         sd_bus_slot_unref(d->slot_disconnected);
      71           0 :         sd_bus_slot_unref(d->slot_job_removed);
      72             : 
      73           0 :         sd_bus_unref(d->bus);
      74             : 
      75           0 :         free(d->name);
      76           0 :         free(d->result);
      77             : 
      78           0 :         free(d);
      79             : }
      80             : 
      81           0 : int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
      82           0 :         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
      83             :         int r;
      84             : 
      85           0 :         assert(bus);
      86           0 :         assert(ret);
      87             : 
      88           0 :         d = new(BusWaitForJobs, 1);
      89           0 :         if (!d)
      90           0 :                 return -ENOMEM;
      91             : 
      92           0 :         *d = (BusWaitForJobs) {
      93           0 :                 .bus = sd_bus_ref(bus),
      94             :         };
      95             : 
      96             :         /* When we are a bus client we match by sender. Direct
      97             :          * connections OTOH have no initialized sender field, and
      98             :          * hence we ignore the sender then */
      99           0 :         r = sd_bus_match_signal_async(
     100             :                         bus,
     101           0 :                         &d->slot_job_removed,
     102           0 :                         bus->bus_client ? "org.freedesktop.systemd1" : NULL,
     103             :                         "/org/freedesktop/systemd1",
     104             :                         "org.freedesktop.systemd1.Manager",
     105             :                         "JobRemoved",
     106             :                         match_job_removed, NULL, d);
     107           0 :         if (r < 0)
     108           0 :                 return r;
     109             : 
     110           0 :         r = sd_bus_match_signal_async(
     111             :                         bus,
     112           0 :                         &d->slot_disconnected,
     113             :                         "org.freedesktop.DBus.Local",
     114             :                         NULL,
     115             :                         "org.freedesktop.DBus.Local",
     116             :                         "Disconnected",
     117             :                         match_disconnected, NULL, d);
     118           0 :         if (r < 0)
     119           0 :                 return r;
     120             : 
     121           0 :         *ret = TAKE_PTR(d);
     122             : 
     123           0 :         return 0;
     124             : }
     125             : 
     126           0 : static int bus_process_wait(sd_bus *bus) {
     127             :         int r;
     128             : 
     129             :         for (;;) {
     130           0 :                 r = sd_bus_process(bus, NULL);
     131           0 :                 if (r < 0)
     132           0 :                         return r;
     133           0 :                 if (r > 0)
     134           0 :                         return 0;
     135             : 
     136           0 :                 r = sd_bus_wait(bus, (uint64_t) -1);
     137           0 :                 if (r < 0)
     138           0 :                         return r;
     139             :         }
     140             : }
     141             : 
     142           0 : static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
     143           0 :         _cleanup_free_ char *dbus_path = NULL;
     144             : 
     145           0 :         assert(d);
     146           0 :         assert(d->name);
     147           0 :         assert(result);
     148             : 
     149           0 :         if (!endswith(d->name, ".service"))
     150           0 :                 return -EINVAL;
     151             : 
     152           0 :         dbus_path = unit_dbus_path_from_name(d->name);
     153           0 :         if (!dbus_path)
     154           0 :                 return -ENOMEM;
     155             : 
     156           0 :         return sd_bus_get_property_string(d->bus,
     157             :                                           "org.freedesktop.systemd1",
     158             :                                           dbus_path,
     159             :                                           "org.freedesktop.systemd1.Service",
     160             :                                           "Result",
     161             :                                           NULL,
     162             :                                           result);
     163             : }
     164             : 
     165           0 : static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
     166           0 :         _cleanup_free_ char *service_shell_quoted = NULL;
     167           0 :         const char *systemctl = "systemctl", *journalctl = "journalctl";
     168             : 
     169             :         static const struct {
     170             :                 const char *result, *explanation;
     171             :         } explanations[] = {
     172             :                 { "resources",   "of unavailable resources or another system error" },
     173             :                 { "protocol",    "the service did not take the steps required by its unit configuration" },
     174             :                 { "timeout",     "a timeout was exceeded" },
     175             :                 { "exit-code",   "the control process exited with error code" },
     176             :                 { "signal",      "a fatal signal was delivered to the control process" },
     177             :                 { "core-dump",   "a fatal signal was delivered causing the control process to dump core" },
     178             :                 { "watchdog",    "the service failed to send watchdog ping" },
     179             :                 { "start-limit", "start of the service was attempted too often" }
     180             :         };
     181             : 
     182           0 :         assert(service);
     183             : 
     184           0 :         service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH);
     185             : 
     186           0 :         if (!strv_isempty((char**) extra_args)) {
     187           0 :                 _cleanup_free_ char *t;
     188             : 
     189           0 :                 t = strv_join((char**) extra_args, " ");
     190           0 :                 systemctl = strjoina("systemctl ", t ? : "<args>");
     191           0 :                 journalctl = strjoina("journalctl ", t ? : "<args>");
     192             :         }
     193             : 
     194           0 :         if (!isempty(result)) {
     195             :                 size_t i;
     196             : 
     197           0 :                 for (i = 0; i < ELEMENTSOF(explanations); ++i)
     198           0 :                         if (streq(result, explanations[i].result))
     199           0 :                                 break;
     200             : 
     201           0 :                 if (i < ELEMENTSOF(explanations)) {
     202           0 :                         log_error("Job for %s failed because %s.\n"
     203             :                                   "See \"%s status %s\" and \"%s -xe\" for details.\n",
     204             :                                   service,
     205             :                                   explanations[i].explanation,
     206             :                                   systemctl,
     207             :                                   service_shell_quoted ?: "<service>",
     208             :                                   journalctl);
     209           0 :                         goto finish;
     210             :                 }
     211             :         }
     212             : 
     213           0 :         log_error("Job for %s failed.\n"
     214             :                   "See \"%s status %s\" and \"%s -xe\" for details.\n",
     215             :                   service,
     216             :                   systemctl,
     217             :                   service_shell_quoted ?: "<service>",
     218             :                   journalctl);
     219             : 
     220           0 : finish:
     221             :         /* For some results maybe additional explanation is required */
     222           0 :         if (streq_ptr(result, "start-limit"))
     223           0 :                 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
     224             :                          "followed by \"%1$s start %2$s\" again.",
     225             :                          systemctl,
     226             :                          service_shell_quoted ?: "<service>");
     227           0 : }
     228             : 
     229           0 : static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
     230           0 :         assert(d);
     231           0 :         assert(d->name);
     232           0 :         assert(d->result);
     233             : 
     234           0 :         if (!quiet) {
     235           0 :                 if (streq(d->result, "canceled"))
     236           0 :                         log_error("Job for %s canceled.", strna(d->name));
     237           0 :                 else if (streq(d->result, "timeout"))
     238           0 :                         log_error("Job for %s timed out.", strna(d->name));
     239           0 :                 else if (streq(d->result, "dependency"))
     240           0 :                         log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
     241           0 :                 else if (streq(d->result, "invalid"))
     242           0 :                         log_error("%s is not active, cannot reload.", strna(d->name));
     243           0 :                 else if (streq(d->result, "assert"))
     244           0 :                         log_error("Assertion failed on job for %s.", strna(d->name));
     245           0 :                 else if (streq(d->result, "unsupported"))
     246           0 :                         log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
     247           0 :                 else if (streq(d->result, "collected"))
     248           0 :                         log_error("Queued job for %s was garbage collected.", strna(d->name));
     249           0 :                 else if (streq(d->result, "once"))
     250           0 :                         log_error("Unit %s was started already once and can't be started again.", strna(d->name));
     251           0 :                 else if (!STR_IN_SET(d->result, "done", "skipped")) {
     252             : 
     253           0 :                         if (d->name && endswith(d->name, ".service")) {
     254           0 :                                 _cleanup_free_ char *result = NULL;
     255             :                                 int q;
     256             : 
     257           0 :                                 q = bus_job_get_service_result(d, &result);
     258           0 :                                 if (q < 0)
     259           0 :                                         log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
     260             : 
     261           0 :                                 log_job_error_with_service_result(d->name, result, extra_args);
     262             :                         } else
     263           0 :                                 log_error("Job failed. See \"journalctl -xe\" for details.");
     264             :                 }
     265             :         }
     266             : 
     267           0 :         if (STR_IN_SET(d->result, "canceled", "collected"))
     268           0 :                 return -ECANCELED;
     269           0 :         else if (streq(d->result, "timeout"))
     270           0 :                 return -ETIME;
     271           0 :         else if (streq(d->result, "dependency"))
     272           0 :                 return -EIO;
     273           0 :         else if (streq(d->result, "invalid"))
     274           0 :                 return -ENOEXEC;
     275           0 :         else if (streq(d->result, "assert"))
     276           0 :                 return -EPROTO;
     277           0 :         else if (streq(d->result, "unsupported"))
     278           0 :                 return -EOPNOTSUPP;
     279           0 :         else if (streq(d->result, "once"))
     280           0 :                 return -ESTALE;
     281           0 :         else if (STR_IN_SET(d->result, "done", "skipped"))
     282           0 :                 return 0;
     283             : 
     284           0 :         return log_debug_errno(SYNTHETIC_ERRNO(EIO),
     285             :                                "Unexpected job result, assuming server side newer than us: %s", d->result);
     286             : }
     287             : 
     288           0 : int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
     289           0 :         int r = 0;
     290             : 
     291           0 :         assert(d);
     292             : 
     293           0 :         while (!set_isempty(d->jobs)) {
     294             :                 int q;
     295             : 
     296           0 :                 q = bus_process_wait(d->bus);
     297           0 :                 if (q < 0)
     298           0 :                         return log_error_errno(q, "Failed to wait for response: %m");
     299             : 
     300           0 :                 if (d->name && d->result) {
     301           0 :                         q = check_wait_response(d, quiet, extra_args);
     302             :                         /* Return the first error as it is most likely to be
     303             :                          * meaningful. */
     304           0 :                         if (q < 0 && r == 0)
     305           0 :                                 r = q;
     306             : 
     307           0 :                         log_debug_errno(q, "Got result %s/%m for job %s", d->result, d->name);
     308             :                 }
     309             : 
     310           0 :                 d->name = mfree(d->name);
     311           0 :                 d->result = mfree(d->result);
     312             :         }
     313             : 
     314           0 :         return r;
     315             : }
     316             : 
     317           0 : int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
     318             :         int r;
     319             : 
     320           0 :         assert(d);
     321             : 
     322           0 :         r = set_ensure_allocated(&d->jobs, &string_hash_ops);
     323           0 :         if (r < 0)
     324           0 :                 return r;
     325             : 
     326           0 :         return set_put_strdup(d->jobs, path);
     327             : }
     328             : 
     329           0 : int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
     330             :         int r;
     331             : 
     332           0 :         r = bus_wait_for_jobs_add(d, path);
     333           0 :         if (r < 0)
     334           0 :                 return log_oom();
     335             : 
     336           0 :         return bus_wait_for_jobs(d, quiet, NULL);
     337             : }

Generated by: LCOV version 1.14