[Libguestfs] [PATCH 2/2] daemon: Replace GUESTFSD_EXT_CMD with --print-external-commands.

Richard W.M. Jones rjones at redhat.com
Mon Jul 24 16:10:14 UTC 2017


GUESTFSD_EXT_CMD is used by OpenSUSE to track which external commands
are run by the daemon and package those commands into the appliance.
However because this uses linker trickery it won't work from OCaml
code.

Replace it with a [nearly] standard C mechanism.  Files still have to
declare the external commands they will use, eg:

  DECLARE_EXTERNAL_COMMANDS ("btrfs", "btrfstune", "btrfsck")

and these are collected up by a constructor into an array and can be
printed at build time by doing:

  $ ./daemon/guestfsd --print-external-commands
  base64
  blkid
  blkls
  blockdev
  btrfs
  btrfsck
  &c.

This also adds a simple test.
---
 daemon/9p.c                            |   5 +-
 daemon/Makefile.am                     |   5 ++
 daemon/available.c                     |   7 +--
 daemon/base64.c                        |   6 +-
 daemon/blkid.c                         |  10 +--
 daemon/blockdev.c                      |   4 +-
 daemon/btrfs.c                         | 110 ++++++++++++++++-----------------
 daemon/checksum.c                      |  28 ++++-----
 daemon/cmp.c                           |   4 +-
 daemon/compress.c                      |  24 +++----
 daemon/cpio.c                          |   4 +-
 daemon/cpmv.c                          |  11 ++--
 daemon/daemon.h                        |  11 +++-
 daemon/dd.c                            |   4 +-
 daemon/debug.c                         |  34 ++++------
 daemon/df.c                            |   6 +-
 daemon/dir.c                           |   4 +-
 daemon/dmesg.c                         |   4 +-
 daemon/du.c                            |   4 +-
 daemon/ext2.c                          |  49 +++++++--------
 daemon/file.c                          |  10 ++-
 daemon/find.c                          |   4 +-
 daemon/findfs.c                        |   4 +-
 daemon/fsck.c                          |   4 +-
 daemon/fstrim.c                        |   6 +-
 daemon/grub.c                          |   6 +-
 daemon/guestfsd.c                      |  72 +++++++++++++++++++--
 daemon/guestfsd.pod                    |   7 +++
 daemon/hotplug.c                       |   4 +-
 daemon/initrd.c                        |   5 +-
 daemon/inotify.c                       |   5 +-
 daemon/isoinfo.c                       |   4 +-
 daemon/labels.c                        |   4 +-
 daemon/ldm.c                           |  22 +++----
 daemon/link.c                          |   4 +-
 daemon/ls.c                            |   6 +-
 daemon/luks.c                          |  14 ++---
 daemon/lvm-filter.c                    |  19 +++---
 daemon/lvm.c                           |  68 ++++++++++----------
 daemon/md.c                            |  10 +--
 daemon/mkfs.c                          |   7 +--
 daemon/modprobe.c                      |   6 +-
 daemon/mount.c                         |  15 +++--
 daemon/ntfs.c                          |  22 +++----
 daemon/ntfsclone.c                     |   7 +--
 daemon/parted.c                        |  66 ++++++++++----------
 daemon/rsync.c                         |   6 +-
 daemon/scrub.c                         |  10 +--
 daemon/selinux-relabel.c               |   8 +--
 daemon/sfdisk.c                        |   9 ++-
 daemon/sh.c                            |  20 +++---
 daemon/sleuthkit.c                     |  13 ++--
 daemon/squashfs.c                      |   6 +-
 daemon/swap.c                          |  29 ++++-----
 daemon/syslinux.c                      |  11 ++--
 daemon/tar.c                           |   6 +-
 daemon/test-print-external-commands.sh |  23 +++++++
 daemon/xfs.c                           |  17 +++--
 daemon/zero.c                          |  10 +--
 daemon/zerofree.c                      |   6 +-
 60 files changed, 479 insertions(+), 430 deletions(-)

diff --git a/daemon/9p.c b/daemon/9p.c
index fc5b01736..b29d41d85 100644
--- a/daemon/9p.c
+++ b/daemon/9p.c
@@ -33,7 +33,8 @@
 #include "actions.h"
 
 #define BUS_PATH "/sys/bus/virtio/drivers/9pnet_virtio"
-GUESTFSD_EXT_CMD(str_mount, mount);
+
+DECLARE_EXTERNAL_COMMANDS ("mount")
 
 static char *read_whole_file (const char *filename);
 
@@ -215,7 +216,7 @@ do_mount_9p (const char *mount_tag, const char *mountpoint, const char *options)
   }
 
   r = command (NULL, &err,
-               str_mount, "-o", opts, "-t", "9p", mount_tag, mp, NULL);
+               "mount", "-o", opts, "-t", "9p", mount_tag, mp, NULL);
   if (r == -1) {
     reply_with_error ("%s on %s: %s", mount_tag, mountpoint, err);
     return -1;
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index eedf09d52..8f7258343 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -220,6 +220,11 @@ guestfsd_CFLAGS = \
 	$(YAJL_CFLAGS) \
 	$(PCRE_CFLAGS)
 
+# Tests.
+
+TESTS_ENVIRONMENT = $(top_builddir)/run --test
+TESTS = test-print-external-commands.sh
+
 # Manual pages and HTML files for the website.
 if INSTALL_DAEMON
 man_MANS = guestfsd.8
diff --git a/daemon/available.c b/daemon/available.c
index 977c4dead..dfffe5da1 100644
--- a/daemon/available.c
+++ b/daemon/available.c
@@ -30,8 +30,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_grep, grep);
-GUESTFSD_EXT_CMD(str_modprobe, modprobe);
+DECLARE_EXTERNAL_COMMANDS ("grep", "modprobe")
 
 int
 do_internal_feature_available (const char *group)
@@ -79,7 +78,7 @@ test_proc_filesystems (const char *filesystem)
     return -1;
   }
 
-  r = commandr (NULL, &err, str_grep, regex, "/proc/filesystems", NULL);
+  r = commandr (NULL, &err, "grep", regex, "/proc/filesystems", NULL);
   if (r == -1 || r >= 2) {
     fprintf (stderr, "grep /proc/filesystems: %s", err);
     return -1;
@@ -92,7 +91,7 @@ test_proc_filesystems (const char *filesystem)
 static void
 modprobe (const char *module)
 {
-  ignore_value (command (NULL, NULL, str_modprobe, module, NULL));
+  ignore_value (command (NULL, NULL, "modprobe", module, NULL));
 }
 
 /* Internal function for testing if a filesystem is available.  Note
diff --git a/daemon/base64.c b/daemon/base64.c
index 3468c3342..79b31a805 100644
--- a/daemon/base64.c
+++ b/daemon/base64.c
@@ -30,7 +30,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_base64, base64);
+DECLARE_EXTERNAL_COMMANDS ("base64")
 
 static int
 write_cb (void *fd_ptr, const void *buf, size_t len)
@@ -48,7 +48,7 @@ do_base64_in (const char *file)
   CLEANUP_FREE char *cmd = NULL;
   int fd;
 
-  if (asprintf_nowarn (&cmd, "%s -d -i > %R", str_base64, file) == -1) {
+  if (asprintf_nowarn (&cmd, "%s -d -i > %R", "base64", file) == -1) {
     err = errno;
     cancel_receive ();
     errno = err;
@@ -132,7 +132,7 @@ do_base64_out (const char *file)
   }
 
   /* Construct the command. */
-  if (asprintf_nowarn (&cmd, "%s %Q", str_base64, buf) == -1) {
+  if (asprintf_nowarn (&cmd, "%s %Q", "base64", buf) == -1) {
     reply_with_perror ("asprintf");
     return -1;
   }
diff --git a/daemon/blkid.c b/daemon/blkid.c
index 1fe5ff93a..8de025495 100644
--- a/daemon/blkid.c
+++ b/daemon/blkid.c
@@ -28,7 +28,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_blkid, blkid);
+DECLARE_EXTERNAL_COMMANDS ("blkid")
 
 char *
 get_blkid_tag (const char *device, const char *tag)
@@ -39,7 +39,7 @@ get_blkid_tag (const char *device, const char *tag)
   size_t len;
 
   r = commandr (&out, &err,
-                str_blkid,
+                "blkid",
                 /* Adding -c option kills all caching, even on RHEL 5. */
                 "-c", "/dev/null",
                 "-o", "value", "-s", tag, device, NULL);
@@ -107,7 +107,7 @@ test_blkid_p_i_opt (void)
   int r;
   CLEANUP_FREE char *err = NULL, *err2 = NULL;
 
-  r = commandr (NULL, &err, str_blkid, "-p", "/dev/null", NULL);
+  r = commandr (NULL, &err, "blkid", "-p", "/dev/null", NULL);
   if (r == -1) {
     /* This means we couldn't run the blkid command at all. */
   command_failed:
@@ -119,7 +119,7 @@ test_blkid_p_i_opt (void)
     return 0;
   }
 
-  r = commandr (NULL, &err2, str_blkid, "-i", NULL);
+  r = commandr (NULL, &err2, "blkid", "-i", NULL);
   if (r == -1)
     goto command_failed;
 
@@ -140,7 +140,7 @@ blkid_with_p_i_opt (const char *device)
   CLEANUP_FREE_STRING_LIST char **lines = NULL;
   CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
 
-  r = command (&out, &err, str_blkid, "-c", "/dev/null",
+  r = command (&out, &err, "blkid", "-c", "/dev/null",
                "-p", "-i", "-o", "export", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
diff --git a/daemon/blockdev.c b/daemon/blockdev.c
index 6e8821d92..6fdb54cc1 100644
--- a/daemon/blockdev.c
+++ b/daemon/blockdev.c
@@ -28,7 +28,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_blockdev, blockdev);
+DECLARE_EXTERNAL_COMMANDS ("blockdev")
 
 /* These functions are all about using the blockdev command, so
  * we centralize it in one call.
@@ -40,7 +40,7 @@ call_blockdev (const char *device, const char *switc, int extraarg, int prints)
   int64_t rv;
   CLEANUP_FREE char *out = NULL, *err = NULL;
   const char *argv[] = {
-    str_blockdev,
+    "blockdev",
     switc,
     NULL,
     NULL,
diff --git a/daemon/btrfs.c b/daemon/btrfs.c
index 5f1e5d1d0..c7f08fb46 100644
--- a/daemon/btrfs.c
+++ b/daemon/btrfs.c
@@ -33,12 +33,8 @@
 #include "c-ctype.h"
 #include "ignore-value.h"
 
-GUESTFSD_EXT_CMD(str_btrfs, btrfs);
-GUESTFSD_EXT_CMD(str_btrfstune, btrfstune);
-GUESTFSD_EXT_CMD(str_btrfsck, btrfsck);
-GUESTFSD_EXT_CMD(str_mkfs_btrfs, mkfs.btrfs);
-GUESTFSD_EXT_CMD(str_umount, umount);
-GUESTFSD_EXT_CMD(str_btrfsimage, btrfs-image);
+DECLARE_EXTERNAL_COMMANDS ("btrfs", "btrfstune", "btrfsck", "mkfs.btrfs",
+                           "umount", "btrfs-image")
 
 COMPILE_REGEXP (re_btrfs_subvolume_list,
                 "ID\\s+(\\d+).*\\s"
@@ -51,7 +47,7 @@ int
 optgroup_btrfs_available (void)
 {
   return test_mode ||
-    (prog_exists (str_btrfs) && filesystem_available ("btrfs") > 0);
+    (prog_exists ("btrfs") && filesystem_available ("btrfs") > 0);
 }
 
 char *
@@ -62,7 +58,7 @@ btrfs_get_label (const char *device)
   char *out = NULL;
   size_t len;
 
-  r = command (&out, &err, str_btrfs, "filesystem", "label",
+  r = command (&out, &err, "btrfs", "filesystem", "label",
                device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -84,7 +80,7 @@ btrfs_set_label (const char *device, const char *label)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (NULL, &err, str_btrfs, "filesystem", "label",
+  r = command (NULL, &err, "btrfs", "filesystem", "label",
                device, label, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -110,7 +106,7 @@ do_btrfs_filesystem_resize (const char *filesystem, int64_t size)
   size_t i = 0;
   char size_str[32];
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "filesystem");
   ADD_ARG (argv, i, "resize");
 
@@ -169,7 +165,7 @@ do_mkfs_btrfs (char *const *devices,
     return -1;
   }
 
-  ADD_ARG (argv, i, str_mkfs_btrfs);
+  ADD_ARG (argv, i, "mkfs.btrfs");
 
   /* Optional arguments. */
   if (optargs_bitmask & GUESTFS_MKFS_BTRFS_ALLOCSTART_BITMASK) {
@@ -290,7 +286,7 @@ do_btrfs_subvolume_snapshot (const char *source, const char *dest, int ro,
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "subvolume");
   ADD_ARG (argv, i, "snapshot");
 
@@ -333,7 +329,7 @@ do_btrfs_subvolume_delete (const char *subvolume)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "subvolume");
   ADD_ARG (argv, i, "delete");
   ADD_ARG (argv, i, subvolume_buf);
@@ -364,7 +360,7 @@ do_btrfs_subvolume_create (const char *dest, const char *qgroupid)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "subvolume");
   ADD_ARG (argv, i, "create");
 
@@ -426,7 +422,7 @@ umount (char *fs_buf, const mountable_t *fs)
   if (fs->type != MOUNTABLE_PATH) {
     CLEANUP_FREE char *err = NULL;
 
-    if (command (NULL, &err, str_umount, fs_buf, NULL) == -1) {
+    if (command (NULL, &err, "umount", fs_buf, NULL) == -1) {
       reply_with_error ("umount: %s", err);
       return -1;
     }
@@ -455,7 +451,7 @@ do_btrfs_subvolume_list (const mountable_t *fs)
     if (!fs_buf)
       return NULL;
 
-    ADD_ARG (argv, i, str_btrfs);
+    ADD_ARG (argv, i, "btrfs");
     ADD_ARG (argv, i, "subvolume");
     ADD_ARG (argv, i, "list");
     ADD_ARG (argv, i, fs_buf);
@@ -590,7 +586,7 @@ do_btrfs_subvolume_set_default (int64_t id, const char *fs)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "subvolume");
   ADD_ARG (argv, i, "set-default");
   ADD_ARG (argv, i, buf);
@@ -622,7 +618,7 @@ do_btrfs_subvolume_get_default (const mountable_t *fs)
   if (fs_buf == NULL)
     goto error;
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "subvolume");
   ADD_ARG (argv, i, "get-default");
   ADD_ARG (argv, i, fs_buf);
@@ -661,7 +657,7 @@ do_btrfs_filesystem_sync (const char *fs)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "filesystem");
   ADD_ARG (argv, i, "sync");
   ADD_ARG (argv, i, fs_buf);
@@ -692,7 +688,7 @@ do_btrfs_filesystem_balance (const char *fs)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "balance");
   ADD_ARG (argv, i, fs_buf);
   ADD_ARG (argv, i, NULL);
@@ -715,7 +711,7 @@ test_btrfs_device_add_needs_force (void)
   int r;
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
-  r = command (&out, &err, str_btrfs, "device", "add", "--help", NULL);
+  r = command (&out, &err, "btrfs", "device", "add", "--help", NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", "btrfs device add --help", err);
     return -1;
@@ -756,7 +752,7 @@ do_btrfs_device_add (char *const *devices, const char *fs)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "device");
   ADD_ARG (argv, i, "add");
 
@@ -799,7 +795,7 @@ do_btrfs_device_delete (char *const *devices, const char *fs)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "device");
   ADD_ARG (argv, i, "delete");
 
@@ -840,7 +836,7 @@ test_btrfstune_uuid_opt (void)
 
   CLEANUP_FREE char *err = NULL;
 
