From: Richard W.M. Jones Date: Mon, 29 Jun 2009 10:47:07 +0000 (+0100) Subject: Generated code for head/tail commands. X-Git-Tag: 1.0.54~13 X-Git-Url: http://git.annexia.org/?p=libguestfs.git;a=commitdiff_plain;h=826020fe18bf2eee43f8afea392874bb88c0650a Generated code for head/tail commands. --- diff --git a/capitests/tests.c b/capitests/tests.c index 0254437..7a30498 100644 --- a/capitests/tests.c +++ b/capitests/tests.c @@ -153,6 +153,1246 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_scrub_freespace\" has no tests\n"); } +static int test_tail_n_0_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "tail_n") == NULL; + str = getenv ("SKIP_TEST_TAIL_N_0"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_TAIL_N"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_tail_n_0 (void) +{ + if (test_tail_n_0_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_tail_n_0"); + return 0; + } + + /* InitBasicFS for test_tail_n_0: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for tail_n (0) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_tail_n (g, 3, path); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_tail_n_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9997abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_tail_n_0: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_tail_n_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9998abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_tail_n_0: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (!r[2]) { + fprintf (stderr, "test_tail_n_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9999abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[2], expected) != 0) { + fprintf (stderr, "test_tail_n_0: expected \"%s\" but got \"%s\"\n", expected, r[2]); + return -1; + } + } + if (r[3] != NULL) { + fprintf (stderr, "test_tail_n_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_tail_n_1_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "tail_n") == NULL; + str = getenv ("SKIP_TEST_TAIL_N_1"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_TAIL_N"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_tail_n_1 (void) +{ + if (test_tail_n_1_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_tail_n_1"); + return 0; + } + + /* InitBasicFS for test_tail_n_1: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for tail_n (1) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_tail_n (g, -9998, path); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_tail_n_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9997abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_tail_n_1: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_tail_n_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9998abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_tail_n_1: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (!r[2]) { + fprintf (stderr, "test_tail_n_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9999abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[2], expected) != 0) { + fprintf (stderr, "test_tail_n_1: expected \"%s\" but got \"%s\"\n", expected, r[2]); + return -1; + } + } + if (r[3] != NULL) { + fprintf (stderr, "test_tail_n_1: 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_tail_n_2_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "tail_n") == NULL; + str = getenv ("SKIP_TEST_TAIL_N_2"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_TAIL_N"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_tail_n_2 (void) +{ + if (test_tail_n_2_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_tail_n_2"); + return 0; + } + + /* InitBasicFS for test_tail_n_2: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for tail_n (2) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_tail_n (g, 0, path); + if (r == NULL) + return -1; + if (r[0] != NULL) { + fprintf (stderr, "test_tail_n_2: 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_tail_0_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "tail") == NULL; + str = getenv ("SKIP_TEST_TAIL_0"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_TAIL"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_tail_0 (void) +{ + if (test_tail_0_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_tail_0"); + return 0; + } + + /* InitBasicFS for test_tail_0: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for tail (0) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_tail (g, path); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9990abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9991abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (!r[2]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9992abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[2], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[2]); + return -1; + } + } + if (!r[3]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9993abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[3], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[3]); + return -1; + } + } + if (!r[4]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9994abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[4], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[4]); + return -1; + } + } + if (!r[5]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9995abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[5], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[5]); + return -1; + } + } + if (!r[6]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9996abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[6], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[6]); + return -1; + } + } + if (!r[7]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9997abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[7], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[7]); + return -1; + } + } + if (!r[8]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9998abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[8], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[8]); + return -1; + } + } + if (!r[9]) { + fprintf (stderr, "test_tail_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9999abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[9], expected) != 0) { + fprintf (stderr, "test_tail_0: expected \"%s\" but got \"%s\"\n", expected, r[9]); + return -1; + } + } + if (r[10] != NULL) { + fprintf (stderr, "test_tail_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_head_n_0_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "head_n") == NULL; + str = getenv ("SKIP_TEST_HEAD_N_0"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_HEAD_N"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_head_n_0 (void) +{ + if (test_head_n_0_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_head_n_0"); + return 0; + } + + /* InitBasicFS for test_head_n_0: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for head_n (0) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_head_n (g, 3, path); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_head_n_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "0abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_head_n_0: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_head_n_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "1abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_head_n_0: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (!r[2]) { + fprintf (stderr, "test_head_n_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "2abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[2], expected) != 0) { + fprintf (stderr, "test_head_n_0: expected \"%s\" but got \"%s\"\n", expected, r[2]); + return -1; + } + } + if (r[3] != NULL) { + fprintf (stderr, "test_head_n_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_head_n_1_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "head_n") == NULL; + str = getenv ("SKIP_TEST_HEAD_N_1"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_HEAD_N"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_head_n_1 (void) +{ + if (test_head_n_1_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_head_n_1"); + return 0; + } + + /* InitBasicFS for test_head_n_1: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for head_n (1) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_head_n (g, -9997, path); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_head_n_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "0abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_head_n_1: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_head_n_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "1abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_head_n_1: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (!r[2]) { + fprintf (stderr, "test_head_n_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "2abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[2], expected) != 0) { + fprintf (stderr, "test_head_n_1: expected \"%s\" but got \"%s\"\n", expected, r[2]); + return -1; + } + } + if (r[3] != NULL) { + fprintf (stderr, "test_head_n_1: 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_head_n_2_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "head_n") == NULL; + str = getenv ("SKIP_TEST_HEAD_N_2"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_HEAD_N"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_head_n_2 (void) +{ + if (test_head_n_2_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_head_n_2"); + return 0; + } + + /* InitBasicFS for test_head_n_2: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for head_n (2) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_head_n (g, 0, path); + if (r == NULL) + return -1; + if (r[0] != NULL) { + fprintf (stderr, "test_head_n_2: 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_head_0_skip (void) +{ + const char *str; + + str = getenv ("TEST_ONLY"); + if (str) + return strstr (str, "head") == NULL; + str = getenv ("SKIP_TEST_HEAD_0"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_HEAD"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_head_0 (void) +{ + if (test_head_0_skip ()) { + printf ("%s skipped (reason: environment variable set)\n", "test_head_0"); + return 0; + } + + /* InitBasicFS for test_head_0: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + 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"; + 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"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for head (0) */ + { + char options[] = "ro"; + char vfstype[] = "squashfs"; + char device[] = "/dev/sdd"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount_vfs (g, options, vfstype, device, mountpoint); + if (r == -1) + return -1; + } + { + char path[] = "/10klines"; + char **r; + int i; + suppress_error = 0; + r = guestfs_head (g, path); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "0abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "1abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (!r[2]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "2abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[2], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[2]); + return -1; + } + } + if (!r[3]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "3abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[3], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[3]); + return -1; + } + } + if (!r[4]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "4abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[4], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[4]); + return -1; + } + } + if (!r[5]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "5abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[5], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[5]); + return -1; + } + } + if (!r[6]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "6abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[6], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[6]); + return -1; + } + } + if (!r[7]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "7abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[7], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[7]); + return -1; + } + } + if (!r[8]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "8abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[8], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[8]); + return -1; + } + } + if (!r[9]) { + fprintf (stderr, "test_head_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "9abcdefghijklmnopqrstuvwxyz"; + if (strcmp (r[9], expected) != 0) { + fprintf (stderr, "test_head_0: expected \"%s\" but got \"%s\"\n", expected, r[9]); + return -1; + } + } + if (r[10] != NULL) { + fprintf (stderr, "test_head_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_wc_c_0_skip (void) { const char *str; @@ -17267,9 +18507,57 @@ int main (int argc, char *argv[]) /* Cancel previous alarm. */ alarm (0); - nr_tests = 156; + nr_tests = 164; test_num++; + printf ("%3d/%3d test_tail_n_0\n", test_num, nr_tests); + if (test_tail_n_0 () == -1) { + printf ("test_tail_n_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_tail_n_1\n", test_num, nr_tests); + if (test_tail_n_1 () == -1) { + printf ("test_tail_n_1 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_tail_n_2\n", test_num, nr_tests); + if (test_tail_n_2 () == -1) { + printf ("test_tail_n_2 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_tail_0\n", test_num, nr_tests); + if (test_tail_0 () == -1) { + printf ("test_tail_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_head_n_0\n", test_num, nr_tests); + if (test_head_n_0 () == -1) { + printf ("test_head_n_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_head_n_1\n", test_num, nr_tests); + if (test_head_n_1 () == -1) { + printf ("test_head_n_1 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_head_n_2\n", test_num, nr_tests); + if (test_head_n_2 () == -1) { + printf ("test_head_n_2 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_head_0\n", test_num, nr_tests); + if (test_head_0 () == -1) { + printf ("test_head_0 FAILED\n"); + failed++; + } + test_num++; printf ("%3d/%3d test_wc_c_0\n", test_num, nr_tests); if (test_wc_c_0 () == -1) { printf ("test_wc_c_0 FAILED\n"); diff --git a/daemon/actions.h b/daemon/actions.h index 2de9aa2..8beeed6 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -119,7 +119,7 @@ extern char **do_strings_e (char *encoding, char *path); extern char *do_hexdump (char *path); extern int do_zerofree (char *device); extern int do_pvresize (char *device); -extern int do_sfdisk_N (char *device, int n, int cyls, int heads, int sectors, char *line); +extern int do_sfdisk_N (char *device, int partnum, int cyls, int heads, int sectors, char *line); extern char *do_sfdisk_l (char *device); extern char *do_sfdisk_kernel_geometry (char *device); extern char *do_sfdisk_disk_geometry (char *device); @@ -141,3 +141,7 @@ extern char *do_mkdtemp (char *template); extern int do_wc_l (char *path); extern int do_wc_w (char *path); extern int do_wc_c (char *path); +extern char **do_head (char *path); +extern char **do_head_n (int nrlines, char *path); +extern char **do_tail (char *path); +extern char **do_tail_n (int nrlines, char *path); diff --git a/daemon/stubs.c b/daemon/stubs.c index ab58473..170726c 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -2450,7 +2450,7 @@ static void sfdisk_N_stub (XDR *xdr_in) int r; struct guestfs_sfdisk_N_args args; char *device; - int n; + int partnum; int cyls; int heads; int sectors; @@ -2463,13 +2463,13 @@ static void sfdisk_N_stub (XDR *xdr_in) return; } device = args.device; - n = args.n; + partnum = args.partnum; cyls = args.cyls; heads = args.heads; sectors = args.sectors; line = args.line; - r = do_sfdisk_N (device, n, cyls, heads, sectors, line); + r = do_sfdisk_N (device, partnum, cyls, heads, sectors, line); if (r == -1) /* do_sfdisk_N has already called reply_with_error */ goto done; @@ -3031,6 +3031,122 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_wc_c_args, (char *) &args); } +static void head_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_head_args args; + char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_head_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "head"); + return; + } + path = args.path; + + r = do_head (path); + if (r == NULL) + /* do_head has already called reply_with_error */ + goto done; + + struct guestfs_head_ret ret; + ret.lines.lines_len = count_strings (r); + ret.lines.lines_val = r; + reply ((xdrproc_t) &xdr_guestfs_head_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_head_args, (char *) &args); +} + +static void head_n_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_head_n_args args; + int nrlines; + char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_head_n_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "head_n"); + return; + } + nrlines = args.nrlines; + path = args.path; + + r = do_head_n (nrlines, path); + if (r == NULL) + /* do_head_n has already called reply_with_error */ + goto done; + + struct guestfs_head_n_ret ret; + ret.lines.lines_len = count_strings (r); + ret.lines.lines_val = r; + reply ((xdrproc_t) &xdr_guestfs_head_n_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_head_n_args, (char *) &args); +} + +static void tail_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_tail_args args; + char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_tail_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "tail"); + return; + } + path = args.path; + + r = do_tail (path); + if (r == NULL) + /* do_tail has already called reply_with_error */ + goto done; + + struct guestfs_tail_ret ret; + ret.lines.lines_len = count_strings (r); + ret.lines.lines_val = r; + reply ((xdrproc_t) &xdr_guestfs_tail_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_tail_args, (char *) &args); +} + +static void tail_n_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_tail_n_args args; + int nrlines; + char *path; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_tail_n_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "tail_n"); + return; + } + nrlines = args.nrlines; + path = args.path; + + r = do_tail_n (nrlines, path); + if (r == NULL) + /* do_tail_n has already called reply_with_error */ + goto done; + + struct guestfs_tail_n_ret ret; + ret.lines.lines_len = count_strings (r); + ret.lines.lines_val = r; + reply ((xdrproc_t) &xdr_guestfs_tail_n_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_tail_n_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -3394,6 +3510,18 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_WC_C: wc_c_stub (xdr_in); break; + case GUESTFS_PROC_HEAD: + head_stub (xdr_in); + break; + case GUESTFS_PROC_HEAD_N: + head_n_stub (xdr_in); + break; + case GUESTFS_PROC_TAIL: + tail_stub (xdr_in); + break; + case GUESTFS_PROC_TAIL_N: + tail_n_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index 89fa1fb..9bd53ec 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -86,6 +86,8 @@ void list_commands (void) printf ("%-20s %s\n", "get-verbose", "get verbose mode"); printf ("%-20s %s\n", "glob-expand", "expand a wildcard path"); printf ("%-20s %s\n", "grub-install", "install GRUB"); + printf ("%-20s %s\n", "head", "return first 10 lines of a file"); + printf ("%-20s %s\n", "head-n", "return first N lines of a file"); 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"); @@ -151,6 +153,8 @@ void list_commands (void) 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", "tail", "return last 10 lines of a file"); + printf ("%-20s %s\n", "tail-n", "return last N lines of a file"); printf ("%-20s %s\n", "tar-in", "unpack tarfile to directory"); printf ("%-20s %s\n", "tar-out", "pack directory into tarfile"); printf ("%-20s %s\n", "tgz-in", "unpack compressed tarball to directory"); @@ -535,7 +539,7 @@ void display_command (const char *cmd) pod2text ("pvresize - resize an LVM physical volume", " pvresize \n\nThis resizes (expands or shrinks) an existing LVM physical\nvolume to match the new size of the underlying device."); else if (strcasecmp (cmd, "sfdisk_N") == 0 || strcasecmp (cmd, "sfdisk-N") == 0) - pod2text ("sfdisk-N - modify a single partition on a block device", " sfdisk-N \n\nThis runs L option to modify just the single\npartition C (note: C counts from 1).\n\nFor other parameters, see C. You should usually\npass C<0> for the cyls/heads/sectors parameters.\n\nB."); + pod2text ("sfdisk-N - modify a single partition on a block device", " sfdisk-N \n\nThis runs L option to modify just the single\npartition C (note: C counts from 1).\n\nFor other parameters, see C. You should usually\npass C<0> for the cyls/heads/sectors parameters.\n\nB."); else if (strcasecmp (cmd, "sfdisk_l") == 0 || strcasecmp (cmd, "sfdisk-l") == 0) pod2text ("sfdisk-l - display the partition table", " sfdisk-l \n\nThis displays the partition table on C, in the\nhuman-readable output of the L command. It is\nnot intended to be parsed."); @@ -600,6 +604,18 @@ void display_command (const char *cmd) if (strcasecmp (cmd, "wc_c") == 0 || strcasecmp (cmd, "wc-c") == 0) pod2text ("wc-c - count characters in a file", " wc-c \n\nThis command counts the characters in a file, using the\nC external command."); else + if (strcasecmp (cmd, "head") == 0) + pod2text ("head - return first 10 lines of a file", " head \n\nThis command returns up to the first 10 lines of a file as\na list of strings.\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, "head_n") == 0 || strcasecmp (cmd, "head-n") == 0) + pod2text ("head-n - return first N lines of a file", " head-n \n\nIf the parameter C is a positive number, this returns the first\nC lines of the file C.\n\nIf the parameter C is a negative number, this returns lines\nfrom the file C, excluding the last C lines.\n\nIf the parameter C is zero, this returns an empty list.\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, "tail") == 0) + pod2text ("tail - return last 10 lines of a file", " tail \n\nThis command returns up to the last 10 lines of a file as\na list of strings.\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, "tail_n") == 0 || strcasecmp (cmd, "tail-n") == 0) + pod2text ("tail-n - return last N lines of a file", " tail-n \n\nIf the parameter C is a positive number, this returns the last\nC lines of the file C.\n\nIf the parameter C is a negative number, this returns lines\nfrom the file C, starting with the C<-nrlines>th line.\n\nIf the parameter C is zero, this returns an empty list.\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); } @@ -2584,7 +2600,7 @@ static int run_sfdisk_N (const char *cmd, int argc, char *argv[]) { int r; const char *device; - int n; + int partnum; int cyls; int heads; int sectors; @@ -2595,12 +2611,12 @@ static int run_sfdisk_N (const char *cmd, int argc, char *argv[]) return -1; } device = argv[0]; - n = atoi (argv[1]); + partnum = atoi (argv[1]); cyls = atoi (argv[2]); heads = atoi (argv[3]); sectors = atoi (argv[4]); line = argv[5]; - r = guestfs_sfdisk_N (g, device, n, cyls, heads, sectors, line); + r = guestfs_sfdisk_N (g, device, partnum, cyls, heads, sectors, line); return r; } @@ -2936,6 +2952,78 @@ static int run_wc_c (const char *cmd, int argc, char *argv[]) return 0; } +static int run_head (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_head (g, path); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + +static int run_head_n (const char *cmd, int argc, char *argv[]) +{ + char **r; + int nrlines; + 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; + } + nrlines = atoi (argv[0]); + path = argv[1]; + r = guestfs_head_n (g, nrlines, path); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + +static int run_tail (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_tail (g, path); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + +static int run_tail_n (const char *cmd, int argc, char *argv[]) +{ + char **r; + int nrlines; + 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; + } + nrlines = atoi (argv[0]); + path = argv[1]; + r = guestfs_tail_n (g, nrlines, path); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -3361,6 +3449,18 @@ int run_action (const char *cmd, int argc, char *argv[]) if (strcasecmp (cmd, "wc_c") == 0 || strcasecmp (cmd, "wc-c") == 0) return run_wc_c (cmd, argc, argv); else + if (strcasecmp (cmd, "head") == 0) + return run_head (cmd, argc, argv); + else + if (strcasecmp (cmd, "head_n") == 0 || strcasecmp (cmd, "head-n") == 0) + return run_head_n (cmd, argc, argv); + else + if (strcasecmp (cmd, "tail") == 0) + return run_tail (cmd, argc, argv); + else + if (strcasecmp (cmd, "tail_n") == 0 || strcasecmp (cmd, "tail-n") == 0) + return run_tail_n (cmd, argc, argv); + else { fprintf (stderr, "%s: unknown command\n", cmd); return -1; diff --git a/fish/completion.c b/fish/completion.c index f81e047..a2febb7 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -187,6 +187,10 @@ static const char *const commands[] = { "wc-l", "wc-w", "wc-c", + "head", + "head-n", + "tail", + "tail-n", NULL }; diff --git a/guestfish-actions.pod b/guestfish-actions.pod index ee0b14d..d45c001 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -705,6 +705,33 @@ See that manual page for more details. This command installs GRUB (the Grand Unified Bootloader) on C, with the root directory being C. +=head2 head + + head path + +This command returns up to the first 10 lines of a file as +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. + +=head2 head-n + + head-n nrlines path + +If the parameter C is a positive number, this returns the first +C lines of the file C. + +If the parameter C is a negative number, this returns lines +from the file C, excluding the last C lines. + +If the parameter C is zero, this returns an empty list. + +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 hexdump hexdump path @@ -1266,7 +1293,7 @@ can easily destroy all your data>. =head2 sfdisk-N - sfdisk-N device n cyls heads sectors line + sfdisk-N device partnum cyls heads sectors line This runs L option to modify just the single partition C (note: C counts from 1). @@ -1395,6 +1422,33 @@ underlying disk image. You should always call this if you have modified a disk image, before closing the handle. +=head2 tail + + tail path + +This command returns up to the last 10 lines of a file as +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. + +=head2 tail-n + + tail-n nrlines path + +If the parameter C is a positive number, this returns the last +C lines of the file C. + +If the parameter C is a negative number, this returns lines +from the file C, starting with the C<-nrlines>th line. + +If the parameter C is zero, this returns an empty list. + +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 tar-in tar-in (tarfile|-) directory diff --git a/guestfs-actions.pod b/guestfs-actions.pod index 530a413..68357d6 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -915,6 +915,44 @@ C, with the root directory being C. This function returns 0 on success or -1 on error. +=head2 guestfs_head + + char **guestfs_head (guestfs_h *handle, + const char *path); + +This command returns up to the first 10 lines of a file as +a list of strings. + +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_head_n + + char **guestfs_head_n (guestfs_h *handle, + int nrlines, + const char *path); + +If the parameter C is a positive number, this returns the first +C lines of the file C. + +If the parameter C is a negative number, this returns lines +from the file C, excluding the last C lines. + +If the parameter C is zero, this returns an empty list. + +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_hexdump char *guestfs_hexdump (guestfs_h *handle, @@ -1689,7 +1727,7 @@ can easily destroy all your data>. int guestfs_sfdisk_N (guestfs_h *handle, const char *device, - int n, + int partnum, int cyls, int heads, int sectors, @@ -1873,6 +1911,44 @@ closing the handle. This function returns 0 on success or -1 on error. +=head2 guestfs_tail + + char **guestfs_tail (guestfs_h *handle, + const char *path); + +This command returns up to the last 10 lines of a file as +a list of strings. + +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_tail_n + + char **guestfs_tail_n (guestfs_h *handle, + int nrlines, + const char *path); + +If the parameter C is a positive number, this returns the last +C lines of the file C. + +If the parameter C is a negative number, this returns lines +from the file C, starting with the C<-nrlines>th line. + +If the parameter C is zero, this returns an empty list. + +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_tar_in int guestfs_tar_in (guestfs_h *handle, diff --git a/haskell/Guestfs.hs b/haskell/Guestfs.hs index 8101b52..dfa385a 100644 --- a/haskell/Guestfs.hs +++ b/haskell/Guestfs.hs @@ -1158,8 +1158,8 @@ foreign import ccall unsafe "guestfs_sfdisk_N" c_sfdisk_N :: GuestfsP -> CString -> CInt -> CInt -> CInt -> CInt -> CString -> IO (CInt) sfdisk_N :: GuestfsH -> String -> Int -> Int -> Int -> Int -> String -> IO () -sfdisk_N h device n cyls heads sectors line = do - r <- withCString device $ \device -> withCString line $ \line -> withForeignPtr h (\p -> c_sfdisk_N p device (fromIntegral n) (fromIntegral cyls) (fromIntegral heads) (fromIntegral sectors) line) +sfdisk_N h device partnum cyls heads sectors line = do + r <- withCString device $ \device -> withCString line $ \line -> withForeignPtr h (\p -> c_sfdisk_N p device (fromIntegral partnum) (fromIntegral cyls) (fromIntegral heads) (fromIntegral sectors) line) if (r == -1) then do err <- last_error h diff --git a/images/Makefile.am b/images/Makefile.am index 46488eb..71d63a8 100644 --- a/images/Makefile.am +++ b/images/Makefile.am @@ -56,7 +56,7 @@ test.sqsh: $(squash_files) rm -f $@ $@-t i=0; \ while [ $$i -lt 10000 ]; do \ - echo "abcdefghijklmnopqrstuvwxyz"; \ + echo "$${i}abcdefghijklmnopqrstuvwxyz"; \ i=$$(($$i+1)); \ done > $@-t mv $@-t $@ diff --git a/java/com/redhat/et/libguestfs/GuestFS.java b/java/com/redhat/et/libguestfs/GuestFS.java index 64b0802..3091332 100644 --- a/java/com/redhat/et/libguestfs/GuestFS.java +++ b/java/com/redhat/et/libguestfs/GuestFS.java @@ -3112,14 +3112,14 @@ public HashMap test0rhashtableerr () *

