/* Use vfs-type to look for a filesystem of some sort on 'dev'. * Apart from some types which we ignore, add the result to the * 'ret' string list. */ static int check_with_vfs_type (guestfs_h *g, const char *device, struct stringsbuf *sb) { const char *v; CLEANUP_FREE char *vfs_type = NULL; guestfs_push_error_handler (g, NULL, NULL); vfs_type = guestfs_vfs_type (g, device); guestfs_pop_error_handler (g); if (!vfs_type) v = "unknown"; else if (STREQ (vfs_type, "")) v = "unknown"; else if (STREQ (vfs_type, "btrfs")) { CLEANUP_FREE_BTRFSSUBVOLUME_LIST struct guestfs_btrfssubvolume_list *vols = guestfs_btrfs_subvolume_list (g, device); if (vols == NULL) return -1; for (size_t i = 0; i < vols->len; i++) { struct guestfs_btrfssubvolume *this = &vols->val[i]; guestfs___add_sprintf (g, sb, "btrfsvol:%s/%s", device, this->btrfssubvolume_path); guestfs___add_string (g, sb, "btrfs"); } v = vfs_type; }
/* Use vfs-type to look for a filesystem of some sort on 'dev'. * Apart from some types which we ignore, add the result to the * 'ret' string list. */ static void check_with_vfs_type (guestfs_h *g, const char *device, char ***ret, size_t *ret_size) { char *v; char *vfs_type; guestfs_push_error_handler (g, NULL, NULL); vfs_type = guestfs_vfs_type (g, device); guestfs_pop_error_handler (g); if (!vfs_type) v = safe_strdup (g, "unknown"); else if (STREQ (vfs_type, "")) { v = safe_strdup (g, "unknown"); free (vfs_type); } else if (STREQ (vfs_type, "btrfs")) { CLEANUP_FREE_BTRFSSUBVOLUME_LIST struct guestfs_btrfssubvolume_list *vols = guestfs_btrfs_subvolume_list (g, device); for (size_t i = 0; i < vols->len; i++) { struct guestfs_btrfssubvolume *this = &vols->val[i]; char *mountable = safe_asprintf (g, "btrfsvol:%s/%s", device, this->btrfssubvolume_path); add_vfs (g, mountable, safe_strdup (g, "btrfs"), ret, ret_size); } v = vfs_type; }
/* There are several sources we might use: * - /ProgramData/Microsoft/Windows Live/WLive48x48.png * - w-brand.png (in a very long directory name) * - /Windows/System32/slui.exe --type=14 group icon #2 */ static char * icon_windows_8 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) { CLEANUP_FREE char *filename_case = NULL; CLEANUP_FREE char *filename_downloaded = NULL; int r; char *ret; filename_case = guestfs___case_sensitive_path_silently (g, "/ProgramData/Microsoft/Windows Live/WLive48x48.png"); if (filename_case == NULL) return NOT_FOUND; /* Not an error since a parent dir might not exist. */ guestfs_push_error_handler (g, NULL, NULL); r = guestfs_is_file (g, filename_case); guestfs_pop_error_handler (g); if (r == -1) return NULL; if (r == 0) return NOT_FOUND; filename_downloaded = guestfs___download_to_tmp (g, fs, filename_case, "wlive48x48.png", 8192); if (filename_downloaded == NULL) return NOT_FOUND; if (read_whole_file (g, filename_downloaded, &ret, size_r) == -1) return NULL; return ret; }
int run_supported (const char *cmd, size_t argc, char *argv[]) { /* As a side-effect this also checks that we've called 'launch'. */ CLEANUP_FREE_STRING_LIST char **groups = guestfs_available_all_groups (g); if (groups == NULL) return -1; /* Temporarily replace the error handler so that messages don't get * printed to stderr while we are issuing commands. */ guestfs_push_error_handler (g, NULL, NULL); /* Work out the max string length of any group name. */ size_t i; size_t len = 0; for (i = 0; groups[i] != NULL; ++i) { size_t l = strlen (groups[i]); if (l > len) len = l; } for (i = 0; groups[i] != NULL; ++i) { char *gg[] = { groups[i], NULL }; int r = guestfs_available (g, gg); const char *str = r == 0 ? _("yes") : _("no"); printf ("%*s %s\n", (int) len, groups[i], str); } /* Restore error handler. */ guestfs_pop_error_handler (g); return 0; }
static void check_architecture (guestfs_h *g, struct inspect_fs *fs) { const char *binaries[] = { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" }; size_t i; char *arch; for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) { if (guestfs_is_file (g, binaries[i]) > 0) { /* Ignore errors from file_architecture call. */ guestfs_push_error_handler (g, NULL, NULL); arch = guestfs_file_architecture (g, binaries[i]); guestfs_pop_error_handler (g); if (arch) { /* String will be owned by handle, freed by * guestfs___free_inspect_info. */ fs->arch = arch; break; } } } }
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)); }
static int do_log (void) { CLEANUP_FREE_STRING_LIST char **roots = NULL; char *root; CLEANUP_FREE char *type = NULL; CLEANUP_FREE_STRING_LIST char **journal_files = NULL; /* Get root mountpoint. fish/inspect.c guarantees the assertions * below. */ roots = guestfs_inspect_get_roots (g); assert (roots); assert (roots[0] != NULL); assert (roots[1] == NULL); root = roots[0]; type = guestfs_inspect_get_type (g, root); if (!type) return -1; /* Windows needs special handling. */ if (STREQ (type, "windows")) { if (guestfs_inspect_get_major_version (g, root) >= 6) return do_log_windows_evtx (); fprintf (stderr, _("%s: Windows Event Log for pre-Vista guests is not supported.\n"), guestfs_int_program_name); return -1; } /* systemd journal? */ guestfs_push_error_handler (g, NULL, NULL); journal_files = guestfs_ls (g, JOURNAL_DIR); guestfs_pop_error_handler (g); if (STREQ (type, "linux") && journal_files != NULL && journal_files[0] != NULL) return do_log_journal (); /* Regular /var/log text files with different names. */ if (STRNEQ (type, "windows")) { const char *logfiles[] = { "/var/log/syslog", "/var/log/messages", NULL }; size_t i; for (i = 0; logfiles[i] != NULL; ++i) { if (guestfs_is_file_opts (g, logfiles[i], GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) == 1) return do_log_text_file (logfiles[i]); } } /* Otherwise, there are no log files. Hmm, is this right? XXX */ return 0; }
/* NB: This function DOES NOT test for the existence of the file. It * will return non-NULL even if the file/directory does not exist. * You have to call guestfs_is_file{,_opts} etc. */ char * guestfs___case_sensitive_path_silently (guestfs_h *g, const char *path) { char *ret; guestfs_push_error_handler (g, NULL, NULL); ret = guestfs_case_sensitive_path (g, path); guestfs_pop_error_handler (g); return ret; }
/* Check that the named file 'filename' is a PNG file and is reasonable. * If it is, download and return it. */ static char * get_png (guestfs_h *g, struct inspect_fs *fs, const char *filename, size_t *size_r, size_t max_size) { char *ret; CLEANUP_FREE char *real = NULL; CLEANUP_FREE char *type = NULL; CLEANUP_FREE char *local = NULL; int r, w, h; r = guestfs_is_file_opts (g, filename, GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1); if (r == -1) return NULL; /* a real error */ if (r == 0) return NOT_FOUND; /* Resolve the path, in case it's a symbolic link (as in RHEL 7). */ guestfs_push_error_handler (g, NULL, NULL); real = guestfs_realpath (g, filename); guestfs_pop_error_handler (g); if (real == NULL) return NOT_FOUND; /* could just be a broken link */ /* Check the file type and geometry. */ type = guestfs_file (g, real); if (!type) return NOT_FOUND; if (!STRPREFIX (type, "PNG image data, ")) return NOT_FOUND; if (sscanf (&type[16], "%d x %d", &w, &h) != 2) return NOT_FOUND; if (w < 16 || h < 16 || w > 1024 || h > 1024) return NOT_FOUND; /* Define a maximum reasonable size based on the geometry. This * also limits the maximum we allocate below to around 4 MB. */ if (max_size == 0) max_size = 4 * w * h; local = guestfs___download_to_tmp (g, fs, real, "icon", max_size); if (!local) return NOT_FOUND; /* Successfully passed checks and downloaded. Read it into memory. */ if (read_whole_file (g, local, &ret, size_r) == -1) return NULL; return ret; }
static char * icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) { CLEANUP_FREE char *filename = NULL; CLEANUP_FREE char *filename_case = NULL; CLEANUP_FREE char *filename_downloaded = NULL; CLEANUP_FREE char *pngfile = NULL; CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g); int r; char *ret; /* Download %systemroot%\explorer.exe */ filename = safe_asprintf (g, "%s/explorer.exe", fs->windows_systemroot); filename_case = guestfs_case_sensitive_path (g, filename); if (filename_case == NULL) return NULL; guestfs_push_error_handler (g, NULL, NULL); r = guestfs_is_file (g, filename_case); guestfs_pop_error_handler (g); if (r == -1) return NULL; if (r == 0) return NOT_FOUND; filename_downloaded = guestfs___download_to_tmp (g, fs, filename_case, "explorer.exe", MAX_WINDOWS_EXPLORER_SIZE); if (filename_downloaded == NULL) return NOT_FOUND; pngfile = safe_asprintf (g, "%s/windows-7-icon.png", g->tmpdir); guestfs___cmd_add_string_unquoted (cmd, WRESTOOL " -x --type=2 --name=6801 "); guestfs___cmd_add_string_quoted (cmd, filename_downloaded); guestfs___cmd_add_string_unquoted (cmd, " | " BMPTOPNM " | " PAMCUT " -bottom 54 | " PNMTOPNG " > "); guestfs___cmd_add_string_quoted (cmd, pngfile); r = guestfs___cmd_run (cmd); if (r == -1) return NULL; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) return NOT_FOUND; if (read_whole_file (g, pngfile, &ret, size_r) == -1) return NULL; return ret; }
int main (int argc, char *argv[]) { guestfs_h *g; size_t i; int lengths[] = { 0, 1, 1024, GUESTFS_ERROR_LEN-2, GUESTFS_ERROR_LEN-1, GUESTFS_ERROR_LEN, GUESTFS_ERROR_LEN+1, GUESTFS_ERROR_LEN+2, GUESTFS_ERROR_LEN*2, -1 }; char len_s[64]; char *args[2]; g = guestfs_create (); if (g == NULL) { perror ("guestfs_create"); exit (EXIT_FAILURE); } if (guestfs_add_drive (g, "/dev/null") == -1) exit (EXIT_FAILURE); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); guestfs_push_error_handler (g, NULL, NULL); for (i = 0; lengths[i] != -1; ++i) { snprintf (len_s, sizeof len_s, "%d", lengths[i]); args[0] = len_s; args[1] = NULL; if (guestfs_debug (g, "error", args) != NULL) { fprintf (stderr, "%s: unexpected return value from 'debug error'\n", argv[0]); exit (EXIT_FAILURE); } /* EROFS is a magic value returned by debug_error in the daemon. */ if (guestfs_last_errno (g) != EROFS) { fprintf (stderr, "%s: unexpected error from 'debug error': %s\n", argv[0], guestfs_last_error (g)); exit (EXIT_FAILURE); } /* else OK */ } guestfs_pop_error_handler (g); guestfs_close (g); exit (EXIT_SUCCESS); }
static int feature_available (guestfs_h *g, const char *feature) { /* If there's an error we should ignore it, so to do that we have to * temporarily replace the error handler with a null one. */ guestfs_push_error_handler (g, NULL, NULL); const char *groups[] = { feature, NULL }; int r = guestfs_available (g, (char * const *) groups); guestfs_pop_error_handler (g); return r == 0 ? 1 : 0; }
/* Since we want this function to be robust against very bad failure * cases (hello, https://bugzilla.kernel.org/show_bug.cgi?id=18792) it * won't exit on guestfs failures. */ int df_on_handle (guestfs_h *g, const char *name, const char *uuid, FILE *fp) { size_t i; CLEANUP_FREE_STRING_LIST char **devices = NULL; CLEANUP_FREE_STRING_LIST char **fses = NULL; if (verbose) fprintf (stderr, "df_on_handle: %s\n", name); devices = guestfs_list_devices (g); if (devices == NULL) return -1; fses = guestfs_list_filesystems (g); if (fses == NULL) return -1; for (i = 0; fses[i] != NULL; i += 2) { if (STRNEQ (fses[i+1], "") && STRNEQ (fses[i+1], "swap") && STRNEQ (fses[i+1], "unknown")) { const char *dev = fses[i]; CLEANUP_FREE_STATVFS struct guestfs_statvfs *stat = NULL; if (verbose) fprintf (stderr, "df_on_handle: %s dev %s\n", name, dev); /* Try mounting and stating the device. This might reasonably * fail, so don't show errors. */ guestfs_push_error_handler (g, NULL, NULL); if (guestfs_mount_ro (g, dev, "/") == 0) { stat = guestfs_statvfs (g, "/"); guestfs_umount_all (g); } guestfs_pop_error_handler (g); if (stat) print_stat (fp, name, uuid, dev, stat); } } return 0; }
int guestfs_impl_mount_local_run (guestfs_h *g) { int r, mounted; gl_lock_lock (mount_local_lock); mounted = g->localmountpoint != NULL; gl_lock_unlock (mount_local_lock); if (!mounted) { error (g, _("you must call guestfs_mount_local first")); return -1; } /* Test if root is mounted. We do this by using a side-effect of * guestfs_exists (which is that it calls NEED_ROOT). */ guestfs_push_error_handler (g, NULL, NULL); r = guestfs_exists (g, "/"); guestfs_pop_error_handler (g); if (r == -1) { error (g, _("you must call 'guestfs_mount' first to mount a filesystem on '/'.\nNote: '%s' is still mounted. Use 'guestunmount %s' to clean up."), g->localmountpoint, g->localmountpoint); return -1; } debug (g, "%s: entering fuse_loop", __func__); /* Enter the main loop. */ r = fuse_loop (g->fuse); if (r != 0) perrorf (g, _("fuse_loop: %s"), g->localmountpoint); debug (g, "%s: leaving fuse_loop", __func__); guestfs_int_free_fuse (g); gl_lock_lock (mount_local_lock); g->localmountpoint = NULL; gl_lock_unlock (mount_local_lock); /* By inspection, I found that fuse_loop only returns 0 or -1, but * don't rely on this in future. */ return r == 0 ? 0 : -1; }
/* Parse the hostname from /etc/sysconfig/network. This must be * called from the inspect_with_augeas wrapper. Note that F18+ and * RHEL7+ use /etc/hostname just like Debian. */ static int check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs) { char *hostname; /* Errors here are not fatal (RHBZ#726739), since it could be * just missing HOSTNAME field in the file. */ guestfs_push_error_handler (g, NULL, NULL); hostname = guestfs_aug_get (g, "/files/etc/sysconfig/network/HOSTNAME"); guestfs_pop_error_handler (g); /* This is freed by guestfs___free_inspect_info. Note that hostname * could be NULL because we ignored errors above. */ fs->hostname = hostname; return 0; }
static int copy_attributes (const char *src, const char *dest) { CLEANUP_FREE_STAT struct guestfs_stat *stat = NULL; const char *linuxxattrs[] = { "linuxxattrs", NULL }; int has_linuxxattrs; CLEANUP_FREE char *selinux_context = NULL; size_t selinux_context_size; has_linuxxattrs = guestfs_feature_available (g, (char **) linuxxattrs); /* Get the mode. */ stat = guestfs_stat (g, src); if (stat == NULL) return -1; /* Get the SELinux context. XXX Should we copy over other extended * attributes too? */ if (has_linuxxattrs) { guestfs_push_error_handler (g, NULL, NULL); selinux_context = guestfs_getxattr (g, src, "security.selinux", &selinux_context_size); /* selinux_context could be NULL. This isn't an error. */ guestfs_pop_error_handler (g); } /* Set the permissions (inc. sticky and set*id bits), UID, GID. */ if (guestfs_chmod (g, stat->mode & 07777, dest) == -1) return -1; if (guestfs_chown (g, stat->uid, stat->gid, dest) == -1) return -1; /* Set the SELinux context. */ if (has_linuxxattrs && selinux_context) { if (guestfs_setxattr (g, "security.selinux", selinux_context, (int) selinux_context_size, dest) == -1) return -1; } return 0; }
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)); }
static int get_mbr_id (const char *dev, const char *parent_name) { CLEANUP_FREE char *parttype = NULL; int mbr_id = -1, partnum; guestfs_push_error_handler (g, NULL, NULL); parttype = guestfs_part_get_parttype (g, parent_name); if (parttype && STREQ (parttype, "msdos")) { partnum = guestfs_part_to_partnum (g, dev); if (partnum >= 0) mbr_id = guestfs_part_get_mbr_id (g, parent_name, partnum); } guestfs_pop_error_handler (g); return mbr_id; }
/* Rescan everything so the kernel knows that there are no partition * tables, VGs etc. Returns 0 on success, 1 if we need to retry. */ static int do_rescan (char **devices) { size_t i; size_t errors = 0; guestfs_push_error_handler (g, NULL, NULL); for (i = 0; devices[i] != NULL; ++i) { if (guestfs_blockdev_rereadpt (g, devices[i]) == -1) errors++; } if (guestfs_vgscan (g) == -1) errors++; guestfs_pop_error_handler (g); return errors ? 1 : 0; }
/* Use vfs-type to look for a filesystem of some sort on 'dev'. * Apart from some types which we ignore, add the result to the * 'ret' string list. */ static int check_with_vfs_type (guestfs_h *g, const char *device, struct stringsbuf *sb) { const char *v; CLEANUP_FREE char *vfs_type = NULL; guestfs_push_error_handler (g, NULL, NULL); vfs_type = guestfs_vfs_type (g, device); guestfs_pop_error_handler (g); if (!vfs_type) v = "unknown"; else if (STREQ (vfs_type, "")) v = "unknown"; else if (STREQ (vfs_type, "btrfs")) { CLEANUP_FREE_BTRFSSUBVOLUME_LIST struct guestfs_btrfssubvolume_list *vols = guestfs_btrfs_subvolume_list (g, device); if (vols == NULL) return -1; int64_t default_volume = guestfs_btrfs_subvolume_get_default (g, device); for (size_t i = 0; i < vols->len; i++) { struct guestfs_btrfssubvolume *this = &vols->val[i]; /* Ignore the default subvolume. We get it by simply mounting * the whole device of this btrfs filesystem. */ if (this->btrfssubvolume_id == (uint64_t) default_volume) continue; guestfs_int_add_sprintf (g, sb, "btrfsvol:%s/%s", device, this->btrfssubvolume_path); guestfs_int_add_string (g, sb, "btrfs"); } v = vfs_type; }
/* If we found a filesystem on the parent device then ignore the * partitions within. */ static int parent_device_already_probed (guestfs_h *g, const char *partition) { char *device; size_t i; guestfs_push_error_handler (g, NULL, NULL); device = guestfs_part_to_dev (g, partition); guestfs_pop_error_handler (g); if (!device) return 0; for (i = 0; i < g->nr_fses; ++i) { if (STREQ (device, g->fses[i].device)) { free (device); return 1; } } free (device); return 0; }
/* This is a convenience function, but we might consider exporting * it as an API in future. */ int guestfs_int_get_backend_setting_bool (guestfs_h *g, const char *name) { CLEANUP_FREE char *value = NULL; int b; guestfs_push_error_handler (g, NULL, NULL); value = guestfs_get_backend_setting (g, name); guestfs_pop_error_handler (g); if (value == NULL && guestfs_last_errno (g) == ESRCH) return 0; if (value == NULL) return -1; b = guestfs_int_is_true (value); if (b == -1) return -1; return b; }
/* This is called for whole block devices. See if the device is an * ISO and we are able to read the ISO info from it. In that case, * try using libosinfo to map from the volume ID and other strings * directly to the operating system type. */ int guestfs_int_check_installer_iso (guestfs_h *g, struct inspect_fs *fs, const char *device) { CLEANUP_FREE_ISOINFO struct guestfs_isoinfo *isoinfo = NULL; const struct osinfo *osinfo; int r; guestfs_push_error_handler (g, NULL, NULL); isoinfo = guestfs_isoinfo_device (g, device); guestfs_pop_error_handler (g); if (!isoinfo) return 0; r = guestfs_int_osinfo_map (g, isoinfo, &osinfo); if (r == -1) /* Fatal error. */ return -1; if (r == 0) /* Could not locate any matching ISO. */ return 0; /* Otherwise we matched an ISO, so fill in the fs fields. */ fs->mountable = safe_strdup (g, device); fs->role = OS_ROLE_ROOT; if (osinfo->is_installer) fs->format = OS_FORMAT_INSTALLER; fs->type = osinfo->type; fs->distro = osinfo->distro; fs->product_name = osinfo->product_name ? safe_strdup (g, osinfo->product_name) : NULL; guestfs_int_version_from_values (&fs->version, osinfo->major_version, osinfo->minor_version, 0); fs->arch = osinfo->arch ? safe_strdup (g, osinfo->arch) : NULL; fs->is_live_disk = osinfo->is_live_disk; guestfs_int_check_package_format (g, fs); guestfs_int_check_package_management (g, fs); return 1; }
int main (int argc, char *argv[]) { guestfs_h *g; int fd, r; char tempdir[] = "/tmp/mlXXXXXX"; pid_t pid; char *shell, *p; if (argc != 2) { usage (); exit (EXIT_FAILURE); } printf ("\n" "This is the 'mount-local' demonstration program. Follow the\n" "instructions on screen.\n" "\n" "Creating and formatting the disk image, please wait a moment ...\n"); fflush (stdout); /* Create the output disk image: raw sparse. */ fd = open (argv[1], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0644); if (fd == -1) { perror (argv[1]); exit (EXIT_FAILURE); } if (ftruncate (fd, SIZE_MB * 1024 * 1024) == -1) { perror ("truncate"); close (fd); exit (EXIT_FAILURE); } if (close (fd) == -1) { perror ("close"); exit (EXIT_FAILURE); } /* Guestfs handle. */ g = guestfs_create (); if (g == NULL) { perror ("could not create libguestfs handle"); exit (EXIT_FAILURE); } /* Create the disk image and format it with a partition and a filesystem. */ if (guestfs_add_drive_opts (g, argv[1], GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", -1) == -1) exit (EXIT_FAILURE); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1) exit (EXIT_FAILURE); if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1) exit (EXIT_FAILURE); /* Mount the empty filesystem. */ if (guestfs_mount_options (g, MOUNT_OPTIONS, "/dev/sda1", "/") == -1) exit (EXIT_FAILURE); /* Create a file in the new filesystem. */ if (guestfs_touch (g, "/PUT_FILES_AND_DIRECTORIES_HERE") == -1) exit (EXIT_FAILURE); /* Create a temporary mount directory. */ if (mkdtemp (tempdir) == NULL) { perror ("mkdtemp"); exit (EXIT_FAILURE); } /* Mount the filesystem. */ if (guestfs_mount_local (g, tempdir, -1) == -1) exit (EXIT_FAILURE); /* Fork the shell for the user. */ pid = fork (); if (pid == -1) { perror ("fork"); exit (EXIT_FAILURE); } if (pid == 0) { /* Child. */ if (chdir (tempdir) == -1) { perror (tempdir); _exit (EXIT_FAILURE); } printf ("\n" "The *current directory* is a FUSE filesystem backed by the disk\n" "image which is managed by libguestfs. Any files or directories\n" "you copy into here (up to %d MB) will be saved into the disk\n" "image. You can also delete files, create certain special files\n" "and so on.\n" "\n" "When you have finished adding files, hit ^D or type 'exit' to\n" "exit the shell and return to the mount-local program.\n" "\n", SIZE_MB); shell = getenv ("SHELL"); if (!shell) r = system ("/bin/sh"); else { /* Set a magic prompt. We only know how to do this for bash. */ p = strrchr (shell, '/'); if (p && strcmp (p+1, "bash") == 0) { size_t len = 64 + strlen (shell); char buf[len]; snprintf (buf, len, "PS1='mount-local-shell> ' %s --norc -i", shell); r = system (buf); } else r = system (shell); } if (r == -1) { fprintf (stderr, "error: failed to run sub-shell (%s) " "(is $SHELL set correctly?)\n", shell); //FALLTHROUGH } chdir ("/"); guestfs_umount_local (g, GUESTFS_UMOUNT_LOCAL_RETRY, 1, -1); _exit (EXIT_SUCCESS); } /* Note that we are *not* waiting for the child yet. We want to * run the FUSE code in parallel with the subshell. */ /* We're going to hide libguestfs errors here, but in a real program * you would probably want to log them somewhere. */ guestfs_push_error_handler (g, NULL, NULL); /* Now run the FUSE thread. */ if (guestfs_mount_local_run (g) == -1) exit (EXIT_FAILURE); guestfs_pop_error_handler (g); waitpid (pid, NULL, 0); /* Shutdown the handle explicitly so write errors can be detected. */ if (guestfs_shutdown (g) == -1) exit (EXIT_FAILURE); guestfs_close (g); printf ("\n" "Any files or directories that you copied in have been saved into\n" "the disk image called '%s'.\n" "\n" "Try opening the disk image with guestfish to see those files:\n" "\n" " guestfish -a %s -m /dev/sda1\n" "\n", argv[1], argv[1]); exit (EXIT_SUCCESS); }
static void * start_thread (void *vi) { guestfs_h *g; int r, thread_id = *(int *)vi; const char *error; g = guestfs_create (); if (g == NULL) { perror ("guestfs_create"); *(int *)vi = -1; pthread_exit (vi); } if (guestfs_add_drive_opts (g, "/dev/null", GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, -1) == -1) { *(int *)vi = -1; pthread_exit (vi); } /* Fake out qemu. */ if (guestfs_set_qemu (g, "/bin/true") == -1) { *(int *)vi = -1; pthread_exit (vi); } /* Wait for the other threads to finish starting up. */ r = pthread_barrier_wait (&barrier); if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) { fprintf (stderr, "pthread_barrier_wait: [thread %d]: %s\n", thread_id, strerror (r)); *(int *)vi = -1; pthread_exit (vi); } /* Launch the handle. Because of the faked out qemu, we expect this * will fail with "child process died unexpectedly". We are * interested in other failures. */ guestfs_push_error_handler (g, NULL, NULL); r = guestfs_launch (g); error = guestfs_last_error (g); if (r == 0) { /* This should NOT happen. */ fprintf (stderr, "rhbz790721: [thread %d]: " "strangeness in test: expected launch to fail, but it didn't!\n", thread_id); *(int *)vi = -1; pthread_exit (vi); } if (error == NULL) { /* This also should NOT happen. */ fprintf (stderr, "rhbz790721: [thread %d]: " "strangeness in test: no error message!\n", thread_id); *(int *)vi = -1; pthread_exit (vi); } /* If this happens, it indicates a bug/race in the appliance * building code which is what this regression test is designed to * spot. */ if (STRNEQ (error, "child process died unexpectedly")) { fprintf (stderr, "rhbz790721: [thread %d]: error: %s\n", thread_id, error); *(int *)vi = -1; pthread_exit (vi); } guestfs_pop_error_handler (g); /* Close the handle. */ guestfs_close (g); *(int *)vi = 0; pthread_exit (vi); }
static int do_make_fs (const char *input, const char *output_str) { const char *dev, *options; CLEANUP_UNLINK_FREE char *output = NULL; uint64_t estimate, size; struct guestfs_disk_create_argv optargs; CLEANUP_FREE char *ifmt = NULL; CLEANUP_FREE char *ifile = NULL; pid_t pid; int status, fd; /* Use of CLEANUP_UNLINK_FREE *output ensures the output file is * deleted unless we successfully reach the end of this function. */ output = strdup (output_str); if (output == NULL) { perror ("strdup"); return -1; } /* Input. What is it? Estimate how much space it will need. */ if (estimate_input (input, &estimate, &ifmt) == -1) return -1; if (verbose) { fprintf (stderr, "input format = %s\n", ifmt); fprintf (stderr, "estimate = %" PRIu64 " bytes " "(%" PRIu64 " 1K blocks, %" PRIu64 " 4K blocks)\n", estimate, estimate / 1024, estimate / 4096); } estimate += 256 * 1024; /* For superblocks &c. */ if (STRPREFIX (type, "ext") && type[3] >= '3') { /* For ext3+, add some more for the journal. */ estimate += 1024 * 1024; } else if (STREQ (type, "ntfs")) { estimate += 4 * 1024 * 1024; /* NTFS journal. */ } else if (STREQ (type, "btrfs")) { /* For BTRFS, the minimum metadata allocation is 256MB, with data * additional to that. Note that we disable data and metadata * duplication below. */ estimate += 256 * 1024 * 1024; } /* Add 10%, see above. */ estimate *= 1.10; /* Calculate the output size. */ if (size_str == NULL) size = estimate; else if (parse_size (size_str, estimate, &size) == -1) return -1; /* Create the output disk. */ optargs.bitmask = 0; if (STREQ (format, "qcow2")) { optargs.bitmask |= GUESTFS_DISK_CREATE_PREALLOCATION_BITMASK; optargs.preallocation = "metadata"; } if (guestfs_disk_create_argv (g, output, format, size, &optargs) == -1) return -1; if (guestfs_add_drive_opts (g, output, GUESTFS_ADD_DRIVE_OPTS_FORMAT, format, -1) == -1) return -1; if (guestfs_launch (g) == -1) return -1; if (check_ntfs_available () == -1) return -1; /* Partition the disk. */ dev = "/dev/sda"; if (partition) { int mbr_id = 0; if (STREQ (partition, "")) partition = "mbr"; if (guestfs_part_disk (g, dev, partition) == -1) return -1; dev = "/dev/sda1"; /* Set the partition type byte if it's MBR and the filesystem type * is one that we know about. */ if (STREQ (partition, "mbr") || STREQ (partition, "msdos")) { if (STREQ (type, "msdos")) /* According to Wikipedia. However I have not actually tried this. */ mbr_id = 0x1; else if (STREQ (type, "vfat") || STREQ (type, "fat")) mbr_id = 0xb; else if (STREQ (type, "ntfs")) mbr_id = 0x7; else if (STRPREFIX (type, "ext")) mbr_id = 0x83; else if (STREQ (type, "minix")) mbr_id = 0x81; } if (mbr_id != 0) { if (guestfs_part_set_mbr_id (g, "/dev/sda", 1, mbr_id) == -1) return -1; } } if (verbose) fprintf (stderr, "creating %s filesystem on %s ...\n", type, dev); /* Create the filesystem. */ if (STRNEQ (type, "btrfs")) { int r; struct guestfs_mkfs_opts_argv optargs = { .bitmask = 0 }; if (label) { optargs.label = label; optargs.bitmask |= GUESTFS_MKFS_OPTS_LABEL_BITMASK; } guestfs_push_error_handler (g, NULL, NULL); r = guestfs_mkfs_opts_argv (g, type, dev, &optargs); guestfs_pop_error_handler (g); if (r == -1) { /* Provide more guidance in the error message (RHBZ#823883). */ fprintf (stderr, "%s: 'mkfs' (create filesystem) operation failed: %s\n", guestfs_int_program_name, guestfs_last_error (g)); if (STREQ (type, "fat")) fprintf (stderr, "Instead of 'fat', try 'vfat' (long filenames) or 'msdos' (short filenames).\n"); else fprintf (stderr, "Is '%s' a correct filesystem type?\n", type); return -1; } } else {
static void output_root (xmlTextWriterPtr xo, char *root) { char *str; int i, r; char buf[32]; char *canonical_root; size_t size; XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "operatingsystem")); canonical_root = guestfs_canonical_device_name (g, root); if (canonical_root == NULL) exit (EXIT_FAILURE); XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "root", BAD_CAST canonical_root)); free (canonical_root); str = guestfs_inspect_get_type (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "name", BAD_CAST str)); free (str); str = guestfs_inspect_get_arch (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "arch", BAD_CAST str)); free (str); str = guestfs_inspect_get_distro (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "distro", BAD_CAST str)); free (str); str = guestfs_inspect_get_product_name (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "product_name", BAD_CAST str)); free (str); str = guestfs_inspect_get_product_variant (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "product_variant", BAD_CAST str)); free (str); i = guestfs_inspect_get_major_version (g, root); snprintf (buf, sizeof buf, "%d", i); XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "major_version", BAD_CAST buf)); i = guestfs_inspect_get_minor_version (g, root); snprintf (buf, sizeof buf, "%d", i); XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "minor_version", BAD_CAST buf)); str = guestfs_inspect_get_package_format (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "package_format", BAD_CAST str)); free (str); str = guestfs_inspect_get_package_management (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "package_management", BAD_CAST str)); free (str); /* inspect-get-windows-systemroot will fail with non-windows guests, * or if the systemroot could not be determined for a windows guest. * Disable error output around this call. */ guestfs_push_error_handler (g, NULL, NULL); str = guestfs_inspect_get_windows_systemroot (g, root); if (str) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "windows_systemroot", BAD_CAST str)); free (str); str = guestfs_inspect_get_windows_current_control_set (g, root); if (str) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "windows_current_control_set", BAD_CAST str)); free (str); guestfs_pop_error_handler (g); str = guestfs_inspect_get_hostname (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "hostname", BAD_CAST str)); free (str); str = guestfs_inspect_get_format (g, root); if (!str) exit (EXIT_FAILURE); if (STRNEQ (str, "unknown")) XMLERROR (-1, xmlTextWriterWriteElement (xo, BAD_CAST "format", BAD_CAST str)); free (str); r = guestfs_inspect_is_live (g, root); if (r > 0) { XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "live")); XMLERROR (-1, xmlTextWriterEndElement (xo)); } r = guestfs_inspect_is_netinst (g, root); if (r > 0) { XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "netinst")); XMLERROR (-1, xmlTextWriterEndElement (xo)); } r = guestfs_inspect_is_multipart (g, root); if (r > 0) { XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "multipart")); XMLERROR (-1, xmlTextWriterEndElement (xo)); } output_mountpoints (xo, root); output_filesystems (xo, root); output_drive_mappings (xo, root); /* We need to mount everything up in order to read out the list of * applications and the icon, ie. everything below this point. */ inspect_mount_root (g, root); output_applications (xo, root); /* Don't return favicon. RHEL 7 and Fedora have crappy 16x16 * favicons in the base distro. */ str = guestfs_inspect_get_icon (g, root, &size, GUESTFS_INSPECT_GET_ICON_FAVICON, 0, -1); if (!str) exit (EXIT_FAILURE); if (size > 0) { XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "icon")); XMLERROR (-1, xmlTextWriterWriteBase64 (xo, str, 0, size)); XMLERROR (-1, xmlTextWriterEndElement (xo)); } /* Note we must free (str) even if size == 0, because that indicates * there was no icon. */ free (str); /* Unmount (see inspect_mount_root above). */ if (guestfs_umount_all (g) == -1) exit (EXIT_FAILURE); XMLERROR (-1, xmlTextWriterEndElement (xo)); }
static void do_output_filesystems (void) { size_t i; CLEANUP_FREE_STRING_LIST char **fses = guestfs_list_filesystems (g); if (fses == NULL) exit (EXIT_FAILURE); for (i = 0; fses[i] != NULL; i += 2) { CLEANUP_FREE char *dev = NULL, *vfs_label = NULL, *vfs_uuid = NULL; CLEANUP_FREE_STRING_LIST char **parents = NULL; int64_t size = -1; /* Skip swap and unknown, unless --extra flag was given. */ if (!(output & OUTPUT_FILESYSTEMS_EXTRA) && (STREQ (fses[i+1], "swap") || STREQ (fses[i+1], "unknown"))) continue; dev = guestfs_canonical_device_name (g, fses[i]); if (dev == NULL) exit (EXIT_FAILURE); /* Only bother to look these up if we will be displaying them, * otherwise pass them as NULL. */ if ((columns & COLUMN_VFS_LABEL)) { guestfs_push_error_handler (g, NULL, NULL); vfs_label = guestfs_vfs_label (g, fses[i]); guestfs_pop_error_handler (g); if (vfs_label == NULL) { vfs_label = strdup (""); if (!vfs_label) { perror ("strdup"); exit (EXIT_FAILURE); } } } if ((columns & COLUMN_UUID)) { guestfs_push_error_handler (g, NULL, NULL); vfs_uuid = guestfs_vfs_uuid (g, fses[i]); guestfs_pop_error_handler (g); if (vfs_uuid == NULL) { vfs_uuid = strdup (""); if (!vfs_uuid) { perror ("strdup"); exit (EXIT_FAILURE); } } } if ((columns & COLUMN_SIZE)) { size = guestfs_blockdev_getsize64 (g, fses[i]); if (size == -1) exit (EXIT_FAILURE); } if (is_md (fses[i])) parents = parents_of_md (fses[i]); else parents = no_parents (); write_row (dev, "filesystem", fses[i+1], vfs_label, -1, size, parents, vfs_uuid); } }
/* Find out if 'device' contains a filesystem. If it does, add * another entry in g->fses. */ int guestfs_int_check_for_filesystem_on (guestfs_h *g, const char *mountable) { CLEANUP_FREE char *vfs_type = NULL; int is_swap, r; struct inspect_fs *fs; CLEANUP_FREE_INTERNAL_MOUNTABLE struct guestfs_internal_mountable *m = NULL; int whole_device = 0; /* Get vfs-type in order to check if it's a Linux(?) swap device. * If there's an error we should ignore it, so to do that we have to * temporarily replace the error handler with a null one. */ guestfs_push_error_handler (g, NULL, NULL); vfs_type = guestfs_vfs_type (g, mountable); guestfs_pop_error_handler (g); is_swap = vfs_type && STREQ (vfs_type, "swap"); debug (g, "check_for_filesystem_on: %s (%s)", mountable, vfs_type ? vfs_type : "failed to get vfs type"); if (is_swap) { extend_fses (g); fs = &g->fses[g->nr_fses-1]; fs->mountable = safe_strdup (g, mountable); return 0; } m = guestfs_internal_parse_mountable (g, mountable); if (m == NULL) return -1; /* If it's a whole device, see if it is an install ISO. */ if (m->im_type == MOUNTABLE_DEVICE) { whole_device = guestfs_is_whole_device (g, m->im_device); if (whole_device == -1) { return -1; } } if (whole_device) { extend_fses (g); fs = &g->fses[g->nr_fses-1]; r = guestfs_int_check_installer_iso (g, fs, m->im_device); if (r == -1) { /* Fatal error. */ g->nr_fses--; return -1; } if (r > 0) /* Found something. */ return 0; /* Didn't find anything. Fall through ... */ g->nr_fses--; } /* Try mounting the device. As above, ignore errors. */ guestfs_push_error_handler (g, NULL, NULL); if (vfs_type && STREQ (vfs_type, "ufs")) { /* Hack for the *BSDs. */ /* FreeBSD fs is a variant of ufs called ufs2 ... */ r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", mountable, "/"); if (r == -1) /* while NetBSD and OpenBSD use another variant labeled 44bsd */ r = guestfs_mount_vfs (g, "ro,ufstype=44bsd", "ufs", mountable, "/"); } else { r = guestfs_mount_ro (g, mountable, "/"); } guestfs_pop_error_handler (g); if (r == -1) return 0; /* Do the rest of the checks. */ r = check_filesystem (g, mountable, m, whole_device); /* Unmount the filesystem. */ if (guestfs_umount_all (g) == -1) return -1; return r; }
static void test_virtio_serial (void) { int fd, r, eh; char tmpfile[] = "/tmp/speedtestXXXXXX"; struct sigaction sa, old_sa; if (!virtio_serial_upload && !virtio_serial_download) return; /* Create a sparse file. We could upload from /dev/zero, but we * won't get progress messages because libguestfs tests if the * source file is a regular file. */ fd = mkstemp (tmpfile); if (fd == -1) error (EXIT_FAILURE, errno, "mkstemp: %s", tmpfile); if (ftruncate (fd, TEST_SERIAL_MAX_SIZE) == -1) error (EXIT_FAILURE, errno, "ftruncate"); if (close (fd) == -1) error (EXIT_FAILURE, errno, "close"); g = guestfs_create (); if (!g) error (EXIT_FAILURE, errno, "guestfs_create"); if (guestfs_add_drive_scratch (g, INT64_C (100*1024*1024), -1) == -1) exit (EXIT_FAILURE); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); /* Make and mount a filesystem which will be used by the download test. */ if (guestfs_mkfs (g, "ext4", "/dev/sda") == -1) exit (EXIT_FAILURE); if (guestfs_mount (g, "/dev/sda", "/") == -1) exit (EXIT_FAILURE); /* Time out the upload after TEST_SERIAL_MAX_TIME seconds have passed. */ memset (&sa, 0, sizeof sa); sa.sa_handler = stop_transfer; sa.sa_flags = SA_RESTART; sigaction (SIGALRM, &sa, &old_sa); /* Get progress messages, which will tell us how much data has been * transferred. */ eh = guestfs_set_event_callback (g, progress_cb, GUESTFS_EVENT_PROGRESS, 0, NULL); if (eh == -1) exit (EXIT_FAILURE); if (virtio_serial_upload) { gettimeofday (&start, NULL); rate = -1; operation = "upload"; alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME); /* For the upload test, upload the sparse file to /dev/null in the * appliance. Hopefully this is mostly testing just virtio-serial. */ guestfs_push_error_handler (g, NULL, NULL); r = guestfs_upload (g, tmpfile, "/dev/null"); alarm (0); unlink (tmpfile); guestfs_pop_error_handler (g); /* It's possible that the upload will finish before the alarm fires, * or that the upload will be stopped by the alarm. */ if (r == -1 && guestfs_last_errno (g) != EINTR) { fprintf (stderr, "%s: expecting upload command to return EINTR\n%s\n", guestfs_int_program_name, guestfs_last_error (g)); exit (EXIT_FAILURE); } if (rate == -1) { rate_error: fprintf (stderr, "%s: internal error: progress callback was not called! (r=%d, errno=%d)\n", guestfs_int_program_name, r, guestfs_last_errno (g)); exit (EXIT_FAILURE); } print_rate ("virtio-serial upload rate:", rate); } if (virtio_serial_download) { /* For the download test, download a sparse file within the * appliance to /dev/null on the host. */ if (guestfs_touch (g, "/sparse") == -1) exit (EXIT_FAILURE); if (guestfs_truncate_size (g, "/sparse", TEST_SERIAL_MAX_SIZE) == -1) exit (EXIT_FAILURE); gettimeofday (&start, NULL); rate = -1; operation = "download"; alarm (max_time_override > 0 ? max_time_override : TEST_SERIAL_MAX_TIME); guestfs_push_error_handler (g, NULL, NULL); r = guestfs_download (g, "/sparse", "/dev/null"); alarm (0); guestfs_pop_error_handler (g); if (r == -1 && guestfs_last_errno (g) != EINTR) { fprintf (stderr, "%s: expecting download command to return EINTR\n%s\n", guestfs_int_program_name, guestfs_last_error (g)); exit (EXIT_FAILURE); } if (rate == -1) goto rate_error; print_rate ("virtio-serial download rate:", rate); } if (guestfs_shutdown (g) == -1) exit (EXIT_FAILURE); guestfs_close (g); /* Restore SIGALRM signal handler. */ sigaction (SIGALRM, &old_sa, NULL); }