-  int r = commandr (NULL, &err, str_btrfstune, "--help", NULL);
+  int r = commandr (NULL, &err, "btrfstune", "--help", NULL);
 
   if (r == -1) {
     reply_with_error ("btrfstune: %s", err);
@@ -868,7 +864,7 @@ do_btrfs_set_seeding (const char *device, int svalue)
 
   const char *s_value = svalue ? "1" : "0";
 
-  r = commandr (NULL, &err, str_btrfstune, "-S", s_value, device, NULL);
+  r = commandr (NULL, &err, "btrfstune", "-S", s_value, device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
@@ -887,7 +883,7 @@ btrfs_set_uuid (const char *device, const char *uuid)
   if (has_uuid_opts <= 0)
     NOT_SUPPORTED (-1, "btrfs filesystems' UUID cannot be changed");
 
-  r = commandr (NULL, &err, str_btrfstune, "-f", "-U", uuid, device, NULL);
+  r = commandr (NULL, &err, "btrfstune", "-f", "-U", uuid, device, NULL);
 
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
@@ -907,7 +903,7 @@ btrfs_set_uuid_random (const char *device)
   if (has_uuid_opts <= 0)
     NOT_SUPPORTED (-1, "btrfs filesystems' UUID cannot be changed");
 
-  r = commandr (NULL, &err, str_btrfstune, "-f", "-u", device, NULL);
+  r = commandr (NULL, &err, "btrfstune", "-f", "-u", device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
@@ -927,7 +923,7 @@ do_btrfs_fsck (const char *device, int64_t superblock, int repair)
   const char *argv[MAX_ARGS];
   char super_s[64];
 
-  ADD_ARG (argv, i, str_btrfsck);
+  ADD_ARG (argv, i, "btrfsck");
 
   /* Optional arguments. */
   if (optargs_bitmask & GUESTFS_BTRFS_FSCK_SUPERBLOCK_BITMASK) {
@@ -1025,7 +1021,7 @@ do_btrfs_subvolume_show (const char *subvolume)
     return NULL;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "subvolume");
   ADD_ARG (argv, i, "show");
   ADD_ARG (argv, i, subvolume_buf);
@@ -1164,7 +1160,7 @@ do_btrfs_quota_enable (const mountable_t *fs, int enable)
   if (fs_buf == NULL)
     goto error;
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "quota");
   if (enable)
     ADD_ARG (argv, i, "enable");
@@ -1199,7 +1195,7 @@ do_btrfs_quota_rescan (const mountable_t *fs)
   if (fs_buf == NULL)
     goto error;
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "quota");
   ADD_ARG (argv, i, "rescan");
   ADD_ARG (argv, i, fs_buf);
@@ -1234,7 +1230,7 @@ do_btrfs_qgroup_limit (const char *subvolume, int64_t size)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "qgroup");
   ADD_ARG (argv, i, "limit");
   snprintf (size_str, sizeof size_str, "%" PRIi64, size);
@@ -1267,7 +1263,7 @@ do_btrfs_qgroup_create (const char *qgroupid, const char *subvolume)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "qgroup");
   ADD_ARG (argv, i, "create");
   ADD_ARG (argv, i, qgroupid);
@@ -1299,7 +1295,7 @@ do_btrfs_qgroup_destroy (const char *qgroupid, const char *subvolume)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "qgroup");
   ADD_ARG (argv, i, "destroy");
   ADD_ARG (argv, i, qgroupid);
@@ -1332,7 +1328,7 @@ test_btrfs_qgroup_show_raw_opt (void)
   CLEANUP_FREE char *err = NULL;
   CLEANUP_FREE char *out = NULL;
 
-  int r = commandr (&out, &err, str_btrfs, "qgroup", "show", "--help", NULL);
+  int r = commandr (&out, &err, "btrfs", "qgroup", "show", "--help", NULL);
 
   if (r == -1) {
     reply_with_error ("btrfs qgroup show --help: %s", err);
@@ -1366,7 +1362,7 @@ do_btrfs_qgroup_show (const char *path)
     return NULL;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "qgroup");
   ADD_ARG (argv, i, "show");
   if (has_raw_opt > 0)
@@ -1449,7 +1445,7 @@ do_btrfs_qgroup_assign (const char *src, const char *dst, const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "qgroup");
   ADD_ARG (argv, i, "assign");
   ADD_ARG (argv, i, src);
@@ -1482,7 +1478,7 @@ do_btrfs_qgroup_remove (const char *src, const char *dst, const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "qgroup");
   ADD_ARG (argv, i, "remove");
   ADD_ARG (argv, i, src);
@@ -1515,7 +1511,7 @@ do_btrfs_scrub_start (const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "scrub");
   ADD_ARG (argv, i, "start");
   ADD_ARG (argv, i, path_buf);
@@ -1546,7 +1542,7 @@ do_btrfs_scrub_cancel (const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "scrub");
   ADD_ARG (argv, i, "cancel");
   ADD_ARG (argv, i, path_buf);
@@ -1577,7 +1573,7 @@ do_btrfs_scrub_resume (const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "scrub");
   ADD_ARG (argv, i, "resume");
   ADD_ARG (argv, i, path_buf);
@@ -1608,7 +1604,7 @@ do_btrfs_balance_pause (const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "balance");
   ADD_ARG (argv, i, "pause");
   ADD_ARG (argv, i, path_buf);
@@ -1639,7 +1635,7 @@ do_btrfs_balance_cancel (const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "balance");
   ADD_ARG (argv, i, "cancel");
   ADD_ARG (argv, i, path_buf);
@@ -1670,7 +1666,7 @@ do_btrfs_balance_resume (const char *path)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "balance");
   ADD_ARG (argv, i, "resume");
   ADD_ARG (argv, i, path_buf);
@@ -1702,7 +1698,7 @@ do_btrfs_filesystem_defragment (const char *path, int flush, const char *compres
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "filesystem");
   ADD_ARG (argv, i, "defragment");
   ADD_ARG (argv, i, "-r");
@@ -1742,7 +1738,7 @@ do_btrfs_rescue_chunk_recover (const char *device)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "rescue");
   ADD_ARG (argv, i, "chunk-recover");
   ADD_ARG (argv, i, "-y");
@@ -1767,7 +1763,7 @@ do_btrfs_rescue_super_recover (const char *device)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "rescue");
   ADD_ARG (argv, i, "super-recover");
   ADD_ARG (argv, i, "-y");
@@ -1805,7 +1801,7 @@ do_btrfs_balance_status (const char *path)
     return NULL;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "balance");
   ADD_ARG (argv, i, "status");
   ADD_ARG (argv, i, path_buf);
@@ -1918,7 +1914,7 @@ do_btrfs_scrub_status (const char *path)
     return NULL;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "scrub");
   ADD_ARG (argv, i, "status");
   ADD_ARG (argv, i, "-R");
@@ -2052,7 +2048,7 @@ do_btrfstune_seeding (const char *device, int svalue)
   int r;
   const char *s_value = svalue ? "1" : "0";
 
-  ADD_ARG (argv, i, str_btrfstune);
+  ADD_ARG (argv, i, "btrfstune");
   ADD_ARG (argv, i, "-S");
   ADD_ARG (argv, i, s_value);
   if (svalue == 0)
@@ -2078,7 +2074,7 @@ do_btrfstune_enable_extended_inode_refs (const char *device)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  ADD_ARG (argv, i, str_btrfstune);
+  ADD_ARG (argv, i, "btrfstune");
   ADD_ARG (argv, i, "-r");
   ADD_ARG (argv, i, device);
   ADD_ARG (argv, i, NULL);
@@ -2101,7 +2097,7 @@ do_btrfstune_enable_skinny_metadata_extent_refs (const char *device)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  ADD_ARG (argv, i, str_btrfstune);
+  ADD_ARG (argv, i, "btrfstune");
   ADD_ARG (argv, i, "-x");
   ADD_ARG (argv, i, device);
   ADD_ARG (argv, i, NULL);
@@ -2137,7 +2133,7 @@ do_btrfs_image (char *const *sources, const char *image,
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfsimage);
+  ADD_ARG (argv, i, "btrfsimage");
 
   if ((optargs_bitmask & GUESTFS_BTRFS_IMAGE_COMPRESSLEVEL_BITMASK)
       && compresslevel >= 0) {
@@ -2182,7 +2178,7 @@ do_btrfs_replace (const char *srcdev, const char *targetdev,
     return -1;
   }
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "replace");
   ADD_ARG (argv, i, "start");
   ADD_ARG (argv, i, "-B");
@@ -2213,7 +2209,7 @@ do_btrfs_filesystem_show (const char *device)
   CLEANUP_FREE_STRING_LIST char **lines = NULL;
   int r;
 
-  ADD_ARG (argv, i, str_btrfs);
+  ADD_ARG (argv, i, "btrfs");
   ADD_ARG (argv, i, "filesystem");
   ADD_ARG (argv, i, "show");
   ADD_ARG (argv, i, device);
@@ -2305,7 +2301,7 @@ test_btrfs_min_dev_size (void)
   if (result != -1)
     return result;
 
-  r = commandr (&out, &err, str_btrfs, "--help", NULL);
+  r = commandr (&out, &err, "btrfs", "--help", NULL);
 
   if (r == -1) {
     reply_with_error ("btrfs: %s", err);
@@ -2340,7 +2336,7 @@ btrfs_minimum_size (const char *path)
     return -1;
   }
 
-  r = command (&out, &err, str_btrfs, "inspect-internal",
+  r = command (&out, &err, "btrfs", "inspect-internal",
                "min-dev-size", buf, NULL);
 
   if (r == -1) {
diff --git a/daemon/checksum.c b/daemon/checksum.c
index da47a0931..244ff6319 100644
--- a/daemon/checksum.c
+++ b/daemon/checksum.c
@@ -29,33 +29,27 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_find, find);
-GUESTFSD_EXT_CMD(str_xargs, xargs);
-GUESTFSD_EXT_CMD(str_cksum, cksum);
-GUESTFSD_EXT_CMD(str_md5sum, md5sum);
-GUESTFSD_EXT_CMD(str_sha1sum, sha1sum);
-GUESTFSD_EXT_CMD(str_sha224sum, sha224sum);
-GUESTFSD_EXT_CMD(str_sha256sum, sha256sum);
-GUESTFSD_EXT_CMD(str_sha384sum, sha384sum);
-GUESTFSD_EXT_CMD(str_sha512sum, sha512sum);
+DECLARE_EXTERNAL_COMMANDS ("find", "xargs", "cksum", "md5sum",
+                           "sha1sum", "sha224sum", "sha256sum", "sha384sum",
+                           "sha512sum")
 
 static const char *
 program_of_csum (const char *csumtype)
 {
   if (STRCASEEQ (csumtype, "crc"))
-    return str_cksum;
+    return "cksum";
   else if (STRCASEEQ (csumtype, "md5"))
-    return str_md5sum;
+    return "md5sum";
   else if (STRCASEEQ (csumtype, "sha1"))
-    return str_sha1sum;
+    return "sha1sum";
   else if (STRCASEEQ (csumtype, "sha224"))
-    return str_sha224sum;
+    return "sha224sum";
   else if (STRCASEEQ (csumtype, "sha256"))
-    return str_sha256sum;
+    return "sha256sum";
   else if (STRCASEEQ (csumtype, "sha384"))
-    return str_sha384sum;
+    return "sha384sum";
   else if (STRCASEEQ (csumtype, "sha512"))
-    return str_sha512sum;
+    return "sha512sum";
   else {
     reply_with_error ("unknown checksum type, expecting crc|md5|sha1|sha224|sha256|sha384|sha512");
     return NULL;
@@ -166,7 +160,7 @@ do_checksums_out (const char *csumtype, const char *dir)
 
   cmd = NULL;
   if (asprintf_nowarn (&cmd, "cd %Q && %s -type f -print0 | %s -0 %s",
-                       sysrootdir, str_find, str_xargs, program) == -1) {
+                       sysrootdir, "find", "xargs", program) == -1) {
     reply_with_perror ("asprintf");
     return -1;
   }
diff --git a/daemon/cmp.c b/daemon/cmp.c
index 61506b6c1..01ca13d47 100644
--- a/daemon/cmp.c
+++ b/daemon/cmp.c
@@ -27,7 +27,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_cmp, cmp);
+DECLARE_EXTERNAL_COMMANDS ("cmp")
 
 int
 do_equal (const char *file1, const char *file2)
@@ -48,7 +48,7 @@ do_equal (const char *file1, const char *file2)
     return -1;
   }
 
-  r = commandr (NULL, &err, str_cmp, "-s", file1buf, file2buf, NULL);
+  r = commandr (NULL, &err, "cmp", "-s", file1buf, file2buf, NULL);
   if (r == -1 || r > 1) {
     reply_with_error ("%s", err);
     return -1;
diff --git a/daemon/compress.c b/daemon/compress.c
index 36f4e66f7..ca8678965 100644
--- a/daemon/compress.c
+++ b/daemon/compress.c
@@ -27,11 +27,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_compress, compress);
-GUESTFSD_EXT_CMD(str_gzip, gzip);
-GUESTFSD_EXT_CMD(str_bzip2, bzip2);
-GUESTFSD_EXT_CMD(str_xz, xz);
-GUESTFSD_EXT_CMD(str_lzop, lzop);
+DECLARE_EXTERNAL_COMMANDS ("compress", "gzip", "bzip2", "xz", "lzop")
 
 /* Has one FileOut parameter. */
 static int
@@ -126,15 +122,15 @@ get_filter (const char *ctype, int level, char *ret, size_t n)
       reply_with_error ("compress: cannot use optional level parameter with this compression type");
       return -1;
     }