* @throws LibGuestFSException */ - public void sfdisk_N (String device, int n, int cyls, int heads, int sectors, String line) + public void sfdisk_N (String device, int partnum, int cyls, int heads, int sectors, String line) throws LibGuestFSException { if (g == 0) throw new LibGuestFSException ("sfdisk_N: handle is closed"); - _sfdisk_N (g, device, n, cyls, heads, sectors, line); + _sfdisk_N (g, device, partnum, cyls, heads, sectors, line); } - private native void _sfdisk_N (long g, String device, int n, int cyls, int heads, int sectors, String line) + private native void _sfdisk_N (long g, String device, int partnum, int cyls, int heads, int sectors, String line) throws LibGuestFSException; /** @@ -3623,4 +3623,106 @@ public HashMap test0rhashtableerr () private native int _wc_c (long g, String path) throws LibGuestFSException; + /** + * return first 10 lines of a file + *

+ * This command returns up to the first 10 lines of a file + * as 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. + *

+ * @throws LibGuestFSException + */ + public String[] head (String path) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("head: handle is closed"); + return _head (g, path); + } + private native String[] _head (long g, String path) + throws LibGuestFSException; + + /** + * return first N lines of a file + *

+ * If the parameter "nrlines" is a positive number, this + * returns the first "nrlines" lines of the file "path". + *

+ * If the parameter "nrlines" is a negative number, this + * returns lines from the file "path", excluding the last + * "nrlines" lines. + *

+ * If the parameter "nrlines" is zero, this returns an + * empty list. + *

+ * 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[] head_n (int nrlines, String path) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("head_n: handle is closed"); + return _head_n (g, nrlines, path); + } + private native String[] _head_n (long g, int nrlines, String path) + throws LibGuestFSException; + + /** + * return last 10 lines of a file + *

+ * This command returns up to the last 10 lines of a file + * as 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. + *

+ * @throws LibGuestFSException + */ + public String[] tail (String path) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("tail: handle is closed"); + return _tail (g, path); + } + private native String[] _tail (long g, String path) + throws LibGuestFSException; + + /** + * return last N lines of a file + *

+ * If the parameter "nrlines" is a positive number, this + * returns the last "nrlines" lines of the file "path". + *

+ * If the parameter "nrlines" is a negative number, this + * returns lines from the file "path", starting with the + * "-nrlines"th line. + *

+ * If the parameter "nrlines" is zero, this returns an + * empty list. + *

+ * 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[] tail_n (int nrlines, String path) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("tail_n: handle is closed"); + return _tail_n (g, nrlines, path); + } + private native String[] _tail_n (long g, int nrlines, String path) + throws LibGuestFSException; + } diff --git a/java/com_redhat_et_libguestfs_GuestFS.c b/java/com_redhat_et_libguestfs_GuestFS.c index 3a16259..501e8d1 100644 --- a/java/com_redhat_et_libguestfs_GuestFS.c +++ b/java/com_redhat_et_libguestfs_GuestFS.c @@ -3728,24 +3728,24 @@ Java_com_redhat_et_libguestfs_GuestFS__1pvresize JNIEXPORT void JNICALL Java_com_redhat_et_libguestfs_GuestFS__1sfdisk_1N - (JNIEnv *env, jobject obj, jlong jg, jstring jdevice, jint jn, jint jcyls, jint jheads, jint jsectors, jstring jline) + (JNIEnv *env, jobject obj, jlong jg, jstring jdevice, jint jpartnum, jint jcyls, jint jheads, jint jsectors, jstring jline) { guestfs_h *g = (guestfs_h *) (long) jg; int r; const char *device; - int n; + int partnum; int cyls; int heads; int sectors; const char *line; device = (*env)->GetStringUTFChars (env, jdevice, NULL); - n = jn; + partnum = jpartnum; cyls = jcyls; heads = jheads; sectors = jsectors; line = (*env)->GetStringUTFChars (env, jline, NULL); - r = guestfs_sfdisk_N (g, device, n, cyls, heads, sectors, line); + r = guestfs_sfdisk_N (g, device, partnum, cyls, heads, sectors, line); (*env)->ReleaseStringUTFChars (env, jdevice, device); (*env)->ReleaseStringUTFChars (env, jline, line); if (r == -1) { @@ -4199,3 +4199,139 @@ Java_com_redhat_et_libguestfs_GuestFS__1wc_1c return (jint) r; } +JNIEXPORT jobjectArray JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1head + (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_head (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__1head_1n + (JNIEnv *env, jobject obj, jlong jg, jint jnrlines, jstring jpath) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jobjectArray jr; + int r_len; + jclass cl; + jstring jstr; + char **r; + int nrlines; + const char *path; + int i; + + nrlines = jnrlines; + path = (*env)->GetStringUTFChars (env, jpath, NULL); + r = guestfs_head_n (g, nrlines, 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__1tail + (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_tail (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__1tail_1n + (JNIEnv *env, jobject obj, jlong jg, jint jnrlines, jstring jpath) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jobjectArray jr; + int r_len; + jclass cl; + jstring jstr; + char **r; + int nrlines; + const char *path; + int i; + + nrlines = jnrlines; + path = (*env)->GetStringUTFChars (env, jpath, NULL); + r = guestfs_tail_n (g, nrlines, 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; +} + diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index b84504e..c631024 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -287,3 +287,7 @@ external mkdtemp : t -> string -> string = "ocaml_guestfs_mkdtemp" external wc_l : t -> string -> int = "ocaml_guestfs_wc_l" external wc_w : t -> string -> int = "ocaml_guestfs_wc_w" external wc_c : t -> string -> int = "ocaml_guestfs_wc_c" +external head : t -> string -> string array = "ocaml_guestfs_head" +external head_n : t -> int -> string -> string array = "ocaml_guestfs_head_n" +external tail : t -> string -> string array = "ocaml_guestfs_tail" +external tail_n : t -> int -> string -> string array = "ocaml_guestfs_tail_n" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 4c1ef72..97180b0 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -640,3 +640,15 @@ val wc_w : t -> string -> int val wc_c : t -> string -> int (** count characters in a file *) +val head : t -> string -> string array +(** return first 10 lines of a file *) + +val head_n : t -> int -> string -> string array +(** return first N lines of a file *) + +val tail : t -> string -> string array +(** return last 10 lines of a file *) + +val tail_n : t -> int -> string -> string array +(** return last N lines of a file *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index beb41d7..d8b0567 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -3901,9 +3901,9 @@ ocaml_guestfs_pvresize (value gv, value devicev) } CAMLprim value -ocaml_guestfs_sfdisk_N (value gv, value devicev, value nv, value cylsv, value headsv, value sectorsv, value linev) +ocaml_guestfs_sfdisk_N (value gv, value devicev, value partnumv, value cylsv, value headsv, value sectorsv, value linev) { - CAMLparam5 (gv, devicev, nv, cylsv, headsv); + CAMLparam5 (gv, devicev, partnumv, cylsv, headsv); CAMLxparam2 (sectorsv, linev); CAMLlocal1 (rv); @@ -3912,7 +3912,7 @@ ocaml_guestfs_sfdisk_N (value gv, value devicev, value nv, value cylsv, value he caml_failwith ("sfdisk_N: used handle after closing it"); const char *device = String_val (devicev); - int n = Int_val (nv); + int partnum = Int_val (partnumv); int cyls = Int_val (cylsv); int heads = Int_val (headsv); int sectors = Int_val (sectorsv); @@ -3920,7 +3920,7 @@ ocaml_guestfs_sfdisk_N (value gv, value devicev, value nv, value cylsv, value he int r; caml_enter_blocking_section (); - r = guestfs_sfdisk_N (g, device, n, cyls, heads, sectors, line); + r = guestfs_sfdisk_N (g, device, partnum, cyls, heads, sectors, line); caml_leave_blocking_section (); if (r == -1) ocaml_guestfs_raise_error (g, "sfdisk_N"); @@ -4436,3 +4436,109 @@ ocaml_guestfs_wc_c (value gv, value pathv) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_head (value gv, value pathv) +{ + CAMLparam2 (gv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("head: used handle after closing it"); + + const char *path = String_val (pathv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_head (g, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "head"); + + 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_head_n (value gv, value nrlinesv, value pathv) +{ + CAMLparam3 (gv, nrlinesv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("head_n: used handle after closing it"); + + int nrlines = Int_val (nrlinesv); + const char *path = String_val (pathv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_head_n (g, nrlines, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "head_n"); + + 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_tail (value gv, value pathv) +{ + CAMLparam2 (gv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("tail: used handle after closing it"); + + const char *path = String_val (pathv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_tail (g, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "tail"); + + 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_tail_n (value gv, value nrlinesv, value pathv) +{ + CAMLparam3 (gv, nrlinesv, pathv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("tail_n: used handle after closing it"); + + int nrlines = Int_val (nrlinesv); + const char *path = String_val (pathv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_tail_n (g, nrlines, path); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "tail_n"); + + rv = caml_copy_string_array ((const char **) r); + for (i = 0; r[i] != NULL; ++i) free (r[i]); + free (r); + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index 1dc634f..bcd7304 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -2386,10 +2386,10 @@ PREINIT: croak ("pvresize: %s", guestfs_last_error (g)); void -sfdisk_N (g, device, n, cyls, heads, sectors, line) +sfdisk_N (g, device, partnum, cyls, heads, sectors, line) guestfs_h *g; char *device; - int n; + int partnum; int cyls; int heads; int sectors; @@ -2397,7 +2397,7 @@ sfdisk_N (g, device, n, cyls, heads, sectors, line) PREINIT: int r; PPCODE: - r = guestfs_sfdisk_N (g, device, n, cyls, heads, sectors, line); + r = guestfs_sfdisk_N (g, device, partnum, cyls, heads, sectors, line); if (r == -1) croak ("sfdisk_N: %s", guestfs_last_error (g)); @@ -2692,3 +2692,81 @@ PREINIT: OUTPUT: RETVAL +void +head (g, path) + guestfs_h *g; + char *path; +PREINIT: + char **lines; + int i, n; + PPCODE: + lines = guestfs_head (g, path); + if (lines == NULL) + croak ("head: %s", guestfs_last_error (g)); + for (n = 0; lines[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (lines[i], 0))); + free (lines[i]); + } + free (lines); + +void +head_n (g, nrlines, path) + guestfs_h *g; + int nrlines; + char *path; +PREINIT: + char **lines; + int i, n; + PPCODE: + lines = guestfs_head_n (g, nrlines, path); + if (lines == NULL) + croak ("head_n: %s", guestfs_last_error (g)); + for (n = 0; lines[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (lines[i], 0))); + free (lines[i]); + } + free (lines); + +void +tail (g, path) + guestfs_h *g; + char *path; +PREINIT: + char **lines; + int i, n; + PPCODE: + lines = guestfs_tail (g, path); + if (lines == NULL) + croak ("tail: %s", guestfs_last_error (g)); + for (n = 0; lines[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (lines[i], 0))); + free (lines[i]); + } + free (lines); + +void +tail_n (g, nrlines, path) + guestfs_h *g; + int nrlines; + char *path; +PREINIT: + char **lines; + int i, n; + PPCODE: + lines = guestfs_tail_n (g, nrlines, path); + if (lines == NULL) + croak ("tail_n: %s", guestfs_last_error (g)); + for (n = 0; lines[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (lines[i], 0))); + free (lines[i]); + } + free (lines); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index 8871b68..3d15738 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -694,6 +694,29 @@ See that manual page for more details. This command installs GRUB (the Grand Unified Bootloader) on C, with the root directory being C. +=item @lines = $h->head ($path); + +This command returns up to the first 10 lines of a file as +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. + +=item @lines = $h->head_n ($nrlines, $path); + +If the parameter C is a positive number, this returns the first +C lines of the file C. + +If the parameter C is a negative number, this returns lines +from the file C, excluding the last C lines. + +If the parameter C is zero, this returns an empty list. + +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 $dump = $h->hexdump ($path); This runs C on the given C. The result is @@ -1161,7 +1184,7 @@ See also: C<$h-Esfdisk_l>, C<$h-Esfdisk_N> B. -=item $h->sfdisk_N ($device, $n, $cyls, $heads, $sectors, $line); +=item $h->sfdisk_N ($device, $partnum, $cyls, $heads, $sectors, $line); This runs L option to modify just the single partition C (note: C counts from 1). @@ -1268,6 +1291,29 @@ underlying disk image. You should always call this if you have modified a disk image, before closing the handle. +=item @lines = $h->tail ($path); + +This command returns up to the last 10 lines of a file as +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. + +=item @lines = $h->tail_n ($nrlines, $path); + +If the parameter C is a positive number, this returns the last +C lines of the file C. + +If the parameter C is a negative number, this returns lines +from the file C, starting with the C<-nrlines>th line. + +If the parameter C is zero, this returns an empty list. + +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->tar_in ($tarfile, $directory); This command uploads and unpacks local file C (an diff --git a/python/guestfs-py.c b/python/guestfs-py.c index 07f3c46..7d28af4 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -4153,18 +4153,18 @@ py_guestfs_sfdisk_N (PyObject *self, PyObject *args) PyObject *py_r; int r; const char *device; - int n; + int partnum; int cyls; int heads; int sectors; const char *line; if (!PyArg_ParseTuple (args, (char *) "Osiiiis:guestfs_sfdisk_N", - &py_g, &device, &n, &cyls, &heads, §ors, &line)) + &py_g, &device, &partnum, &cyls, &heads, §ors, &line)) return NULL; g = get_handle (py_g); - r = guestfs_sfdisk_N (g, device, n, cyls, heads, sectors, line); + r = guestfs_sfdisk_N (g, device, partnum, cyls, heads, sectors, line); if (r == -1) { PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); return NULL; @@ -4703,6 +4703,108 @@ py_guestfs_wc_c (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_head (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_head", + &py_g, &path)) + return NULL; + g = get_handle (py_g); + + r = guestfs_head (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_head_n (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char **r; + int nrlines; + const char *path; + + if (!PyArg_ParseTuple (args, (char *) "Ois:guestfs_head_n", + &py_g, &nrlines, &path)) + return NULL; + g = get_handle (py_g); + + r = guestfs_head_n (g, nrlines, 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_tail (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_tail", + &py_g, &path)) + return NULL; + g = get_handle (py_g); + + r = guestfs_tail (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_tail_n (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char **r; + int nrlines; + const char *path; + + if (!PyArg_ParseTuple (args, (char *) "Ois:guestfs_tail_n", + &py_g, &nrlines, &path)) + return NULL; + g = get_handle (py_g); + + r = guestfs_tail_n (g, nrlines, 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 PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -4878,6 +4980,10 @@ static PyMethodDef methods[] = { { (char *) "wc_l", py_guestfs_wc_l, METH_VARARGS, NULL }, { (char *) "wc_w", py_guestfs_wc_w, METH_VARARGS, NULL }, { (char *) "wc_c", py_guestfs_wc_c, METH_VARARGS, NULL }, + { (char *) "head", py_guestfs_head, METH_VARARGS, NULL }, + { (char *) "head_n", py_guestfs_head_n, METH_VARARGS, NULL }, + { (char *) "tail", py_guestfs_tail, METH_VARARGS, NULL }, + { (char *) "tail_n", py_guestfs_tail_n, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index 01318ce..30728be 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -1478,7 +1478,7 @@ class GuestFS: """ return libguestfsmod.pvresize (self._o, device) - def sfdisk_N (self, device, n, cyls, heads, sectors, line): + def sfdisk_N (self, device, partnum, cyls, heads, sectors, line): u"""This runs sfdisk(8) option to modify just the single partition "n" (note: "n" counts from 1). @@ -1488,7 +1488,7 @@ class GuestFS: This command is dangerous. Without careful use you can easily destroy all your data. """ - return libguestfsmod.sfdisk_N (self._o, device, n, cyls, heads, sectors, line) + return libguestfsmod.sfdisk_N (self._o, device, partnum, cyls, heads, sectors, line) def sfdisk_l (self, device): u"""This displays the partition table on "device", in the @@ -1745,3 +1745,65 @@ class GuestFS: """ return libguestfsmod.wc_c (self._o, path) + def head (self, path): + u"""This command returns up to the first 10 lines of a file + as a list of strings. + + 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.head (self._o, path) + + def head_n (self, nrlines, path): + u"""If the parameter "nrlines" is a positive number, this + returns the first "nrlines" lines of the file "path". + + If the parameter "nrlines" is a negative number, this + returns lines from the file "path", excluding the last + "nrlines" lines. + + If the parameter "nrlines" is zero, this returns an + empty list. + + 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.head_n (self._o, nrlines, path) + + def tail (self, path): + u"""This command returns up to the last 10 lines of a file + as a list of strings. + + 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.tail (self._o, path) + + def tail_n (self, nrlines, path): + u"""If the parameter "nrlines" is a positive number, this + returns the last "nrlines" lines of the file "path". + + If the parameter "nrlines" is a negative number, this + returns lines from the file "path", starting with the + "-nrlines"th line. + + If the parameter "nrlines" is zero, this returns an + empty list. + + 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.tail_n (self._o, nrlines, path) + diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index 00736bc..a48ecab 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -3869,7 +3869,7 @@ static VALUE ruby_guestfs_pvresize (VALUE gv, VALUE devicev) return Qnil; } -static VALUE ruby_guestfs_sfdisk_N (VALUE gv, VALUE devicev, VALUE nv, VALUE cylsv, VALUE headsv, VALUE sectorsv, VALUE linev) +static VALUE ruby_guestfs_sfdisk_N (VALUE gv, VALUE devicev, VALUE partnumv, VALUE cylsv, VALUE headsv, VALUE sectorsv, VALUE linev) { guestfs_h *g; Data_Get_Struct (gv, guestfs_h, g); @@ -3881,7 +3881,7 @@ static VALUE ruby_guestfs_sfdisk_N (VALUE gv, VALUE devicev, VALUE nv, VALUE cyl if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "device", "sfdisk_N"); - int n = NUM2INT (nv); + int partnum = NUM2INT (partnumv); int cyls = NUM2INT (cylsv); int heads = NUM2INT (headsv); int sectors = NUM2INT (sectorsv); @@ -3893,7 +3893,7 @@ static VALUE ruby_guestfs_sfdisk_N (VALUE gv, VALUE devicev, VALUE nv, VALUE cyl int r; - r = guestfs_sfdisk_N (g, device, n, cyls, heads, sectors, line); + r = guestfs_sfdisk_N (g, device, partnum, cyls, heads, sectors, line); if (r == -1) rb_raise (e_Error, "%s", guestfs_last_error (g)); @@ -4399,6 +4399,128 @@ static VALUE ruby_guestfs_wc_c (VALUE gv, VALUE pathv) return INT2NUM (r); } +static VALUE ruby_guestfs_head (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", "head"); + + Check_Type (pathv, T_STRING); + const char *path = StringValueCStr (pathv); + if (!path) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "path", "head"); + + char **r; + + r = guestfs_head (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_head_n (VALUE gv, VALUE nrlinesv, VALUE pathv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "head_n"); + + int nrlines = NUM2INT (nrlinesv); + Check_Type (pathv, T_STRING); + const char *path = StringValueCStr (pathv); + if (!path) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "path", "head_n"); + + char **r; + + r = guestfs_head_n (g, nrlines, 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_tail (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", "tail"); + + Check_Type (pathv, T_STRING); + const char *path = StringValueCStr (pathv); + if (!path) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "path", "tail"); + + char **r; + + r = guestfs_tail (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_tail_n (VALUE gv, VALUE nrlinesv, VALUE pathv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "tail_n"); + + int nrlines = NUM2INT (nrlinesv); + Check_Type (pathv, T_STRING); + const char *path = StringValueCStr (pathv); + if (!path) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "path", "tail_n"); + + char **r; + + r = guestfs_tail_n (g, nrlines, 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; +} + /* Initialize the module. */ void Init__guestfs () { @@ -4753,4 +4875,12 @@ void Init__guestfs () ruby_guestfs_wc_w, 1); rb_define_method (c_guestfs, "wc_c", ruby_guestfs_wc_c, 1); + rb_define_method (c_guestfs, "head", + ruby_guestfs_head, 1); + rb_define_method (c_guestfs, "head_n", + ruby_guestfs_head_n, 2); + rb_define_method (c_guestfs, "tail", + ruby_guestfs_tail, 1); + rb_define_method (c_guestfs, "tail_n", + ruby_guestfs_tail_n, 2); } diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 52bd8e4..fc902f4 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -120 +124 diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index ce4cfe1..4a38b45 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -9024,7 +9024,7 @@ static void sfdisk_N_reply_cb (guestfs_h *g, void *data, XDR *xdr) int guestfs_sfdisk_N (guestfs_h *g, const char *device, - int n, + int partnum, int cyls, int heads, int sectors, @@ -9041,7 +9041,7 @@ int guestfs_sfdisk_N (guestfs_h *g, memset (&ctx, 0, sizeof ctx); args.device = (char *) device; - args.n = n; + args.partnum = partnum; args.cyls = cyls; args.heads = heads; args.sectors = sectors; @@ -10989,3 +10989,395 @@ int guestfs_wc_c (guestfs_h *g, return ctx.ret.chars; } +struct head_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_head_ret ret; +}; + +static void head_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct head_ctx *ctx = (struct head_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_head"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_head"); + 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_head"); + return; + } + goto done; + } + if (!xdr_guestfs_head_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_head"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_head (guestfs_h *g, + const char *path) +{ + struct guestfs_head_args args; + struct head_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_head") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.path = (char *) path; + serial = guestfs__send_sync (g, GUESTFS_PROC_HEAD, + (xdrproc_t) xdr_guestfs_head_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, head_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_head"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_HEAD, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.lines.lines_val = + safe_realloc (g, ctx.ret.lines.lines_val, + sizeof (char *) * (ctx.ret.lines.lines_len + 1)); + ctx.ret.lines.lines_val[ctx.ret.lines.lines_len] = NULL; + return ctx.ret.lines.lines_val; +} + +struct head_n_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_head_n_ret ret; +}; + +static void head_n_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct head_n_ctx *ctx = (struct head_n_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_head_n"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_head_n"); + 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_head_n"); + return; + } + goto done; + } + if (!xdr_guestfs_head_n_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_head_n"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_head_n (guestfs_h *g, + int nrlines, + const char *path) +{ + struct guestfs_head_n_args args; + struct head_n_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_head_n") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.nrlines = nrlines; + args.path = (char *) path; + serial = guestfs__send_sync (g, GUESTFS_PROC_HEAD_N, + (xdrproc_t) xdr_guestfs_head_n_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, head_n_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_head_n"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_HEAD_N, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.lines.lines_val = + safe_realloc (g, ctx.ret.lines.lines_val, + sizeof (char *) * (ctx.ret.lines.lines_len + 1)); + ctx.ret.lines.lines_val[ctx.ret.lines.lines_len] = NULL; + return ctx.ret.lines.lines_val; +} + +struct tail_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_tail_ret ret; +}; + +static void tail_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct tail_ctx *ctx = (struct tail_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_tail"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_tail"); + 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_tail"); + return; + } + goto done; + } + if (!xdr_guestfs_tail_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_tail"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_tail (guestfs_h *g, + const char *path) +{ + struct guestfs_tail_args args; + struct tail_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_tail") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.path = (char *) path; + serial = guestfs__send_sync (g, GUESTFS_PROC_TAIL, + (xdrproc_t) xdr_guestfs_tail_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, tail_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_tail"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_TAIL, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.lines.lines_val = + safe_realloc (g, ctx.ret.lines.lines_val, + sizeof (char *) * (ctx.ret.lines.lines_len + 1)); + ctx.ret.lines.lines_val[ctx.ret.lines.lines_len] = NULL; + return ctx.ret.lines.lines_val; +} + +struct tail_n_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_tail_n_ret ret; +}; + +static void tail_n_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct tail_n_ctx *ctx = (struct tail_n_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_tail_n"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_tail_n"); + 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_tail_n"); + return; + } + goto done; + } + if (!xdr_guestfs_tail_n_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_tail_n"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_tail_n (guestfs_h *g, + int nrlines, + const char *path) +{ + struct guestfs_tail_n_args args; + struct tail_n_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_tail_n") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.nrlines = nrlines; + args.path = (char *) path; + serial = guestfs__send_sync (g, GUESTFS_PROC_TAIL_N, + (xdrproc_t) xdr_guestfs_tail_n_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, tail_n_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_tail_n"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_TAIL_N, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.lines.lines_val = + safe_realloc (g, ctx.ret.lines.lines_val, + sizeof (char *) * (ctx.ret.lines.lines_len + 1)); + ctx.ret.lines.lines_val[ctx.ret.lines.lines_len] = NULL; + return ctx.ret.lines.lines_val; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index cbdf0fb..42951cb 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -169,7 +169,7 @@ extern char **guestfs_strings_e (guestfs_h *handle, const char *encoding, const extern char *guestfs_hexdump (guestfs_h *handle, const char *path); extern int guestfs_zerofree (guestfs_h *handle, const char *device); extern int guestfs_pvresize (guestfs_h *handle, const char *device); -extern int guestfs_sfdisk_N (guestfs_h *handle, const char *device, int n, int cyls, int heads, int sectors, const char *line); +extern int guestfs_sfdisk_N (guestfs_h *handle, const char *device, int partnum, int cyls, int heads, int sectors, const char *line); extern char *guestfs_sfdisk_l (guestfs_h *handle, const char *device); extern char *guestfs_sfdisk_kernel_geometry (guestfs_h *handle, const char *device); extern char *guestfs_sfdisk_disk_geometry (guestfs_h *handle, const char *device); @@ -191,3 +191,7 @@ extern char *guestfs_mkdtemp (guestfs_h *handle, const char *template); extern int guestfs_wc_l (guestfs_h *handle, const char *path); extern int guestfs_wc_w (guestfs_h *handle, const char *path); extern int guestfs_wc_c (guestfs_h *handle, const char *path); +extern char **guestfs_head (guestfs_h *handle, const char *path); +extern char **guestfs_head_n (guestfs_h *handle, int nrlines, const char *path); +extern char **guestfs_tail (guestfs_h *handle, const char *path); +extern char **guestfs_tail_n (guestfs_h *handle, int nrlines, const char *path); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index bdcc204..3f08084 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1644,7 +1644,7 @@ xdr_guestfs_sfdisk_N_args (XDR *xdrs, guestfs_sfdisk_N_args *objp) return FALSE; buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); if (buf == NULL) { - if (!xdr_int (xdrs, &objp->n)) + if (!xdr_int (xdrs, &objp->partnum)) return FALSE; if (!xdr_int (xdrs, &objp->cyls)) return FALSE; @@ -1654,7 +1654,7 @@ xdr_guestfs_sfdisk_N_args (XDR *xdrs, guestfs_sfdisk_N_args *objp) return FALSE; } else { - IXDR_PUT_LONG(buf, objp->n); + IXDR_PUT_LONG(buf, objp->partnum); IXDR_PUT_LONG(buf, objp->cyls); IXDR_PUT_LONG(buf, objp->heads); IXDR_PUT_LONG(buf, objp->sectors); @@ -1667,7 +1667,7 @@ xdr_guestfs_sfdisk_N_args (XDR *xdrs, guestfs_sfdisk_N_args *objp) return FALSE; buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT); if (buf == NULL) { - if (!xdr_int (xdrs, &objp->n)) + if (!xdr_int (xdrs, &objp->partnum)) return FALSE; if (!xdr_int (xdrs, &objp->cyls)) return FALSE; @@ -1677,7 +1677,7 @@ xdr_guestfs_sfdisk_N_args (XDR *xdrs, guestfs_sfdisk_N_args *objp) return FALSE; } else { - objp->n = IXDR_GET_LONG(buf); + objp->partnum = IXDR_GET_LONG(buf); objp->cyls = IXDR_GET_LONG(buf); objp->heads = IXDR_GET_LONG(buf); objp->sectors = IXDR_GET_LONG(buf); @@ -1689,7 +1689,7 @@ xdr_guestfs_sfdisk_N_args (XDR *xdrs, guestfs_sfdisk_N_args *objp) if (!xdr_string (xdrs, &objp->device, ~0)) return FALSE; - if (!xdr_int (xdrs, &objp->n)) + if (!xdr_int (xdrs, &objp->partnum)) return FALSE; if (!xdr_int (xdrs, &objp->cyls)) return FALSE; @@ -2043,6 +2043,94 @@ xdr_guestfs_wc_c_ret (XDR *xdrs, guestfs_wc_c_ret *objp) } bool_t +xdr_guestfs_head_args (XDR *xdrs, guestfs_head_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_head_ret (XDR *xdrs, guestfs_head_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_head_n_args (XDR *xdrs, guestfs_head_n_args *objp) +{ + register int32_t *buf; + + if (!xdr_int (xdrs, &objp->nrlines)) + return FALSE; + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_head_n_ret (XDR *xdrs, guestfs_head_n_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_tail_args (XDR *xdrs, guestfs_tail_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_tail_ret (XDR *xdrs, guestfs_tail_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_tail_n_args (XDR *xdrs, guestfs_tail_n_args *objp) +{ + register int32_t *buf; + + if (!xdr_int (xdrs, &objp->nrlines)) + return FALSE; + if (!xdr_string (xdrs, &objp->path, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_tail_n_ret (XDR *xdrs, guestfs_tail_n_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + 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 0c31e79..7fe3a32 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -849,7 +849,7 @@ typedef struct guestfs_pvresize_args guestfs_pvresize_args; struct guestfs_sfdisk_N_args { char *device; - int n; + int partnum; int cyls; int heads; int sectors; @@ -1037,6 +1037,60 @@ struct guestfs_wc_c_ret { }; typedef struct guestfs_wc_c_ret guestfs_wc_c_ret; +struct guestfs_head_args { + char *path; +}; +typedef struct guestfs_head_args guestfs_head_args; + +struct guestfs_head_ret { + struct { + u_int lines_len; + str *lines_val; + } lines; +}; +typedef struct guestfs_head_ret guestfs_head_ret; + +struct guestfs_head_n_args { + int nrlines; + char *path; +}; +typedef struct guestfs_head_n_args guestfs_head_n_args; + +struct guestfs_head_n_ret { + struct { + u_int lines_len; + str *lines_val; + } lines; +}; +typedef struct guestfs_head_n_ret guestfs_head_n_ret; + +struct guestfs_tail_args { + char *path; +}; +typedef struct guestfs_tail_args guestfs_tail_args; + +struct guestfs_tail_ret { + struct { + u_int lines_len; + str *lines_val; + } lines; +}; +typedef struct guestfs_tail_ret guestfs_tail_ret; + +struct guestfs_tail_n_args { + int nrlines; + char *path; +}; +typedef struct guestfs_tail_n_args guestfs_tail_n_args; + +struct guestfs_tail_n_ret { + struct { + u_int lines_len; + str *lines_val; + } lines; +}; +typedef struct guestfs_tail_n_ret guestfs_tail_n_ret; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -1158,7 +1212,11 @@ enum guestfs_procedure { GUESTFS_PROC_WC_L = 118, GUESTFS_PROC_WC_W = 119, GUESTFS_PROC_WC_C = 120, - GUESTFS_PROC_NR_PROCS = 120 + 1, + GUESTFS_PROC_HEAD = 121, + GUESTFS_PROC_HEAD_N = 122, + GUESTFS_PROC_TAIL = 123, + GUESTFS_PROC_TAIL_N = 124, + GUESTFS_PROC_NR_PROCS = 124 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -1376,6 +1434,14 @@ extern bool_t xdr_guestfs_wc_w_args (XDR *, guestfs_wc_w_args*); extern bool_t xdr_guestfs_wc_w_ret (XDR *, guestfs_wc_w_ret*); extern bool_t xdr_guestfs_wc_c_args (XDR *, guestfs_wc_c_args*); extern bool_t xdr_guestfs_wc_c_ret (XDR *, guestfs_wc_c_ret*); +extern bool_t xdr_guestfs_head_args (XDR *, guestfs_head_args*); +extern bool_t xdr_guestfs_head_ret (XDR *, guestfs_head_ret*); +extern bool_t xdr_guestfs_head_n_args (XDR *, guestfs_head_n_args*); +extern bool_t xdr_guestfs_head_n_ret (XDR *, guestfs_head_n_ret*); +extern bool_t xdr_guestfs_tail_args (XDR *, guestfs_tail_args*); +extern bool_t xdr_guestfs_tail_ret (XDR *, guestfs_tail_ret*); +extern bool_t xdr_guestfs_tail_n_args (XDR *, guestfs_tail_n_args*); +extern bool_t xdr_guestfs_tail_n_ret (XDR *, guestfs_tail_n_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*); @@ -1552,6 +1618,14 @@ extern bool_t xdr_guestfs_wc_w_args (); extern bool_t xdr_guestfs_wc_w_ret (); extern bool_t xdr_guestfs_wc_c_args (); extern bool_t xdr_guestfs_wc_c_ret (); +extern bool_t xdr_guestfs_head_args (); +extern bool_t xdr_guestfs_head_ret (); +extern bool_t xdr_guestfs_head_n_args (); +extern bool_t xdr_guestfs_head_n_ret (); +extern bool_t xdr_guestfs_tail_args (); +extern bool_t xdr_guestfs_tail_ret (); +extern bool_t xdr_guestfs_tail_n_args (); +extern bool_t xdr_guestfs_tail_n_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 e0f106c..fd8953f 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -658,7 +658,7 @@ struct guestfs_pvresize_args { struct guestfs_sfdisk_N_args { string device<>; - int n; + int partnum; int cyls; int heads; int sectors; @@ -800,6 +800,40 @@ struct guestfs_wc_c_ret { int chars; }; +struct guestfs_head_args { + string path<>; +}; + +struct guestfs_head_ret { + str lines<>; +}; + +struct guestfs_head_n_args { + int nrlines; + string path<>; +}; + +struct guestfs_head_n_ret { + str lines<>; +}; + +struct guestfs_tail_args { + string path<>; +}; + +struct guestfs_tail_ret { + str lines<>; +}; + +struct guestfs_tail_n_args { + int nrlines; + string path<>; +}; + +struct guestfs_tail_n_ret { + str lines<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -921,6 +955,10 @@ enum guestfs_procedure { GUESTFS_PROC_WC_L = 118, GUESTFS_PROC_WC_W = 119, GUESTFS_PROC_WC_C = 120, + GUESTFS_PROC_HEAD = 121, + GUESTFS_PROC_HEAD_N = 122, + GUESTFS_PROC_TAIL = 123, + GUESTFS_PROC_TAIL_N = 124, GUESTFS_PROC_NR_PROCS };