static void output_mountpoints (xmlTextWriterPtr xo, char *root) { size_t i; CLEANUP_FREE_STRING_LIST char **mountpoints = guestfs_inspect_get_mountpoints (g, root); if (mountpoints == NULL) exit (EXIT_FAILURE); /* Sort by key length, shortest key first, and then name, so the * output is stable. */ qsort (mountpoints, guestfs_int_count_strings (mountpoints) / 2, 2 * sizeof (char *), compare_keys_len); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "mountpoints")); for (i = 0; mountpoints[i] != NULL; i += 2) { CLEANUP_FREE char *p = guestfs_canonical_device_name (g, mountpoints[i+1]); if (!p) exit (EXIT_FAILURE); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "mountpoint")); XMLERROR (-1, xmlTextWriterWriteAttribute (xo, BAD_CAST "dev", BAD_CAST p)); XMLERROR (-1, xmlTextWriterWriteString (xo, BAD_CAST mountpoints[i])); XMLERROR (-1, xmlTextWriterEndElement (xo)); } XMLERROR (-1, xmlTextWriterEndElement (xo)); }
int guestfs_impl_set_backend_setting (guestfs_h *g, const char *name, const char *value) { char *new_setting; size_t len; new_setting = safe_asprintf (g, "%s=%s", name, value); if (g->backend_settings == NULL) { g->backend_settings = safe_malloc (g, sizeof (char *)); g->backend_settings[0] = NULL; len = 0; } else { ignore_value (guestfs_clear_backend_setting (g, name)); len = guestfs_int_count_strings (g->backend_settings); } g->backend_settings = safe_realloc (g, g->backend_settings, (len+2) * sizeof (char *)); g->backend_settings[len++] = new_setting; g->backend_settings[len++] = NULL; return 0; }
static void test_stringsbuf (void) { guestfs_h *g; DECLARE_STRINGSBUF (sb); g = guestfs_create (); assert (g); guestfs_int_add_string (g, &sb, "aaa"); guestfs_int_add_string (g, &sb, "bbb"); guestfs_int_add_string (g, &sb, "ccc"); guestfs_int_add_string (g, &sb, ""); guestfs_int_end_stringsbuf (g, &sb); assert (sb.size == 5 /* 4 strings + terminating NULL */); assert (STREQ (sb.argv[0], "aaa")); assert (STREQ (sb.argv[1], "bbb")); assert (STREQ (sb.argv[2], "ccc")); assert (STREQ (sb.argv[3], "")); assert (sb.argv[4] == NULL); assert (guestfs_int_count_strings (sb.argv) == 4); guestfs_int_free_stringsbuf (&sb); guestfs_close (g); }
/* List files in the appliance. */ static char * debug_ll (const char *subcmd, size_t argc, char *const *const argv) { const size_t len = guestfs_int_count_strings (argv); CLEANUP_FREE const char **cargv = NULL; size_t i; int r; char *out; CLEANUP_FREE char *err = NULL; cargv = malloc (sizeof (char *) * (len+3)); if (cargv == NULL) { reply_with_perror ("malloc"); return NULL; } cargv[0] = "ls"; cargv[1] = "-la"; for (i = 0; i < len; ++i) cargv[2+i] = argv[i]; cargv[2+len] = NULL; r = commandv (&out, &err, (void *) cargv); if (r == -1) { reply_with_error ("ll: %s", err); free (out); return NULL; } return out; }
static void output_filesystems (xmlTextWriterPtr xo, char *root) { char *str; size_t i; CLEANUP_FREE_STRING_LIST char **filesystems = guestfs_inspect_get_filesystems (g, root); if (filesystems == NULL) exit (EXIT_FAILURE); /* Sort by name so the output is stable. */ qsort (filesystems, guestfs_int_count_strings (filesystems), sizeof (char *), compare_keys); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystems")); for (i = 0; filesystems[i] != NULL; ++i) { str = guestfs_canonical_device_name (g, filesystems[i]); if (!str) exit (EXIT_FAILURE); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "filesystem")); XMLERROR (-1, xmlTextWriterWriteAttribute (xo, BAD_CAST "dev", BAD_CAST str)); free (str); guestfs_push_error_handler (g, NULL, NULL); str = guestfs_vfs_type (g, filesystems[i]); if (str && str[0]) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "type", BAD_CAST str)); free (str); str = guestfs_vfs_label (g, filesystems[i]); if (str && str[0]) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "label", BAD_CAST str)); free (str); str = guestfs_vfs_uuid (g, filesystems[i]); if (str && str[0]) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "uuid", BAD_CAST str)); free (str); guestfs_pop_error_handler (g); XMLERROR (-1, xmlTextWriterEndElement (xo)); } XMLERROR (-1, xmlTextWriterEndElement (xo)); }
/* Perform conversion using the kernel method. */ void kernel_conversion (struct config *config, char **cmdline, int cmdline_source) { const char *p; /* Pre-conversion command. */ p = get_cmdline_key (cmdline, "p2v.pre"); if (p) run_command ("p2v.pre", p); /* Connect to and interrogate virt-v2v on the conversion server. */ p = get_cmdline_key (cmdline, "p2v.skip_test_connection"); if (!p) { wait_network_online (config); if (test_connection (config) == -1) { const char *err = get_ssh_error (); error (EXIT_FAILURE, 0, "error opening control connection to %s:%d: %s", config->remote.server, config->remote.port, err); } } /* Some disks must have been specified for conversion. */ if (config->disks == NULL || guestfs_int_count_strings (config->disks) == 0) error (EXIT_FAILURE, 0, "no non-removable disks were discovered on this machine.\n" "virt-p2v looked in /sys/block and in p2v.disks on the kernel command line.\n" "This is a fatal error and virt-p2v cannot continue."); /* Perform the conversion in text mode. */ if (start_conversion (config, notify_ui_callback) == -1) { const char *err = get_conversion_error (); fprintf (stderr, "%s: error during conversion: %s\n", getprogname (), err); p = get_cmdline_key (cmdline, "p2v.fail"); if (p) run_command ("p2v.fail", p); exit (EXIT_FAILURE); } ansi_green (stdout); printf ("Conversion finished successfully."); ansi_restore (stdout); putchar ('\n'); p = get_cmdline_key (cmdline, "p2v.post"); if (!p) { if (geteuid () == 0 && cmdline_source == CMDLINE_SOURCE_PROC_CMDLINE) p = "poweroff"; } if (p) run_command ("p2v.post", p); }
/** * Test C<guestfs_int_split_string>. */ static void test_split (void) { char **ret; ret = guestfs_int_split_string (':', ""); assert (ret); assert (guestfs_int_count_strings (ret) == 0); guestfs_int_free_string_list (ret); ret = guestfs_int_split_string (':', "a"); assert (ret); assert (guestfs_int_count_strings (ret) == 1); assert (STREQ (ret[0], "a")); guestfs_int_free_string_list (ret); ret = guestfs_int_split_string (':', ":"); assert (ret); assert (guestfs_int_count_strings (ret) == 2); assert (STREQ (ret[0], "")); assert (STREQ (ret[1], "")); guestfs_int_free_string_list (ret); ret = guestfs_int_split_string (':', "::"); assert (ret); assert (guestfs_int_count_strings (ret) == 3); assert (STREQ (ret[0], "")); assert (STREQ (ret[1], "")); assert (STREQ (ret[2], "")); guestfs_int_free_string_list (ret); ret = guestfs_int_split_string (':', ":a"); assert (ret); assert (guestfs_int_count_strings (ret) == 2); assert (STREQ (ret[0], "")); assert (STREQ (ret[1], "a")); guestfs_int_free_string_list (ret); ret = guestfs_int_split_string (':', "a:"); assert (ret); assert (guestfs_int_count_strings (ret) == 2); assert (STREQ (ret[0], "a")); assert (STREQ (ret[1], "")); guestfs_int_free_string_list (ret); ret = guestfs_int_split_string (':', "a:b:c"); assert (ret); assert (guestfs_int_count_strings (ret) == 3); assert (STREQ (ret[0], "a")); assert (STREQ (ret[1], "b")); assert (STREQ (ret[2], "c")); guestfs_int_free_string_list (ret); }
struct guestfs_xattr_list * guestfs_impl_lxattrlist (guestfs_h *g, const char *dir, char *const *names) { size_t len = guestfs_int_count_strings (names); size_t i, old_len; struct guestfs_xattr_list *ret; ret = safe_malloc (g, sizeof *ret); ret->len = 0; ret->val = NULL; while (len > 0) { CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL; /* Note we don't need to free up the strings because take_strings * does not do a deep copy. */ CLEANUP_FREE char **first = take_strings (g, names, LXATTRLIST_MAX, &names); len = len <= LXATTRLIST_MAX ? 0 : len - LXATTRLIST_MAX; xattrs = guestfs_internal_lxattrlist (g, dir, first); if (xattrs == NULL) { guestfs_free_xattr_list (ret); return NULL; } /* Append xattrs to ret. */ old_len = ret->len; ret->len += xattrs->len; ret->val = safe_realloc (g, ret->val, ret->len * sizeof (struct guestfs_xattr)); for (i = 0; i < xattrs->len; ++i, ++old_len) { /* We have to make a deep copy of the attribute name and value. */ ret->val[old_len].attrname = safe_strdup (g, xattrs->val[i].attrname); ret->val[old_len].attrval = safe_malloc (g, xattrs->val[i].attrval_len); ret->val[old_len].attrval_len = xattrs->val[i].attrval_len; memcpy (ret->val[old_len].attrval, xattrs->val[i].attrval, xattrs->val[i].attrval_len); } } return ret; }
static void output_drive_mappings (xmlTextWriterPtr xo, char *root) { CLEANUP_FREE_STRING_LIST char **drive_mappings = NULL; char *str; size_t i; guestfs_push_error_handler (g, NULL, NULL); drive_mappings = guestfs_inspect_get_drive_mappings (g, root); guestfs_pop_error_handler (g); if (drive_mappings == NULL) return; if (drive_mappings[0] == NULL) return; /* Sort by key. */ qsort (drive_mappings, guestfs_int_count_strings (drive_mappings) / 2, 2 * sizeof (char *), compare_keys_nocase); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "drive_mappings")); for (i = 0; drive_mappings[i] != NULL; i += 2) { str = guestfs_canonical_device_name (g, drive_mappings[i+1]); if (!str) exit (EXIT_FAILURE); XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "drive_mapping")); XMLERROR (-1, xmlTextWriterWriteAttribute (xo, BAD_CAST "name", BAD_CAST drive_mappings[i])); XMLERROR (-1, xmlTextWriterWriteString (xo, BAD_CAST str)); XMLERROR (-1, xmlTextWriterEndElement (xo)); free (str); } XMLERROR (-1, xmlTextWriterEndElement (xo)); }
struct guestfs_statns_list * guestfs_impl_lstatnslist (guestfs_h *g, const char *dir, char * const*names) { size_t len = guestfs_int_count_strings (names); size_t old_len; struct guestfs_statns_list *ret; ret = safe_malloc (g, sizeof *ret); ret->len = 0; ret->val = NULL; while (len > 0) { CLEANUP_FREE_STATNS_LIST struct guestfs_statns_list *stats = NULL; /* Note we don't need to free up the strings because take_strings * does not do a deep copy. */ CLEANUP_FREE char **first = take_strings (g, names, LSTATNSLIST_MAX, &names); len = len <= LSTATNSLIST_MAX ? 0 : len - LSTATNSLIST_MAX; stats = guestfs_internal_lstatnslist (g, dir, first); if (stats == NULL) { guestfs_free_statns_list (ret); return NULL; } /* Append stats to ret. */ old_len = ret->len; ret->len += stats->len; ret->val = safe_realloc (g, ret->val, ret->len * sizeof (struct guestfs_statns)); memcpy (&ret->val[old_len], stats->val, stats->len * sizeof (struct guestfs_statns)); } return ret; }
/** * Start ssh subprocess with the standard arguments and possibly some * optional arguments. Also handles authentication. */ static mexp_h * start_ssh (struct config *config, char **extra_args, int wait_prompt) { size_t i, j, nr_args, count; char port_str[64]; CLEANUP_FREE /* [sic] */ const char **args = NULL; mexp_h *h; const int ovecsize = 12; int ovector[ovecsize]; int saved_timeout; int using_password_auth; if (cache_ssh_identity (config) == -1) return NULL; /* Are we using password or identity authentication? */ using_password_auth = config->identity_file == NULL; /* Create the ssh argument array. */ nr_args = 0; if (extra_args != NULL) nr_args = guestfs_int_count_strings (extra_args); if (using_password_auth) nr_args += 11; else nr_args += 13; args = malloc (sizeof (char *) * nr_args); if (args == NULL) error (EXIT_FAILURE, errno, "malloc"); j = 0; args[j++] = "ssh"; args[j++] = "-p"; /* Port. */ snprintf (port_str, sizeof port_str, "%d", config->port); args[j++] = port_str; args[j++] = "-l"; /* Username. */ args[j++] = config->username ? config->username : "******"; args[j++] = "-o"; /* Host key will always be novel. */ args[j++] = "StrictHostKeyChecking=no"; if (using_password_auth) { /* Only use password authentication. */ args[j++] = "-o"; args[j++] = "PreferredAuthentications=keyboard-interactive,password"; } else { /* Use identity file (private key). */ args[j++] = "-o"; args[j++] = "PreferredAuthentications=publickey"; args[j++] = "-i"; args[j++] = config->identity_file; } if (extra_args != NULL) { for (i = 0; extra_args[i] != NULL; ++i) args[j++] = extra_args[i]; } args[j++] = config->server; /* Conversion server. */ args[j++] = NULL; assert (j == nr_args); h = mexp_spawnv ("ssh", (char **) args); if (h == NULL) return NULL; if (using_password_auth && config->password && strlen (config->password) > 0) { /* Wait for the password prompt. */ switch (mexp_expect (h, (mexp_regexp[]) { { 100, .re = password_re }, { 0 } }, ovector, ovecsize)) {
/** * Start ssh subprocess with the standard arguments and possibly some * optional arguments. Also handles authentication. */ static mexp_h * start_ssh (unsigned spawn_flags, struct config *config, char **extra_args, int wait_prompt) { size_t i, j, nr_args, count; char port_str[64]; char connect_timeout_str[128]; CLEANUP_FREE /* [sic] */ const char **args = NULL; mexp_h *h; const int ovecsize = 12; int ovector[ovecsize]; int saved_timeout; int using_password_auth; if (cache_ssh_identity (config) == -1) return NULL; /* Are we using password or identity authentication? */ using_password_auth = config->identity_file == NULL; /* Create the ssh argument array. */ nr_args = 0; if (extra_args != NULL) nr_args = guestfs_int_count_strings (extra_args); if (using_password_auth) nr_args += 13; else nr_args += 15; args = malloc (sizeof (char *) * nr_args); if (args == NULL) error (EXIT_FAILURE, errno, "malloc"); j = 0; args[j++] = "ssh"; args[j++] = "-p"; /* Port. */ snprintf (port_str, sizeof port_str, "%d", config->port); args[j++] = port_str; args[j++] = "-l"; /* Username. */ args[j++] = config->username ? config->username : "******"; args[j++] = "-o"; /* Host key will always be novel. */ args[j++] = "StrictHostKeyChecking=no"; args[j++] = "-o"; /* ConnectTimeout */ snprintf (connect_timeout_str, sizeof connect_timeout_str, "ConnectTimeout=%d", SSH_TIMEOUT); args[j++] = connect_timeout_str; if (using_password_auth) { /* Only use password authentication. */ args[j++] = "-o"; args[j++] = "PreferredAuthentications=keyboard-interactive,password"; } else { /* Use identity file (private key). */ args[j++] = "-o"; args[j++] = "PreferredAuthentications=publickey"; args[j++] = "-i"; args[j++] = config->identity_file; } if (extra_args != NULL) { for (i = 0; extra_args[i] != NULL; ++i) args[j++] = extra_args[i]; } args[j++] = config->server; /* Conversion server. */ args[j++] = NULL; assert (j == nr_args); #if DEBUG_STDERR fputs ("ssh command: ", stderr); for (i = 0; i < nr_args - 1; ++i) { if (i > 0) fputc (' ', stderr); fputs (args[i], stderr); } fputc ('\n', stderr); #endif /* Create the miniexpect handle. */ h = mexp_spawnvf (spawn_flags, "ssh", (char **) args); if (h == NULL) { set_ssh_internal_error ("ssh: mexp_spawnvf: %m"); return NULL; } /* We want the ssh ConnectTimeout to be less than the miniexpect * timeout, so that if the server is completely unresponsive we * still see the error from ssh, not a timeout from miniexpect. The * obvious solution to this is to set ConnectTimeout (above) and to * set the miniexpect timeout to be a little bit larger. */ mexp_set_timeout (h, SSH_TIMEOUT + 20); if (using_password_auth && config->password && strlen (config->password) > 0) { CLEANUP_FREE char *ssh_message = NULL; /* Wait for the password prompt. */ wait_password_again: switch (mexp_expect (h, (mexp_regexp[]) { { 100, .re = password_re },
void kernel_configuration (struct config *config, char **cmdline, int cmdline_source) { const char *p; p = get_cmdline_key (cmdline, "p2v.pre"); if (p) run_command (config->verbose, "p2v.pre", p); p = get_cmdline_key (cmdline, "p2v.server"); assert (p); /* checked by caller */ free (config->server); config->server = strdup (p); p = get_cmdline_key (cmdline, "p2v.port"); if (p) { if (sscanf (p, "%d", &config->port) != 1) { fprintf (stderr, "%s: cannot parse p2v.port from kernel command line", guestfs_int_program_name); exit (EXIT_FAILURE); } } p = get_cmdline_key (cmdline, "p2v.username"); if (p) { free (config->username); config->username = strdup (p); } p = get_cmdline_key (cmdline, "p2v.password"); if (p) { free (config->password); config->password = strdup (p); } p = get_cmdline_key (cmdline, "p2v.identity"); if (p) { free (config->identity_url); config->identity_url = strdup (p); config->identity_file_needs_update = 1; } p = get_cmdline_key (cmdline, "p2v.sudo"); if (p) config->sudo = 1; /* We should now be able to connect and interrogate virt-v2v * on the conversion server. */ p = get_cmdline_key (cmdline, "p2v.skip_test_connection"); if (!p) { wait_network_online (config); if (test_connection (config) == -1) { const char *err = get_ssh_error (); fprintf (stderr, "%s: error opening control connection to %s:%d: %s\n", guestfs_int_program_name, config->server, config->port, err); exit (EXIT_FAILURE); } } p = get_cmdline_key (cmdline, "p2v.name"); if (p) { free (config->guestname); config->guestname = strdup (p); } p = get_cmdline_key (cmdline, "p2v.vcpus"); if (p) { if (sscanf (p, "%d", &config->vcpus) != 1) { fprintf (stderr, "%s: cannot parse p2v.vcpus from kernel command line\n", guestfs_int_program_name); exit (EXIT_FAILURE); } } p = get_cmdline_key (cmdline, "p2v.memory"); if (p) { char mem_code; if (sscanf (p, "%" SCNu64 "%c", &config->memory, &mem_code) != 2) { fprintf (stderr, "%s: cannot parse p2v.memory from kernel command line\n", guestfs_int_program_name); exit (EXIT_FAILURE); } config->memory *= 1024; if (mem_code == 'M' || mem_code == 'm' || mem_code == 'G' || mem_code == 'g') config->memory *= 1024; if (mem_code == 'G' || mem_code == 'g') config->memory *= 1024; if (mem_code != 'M' && mem_code != 'm' && mem_code != 'G' && mem_code != 'g') { fprintf (stderr, "%s: p2v.memory on kernel command line must be followed by 'G' or 'M'\n", guestfs_int_program_name); exit (EXIT_FAILURE); } } p = get_cmdline_key (cmdline, "p2v.disks"); if (p) { CLEANUP_FREE char *t; t = strdup (p); guestfs_int_free_string_list (config->disks); config->disks = guestfs_int_split_string (',', t); } p = get_cmdline_key (cmdline, "p2v.removable"); if (p) { CLEANUP_FREE char *t; t = strdup (p); guestfs_int_free_string_list (config->removable); config->removable = guestfs_int_split_string (',', t); } p = get_cmdline_key (cmdline, "p2v.interfaces"); if (p) { CLEANUP_FREE char *t; t = strdup (p); guestfs_int_free_string_list (config->interfaces); config->interfaces = guestfs_int_split_string (',', t); } p = get_cmdline_key (cmdline, "p2v.network"); if (p) { CLEANUP_FREE char *t; t = strdup (p); guestfs_int_free_string_list (config->network_map); config->network_map = guestfs_int_split_string (',', t); } p = get_cmdline_key (cmdline, "p2v.o"); if (p) { free (config->output); config->output = strdup (p); } p = get_cmdline_key (cmdline, "p2v.oa"); if (p) { if (STREQ (p, "sparse")) config->output_allocation = OUTPUT_ALLOCATION_SPARSE; else if (STREQ (p, "preallocated")) config->output_allocation = OUTPUT_ALLOCATION_PREALLOCATED; else fprintf (stderr, "%s: warning: don't know what p2v.oa=%s means\n", guestfs_int_program_name, p); } p = get_cmdline_key (cmdline, "p2v.oc"); if (p) { free (config->output_connection); config->output_connection = strdup (p); } p = get_cmdline_key (cmdline, "p2v.of"); if (p) { free (config->output_format); config->output_format = strdup (p); } p = get_cmdline_key (cmdline, "p2v.os"); if (p) { free (config->output_storage); config->output_storage = strdup (p); } /* Undocumented command line tool used for testing command line parsing. */ p = get_cmdline_key (cmdline, "p2v.dump_config_and_exit"); if (p) { print_config (config, stdout); exit (EXIT_SUCCESS); } /* Some disks must have been specified for conversion. */ if (config->disks == NULL || guestfs_int_count_strings (config->disks) == 0) { fprintf (stderr, "%s: error: no non-removable disks were discovered on this machine.\n", guestfs_int_program_name); fprintf (stderr, "virt-p2v looked in /sys/block and in p2v.disks on the kernel command line.\n"); fprintf (stderr, "This is a fatal error and virt-p2v cannot continue.\n"); exit (EXIT_FAILURE); } /* Perform the conversion in text mode. */ if (start_conversion (config, notify_ui_callback) == -1) { const char *err = get_conversion_error (); fprintf (stderr, "%s: error during conversion: %s\n", guestfs_int_program_name, err); p = get_cmdline_key (cmdline, "p2v.fail"); if (p) run_command (config->verbose, "p2v.fail", p); exit (EXIT_FAILURE); } p = get_cmdline_key (cmdline, "p2v.post"); if (!p) { if (geteuid () == 0 && cmdline_source == CMDLINE_SOURCE_PROC_CMDLINE) p = "poweroff"; } if (p) run_command (config->verbose, "p2v.post", p); }
int run_glob (const char *cmd, size_t argc, char *argv[]) { /* For 'glob cmd foo /s* /usr/s*' this could be: * * (globs[0]) globs[1] globs[1] globs[2] * (cmd) foo /sbin /usr/sbin * /srv /usr/share * /sys /usr/src * * and then we call every combination (ie. 1x3x3) of * argv[1-]. */ CLEANUP_FREE char ***globs = NULL; CLEANUP_FREE size_t *posn = NULL; CLEANUP_FREE size_t *count = NULL; size_t i; int r = 0; globs = malloc (sizeof (char **) * argc); posn = malloc (sizeof (size_t) * argc); count = malloc (sizeof (size_t) * argc); if (globs == NULL || posn == NULL || count == NULL) { perror ("malloc"); return -1; } if (argc < 1) { fprintf (stderr, _("use 'glob command [args...]'\n")); return -1; } /* This array will record the current execution position * in the Cartesian product. * NB. globs[0], posn[0], count[0] are ignored. */ for (i = 1; i < argc; ++i) posn[i] = 0; for (i = 1; i < argc; ++i) globs[i] = NULL; for (i = 1; i < argc; ++i) { char **pp; /* If it begins with "/dev/" then treat it as a globbable device * name. */ if (STRPREFIX (argv[i], "/dev/")) { pp = expand_devicename (g, argv[i]); if (pp == NULL) { r = -1; goto error; } } /* If it begins with "/" it might be a globbable pathname. */ else if (argv[i][0] == '/') { pp = expand_pathname (g, argv[i]); if (pp == NULL) { r = -1; goto error; } } /* Doesn't begin with '/' */ else { pp = single_element_list (argv[i]); if (pp == NULL) { r = -1; goto error; } } globs[i] = pp; count[i] = guestfs_int_count_strings (pp); } /* Issue the commands. */ if (glob_issue (argv[0], argc, globs, posn, count, &r) == -1) { r = -1; goto error; } /* Free resources. */ error: for (i = 1; i < argc; ++i) if (globs[i]) guestfs_int_free_string_list (globs[i]); return r; }
void parse_config (void) { const char *home; /* Try the global configuration first. */ read_config_from_file (etc_filename); { /* Then read the configuration from XDG system paths. */ const char *xdg_env, *var; CLEANUP_FREE_STRING_LIST char **xdg_config_dirs = NULL; size_t xdg_config_dirs_count; xdg_env = getenv ("XDG_CONFIG_DIRS"); var = xdg_env != NULL && xdg_env[0] != 0 ? xdg_env : "/etc/xdg"; xdg_config_dirs = guestfs_int_split_string (':', var); xdg_config_dirs_count = guestfs_int_count_strings (xdg_config_dirs); for (size_t i = xdg_config_dirs_count; i > 0; --i) { CLEANUP_FREE char *path = NULL; const char *dir = xdg_config_dirs[i - 1]; if (asprintf (&path, "%s/libguestfs/" GLOBAL_CONFIG_FILENAME, dir) == -1) { perror ("asprintf"); exit (EXIT_FAILURE); } read_config_from_file (path); } } /* Read the configuration from $HOME, to override system settings. */ home = getenv ("HOME"); if (home != NULL) { { /* Old-style configuration file first. */ CLEANUP_FREE char *path = NULL; if (asprintf (&path, "%s/%s", home, home_filename) == -1) { perror ("asprintf"); exit (EXIT_FAILURE); } read_config_from_file (path); } { /* Then, XDG_CONFIG_HOME path. */ CLEANUP_FREE char *path = NULL; CLEANUP_FREE char *home_copy = strdup (home); const char *xdg_env; if (home_copy == NULL) { perror ("strdup"); exit (EXIT_FAILURE); } xdg_env = getenv ("XDG_CONFIG_HOME"); if (xdg_env == NULL) { if (asprintf (&path, "%s/.config/libguestfs/" GLOBAL_CONFIG_FILENAME, home_copy) == -1) { perror ("asprintf"); exit (EXIT_FAILURE); } } else { if (asprintf (&path, "%s/libguestfs/" GLOBAL_CONFIG_FILENAME, xdg_env) == -1) { perror ("asprintf"); exit (EXIT_FAILURE); } } read_config_from_file (path); } } }