-    snprintf (ret, n, "%s -c", str_compress);
+    snprintf (ret, n, "%s -c", "compress");
     return 0;
   }
   else if (STREQ (ctype, "gzip")) {
     CHECK_SUPPORTED ("gzip");
     if (level == -1)
-      snprintf (ret, n, "%s -c", str_gzip);
+      snprintf (ret, n, "%s -c", "gzip");
     else if (level >= 1 && level <= 9)
-      snprintf (ret, n, "%s -c -%d", str_gzip, level);
+      snprintf (ret, n, "%s -c -%d", "gzip", level);
     else {
       reply_with_error ("gzip: incorrect value for level parameter");
       return -1;
@@ -144,9 +140,9 @@ get_filter (const char *ctype, int level, char *ret, size_t n)
   else if (STREQ (ctype, "bzip2")) {
     CHECK_SUPPORTED ("bzip2");
     if (level == -1)
-      snprintf (ret, n, "%s -c", str_bzip2);
+      snprintf (ret, n, "%s -c", "bzip2");
     else if (level >= 1 && level <= 9)
-      snprintf (ret, n, "%s -c -%d", str_bzip2, level);
+      snprintf (ret, n, "%s -c -%d", "bzip2", level);
     else {
       reply_with_error ("bzip2: incorrect value for level parameter");
       return -1;
@@ -156,9 +152,9 @@ get_filter (const char *ctype, int level, char *ret, size_t n)
   else if (STREQ (ctype, "xz")) {
     CHECK_SUPPORTED ("xz");
     if (level == -1)
-      snprintf (ret, n, "%s -c", str_xz);
+      snprintf (ret, n, "%s -c", "xz");
     else if (level >= 0 && level <= 9)
-      snprintf (ret, n, "%s -c -%d", str_xz, level);
+      snprintf (ret, n, "%s -c -%d", "xz", level);
     else {
       reply_with_error ("xz: incorrect value for level parameter");
       return -1;
@@ -168,9 +164,9 @@ get_filter (const char *ctype, int level, char *ret, size_t n)
   else if (STREQ (ctype, "lzop")) {
     CHECK_SUPPORTED ("lzop");
     if (level == -1)
-      snprintf (ret, n, "%s -c", str_lzop);
+      snprintf (ret, n, "%s -c", "lzop");
     else if (level >= 1 && level <= 9)
-      snprintf (ret, n, "%s -c -%d", str_lzop, level);
+      snprintf (ret, n, "%s -c -%d", "lzop", level);
     else {
       reply_with_error ("lzop: incorrect value for level parameter");
       return -1;
diff --git a/daemon/cpio.c b/daemon/cpio.c
index d1459b15d..0601a19ca 100644
--- a/daemon/cpio.c
+++ b/daemon/cpio.c
@@ -33,7 +33,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_cpio, cpio);
+DECLARE_EXTERNAL_COMMANDS ("cpio")
 
 /* Has one FileOut parameter. */
 /* Takes optional arguments, consult optargs_bitmask. */
@@ -82,7 +82,7 @@ do_cpio_out (const char *dir, const char *format)
 
   if (asprintf_nowarn (&cmd, "cd %Q && find -print0 | %s -0 -o -H %s --quiet",
                        buf,
-                       str_cpio,
+                       "cpio",
                        format) == -1) {
     reply_with_perror ("asprintf");
     return -1;
diff --git a/daemon/cpmv.c b/daemon/cpmv.c
index 841ac95ce..b831786dc 100644
--- a/daemon/cpmv.c
+++ b/daemon/cpmv.c
@@ -25,33 +25,32 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_cp, cp);
-GUESTFSD_EXT_CMD(str_mv, mv);
+DECLARE_EXTERNAL_COMMANDS ("cp", "mv")
 
 static int cpmv_cmd (const char *cmd, const char *flags, const char *src, const char *dest);
 
 int
 do_cp (const char *src, const char *dest)
 {
-  return cpmv_cmd (str_cp, NULL, src, dest);
+  return cpmv_cmd ("cp", NULL, src, dest);
 }
 
 int
 do_cp_a (const char *src, const char *dest)
 {
-  return cpmv_cmd (str_cp, "-a", src, dest);
+  return cpmv_cmd ("cp", "-a", src, dest);
 }
 
 int
 do_cp_r (const char *src, const char *dest)
 {
-  return cpmv_cmd (str_cp, "-rP", src, dest);
+  return cpmv_cmd ("cp", "-rP", src, dest);
 }
 
 int
 do_mv (const char *src, const char *dest)
 {
-  return cpmv_cmd (str_mv, NULL, src, dest);
+  return cpmv_cmd ("mv", NULL, src, dest);
 }
 
 static int
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 50ce41306..0f0d42836 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -84,6 +84,8 @@ extern int random_name (char *template);
 extern char *get_random_uuid (void);
 extern char *make_exclude_from_file (const char *function, char *const *excludes);
 extern int asprintf_nowarn (char **strp, const char *fmt, ...);
+extern void declare_external_commands (const char *cmds[]);
+extern void print_external_commands (void);
 
 /* mountable functions (in guestfsd.c) */
 extern char *mountable_to_string (const mountable_t *mountable);
@@ -397,7 +399,12 @@ extern int upload_to_fd (int fd, const char *filename);
     }                                                                   \
   } while (0)
 
-#define __external_command __attribute__((__section__(".guestfsd_ext_cmds")))
-#define GUESTFSD_EXT_CMD(___ext_cmd_var, ___ext_cmd_str) static const char ___ext_cmd_var[] __external_command = #___ext_cmd_str
+#define DECLARE_EXTERNAL_COMMANDS(cmd, ...)                             \
+  static void init_external_commands (void) __attribute__((constructor)); \
+  static void init_external_commands (void)                             \
+  {                                                                     \
+    static const char *cmds[] = { cmd, ##__VA_ARGS__, NULL };           \
+    declare_external_commands (cmds);                                   \
+  }
 
 #endif /* GUESTFSD_DAEMON_H */
diff --git a/daemon/dd.c b/daemon/dd.c
index d0390d8b9..1fd4e556c 100644
--- a/daemon/dd.c
+++ b/daemon/dd.c
@@ -27,7 +27,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_dd, dd);
+DECLARE_EXTERNAL_COMMANDS ("dd")
 
 int
 do_dd (const char *src, const char *dest)
@@ -59,7 +59,7 @@ do_dd (const char *src, const char *dest)
     return -1;
   }
 
-  r = command (NULL, &err, str_dd, "bs=1024K", if_arg, of_arg, NULL);
+  r = command (NULL, &err, "dd", "bs=1024K", if_arg, of_arg, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s: %s", src, dest, err);
     return -1;
diff --git a/daemon/debug.c b/daemon/debug.c
index e2d43a7ca..ddda03463 100644
--- a/daemon/debug.c
+++ b/daemon/debug.c
@@ -33,15 +33,8 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_printenv, printenv);
-GUESTFSD_EXT_CMD(str_ldd, ldd);
-GUESTFSD_EXT_CMD(str_ls, ls);
-GUESTFSD_EXT_CMD(str_find, find);
-GUESTFSD_EXT_CMD(str_xargs, xargs);
-GUESTFSD_EXT_CMD(str_file, file);
-GUESTFSD_EXT_CMD(str_grep, grep);
-GUESTFSD_EXT_CMD(str_gawk, gawk);
-GUESTFSD_EXT_CMD(str_sh, sh);
+DECLARE_EXTERNAL_COMMANDS ("printenv", "ldd", "ls", "find", "xargs",
+                           "file", "grep", "gawk", "sh")
 
 /* This command exposes debugging information, internals and
  * status.  There is no comprehensive documentation for this
@@ -301,7 +294,7 @@ debug_env (const char *subcmd, size_t argc, char *const *const argv)
   char *out;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (&out, &err, str_printenv, NULL);
+  r = command (&out, &err, "printenv", NULL);
   if (r == -1) {
     reply_with_error ("printenv: %s", err);
     free (out);
@@ -380,16 +373,13 @@ debug_binaries (const char *subcmd, size_t argc, char *const *const argv)
   int r;
   char *out;
   CLEANUP_FREE char *err = NULL;
-  char cmd[256];
+  const char *cmd =
+    "find / -xdev -type f -executable "
+    "| xargs file -i "
+    "| grep application/x-executable "
+    "| gawk -F: '{print $1}'";
 
-  snprintf (cmd, sizeof (cmd),
-            "%s / -xdev -type f -executable "
-            "| %s %s -i "
-            "| %s application/x-executable "
-            "| %s -F: '{print $1}'",
-            str_find, str_xargs, str_file, str_grep, str_gawk);
-
-  r = command (&out, &err, str_sh, "-c", cmd, NULL);
+  r = command (&out, &err, "sh", "-c", cmd, NULL);
   if (r == -1) {
     reply_with_error ("find: %s", err);
     free (out);
@@ -420,7 +410,7 @@ debug_ldd (const char *subcmd, size_t argc, char *const *const argv)
    * Also 'ldd' randomly sends messages to stderr and errors to stdout
    * depending on the phase of the moon.
    */
-  r = command (&out, &err, str_ldd, "-r", argv[0], NULL);
+  r = command (&out, &err, "ldd", "-r", argv[0], NULL);
   if (r == -1) {
     reply_with_error ("ldd: %s: %s", argv[0], err);
     free (out);
@@ -457,7 +447,7 @@ debug_ls (const char *subcmd, size_t argc, char *const *const argv)
     return NULL;
   }
 
-  cargv[0] = str_ls;
+  cargv[0] = "ls";
   cargv[1] = "-a";
   for (i = 0; i < len; ++i)
     cargv[2+i] = argv[i];
@@ -490,7 +480,7 @@ debug_ll (const char *subcmd, size_t argc, char *const *const argv)
     return NULL;
   }
 
-  cargv[0] = str_ls;
+  cargv[0] = "ls";
   cargv[1] = "-la";
   for (i = 0; i < len; ++i)
     cargv[2+i] = argv[i];
diff --git a/daemon/df.c b/daemon/df.c
index 80b765f30..b6578c6d4 100644
--- a/daemon/df.c
+++ b/daemon/df.c
@@ -27,7 +27,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_df, df);
+DECLARE_EXTERNAL_COMMANDS ("df")
 
 char *
 do_df (void)
@@ -38,7 +38,7 @@ do_df (void)
 
   NEED_ROOT (0, return NULL);
 
-  r = command (&out, &err, str_df, NULL);
+  r = command (&out, &err, "df", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
@@ -57,7 +57,7 @@ do_df_h (void)
 
   NEED_ROOT (0, return NULL);
 
-  r = command (&out, &err, str_df, "-h", NULL);
+  r = command (&out, &err, "df", "-h", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
diff --git a/daemon/dir.c b/daemon/dir.c
index 07dc68f17..4f4ba207e 100644
--- a/daemon/dir.c
+++ b/daemon/dir.c
@@ -29,7 +29,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_rm, rm);
+DECLARE_EXTERNAL_COMMANDS ("rm")
 
 int
 do_rmdir (const char *path)
@@ -69,7 +69,7 @@ do_rm_rf (const char *path)
     return -1;
   }
 
-  r = command (NULL, &err, str_rm, "-rf", buf, NULL);
+  r = command (NULL, &err, "rm", "-rf", buf, NULL);
   /* rm -rf is never supposed to fail.  I/O errors perhaps? */
   if (r == -1) {
     reply_with_error ("%s: %s", path, err);
diff --git a/daemon/dmesg.c b/daemon/dmesg.c
index 5e16861f2..3e24c5101 100644
--- a/daemon/dmesg.c
+++ b/daemon/dmesg.c
@@ -27,7 +27,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_dmesg, dmesg);
+DECLARE_EXTERNAL_COMMANDS ("dmesg")
 
 char *
 do_dmesg (void)
@@ -36,7 +36,7 @@ do_dmesg (void)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  r = command (&out, &err, str_dmesg, NULL);
+  r = command (&out, &err, "dmesg", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
diff --git a/daemon/du.c b/daemon/du.c
index 5bb764075..e4203f2dd 100644
--- a/daemon/du.c
+++ b/daemon/du.c
@@ -28,7 +28,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_du, du);
+DECLARE_EXTERNAL_COMMANDS ("du")
 
 int64_t
 do_du (const char *path)
@@ -46,7 +46,7 @@ do_du (const char *path)
 
   pulse_mode_start ();
 
-  r = command (&out, &err, str_du, "-s", buf, NULL);
+  r = command (&out, &err, "du", "-s", buf, NULL);
   if (r == -1) {
     pulse_mode_cancel ();
     reply_with_error ("%s: %s", path, err);
diff --git a/daemon/ext2.c b/daemon/ext2.c
index d694b236e..b64c93e83 100644
--- a/daemon/ext2.c
+++ b/daemon/ext2.c
@@ -33,13 +33,8 @@
 
 #define MAX_ARGS 128
 
-GUESTFSD_EXT_CMD(str_tune2fs, tune2fs);
-GUESTFSD_EXT_CMD(str_e2fsck, e2fsck);
-GUESTFSD_EXT_CMD(str_resize2fs, resize2fs);
-GUESTFSD_EXT_CMD(str_mke2fs, mke2fs);
-GUESTFSD_EXT_CMD(str_lsattr, lsattr);
-GUESTFSD_EXT_CMD(str_chattr, chattr);
-GUESTFSD_EXT_CMD(str_e2label, e2label);
+DECLARE_EXTERNAL_COMMANDS ("tune2fs", "e2fsck", "resize2fs",
+                           "mke2fs", "lsattr", "chattr", "e2label")
 
 /* https://bugzilla.redhat.com/show_bug.cgi?id=978302#c1 */
 int
@@ -57,7 +52,7 @@ do_tune2fs_l (const char *device)
   char *p, *pend, *colon;
   CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
 
-  r = command (&out, &err, str_tune2fs, "-l", device, NULL);
+  r = command (&out, &err, "tune2fs", "-l", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -136,7 +131,7 @@ do_set_e2label (const char *device, const char *label)
     return -1;
   }
 
-  r = command (NULL, &err, str_e2label, device, label, NULL);
+  r = command (NULL, &err, "e2label", device, label, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -162,7 +157,7 @@ do_set_e2uuid (const char *device, const char *uuid)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (NULL, &err, str_tune2fs, "-U", uuid, device, NULL);
+  r = command (NULL, &err, "tune2fs", "-U", uuid, device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -215,7 +210,7 @@ do_resize2fs (const char *device)
   if (if_not_mounted_run_e2fsck (device) == -1)
     return -1;
 
-  r = command (NULL, &err, str_resize2fs, device, NULL);
+  r = command (NULL, &err, "resize2fs", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -247,7 +242,7 @@ do_resize2fs_size (const char *device, int64_t size)
   char buf[32];
   snprintf (buf, sizeof buf, "%" PRIi64 "K", size);
 
-  r = command (NULL, &err, str_resize2fs, device, buf, NULL);
+  r = command (NULL, &err, "resize2fs", device, buf, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -265,7 +260,7 @@ do_resize2fs_M (const char *device)
   if (if_not_mounted_run_e2fsck (device) == -1)
     return -1;
 
-  r = command (NULL, &err, str_resize2fs, "-M", device, NULL);
+  r = command (NULL, &err, "resize2fs", "-M", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -311,7 +306,7 @@ ext_minimum_size (const char *device)
   long block_size;
   const char *pattern = "Estimated minimum size of the filesystem: ";
 
-  r = command (&out, &err, str_resize2fs, "-P", "-f", device, NULL);
+  r = command (&out, &err, "resize2fs", "-P", "-f", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -376,7 +371,7 @@ do_e2fsck (const char *device,
     return -1;
   }
 
-  ADD_ARG (argv, i, str_e2fsck);
+  ADD_ARG (argv, i, "e2fsck");
   ADD_ARG (argv, i, "-f");
 
   if (correct)
@@ -425,7 +420,7 @@ do_mke2journal (int blocksize, const char *device)
   wipe_device_before_mkfs (device);
 
   r = command (NULL, &err,
-               str_mke2fs, "-F", "-O", "journal_dev", "-b", blocksize_s,
+               "mke2fs", "-F", "-O", "journal_dev", "-b", blocksize_s,
                device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -453,7 +448,7 @@ do_mke2journal_L (int blocksize, const char *label, const char *device)
   wipe_device_before_mkfs (device);
 
   r = command (NULL, &err,
-               str_mke2fs, "-F", "-O", "journal_dev", "-b", blocksize_s,
+               "mke2fs", "-F", "-O", "journal_dev", "-b", blocksize_s,
                "-L", label,
                device, NULL);
   if (r == -1) {
@@ -476,7 +471,7 @@ do_mke2journal_U (int blocksize, const char *uuid, const char *device)
   wipe_device_before_mkfs (device);
 
   r = command (NULL, &err,
-               str_mke2fs, "-F", "-O", "journal_dev", "-b", blocksize_s,
+               "mke2fs", "-F", "-O", "journal_dev", "-b", blocksize_s,
                "-U", uuid,
                device, NULL);
   if (r == -1) {
@@ -511,7 +506,7 @@ do_mke2fs_J (const char *fstype, int blocksize, const char *device,
   wipe_device_before_mkfs (device);
 
   r = command (NULL, &err,
-               str_mke2fs, "-F", "-t", fstype, "-J", jdev, "-b", blocksize_s,
+               "mke2fs", "-F", "-t", fstype, "-J", jdev, "-b", blocksize_s,
                device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -551,7 +546,7 @@ do_mke2fs_JL (const char *fstype, int blocksize, const char *device,
   wipe_device_before_mkfs (device);
 
   r = command (NULL, &err,
-               str_mke2fs, "-F", "-t", fstype, "-J", jdev, "-b", blocksize_s,
+               "mke2fs", "-F", "-t", fstype, "-J", jdev, "-b", blocksize_s,
                device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -585,7 +580,7 @@ do_mke2fs_JU (const char *fstype, int blocksize, const char *device,
   wipe_device_before_mkfs (device);
 
   r = command (NULL, &err,
-               str_mke2fs, "-F", "-t", fstype, "-J", jdev, "-b", blocksize_s,
+               "mke2fs", "-F", "-t", fstype, "-J", jdev, "-b", blocksize_s,
                device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -621,7 +616,7 @@ do_tune2fs (const char *device, /* only required parameter */
   char reservedblockscount_s[64];
   char user_s[64];
 
-  ADD_ARG (argv, i, str_tune2fs);
+  ADD_ARG (argv, i, "tune2fs");
 
   if (optargs_bitmask & GUESTFS_TUNE2FS_FORCE_BITMASK) {
     if (force)
@@ -759,7 +754,7 @@ do_get_e2attrs (const char *filename)
     return NULL;
   }
 
-  r = command (&out, &err, str_lsattr, "-d", "--", buf, NULL);
+  r = command (&out, &err, "lsattr", "-d", "--", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s: %s", "lsattr", filename, err);
     free (out);
@@ -850,7 +845,7 @@ do_set_e2attrs (const char *filename, const char *attrs, int clear)
     return -1;
   }
 
-  r = command (NULL, &err, str_chattr, attr_arg, "--", buf, NULL);
+  r = command (NULL, &err, "chattr", attr_arg, "--", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s: %s", "chattr", filename, err);
     return -1;
@@ -872,7 +867,7 @@ do_get_e2generation (const char *filename)
     return -1;
   }
 
-  r = command (&out, &err, str_lsattr, "-dv", "--", buf, NULL);
+  r = command (&out, &err, "lsattr", "-dv", "--", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s: %s", "lsattr", filename, err);
     return -1;
@@ -908,7 +903,7 @@ do_set_e2generation (const char *filename, int64_t generation)
   snprintf (generation_str, sizeof generation_str,
             "%" PRIu64, (uint64_t) generation);
 
-  r = command (NULL, &err, str_chattr, "-v", generation_str, "--", buf, NULL);
+  r = command (NULL, &err, "chattr", "-v", generation_str, "--", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s: %s", "chattr", filename, err);
     return -1;
@@ -979,7 +974,7 @@ do_mke2fs (const char *device,               /* 0 */
   char maxonlineresize_s[74];
   size_t i = 0;
 
-  ADD_ARG (argv, i, str_mke2fs);
+  ADD_ARG (argv, i, "mke2fs");
 
   if (optargs_bitmask & GUESTFS_MKE2FS_BLOCKSIZE_BITMASK) {
     if (blocksize < 0) {
diff --git a/daemon/file.c b/daemon/file.c
index 84874dc6f..794409eb4 100644
--- a/daemon/file.c
+++ b/daemon/file.c
@@ -30,9 +30,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_file, file);
-GUESTFSD_EXT_CMD(str_zcat, zcat);
-GUESTFSD_EXT_CMD(str_bzcat, bzcat);
+DECLARE_EXTERNAL_COMMANDS ("file", "zcat", "bzcat")
 
 int
 do_touch (const char *path)
@@ -505,7 +503,7 @@ do_file (const char *path)
 
   char *out;
   CLEANUP_FREE char *err = NULL;
-  int r = command (&out, &err, str_file, flags, path, NULL);
+  int r = command (&out, &err, "file", flags, path, NULL);
 
   if (r == -1) {
     free (out);
@@ -539,9 +537,9 @@ do_zfile (const char *method, const char *path)
   char line[256];
 
   if (STREQ (method, "gzip") || STREQ (method, "compress"))
-    zcat = str_zcat;
+    zcat = "zcat";
   else if (STREQ (method, "bzip2"))
-    zcat = str_bzcat;
+    zcat = "bzcat";
   else {
     reply_with_error ("unknown method");
     return NULL;
diff --git a/daemon/find.c b/daemon/find.c
index 3a0cf5d73..e74883288 100644
--- a/daemon/find.c
+++ b/daemon/find.c
@@ -30,7 +30,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_find, find);
+DECLARE_EXTERNAL_COMMANDS ("find")
 
 static int
 input_to_nul (FILE *fp, char *buf, size_t maxlen)
@@ -87,7 +87,7 @@ do_find0 (const char *dir)
 
   sysrootdirlen = strlen (sysrootdir);
 
-  if (asprintf_nowarn (&cmd, "%s %Q -print0", str_find, sysrootdir) == -1) {
+  if (asprintf_nowarn (&cmd, "find %Q -print0", sysrootdir) == -1) {
     reply_with_perror ("asprintf");
     return -1;
   }
diff --git a/daemon/findfs.c b/daemon/findfs.c
index f44137038..fe1def360 100644
--- a/daemon/findfs.c
+++ b/daemon/findfs.c
@@ -26,7 +26,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_findfs, findfs);
+DECLARE_EXTERNAL_COMMANDS ("findfs")
 
 static char *
 findfs (const char *tag, const char *label_or_uuid)
@@ -50,7 +50,7 @@ findfs (const char *tag, const char *label_or_uuid)
     return NULL;
   }
 
-  r = command (&out, &err, str_findfs, arg, NULL);
+  r = command (&out, &err, "findfs", arg, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
diff --git a/daemon/fsck.c b/daemon/fsck.c
index 540f779d3..5643460e1 100644
--- a/daemon/fsck.c
+++ b/daemon/fsck.c
@@ -26,7 +26,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_fsck, fsck);
+DECLARE_EXTERNAL_COMMANDS ("fsck")
 
 int
 do_fsck (const char *fstype, const char *device)
@@ -34,7 +34,7 @@ do_fsck (const char *fstype, const char *device)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  r = commandr (NULL, &err, str_fsck, "-a", "-t", fstype, device, NULL);
+  r = commandr (NULL, &err, "fsck", "-a", "-t", fstype, device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
diff --git a/daemon/fstrim.c b/daemon/fstrim.c
index 527acfd48..913267054 100644
--- a/daemon/fstrim.c
+++ b/daemon/fstrim.c
@@ -30,12 +30,12 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_fstrim, fstrim);
+DECLARE_EXTERNAL_COMMANDS ("fstrim")
 
 int
 optgroup_fstrim_available (void)
 {
-  return prog_exists (str_fstrim);
+  return prog_exists ("fstrim");
 }
 
 /* Takes optional arguments, consult optargs_bitmask. */
@@ -55,7 +55,7 @@ do_fstrim (const char *path,
    */
   sync_disks ();
 
-  ADD_ARG (argv, i, str_fstrim);
+  ADD_ARG (argv, i, "fstrim");
 
   if ((optargs_bitmask & GUESTFS_FSTRIM_OFFSET_BITMASK)) {
     if (offset < 0) {
diff --git a/daemon/grub.c b/daemon/grub.c
index 9bcf373bb..c76d269d8 100644
--- a/daemon/grub.c
+++ b/daemon/grub.c
@@ -26,12 +26,12 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_grub_install, grub-install);
+DECLARE_EXTERNAL_COMMANDS ("grub-install")
 
 int
 optgroup_grub_available (void)
 {
-  return prog_exists (str_grub_install);
+  return prog_exists ("grub-install");
 }
 
 int
@@ -46,7 +46,7 @@ do_grub_install (const char *root, const char *device)
   }
 
   r = command (verbose ? &out : NULL, &err,
-               str_grub_install, buf, device, NULL);
+               "grub-install", buf, device, NULL);
 
   if (r == -1) {
     if (verbose)
diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c
index b3f40628b..86592e8dc 100644
--- a/daemon/guestfsd.c
+++ b/daemon/guestfsd.c
@@ -63,8 +63,7 @@
 
 #include "daemon.h"
 
-GUESTFSD_EXT_CMD(str_udevadm, udevadm);
-GUESTFSD_EXT_CMD(str_uuidgen, uuidgen);
+DECLARE_EXTERNAL_COMMANDS ("udevadm", "uuidgen")
 
 #ifndef MAX
 # define MAX(a,b) ((a)>(b)?(a):(b))
@@ -144,6 +143,7 @@ main (int argc, char *argv[])
     { "channel", 1, 0, 'c' },
     { "listen", 0, 0, 'l' },
     { "network", 0, 0, 'n' },
+    { "print-external-commands", 0, 0, 0 },
     { "test", 0, 0, 't' },
     { "verbose", 0, 0, 'v' },
     { 0, 0, 0, 0 }
@@ -151,6 +151,8 @@ main (int argc, char *argv[])
   int c;
   const char *channel = NULL;
   int listen_mode = 0;
+  int option_index;
+  int do_print_external_commands = 0;
 
   ignore_value (chdir ("/"));
 
@@ -185,10 +187,19 @@ main (int argc, char *argv[])
     root_device = statbuf.st_dev;
 
   for (;;) {
-    c = getopt_long (argc, argv, options, long_options, NULL);
+    c = getopt_long (argc, argv, options, long_options, &option_index);
     if (c == -1) break;
 
     switch (c) {
+    case 0:                     /* options which are long only */
+      if (STREQ (long_options[option_index].name, "print-external-commands"))
+        do_print_external_commands = 1;
+      else
+        error (EXIT_FAILURE, 0,
+               "unknown long option: %s (%d)",
+               long_options[option_index].name, option_index);
+      break;
+
     case 'c':
       channel = optarg;
       break;
@@ -234,6 +245,12 @@ main (int argc, char *argv[])
     exit (EXIT_FAILURE);
   }
 
+  /* Handle the --print-external-commands option. */
+  if (do_print_external_commands) {
+    print_external_commands ();
+    exit (EXIT_SUCCESS);
+  }
+
 #ifndef WIN32
   /* Make sure SIGPIPE doesn't kill us. */
   struct sigaction sa;
@@ -1095,7 +1112,7 @@ udev_settle_file (const char *file)
   size_t i = 0;
   int r;
 
-  ADD_ARG (argv, i, str_udevadm);
+  ADD_ARG (argv, i, "udevadm");
   if (verbose)
     ADD_ARG (argv, i, "--debug");
 
@@ -1124,7 +1141,7 @@ get_random_uuid (void)
   char *out;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (&out, &err, str_uuidgen, NULL);
+  r = command (&out, &err, "uuidgen", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -1205,3 +1222,48 @@ cleanup_free_mountable (mountable_t *mountable)
     free (mountable->volume);
   }
 }
+
+static const char **external_commands = NULL;
+static size_t nr_external_commands = 0;
+
+/* This is called from C by the DECLARE_EXTERNAL_COMMANDS macro. */
+void
+declare_external_commands (const char *cmds[])
+{
+  size_t i;
+  size_t n = guestfs_int_count_strings ((char **) cmds);
+
+  external_commands = realloc (external_commands,
+                               sizeof (char *) * (nr_external_commands + n));
+  if (external_commands == NULL)
+    error (EXIT_FAILURE, errno, "realloc");
+
+  for (i = 0; i < n; ++i)
+    external_commands[nr_external_commands++] = cmds[i];
+}
+
+void
+print_external_commands (void)
+{
+  size_t i;
+  FILE *fp;
+
+  fp = popen ("sort -u", "w");
+  if (fp == NULL)
+    error (EXIT_FAILURE, errno, "popen: sort");
+
+  for (i = 0; i < nr_external_commands; ++i)
+    fprintf (fp, "%s\n", external_commands[i]);
+
+  pclose (fp);
+}
+
+static void free_external_commands (void) __attribute__((destructor));
+
+static void
+free_external_commands (void)
+{
+  free (external_commands);
+  external_commands = NULL;
+  nr_external_commands = 0;
+}
diff --git a/daemon/guestfsd.pod b/daemon/guestfsd.pod
index 2ff591548..b2d8f87ea 100644
--- a/daemon/guestfsd.pod
+++ b/daemon/guestfsd.pod
@@ -78,6 +78,13 @@ the daemon.
 
 Enable network features in the daemon.
 
+=item B<--print-external-commands>
+
+Print the names of external commands run by the daemon.
+
+This is used by some downstream packagers to ensure that the correct
+commands are added to the appliance at build time.
+
 =item B<-r>
 
 Set the root filesystem to be F</> (instead of the default which is
diff --git a/daemon/hotplug.c b/daemon/hotplug.c
index 234f51ed2..5ce1f6dd2 100644
--- a/daemon/hotplug.c
+++ b/daemon/hotplug.c
@@ -77,7 +77,7 @@ do_internal_hot_add_drive (const char *label)
   return -1;
 }
 
-GUESTFSD_EXT_CMD(str_fuser, fuser);
+DECLARE_EXTERNAL_COMMANDS ("fuser")
 
 /* This function is called before a drive is hot-unplugged. */
 int
@@ -96,7 +96,7 @@ do_internal_hot_remove_drive_precheck (const char *label)
     return -1;
   }
 
-  r = commandr (&out, &err, str_fuser, "-v", "-m", path, NULL);
+  r = commandr (&out, &err, "fuser", "-v", "-m", path, NULL);
   if (r == -1) {
     reply_with_error ("fuser: %s: %s", path, err);
     return -1;
diff --git a/daemon/initrd.c b/daemon/initrd.c
index 21865debf..24a2d9864 100644
--- a/daemon/initrd.c
+++ b/daemon/initrd.c
@@ -31,8 +31,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_zcat, zcat);
-GUESTFSD_EXT_CMD(str_cpio, cpio);
+DECLARE_EXTERNAL_COMMANDS ("zcat", "cpio")
 
 char **
 do_initrd_list (const char *path)
@@ -46,7 +45,7 @@ do_initrd_list (const char *path)
   int ret;
 
   /* "zcat /sysroot/<path> | cpio --quiet -it", but path must be quoted. */
-  if (asprintf_nowarn (&cmd, "%s %R | %s --quiet -it", str_zcat, path, str_cpio) == -1) {
+  if (asprintf_nowarn (&cmd, "zcat %R | cpio --quiet -it", path) == -1) {
     reply_with_perror ("asprintf");
     return NULL;
   }
diff --git a/daemon/inotify.c b/daemon/inotify.c
index b9bfed713..5ceaacfe7 100644
--- a/daemon/inotify.c
+++ b/daemon/inotify.c
@@ -38,7 +38,8 @@
 #include "optgroups.h"
 
 #ifdef HAVE_SYS_INOTIFY_H
-GUESTFSD_EXT_CMD(str_sort, sort);
+
+DECLARE_EXTERNAL_COMMANDS ("sort")
 
 /* Currently open inotify handle, or -1 if not opened. */
 static int inotify_fd = -1;
@@ -327,7 +328,7 @@ do_inotify_files (void)
     return NULL;
   }
 
-  snprintf (cmd, sizeof cmd, "%s -u > %s", str_sort, tempfile);
+  snprintf (cmd, sizeof cmd, "sort -u > %s", tempfile);
 
   fp = popen (cmd, "w");
   if (fp == NULL) {
diff --git a/daemon/isoinfo.c b/daemon/isoinfo.c
index 3e1acf848..c59a698aa 100644
--- a/daemon/isoinfo.c
+++ b/daemon/isoinfo.c
@@ -30,7 +30,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_isoinfo, isoinfo);
+DECLARE_EXTERNAL_COMMANDS ("isoinfo")
 
 static int
 parse_uint32 (uint32_t *ret, const char *str)
@@ -246,7 +246,7 @@ isoinfo (const char *path)
   /* --debug is necessary to get additional fields, in particular
    * the date & time fields.
    */
-  r = command (&out, &err, str_isoinfo, "--debug", "-d", "-i", path, NULL);
+  r = command (&out, &err, "isoinfo", "--debug", "-d", "-i", path, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
diff --git a/daemon/labels.c b/daemon/labels.c
index aaa3eaf89..f247c9ef5 100644
--- a/daemon/labels.c
+++ b/daemon/labels.c
@@ -27,7 +27,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_dosfslabel, dosfslabel);
+DECLARE_EXTERNAL_COMMANDS ("dosfslabel")
 
 static int
 dosfslabel (const char *device, const char *label)
@@ -35,7 +35,7 @@ dosfslabel (const char *device, const char *label)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (NULL, &err, str_dosfslabel, device, label, NULL);
+  r = command (NULL, &err, "dosfslabel", device, label, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
diff --git a/daemon/ldm.c b/daemon/ldm.c
index 75418e8d3..3e1b72f39 100644
--- a/daemon/ldm.c
+++ b/daemon/ldm.c
@@ -39,12 +39,12 @@
 #pragma GCC diagnostic ignored "-Wnull-dereference"
 #endif
 
-GUESTFSD_EXT_CMD(str_ldmtool, ldmtool);
+DECLARE_EXTERNAL_COMMANDS ("ldmtool")
 
 int
 optgroup_ldm_available (void)
 {
-  return prog_exists (str_ldmtool);
+  return prog_exists ("ldmtool");
 }
 
 static int
@@ -134,7 +134,7 @@ do_ldmtool_create_all (void)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (NULL, &err, str_ldmtool, "create", "all", NULL);
+  r = command (NULL, &err, "ldmtool", "create", "all", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -148,7 +148,7 @@ do_ldmtool_remove_all (void)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (NULL, &err, str_ldmtool, "remove", "all", NULL);
+  r = command (NULL, &err, "ldmtool", "remove", "all", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -327,7 +327,7 @@ do_ldmtool_scan_devices (char * const * devices)
     return NULL;
   }
 
-  argv[0] = str_ldmtool;
+  argv[0] = "ldmtool";
   argv[1] = "scan";
   for (i = 0; i < nr_devices; ++i)
     argv[2+i] = devices[i];
@@ -349,7 +349,7 @@ do_ldmtool_diskgroup_name (const char *diskgroup)
   int r;
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
-  r = command (&out, &err, str_ldmtool, "show", "diskgroup", diskgroup, NULL);
+  r = command (&out, &err, "ldmtool", "show", "diskgroup", diskgroup, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -365,7 +365,7 @@ do_ldmtool_diskgroup_volumes (const char *diskgroup)
   int r;
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
-  r = command (&out, &err, str_ldmtool, "show", "diskgroup", diskgroup, NULL);
+  r = command (&out, &err, "ldmtool", "show", "diskgroup", diskgroup, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -382,7 +382,7 @@ do_ldmtool_diskgroup_disks (const char *diskgroup)
   int r;
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
-  r = command (&out, &err, str_ldmtool, "show", "diskgroup", diskgroup, NULL);
+  r = command (&out, &err, "ldmtool", "show", "diskgroup", diskgroup, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -399,7 +399,7 @@ do_ldmtool_volume_type (const char *diskgroup, const char *volume)
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
   r = command (&out, &err,
-               str_ldmtool, "show", "volume", diskgroup, volume, NULL);
+               "ldmtool", "show", "volume", diskgroup, volume, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -416,7 +416,7 @@ do_ldmtool_volume_hint (const char *diskgroup, const char *volume)
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
   r = command (&out, &err,
-               str_ldmtool, "show", "volume", diskgroup, volume, NULL);
+               "ldmtool", "show", "volume", diskgroup, volume, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -433,7 +433,7 @@ do_ldmtool_volume_partitions (const char *diskgroup, const char *volume)
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
   r = command (&out, &err,
-               str_ldmtool, "show", "volume", diskgroup, volume, NULL);
+               "ldmtool", "show", "volume", diskgroup, volume, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
diff --git a/daemon/link.c b/daemon/link.c
index 3ce54fa37..f0bd34034 100644
--- a/daemon/link.c
+++ b/daemon/link.c
@@ -30,7 +30,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_ln, ln);
+DECLARE_EXTERNAL_COMMANDS ("ln")
 
 char *
 do_readlink (const char *path)
@@ -140,7 +140,7 @@ _symlink (const char *flag, const char *target, const char *linkname)
   }
 
   r = command (NULL, &err,
-               str_ln, flag, "--", /* target could begin with '-' */
+               "ln", flag, "--", /* target could begin with '-' */
                target, buf_linkname, NULL);
   if (r == -1) {
     reply_with_error ("ln %s: %s: %s: %s",
diff --git a/daemon/ls.c b/daemon/ls.c
index 0e2f110f5..9c97cf51f 100644
--- a/daemon/ls.c
+++ b/daemon/ls.c
@@ -30,7 +30,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_ls, ls);
+DECLARE_EXTERNAL_COMMANDS ("ls")
 
 /* Has one FileOut parameter. */
 int
@@ -118,7 +118,7 @@ do_ll (const char *path)
     return NULL;
   }
 
-  r = command (&out, &err, str_ls, "-la", spath, NULL);
+  r = command (&out, &err, "ls", "-la", spath, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
@@ -151,7 +151,7 @@ do_llz (const char *path)
     return NULL;
   }
 
-  r = command (&out, &err, str_ls, "-laZ", spath, NULL);
+  r = command (&out, &err, "ls", "-laZ", spath, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
diff --git a/daemon/luks.c b/daemon/luks.c
index 53bb820d6..33873a0b9 100644
--- a/daemon/luks.c
+++ b/daemon/luks.c
@@ -28,12 +28,12 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_cryptsetup, cryptsetup);
+DECLARE_EXTERNAL_COMMANDS ("cryptsetup")
 
 int
 optgroup_luks_available (void)
 {
-  return prog_exists (str_cryptsetup);
+  return prog_exists ("cryptsetup");
 }
 
 /* Callers must also call remove_temp (tempfile). */
@@ -108,7 +108,7 @@ luks_open (const char *device, const char *key, const char *mapname,
   const char *argv[MAX_ARGS];
   size_t i = 0;
 
-  ADD_ARG (argv, i, str_cryptsetup);
+  ADD_ARG (argv, i, "cryptsetup");
   ADD_ARG (argv, i, "-d");
   ADD_ARG (argv, i, tempfile);
   if (readonly) ADD_ARG (argv, i, "--readonly");
@@ -155,7 +155,7 @@ do_luks_close (const char *device)
   const char *mapname = &device[12];
 
   CLEANUP_FREE char *err = NULL;
-  int r = command (NULL, &err, str_cryptsetup, "luksClose", mapname, NULL);
+  int r = command (NULL, &err, "cryptsetup", "luksClose", mapname, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -178,7 +178,7 @@ luks_format (const char *device, const char *key, int keyslot,
   char keyslot_s[16];
   size_t i = 0;
 
-  ADD_ARG (argv, i, str_cryptsetup);
+  ADD_ARG (argv, i, "cryptsetup");
   ADD_ARG (argv, i, "-q");
   if (cipher) {
     ADD_ARG (argv, i, "--cipher");
@@ -237,7 +237,7 @@ do_luks_add_key (const char *device, const char *key, const char *newkey,
   char keyslot_s[16];
   size_t i = 0;
 
-  ADD_ARG (argv, i, str_cryptsetup);
+  ADD_ARG (argv, i, "cryptsetup");
   ADD_ARG (argv, i, "-q");
   ADD_ARG (argv, i, "-d");
   ADD_ARG (argv, i, keyfile);
@@ -273,7 +273,7 @@ do_luks_kill_slot (const char *device, const char *key, int keyslot)
   char keyslot_s[16];
   size_t i = 0;
 
-  ADD_ARG (argv, i, str_cryptsetup);
+  ADD_ARG (argv, i, "cryptsetup");
   ADD_ARG (argv, i, "-q");
   ADD_ARG (argv, i, "-d");
   ADD_ARG (argv, i, tempfile);
diff --git a/daemon/lvm-filter.c b/daemon/lvm-filter.c
index 5c9ce1866..deaecfb67 100644
--- a/daemon/lvm-filter.c
+++ b/daemon/lvm-filter.c
@@ -36,10 +36,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_lvm, lvm);
-GUESTFSD_EXT_CMD(str_cp, cp);
-GUESTFSD_EXT_CMD(str_rm, rm);
-GUESTFSD_EXT_CMD(str_lvmetad, lvmetad);
+DECLARE_EXTERNAL_COMMANDS ("lvm", "cp", "rm", "lvmetad")
 
 /* This runs during daemon start up and creates a complete copy of
  * /etc/lvm so that we can modify it as we desire.  We set
@@ -79,7 +76,7 @@ copy_lvm (void)
     error (EXIT_FAILURE, errno, "mkdtemp: %s", lvm_system_dir);
 
   /* Copy the entire directory */
-  snprintf (cmd, sizeof cmd, "%s -a /etc/lvm/ %s", str_cp, lvm_system_dir);
+  snprintf (cmd, sizeof cmd, "%s -a /etc/lvm/ %s", "cp", lvm_system_dir);
   r = system (cmd);
   if (r == -1) {
     perror (cmd);
@@ -106,13 +103,11 @@ copy_lvm (void)
 void
 start_lvmetad (void)
 {
-  char cmd[64];
   int r;
 
-  snprintf (cmd, sizeof cmd, "%s", str_lvmetad);
   if (verbose)
-    printf ("%s\n", cmd);
-  r = system (cmd);
+    printf ("%s\n", "lvmetad");
+  r = system ("lvmetad");
   if (r == -1)
     perror ("system/lvmetad");
   else if (!WIFEXITED (r) || WEXITSTATUS (r) != 0)
@@ -124,7 +119,7 @@ rm_lvm_system_dir (void)
 {
   char cmd[64];
 
-  snprintf (cmd, sizeof cmd, "%s -rf %s", str_rm, lvm_system_dir);
+  snprintf (cmd, sizeof cmd, "rm -rf %s", lvm_system_dir);
   ignore_value (system (cmd));
 }
 
@@ -232,7 +227,7 @@ static int
 vgchange (const char *vgchange_flag)
 {
   CLEANUP_FREE char *err = NULL;
-  int r = command (NULL, &err, str_lvm, "vgchange", vgchange_flag, NULL);
+  int r = command (NULL, &err, "lvm", "vgchange", vgchange_flag, NULL);
   if (r == -1) {
     reply_with_error ("vgchange %s: %s", vgchange_flag, err);
     return -1;
@@ -265,7 +260,7 @@ rescan (void)
   unlink (lvm_cache);
 
   CLEANUP_FREE char *err = NULL;
-  int r = command (NULL, &err, str_lvm, "vgscan", NULL);
+  int r = command (NULL, &err, "lvm", "vgscan", NULL);
   if (r == -1) {
     reply_with_error ("vgscan: %s", err);
     return -1;
diff --git a/daemon/lvm.c b/daemon/lvm.c
index 5d12b009f..ee2ca8577 100644
--- a/daemon/lvm.c
+++ b/daemon/lvm.c
@@ -32,12 +32,12 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_lvm, lvm);
+DECLARE_EXTERNAL_COMMANDS ("lvm")
 
 int
 optgroup_lvm2_available (void)
 {
-  return prog_exists (str_lvm);
+  return prog_exists ("lvm");
 }
 
 /* LVM actions.  Keep an eye on liblvm, although at the time
@@ -194,7 +194,7 @@ do_pvs (void)
   int r;
 
   r = command (&out, &err,
-               str_lvm, "pvs", "-o", "pv_name", "--noheadings", NULL);
+               "lvm", "pvs", "-o", "pv_name", "--noheadings", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
@@ -212,7 +212,7 @@ do_vgs (void)
   int r;
 
   r = command (&out, &err,
-               str_lvm, "vgs", "-o", "vg_name", "--noheadings", NULL);
+               "lvm", "vgs", "-o", "vg_name", "--noheadings", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
@@ -235,7 +235,7 @@ test_lvs_has_S_opt (void)
   CLEANUP_FREE char *out = NULL;
   CLEANUP_FREE char *err = NULL;
 
-  int r = command (&out, &err, str_lvm, "lvs", "--help", NULL);
+  int r = command (&out, &err, "lvm", "lvs", "--help", NULL);
   if (r == -1) {
     reply_with_error ("lvm lvs --help: %s", err);
     return -1;
@@ -262,7 +262,7 @@ do_lvs (void)
 
   if (has_S > 0) {
     r = command (&out, &err,
-                 str_lvm, "lvs",
+                 "lvm", "lvs",
                  "-o", "vg_name,lv_name",
                  "-S", "lv_role=public && lv_skip_activation!=yes",
                  "--noheadings",
@@ -276,7 +276,7 @@ do_lvs (void)
     return convert_lvm_output (out, "/dev/");
   } else {
     r = command (&out, &err,
-                 str_lvm, "lvs",
+                 "lvm", "lvs",
                  "-o", "lv_attr,vg_name,lv_name",
                  "--noheadings",
                  "--separator", ":", NULL);
@@ -319,7 +319,7 @@ do_pvcreate (const char *device)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "pvcreate", "--force", device, NULL);
+               "lvm", "pvcreate", "--force", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -343,7 +343,7 @@ do_vgcreate (const char *volgroup, char *const *physvols)
     reply_with_perror ("malloc");
     return -1;
   }
-  argv[0] = str_lvm;
+  argv[0] = "lvm";
   argv[1] = "vgcreate";
   argv[2] = volgroup;
   for (i = 3; i < argc+1; ++i)
@@ -370,7 +370,7 @@ do_lvcreate (const char *logvol, const char *volgroup, int mbytes)
   snprintf (size, sizeof size, "%d", mbytes);
 
   r = command (NULL, &err,
-               str_lvm, "lvcreate",
+               "lvm", "lvcreate",
                "-L", size, "-n", logvol, volgroup, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -397,7 +397,7 @@ do_lvcreate_free (const char *logvol, const char *volgroup, int percent)
   snprintf (size, sizeof size, "%d%%FREE", percent);
 
   r = command (NULL, &err,
-               str_lvm, "lvcreate",
+               "lvm", "lvcreate",
                "-l", size, "-n", logvol, volgroup, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -430,7 +430,7 @@ do_lvresize (const char *logvol, int mbytes)
   snprintf (size, sizeof size, "%d", mbytes);
 
   r = command (NULL, &err,
-               str_lvm, "lvresize",
+               "lvm", "lvresize",
                "--force", "-L", size, logvol, NULL);
   if (r == -1) {
     if (!ignore_same_size_error (err)) {
@@ -457,7 +457,7 @@ do_lvresize_free (const char *logvol, int percent)
   snprintf (size, sizeof size, "+%d%%FREE", percent);
 
   r = command (NULL, &err,
-               str_lvm, "lvresize", "-l", size, logvol, NULL);
+               "lvm", "lvresize", "-l", size, logvol, NULL);
   if (r == -1) {
     if (!ignore_same_size_error (err)) {
       reply_with_error ("%s", err);
@@ -489,10 +489,10 @@ do_lvm_remove_all (void)
       /* Deactivate the LV first.  On Ubuntu, lvremove '-f' option
        * does not remove active LVs reliably.
        */
-      (void) command (NULL, NULL, str_lvm, "lvchange", "-an", xs[i], NULL);
+      (void) command (NULL, NULL, "lvm", "lvchange", "-an", xs[i], NULL);
       udev_settle ();
 
-      r = command (NULL, &err, str_lvm, "lvremove", "-f", xs[i], NULL);
+      r = command (NULL, &err, "lvm", "lvremove", "-f", xs[i], NULL);
       if (r == -1) {
         reply_with_error ("lvremove: %s: %s", xs[i], err);
         return -1;
@@ -510,10 +510,10 @@ do_lvm_remove_all (void)
       CLEANUP_FREE char *err = NULL;
 
       /* Deactivate the VG first, see note above. */
-      (void) command (NULL, NULL, str_lvm, "vgchange", "-an", xs[i], NULL);
+      (void) command (NULL, NULL, "lvm", "vgchange", "-an", xs[i], NULL);
       udev_settle ();
 
-      r = command (NULL, &err, str_lvm, "vgremove", "-f", xs[i], NULL);
+      r = command (NULL, &err, "lvm", "vgremove", "-f", xs[i], NULL);
       if (r == -1) {
         reply_with_error ("vgremove: %s: %s", xs[i], err);
         return -1;
@@ -530,7 +530,7 @@ do_lvm_remove_all (void)
     for (i = 0; xs[i] != NULL; ++i) {
       CLEANUP_FREE char *err = NULL;
 
-      r = command (NULL, &err, str_lvm, "pvremove", "-f", xs[i], NULL);
+      r = command (NULL, &err, "lvm", "pvremove", "-f", xs[i], NULL);
       if (r == -1) {
         reply_with_error ("pvremove: %s: %s", xs[i], err);
         return -1;
@@ -551,7 +551,7 @@ do_lvremove (const char *device)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "lvremove", "-f", device, NULL);
+               "lvm", "lvremove", "-f", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -569,7 +569,7 @@ do_vgremove (const char *device)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "vgremove", "-f", device, NULL);
+               "lvm", "vgremove", "-f", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -587,7 +587,7 @@ do_pvremove (const char *device)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "pvremove", "-ff", device, NULL);
+               "lvm", "pvremove", "-ff", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -605,7 +605,7 @@ do_pvresize (const char *device)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "pvresize", device, NULL);
+               "lvm", "pvresize", device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
@@ -624,7 +624,7 @@ do_pvresize_size (const char *device, int64_t size)
   snprintf (buf, sizeof buf, "%" PRIi64 "b", size);
 
   r = command (NULL, &err,
-               str_lvm, "pvresize",
+               "lvm", "pvresize",
                "--yes",
                "--setphysicalvolumesize", buf,
                device, NULL);
@@ -650,7 +650,7 @@ do_vg_activate (int activate, char *const *volgroups)
     return -1;
   }
 
-  argv[0] = str_lvm;
+  argv[0] = "lvm";
   argv[1] = "vgchange";
   argv[2] = "-a";
   argv[3] = activate ? "y" : "n";
@@ -682,7 +682,7 @@ do_lvrename (const char *logvol, const char *newlogvol)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "lvrename",
+               "lvm", "lvrename",
                logvol, newlogvol, NULL);
   if (r == -1) {
     reply_with_error ("%s -> %s: %s", logvol, newlogvol, err);
@@ -701,7 +701,7 @@ do_vgrename (const char *volgroup, const char *newvolgroup)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "vgrename",
+               "lvm", "vgrename",
                volgroup, newvolgroup, NULL);
   if (r == -1) {
     reply_with_error ("%s -> %s: %s", volgroup, newvolgroup, err);
@@ -719,7 +719,7 @@ get_lvm_field (const char *cmd, const char *field, const char *device)
   char *out;
   CLEANUP_FREE char *err = NULL;
   int r = command (&out, &err,
-                   str_lvm, cmd,
+                   "lvm", cmd,
                    "--unbuffered", "--noheadings", "-o", field,
                    device, NULL);
   if (r == -1) {
@@ -756,7 +756,7 @@ get_lvm_fields (const char *cmd, const char *field, const char *device)
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
   int r = command (&out, &err,
-                   str_lvm, cmd,
+                   "lvm", cmd,
                    "--unbuffered", "--noheadings", "-o", field,
                    device, NULL);
   if (r == -1) {
@@ -795,7 +795,7 @@ do_vgscan (void)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "vgscan", NULL);
+               "lvm", "vgscan", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -983,7 +983,7 @@ do_vgmeta (const char *vg, size_t *size_r)
 
   close (fd);
 
-  r = command (NULL, &err, str_lvm, "vgcfgbackup", "-f", tmp, vg, NULL);
+  r = command (NULL, &err, "lvm", "vgcfgbackup", "-f", tmp, vg, NULL);
   if (r == -1) {
     reply_with_error ("vgcfgbackup: %s", err);
     return NULL;
@@ -1056,7 +1056,7 @@ do_pvchange_uuid (const char *device)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "pvchange", "-u", device, NULL);
+               "lvm", "pvchange", "-u", device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
@@ -1074,7 +1074,7 @@ do_pvchange_uuid_all (void)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "pvchange", "-u", "-a", NULL);
+               "lvm", "pvchange", "-u", "-a", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -1092,7 +1092,7 @@ do_vgchange_uuid (const char *vg)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "vgchange", "-u", vg, NULL);
+               "lvm", "vgchange", "-u", vg, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", vg, err);
     return -1;
@@ -1110,7 +1110,7 @@ do_vgchange_uuid_all (void)
   int r;
 
   r = command (NULL, &err,
-               str_lvm, "vgchange", "-u", NULL);
+               "lvm", "vgchange", "-u", NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
diff --git a/daemon/md.c b/daemon/md.c
index 64d98fae5..7e6c067a3 100644
--- a/daemon/md.c
+++ b/daemon/md.c
@@ -37,12 +37,12 @@
 #include "optgroups.h"
 #include "c-ctype.h"
 
-GUESTFSD_EXT_CMD(str_mdadm, mdadm);
+DECLARE_EXTERNAL_COMMANDS ("mdadm");
 
 int
 optgroup_mdadm_available (void)
 {
-  return prog_exists (str_mdadm);
+  return prog_exists ("mdadm");
 }
 
 static size_t
@@ -138,7 +138,7 @@ do_md_create (const char *name, char *const *devices,
   const char *argv[MAX_ARGS];
   size_t i = 0;
 
-  ADD_ARG (argv, i, str_mdadm);
+  ADD_ARG (argv, i, "mdadm");
   ADD_ARG (argv, i, "--create");
   /* --run suppresses "Continue creating array" question */
   ADD_ARG (argv, i, "--run");
@@ -292,7 +292,7 @@ do_md_detail (const char *md)
 
   CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret);
 
-  const char *mdadm[] = { str_mdadm, "-D", "--export", md, NULL };
+  const char *mdadm[] = { "mdadm", "-D", "--export", md, NULL };
   r = commandv (&out, &err, mdadm);
   if (r == -1) {
     reply_with_error ("%s", err);
@@ -353,7 +353,7 @@ do_md_stop (const char *md)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  const char *mdadm[] = { str_mdadm, "--stop", md, NULL};
+  const char *mdadm[] = { "mdadm", "--stop", md, NULL};
   r = commandv (NULL, &err, mdadm);
   if (r == -1) {
     reply_with_error ("%s", err);
diff --git a/daemon/mkfs.c b/daemon/mkfs.c
index a376a1780..3f7fe84f5 100644
--- a/daemon/mkfs.c
+++ b/daemon/mkfs.c
@@ -30,8 +30,7 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_mke2fs, mke2fs);
-GUESTFSD_EXT_CMD(str_mkfs, mkfs);
+DECLARE_EXTERNAL_COMMANDS ("mke2fs", "mkfs")
 
 /* Takes optional arguments, consult optargs_bitmask. */
 int
@@ -55,9 +54,9 @@ do_mkfs (const char *fstype, const char *device, int blocksize,
    * option.
    */
   if (extfs)
-    ADD_ARG (argv, i, str_mke2fs);
+    ADD_ARG (argv, i, "mke2fs");
   else
-    ADD_ARG (argv, i, str_mkfs);
+    ADD_ARG (argv, i, "mkfs");
 
   ADD_ARG (argv, i, "-t");
   ADD_ARG (argv, i, fstype);
diff --git a/daemon/modprobe.c b/daemon/modprobe.c
index 0b7896ae7..dc0ad65c0 100644
--- a/daemon/modprobe.c
+++ b/daemon/modprobe.c
@@ -27,7 +27,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_modprobe, modprobe);
+DECLARE_EXTERNAL_COMMANDS ("modprobe")
 
 int
 optgroup_linuxmodules_available (void)
@@ -39,7 +39,7 @@ optgroup_linuxmodules_available (void)
   if (access ("/proc/modules", R_OK) == -1 && errno == ENOENT)
     return 0;
 
-  return prog_exists (str_modprobe);
+  return prog_exists ("modprobe");
 }
 
 int
@@ -48,7 +48,7 @@ do_modprobe (const char *module)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  r = command (NULL, &err, str_modprobe, module, NULL);
+  r = command (NULL, &err, "modprobe", module, NULL);
 
   if (r == -1) {
     reply_with_error ("%s", err);
diff --git a/daemon/mount.c b/daemon/mount.c
index 0ad9626a7..e001609ea 100644
--- a/daemon/mount.c
+++ b/daemon/mount.c
@@ -33,8 +33,7 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_mount, mount);
-GUESTFSD_EXT_CMD(str_umount, umount);
+DECLARE_EXTERNAL_COMMANDS ("mount", "umount")
 
 /* You must mount something on "/" first before many operations.
  * Hence we have an internal function which can test if something is
@@ -176,11 +175,11 @@ mount_vfs_nochroot (const char *options, const char *vfstype,
   int r;
   if (vfstype)
     r = command (NULL, &error,
-                 str_mount, "-o", options_plus ? options_plus : options,
+                 "mount", "-o", options_plus ? options_plus : options,
                  "-t", vfstype, device, mp, NULL);
   else
     r = command (NULL, &error,
-                 str_mount, "-o", options_plus ? options_plus : options,
+                 "mount", "-o", options_plus ? options_plus : options,
                  device, mp, NULL);
   if (r == -1) {
     reply_with_error ("%s on %s (options: '%s'): %s",
@@ -238,7 +237,7 @@ do_umount (const char *pathordevice,
   /* Use the external /bin/umount program, so that /etc/mtab is kept
    * updated.
    */
-  ADD_ARG (argv, i, str_umount);
+  ADD_ARG (argv, i, "umount");
 
   if (force)
     ADD_ARG (argv, i, "-f");
@@ -412,7 +411,7 @@ do_umount_all (void)
   for (i = 0; i < mounts.size; ++i) {
     CLEANUP_FREE char *err = NULL;
 
-    r = command (NULL, &err, str_umount, mounts.argv[i], NULL);
+    r = command (NULL, &err, "umount", mounts.argv[i], NULL);
     if (r == -1) {
       reply_with_error ("umount: %s: %s", mounts.argv[i], err);
       return -1;
@@ -445,7 +444,7 @@ do_mount_loop (const char *file, const char *mountpoint)
     return -1;
   }
 
-  r = command (NULL, &error, str_mount, "-o", "loop", buf, mp, NULL);
+  r = command (NULL, &error, "mount", "-o", "loop", buf, mp, NULL);
   if (r == -1) {
     reply_with_error ("%s on %s: %s", file, mountpoint, error);
     return -1;
@@ -480,7 +479,7 @@ do_remount (const char *mountpoint, int rw)
 
   /* XXX Do we need to check the mountpoint exists? */
 
-  r = command (NULL, &err, str_mount, "-o", options, mp, NULL);
+  r = command (NULL, &err, "mount", "-o", options, mp, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s: %s", mountpoint, options, err);
     return -1;
diff --git a/daemon/ntfs.c b/daemon/ntfs.c
index 3b759fef9..c13bcc70c 100644
--- a/daemon/ntfs.c
+++ b/daemon/ntfs.c
@@ -31,21 +31,19 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_ntfs3g_probe, ntfs-3g.probe);
-GUESTFSD_EXT_CMD(str_ntfsresize, ntfsresize);
-GUESTFSD_EXT_CMD(str_ntfsfix, ntfsfix);
-GUESTFSD_EXT_CMD(str_ntfslabel, ntfslabel);
+DECLARE_EXTERNAL_COMMANDS ("ntfs-3g.probe",
+                           "ntfsresize", "ntfsfix", "ntfslabel")
 
 int
 optgroup_ntfs3g_available (void)
 {
-  return prog_exists (str_ntfs3g_probe);
+  return prog_exists ("ntfs3g.probe");
 }
 
 int
 optgroup_ntfsprogs_available (void)
 {
-  return prog_exists (str_ntfsresize);
+  return prog_exists ("ntfsresize");
 }
 
 char *
@@ -56,7 +54,7 @@ ntfs_get_label (const char *device)
   char *out = NULL;
   size_t len;
 
-  r = command (&out, &err, str_ntfslabel, device, NULL);
+  r = command (&out, &err, "ntfslabel", device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     free (out);
@@ -81,7 +79,7 @@ ntfs_set_label (const char *device, const char *label)
    * characters and return an error.  This is not so easy since we
    * don't have the required libraries.
    */
-  r = command (NULL, &err, str_ntfslabel, device, label, NULL);
+  r = command (NULL, &err, "ntfslabel", device, label, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -99,7 +97,7 @@ do_ntfs_3g_probe (int rw, const char *device)
 
   rw_flag = rw ? "-w" : "-r";
 
-  r = commandr (NULL, &err, str_ntfs3g_probe, rw_flag, device, NULL);
+  r = commandr (NULL, &err, "ntfs3g.probe", rw_flag, device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
@@ -118,7 +116,7 @@ do_ntfsresize (const char *device, int64_t size, int force)
   size_t i = 0;
   char size_str[32];
 
-  ADD_ARG (argv, i, str_ntfsresize);
+  ADD_ARG (argv, i, "ntfsresize");
   ADD_ARG (argv, i, "-P");
 
   if (optargs_bitmask & GUESTFS_NTFSRESIZE_SIZE_BITMASK) {
@@ -170,7 +168,7 @@ ntfs_minimum_size (const char *device)
   int32_t cluster_size = 0;
 
   /* FS may be marked for check, so force ntfsresize */
-  r = command (&out, &err, str_ntfsresize, "--info", "-ff", device, NULL);
+  r = command (&out, &err, "ntfsresize", "--info", "-ff", device, NULL);
 
   lines = split_lines (out);
   if (lines == NULL)
@@ -249,7 +247,7 @@ do_ntfsfix (const char *device, int clearbadsectors)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  ADD_ARG (argv, i, str_ntfsfix);
+  ADD_ARG (argv, i, "ntfsfix");
 
   if ((optargs_bitmask & GUESTFS_NTFSFIX_CLEARBADSECTORS_BITMASK) &&
       clearbadsectors)
diff --git a/daemon/ntfsclone.c b/daemon/ntfsclone.c
index dc3ac278d..020ce89d5 100644
--- a/daemon/ntfsclone.c
+++ b/daemon/ntfsclone.c
@@ -32,7 +32,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_ntfsclone, ntfsclone);
+DECLARE_EXTERNAL_COMMANDS ("ntfsclone")
 
 /* Read the error file.  Returns a string that the caller must free. */
 static char *
@@ -83,7 +83,7 @@ do_ntfsclone_in (const char *device)
 
   /* Construct the command. */
   if (asprintf (&cmd, "%s -O %s --restore-image - 2> %s",
-                str_ntfsclone, device, error_file) == -1) {
+                "ntfsclone", device, error_file) == -1) {
     err = errno;
     r = cancel_receive ();
     errno = err;
@@ -161,8 +161,7 @@ do_ntfsclone_out (const char *device,
   }
 
   /* Construct the ntfsclone command. */
-  if (asprintf (&cmd, "%s -o - --save-image%s%s%s%s%s %s",
-                str_ntfsclone,
+  if (asprintf (&cmd, "ntfsclone -o - --save-image%s%s%s%s%s %s",
                 (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_METADATAONLY_BITMASK) && metadataonly ? " --metadata" : "",
                 (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_RESCUE_BITMASK) && rescue ? " --rescue" : "",
                 (optargs_bitmask & GUESTFS_NTFSCLONE_OUT_IGNOREFSCHECK_BITMASK) && ignorefscheck ? " --ignore-fs-check" : "",
diff --git a/daemon/parted.c b/daemon/parted.c
index 03e83cb32..f93d710c5 100644
--- a/daemon/parted.c
+++ b/daemon/parted.c
@@ -30,9 +30,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_parted, parted);
-GUESTFSD_EXT_CMD(str_sfdisk, sfdisk);
-GUESTFSD_EXT_CMD(str_sgdisk, sgdisk);
+DECLARE_EXTERNAL_COMMANDS ("parted", "sfdisk", "sgdisk")
 
 /* Notes:
  *
@@ -88,7 +86,7 @@ do_part_init (const char *device, const char *parttype)
   udev_settle ();
 
   r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                str_parted, "-s", "--", device, "mklabel", parttype, NULL);
+                "parted", "-s", "--", device, "mklabel", parttype, NULL);
   if (r == -1) {
     reply_with_error ("parted: %s: %s", device, err);
     return -1;
@@ -141,7 +139,7 @@ do_part_add (const char *device, const char *prlogex,
    * this as a bug in the parted mkpart command.
    */
   r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                str_parted, "-s", "--",
+                "parted", "-s", "--",
                 device, "mkpart", prlogex, startstr, endstr, NULL);
   if (r == -1) {
     reply_with_error ("parted: %s: %s", device, err);
@@ -170,7 +168,7 @@ do_part_del (const char *device, int partnum)
   udev_settle ();
 
   r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                str_parted, "-s", "--", device, "rm", partnum_str, NULL);
+                "parted", "-s", "--", device, "rm", partnum_str, NULL);
   if (r == -1) {
     reply_with_error ("parted: %s: %s", device, err);
     return -1;
@@ -209,7 +207,7 @@ do_part_disk (const char *device, const char *parttype)
   udev_settle ();
 
   r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                str_parted, "-s", "--",
+                "parted", "-s", "--",
                 device,
                 "mklabel", parttype,
                 /* See comment about about the parted mkpart command. */
@@ -243,7 +241,7 @@ do_part_set_bootable (const char *device, int partnum, int bootable)
   udev_settle ();
 
   r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                str_parted, "-s", "--",
+                "parted", "-s", "--",
                 device, "set", partstr, "boot", bootable ? "on" : "off", NULL);
   if (r == -1) {
     reply_with_error ("parted: %s: %s", device, err);
@@ -273,7 +271,7 @@ do_part_set_name (const char *device, int partnum, const char *name)
   udev_settle ();
 
   r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                str_parted, "-s", "--", device, "name", partstr, name, NULL);
+                "parted", "-s", "--", device, "name", partstr, name, NULL);
   if (r == -1) {
     reply_with_error ("parted: %s: %s", device, err);
     return -1;
@@ -323,11 +321,11 @@ print_partition_table (const char *device, bool add_m_option)
   udev_settle ();
 
   if (add_m_option)
-    r = command (&out, &err, str_parted, "-m", "-s", "--", device,
+    r = command (&out, &err, "parted", "-m", "-s", "--", device,
                  "unit", "b",
                  "print", NULL);
   else
-    r = command (&out, &err, str_parted, "-s", "--", device,
+    r = command (&out, &err, "parted", "-s", "--", device,
                  "unit", "b",
                  "print", NULL);
 
@@ -511,7 +509,7 @@ test_sfdisk_has_part_type (void)
   int r;
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
-  r = command (&out, &err, str_sfdisk, "--help", NULL);
+  r = command (&out, &err, "sfdisk", "--help", NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", "sfdisk --help", err);
     return -1;
@@ -545,7 +543,7 @@ do_part_get_mbr_id (const char *device, int partnum)
 
   udev_settle ();
 
-  r = command (&out, &err, str_sfdisk, param, device, partnum_str, NULL);
+  r = command (&out, &err, "sfdisk", param, device, partnum_str, NULL);
   if (r == -1) {
     reply_with_error ("sfdisk %s: %s", param, err);
     return -1;
@@ -585,7 +583,7 @@ do_part_set_mbr_id (const char *device, int partnum, int idbyte)
 
   udev_settle ();
 
-  r = command (NULL, &err, str_sfdisk,
+  r = command (NULL, &err, "sfdisk",
                param, device, partnum_str, idbyte_str, NULL);
   if (r == -1) {
     reply_with_error ("sfdisk %s: %s", param, err);
@@ -600,7 +598,7 @@ do_part_set_mbr_id (const char *device, int partnum, int idbyte)
 int
 optgroup_gdisk_available (void)
 {
-  return prog_exists (str_sgdisk);
+  return prog_exists ("sgdisk");
 }
 
 int
@@ -619,10 +617,10 @@ do_part_set_gpt_type (const char *device, int partnum, const char *guid)
 
   CLEANUP_FREE char *err = NULL;
   int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                    str_sgdisk, device, "-t", typecode, NULL);
+                    "sgdisk", device, "-t", typecode, NULL);
 
   if (r == -1) {
-    reply_with_error ("%s %s -t %s: %s", str_sgdisk, device, typecode, err);
+    reply_with_error ("%s %s -t %s: %s", "sgdisk", device, typecode, err);
     return -1;
   }
 
@@ -645,10 +643,10 @@ do_part_set_gpt_guid (const char *device, int partnum, const char *guid)
 
   CLEANUP_FREE char *err = NULL;
   int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                    str_sgdisk, device, "-u", typecode, NULL);
+                    "sgdisk", device, "-u", typecode, NULL);
 
   if (r == -1) {
-    reply_with_error ("%s %s -u %s: %s", str_sgdisk, device, typecode, err);
+    reply_with_error ("%s %s -u %s: %s", "sgdisk", device, typecode, err);
     return -1;
   }
 
@@ -674,10 +672,10 @@ sgdisk_info_extract_field (const char *device, int partnum, const char *field,
 
   CLEANUP_FREE char *err = NULL;
   int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                    str_sgdisk, device, "-i", partnum_str, NULL);
+                    "sgdisk", device, "-i", partnum_str, NULL);
 
   if (r == -1) {
-    reply_with_error ("%s %s -i %s: %s", str_sgdisk, device, partnum_str, err);
+    reply_with_error ("%s %s -i %s: %s", "sgdisk", device, partnum_str, err);
     return NULL;
   }
 
@@ -686,7 +684,7 @@ sgdisk_info_extract_field (const char *device, int partnum, const char *field,
   CLEANUP_FREE_STRING_LIST char **lines = split_lines (err);
   if (lines == NULL) {
     reply_with_error ("'%s %s -i %i' returned no output",
-                      str_sgdisk, device, partnum);
+                      "sgdisk", device, partnum);
     return NULL;
   }
 
@@ -946,16 +944,16 @@ do_part_get_disk_guid (const char *device)
 
   CLEANUP_FREE char *err = NULL;
   int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                    str_sgdisk, device, "-p", NULL);
+                    "sgdisk", device, "-p", NULL);
   if (r == -1) {
-    reply_with_error ("%s %s -p: %s", str_sgdisk, device, err);
+    reply_with_error ("%s %s -p: %s", "sgdisk", device, err);
     return NULL;
   }
 
   CLEANUP_FREE_STRING_LIST char **lines = split_lines (err);
   if (lines == NULL) {
     reply_with_error ("'%s %s -p' returned no output",
-                      str_sgdisk, device);
+                      "sgdisk", device);
     return NULL;
   }
 
@@ -988,10 +986,10 @@ do_part_set_disk_guid (const char *device, const char *guid)
 {
   CLEANUP_FREE char *err = NULL;
   int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                    str_sgdisk, device, "-U", guid, NULL);
+                    "sgdisk", device, "-U", guid, NULL);
 
   if (r == -1) {
-    reply_with_error ("%s %s -U %s: %s", str_sgdisk, device, guid, err);
+    reply_with_error ("%s %s -U %s: %s", "sgdisk", device, guid, err);
     return -1;
   }
 
@@ -1003,10 +1001,10 @@ do_part_set_disk_guid_random (const char *device)
 {
   CLEANUP_FREE char *err = NULL;
   int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                    str_sgdisk, device, "-U", "R", NULL);
+                    "sgdisk", device, "-U", "R", NULL);
 
   if (r == -1) {
-    reply_with_error ("%s %s -U R: %s", str_sgdisk, device, err);
+    reply_with_error ("%s %s -U R: %s", "sgdisk", device, err);
     return -1;
   }
 
@@ -1022,25 +1020,25 @@ do_part_expand_gpt(const char *device)
    * (e.g. recreate partition table and so on).
    * We do not want such behavior, so dry-run at first.*/
   int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                    str_sgdisk, "--pretend", "-e", device, NULL);
+                    "sgdisk", "--pretend", "-e", device, NULL);
 
   if (r == -1) {
-    reply_with_error ("%s --pretend -e %s: %s", str_sgdisk, device, err);
+    reply_with_error ("%s --pretend -e %s: %s", "sgdisk", device, err);
     return -1;
   }
   if (err && strlen(err) != 0) {
     /* Unexpected actions. */
-    reply_with_error ("%s --pretend -e %s: %s", str_sgdisk, device, err);
+    reply_with_error ("%s --pretend -e %s: %s", "sgdisk", device, err);
     return -1;
   }
   free(err);
 
   /* Now we can do a real run. */
   r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
-                str_sgdisk, "-e", device, NULL);
+                "sgdisk", "-e", device, NULL);
 
   if (r == -1) {
-    reply_with_error ("%s -e %s: %s", str_sgdisk, device, err);
+    reply_with_error ("%s -e %s: %s", "sgdisk", device, err);
     return -1;
   }
 
diff --git a/daemon/rsync.c b/daemon/rsync.c
index d238d6174..6c3531d39 100644
--- a/daemon/rsync.c
+++ b/daemon/rsync.c
@@ -30,12 +30,12 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_rsync, rsync);
+DECLARE_EXTERNAL_COMMANDS ("rsync")
 
 int
 optgroup_rsync_available (void)
 {
-  return prog_exists (str_rsync);
+  return prog_exists ("rsync");
 }
 
 static int
@@ -48,7 +48,7 @@ rsync (const char *src, const char *src_orig,
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  ADD_ARG (argv, i, str_rsync);
+  ADD_ARG (argv, i, "rsync");
 
   if (archive)
     ADD_ARG (argv, i, "--archive");
diff --git a/daemon/scrub.c b/daemon/scrub.c
index c10b0a0c1..a48a2786a 100644
--- a/daemon/scrub.c
+++ b/daemon/scrub.c
@@ -28,12 +28,12 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_scrub, scrub);
+DECLARE_EXTERNAL_COMMANDS ("scrub")
 
 int
 optgroup_scrub_available (void)
 {
-  return prog_exists (str_scrub);
+  return prog_exists ("scrub");
 }
 
 int
@@ -42,7 +42,7 @@ do_scrub_device (const char *device)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  r = command (NULL, &err, str_scrub, device, NULL);
+  r = command (NULL, &err, "scrub", device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
@@ -68,7 +68,7 @@ do_scrub_file (const char *file)
     return -1;
   }
 
-  r = command (NULL, &err, str_scrub, "-r", buf, NULL);
+  r = command (NULL, &err, "scrub", "-r", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", file, err);
     return -1;
@@ -91,7 +91,7 @@ do_scrub_freespace (const char *dir)
     return -1;
   }
 
-  r = command (NULL, &err, str_scrub, "-X", buf, NULL);
+  r = command (NULL, &err, "scrub", "-X", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", dir, err);
     return -1;
diff --git a/daemon/selinux-relabel.c b/daemon/selinux-relabel.c
index fa80cec7a..9e73659d7 100644
--- a/daemon/selinux-relabel.c
+++ b/daemon/selinux-relabel.c
@@ -29,14 +29,14 @@
 
 #include "ignore-value.h"
 
-GUESTFSD_EXT_CMD(str_setfiles, setfiles);
+DECLARE_EXTERNAL_COMMANDS ("setfiles")
 
 #define MAX_ARGS 64
 
 int
 optgroup_selinuxrelabel_available (void)
 {
-  return prog_exists (str_setfiles);
+  return prog_exists ("setfiles");
 }
 
 static int
@@ -46,7 +46,7 @@ setfiles_has_m_option (void)
   CLEANUP_FREE char *err = NULL;
 
   if (flag == -1) {
-    ignore_value (command (NULL, &err, str_setfiles, "-m", NULL));
+    ignore_value (command (NULL, &err, "setfiles", "-m", NULL));
     flag = err && strstr (err, /* "invalid option -- " */ "'m'") == NULL;
   }
 
@@ -86,7 +86,7 @@ do_selinux_relabel (const char *specfile, const char *path,
    * observations to the bug report:
    * https://bugzilla.redhat.com/show_bug.cgi?id=1396297
    */
-  ADD_ARG (argv, i, str_setfiles);
+  ADD_ARG (argv, i, "setfiles");
   if (force)
     ADD_ARG (argv, i, "-F");
 
diff --git a/daemon/sfdisk.c b/daemon/sfdisk.c
index e32e63707..ce86666d8 100644
--- a/daemon/sfdisk.c
+++ b/daemon/sfdisk.c
@@ -28,8 +28,7 @@
 #include "daemon.h"
 #include "actions.h"
 
-GUESTFSD_EXT_CMD(str_sfdisk, sfdisk);
-GUESTFSD_EXT_CMD(str_blockdev, blockdev);
+DECLARE_EXTERNAL_COMMANDS ("sfdisk", "blockdev")
 
 static int
 sfdisk (const char *device, int n, int cyls, int heads, int sectors,
@@ -40,7 +39,7 @@ sfdisk (const char *device, int n, int cyls, int heads, int sectors,
   char buf[256];
   int i;
 
-  strcpy (buf, str_sfdisk);
+  strcpy (buf, "sfdisk");
 
   if (n > 0)
     sprintf (buf + strlen (buf), " -N %d", n);
@@ -103,7 +102,7 @@ sfdisk (const char *device, int n, int cyls, int heads, int sectors,
    * other component.  In any case, reread the partition table
    * unconditionally here.
    */
-  (void) command (NULL, NULL, str_blockdev, "--rereadpt", device, NULL);
+  (void) command (NULL, NULL, "blockdev", "--rereadpt", device, NULL);
 
   udev_settle ();
 
@@ -139,7 +138,7 @@ sfdisk_flag (const char *device, const char *flag)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  r = command (&out, &err, str_sfdisk, flag, device, NULL);
+  r = command (&out, &err, "sfdisk", flag, device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     free (out);
diff --git a/daemon/sh.c b/daemon/sh.c
index baebd3960..afb98e292 100644
--- a/daemon/sh.c
+++ b/daemon/sh.c
@@ -32,9 +32,7 @@
 
 #include "ignore-value.h"
 
-GUESTFSD_EXT_CMD(str_cp, cp);
-GUESTFSD_EXT_CMD(str_mount, mount);
-GUESTFSD_EXT_CMD(str_umount, umount);
+DECLARE_EXTERNAL_COMMANDS ("cp", "mount", "umount")
 
 #ifdef HAVE_ATTRIBUTE_CLEANUP
 #define CLEANUP_BIND_STATE __attribute__((cleanup(free_bind_state)))
@@ -100,20 +98,20 @@ bind_mount (struct bind_state *bs)
    * However I have not found a reliable way to unmount the same set
    * of directories (umount -R does NOT work).
    */
-  r = command (NULL, NULL, str_mount, "--bind", "/dev", bs->sysroot_dev, NULL);
+  r = command (NULL, NULL, "mount", "--bind", "/dev", bs->sysroot_dev, NULL);
   bs->dev_ok = r != -1;
-  r = command (NULL, NULL, str_mount, "--bind", "/dev/pts", bs->sysroot_dev_pts, NULL);
+  r = command (NULL, NULL, "mount", "--bind", "/dev/pts", bs->sysroot_dev_pts, NULL);
   bs->dev_pts_ok = r != -1;
-  r = command (NULL, NULL, str_mount, "--bind", "/proc", bs->sysroot_proc, NULL);
+  r = command (NULL, NULL, "mount", "--bind", "/proc", bs->sysroot_proc, NULL);
   bs->proc_ok = r != -1;
   /* Note on the next line we have to bind-mount /sys/fs/selinux (appliance
    * kernel) on top of /selinux (where guest is expecting selinux).
    */
-  r = command (NULL, NULL, str_mount, "--bind", "/sys/fs/selinux", bs->sysroot_selinux, NULL);
+  r = command (NULL, NULL, "mount", "--bind", "/sys/fs/selinux", bs->sysroot_selinux, NULL);
   bs->selinux_ok = r != -1;
-  r = command (NULL, NULL, str_mount, "--bind", "/sys", bs->sysroot_sys, NULL);
+  r = command (NULL, NULL, "mount", "--bind", "/sys", bs->sysroot_sys, NULL);
   bs->sys_ok = r != -1;
-  r = command (NULL, NULL, str_mount, "--bind", "/sys/fs/selinux", bs->sysroot_sys_fs_selinux, NULL);
+  r = command (NULL, NULL, "mount", "--bind", "/sys/fs/selinux", bs->sysroot_sys_fs_selinux, NULL);
   bs->sys_fs_selinux_ok = r != -1;
 
   bs->mounted = true;
@@ -124,7 +122,7 @@ bind_mount (struct bind_state *bs)
 static inline void
 umount_ignore_fail (const char *path)
 {
-  ignore_value (command (NULL, NULL, str_umount, path, NULL));
+  ignore_value (command (NULL, NULL, "umount", path, NULL));
 }
 
 static void
@@ -205,7 +203,7 @@ set_up_etc_resolv_conf (struct resolver_state *rs)
   /* Now that the guest's <sysroot>/etc/resolv.conf is out the way, we
    * can create our own copy of the appliance /etc/resolv.conf.
    */
-  ignore_value (command (NULL, NULL, str_cp, "/etc/resolv.conf",
+  ignore_value (command (NULL, NULL, "cp", "/etc/resolv.conf",
                          rs->sysroot_etc_resolv_conf, NULL));
 
   rs->mounted = true;
diff --git a/daemon/sleuthkit.c b/daemon/sleuthkit.c
index bdbdb0f89..bde506ac4 100644
--- a/daemon/sleuthkit.c
+++ b/daemon/sleuthkit.c
@@ -31,8 +31,7 @@
 
 static int send_command_output (const char *cmd);
 
-GUESTFSD_EXT_CMD(str_icat, icat);
-GUESTFSD_EXT_CMD(str_blkls, blkls);
+DECLARE_EXTERNAL_COMMANDS ("icat", "blkls")
 
 int
 do_download_inode (const mountable_t *mountable, int64_t inode)
@@ -47,8 +46,8 @@ do_download_inode (const mountable_t *mountable, int64_t inode)
   }
 
   /* Construct the command. */
-  ret = asprintf (&cmd, "%s -r %s %" PRIi64,
-                  str_icat, mountable->device, inode);
+  ret = asprintf (&cmd, "icat -r %s %" PRIi64,
+                  mountable->device, inode);
   if (ret < 0) {
     reply_with_perror ("asprintf");
     return -1;
@@ -84,8 +83,8 @@ do_download_blocks (const mountable_t *mountable, int64_t start, int64_t stop,
     params = "";
 
   /* Construct the command. */
-  ret = asprintf (&cmd, "%s %s %s %" PRIi64 "-%" PRIi64,
-                  str_blkls, mountable->device, params, start, stop);
+  ret = asprintf (&cmd, "blkls %s %s %" PRIi64 "-%" PRIi64,
+                  mountable->device, params, start, stop);
   if (ret < 0) {
     reply_with_perror ("asprintf");
     return -1;
@@ -155,5 +154,5 @@ send_command_output (const char *cmd)
 int
 optgroup_sleuthkit_available (void)
 {
-  return prog_exists (str_icat);
+  return prog_exists ("icat");
 }
diff --git a/daemon/squashfs.c b/daemon/squashfs.c
index c30777661..0564222ff 100644
--- a/daemon/squashfs.c
+++ b/daemon/squashfs.c
@@ -29,12 +29,12 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_mksquashfs, mksquashfs);
+DECLARE_EXTERNAL_COMMANDS ("mksquashfs")
 
 int
 optgroup_squashfs_available (void)
 {
-  return prog_exists (str_mksquashfs);
+  return prog_exists ("mksquashfs");
 }
 
 /* Takes optional arguments, consult optargs_bitmask. */
@@ -80,7 +80,7 @@ do_mksquashfs (const char *path, const char *compress, char *const *excludes)
     return -1;
   }
 
-  ADD_ARG (argv, i, str_mksquashfs);
+  ADD_ARG (argv, i, "mksquashfs");
   ADD_ARG (argv, i, buf);
   ADD_ARG (argv, i, tmpfile);
   ADD_ARG (argv, i, "-noappend");
diff --git a/daemon/swap.c b/daemon/swap.c
index fea4a9dcf..30b37d9a6 100644
--- a/daemon/swap.c
+++ b/daemon/swap.c
@@ -31,10 +31,7 @@
 
 #include "ignore-value.h"
 
-GUESTFSD_EXT_CMD(str_mkswap, mkswap);
-GUESTFSD_EXT_CMD(str_swapon, swapon);
-GUESTFSD_EXT_CMD(str_swapoff, swapoff);
-GUESTFSD_EXT_CMD(str_swaplabel, swaplabel);
+DECLARE_EXTERNAL_COMMANDS ("mkswap", "swapon", "swapoff", "swaplabel")
 
 /* Confirmed this is true for Linux swap partitions from the Linux sources. */
 #define SWAP_LABEL_MAX 16
@@ -55,7 +52,7 @@ do_mkswap (const char *device, const char *label, const char *uuid)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  ADD_ARG (argv, i, str_mkswap);
+  ADD_ARG (argv, i, "mkswap");
   ADD_ARG (argv, i, "-f");
 
   if (optargs_bitmask & GUESTFS_MKSWAP_LABEL_BITMASK) {
@@ -117,7 +114,7 @@ do_mkswap_file (const char *path)
     return -1;
   }
 
-  r = command (NULL, &err, str_mkswap, "-f", buf, NULL);
+  r = command (NULL, &err, "mkswap", "-f", buf, NULL);
 
   if (r == -1) {
     reply_with_error ("%s: %s", path, err);
@@ -154,13 +151,13 @@ swaponoff (const char *cmd, const char *flag, const char *value)
 int
 do_swapon_device (const char *device)
 {
-  return swaponoff (str_swapon, NULL, device);
+  return swaponoff ("swapon", NULL, device);
 }
 
 int
 do_swapoff_device (const char *device)
 {
-  return swaponoff (str_swapoff, NULL, device);
+  return swaponoff ("swapoff", NULL, device);
 }
 
 int
@@ -174,7 +171,7 @@ do_swapon_file (const char *path)
     return -1;
   }
 
-  return swaponoff (str_swapon, NULL, buf);
+  return swaponoff ("swapon", NULL, buf);
 }
 
 int
@@ -188,7 +185,7 @@ do_swapoff_file (const char *path)
     return -1;
   }
 
-  return swaponoff (str_swapoff, NULL, buf);
+  return swaponoff ("swapoff", NULL, buf);
 }
 
 int
@@ -200,7 +197,7 @@ do_swapon_label (const char *label)
     return -1;
   }
 
-  return swaponoff (str_swapon, "-L", label);
+  return swaponoff ("swapon", "-L", label);
 }
 
 int
@@ -212,19 +209,19 @@ do_swapoff_label (const char *label)
     return -1;
   }
 
-  return swaponoff (str_swapoff, "-L", label);
+  return swaponoff ("swapoff", "-L", label);
 }
 
 int
 do_swapon_uuid (const char *uuid)
 {
-  return swaponoff (str_swapon, "-U", uuid);
+  return swaponoff ("swapon", "-U", uuid);
 }
 
 int
 do_swapoff_uuid (const char *uuid)
 {
-  return swaponoff (str_swapoff, "-U", uuid);
+  return swaponoff ("swapoff", "-U", uuid);
 }
 
 int
@@ -233,7 +230,7 @@ swap_set_uuid (const char *device, const char *uuid)
   int r;
   CLEANUP_FREE char *err = NULL;
 
-  r = command (NULL, &err, str_swaplabel, "-U", uuid, device, NULL);
+  r = command (NULL, &err, "swaplabel", "-U", uuid, device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
@@ -254,7 +251,7 @@ swap_set_label (const char *device, const char *label)
     return -1;
   }
 
-  r = command (NULL, &err, str_swaplabel, "-L", label, device, NULL);
+  r = command (NULL, &err, "swaplabel", "-L", label, device, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return -1;
diff --git a/daemon/syslinux.c b/daemon/syslinux.c
index 08da170d5..e2a4ef980 100644
--- a/daemon/syslinux.c
+++ b/daemon/syslinux.c
@@ -28,19 +28,18 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_syslinux, syslinux);
-GUESTFSD_EXT_CMD(str_extlinux, extlinux);
+DECLARE_EXTERNAL_COMMANDS ("syslinux", "extlinux")
 
 int
 optgroup_syslinux_available (void)
 {
-  return prog_exists (str_syslinux);
+  return prog_exists ("syslinux");
 }
 
 int
 optgroup_extlinux_available (void)
 {
-  return prog_exists (str_extlinux);
+  return prog_exists ("extlinux");
 }
 
 /* Takes optional arguments, consult optargs_bitmask. */
@@ -53,7 +52,7 @@ do_syslinux (const char *device, const char *directory)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  ADD_ARG (argv, i, str_syslinux);
+  ADD_ARG (argv, i, "syslinux");
   ADD_ARG (argv, i, "--install");
   ADD_ARG (argv, i, "--force");
 
@@ -86,7 +85,7 @@ do_extlinux (const char *directory)
     return -1;
   }
 
-  r = command (NULL, &err, str_extlinux, "--install", buf, NULL);
+  r = command (NULL, &err, "extlinux", "--install", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", directory, err);
     return -1;
diff --git a/daemon/tar.c b/daemon/tar.c
index c23aa0a86..85c5b8bca 100644
--- a/daemon/tar.c
+++ b/daemon/tar.c
@@ -35,7 +35,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_tar, tar);
+DECLARE_EXTERNAL_COMMANDS ("tar")
 
 int
 optgroup_xz_available (void)
@@ -187,7 +187,7 @@ do_tar_in (const char *dir, const char *compress, int xattrs, int selinux, int a
 
   /* "tar -C /sysroot%s -xf -" but we have to quote the dir. */
   if (asprintf_nowarn (&cmd, "%s -C %R%s -xf - %s%s%s%s2> %s",
-                       str_tar,
+                       "tar",
                        dir, filter,
                        chown_supported ? "" : "--no-same-owner ",
                        xattrs ? "--xattrs " : "",
@@ -344,7 +344,7 @@ do_tar_out (const char *dir, const char *compress, int numericowner,
 
   /* "tar -C /sysroot%s -cf - ." but we have to quote the dir. */
   if (asprintf_nowarn (&cmd, "%s -C %Q%s%s%s%s%s%s%s -cf - .",
-                       str_tar,
+                       "tar",
                        buf, filter,
                        numericowner ? " --numeric-owner" : "",
                        exclude_from_file ? " -X " : "",
diff --git a/daemon/test-print-external-commands.sh b/daemon/test-print-external-commands.sh
new file mode 100755
index 000000000..e18f435b1
--- /dev/null
+++ b/daemon/test-print-external-commands.sh
@@ -0,0 +1,23 @@
+#!/bin/bash -
+# libguestfs
+# Copyright (C) 2009-2017 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+set -e
+
+$TEST_FUNCTIONS
+
+$VG ./guestfsd --print-external-commands
diff --git a/daemon/xfs.c b/daemon/xfs.c
index a0d08b2c9..4f826ba61 100644
--- a/daemon/xfs.c
+++ b/daemon/xfs.c
@@ -32,16 +32,13 @@
 
 #define MAX_ARGS 64
 
-GUESTFSD_EXT_CMD(str_mkfs_xfs, mkfs.xfs);
-GUESTFSD_EXT_CMD(str_xfs_admin, xfs_admin);
-GUESTFSD_EXT_CMD(str_xfs_info, xfs_info);
-GUESTFSD_EXT_CMD(str_xfs_growfs, xfs_growfs);
-GUESTFSD_EXT_CMD(str_xfs_repair, xfs_repair);
+DECLARE_EXTERNAL_COMMANDS ("mkfs.xfs",
+                           "xfs_admin", "xfs_info", "xfs_growfs", "xfs_repair")
 
 int
 optgroup_xfs_available (void)
 {
-  return prog_exists (str_mkfs_xfs);
+  return prog_exists ("mkfs.xfs");
 }
 
 /* Return everything up to the first comma, equals or space in the input
@@ -341,7 +338,7 @@ do_xfs_info (const char *pathordevice)
     return NULL;
   }
 
-  r = command (&out, &err, str_xfs_info, buf, NULL);
+  r = command (&out, &err, "xfs_info", buf, NULL);
   if (r == -1) {
     reply_with_error ("%s", err);
     return NULL;
@@ -376,7 +373,7 @@ do_xfs_growfs (const char *path,
     return -1;
   }
 
-  ADD_ARG (argv, i, str_xfs_growfs);
+  ADD_ARG (argv, i, "xfs_growfs");
 
   /* Optional arguments */
   if (!(optargs_bitmask & GUESTFS_XFS_GROWFS_DATASEC_BITMASK))
@@ -487,7 +484,7 @@ do_xfs_admin (const char *device,
   const char *argv[MAX_ARGS];
   size_t i = 0;
 
-  ADD_ARG (argv, i, str_xfs_admin);
+  ADD_ARG (argv, i, "xfs_admin");
 
   /* Optional arguments */
   if (!(optargs_bitmask & GUESTFS_XFS_ADMIN_EXTUNWRITTEN_BITMASK))
@@ -564,7 +561,7 @@ do_xfs_repair (const char *device,
   size_t i = 0;
   int is_device;
 
-  ADD_ARG (argv, i, str_xfs_repair);
+  ADD_ARG (argv, i, "xfs_repair");
 
   /* Optional arguments */
   if (optargs_bitmask & GUESTFS_XFS_REPAIR_FORCELOGZERO_BITMASK) {
diff --git a/daemon/zero.c b/daemon/zero.c
index 5166807b5..e9126b757 100644
--- a/daemon/zero.c
+++ b/daemon/zero.c
@@ -32,7 +32,7 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_wipefs, wipefs);
+DECLARE_EXTERNAL_COMMANDS ("wipefs")
 
 static const char zero_buf[4096];
 
@@ -81,7 +81,7 @@ do_zero (const char *device)
 int
 optgroup_wipefs_available (void)
 {
-  return prog_exists (str_wipefs);
+  return prog_exists ("wipefs");
 }
 
 /* See RHBZ#872831 */
@@ -93,7 +93,7 @@ wipefs_has_force_option (void)
   CLEANUP_FREE char *out = NULL, *err = NULL;
 
   if (flag == -1) {
-    r = command (&out, &err, str_wipefs, "--help", NULL);
+    r = command (&out, &err, "wipefs", "--help", NULL);
     if (r == -1) {
       reply_with_error ("%s", err);
       return -1;
@@ -118,7 +118,7 @@ do_wipefs (const char *device)
   if (force == -1)
     return -1;
 
-  ADD_ARG (argv, i, str_wipefs);
+  ADD_ARG (argv, i, "wipefs");
   ADD_ARG (argv, i, "-a");
   if (force)
     ADD_ARG (argv, i, "--force");
@@ -379,7 +379,7 @@ wipe_device_before_mkfs (const char *device)
   if (force == -1)
     return;
 
-  ADD_ARG (argv, i, str_wipefs);
+  ADD_ARG (argv, i, "wipefs");
   ADD_ARG (argv, i, "-a");
   if (force)
     ADD_ARG (argv, i, "--force");
diff --git a/daemon/zerofree.c b/daemon/zerofree.c
index 4ba17dd81..b7bcb48fc 100644
--- a/daemon/zerofree.c
+++ b/daemon/zerofree.c
@@ -28,12 +28,12 @@
 #include "actions.h"
 #include "optgroups.h"
 
-GUESTFSD_EXT_CMD(str_zerofree, zerofree);
+DECLARE_EXTERNAL_COMMANDS ("zerofree")
 
 int
 optgroup_zerofree_available (void)
 {
-  return prog_exists (str_zerofree);
+  return prog_exists ("zerofree");
 }
 
 int
@@ -42,7 +42,7 @@ do_zerofree (const char *device)
   CLEANUP_FREE char *err = NULL;
   int r;
 
-  r = command (NULL, &err, str_zerofree, device, NULL);
+  r = command (NULL, &err, "zerofree", device, NULL);
   if (r == -1) {
     reply_with_error ("%s: %s", device, err);
     return -1;
-- 
2.13.2




More information about the Libguestfs mailing list