+
+/* Split an output string into a NULL-terminated list of lines.
+ * Typically this is used where we have run an external command
+ * which has printed out a list of things, and we want to return
+ * an actual list.
+ *
+ * The corner cases here are quite tricky. Note in particular:
+ *
+ * "" -> []
+ * "\n" -> [""]
+ * "a\nb" -> ["a"; "b"]
+ * "a\nb\n" -> ["a"; "b"]
+ * "a\nb\n\n" -> ["a"; "b"; ""]
+ *
+ * The original string is written over and destroyed by this
+ * function (which is usually OK because it's the 'out' string
+ * from command()). You can free the original string, because
+ * add_string() strdups the strings.
+ */
+char **
+split_lines (char *str)
+{
+ char **lines = NULL;
+ int size = 0, alloc = 0;
+ char *p, *pend;
+
+ if (STREQ (str, ""))
+ goto empty_list;
+
+ p = str;
+ while (p) {
+ /* Empty last line? */
+ if (p[0] == '\0')
+ break;
+
+ pend = strchr (p, '\n');
+ if (pend) {
+ *pend = '\0';
+ pend++;
+ }
+
+ if (add_string (&lines, &size, &alloc, p) == -1) {
+ return NULL;
+ }
+
+ p = pend;
+ }
+
+ empty_list:
+ if (add_string (&lines, &size, &alloc, NULL) == -1)
+ return NULL;
+
+ return lines;
+}
+
+/* Skip leading and trailing whitespace, updating the original string
+ * in-place.
+ */
+void
+trim (char *str)
+{
+ size_t len = strlen (str);
+
+ while (len > 0 && c_isspace (str[len-1])) {
+ str[len-1] = '\0';
+ len--;
+ }
+
+ const char *p = str;
+ while (*p && c_isspace (*p)) {
+ p++;
+ len--;
+ }
+
+ memmove (str, p, len+1);
+}
+
+/* printf helper function so we can use %Q ("quoted") and %R to print
+ * shell-quoted strings. See guestfs(3)/EXTENDING LIBGUESTFS for more
+ * details.
+ */
+static int
+print_shell_quote (FILE *stream,
+ const struct printf_info *info ATTRIBUTE_UNUSED,
+ const void *const *args)
+{
+#define SAFE(c) (c_isalnum((c)) || \
+ (c) == '/' || (c) == '-' || (c) == '_' || (c) == '.')
+ int i, len;
+ const char *str = *((const char **) (args[0]));
+
+ for (i = len = 0; str[i]; ++i) {
+ if (!SAFE(str[i])) {
+ putc ('\\', stream);
+ len ++;
+ }
+ putc (str[i], stream);
+ len ++;
+ }
+
+ return len;
+}
+
+static int
+print_sysroot_shell_quote (FILE *stream,
+ const struct printf_info *info,
+ const void *const *args)
+{
+ fputs (sysroot, stream);
+ return sysroot_len + print_shell_quote (stream, info, args);
+}
+
+#ifdef HAVE_REGISTER_PRINTF_SPECIFIER
+static int
+print_arginfo (const struct printf_info *info ATTRIBUTE_UNUSED,
+ size_t n, int *argtypes, int *size)
+{
+ if (n > 0) {
+ argtypes[0] = PA_STRING;
+ size[0] = sizeof (const char *);
+ }
+ return 1;
+}
+#else
+#ifdef HAVE_REGISTER_PRINTF_FUNCTION
+static int
+print_arginfo (const struct printf_info *info, size_t n, int *argtypes)
+{
+ if (n > 0)
+ argtypes[0] = PA_STRING;
+ return 1;
+}
+#else
+#error "HAVE_REGISTER_PRINTF_{SPECIFIER|FUNCTION} not defined"
+#endif
+#endif
+
+/* Perform device name translation. Don't call this directly -
+ * use the RESOLVE_DEVICE macro.
+ *
+ * See guestfs(3) for the algorithm.
+ *
+ * We have to open the device and test for ENXIO, because
+ * the device nodes themselves will exist in the appliance.
+ */
+int
+device_name_translation (char *device)
+{
+ int fd;
+
+ fd = open (device, O_RDONLY);
+ if (fd >= 0) {
+ close_ok:
+ close (fd);
+ return 0;
+ }
+
+ if (errno != ENXIO && errno != ENOENT)
+ return -1;
+
+ /* If the name begins with "/dev/sd" then try the alternatives. */
+ if (STRNEQLEN (device, "/dev/sd", 7))
+ return -1;
+
+ device[5] = 'h'; /* /dev/hd (old IDE driver) */
+ fd = open (device, O_RDONLY);
+ if (fd >= 0)
+ goto close_ok;
+
+ device[5] = 'v'; /* /dev/vd (for virtio devices) */
+ fd = open (device, O_RDONLY);
+ if (fd >= 0)
+ goto close_ok;
+
+ device[5] = 's'; /* Restore original device name. */
+ return -1;
+}
+
+/* Check program exists and is executable on $PATH. Actually, we
+ * just assume PATH contains the default entries (see main() above).
+ */
+int
+prog_exists (const char *prog)
+{
+ static const char * const dirs[] =
+ { "/sbin", "/usr/sbin", "/bin", "/usr/bin" };
+ size_t i;
+ char buf[1024];
+
+ for (i = 0; i < sizeof dirs / sizeof dirs[0]; ++i) {
+ snprintf (buf, sizeof buf, "%s/%s", dirs[i], prog);
+ if (access (buf, X_OK) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/* LVM and other commands aren't synchronous, especially when udev is
+ * involved. eg. You can create or remove some device, but the /dev
+ * device node won't appear until some time later. This means that
+ * you get an error if you run one command followed by another.
+ *
+ * Use 'udevadm settle' after certain commands, but don't be too
+ * fussed if it fails.
+ *
+ * 'udevsettle' was the old name for this command (RHEL 5). This was
+ * deprecated in favour of 'udevadm settle'. The old 'udevsettle'
+ * command was left as a symlink. Then in Fedora 13 the old symlink
+ * remained but it stopped working (RHBZ#548121), so we have to be
+ * careful not to assume that we can use 'udevsettle' if it exists.
+ */
+void
+udev_settle (void)
+{
+ (void) command (NULL, NULL, "udevadm", "settle", NULL);
+ (void) command (NULL, NULL, "udevsettle", NULL);
+}