The current protocol is message-oriented and doesn't support
streaming. But it's simple enough that we could add this.
-
-----------------------------------------------------------------------
-
-(From Ján ONDREJ (SAL))
-
-guestfish edit <filename>
ABS_PATH (path, -1);
CHROOT_IN;
- fd = open (path, O_WRONLY | O_CREAT | O_NOCTTY | O_NONBLOCK, 0666);
+ fd = open (path, O_WRONLY | O_CREAT | O_NOCTTY, 0666);
CHROOT_OUT;
if (fd == -1) {
size = strlen (content);
CHROOT_IN;
- fd = open (path, O_WRONLY | O_CREAT | O_NOCTTY | O_NONBLOCK, 0666);
+ fd = open (path, O_WRONLY | O_TRUNC | O_CREAT | O_NOCTTY, 0666);
CHROOT_OUT;
if (fd == -1) {
alloc.c \
cmds.c \
completion.c \
+ edit.c \
fish.c \
fish.h
guestfish_CFLAGS = \
static int parse_size (const char *str, off_t *size_rtn);
int
-do_alloc (int argc, char *argv[])
+do_alloc (const char *cmd, int argc, char *argv[])
{
off_t size;
int fd;
return -1;
}
- fd = open (argv[0], O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
+ fd = open (argv[0], O_WRONLY|O_CREAT|O_NOCTTY|O_TRUNC, 0666);
if (fd == -1) {
perror (argv[0]);
return -1;
--- /dev/null
+/* guestfish - the filesystem interactive shell
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "fish.h"
+
+/* guestfish edit command, suggested by Ján Ondrej, implemented by RWMJ */
+
+static int
+xwrite (int fd, const void *buf, size_t len)
+{
+ int r;
+
+ while (len > 0) {
+ r = write (fd, buf, len);
+ if (r == -1) {
+ perror ("write");
+ return -1;
+ }
+ buf += r;
+ len -= r;
+ }
+
+ return 0;
+}
+
+static char *
+load_file (const char *filename, int *len_r)
+{
+ int fd, r, start;
+ char *content = NULL, *p;
+ char buf[65536];
+
+ *len_r = 0;
+
+ fd = open (filename, O_RDONLY);
+ if (fd == -1) {
+ perror (filename);
+ return NULL;
+ }
+
+ while ((r = read (fd, buf, sizeof buf)) > 0) {
+ start = *len_r;
+ *len_r += r;
+ p = realloc (content, *len_r + 1);
+ if (p == NULL) {
+ perror ("realloc");
+ free (content);
+ return NULL;
+ }
+ content = p;
+ memcpy (content + start, buf, r);
+ content[start+r] = '\0';
+ }
+
+ if (r == -1) {
+ perror (filename);
+ free (content);
+ return NULL;
+ }
+
+ if (close (fd) == -1) {
+ perror (filename);
+ free (content);
+ return NULL;
+ }
+
+ return content;
+}
+
+int
+do_edit (const char *cmd, int argc, char *argv[])
+{
+ char filename[] = "/tmp/guestfishXXXXXX";
+ char buf[256];
+ const char *editor;
+ char *content, *content_new;
+ int r, fd, size;
+
+ if (argc != 1) {
+ fprintf (stderr, "use '%s filename' to edit a file\n", cmd);
+ return -1;
+ }
+
+ /* Choose an editor. */
+ if (strcasecmp (cmd, "vi") == 0)
+ editor = "vi";
+ else if (strcasecmp (cmd, "emacs") == 0)
+ editor = "emacs -nw";
+ else {
+ editor = getenv ("EDITOR");
+ if (editor == NULL)
+ editor = "vi"; /* could be cruel here and choose ed(1) */
+ }
+
+ /* Download the file and write it to a temporary. */
+ fd = mkstemp (filename);
+ if (fd == -1) {
+ perror ("mkstemp");
+ return -1;
+ }
+
+ if ((content = guestfs_cat (g, argv[0])) == NULL) {
+ close (fd);
+ unlink (filename);
+ return -1;
+ }
+
+ if (xwrite (fd, content, strlen (content)) == -1) {
+ close (fd);
+ unlink (filename);
+ free (content);
+ return -1;
+ }
+
+ if (close (fd) == -1) {
+ perror (filename);
+ unlink (filename);
+ free (content);
+ return -1;
+ }
+
+ /* Edit it. */
+ /* XXX Safe? */
+ snprintf (buf, sizeof buf, "%s %s", editor, filename);
+
+ r = system (buf);
+ if (r != 0) {
+ perror (buf);
+ unlink (filename);
+ free (content);
+ return -1;
+ }
+
+ /* Reload it. */
+ content_new = load_file (filename, &size);
+ if (content_new == NULL) {
+ unlink (filename);
+ free (content);
+ return -1;
+ }
+
+ unlink (filename);
+
+ /* Changed? */
+ if (strlen (content) == size && strncmp (content, content_new, size) == 0) {
+ free (content);
+ free (content_new);
+ return 0;
+ }
+
+ /* Write new content. */
+ if (guestfs_write_file (g, argv[0], content_new, size) == -1) {
+ free (content);
+ free (content_new);
+ return -1;
+ }
+
+ free (content);
+ free (content_new);
+ return 0;
+}
}
else if (strcasecmp (cmd, "alloc") == 0 ||
strcasecmp (cmd, "allocate") == 0)
- return do_alloc (argc, argv);
+ return do_alloc (cmd, argc, argv);
+ else if (strcasecmp (cmd, "edit") == 0 ||
+ strcasecmp (cmd, "vi") == 0 ||
+ strcasecmp (cmd, "emacs") == 0)
+ return do_edit (cmd, argc, argv);
else
return run_action (cmd, argc, argv);
}
printf ("%-20s %s\n",
"alloc", "allocate an image");
+ printf ("%-20s %s\n",
+ "edit", "edit a file in the image");
/* actions are printed after this (see list_commands) */
}
{
/* help for actions is auto-generated, see display_command */
- if (strcasecmp (cmd, "alloc") == 0)
+ if (strcasecmp (cmd, "alloc") == 0 ||
+ strcasecmp (cmd, "allocate") == 0)
printf ("alloc - allocate an image\n"
" alloc <filename> <size>\n"
"\n"
" <nn>M or <nn>MB number of megabytes\n"
" <nn>G or <nn>GB number of gigabytes\n"
" <nn>sects number of 512 byte sectors\n");
+ else if (strcasecmp (cmd, "edit") == 0 ||
+ strcasecmp (cmd, "vi") == 0 ||
+ strcasecmp (cmd, "emacs") == 0)
+ printf ("edit - edit a file in the image\n"
+ " edit <filename>\n"
+ "\n"
+ " This is used to edit a file.\n"
+ "\n"
+ " It is the equivalent of (and is implemented by)\n"
+ " running \"cat\", editing locally, and then \"write-file\".\n"
+ "\n"
+ " Normally it uses $EDITOR, but if you use the aliases\n"
+ " \"vi\" or \"emacs\" you will get those editors.\n"
+ "\n"
+ " NOTE: This will not work reliably for large files\n"
+ " (> 2 MB) or binary files containing \\0 bytes.\n");
else if (strcasecmp (cmd, "help") == 0)
printf ("help - display a list of commands or help on a command\n"
" help cmd\n"
" help\n");
- else if (strcasecmp (cmd, "quit") == 0)
+ else if (strcasecmp (cmd, "quit") == 0 ||
+ strcasecmp (cmd, "exit") == 0 ||
+ strcasecmp (cmd, "q") == 0)
printf ("quit - quit guestfish\n"
" quit\n");
else
extern char **do_completion (const char *text, int start, int end);
/* in alloc.c */
-extern int do_alloc (int argc, char *argv[]);
+extern int do_alloc (const char *cmd, int argc, char *argv[]);
+
+/* in edit.c */
+extern int do_edit (const char *cmd, int argc, char *argv[]);
#endif /* FISH_H */
add disk.img
run
mount /dev/VolGroup00/LogVol00 /
- upload new_motd /etc/motd
+ write_file /etc/motd "Hello users" 0
_EOF_
List the LVs in a guest:
the string C<,> (comma).");
("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
- [InitEmpty, TestOutput (
- [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
- ["mkfs"; "ext2"; "/dev/sda1"];
- ["mount"; "/dev/sda1"; "/"];
- ["write_file"; "/new"; "new file contents"; "0"];
- ["cat"; "/new"]], "new file contents")],
+ [InitBasicFS, TestOutput (
+ [["write_file"; "/new"; "new file contents"; "0"];
+ ["cat"; "/new"]], "new file contents");
+ InitBasicFS, TestOutput (
+ [["write_file"; "/new"; "\nnew file contents\n"; "0"];
+ ["cat"; "/new"]], "\nnew file contents\n");
+ InitBasicFS, TestOutput (
+ [["write_file"; "/new"; "\n\n"; "0"];
+ ["cat"; "/new"]], "\n\n");
+ InitBasicFS, TestOutput (
+ [["write_file"; "/new"; ""; "0"];
+ ["cat"; "/new"]], "");
+ InitBasicFS, TestOutput (
+ [["write_file"; "/new"; "\n\n\n"; "0"];
+ ["cat"; "/new"]], "\n\n\n");
+ InitBasicFS, TestOutput (
+ [["write_file"; "/new"; "\n"; "0"];
+ ["cat"; "/new"]], "\n")],
"create a file",
"\
This call creates a file called C<path>. The contents of the
static int test_write_file_0 (void)
{
- /* InitEmpty for write_file (0) */
+ /* InitBasicFS for write_file (0): create ext2 on /dev/sda1 */
{
int r;
suppress_error = 0;
if (r == -1)
return -1;
}
- /* TestOutput for write_file (0) */
{
char *lines[] = {
",",
if (r == -1)
return -1;
}
+ /* TestOutput for write_file (0) */
{
int r;
suppress_error = 0;
return 0;
}
+static int test_write_file_1 (void)
+{
+ /* InitBasicFS for write_file (1): create ext2 on /dev/sda1 */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_umount_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_lvm_remove_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *lines[] = {
+ ",",
+ NULL
+ };
+ int r;
+ suppress_error = 0;
+ r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mount (g, "/dev/sda1", "/");
+ if (r == -1)
+ return -1;
+ }
+ /* TestOutput for write_file (1) */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_write_file (g, "/new", "\nnew file contents\n", 0);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *r;
+ suppress_error = 0;
+ r = guestfs_cat (g, "/new");
+ if (r == NULL)
+ return -1;
+ if (strcmp (r, "\nnew file contents\n") != 0) {
+ fprintf (stderr, "test_write_file_1: expected \"\nnew file contents\n\" but got \"%s\"\n", r);
+ return -1;
+ }
+ free (r);
+ }
+ return 0;
+}
+
+static int test_write_file_2 (void)
+{
+ /* InitBasicFS for write_file (2): create ext2 on /dev/sda1 */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_umount_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_lvm_remove_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *lines[] = {
+ ",",
+ NULL
+ };
+ int r;
+ suppress_error = 0;
+ r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mount (g, "/dev/sda1", "/");
+ if (r == -1)
+ return -1;
+ }
+ /* TestOutput for write_file (2) */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_write_file (g, "/new", "\n\n", 0);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *r;
+ suppress_error = 0;
+ r = guestfs_cat (g, "/new");
+ if (r == NULL)
+ return -1;
+ if (strcmp (r, "\n\n") != 0) {
+ fprintf (stderr, "test_write_file_2: expected \"\n\n\" but got \"%s\"\n", r);
+ return -1;
+ }
+ free (r);
+ }
+ return 0;
+}
+
+static int test_write_file_3 (void)
+{
+ /* InitBasicFS for write_file (3): create ext2 on /dev/sda1 */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_umount_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_lvm_remove_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *lines[] = {
+ ",",
+ NULL
+ };
+ int r;
+ suppress_error = 0;
+ r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mount (g, "/dev/sda1", "/");
+ if (r == -1)
+ return -1;
+ }
+ /* TestOutput for write_file (3) */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_write_file (g, "/new", "", 0);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *r;
+ suppress_error = 0;
+ r = guestfs_cat (g, "/new");
+ if (r == NULL)
+ return -1;
+ if (strcmp (r, "") != 0) {
+ fprintf (stderr, "test_write_file_3: expected \"\" but got \"%s\"\n", r);
+ return -1;
+ }
+ free (r);
+ }
+ return 0;
+}
+
+static int test_write_file_4 (void)
+{
+ /* InitBasicFS for write_file (4): create ext2 on /dev/sda1 */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_umount_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_lvm_remove_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *lines[] = {
+ ",",
+ NULL
+ };
+ int r;
+ suppress_error = 0;
+ r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mount (g, "/dev/sda1", "/");
+ if (r == -1)
+ return -1;
+ }
+ /* TestOutput for write_file (4) */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_write_file (g, "/new", "\n\n\n", 0);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *r;
+ suppress_error = 0;
+ r = guestfs_cat (g, "/new");
+ if (r == NULL)
+ return -1;
+ if (strcmp (r, "\n\n\n") != 0) {
+ fprintf (stderr, "test_write_file_4: expected \"\n\n\n\" but got \"%s\"\n", r);
+ return -1;
+ }
+ free (r);
+ }
+ return 0;
+}
+
+static int test_write_file_5 (void)
+{
+ /* InitBasicFS for write_file (5): create ext2 on /dev/sda1 */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_umount_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_lvm_remove_all (g);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *lines[] = {
+ ",",
+ NULL
+ };
+ int r;
+ suppress_error = 0;
+ r = guestfs_sfdisk (g, "/dev/sda", 0, 0, 0, lines);
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mkfs (g, "ext2", "/dev/sda1");
+ if (r == -1)
+ return -1;
+ }
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_mount (g, "/dev/sda1", "/");
+ if (r == -1)
+ return -1;
+ }
+ /* TestOutput for write_file (5) */
+ {
+ int r;
+ suppress_error = 0;
+ r = guestfs_write_file (g, "/new", "\n", 0);
+ if (r == -1)
+ return -1;
+ }
+ {
+ char *r;
+ suppress_error = 0;
+ r = guestfs_cat (g, "/new");
+ if (r == NULL)
+ return -1;
+ if (strcmp (r, "\n") != 0) {
+ fprintf (stderr, "test_write_file_5: expected \"\n\" but got \"%s\"\n", r);
+ return -1;
+ }
+ free (r);
+ }
+ return 0;
+}
+
static int test_mkfs_0 (void)
{
/* InitEmpty for mkfs (0) */
exit (1);
}
- nr_tests = 58;
+ nr_tests = 63;
test_num++;
printf ("%3d/%3d test_blockdev_rereadpt_0\n", test_num, nr_tests);
failed++;
}
test_num++;
+ printf ("%3d/%3d test_write_file_1\n", test_num, nr_tests);
+ if (test_write_file_1 () == -1) {
+ printf ("test_write_file_1 FAILED\n");
+ failed++;
+ }
+ test_num++;
+ printf ("%3d/%3d test_write_file_2\n", test_num, nr_tests);
+ if (test_write_file_2 () == -1) {
+ printf ("test_write_file_2 FAILED\n");
+ failed++;
+ }
+ test_num++;
+ printf ("%3d/%3d test_write_file_3\n", test_num, nr_tests);
+ if (test_write_file_3 () == -1) {
+ printf ("test_write_file_3 FAILED\n");
+ failed++;
+ }
+ test_num++;
+ printf ("%3d/%3d test_write_file_4\n", test_num, nr_tests);
+ if (test_write_file_4 () == -1) {
+ printf ("test_write_file_4 FAILED\n");
+ failed++;
+ }
+ test_num++;
+ printf ("%3d/%3d test_write_file_5\n", test_num, nr_tests);
+ if (test_write_file_5 () == -1) {
+ printf ("test_write_file_5 FAILED\n");
+ failed++;
+ }
+ test_num++;
printf ("%3d/%3d test_mkfs_0\n", test_num, nr_tests);
if (test_mkfs_0 () == -1) {
printf ("test_mkfs_0 FAILED\n");