/* Run a diff on two files. */ static void diff (struct file *file1, guestfs_h *g1, struct file *file2, guestfs_h *g2) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g1); CLEANUP_FREE char *tmpd, *tmpda = NULL, *tmpdb = NULL, *cmd = NULL; int r; assert (is_reg (file1->stat->st_mode)); assert (is_reg (file2->stat->st_mode)); if (asprintf (&tmpd, "%s/virtdiffXXXXXX", tmpdir) < 0) { perror ("asprintf"); exit (EXIT_FAILURE); } if (mkdtemp (tmpd) == NULL) { perror ("mkdtemp"); exit (EXIT_FAILURE); } if (asprintf (&tmpda, "%s/a", tmpd) < 0 || asprintf (&tmpdb, "%s/b", tmpd) < 0) { perror ("asprintf"); exit (EXIT_FAILURE); } if (guestfs_download (g1, file1->path, tmpda) == -1) goto out; if (guestfs_download (g2, file2->path, tmpdb) == -1) goto out; /* Note that the tmpdir is safe, and the rest of the path * should not need quoting. */ if (asprintf (&cmd, "diff -u '%s' '%s' | tail -n +3", tmpda, tmpdb) < 0) { perror ("asprintf"); exit (EXIT_FAILURE); } if (verbose) fprintf (stderr, "%s\n", cmd); r = system (cmd); if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { fprintf (stderr, _("%s: external diff command failed\n"), guestfs_int_program_name); goto out; } printf ("@@ %s @@\n", _("End of diff")); out: unlink (tmpda); unlink (tmpdb); rmdir (tmpd); }
char * guestfs_impl_read_file (guestfs_h *g, const char *path, size_t *size_r) { int fd = -1; size_t size; CLEANUP_UNLINK_FREE char *tmpfile = NULL; char *ret = NULL; struct stat statbuf; tmpfile = guestfs_int_make_temp_path (g, "cat", NULL); if (tmpfile == NULL) goto err; if (guestfs_download (g, path, tmpfile) == -1) goto err; fd = open (tmpfile, O_RDONLY|O_CLOEXEC); if (fd == -1) { perrorf (g, "open: %s", tmpfile); goto err; } /* Read the whole file into memory. */ if (fstat (fd, &statbuf) == -1) { perrorf (g, "stat: %s", tmpfile); goto err; } /* Don't use safe_malloc, because we want to return an errno to the caller. */ size = statbuf.st_size; ret = malloc (size + 1); if (!ret) { perrorf (g, "malloc: %zu bytes", size + 1); goto err; } if (full_read (fd, ret, size) != size) { perrorf (g, "full-read: %s: %zu bytes", tmpfile, size + 1); goto err; } ret[size] = '\0'; if (close (fd) == -1) { perrorf (g, "close: %s", tmpfile); goto err; } /* Mustn't touch *size_r until we are sure that we won't return any * error (RHBZ#589039). */ *size_r = size; return ret; err: free (ret); if (fd >= 0) close (fd); return NULL; }
/* Download a guest file to a local temporary file. The file is * cached in the temporary directory, and is not downloaded again. * * The name of the temporary (downloaded) file is returned. The * caller must free the pointer, but does *not* need to delete the * temporary file. It will be deleted when the handle is closed. * * Refuse to download the guest file if it is larger than max_size. * On this and other errors, NULL is returned. * * There is actually one cache per 'struct inspect_fs *' in order * to handle the case of multiple roots. */ char * guestfs___download_to_tmp (guestfs_h *g, struct inspect_fs *fs, const char *filename, const char *basename, uint64_t max_size) { char *r; int fd; char devfd[32]; int64_t size; /* Make the basename unique by prefixing it with the fs number. * This also ensures there is one cache per filesystem. */ if (asprintf (&r, "%s/%td-%s", g->tmpdir, fs - g->fses, basename) == -1) { perrorf (g, "asprintf"); return NULL; } /* If the file has already been downloaded, return. */ if (access (r, R_OK) == 0) return r; /* Check size of remote file. */ size = guestfs_filesize (g, filename); if (size == -1) /* guestfs_filesize failed and has already set error in handle */ goto error; if ((uint64_t) size > max_size) { error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), filename, size); goto error; } fd = open (r, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0600); if (fd == -1) { perrorf (g, "open: %s", r); goto error; } snprintf (devfd, sizeof devfd, "/dev/fd/%d", fd); if (guestfs_download (g, filename, devfd) == -1) { unlink (r); close (fd); goto error; } if (close (fd) == -1) { perrorf (g, "close: %s", r); unlink (r); goto error; } return r; error: free (r); return NULL; }
int run_copy_out (const char *cmd, size_t argc, char *argv[]) { if (argc < 2) { fprintf (stderr, _("use 'copy-out <remote> [<remote>...] <localdir>' to copy files out of the image\n")); return -1; } /* Local directory is always the last arg. */ const char *local = argv[argc-1]; int nr_remotes = argc-1; struct stat statbuf; if (stat (local, &statbuf) == -1 || ! (S_ISDIR (statbuf.st_mode))) { fprintf (stderr, _("copy-out: target '%s' is not a directory\n"), local); return -1; } /* Download each remote one at a time using tar-out. */ int i, r; for (i = 0; i < nr_remotes; ++i) { char *remote = argv[i]; /* Allow win:... prefix on remotes. */ remote = win_prefix (remote); if (remote == NULL) return -1; /* If the remote is a file, download it. If it's a directory, * create the directory in local first before using tar-out. */ r = guestfs_is_file (g, remote); if (r == -1) { free (remote); return -1; } if (r == 1) { /* is file */ char buf[PATH_MAX]; const char *basename; if (split_path (buf, sizeof buf, remote, NULL, &basename) == -1) { free (remote); return -1; } char filename[PATH_MAX]; snprintf (filename, sizeof filename, "%s/%s", local, basename); if (guestfs_download (g, remote, filename) == -1) { free (remote); return -1; } } else { /* not a regular file */ r = guestfs_is_dir (g, remote); if (r == -1) { free (remote); return -1; } if (r == 0) { fprintf (stderr, _("copy-out: '%s' is not a file or directory\n"), remote); free (remote); return -1; } char buf[PATH_MAX]; const char *basename; if (split_path (buf, sizeof buf, remote, NULL, &basename) == -1) { free (remote); return -1; } struct fd_pid fdpid = make_tar_output (local, basename); if (fdpid.fd == -1) { free (remote); return -1; } char fdbuf[64]; snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fdpid.fd); int r = guestfs_tar_out (g, remote, fdbuf); if (close (fdpid.fd) == -1) { perror ("close (tar-output subprocess)"); free (remote); r = -1; } int status; if (waitpid (fdpid.pid, &status, 0) == -1) { perror ("wait (tar-output subprocess)"); free (remote); return -1; } if (!(WIFEXITED (status) && WEXITSTATUS (status) == 0)) { free (remote); return -1; } if (r == -1) { free (remote); return -1; } } free (remote); } return 0; }
/* For Windows >= Vista, if evtxdump.py is installed then we can * use it to dump the System.evtx log. */ static int do_log_windows_evtx (void) { CLEANUP_FREE char *filename = NULL; CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); CLEANUP_UNLINK_FREE char *localfile = NULL; CLEANUP_FREE char *cmd = NULL; char dev_fd[64]; int fd, status; if (system ("evtxdump.py -h >/dev/null 2>&1") != 0) { fprintf (stderr, _("%s: you need to install 'evtxdump.py' (from the python-evtx package)\n" "in order to parse Windows Event Logs. If you cannot install this, then\n" "use virt-copy-out(1) to copy the contents of /Windows/System32/winevt/Logs\n" "from this guest, and examine in a binary file viewer.\n"), guestfs_int_program_name); return -1; } /* Check if System.evtx exists. XXX Allow the filename to be * configurable, since there are many logs. */ filename = guestfs_case_sensitive_path (g, "/Windows/System32/winevt/Logs/System.evtx"); if (filename == NULL) return -1; /* Note that guestfs_case_sensitive_path does NOT check for existence. */ if (guestfs_is_file_opts (g, filename, GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) <= 0) { fprintf (stderr, _("%s: Windows Event Log file (%s) not found\n"), guestfs_int_program_name, filename); return -1; } /* Download the file to a temporary. Python-evtx wants to mmap * the file so we cannot use a pipe. */ if (asprintf (&localfile, "%s/virtlogXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } if ((fd = mkstemp (localfile)) == -1) { perror ("mkstemp"); return -1; } snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fd); if (guestfs_download (g, filename, dev_fd) == -1) return -1; close (fd); /* This should be safe as long as $TMPDIR is not set to something wild. */ if (asprintf (&cmd, "evtxdump.py '%s'", localfile) == -1) { perror ("asprintf"); return -1; } status = system (cmd); if (status) { char buf[256]; fprintf (stderr, "%s: %s\n", guestfs_int_program_name, guestfs_int_exit_status_to_string (status, "evtxdump.py", buf, sizeof buf)); return -1; } return 0; }
static int do_log_text_file (const char *filename) { return guestfs_download (g, filename, "/dev/stdout"); }
static void edit (const char *filename, const char *root) { char *filename_to_free = NULL; const char *tmpdir = guestfs_tmpdir (); char tmpfile[strlen (tmpdir) + 32]; sprintf (tmpfile, "%s/virteditXXXXXX", tmpdir); int fd; char fdbuf[32]; char *upload_from = NULL; char *newname = NULL; char *backupname = NULL; /* Windows? Special handling is required. */ if (is_windows (g, root)) filename = filename_to_free = windows_path (g, root, filename); /* Download the file to a temporary. */ fd = mkstemp (tmpfile); if (fd == -1) { perror ("mkstemp"); exit (EXIT_FAILURE); } snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd); if (guestfs_download (g, filename, fdbuf) == -1) goto error; if (close (fd) == -1) { perror (tmpfile); goto error; } if (!perl_expr) upload_from = edit_interactively (tmpfile); else upload_from = edit_non_interactively (tmpfile); /* We don't always need to upload: upload_from could be NULL because * the user closed the editor without changing the file. */ if (upload_from) { /* Upload to a new file in the same directory, so if it fails we * don't end up with a partially written file. Give the new file * a completely random name so we have only a tiny chance of * overwriting some existing file. */ newname = generate_random_name (filename); if (guestfs_upload (g, upload_from, newname) == -1) goto error; /* Backup or overwrite the file. */ if (backup_extension) { backupname = generate_backup_name (filename); if (guestfs_mv (g, filename, backupname) == -1) goto error; } if (guestfs_mv (g, newname, filename) == -1) goto error; } unlink (tmpfile); free (filename_to_free); free (upload_from); free (newname); free (backupname); return; error: unlink (tmpfile); exit (EXIT_FAILURE); }
int run_edit (const char *cmd, size_t argc, char *argv[]) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); CLEANUP_UNLINK_FREE char *filename = NULL; char buf[256]; const char *editor; CLEANUP_FREE char *remotefilename = NULL, *newname = NULL; struct stat oldstat, newstat; int r, fd; if (argc != 1) { fprintf (stderr, _("use '%s filename' to edit a file\n"), cmd); return -1; } /* Choose an editor. */ if (STRCASEEQ (cmd, "vi")) editor = "vi"; else if (STRCASEEQ (cmd, "emacs")) editor = "emacs -nw"; else { editor = getenv ("EDITOR"); if (editor == NULL) editor = "vi"; /* could be cruel here and choose ed(1) */ } /* Handle 'win:...' prefix. */ remotefilename = win_prefix (argv[0]); if (remotefilename == NULL) return -1; /* Download the file and write it to a temporary. */ if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remotefilename, buf) == -1) { close (fd); return -1; } if (close (fd) == -1) { perror (filename); return -1; } /* Get the old stat. */ if (stat (filename, &oldstat) == -1) { perror (filename); return -1; } /* Edit it. */ /* XXX Safe? */ snprintf (buf, sizeof buf, "%s %s", editor, filename); r = system (buf); if (r != 0) { perror (buf); return -1; } /* Get the new stat. */ if (stat (filename, &newstat) == -1) { perror (filename); return -1; } /* Changed? */ if (oldstat.st_ctime == newstat.st_ctime && oldstat.st_size == newstat.st_size) return 0; /* Upload to a new file in the same directory, so if it fails we * don't end up with a partially written file. Give the new file * a completely random name so we have only a tiny chance of * overwriting some existing file. */ newname = generate_random_name (remotefilename); if (!newname) return -1; /* Write new content. */ if (guestfs_upload (g, filename, newname) == -1) return -1; /* Set the permissions, UID, GID and SELinux context of the new * file to match the old file (RHBZ#788641). */ if (guestfs_copy_attributes (g, remotefilename, newname, GUESTFS_COPY_ATTRIBUTES_ALL, 1, -1) == -1) return -1; if (guestfs_mv (g, newname, remotefilename) == -1) return -1; return 0; }
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); }
int run_more (const char *cmd, size_t argc, char *argv[]) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g); CLEANUP_UNLINK_FREE char *filename = NULL; char buf[256]; CLEANUP_FREE char *remote = NULL; const char *pager; int r, fd; if (argc != 1) { fprintf (stderr, _("use '%s filename' to page a file\n"), cmd); return -1; } /* Choose a pager. */ if (STRCASEEQ (cmd, "less")) pager = "less"; else { pager = getenv ("PAGER"); if (pager == NULL) pager = "more"; } /* Allow win:... prefix on remote. */ remote = win_prefix (argv[0]); if (remote == NULL) return -1; /* Download the file and write it to a temporary. */ if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remote, buf) == -1) { close (fd); return -1; } if (close (fd) == -1) { perror (filename); return -1; } /* View it. */ /* XXX Safe? */ snprintf (buf, sizeof buf, "%s %s", pager, filename); r = system (buf); if (r != 0) { perror (buf); return -1; } return 0; }
int run_more (const char *cmd, size_t argc, char *argv[]) { TMP_TEMPLATE_ON_STACK (filename); char buf[256]; char *remote; const char *pager; int r, fd; if (argc != 1) { fprintf (stderr, _("use '%s filename' to page a file\n"), cmd); return -1; } /* Choose a pager. */ if (STRCASEEQ (cmd, "less")) pager = "less"; else { pager = getenv ("PAGER"); if (pager == NULL) pager = "more"; } remote = argv[0]; /* Allow win:... prefix on remote. */ remote = win_prefix (remote); if (remote == NULL) return -1; /* Download the file and write it to a temporary. */ fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); free (remote); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remote, buf) == -1) { close (fd); unlink (filename); free (remote); return -1; } if (close (fd) == -1) { perror (filename); unlink (filename); free (remote); return -1; } /* View it. */ /* XXX Safe? */ snprintf (buf, sizeof buf, "%s %s", pager, filename); r = system (buf); unlink (filename); if (r != 0) { perror (buf); free (remote); return -1; } free (remote); return 0; }
int run_display (const char *cmd, size_t argc, char *argv[]) { TMP_TEMPLATE_ON_STACK (filename); char *remote; const char *display; char buf[256]; int r, fd; if (argc != 1) { fprintf (stderr, _("display filename\n")); return -1; } /* Choose a display command. */ display = getenv ("GUESTFISH_DISPLAY_IMAGE"); if (display == NULL) display = "display"; remote = argv[0]; /* Allow win:... prefix on remote. */ remote = win_prefix (remote); if (remote == NULL) return -1; /* Download the file and write it to a temporary. */ fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); free (remote); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remote, buf) == -1) { close (fd); unlink (filename); free (remote); return -1; } if (close (fd) == -1) { perror (filename); unlink (filename); free (remote); return -1; } /* View it. */ snprintf (buf, sizeof buf, "%s %s", display, filename); r = system (buf); unlink (filename); if (r != 0) { perror (buf); free (remote); return -1; } free (remote); return 0; }
int guestfs_impl_copy_out (guestfs_h *g, const char *remotepath, const char *localdir) { struct stat statbuf; int r; if (stat (localdir, &statbuf) == -1 || ! (S_ISDIR (statbuf.st_mode))) { error (g, _("target '%s' is not a directory"), localdir); return -1; } /* If the remote is a file, download it. If it's a directory, * create the directory in localdir first before using tar-out. */ r = guestfs_is_file (g, remotepath); if (r == -1) return -1; if (r == 1) { /* is file */ CLEANUP_FREE char *filename = NULL; size_t buf_len = strlen (remotepath) + 1; CLEANUP_FREE char *buf = safe_malloc (g, buf_len); const char *basename; if (split_path (g, buf, buf_len, remotepath, NULL, &basename) == -1) return -1; if (asprintf (&filename, "%s/%s", localdir, basename) == -1) { perrorf (g, "asprintf"); return -1; } if (guestfs_download (g, remotepath, filename) == -1) return -1; } else { /* not a regular file */ CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); struct copy_out_child_data data; char fdbuf[64]; int fd; r = guestfs_is_dir (g, remotepath); if (r == -1) return -1; if (r == 0) { error (g, _("'%s' is not a file or directory"), remotepath); return -1; } size_t buf_len = strlen (remotepath) + 1; CLEANUP_FREE char *buf = safe_malloc (g, buf_len); const char *basename; if (split_path (g, buf, buf_len, remotepath, NULL, &basename) == -1) return -1; /* RHBZ#845522: If remotepath == "/" then basename would be an empty * string. Replace it with "." so that make_tar_output writes * to "localdir/." */ if (STREQ (basename, "")) basename = "."; data.localdir = localdir; data.basename = basename; guestfs_int_cmd_set_child_callback (cmd, &child_setup, &data); guestfs_int_cmd_add_arg (cmd, "tar"); guestfs_int_cmd_add_arg (cmd, "-xf"); guestfs_int_cmd_add_arg (cmd, "-"); guestfs_int_cmd_clear_capture_errors (cmd); fd = guestfs_int_cmd_pipe_run (cmd, "w"); if (fd == -1) return -1; snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd); r = guestfs_tar_out (g, remotepath, fdbuf); if (close (fd) == -1) { perrorf (g, "close (tar-output subprocess)"); return -1; } r = guestfs_int_cmd_pipe_wait (cmd); if (r == -1) return -1; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { CLEANUP_FREE char *errors = guestfs_int_cmd_get_pipe_errors (cmd); if (errors == NULL) return -1; error (g, "tar subprocess failed: %s", errors); return -1; } } return 0; }
int run_display (const char *cmd, size_t argc, char *argv[]) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *filename = NULL; CLEANUP_FREE char *remote = NULL; const char *display; char buf[256]; int r, fd; if (argc != 1) { fprintf (stderr, _("display filename\n")); return -1; } /* Choose a display command. */ display = getenv ("GUESTFISH_DISPLAY_IMAGE"); if (display == NULL) display = "display"; /* Allow win:... prefix on remote. */ remote = win_prefix (argv[0]); if (remote == NULL) return -1; /* Download the file and write it to a temporary. */ if (asprintf (&filename, "%s/guestfishXXXXXX", tmpdir) == -1) { perror ("asprintf"); return -1; } fd = mkstemp (filename); if (fd == -1) { perror ("mkstemp"); return -1; } snprintf (buf, sizeof buf, "/dev/fd/%d", fd); if (guestfs_download (g, remote, buf) == -1) { close (fd); unlink (filename); return -1; } if (close (fd) == -1) { perror (filename); unlink (filename); return -1; } /* View it. */ snprintf (buf, sizeof buf, "%s %s", display, filename); r = system (buf); unlink (filename); if (r != 0) { perror (buf); return -1; } return 0; }
int main (int argc, char *argv[]) { /* Set global program name that is not polluted with libtool artifacts. */ set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEBASEDIR); textdomain (PACKAGE); enum { HELP_OPTION = CHAR_MAX + 1 }; static const char *options = "a:c:d:vVx"; static const struct option long_options[] = { { "add", 1, 0, 'a' }, { "connect", 1, 0, 'c' }, { "domain", 1, 0, 'd' }, { "echo-keys", 0, 0, 0 }, { "format", 2, 0, 0 }, { "help", 0, 0, HELP_OPTION }, { "keys-from-stdin", 0, 0, 0 }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } }; struct drv *drvs = NULL; struct drv *drv; const char *format = NULL; int c; int option_index; g = guestfs_create (); if (g == NULL) { fprintf (stderr, _("guestfs_create: failed to create handle\n")); exit (EXIT_FAILURE); } argv[0] = bad_cast (program_name); for (;;) { 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, "keys-from-stdin")) { keys_from_stdin = 1; } else if (STREQ (long_options[option_index].name, "echo-keys")) { echo_keys = 1; } else if (STREQ (long_options[option_index].name, "format")) { if (!optarg || STREQ (optarg, "")) format = NULL; else format = optarg; } else { fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), program_name, long_options[option_index].name, option_index); exit (EXIT_FAILURE); } break; case 'a': OPTION_a; break; case 'c': OPTION_c; break; case 'd': OPTION_d; break; case 'v': OPTION_v; break; case 'V': OPTION_V; break; case 'x': OPTION_x; break; case HELP_OPTION: usage (EXIT_SUCCESS); default: usage (EXIT_FAILURE); } } /* Old-style syntax? There were no -a or -d options in the old * virt-cat which is how we detect this. */ if (drvs == NULL) { /* argc - 1 because last parameter is the single filename. */ while (optind < argc - 1) { if (strchr (argv[optind], '/') || access (argv[optind], F_OK) == 0) { /* simulate -a option */ drv = calloc (1, sizeof (struct drv)); if (!drv) { perror ("malloc"); exit (EXIT_FAILURE); } drv->type = drv_a; drv->a.filename = argv[optind]; drv->next = drvs; drvs = drv; } else { /* simulate -d option */ drv = calloc (1, sizeof (struct drv)); if (!drv) { perror ("malloc"); exit (EXIT_FAILURE); } drv->type = drv_d; drv->d.guest = argv[optind]; drv->next = drvs; drvs = drv; } optind++; } } /* These are really constants, but they have to be variables for the * options parsing code. Assert here that they have known-good * values. */ assert (read_only == 1); assert (inspector == 1); assert (live == 0); /* User must specify at least one filename on the command line. */ if (optind >= argc || argc - optind < 1) usage (EXIT_FAILURE); /* User must have specified some drives. */ if (drvs == NULL) usage (EXIT_FAILURE); /* Add drives, inspect and mount. Note that inspector is always true, * and there is no -m option. */ add_drives (drvs, 'a'); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); inspect_mount (); /* Free up data structures, no longer needed after this point. */ free_drives (drvs); unsigned errors = 0; int windows; char *root, **roots; /* Get root mountpoint. See: fish/inspect.c:inspect_mount */ roots = guestfs_inspect_get_roots (g); assert (roots); assert (roots[0] != NULL); assert (roots[1] == NULL); root = roots[0]; free (roots); /* Windows? Special handling is required. */ windows = is_windows (g, root); for (; optind < argc; optind++) { char *filename_to_free = NULL; const char *filename = argv[optind]; if (windows) { filename = filename_to_free = windows_path (g, root, filename); if (filename == NULL) { errors++; continue; } } if (guestfs_download (g, filename, "/dev/stdout") == -1) errors++; free (filename_to_free); } free (root); guestfs_close (g); exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE); }
static char * cpio_arch (guestfs_h *g, const char *file, const char *path) { CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL; CLEANUP_FREE char *initrd = NULL; CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g); char *ret = NULL; const char *method; int64_t size; int r; size_t i; if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) { perror ("asprintf"); return NULL; } if (strstr (file, "gzip")) method = "zcat"; else if (strstr (file, "bzip2")) method = "bzcat"; else method = "cat"; /* Security: Refuse to download initrd if it is huge. */ size = guestfs_filesize (g, path); if (size == -1 || size > 100000000) { error (g, _("size of %s unreasonable (%" PRIi64 " bytes)"), path, size); goto out; } if (mkdtemp (dir) == NULL) { perrorf (g, "mkdtemp"); goto out; } initrd = safe_asprintf (g, "%s/initrd", dir); if (guestfs_download (g, path, initrd) == -1) goto out; /* Construct a command to extract named binaries from the initrd file. */ guestfs___cmd_add_string_unquoted (cmd, "cd "); guestfs___cmd_add_string_quoted (cmd, dir); guestfs___cmd_add_string_unquoted (cmd, " && "); guestfs___cmd_add_string_unquoted (cmd, method); guestfs___cmd_add_string_unquoted (cmd, " initrd | cpio --quiet -id"); for (i = 0; initrd_binaries[i] != NULL; ++i) { guestfs___cmd_add_string_unquoted (cmd, " "); guestfs___cmd_add_string_quoted (cmd, initrd_binaries[i]); } r = guestfs___cmd_run (cmd); if (r == -1) goto out; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { guestfs___external_command_failed (g, r, "cpio", path); goto out; } for (i = 0; initrd_binaries[i] != NULL; ++i) { CLEANUP_FREE char *bin = safe_asprintf (g, "%s/%s", dir, initrd_binaries[i]); if (is_regular_file (bin)) { int flags = g->verbose ? MAGIC_DEBUG : 0; flags |= MAGIC_ERROR | MAGIC_RAW; magic_t m = magic_open (flags); if (m == NULL) { perrorf (g, "magic_open"); goto out; } if (magic_load (m, NULL) == -1) { perrorf (g, "magic_load: default magic database file"); magic_close (m); goto out; } const char *line = magic_file (m, bin); if (line == NULL) { perrorf (g, "magic_file: %s", bin); magic_close (m); goto out; } CLEANUP_FREE char *elf_arch = match1 (g, line, re_file_elf); if (elf_arch != NULL) { ret = canonical_elf_arch (g, elf_arch); magic_close (m); goto out; } magic_close (m); } } error (g, "file_architecture: could not determine architecture of cpio archive"); out: guestfs___recursive_remove_dir (g, dir); return ret; }