LCOV - code coverage report
Current view: top level - portable - portablectl.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 25 481 5.2 %
Date: 2019-08-22 15:41:25 Functions: 4 19 21.1 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <getopt.h>
       5             : 
       6             : #include "sd-bus.h"
       7             : 
       8             : #include "alloc-util.h"
       9             : #include "bus-error.h"
      10             : #include "bus-util.h"
      11             : #include "def.h"
      12             : #include "dirent-util.h"
      13             : #include "env-file.h"
      14             : #include "fd-util.h"
      15             : #include "fileio.h"
      16             : #include "format-table.h"
      17             : #include "fs-util.h"
      18             : #include "locale-util.h"
      19             : #include "machine-image.h"
      20             : #include "main-func.h"
      21             : #include "pager.h"
      22             : #include "parse-util.h"
      23             : #include "path-util.h"
      24             : #include "pretty-print.h"
      25             : #include "spawn-polkit-agent.h"
      26             : #include "string-util.h"
      27             : #include "strv.h"
      28             : #include "terminal-util.h"
      29             : #include "verbs.h"
      30             : 
      31             : static PagerFlags arg_pager_flags = 0;
      32             : static bool arg_legend = true;
      33             : static bool arg_ask_password = true;
      34             : static bool arg_quiet = false;
      35             : static const char *arg_profile = "default";
      36             : static const char* arg_copy_mode = NULL;
      37             : static bool arg_runtime = false;
      38             : static bool arg_reload = true;
      39             : static bool arg_cat = false;
      40             : static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
      41             : static const char *arg_host = NULL;
      42             : 
      43           0 : static int determine_image(const char *image, bool permit_non_existing, char **ret) {
      44             :         int r;
      45             : 
      46             :         /* If the specified name is a valid image name, we pass it as-is to portabled, which will search for it in the
      47             :          * usual search directories. Otherwise we presume it's a path, and will normalize it on the client's side
      48             :          * (among other things, to make the path independent of the client's working directory) before passing it
      49             :          * over. */
      50             : 
      51           0 :         if (image_name_is_valid(image)) {
      52             :                 char *c;
      53             : 
      54           0 :                 if (!arg_quiet && laccess(image, F_OK) >= 0)
      55           0 :                         log_warning("Ambiguous invocation: current working directory contains file matching non-path argument '%s', ignoring. "
      56             :                                     "Prefix argument with './' to force reference to file in current working directory.", image);
      57             : 
      58           0 :                 c = strdup(image);
      59           0 :                 if (!c)
      60           0 :                         return log_oom();
      61             : 
      62           0 :                 *ret = c;
      63           0 :                 return 0;
      64             :         }
      65             : 
      66           0 :         if (arg_transport != BUS_TRANSPORT_LOCAL)
      67           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
      68             :                                        "Operations on images by path not supported when connecting to remote systems.");
      69             : 
      70           0 :         r = chase_symlinks(image, NULL, CHASE_TRAIL_SLASH | (permit_non_existing ? CHASE_NONEXISTENT : 0), ret);
      71           0 :         if (r < 0)
      72           0 :                 return log_error_errno(r, "Cannot normalize specified image path '%s': %m", image);
      73             : 
      74           0 :         return 0;
      75             : }
      76             : 
      77           0 : static int extract_prefix(const char *path, char **ret) {
      78           0 :         _cleanup_free_ char *name = NULL;
      79             :         const char *bn, *underscore;
      80             :         size_t m;
      81             : 
      82           0 :         bn = basename(path);
      83             : 
      84           0 :         underscore = strchr(bn, '_');
      85           0 :         if (underscore)
      86           0 :                 m = underscore - bn;
      87             :         else {
      88             :                 const char *e;
      89             : 
      90           0 :                 e = endswith(bn, ".raw");
      91           0 :                 if (!e)
      92           0 :                         e = strchr(bn, 0);
      93             : 
      94           0 :                 m = e - bn;
      95             :         }
      96             : 
      97           0 :         name = strndup(bn, m);
      98           0 :         if (!name)
      99           0 :                 return -ENOMEM;
     100             : 
     101             :         /*  A slightly reduced version of what's permitted in unit names. With ':' and '\' are removed, as well as '_'
     102             :          *  which we use as delimiter for the second part of the image string, which we ignore for now. */
     103           0 :         if (!in_charset(name, DIGITS LETTERS "-."))
     104           0 :                 return -EINVAL;
     105             : 
     106           0 :         if (!filename_is_valid(name))
     107           0 :                 return -EINVAL;
     108             : 
     109           0 :         *ret = TAKE_PTR(name);
     110             : 
     111           0 :         return 0;
     112             : }
     113             : 
     114           0 : static int determine_matches(const char *image, char **l, bool allow_any, char ***ret) {
     115           0 :         _cleanup_strv_free_ char **k = NULL;
     116             :         int r;
     117             : 
     118             :         /* Determine the matches to apply. If the list is empty we derive the match from the image name. If the list
     119             :          * contains exactly the "-" we return a wildcard list (which is the empty list), but only if this is expressly
     120             :          * permitted. */
     121             : 
     122           0 :         if (strv_isempty(l)) {
     123             :                 char *prefix;
     124             : 
     125           0 :                 r = extract_prefix(image, &prefix);
     126           0 :                 if (r < 0)
     127           0 :                         return log_error_errno(r, "Failed to extract prefix of image name '%s': %m", image);
     128             : 
     129           0 :                 if (!arg_quiet)
     130           0 :                         log_info("(Matching unit files with prefix '%s'.)", prefix);
     131             : 
     132           0 :                 r = strv_consume(&k, prefix);
     133           0 :                 if (r < 0)
     134           0 :                         return log_oom();
     135             : 
     136           0 :         } else if (strv_equal(l, STRV_MAKE("-"))) {
     137             : 
     138           0 :                 if (!allow_any)
     139           0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     140             :                                                "Refusing all unit file match.");
     141             : 
     142           0 :                 if (!arg_quiet)
     143           0 :                         log_info("(Matching all unit files.)");
     144             :         } else {
     145             : 
     146           0 :                 k = strv_copy(l);
     147           0 :                 if (!k)
     148           0 :                         return log_oom();
     149             : 
     150           0 :                 if (!arg_quiet) {
     151           0 :                         _cleanup_free_ char *joined = NULL;
     152             : 
     153           0 :                         joined = strv_join(k, "', '");
     154           0 :                         if (!joined)
     155           0 :                                 return log_oom();
     156             : 
     157           0 :                         log_info("(Matching unit files with prefixes '%s'.)", joined);
     158             :                 }
     159             :         }
     160             : 
     161           0 :         *ret = TAKE_PTR(k);
     162             : 
     163           0 :         return 0;
     164             : }
     165             : 
     166           0 : static int acquire_bus(sd_bus **bus) {
     167             :         int r;
     168             : 
     169           0 :         assert(bus);
     170             : 
     171           0 :         if (*bus)
     172           0 :                 return 0;
     173             : 
     174           0 :         r = bus_connect_transport(arg_transport, arg_host, false, bus);
     175           0 :         if (r < 0)
     176           0 :                 return log_error_errno(r, "Failed to connect to bus: %m");
     177             : 
     178           0 :         (void) sd_bus_set_allow_interactive_authorization(*bus, arg_ask_password);
     179             : 
     180           0 :         return 0;
     181             : }
     182             : 
     183           0 : static int maybe_reload(sd_bus **bus) {
     184           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     185           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
     186             :         int r;
     187             : 
     188           0 :         if (!arg_reload)
     189           0 :                 return 0;
     190             : 
     191           0 :         r = acquire_bus(bus);
     192           0 :         if (r < 0)
     193           0 :                 return r;
     194             : 
     195           0 :         r = sd_bus_message_new_method_call(
     196             :                         *bus,
     197             :                         &m,
     198             :                         "org.freedesktop.systemd1",
     199             :                         "/org/freedesktop/systemd1",
     200             :                         "org.freedesktop.systemd1.Manager",
     201             :                         "Reload");
     202           0 :         if (r < 0)
     203           0 :                 return bus_log_create_error(r);
     204             : 
     205             :         /* Reloading the daemon may take long, hence set a longer timeout here */
     206           0 :         r = sd_bus_call(*bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL);
     207           0 :         if (r < 0)
     208           0 :                 return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r));
     209             : 
     210           0 :         return 0;
     211             : }
     212             : 
     213           0 : static int inspect_image(int argc, char *argv[], void *userdata) {
     214           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
     215           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     216           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     217           0 :         _cleanup_strv_free_ char **matches = NULL;
     218           0 :         _cleanup_free_ char *image = NULL;
     219           0 :         bool nl = false, header = false;
     220             :         const void *data;
     221             :         const char *path;
     222             :         size_t sz;
     223             :         int r;
     224             : 
     225           0 :         r = determine_image(argv[1], false, &image);
     226           0 :         if (r < 0)
     227           0 :                 return r;
     228             : 
     229           0 :         r = determine_matches(argv[1], argv + 2, true, &matches);
     230           0 :         if (r < 0)
     231           0 :                 return r;
     232             : 
     233           0 :         r = acquire_bus(&bus);
     234           0 :         if (r < 0)
     235           0 :                 return r;
     236             : 
     237           0 :         r = sd_bus_message_new_method_call(
     238             :                                 bus,
     239             :                                 &m,
     240             :                                 "org.freedesktop.portable1",
     241             :                                 "/org/freedesktop/portable1",
     242             :                                 "org.freedesktop.portable1.Manager",
     243             :                                 "GetImageMetadata");
     244           0 :         if (r < 0)
     245           0 :                 return bus_log_create_error(r);
     246             : 
     247           0 :         r = sd_bus_message_append(m, "s", image);
     248           0 :         if (r < 0)
     249           0 :                 return bus_log_create_error(r);
     250             : 
     251           0 :         r = sd_bus_message_append_strv(m, matches);
     252           0 :         if (r < 0)
     253           0 :                 return bus_log_create_error(r);
     254             : 
     255           0 :         r = sd_bus_call(bus, m, 0, &error, &reply);
     256           0 :         if (r < 0)
     257           0 :                 return log_error_errno(r, "Failed to inspect image metadata: %s", bus_error_message(&error, r));
     258             : 
     259           0 :         r = sd_bus_message_read(reply, "s", &path);
     260           0 :         if (r < 0)
     261           0 :                 return bus_log_parse_error(r);
     262             : 
     263           0 :         r = sd_bus_message_read_array(reply, 'y', &data, &sz);
     264           0 :         if (r < 0)
     265           0 :                 return bus_log_parse_error(r);
     266             : 
     267           0 :         (void) pager_open(arg_pager_flags);
     268             : 
     269           0 :         if (arg_cat) {
     270           0 :                 printf("%s-- OS Release: --%s\n", ansi_highlight(), ansi_normal());
     271           0 :                 fwrite(data, sz, 1, stdout);
     272           0 :                 fflush(stdout);
     273           0 :                 nl = true;
     274             :         } else {
     275           0 :                 _cleanup_free_ char *pretty_portable = NULL, *pretty_os = NULL;
     276           0 :                 _cleanup_fclose_ FILE *f;
     277             : 
     278           0 :                 f = fmemopen_unlocked((void*) data, sz, "re");
     279           0 :                 if (!f)
     280           0 :                         return log_error_errno(errno, "Failed to open /etc/os-release buffer: %m");
     281             : 
     282           0 :                 r = parse_env_file(f, "/etc/os-release",
     283             :                                    "PORTABLE_PRETTY_NAME", &pretty_portable,
     284             :                                    "PRETTY_NAME", &pretty_os);
     285           0 :                 if (r < 0)
     286           0 :                         return log_error_errno(r, "Failed to parse /etc/os-release: %m");
     287             : 
     288           0 :                 printf("Image:\n\t%s\n"
     289             :                        "Portable Service:\n\t%s\n"
     290             :                        "Operating System:\n\t%s\n",
     291             :                        path,
     292             :                        strna(pretty_portable),
     293             :                        strna(pretty_os));
     294             :         }
     295             : 
     296           0 :         r = sd_bus_message_enter_container(reply, 'a', "{say}");
     297           0 :         if (r < 0)
     298           0 :                 return bus_log_parse_error(r);
     299             : 
     300           0 :         for (;;) {
     301             :                 const char *name;
     302             : 
     303           0 :                 r = sd_bus_message_enter_container(reply, 'e', "say");
     304           0 :                 if (r < 0)
     305           0 :                         return bus_log_parse_error(r);
     306           0 :                 if (r == 0)
     307           0 :                         break;
     308             : 
     309           0 :                 r = sd_bus_message_read(reply, "s", &name);
     310           0 :                 if (r < 0)
     311           0 :                         return bus_log_parse_error(r);
     312             : 
     313           0 :                 r = sd_bus_message_read_array(reply, 'y', &data, &sz);
     314           0 :                 if (r < 0)
     315           0 :                         return bus_log_parse_error(r);
     316             : 
     317           0 :                 if (arg_cat) {
     318           0 :                         if (nl)
     319           0 :                                 fputc('\n', stdout);
     320             : 
     321           0 :                         printf("%s-- Unit file: %s --%s\n", ansi_highlight(), name, ansi_normal());
     322           0 :                         fwrite(data, sz, 1, stdout);
     323           0 :                         fflush(stdout);
     324           0 :                         nl = true;
     325             :                 } else {
     326           0 :                         if (!header) {
     327           0 :                                 fputs("Unit files:\n", stdout);
     328           0 :                                 header = true;
     329             :                         }
     330             : 
     331           0 :                         fputc('\t', stdout);
     332           0 :                         fputs(name, stdout);
     333           0 :                         fputc('\n', stdout);
     334             :                 }
     335             : 
     336           0 :                 r = sd_bus_message_exit_container(reply);
     337           0 :                 if (r < 0)
     338           0 :                         return bus_log_parse_error(r);
     339             :         }
     340             : 
     341           0 :         r = sd_bus_message_exit_container(reply);
     342           0 :         if (r < 0)
     343           0 :                 return bus_log_parse_error(r);
     344             : 
     345           0 :         return 0;
     346             : }
     347             : 
     348           0 : static int print_changes(sd_bus_message *m) {
     349             :         int r;
     350             : 
     351           0 :         if (arg_quiet)
     352           0 :                 return 0;
     353             : 
     354           0 :         r = sd_bus_message_enter_container(m, 'a', "(sss)");
     355           0 :         if (r < 0)
     356           0 :                 return bus_log_parse_error(r);
     357             : 
     358           0 :         for (;;) {
     359             :                 const char *type, *path, *source;
     360             : 
     361           0 :                 r = sd_bus_message_read(m, "(sss)", &type, &path, &source);
     362           0 :                 if (r < 0)
     363           0 :                         return bus_log_parse_error(r);
     364           0 :                 if (r == 0)
     365           0 :                         break;
     366             : 
     367           0 :                 if (streq(type, "symlink"))
     368           0 :                         log_info("Created symlink %s %s %s.", path, special_glyph(SPECIAL_GLYPH_ARROW), source);
     369           0 :                 else if (streq(type, "copy")) {
     370           0 :                         if (isempty(source))
     371           0 :                                 log_info("Copied %s.", path);
     372             :                         else
     373           0 :                                 log_info("Copied %s %s %s.", source, special_glyph(SPECIAL_GLYPH_ARROW), path);
     374           0 :                 } else if (streq(type, "unlink"))
     375           0 :                         log_info("Removed %s.", path);
     376           0 :                 else if (streq(type, "write"))
     377           0 :                         log_info("Written %s.", path);
     378           0 :                 else if (streq(type, "mkdir"))
     379           0 :                         log_info("Created directory %s.", path);
     380             :                 else
     381           0 :                         log_error("Unexpected change: %s/%s/%s", type, path, source);
     382             :         }
     383             : 
     384           0 :         r = sd_bus_message_exit_container(m);
     385           0 :         if (r < 0)
     386           0 :                 return r;
     387             : 
     388           0 :         return 0;
     389             : }
     390             : 
     391           0 : static int attach_image(int argc, char *argv[], void *userdata) {
     392           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
     393           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     394           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     395           0 :         _cleanup_strv_free_ char **matches = NULL;
     396           0 :         _cleanup_free_ char *image = NULL;
     397             :         int r;
     398             : 
     399           0 :         r = determine_image(argv[1], false, &image);
     400           0 :         if (r < 0)
     401           0 :                 return r;
     402             : 
     403           0 :         r = determine_matches(argv[1], argv + 2, false, &matches);
     404           0 :         if (r < 0)
     405           0 :                 return r;
     406             : 
     407           0 :         r = acquire_bus(&bus);
     408           0 :         if (r < 0)
     409           0 :                 return r;
     410             : 
     411           0 :         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
     412             : 
     413           0 :         r = sd_bus_message_new_method_call(
     414             :                                 bus,
     415             :                                 &m,
     416             :                                 "org.freedesktop.portable1",
     417             :                                 "/org/freedesktop/portable1",
     418             :                                 "org.freedesktop.portable1.Manager",
     419             :                                 "AttachImage");
     420           0 :         if (r < 0)
     421           0 :                 return bus_log_create_error(r);
     422             : 
     423           0 :         r = sd_bus_message_append(m, "s", image);
     424           0 :         if (r < 0)
     425           0 :                 return bus_log_create_error(r);
     426             : 
     427           0 :         r = sd_bus_message_append_strv(m, matches);
     428           0 :         if (r < 0)
     429           0 :                 return bus_log_create_error(r);
     430             : 
     431           0 :         r = sd_bus_message_append(m, "sbs", arg_profile, arg_runtime, arg_copy_mode);
     432           0 :         if (r < 0)
     433           0 :                 return bus_log_create_error(r);
     434             : 
     435           0 :         r = sd_bus_call(bus, m, 0, &error, &reply);
     436           0 :         if (r < 0)
     437           0 :                 return log_error_errno(r, "Failed to attach image: %s", bus_error_message(&error, r));
     438             : 
     439           0 :         (void) maybe_reload(&bus);
     440             : 
     441           0 :         print_changes(reply);
     442           0 :         return 0;
     443             : }
     444             : 
     445           0 : static int detach_image(int argc, char *argv[], void *userdata) {
     446           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     447           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     448           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     449           0 :         _cleanup_free_ char *image = NULL;
     450             :         int r;
     451             : 
     452           0 :         r = determine_image(argv[1], true, &image);
     453           0 :         if (r < 0)
     454           0 :                 return r;
     455             : 
     456           0 :         r = acquire_bus(&bus);
     457           0 :         if (r < 0)
     458           0 :                 return r;
     459             : 
     460           0 :         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
     461             : 
     462           0 :         r = sd_bus_call_method(
     463             :                         bus,
     464             :                         "org.freedesktop.portable1",
     465             :                         "/org/freedesktop/portable1",
     466             :                         "org.freedesktop.portable1.Manager",
     467             :                         "DetachImage",
     468             :                         &error,
     469             :                         &reply,
     470             :                         "sb", image, arg_runtime);
     471           0 :         if (r < 0)
     472           0 :                 return log_error_errno(r, "Failed to detach image: %s", bus_error_message(&error, r));
     473             : 
     474           0 :         (void) maybe_reload(&bus);
     475             : 
     476           0 :         print_changes(reply);
     477           0 :         return 0;
     478             : }
     479             : 
     480           0 : static int list_images(int argc, char *argv[], void *userdata) {
     481           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     482           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     483           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     484           0 :         _cleanup_(table_unrefp) Table *table = NULL;
     485             :         int r;
     486             : 
     487           0 :         r = acquire_bus(&bus);
     488           0 :         if (r < 0)
     489           0 :                 return r;
     490             : 
     491           0 :         r = sd_bus_call_method(
     492             :                         bus,
     493             :                         "org.freedesktop.portable1",
     494             :                         "/org/freedesktop/portable1",
     495             :                         "org.freedesktop.portable1.Manager",
     496             :                         "ListImages",
     497             :                         &error,
     498             :                         &reply,
     499             :                         NULL);
     500           0 :         if (r < 0)
     501           0 :                 return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r));
     502             : 
     503           0 :         table = table_new("name", "type", "ro", "crtime", "mtime", "usage", "state");
     504           0 :         if (!table)
     505           0 :                 return log_oom();
     506             : 
     507           0 :         r = sd_bus_message_enter_container(reply, 'a', "(ssbtttso)");
     508           0 :         if (r < 0)
     509           0 :                 return bus_log_parse_error(r);
     510             : 
     511           0 :         for (;;) {
     512             :                 const char *name, *type, *state;
     513             :                 uint64_t crtime, mtime, usage;
     514             :                 TableCell *cell;
     515             :                 bool ro_bool;
     516             :                 int ro_int;
     517             : 
     518           0 :                 r = sd_bus_message_read(reply, "(ssbtttso)", &name, &type, &ro_int, &crtime, &mtime, &usage, &state, NULL);
     519           0 :                 if (r < 0)
     520           0 :                         return bus_log_parse_error(r);
     521           0 :                 if (r == 0)
     522           0 :                         break;
     523             : 
     524           0 :                 r = table_add_many(table,
     525             :                                    TABLE_STRING, name,
     526             :                                    TABLE_STRING, type);
     527           0 :                 if (r < 0)
     528           0 :                         return log_error_errno(r, "Failed to add row to table: %m");
     529             : 
     530           0 :                 ro_bool = ro_int;
     531           0 :                 r = table_add_cell(table, &cell, TABLE_BOOLEAN, &ro_bool);
     532           0 :                 if (r < 0)
     533           0 :                         return log_error_errno(r, "Failed to add row to table: %m");
     534             : 
     535           0 :                 if (ro_bool) {
     536           0 :                         r = table_set_color(table, cell, ansi_highlight_red());
     537           0 :                         if (r < 0)
     538           0 :                                 return log_error_errno(r, "Failed to set table cell color: %m");
     539             :                 }
     540             : 
     541           0 :                 r = table_add_many(table,
     542             :                                    TABLE_TIMESTAMP, crtime,
     543             :                                    TABLE_TIMESTAMP, mtime,
     544             :                                    TABLE_SIZE, usage);
     545           0 :                 if (r < 0)
     546           0 :                         return log_error_errno(r, "Failed to add row to table: %m");
     547             : 
     548           0 :                 r = table_add_cell(table, &cell, TABLE_STRING, state);
     549           0 :                 if (r < 0)
     550           0 :                         return log_error_errno(r, "Failed to add row to table: %m");
     551             : 
     552           0 :                 if (!streq(state, "detached")) {
     553           0 :                         r = table_set_color(table, cell, ansi_highlight_green());
     554           0 :                         if (r < 0)
     555           0 :                                 return log_error_errno(r, "Failed to set table cell color: %m");
     556             :                 }
     557             :         }
     558             : 
     559           0 :         r = sd_bus_message_exit_container(reply);
     560           0 :         if (r < 0)
     561           0 :                 return bus_log_parse_error(r);
     562             : 
     563           0 :         if (table_get_rows(table) > 1) {
     564           0 :                 r = table_set_sort(table, (size_t) 0, (size_t) -1);
     565           0 :                 if (r < 0)
     566           0 :                         return log_error_errno(r, "Failed to sort table: %m");
     567             : 
     568           0 :                 table_set_header(table, arg_legend);
     569             : 
     570           0 :                 r = table_print(table, NULL);
     571           0 :                 if (r < 0)
     572           0 :                         return log_error_errno(r, "Failed to show table: %m");
     573             :         }
     574             : 
     575           0 :         if (arg_legend) {
     576           0 :                 if (table_get_rows(table) > 1)
     577           0 :                         printf("\n%zu images listed.\n", table_get_rows(table) - 1);
     578             :                 else
     579           0 :                         printf("No images.\n");
     580             :         }
     581             : 
     582           0 :         return 0;
     583             : }
     584             : 
     585           0 : static int remove_image(int argc, char *argv[], void *userdata) {
     586           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     587             :         int r, i;
     588             : 
     589           0 :         r = acquire_bus(&bus);
     590           0 :         if (r < 0)
     591           0 :                 return r;
     592             : 
     593           0 :         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
     594             : 
     595           0 :         for (i = 1; i < argc; i++) {
     596           0 :                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     597           0 :                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
     598             : 
     599           0 :                 r = sd_bus_message_new_method_call(
     600             :                                 bus,
     601             :                                 &m,
     602             :                                 "org.freedesktop.portable1",
     603             :                                 "/org/freedesktop/portable1",
     604             :                                 "org.freedesktop.portable1.Manager",
     605             :                                 "RemoveImage");
     606           0 :                 if (r < 0)
     607           0 :                         return bus_log_create_error(r);
     608             : 
     609           0 :                 r = sd_bus_message_append(m, "s", argv[i]);
     610           0 :                 if (r < 0)
     611           0 :                         return bus_log_create_error(r);
     612             : 
     613             :                 /* This is a slow operation, hence turn off any method call timeouts */
     614           0 :                 r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
     615           0 :                 if (r < 0)
     616           0 :                         return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
     617             :         }
     618             : 
     619           0 :         return 0;
     620             : }
     621             : 
     622           0 : static int read_only_image(int argc, char *argv[], void *userdata) {
     623           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     624           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     625           0 :         int b = true, r;
     626             : 
     627           0 :         if (argc > 2) {
     628           0 :                 b = parse_boolean(argv[2]);
     629           0 :                 if (b < 0)
     630           0 :                         return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
     631             :         }
     632             : 
     633           0 :         r = acquire_bus(&bus);
     634           0 :         if (r < 0)
     635           0 :                 return r;
     636             : 
     637           0 :         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
     638             : 
     639           0 :         r = sd_bus_call_method(
     640             :                         bus,
     641             :                         "org.freedesktop.portable1",
     642             :                         "/org/freedesktop/portable1",
     643             :                         "org.freedesktop.portable1.Manager",
     644             :                         "MarkImageReadOnly",
     645             :                         &error,
     646             :                         NULL,
     647           0 :                         "sb", argv[1], b);
     648           0 :         if (r < 0)
     649           0 :                 return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
     650             : 
     651           0 :         return 0;
     652             : }
     653             : 
     654           0 : static int set_limit(int argc, char *argv[], void *userdata) {
     655           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     656           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     657             :         uint64_t limit;
     658             :         int r;
     659             : 
     660           0 :         r = acquire_bus(&bus);
     661           0 :         if (r < 0)
     662           0 :                 return r;
     663             : 
     664           0 :         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
     665             : 
     666           0 :         if (STR_IN_SET(argv[argc-1], "-", "none", "infinity"))
     667           0 :                 limit = (uint64_t) -1;
     668             :         else {
     669           0 :                 r = parse_size(argv[argc-1], 1024, &limit);
     670           0 :                 if (r < 0)
     671           0 :                         return log_error_errno(r, "Failed to parse size: %s", argv[argc-1]);
     672             :         }
     673             : 
     674           0 :         if (argc > 2)
     675             :                 /* With two arguments changes the quota limit of the specified image */
     676           0 :                 r = sd_bus_call_method(
     677             :                                 bus,
     678             :                                 "org.freedesktop.portable1",
     679             :                                 "/org/freedesktop/portable1",
     680             :                                 "org.freedesktop.portable1.Manager",
     681             :                                 "SetImageLimit",
     682             :                                 &error,
     683             :                                 NULL,
     684           0 :                                 "st", argv[1], limit);
     685             :         else
     686             :                 /* With one argument changes the pool quota limit */
     687           0 :                 r = sd_bus_call_method(
     688             :                                 bus,
     689             :                                 "org.freedesktop.portable1",
     690             :                                 "/org/freedesktop/portable1",
     691             :                                 "org.freedesktop.portable1.Manager",
     692             :                                 "SetPoolLimit",
     693             :                                 &error,
     694             :                                 NULL,
     695             :                                 "t", limit);
     696             : 
     697           0 :         if (r < 0)
     698           0 :                 return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
     699             : 
     700           0 :         return 0;
     701             : }
     702             : 
     703           0 : static int is_image_attached(int argc, char *argv[], void *userdata) {
     704           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     705           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     706           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     707           0 :         _cleanup_free_ char *image = NULL;
     708             :         const char *state;
     709             :         int r;
     710             : 
     711           0 :         r = determine_image(argv[1], true, &image);
     712           0 :         if (r < 0)
     713           0 :                 return r;
     714             : 
     715           0 :         r = acquire_bus(&bus);
     716           0 :         if (r < 0)
     717           0 :                 return r;
     718             : 
     719           0 :         r = sd_bus_call_method(
     720             :                         bus,
     721             :                         "org.freedesktop.portable1",
     722             :                         "/org/freedesktop/portable1",
     723             :                         "org.freedesktop.portable1.Manager",
     724             :                         "GetImageState",
     725             :                         &error,
     726             :                         &reply,
     727             :                         "s", image);
     728           0 :         if (r < 0)
     729           0 :                 return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r));
     730             : 
     731           0 :         r = sd_bus_message_read(reply, "s", &state);
     732           0 :         if (r < 0)
     733           0 :                 return r;
     734             : 
     735           0 :         if (!arg_quiet)
     736           0 :                 puts(state);
     737             : 
     738           0 :         return streq(state, "detached");
     739             : }
     740             : 
     741           0 : static int dump_profiles(void) {
     742           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     743           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     744           0 :         _cleanup_strv_free_ char **l = NULL;
     745             :         char **i;
     746             :         int r;
     747             : 
     748           0 :         r = acquire_bus(&bus);
     749           0 :         if (r < 0)
     750           0 :                 return r;
     751             : 
     752           0 :         r = sd_bus_get_property_strv(
     753             :                         bus,
     754             :                         "org.freedesktop.portable1",
     755             :                         "/org/freedesktop/portable1",
     756             :                         "org.freedesktop.portable1.Manager",
     757             :                         "Profiles",
     758             :                         &error,
     759             :                         &l);
     760           0 :         if (r < 0)
     761           0 :                 return log_error_errno(r, "Failed to acquire list of profiles: %s", bus_error_message(&error, r));
     762             : 
     763           0 :         if (arg_legend)
     764           0 :                 log_info("Available unit profiles:");
     765             : 
     766           0 :         STRV_FOREACH(i, l) {
     767           0 :                 fputs(*i, stdout);
     768           0 :                 fputc('\n', stdout);
     769             :         }
     770             : 
     771           0 :         return 0;
     772             : }
     773             : 
     774           3 : static int help(int argc, char *argv[], void *userdata) {
     775           3 :         _cleanup_free_ char *link = NULL;
     776             :         int r;
     777             : 
     778           3 :         (void) pager_open(arg_pager_flags);
     779             : 
     780           3 :         r = terminal_urlify_man("portablectl", "1", &link);
     781           3 :         if (r < 0)
     782           0 :                 return log_oom();
     783             : 
     784           3 :         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
     785             :                "Attach or detach portable services from the local system.\n\n"
     786             :                "  -h --help                   Show this help\n"
     787             :                "     --version                Show package version\n"
     788             :                "     --no-pager               Do not pipe output into a pager\n"
     789             :                "     --no-legend              Do not show the headers and footers\n"
     790             :                "     --no-ask-password        Do not ask for system passwords\n"
     791             :                "  -H --host=[USER@]HOST       Operate on remote host\n"
     792             :                "  -M --machine=CONTAINER      Operate on local container\n"
     793             :                "  -q --quiet                  Suppress informational messages\n"
     794             :                "  -p --profile=PROFILE        Pick security profile for portable service\n"
     795             :                "     --copy=copy|auto|symlink Prefer copying or symlinks if possible\n"
     796             :                "     --runtime                Attach portable service until next reboot only\n"
     797             :                "     --no-reload              Don't reload the system and service manager\n"
     798             :                "     --cat                    When inspecting include unit and os-release file\n"
     799             :                "                              contents\n\n"
     800             :                "Commands:\n"
     801             :                "  list                        List available portable service images\n"
     802             :                "  attach NAME|PATH [PREFIX...]\n"
     803             :                "                              Attach the specified portable service image\n"
     804             :                "  detach NAME|PATH            Detach the specified portable service image\n"
     805             :                "  inspect NAME|PATH [PREFIX...]\n"
     806             :                "                              Show details of specified portable service image\n"
     807             :                "  is-attached NAME|PATH       Query if portable service image is attached\n"
     808             :                "  read-only NAME|PATH [BOOL]  Mark or unmark portable service image read-only\n"
     809             :                "  remove NAME|PATH...         Remove a portable service image\n"
     810             :                "  set-limit [NAME|PATH]       Set image or pool size limit (disk quota)\n"
     811             :                "\nSee the %s for details.\n"
     812             :                , program_invocation_short_name
     813             :                , link
     814             :         );
     815             : 
     816           3 :         return 0;
     817             : }
     818             : 
     819           4 : static int parse_argv(int argc, char *argv[]) {
     820             : 
     821             :         enum {
     822             :                 ARG_VERSION = 0x100,
     823             :                 ARG_NO_PAGER,
     824             :                 ARG_NO_LEGEND,
     825             :                 ARG_NO_ASK_PASSWORD,
     826             :                 ARG_COPY,
     827             :                 ARG_RUNTIME,
     828             :                 ARG_NO_RELOAD,
     829             :                 ARG_CAT,
     830             :         };
     831             : 
     832             :         static const struct option options[] = {
     833             :                 { "help",            no_argument,       NULL, 'h'                 },
     834             :                 { "version",         no_argument,       NULL, ARG_VERSION         },
     835             :                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
     836             :                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
     837             :                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
     838             :                 { "host",            required_argument, NULL, 'H'                 },
     839             :                 { "machine",         required_argument, NULL, 'M'                 },
     840             :                 { "quiet",           no_argument,       NULL, 'q'                 },
     841             :                 { "profile",         required_argument, NULL, 'p'                 },
     842             :                 { "copy",            required_argument, NULL, ARG_COPY            },
     843             :                 { "runtime",         no_argument,       NULL, ARG_RUNTIME         },
     844             :                 { "no-reload",       no_argument,       NULL, ARG_NO_RELOAD       },
     845             :                 { "cat",             no_argument,       NULL, ARG_CAT             },
     846             :                 {}
     847             :         };
     848             : 
     849           4 :         assert(argc >= 0);
     850           4 :         assert(argv);
     851             : 
     852           0 :         for (;;) {
     853             :                 int c;
     854             : 
     855           4 :                 c = getopt_long(argc, argv, "hH:M:qp:", options, NULL);
     856           4 :                 if (c < 0)
     857           0 :                         break;
     858             : 
     859           4 :                 switch (c) {
     860             : 
     861           3 :                 case 'h':
     862           3 :                         return help(0, NULL, NULL);
     863             : 
     864           0 :                 case ARG_VERSION:
     865           0 :                         return version();
     866             : 
     867           0 :                 case ARG_NO_PAGER:
     868           0 :                         arg_pager_flags |= PAGER_DISABLE;
     869           0 :                         break;
     870             : 
     871           0 :                 case ARG_NO_LEGEND:
     872           0 :                         arg_legend = false;
     873           0 :                         break;
     874             : 
     875           0 :                 case ARG_NO_ASK_PASSWORD:
     876           0 :                         arg_ask_password = false;
     877           0 :                         break;
     878             : 
     879           0 :                 case 'H':
     880           0 :                         arg_transport = BUS_TRANSPORT_REMOTE;
     881           0 :                         arg_host = optarg;
     882           0 :                         break;
     883             : 
     884           0 :                 case 'M':
     885           0 :                         arg_transport = BUS_TRANSPORT_MACHINE;
     886           0 :                         arg_host = optarg;
     887           0 :                         break;
     888             : 
     889           0 :                 case 'q':
     890           0 :                         arg_quiet = true;
     891           0 :                         break;
     892             : 
     893           0 :                 case 'p':
     894           0 :                         if (streq(optarg, "help"))
     895           0 :                                 return dump_profiles();
     896             : 
     897           0 :                         if (!filename_is_valid(optarg))
     898           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     899             :                                                        "Unit profile name not valid: %s", optarg);
     900             : 
     901           0 :                         arg_profile = optarg;
     902           0 :                         break;
     903             : 
     904           0 :                 case ARG_COPY:
     905           0 :                         if (streq(optarg, "auto"))
     906           0 :                                 arg_copy_mode = NULL;
     907           0 :                         else if (STR_IN_SET(optarg, "copy", "symlink"))
     908           0 :                                 arg_copy_mode = optarg;
     909           0 :                         else if (streq(optarg, "help")) {
     910           0 :                                 puts("auto\n"
     911             :                                      "copy\n"
     912             :                                      "symlink");
     913           0 :                                 return 0;
     914             :                         } else
     915           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     916             :                                                        "Failed to parse --copy= argument: %s", optarg);
     917             : 
     918           0 :                         break;
     919             : 
     920           0 :                 case ARG_RUNTIME:
     921           0 :                         arg_runtime = true;
     922           0 :                         break;
     923             : 
     924           0 :                 case ARG_NO_RELOAD:
     925           0 :                         arg_reload = false;
     926           0 :                         break;
     927             : 
     928           0 :                 case ARG_CAT:
     929           0 :                         arg_cat = true;
     930           0 :                         break;
     931             : 
     932           1 :                 case '?':
     933           1 :                         return -EINVAL;
     934             : 
     935           0 :                 default:
     936           0 :                         assert_not_reached("Unhandled option");
     937             :                 }
     938             :         }
     939             : 
     940           0 :         return 1;
     941             : }
     942             : 
     943           4 : static int run(int argc, char *argv[]) {
     944             :         static const Verb verbs[] = {
     945             :                 { "help",        VERB_ANY, VERB_ANY, 0,            help              },
     946             :                 { "list",        VERB_ANY, 1,        VERB_DEFAULT, list_images       },
     947             :                 { "attach",      2,        VERB_ANY, 0,            attach_image      },
     948             :                 { "detach",      2,        2,        0,            detach_image      },
     949             :                 { "inspect",     2,        VERB_ANY, 0,            inspect_image     },
     950             :                 { "is-attached", 2,        2,        0,            is_image_attached },
     951             :                 { "read-only",   2,        3,        0,            read_only_image   },
     952             :                 { "remove",      2,        VERB_ANY, 0,            remove_image      },
     953             :                 { "set-limit",   3,        3,        0,            set_limit         },
     954             :                 {}
     955             :         };
     956             : 
     957             :         int r;
     958             : 
     959           4 :         log_show_color(true);
     960           4 :         log_parse_environment();
     961           4 :         log_open();
     962             : 
     963           4 :         r = parse_argv(argc, argv);
     964           4 :         if (r <= 0)
     965           4 :                 return r;
     966             : 
     967           0 :         return dispatch_verb(argc, argv, verbs, NULL);
     968             : }
     969             : 
     970           4 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14