From: Richard Jones Date: Fri, 8 May 2009 13:28:03 +0000 (+0100) Subject: Generated code to support previous 2 commits. X-Git-Tag: 1.0.22~13 X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;h=0faa5dde7b992ba11bb88f77b3424676c7c492e4;p=libguestfs.git Generated code to support previous 2 commits. --- diff --git a/daemon/actions.h b/daemon/actions.h index ac57a12..f4678f8 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -114,3 +114,6 @@ extern int do_drop_caches (int whattodrop); extern char *do_dmesg (void); extern int do_ping_daemon (void); extern int do_equal (const char *file1, const char *file2); +extern char **do_strings (const char *path); +extern char **do_strings_e (const char *encoding, const char *path); +extern char *do_hexdump (const char *path); diff --git a/daemon/stubs.c b/daemon/stubs.c index fd9c7ea..0e5b351 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -2312,6 +2312,91 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_equal_args, (char *) &args); } +static void strings_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_strings_args args; + const char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_strings_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "strings"); + return; + } + path = args.path; + + r = do_strings (path); + if (r == NULL) + /* do_strings has already called reply_with_error */ + goto done; + + struct guestfs_strings_ret ret; + ret.stringsout.stringsout_len = count_strings (r); + ret.stringsout.stringsout_val = r; + reply ((xdrproc_t) &xdr_guestfs_strings_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_strings_args, (char *) &args); +} + +static void strings_e_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_strings_e_args args; + const char *encoding; + const char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_strings_e_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "strings_e"); + return; + } + encoding = args.encoding; + path = args.path; + + r = do_strings_e (encoding, path); + if (r == NULL) + /* do_strings_e has already called reply_with_error */ + goto done; + + struct guestfs_strings_e_ret ret; + ret.stringsout.stringsout_len = count_strings (r); + ret.stringsout.stringsout_val = r; + reply ((xdrproc_t) &xdr_guestfs_strings_e_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_strings_e_args, (char *) &args); +} + +static void hexdump_stub (XDR *xdr_in) +{ + char *r; + struct guestfs_hexdump_args args; + const char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_hexdump_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "hexdump"); + return; + } + path = args.path; + + r = do_hexdump (path); + if (r == NULL) + /* do_hexdump has already called reply_with_error */ + goto done; + + struct guestfs_hexdump_ret ret; + ret.dump = r; + reply ((xdrproc_t) &xdr_guestfs_hexdump_ret, (char *) &ret); + free (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_hexdump_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -2594,6 +2679,15 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_EQUAL: equal_stub (xdr_in); break; + case GUESTFS_PROC_STRINGS: + strings_stub (xdr_in); + break; + case GUESTFS_PROC_STRINGS_E: + strings_e_stub (xdr_in); + break; + case GUESTFS_PROC_HEXDUMP: + hexdump_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index dc5d6a3..8c1cb53 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -81,6 +81,7 @@ void list_commands (void) printf ("%-20s %s\n", "get-state", "get the current state"); printf ("%-20s %s\n", "get-verbose", "get verbose mode"); printf ("%-20s %s\n", "grub-install", "install GRUB"); + printf ("%-20s %s\n", "hexdump", "dump a file in hexadecimal"); printf ("%-20s %s\n", "is-busy", "is busy processing a command"); printf ("%-20s %s\n", "is-config", "is in configuration state"); printf ("%-20s %s\n", "is-dir", "test if file exists"); @@ -126,6 +127,8 @@ void list_commands (void) printf ("%-20s %s\n", "sfdisk", "create partitions on a block device"); printf ("%-20s %s\n", "stat", "get file information"); printf ("%-20s %s\n", "statvfs", "get file system statistics"); + printf ("%-20s %s\n", "strings", "print the printable strings in a file"); + printf ("%-20s %s\n", "strings-e", "print the printable strings in a file"); printf ("%-20s %s\n", "sync", "sync disks, writes are flushed through to the disk image"); printf ("%-20s %s\n", "tar-in", "unpack tarfile to directory"); printf ("%-20s %s\n", "tar-out", "pack directory into tarfile"); @@ -331,7 +334,7 @@ void display_command (const char *cmd) pod2text ("sfdisk - create partitions on a block device", " sfdisk \n\nThis is a direct interface to the L program for creating\npartitions on block devices.\n\nC should be a block device, for example C.\n\nC, C and C are the number of cylinders, heads\nand sectors on the device, which are passed directly to sfdisk as\nthe I<-C>, I<-H> and I<-S> parameters. If you pass C<0> for any\nof these, then the corresponding parameter is omitted. Usually for\n'large' disks, you can just pass C<0> for these, but for small\n(floppy-sized) disks, sfdisk (or rather, the kernel) cannot work\nout the right geometry and you will need to tell it.\n\nC is a list of lines that we feed to C. For more\ninformation refer to the L manpage.\n\nTo create a single partition occupying the whole disk, you would\npass C as a single element list, when the single element being\nthe string C<,> (comma).\n\nB."); else if (strcasecmp (cmd, "write_file") == 0 || strcasecmp (cmd, "write-file") == 0) - pod2text ("write-file - create a file", " write-file \n\nThis call creates a file called C. The contents of the\nfile is the string C (which can contain any 8 bit data),\nwith length C.\n\nAs a special case, if C is C<0>\nthen the length is calculated using C (so in this case\nthe content cannot contain embedded ASCII NULs).\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); + pod2text ("write-file - create a file", " write-file \n\nThis call creates a file called C. The contents of the\nfile is the string C (which can contain any 8 bit data),\nwith length C.\n\nAs a special case, if C is C<0>\nthen the length is calculated using C (so in this case\nthe content cannot contain embedded ASCII NULs).\n\nI Owing to a bug, writing content containing ASCII NUL\ncharacters does I work, even if the length is specified.\nWe hope to resolve this bug in a future version. In the meantime\nuse C.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); else if (strcasecmp (cmd, "umount") == 0 || strcasecmp (cmd, "unmount") == 0) pod2text ("umount - unmount a filesystem", " umount \n\nThis unmounts the given filesystem. The filesystem may be\nspecified either by its mountpoint (path) or the device which\ncontains the filesystem.\n\nYou can use 'unmount' as an alias for this command."); @@ -480,6 +483,15 @@ void display_command (const char *cmd) if (strcasecmp (cmd, "equal") == 0) pod2text ("equal - test if two files have equal contents", " equal \n\nThis compares the two files C and C and returns\ntrue if their content is exactly equal, or false otherwise.\n\nThe external L program is used for the comparison."); else + if (strcasecmp (cmd, "strings") == 0) + pod2text ("strings - print the printable strings in a file", " strings \n\nThis runs the L command on a file and returns\nthe list of printable strings found.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); + else + if (strcasecmp (cmd, "strings_e") == 0 || strcasecmp (cmd, "strings-e") == 0) + pod2text ("strings-e - print the printable strings in a file", " strings-e \n\nThis is like the C command, but allows you to\nspecify the encoding.\n\nSee the L manpage for the full list of encodings.\n\nCommonly useful encodings are C (lower case L) which will\nshow strings inside Windows/x86 files.\n\nThe returned strings are transcoded to UTF-8.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); + else + if (strcasecmp (cmd, "hexdump") == 0) + pod2text ("hexdump - dump a file in hexadecimal", " hexdump \n\nThis runs C on the given C. The result is\nthe human-readable, canonical hex dump of the file.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); + else display_builtin_command (cmd); } @@ -2337,6 +2349,59 @@ static int run_equal (const char *cmd, int argc, char *argv[]) return 0; } +static int run_strings (const char *cmd, int argc, char *argv[]) +{ + char **r; + const char *path; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + path = argv[0]; + r = guestfs_strings (g, path); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + +static int run_strings_e (const char *cmd, int argc, char *argv[]) +{ + char **r; + const char *encoding; + const char *path; + if (argc != 2) { + fprintf (stderr, "%s should have 2 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + encoding = argv[0]; + path = argv[1]; + r = guestfs_strings_e (g, encoding, path); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + +static int run_hexdump (const char *cmd, int argc, char *argv[]) +{ + char *r; + const char *path; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + path = argv[0]; + r = guestfs_hexdump (g, path); + if (r == NULL) return -1; + printf ("%s\n", r); + free (r); + return 0; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -2672,6 +2737,15 @@ int run_action (const char *cmd, int argc, char *argv[]) if (strcasecmp (cmd, "equal") == 0) return run_equal (cmd, argc, argv); else + if (strcasecmp (cmd, "strings") == 0) + return run_strings (cmd, argc, argv); + else + if (strcasecmp (cmd, "strings_e") == 0 || strcasecmp (cmd, "strings-e") == 0) + return run_strings_e (cmd, argc, argv); + else + if (strcasecmp (cmd, "hexdump") == 0) + return run_hexdump (cmd, argc, argv); + else { fprintf (stderr, "%s: unknown command\n", cmd); return -1; diff --git a/fish/completion.c b/fish/completion.c index 640c369..9318288 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -87,6 +87,7 @@ static const char *const commands[] = { "get-state", "get-verbose", "grub-install", + "hexdump", "is-busy", "is-config", "is-dir", @@ -135,6 +136,8 @@ static const char *const commands[] = { "sfdisk", "stat", "statvfs", + "strings", + "strings-e", "sync", "tar-in", "tar-out", diff --git a/guestfish-actions.pod b/guestfish-actions.pod index 62be2a9..04343af 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -591,6 +591,17 @@ This returns the verbose messages flag. This command installs GRUB (the Grand Unified Bootloader) on C, with the root directory being C. +=head2 hexdump + + hexdump path + +This runs C on the given C. The result is +the human-readable, canonical hex dump of the file. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + =head2 is-busy is-busy @@ -1043,6 +1054,35 @@ C should be a file or directory in the mounted file system This is the same as the C system call. +=head2 strings + + strings path + +This runs the L command on a file and returns +the list of printable strings found. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + +=head2 strings-e + + strings-e encoding path + +This is like the C command, but allows you to +specify the encoding. + +See the L manpage for the full list of encodings. + +Commonly useful encodings are C (lower case L) which will +show strings inside Windows/x86 files. + +The returned strings are transcoded to UTF-8. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + =head2 sync sync @@ -1193,6 +1233,11 @@ As a special case, if C is C<0> then the length is calculated using C (so in this case the content cannot contain embedded ASCII NULs). +I Owing to a bug, writing content containing ASCII NUL +characters does I work, even if the length is specified. +We hope to resolve this bug in a future version. In the meantime +use C. + Because of the message protocol, there is a transfer limit of somewhere between 2MB and 4MB. To transfer large files you should use FTP. diff --git a/guestfs-actions.pod b/guestfs-actions.pod index 23e0580..51b80a1 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -770,6 +770,21 @@ C, with the root directory being C. This function returns 0 on success or -1 on error. +=head2 guestfs_hexdump + + char *guestfs_hexdump (guestfs_h *handle, + const char *path); + +This runs C on the given C. The result is +the human-readable, canonical hex dump of the file. + +This function returns a string, or NULL on error. +I. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + =head2 guestfs_is_busy int guestfs_is_busy (guestfs_h *handle); @@ -1411,6 +1426,46 @@ This function returns a C or NULL if there was an error. I after use>. +=head2 guestfs_strings + + char **guestfs_strings (guestfs_h *handle, + const char *path); + +This runs the L command on a file and returns +the list of printable strings found. + +This function returns a NULL-terminated array of strings +(like L), or NULL if there was an error. +I. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + +=head2 guestfs_strings_e + + char **guestfs_strings_e (guestfs_h *handle, + const char *encoding, + const char *path); + +This is like the C command, but allows you to +specify the encoding. + +See the L manpage for the full list of encodings. + +Commonly useful encodings are C (lower case L) which will +show strings inside Windows/x86 files. + +The returned strings are transcoded to UTF-8. + +This function returns a NULL-terminated array of strings +(like L), or NULL if there was an error. +I. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + =head2 guestfs_sync int guestfs_sync (guestfs_h *handle); @@ -1619,6 +1674,11 @@ As a special case, if C is C<0> then the length is calculated using C (so in this case the content cannot contain embedded ASCII NULs). +I Owing to a bug, writing content containing ASCII NUL +characters does I work, even if the length is specified. +We hope to resolve this bug in a future version. In the meantime +use C. + This function returns 0 on success or -1 on error. Because of the message protocol, there is a transfer limit diff --git a/inspector/virt-inspector.pl b/inspector/virt-inspector.pl index b2983b3..a5ba005 100755 --- a/inspector/virt-inspector.pl +++ b/inspector/virt-inspector.pl @@ -575,7 +575,34 @@ sub check_for_applications local $_; my $root_dev = shift; - # XXX rpm -qa, look in Program Files, or whatever + my @apps; + + my $os = $oses{$root_dev}->{os}; + if ($os eq "linux") { + my $distro = $oses{$root_dev}->{distro}; + if ($distro eq "redhat") { + my @lines = $g->command_lines + (["rpm", "-q", "-a", "--qf", + "%{name} %{epoch} %{version} %{release} %{arch}\n"]); + foreach (@lines) { + if (m/^(.*) (.*) (.*) (.*) (.*)$/) { + $epoch = $2; + $epoch = "" if $epoch eq "(none)"; + my $app = { + name => $1, + epoch => $epoch, + version => $3, + release => $4, + arch => $5 + }; + push @apps, $app + } + } + } + } elsif ($os eq "windows") { + # This sucks ... XXX + XXX + } } sub check_for_kernels diff --git a/java/com/redhat/et/libguestfs/GuestFS.java b/java/com/redhat/et/libguestfs/GuestFS.java index cd5fdee..0d9ae9d 100644 --- a/java/com/redhat/et/libguestfs/GuestFS.java +++ b/java/com/redhat/et/libguestfs/GuestFS.java @@ -1487,6 +1487,11 @@ public class GuestFS { * calculated using "strlen" (so in this case the content * cannot contain embedded ASCII NULs). * + * *NB.* Owing to a bug, writing content containing ASCII + * NUL characters does *not* work, even if the length is + * specified. We hope to resolve this bug in a future + * version. In the meantime use "g.upload". + * * Because of the message protocol, there is a transfer * limit of somewhere between 2MB and 4MB. To transfer * large files you should use FTP. @@ -2578,4 +2583,78 @@ public class GuestFS { private native boolean _equal (long g, String file1, String file2) throws LibGuestFSException; + /** + * print the printable strings in a file + * + * This runs the strings(1) command on a file and returns + * the list of printable strings found. + * + * Because of the message protocol, there is a transfer + * limit of somewhere between 2MB and 4MB. To transfer + * large files you should use FTP. + * + * @throws LibGuestFSException + */ + public String[] strings (String path) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("strings: handle is closed"); + return _strings (g, path); + } + private native String[] _strings (long g, String path) + throws LibGuestFSException; + + /** + * print the printable strings in a file + * + * This is like the "g.strings" command, but allows you to + * specify the encoding. + * + * See the strings(1) manpage for the full list of + * encodings. + * + * Commonly useful encodings are "l" (lower case L) which + * will show strings inside Windows/x86 files. + * + * The returned strings are transcoded to UTF-8. + * + * Because of the message protocol, there is a transfer + * limit of somewhere between 2MB and 4MB. To transfer + * large files you should use FTP. + * + * @throws LibGuestFSException + */ + public String[] strings_e (String encoding, String path) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("strings_e: handle is closed"); + return _strings_e (g, encoding, path); + } + private native String[] _strings_e (long g, String encoding, String path) + throws LibGuestFSException; + + /** + * dump a file in hexadecimal + * + * This runs "hexdump -C" on the given "path". The result + * is the human-readable, canonical hex dump of the file. + * + * Because of the message protocol, there is a transfer + * limit of somewhere between 2MB and 4MB. To transfer + * large files you should use FTP. + * + * @throws LibGuestFSException + */ + public String hexdump (String path) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("hexdump: handle is closed"); + return _hexdump (g, path); + } + private native String _hexdump (long g, String path) + throws LibGuestFSException; + } diff --git a/java/com_redhat_et_libguestfs_GuestFS.c b/java/com_redhat_et_libguestfs_GuestFS.c index da287b1..48cd60f 100644 --- a/java/com_redhat_et_libguestfs_GuestFS.c +++ b/java/com_redhat_et_libguestfs_GuestFS.c @@ -2586,3 +2586,93 @@ Java_com_redhat_et_libguestfs_GuestFS__1equal return (jboolean) r; } +JNIEXPORT jobjectArray JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1strings + (JNIEnv *env, jobject obj, jlong jg, jstring jpath) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jobjectArray jr; + int r_len; + jclass cl; + jstring jstr; + char **r; + const char *path; + int i; + + path = (*env)->GetStringUTFChars (env, jpath, NULL); + r = guestfs_strings (g, path); + (*env)->ReleaseStringUTFChars (env, jpath, path); + if (r == NULL) { + throw_exception (env, guestfs_last_error (g)); + return NULL; + } + for (r_len = 0; r[r_len] != NULL; ++r_len) ; + cl = (*env)->FindClass (env, "java/lang/String"); + jstr = (*env)->NewStringUTF (env, ""); + jr = (*env)->NewObjectArray (env, r_len, cl, jstr); + for (i = 0; i < r_len; ++i) { + jstr = (*env)->NewStringUTF (env, r[i]); + (*env)->SetObjectArrayElement (env, jr, i, jstr); + free (r[i]); + } + free (r); + return jr; +} + +JNIEXPORT jobjectArray JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1strings_1e + (JNIEnv *env, jobject obj, jlong jg, jstring jencoding, jstring jpath) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jobjectArray jr; + int r_len; + jclass cl; + jstring jstr; + char **r; + const char *encoding; + const char *path; + int i; + + encoding = (*env)->GetStringUTFChars (env, jencoding, NULL); + path = (*env)->GetStringUTFChars (env, jpath, NULL); + r = guestfs_strings_e (g, encoding, path); + (*env)->ReleaseStringUTFChars (env, jencoding, encoding); + (*env)->ReleaseStringUTFChars (env, jpath, path); + if (r == NULL) { + throw_exception (env, guestfs_last_error (g)); + return NULL; + } + for (r_len = 0; r[r_len] != NULL; ++r_len) ; + cl = (*env)->FindClass (env, "java/lang/String"); + jstr = (*env)->NewStringUTF (env, ""); + jr = (*env)->NewObjectArray (env, r_len, cl, jstr); + for (i = 0; i < r_len; ++i) { + jstr = (*env)->NewStringUTF (env, r[i]); + (*env)->SetObjectArrayElement (env, jr, i, jstr); + free (r[i]); + } + free (r); + return jr; +} + +JNIEXPORT jstring JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1hexdump + (JNIEnv *env, jobject obj, jlong jg, jstring jpath) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jstring jr; + char *r; + const char *path; + + path = (*env)->GetStringUTFChars (env, jpath, NULL); + r = guestfs_hexdump (g, path); + (*env)->ReleaseStringUTFChars (env, jpath, path); + if (r == NULL) { + throw_exception (env, guestfs_last_error (g)); + return NULL; + } + jr = (*env)->NewStringUTF (env, r); + free (r); + return jr; +} + diff --git a/make-initramfs.sh.in b/make-initramfs.sh.in index f27dd3f..ec03dc6 100755 --- a/make-initramfs.sh.in +++ b/make-initramfs.sh.in @@ -25,6 +25,7 @@ unset CDPATH modules=" -i augeas-libs -i bash +-i binutils -i coreutils -i dosfstools -i file diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index 99d5903..ab958b7 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -229,3 +229,6 @@ external drop_caches : t -> int -> unit = "ocaml_guestfs_drop_caches" external dmesg : t -> string = "ocaml_guestfs_dmesg" external ping_daemon : t -> unit = "ocaml_guestfs_ping_daemon" external equal : t -> string -> string -> bool = "ocaml_guestfs_equal" +external strings : t -> string -> string array = "ocaml_guestfs_strings" +external strings_e : t -> string -> string -> string array = "ocaml_guestfs_strings_e" +external hexdump : t -> string -> string = "ocaml_guestfs_hexdump" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 40d1d2a..d44fb06 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -466,3 +466,12 @@ val ping_daemon : t -> unit val equal : t -> string -> string -> bool (** test if two files have equal contents *) +val strings : t -> string -> string array +(** print the printable strings in a file *) + +val strings_e : t -> string -> string -> string array +(** print the printable strings in a file *) + +val hexdump : t -> string -> string +(** dump a file in hexadecimal *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index 7732e1c..a707897 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -3034,3 +3034,80 @@ ocaml_guestfs_equal (value gv, value file1v, value file2v) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_strings (value gv, value pathv) +{ + CAMLparam2 (gv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("strings: used handle after closing it"); + + const char *path = String_val (pathv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_strings (g, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "strings"); + + rv = caml_copy_string_array ((const char **) r); + for (i = 0; r[i] != NULL; ++i) free (r[i]); + free (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_strings_e (value gv, value encodingv, value pathv) +{ + CAMLparam3 (gv, encodingv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("strings_e: used handle after closing it"); + + const char *encoding = String_val (encodingv); + const char *path = String_val (pathv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_strings_e (g, encoding, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "strings_e"); + + rv = caml_copy_string_array ((const char **) r); + for (i = 0; r[i] != NULL; ++i) free (r[i]); + free (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_hexdump (value gv, value pathv) +{ + CAMLparam2 (gv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("hexdump: used handle after closing it"); + + const char *path = String_val (pathv); + char *r; + + caml_enter_blocking_section (); + r = guestfs_hexdump (g, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "hexdump"); + + rv = caml_copy_string (r); + free (r); + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index e95b274..ce697c5 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -1699,3 +1699,57 @@ PREINIT: OUTPUT: RETVAL +void +strings (g, path) + guestfs_h *g; + char *path; +PREINIT: + char **stringsout; + int i, n; + PPCODE: + stringsout = guestfs_strings (g, path); + if (stringsout == NULL) + croak ("strings: %s", guestfs_last_error (g)); + for (n = 0; stringsout[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (stringsout[i], 0))); + free (stringsout[i]); + } + free (stringsout); + +void +strings_e (g, encoding, path) + guestfs_h *g; + char *encoding; + char *path; +PREINIT: + char **stringsout; + int i, n; + PPCODE: + stringsout = guestfs_strings_e (g, encoding, path); + if (stringsout == NULL) + croak ("strings_e: %s", guestfs_last_error (g)); + for (n = 0; stringsout[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (stringsout[i], 0))); + free (stringsout[i]); + } + free (stringsout); + +SV * +hexdump (g, path) + guestfs_h *g; + char *path; +PREINIT: + char *dump; + CODE: + dump = guestfs_hexdump (g, path); + if (dump == NULL) + croak ("hexdump: %s", guestfs_last_error (g)); + RETVAL = newSVpv (dump, 0); + free (dump); + OUTPUT: + RETVAL + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index 116f716..d059ae4 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -582,6 +582,15 @@ This returns the verbose messages flag. This command installs GRUB (the Grand Unified Bootloader) on C, with the root directory being C. +=item $dump = $h->hexdump ($path); + +This runs C on the given C. The result is +the human-readable, canonical hex dump of the file. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + =item $busy = $h->is_busy (); This returns true iff this handle is busy processing a command @@ -958,6 +967,31 @@ C should be a file or directory in the mounted file system This is the same as the C system call. +=item @stringsout = $h->strings ($path); + +This runs the L command on a file and returns +the list of printable strings found. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + +=item @stringsout = $h->strings_e ($encoding, $path); + +This is like the C<$h-Estrings> command, but allows you to +specify the encoding. + +See the L manpage for the full list of encodings. + +Commonly useful encodings are C (lower case L) which will +show strings inside Windows/x86 files. + +The returned strings are transcoded to UTF-8. + +Because of the message protocol, there is a transfer limit +of somewhere between 2MB and 4MB. To transfer large files you should use +FTP. + =item $h->sync (); This syncs the disk, so that any writes are flushed through to the @@ -1076,6 +1110,11 @@ As a special case, if C is C<0> then the length is calculated using C (so in this case the content cannot contain embedded ASCII NULs). +I Owing to a bug, writing content containing ASCII NUL +characters does I work, even if the length is specified. +We hope to resolve this bug in a future version. In the meantime +use C<$h-Eupload>. + Because of the message protocol, there is a transfer limit of somewhere between 2MB and 4MB. To transfer large files you should use FTP. diff --git a/python/guestfs-py.c b/python/guestfs-py.c index 918e2ba..efc5792 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -3254,6 +3254,82 @@ py_guestfs_equal (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_strings (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char **r; + const char *path; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_strings", + &py_g, &path)) + return NULL; + g = get_handle (py_g); + + r = guestfs_strings (g, path); + if (r == NULL) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + py_r = put_string_list (r); + free_strings (r); + return py_r; +} + +static PyObject * +py_guestfs_strings_e (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char **r; + const char *encoding; + const char *path; + + if (!PyArg_ParseTuple (args, (char *) "Oss:guestfs_strings_e", + &py_g, &encoding, &path)) + return NULL; + g = get_handle (py_g); + + r = guestfs_strings_e (g, encoding, path); + if (r == NULL) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + py_r = put_string_list (r); + free_strings (r); + return py_r; +} + +static PyObject * +py_guestfs_hexdump (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char *r; + const char *path; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_hexdump", + &py_g, &path)) + return NULL; + g = get_handle (py_g); + + r = guestfs_hexdump (g, path); + if (r == NULL) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + py_r = PyString_FromString (r); + free (r); + return py_r; +} + static PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -3371,6 +3447,9 @@ static PyMethodDef methods[] = { { (char *) "dmesg", py_guestfs_dmesg, METH_VARARGS, NULL }, { (char *) "ping_daemon", py_guestfs_ping_daemon, METH_VARARGS, NULL }, { (char *) "equal", py_guestfs_equal, METH_VARARGS, NULL }, + { (char *) "strings", py_guestfs_strings, METH_VARARGS, NULL }, + { (char *) "strings_e", py_guestfs_strings_e, METH_VARARGS, NULL }, + { (char *) "hexdump", py_guestfs_hexdump, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index 3db9ac0..9cf9d88 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -743,6 +743,11 @@ class GuestFS: calculated using "strlen" (so in this case the content cannot contain embedded ASCII NULs). + *NB.* Owing to a bug, writing content containing ASCII + NUL characters does *not* work, even if the length is + specified. We hope to resolve this bug in a future + version. In the meantime use "g.upload". + Because of the message protocol, there is a transfer limit of somewhere between 2MB and 4MB. To transfer large files you should use FTP. @@ -1251,3 +1256,45 @@ class GuestFS: """ return libguestfsmod.equal (self._o, file1, file2) + def strings (self, path): + u"""This runs the strings(1) command on a file and returns + the list of printable strings found. + + This function returns a list of strings. + + Because of the message protocol, there is a transfer + limit of somewhere between 2MB and 4MB. To transfer + large files you should use FTP. + """ + return libguestfsmod.strings (self._o, path) + + def strings_e (self, encoding, path): + u"""This is like the "g.strings" command, but allows you to + specify the encoding. + + See the strings(1) manpage for the full list of + encodings. + + Commonly useful encodings are "l" (lower case L) which + will show strings inside Windows/x86 files. + + The returned strings are transcoded to UTF-8. + + This function returns a list of strings. + + Because of the message protocol, there is a transfer + limit of somewhere between 2MB and 4MB. To transfer + large files you should use FTP. + """ + return libguestfsmod.strings_e (self._o, encoding, path) + + def hexdump (self, path): + u"""This runs "hexdump -C" on the given "path". The result + is the human-readable, canonical hex dump of the file. + + Because of the message protocol, there is a transfer + limit of somewhere between 2MB and 4MB. To transfer + large files you should use FTP. + """ + return libguestfsmod.hexdump (self._o, path) + diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index f235c5a..8b87aed 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -2745,6 +2745,91 @@ static VALUE ruby_guestfs_equal (VALUE gv, VALUE file1v, VALUE file2v) return INT2NUM (r); } +static VALUE ruby_guestfs_strings (VALUE gv, VALUE pathv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "strings"); + + const char *path = StringValueCStr (pathv); + if (!path) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "path", "strings"); + + char **r; + + r = guestfs_strings (g, path); + if (r == NULL) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + int i, len = 0; + for (i = 0; r[i] != NULL; ++i) len++; + VALUE rv = rb_ary_new2 (len); + for (i = 0; r[i] != NULL; ++i) { + rb_ary_push (rv, rb_str_new2 (r[i])); + free (r[i]); + } + free (r); + return rv; +} + +static VALUE ruby_guestfs_strings_e (VALUE gv, VALUE encodingv, VALUE pathv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "strings_e"); + + const char *encoding = StringValueCStr (encodingv); + if (!encoding) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "encoding", "strings_e"); + const char *path = StringValueCStr (pathv); + if (!path) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "path", "strings_e"); + + char **r; + + r = guestfs_strings_e (g, encoding, path); + if (r == NULL) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + int i, len = 0; + for (i = 0; r[i] != NULL; ++i) len++; + VALUE rv = rb_ary_new2 (len); + for (i = 0; r[i] != NULL; ++i) { + rb_ary_push (rv, rb_str_new2 (r[i])); + free (r[i]); + } + free (r); + return rv; +} + +static VALUE ruby_guestfs_hexdump (VALUE gv, VALUE pathv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "hexdump"); + + const char *path = StringValueCStr (pathv); + if (!path) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "path", "hexdump"); + + char *r; + + r = guestfs_hexdump (g, path); + if (r == NULL) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + VALUE rv = rb_str_new2 (r); + free (r); + return rv; +} + /* Initialize the module. */ void Init__guestfs () { @@ -2983,4 +3068,10 @@ void Init__guestfs () ruby_guestfs_ping_daemon, 0); rb_define_method (c_guestfs, "equal", ruby_guestfs_equal, 2); + rb_define_method (c_guestfs, "strings", + ruby_guestfs_strings, 1); + rb_define_method (c_guestfs, "strings_e", + ruby_guestfs_strings_e, 2); + rb_define_method (c_guestfs, "hexdump", + ruby_guestfs_hexdump, 1); } diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index 3da1b42..f49e79f 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -8428,3 +8428,288 @@ int guestfs_equal (guestfs_h *g, return ctx.ret.equality; } +struct strings_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_strings_ret ret; +}; + +static void strings_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct strings_ctx *ctx = (struct strings_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_strings"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_strings"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_strings"); + return; + } + goto done; + } + if (!xdr_guestfs_strings_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_strings"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_strings (guestfs_h *g, + const char *path) +{ + struct guestfs_strings_args args; + struct strings_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_strings") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.path = (char *) path; + serial = guestfs__send_sync (g, GUESTFS_PROC_STRINGS, + (xdrproc_t) xdr_guestfs_strings_args, (char *) &args); + if (serial == -1) { + guestfs_set_ready (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, strings_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_strings"); + guestfs_set_ready (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_STRINGS, serial) == -1) { + guestfs_set_ready (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + guestfs_set_ready (g); + return NULL; + } + + guestfs_set_ready (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.stringsout.stringsout_val = + safe_realloc (g, ctx.ret.stringsout.stringsout_val, + sizeof (char *) * (ctx.ret.stringsout.stringsout_len + 1)); + ctx.ret.stringsout.stringsout_val[ctx.ret.stringsout.stringsout_len] = NULL; + return ctx.ret.stringsout.stringsout_val; +} + +struct strings_e_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_strings_e_ret ret; +}; + +static void strings_e_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct strings_e_ctx *ctx = (struct strings_e_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_strings_e"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_strings_e"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_strings_e"); + return; + } + goto done; + } + if (!xdr_guestfs_strings_e_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_strings_e"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_strings_e (guestfs_h *g, + const char *encoding, + const char *path) +{ + struct guestfs_strings_e_args args; + struct strings_e_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_strings_e") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.encoding = (char *) encoding; + args.path = (char *) path; + serial = guestfs__send_sync (g, GUESTFS_PROC_STRINGS_E, + (xdrproc_t) xdr_guestfs_strings_e_args, (char *) &args); + if (serial == -1) { + guestfs_set_ready (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, strings_e_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_strings_e"); + guestfs_set_ready (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_STRINGS_E, serial) == -1) { + guestfs_set_ready (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + guestfs_set_ready (g); + return NULL; + } + + guestfs_set_ready (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.stringsout.stringsout_val = + safe_realloc (g, ctx.ret.stringsout.stringsout_val, + sizeof (char *) * (ctx.ret.stringsout.stringsout_len + 1)); + ctx.ret.stringsout.stringsout_val[ctx.ret.stringsout.stringsout_len] = NULL; + return ctx.ret.stringsout.stringsout_val; +} + +struct hexdump_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_hexdump_ret ret; +}; + +static void hexdump_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct hexdump_ctx *ctx = (struct hexdump_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_hexdump"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_hexdump"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_hexdump"); + return; + } + goto done; + } + if (!xdr_guestfs_hexdump_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_hexdump"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char *guestfs_hexdump (guestfs_h *g, + const char *path) +{ + struct guestfs_hexdump_args args; + struct hexdump_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_hexdump") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.path = (char *) path; + serial = guestfs__send_sync (g, GUESTFS_PROC_HEXDUMP, + (xdrproc_t) xdr_guestfs_hexdump_args, (char *) &args); + if (serial == -1) { + guestfs_set_ready (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, hexdump_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_hexdump"); + guestfs_set_ready (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_HEXDUMP, serial) == -1) { + guestfs_set_ready (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + guestfs_set_ready (g); + return NULL; + } + + guestfs_set_ready (g); + return ctx.ret.dump; /* caller will free */ +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index 0539981..12b33d6 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -133,3 +133,6 @@ extern int guestfs_drop_caches (guestfs_h *handle, int whattodrop); extern char *guestfs_dmesg (guestfs_h *handle); extern int guestfs_ping_daemon (guestfs_h *handle); extern int guestfs_equal (guestfs_h *handle, const char *file1, const char *file2); +extern char **guestfs_strings (guestfs_h *handle, const char *path); +extern char **guestfs_strings_e (guestfs_h *handle, const char *encoding, const char *path); +extern char *guestfs_hexdump (guestfs_h *handle, const char *path); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index 66889cc..d53bcff 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1550,6 +1550,70 @@ xdr_guestfs_equal_ret (XDR *xdrs, guestfs_equal_ret *objp) } bool_t +xdr_guestfs_strings_args (XDR *xdrs, guestfs_strings_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_strings_ret (XDR *xdrs, guestfs_strings_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->stringsout.stringsout_val, (u_int *) &objp->stringsout.stringsout_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_strings_e_args (XDR *xdrs, guestfs_strings_e_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->encoding, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_strings_e_ret (XDR *xdrs, guestfs_strings_e_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->stringsout.stringsout_val, (u_int *) &objp->stringsout.stringsout_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_hexdump_args (XDR *xdrs, guestfs_hexdump_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_hexdump_ret (XDR *xdrs, guestfs_hexdump_ret *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->dump, ~0)) + return FALSE; + return TRUE; +} + +bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { register int32_t *buf; diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 06287c9..87db39b 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -800,6 +800,43 @@ struct guestfs_equal_ret { }; typedef struct guestfs_equal_ret guestfs_equal_ret; +struct guestfs_strings_args { + char *path; +}; +typedef struct guestfs_strings_args guestfs_strings_args; + +struct guestfs_strings_ret { + struct { + u_int stringsout_len; + str *stringsout_val; + } stringsout; +}; +typedef struct guestfs_strings_ret guestfs_strings_ret; + +struct guestfs_strings_e_args { + char *encoding; + char *path; +}; +typedef struct guestfs_strings_e_args guestfs_strings_e_args; + +struct guestfs_strings_e_ret { + struct { + u_int stringsout_len; + str *stringsout_val; + } stringsout; +}; +typedef struct guestfs_strings_e_ret guestfs_strings_e_ret; + +struct guestfs_hexdump_args { + char *path; +}; +typedef struct guestfs_hexdump_args guestfs_hexdump_args; + +struct guestfs_hexdump_ret { + char *dump; +}; +typedef struct guestfs_hexdump_ret guestfs_hexdump_ret; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -894,7 +931,10 @@ enum guestfs_procedure { GUESTFS_PROC_DMESG = 91, GUESTFS_PROC_PING_DAEMON = 92, GUESTFS_PROC_EQUAL = 93, - GUESTFS_PROC_NR_PROCS = 93 + 1, + GUESTFS_PROC_STRINGS = 94, + GUESTFS_PROC_STRINGS_E = 95, + GUESTFS_PROC_HEXDUMP = 96, + GUESTFS_PROC_NR_PROCS = 96 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -1070,6 +1110,12 @@ extern bool_t xdr_guestfs_drop_caches_args (XDR *, guestfs_drop_caches_args*); extern bool_t xdr_guestfs_dmesg_ret (XDR *, guestfs_dmesg_ret*); extern bool_t xdr_guestfs_equal_args (XDR *, guestfs_equal_args*); extern bool_t xdr_guestfs_equal_ret (XDR *, guestfs_equal_ret*); +extern bool_t xdr_guestfs_strings_args (XDR *, guestfs_strings_args*); +extern bool_t xdr_guestfs_strings_ret (XDR *, guestfs_strings_ret*); +extern bool_t xdr_guestfs_strings_e_args (XDR *, guestfs_strings_e_args*); +extern bool_t xdr_guestfs_strings_e_ret (XDR *, guestfs_strings_e_ret*); +extern bool_t xdr_guestfs_hexdump_args (XDR *, guestfs_hexdump_args*); +extern bool_t xdr_guestfs_hexdump_ret (XDR *, guestfs_hexdump_ret*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -1204,6 +1250,12 @@ extern bool_t xdr_guestfs_drop_caches_args (); extern bool_t xdr_guestfs_dmesg_ret (); extern bool_t xdr_guestfs_equal_args (); extern bool_t xdr_guestfs_equal_ret (); +extern bool_t xdr_guestfs_strings_args (); +extern bool_t xdr_guestfs_strings_ret (); +extern bool_t xdr_guestfs_strings_e_args (); +extern bool_t xdr_guestfs_strings_e_ret (); +extern bool_t xdr_guestfs_hexdump_args (); +extern bool_t xdr_guestfs_hexdump_ret (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index f6cab67..4226424 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -623,6 +623,31 @@ struct guestfs_equal_ret { bool equality; }; +struct guestfs_strings_args { + string path<>; +}; + +struct guestfs_strings_ret { + str stringsout<>; +}; + +struct guestfs_strings_e_args { + string encoding<>; + string path<>; +}; + +struct guestfs_strings_e_ret { + str stringsout<>; +}; + +struct guestfs_hexdump_args { + string path<>; +}; + +struct guestfs_hexdump_ret { + string dump<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -717,6 +742,9 @@ enum guestfs_procedure { GUESTFS_PROC_DMESG = 91, GUESTFS_PROC_PING_DAEMON = 92, GUESTFS_PROC_EQUAL = 93, + GUESTFS_PROC_STRINGS = 94, + GUESTFS_PROC_STRINGS_E = 95, + GUESTFS_PROC_HEXDUMP = 96, GUESTFS_PROC_NR_PROCS }; diff --git a/tests.c b/tests.c index a8564e1..3123513 100644 --- a/tests.c +++ b/tests.c @@ -117,6 +117,298 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_get_e2uuid\" has no tests\n"); } +static int test_hexdump_0 (void) +{ + /* InitBasicFS for hexdump (0): create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + device[5] = devchar; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + 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 device[] = "/dev/sda"; + device[5] = devchar; + char lines_0[] = ","; + char *lines[] = { + lines_0, + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, device, 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + char fstype[] = "ext2"; + char device[] = "/dev/sda1"; + device[5] = devchar; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + device[5] = devchar; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutput for hexdump (0) */ + char expected[] = "00000000 68 65 6c 6c 6f 0a 77 6f 72 6c 64 0a |hello.world.|\n0000000c\n"; + { + char path[] = "/new"; + char content[] = "hello\nworld\n"; + int r; + suppress_error = 0; + r = guestfs_write_file (g, path, content, 12); + if (r == -1) + return -1; + } + { + char path[] = "/new"; + char *r; + suppress_error = 0; + r = guestfs_hexdump (g, path); + if (r == NULL) + return -1; + if (strcmp (r, expected) != 0) { + fprintf (stderr, "test_hexdump_0: expected \"%s\" but got \"%s\"\n", expected, r); + return -1; + } + free (r); + } + return 0; +} + +static int test_strings_e_0 (void) +{ + /* InitBasicFS for strings_e (0): create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + device[5] = devchar; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + 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 device[] = "/dev/sda"; + device[5] = devchar; + char lines_0[] = ","; + char *lines[] = { + lines_0, + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, device, 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + char fstype[] = "ext2"; + char device[] = "/dev/sda1"; + device[5] = devchar; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + device[5] = devchar; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for strings_e (0) */ + { + char path[] = "/new"; + char content[] = "hello\nworld\n"; + int r; + suppress_error = 0; + r = guestfs_write_file (g, path, content, 0); + if (r == -1) + return -1; + } + { + char encoding[] = "b"; + char path[] = "/new"; + char **r; + int i; + suppress_error = 0; + r = guestfs_strings_e (g, encoding, path); + if (r == NULL) + return -1; + if (r[0] != NULL) { + fprintf (stderr, "test_strings_e_0: extra elements returned from command\n"); + print_strings (r); + return -1; + } + for (i = 0; r[i] != NULL; ++i) + free (r[i]); + free (r); + } + return 0; +} + +static int test_strings_0 (void) +{ + /* InitBasicFS for strings (0): create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + device[5] = devchar; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + 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 device[] = "/dev/sda"; + device[5] = devchar; + char lines_0[] = ","; + char *lines[] = { + lines_0, + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, device, 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + char fstype[] = "ext2"; + char device[] = "/dev/sda1"; + device[5] = devchar; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + device[5] = devchar; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for strings (0) */ + { + char path[] = "/new"; + char content[] = "hello\nworld\n"; + int r; + suppress_error = 0; + r = guestfs_write_file (g, path, content, 0); + if (r == -1) + return -1; + } + { + char path[] = "/new"; + char **r; + int i; + suppress_error = 0; + r = guestfs_strings (g, path); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_strings_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "hello"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_strings_0: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_strings_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "world"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_strings_0: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (r[2] != NULL) { + fprintf (stderr, "test_strings_0: extra elements returned from command\n"); + print_strings (r); + return -1; + } + for (i = 0; r[i] != NULL; ++i) + free (r[i]); + free (r); + } + return 0; +} + static int test_equal_0 (void) { /* InitBasicFS for equal (0): create ext2 on /dev/sda1 */ @@ -9864,9 +10156,27 @@ int main (int argc, char *argv[]) free (devs[i]); free (devs); - nr_tests = 107; + nr_tests = 110; test_num++; + printf ("%3d/%3d test_hexdump_0\n", test_num, nr_tests); + if (test_hexdump_0 () == -1) { + printf ("test_hexdump_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_strings_e_0\n", test_num, nr_tests); + if (test_strings_e_0 () == -1) { + printf ("test_strings_e_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_strings_0\n", test_num, nr_tests); + if (test_strings_0 () == -1) { + printf ("test_strings_0 FAILED\n"); + failed++; + } + test_num++; printf ("%3d/%3d test_equal_0\n", test_num, nr_tests); if (test_equal_0 () == -1) { printf ("test_equal_0 FAILED\n");