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); }
int main (int argc, char *argv[]) { setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEBASEDIR); textdomain (PACKAGE); parse_config (); enum { HELP_OPTION = CHAR_MAX + 1 }; /* The command line arguments are broadly compatible with (a subset * of) guestfish. Thus we have to deal mainly with -a, -m and --ro. */ static const char options[] = "a:c:d:im:no:rvVwx"; static const struct option long_options[] = { { "add", 1, 0, 'a' }, { "connect", 1, 0, 'c' }, { "dir-cache-timeout", 1, 0, 0 }, { "domain", 1, 0, 'd' }, { "echo-keys", 0, 0, 0 }, { "fd", 1, 0, 0 }, { "format", 2, 0, 0 }, { "fuse-help", 0, 0, 0 }, { "help", 0, 0, HELP_OPTION }, { "inspector", 0, 0, 'i' }, { "keys-from-stdin", 0, 0, 0 }, { "live", 0, 0, 0 }, { "long-options", 0, 0, 0 }, { "mount", 1, 0, 'm' }, { "no-fork", 0, 0, 0 }, { "no-sync", 0, 0, 'n' }, { "option", 1, 0, 'o' }, { "pid-file", 1, 0, 0 }, { "ro", 0, 0, 'r' }, { "rw", 0, 0, 'w' }, { "selinux", 0, 0, 0 }, { "short-options", 0, 0, 0 }, { "trace", 0, 0, 'x' }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } }; struct drv *drvs = NULL; struct mp *mps = NULL; struct mp *mp; char *p; const char *format = NULL; bool format_consumed = true; int c, r; int option_index; struct sigaction sa; int debug_calls = 0; int dir_cache_timeout = -1; int do_fork = 1; char *fuse_options = NULL; char *pid_file = NULL; int pipe_fd = -1; struct guestfs_mount_local_argv optargs; /* LC_ALL=C is required so we can parse error messages. */ setenv ("LC_ALL", "C", 1); memset (&sa, 0, sizeof sa); sa.sa_handler = SIG_IGN; sa.sa_flags = SA_RESTART; sigaction (SIGPIPE, &sa, NULL); g = guestfs_create (); if (g == NULL) error (EXIT_FAILURE, errno, "guestfs_create"); 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, "long-options")) display_long_options (long_options); else if (STREQ (long_options[option_index].name, "short-options")) display_short_options (options); else if (STREQ (long_options[option_index].name, "dir-cache-timeout")) dir_cache_timeout = atoi (optarg); else if (STREQ (long_options[option_index].name, "fuse-help")) fuse_help (); else if (STREQ (long_options[option_index].name, "selinux")) { /* nothing */ } else if (STREQ (long_options[option_index].name, "format")) { OPTION_format; } else 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, "live")) { live = 1; } else if (STREQ (long_options[option_index].name, "pid-file")) { pid_file = optarg; } else if (STREQ (long_options[option_index].name, "no-fork")) { do_fork = 0; } else if (STREQ (long_options[option_index].name, "fd")) { if (sscanf (optarg, "%d", &pipe_fd) != 1 || pipe_fd < 0) error (EXIT_FAILURE, 0, _("unable to parse --fd option value: %s"), optarg); } else error (EXIT_FAILURE, 0, _("unknown long option: %s (%d)"), long_options[option_index].name, option_index); break; case 'a': OPTION_a; break; case 'c': OPTION_c; break; case 'd': OPTION_d; break; case 'i': OPTION_i; break; case 'm': OPTION_m; break; case 'n': OPTION_n; break; case 'o': fuse_opt_add_opt_escaped (&fuse_options, optarg); break; case 'r': OPTION_r; break; case 'v': OPTION_v; break; case 'V': OPTION_V; break; case 'w': OPTION_w; break; case 'x': OPTION_x; debug_calls = 1; do_fork = 0; break; case HELP_OPTION: usage (EXIT_SUCCESS); default: usage (EXIT_FAILURE); } } CHECK_OPTION_format_consumed; /* Check we have the right options. */ if (!live) { if (drvs == NULL) { fprintf (stderr, _("%s: error: you must specify at least one -a or -d option.\n"), getprogname ()); usage (EXIT_FAILURE); } if (!(mps || inspector)) { fprintf (stderr, _("%s: error: you must specify either -i at least one -m option.\n"), getprogname ()); usage (EXIT_FAILURE); } } else { size_t count_d = 0, count_other = 0; struct drv *drv; if (read_only) error (EXIT_FAILURE, 0, _("--live is not compatible with --ro option")); if (inspector) error (EXIT_FAILURE, 0, _("--live is not compatible with -i option")); /* --live: make sure there was one -d option and no -a options */ for (drv = drvs; drv; drv = drv->next) { if (drv->type == drv_d) count_d++; else count_other++; } if (count_d != 1) error (EXIT_FAILURE, 0, _("with --live, you must use exactly one -d option")); if (count_other != 0) error (EXIT_FAILURE, 0, _("--live is not compatible with -a option")); } /* We'd better have a mountpoint. */ if (optind+1 != argc) error (EXIT_FAILURE, 0, _("you must specify a mountpoint in the host filesystem")); /* If we're forking, we can't use the recovery process. */ if (guestfs_set_recovery_proc (g, !do_fork) == -1) exit (EXIT_FAILURE); /* Do the guest drives and mountpoints. */ add_drives (drvs, 'a'); if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); if (inspector) inspect_mount (); mount_mps (mps); free_drives (drvs); free_mps (mps); /* FUSE example does this, not clear if it's necessary, but ... */ if (guestfs_umask (g, 0) == -1) exit (EXIT_FAILURE); optargs.bitmask = 0; if (read_only) { optargs.bitmask |= GUESTFS_MOUNT_LOCAL_READONLY_BITMASK; optargs.readonly = 1; } if (debug_calls) { optargs.bitmask |= GUESTFS_MOUNT_LOCAL_DEBUGCALLS_BITMASK; optargs.debugcalls = 1; } if (dir_cache_timeout > 0) { optargs.bitmask |= GUESTFS_MOUNT_LOCAL_CACHETIMEOUT_BITMASK; optargs.cachetimeout = dir_cache_timeout; } if (fuse_options != NULL) { optargs.bitmask |= GUESTFS_MOUNT_LOCAL_OPTIONS_BITMASK; optargs.options = fuse_options; } if (guestfs_mount_local_argv (g, argv[optind], &optargs) == -1) exit (EXIT_FAILURE); /* Daemonize. */ if (do_fork) { pid_t pid; int fd; pid = fork (); if (pid == -1) error (EXIT_FAILURE, errno, "fork"); if (pid != 0) { /* parent */ if (write_pid_file (pid_file, pid) == -1) _exit (EXIT_FAILURE); if (write_pipe_fd (pipe_fd) == -1) _exit (EXIT_FAILURE); _exit (EXIT_SUCCESS); } /* Emulate what old fuse_daemonize used to do. */ if (setsid () == -1) error (EXIT_FAILURE, errno, "setsid"); ignore_value (chdir ("/")); fd = open ("/dev/null", O_RDWR); if (fd >= 0) { dup2 (fd, 0); dup2 (fd, 1); dup2 (fd, 2); if (fd > 2) close (fd); } } else { /* not forking, write PID file and pipe FD anyway */ if (write_pid_file (pid_file, getpid ()) == -1) exit (EXIT_FAILURE); if (write_pipe_fd (pipe_fd) == -1) exit (EXIT_FAILURE); } /* At the last minute, remove the libguestfs error handler. In code * above this point, the default error handler has been used which * sends all errors to stderr. From now on, the FUSE code will * convert errors into error codes (errnos) when appropriate. */ guestfs_push_error_handler (g, NULL, NULL); /* Main loop. */ r = guestfs_mount_local_run (g); guestfs_pop_error_handler (g); /* Cleanup. */ if (guestfs_shutdown (g) == -1) r = -1; guestfs_close (g); /* Don't delete PID file until the cleanup has been completed. */ if (pid_file) unlink (pid_file); exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE); }