+ int r;
+ char *out;
+ size_t size;
+ FILE *fp;
+ DIR *dir;
+ struct dirent *d;
+ char fname[256], link[256];
+ struct stat statbuf;
+
+ fp = open_memstream (&out, &size);
+ if (!fp) {
+ reply_with_perror ("open_memstream");
+ return NULL;
+ }
+
+ dir = opendir ("/proc/self/fd");
+ if (!dir) {
+ reply_with_perror ("opendir: /proc/self/fd");
+ fclose (fp);
+ return NULL;
+ }
+
+ while ((d = readdir (dir)) != NULL) {
+ if (STREQ (d->d_name, ".") || STREQ (d->d_name, ".."))
+ continue;
+
+ snprintf (fname, sizeof fname, "/proc/self/fd/%s", d->d_name);
+
+ r = lstat (fname, &statbuf);
+ if (r == -1) {
+ reply_with_perror ("stat: %s", fname);
+ fclose (fp);
+ free (out);
+ closedir (dir);
+ return NULL;
+ }
+
+ if (S_ISLNK (statbuf.st_mode)) {
+ r = readlink (fname, link, sizeof link - 1);
+ if (r == -1) {
+ reply_with_perror ("readline: %s", fname);
+ fclose (fp);
+ free (out);
+ closedir (dir);
+ return NULL;
+ }
+ link[r] = '\0';
+
+ fprintf (fp, "%2s %s\n", d->d_name, link);
+ } else
+ fprintf (fp, "%2s 0%o\n", d->d_name, statbuf.st_mode);
+ }
+
+ fclose (fp);
+
+ if (closedir (dir) == -1) {
+ reply_with_perror ("closedir");
+ free (out);
+ return NULL;
+ }
+
+ return out;
+}
+
+/* Force a segfault in the daemon. */
+static char *
+debug_segv (const char *subcmd, int argc, char *const *const argv)
+{
+ *(int*)0 = 0;
+ return NULL;
+}
+
+/* Run an arbitrary shell command using /bin/sh from the appliance.
+ *
+ * Note this is somewhat different from the ordinary guestfs_sh command
+ * because it's not using the guest shell, and is not chrooted.
+ */
+static char *
+debug_sh (const char *subcmd, int argc, char *const *const argv)
+{
+ if (argc < 1) {
+ reply_with_error ("sh: expecting a command to run");
+ return NULL;
+ }
+
+ char *cmd;
+ int len, i, j;
+
+ /* guestfish splits the parameter(s) into a list of strings,
+ * and we have to reassemble them here. Not ideal. XXX
+ */
+ for (i = len = 0; i < argc; ++i)
+ len += strlen (argv[i]) + 1;
+ cmd = malloc (len);
+ if (!cmd) {
+ reply_with_perror ("malloc");
+ return NULL;
+ }
+ for (i = j = 0; i < argc; ++i) {
+ len = strlen (argv[i]);
+ memcpy (&cmd[j], argv[i], len);
+ j += len;
+ cmd[j] = ' ';
+ j++;
+ }
+ cmd[j-1] = '\0';
+
+ /* Set up some environment variables. */
+ setenv ("root", sysroot, 1);
+ if (access ("/sys/block/sda", F_OK) == 0)
+ setenv ("sd", "sd", 1);
+ else if (access ("/sys/block/hda", F_OK) == 0)
+ setenv ("sd", "hd", 1);
+ else if (access ("/sys/block/vda", F_OK) == 0)
+ setenv ("sd", "vd", 1);
+
+ char *err;
+ int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR,
+ "/bin/sh", "-c", cmd, NULL);
+ free (cmd);
+
+ if (r == -1) {
+ reply_with_error ("%s", err);
+ free (err);
+ return NULL;
+ }
+
+ return err;
+}
+
+/* Print the environment that commands get (by running external printenv). */
+static char *
+debug_env (const char *subcmd, int argc, char *const *const argv)
+{
+ int r;
+ char *out, *err;
+
+ r = command (&out, &err, "printenv", NULL);
+ if (r == -1) {
+ reply_with_error ("printenv: %s", err);
+ free (out);
+ free (err);
+ return NULL;
+ }
+
+ free (err);
+
+ return out;
+}
+
+/* List files in the appliance. */
+static char *
+debug_ls (const char *subcmd, int argc, char *const *const argv)
+{
+ int len = count_strings (argv);
+ const char *cargv[len+3];
+ int i;
+
+ cargv[0] = "ls";
+ cargv[1] = "-a";
+ for (i = 0; i < len; ++i)
+ cargv[2+i] = argv[i];
+ cargv[2+len] = NULL;
+
+ int r;
+ char *out, *err;
+
+ r = commandv (&out, &err, (void *) cargv);
+ if (r == -1) {
+ reply_with_error ("ls: %s", err);
+ free (out);
+ free (err);
+ return NULL;
+ }
+
+ free (err);
+
+ return out;
+}
+
+/* List files in the appliance. */
+static char *
+debug_ll (const char *subcmd, int argc, char *const *const argv)
+{
+ int len = count_strings (argv);
+ const char *cargv[len+3];
+ int i;
+
+ cargv[0] = "ls";
+ cargv[1] = "-la";
+ for (i = 0; i < len; ++i)
+ cargv[2+i] = argv[i];
+ cargv[2+len] = NULL;
+
+ int r;
+ char *out, *err;
+
+ r = commandv (&out, &err, (void *) cargv);
+ if (r == -1) {
+ reply_with_error ("ll: %s", err);
+ free (out);
+ free (err);
+ return NULL;
+ }
+
+ free (err);
+
+ return out;