01a230954a66dbba50c5fc57fd488d87c751d045
[libguestfs.git] / src / generator.ml
1 #!/usr/bin/env ocaml
2 (* libguestfs
3  * Copyright (C) 2009 Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *)
19
20 (* This script generates a large amount of code and documentation for
21  * all the daemon actions.
22  *
23  * To add a new action there are only two files you need to change,
24  * this one to describe the interface (see the big table below), and
25  * daemon/<somefile>.c to write the implementation.
26  *
27  * After editing this file, run it (./src/generator.ml) to regenerate
28  * all the output files.
29  *
30  * IMPORTANT: This script should NOT print any warnings.  If it prints
31  * warnings, you should treat them as errors.
32  * [Need to add -warn-error to ocaml command line]
33  *)
34
35 #load "unix.cma";;
36 #load "str.cma";;
37
38 open Printf
39
40 type style = ret * args
41 and ret =
42     (* "RErr" as a return value means an int used as a simple error
43      * indication, ie. 0 or -1.
44      *)
45   | RErr
46     (* "RInt" as a return value means an int which is -1 for error
47      * or any value >= 0 on success.  Only use this for smallish
48      * positive ints (0 <= i < 2^30).
49      *)
50   | RInt of string
51     (* "RInt64" is the same as RInt, but is guaranteed to be able
52      * to return a full 64 bit value, _except_ that -1 means error
53      * (so -1 cannot be a valid, non-error return value).
54      *)
55   | RInt64 of string
56     (* "RBool" is a bool return value which can be true/false or
57      * -1 for error.
58      *)
59   | RBool of string
60     (* "RConstString" is a string that refers to a constant value.
61      * Try to avoid using this.  In particular you cannot use this
62      * for values returned from the daemon, because there is no
63      * thread-safe way to return them in the C API.
64      *)
65   | RConstString of string
66     (* "RString" and "RStringList" are caller-frees. *)
67   | RString of string
68   | RStringList of string
69     (* Some limited tuples are possible: *)
70   | RIntBool of string * string
71     (* LVM PVs, VGs and LVs. *)
72   | RPVList of string
73   | RVGList of string
74   | RLVList of string
75     (* Stat buffers. *)
76   | RStat of string
77   | RStatVFS of string
78     (* Key-value pairs of untyped strings.  Turns into a hashtable or
79      * dictionary in languages which support it.  DON'T use this as a
80      * general "bucket" for results.  Prefer a stronger typed return
81      * value if one is available, or write a custom struct.  Don't use
82      * this if the list could potentially be very long, since it is
83      * inefficient.  Keys should be unique.  NULLs are not permitted.
84      *)
85   | RHashtable of string
86
87 and args = argt list    (* Function parameters, guestfs handle is implicit. *)
88
89     (* Note in future we should allow a "variable args" parameter as
90      * the final parameter, to allow commands like
91      *   chmod mode file [file(s)...]
92      * This is not implemented yet, but many commands (such as chmod)
93      * are currently defined with the argument order keeping this future
94      * possibility in mind.
95      *)
96 and argt =
97   | String of string    (* const char *name, cannot be NULL *)
98   | OptString of string (* const char *name, may be NULL *)
99   | StringList of string(* list of strings (each string cannot be NULL) *)
100   | Bool of string      (* boolean *)
101   | Int of string       (* int (smallish ints, signed, <= 31 bits) *)
102     (* These are treated as filenames (simple string parameters) in
103      * the C API and bindings.  But in the RPC protocol, we transfer
104      * the actual file content up to or down from the daemon.
105      * FileIn: local machine -> daemon (in request)
106      * FileOut: daemon -> local machine (in reply)
107      * In guestfish (only), the special name "-" means read from
108      * stdin or write to stdout.
109      *)
110   | FileIn of string
111   | FileOut of string
112
113 type flags =
114   | ProtocolLimitWarning  (* display warning about protocol size limits *)
115   | DangerWillRobinson    (* flags particularly dangerous commands *)
116   | FishAlias of string   (* provide an alias for this cmd in guestfish *)
117   | FishAction of string  (* call this function in guestfish *)
118   | NotInFish             (* do not export via guestfish *)
119
120 let protocol_limit_warning =
121   "Because of the message protocol, there is a transfer limit 
122 of somewhere between 2MB and 4MB.  To transfer large files you should use
123 FTP."
124
125 let danger_will_robinson =
126   "B<This command is dangerous.  Without careful use you
127 can easily destroy all your data>."
128
129 (* You can supply zero or as many tests as you want per API call.
130  *
131  * Note that the test environment has 3 block devices, of size 500MB,
132  * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc).
133  * Note for partitioning purposes, the 500MB device has 63 cylinders.
134  *
135  * To be able to run the tests in a reasonable amount of time,
136  * the virtual machine and block devices are reused between tests.
137  * So don't try testing kill_subprocess :-x
138  *
139  * Between each test we blockdev-setrw, umount-all, lvm-remove-all.
140  *
141  * If the appliance is running an older Linux kernel (eg. RHEL 5) then
142  * devices are named /dev/hda etc.  To cope with this, the test suite
143  * adds some hairly logic to detect this case, and then automagically
144  * replaces all strings which match "/dev/sd.*" with "/dev/hd.*".
145  * When writing test cases you shouldn't have to worry about this
146  * difference.
147  *
148  * Don't assume anything about the previous contents of the block
149  * devices.  Use 'Init*' to create some initial scenarios.
150  *
151  * You can add a prerequisite clause to any individual test.  This
152  * is a run-time check, which, if it fails, causes the test to be
153  * skipped.  Useful if testing a command which might not work on
154  * all variations of libguestfs builds.  A test that has prerequisite
155  * of 'Always' is run unconditionally.
156  *)
157 type tests = (test_init * test_prereq * test) list
158 and test =
159     (* Run the command sequence and just expect nothing to fail. *)
160   | TestRun of seq
161     (* Run the command sequence and expect the output of the final
162      * command to be the string.
163      *)
164   | TestOutput of seq * string
165     (* Run the command sequence and expect the output of the final
166      * command to be the list of strings.
167      *)
168   | TestOutputList of seq * string list
169     (* Run the command sequence and expect the output of the final
170      * command to be the integer.
171      *)
172   | TestOutputInt of seq * int
173     (* Run the command sequence and expect the output of the final
174      * command to be a true value (!= 0 or != NULL).
175      *)
176   | TestOutputTrue of seq
177     (* Run the command sequence and expect the output of the final
178      * command to be a false value (== 0 or == NULL, but not an error).
179      *)
180   | TestOutputFalse of seq
181     (* Run the command sequence and expect the output of the final
182      * command to be a list of the given length (but don't care about
183      * content).
184      *)
185   | TestOutputLength of seq * int
186     (* Run the command sequence and expect the output of the final
187      * command to be a structure.
188      *)
189   | TestOutputStruct of seq * test_field_compare list
190     (* Run the command sequence and expect the final command (only)
191      * to fail.
192      *)
193   | TestLastFail of seq
194
195 and test_field_compare =
196   | CompareWithInt of string * int
197   | CompareWithString of string * string
198   | CompareFieldsIntEq of string * string
199   | CompareFieldsStrEq of string * string
200
201 (* Test prerequisites. *)
202 and test_prereq =
203     (* Test always runs. *)
204   | Always
205     (* Test is currently disabled - eg. it fails, or it tests some
206      * unimplemented feature.
207      *)
208   | Disabled
209     (* 'string' is some C code (a function body) that should return
210      * true or false.  The test will run if the code returns true.
211      *)
212   | If of string
213     (* As for 'If' but the test runs _unless_ the code returns true. *)
214   | Unless of string
215
216 (* Some initial scenarios for testing. *)
217 and test_init =
218     (* Do nothing, block devices could contain random stuff including
219      * LVM PVs, and some filesystems might be mounted.  This is usually
220      * a bad idea.
221      *)
222   | InitNone
223     (* Block devices are empty and no filesystems are mounted. *)
224   | InitEmpty
225     (* /dev/sda contains a single partition /dev/sda1, which is formatted
226      * as ext2, empty [except for lost+found] and mounted on /.
227      * /dev/sdb and /dev/sdc may have random content.
228      * No LVM.
229      *)
230   | InitBasicFS
231     (* /dev/sda:
232      *   /dev/sda1 (is a PV):
233      *     /dev/VG/LV (size 8MB):
234      *       formatted as ext2, empty [except for lost+found], mounted on /
235      * /dev/sdb and /dev/sdc may have random content.
236      *)
237   | InitBasicFSonLVM
238
239 (* Sequence of commands for testing. *)
240 and seq = cmd list
241 and cmd = string list
242
243 (* Canned test prerequisites. *)
244 let env_is_true env =
245   sprintf "const char *str = getenv (\"%s\");
246   return str && strcmp (str, \"1\") == 0;" env
247
248 (* Note about long descriptions: When referring to another
249  * action, use the format C<guestfs_other> (ie. the full name of
250  * the C function).  This will be replaced as appropriate in other
251  * language bindings.
252  *
253  * Apart from that, long descriptions are just perldoc paragraphs.
254  *)
255
256 let non_daemon_functions = [
257   ("launch", (RErr, []), -1, [FishAlias "run"; FishAction "launch"],
258    [],
259    "launch the qemu subprocess",
260    "\
261 Internally libguestfs is implemented by running a virtual machine
262 using L<qemu(1)>.
263
264 You should call this after configuring the handle
265 (eg. adding drives) but before performing any actions.");
266
267   ("wait_ready", (RErr, []), -1, [NotInFish],
268    [],
269    "wait until the qemu subprocess launches",
270    "\
271 Internally libguestfs is implemented by running a virtual machine
272 using L<qemu(1)>.
273
274 You should call this after C<guestfs_launch> to wait for the launch
275 to complete.");
276
277   ("kill_subprocess", (RErr, []), -1, [],
278    [],
279    "kill the qemu subprocess",
280    "\
281 This kills the qemu subprocess.  You should never need to call this.");
282
283   ("add_drive", (RErr, [String "filename"]), -1, [FishAlias "add"],
284    [],
285    "add an image to examine or modify",
286    "\
287 This function adds a virtual machine disk image C<filename> to the
288 guest.  The first time you call this function, the disk appears as IDE
289 disk 0 (C</dev/sda>) in the guest, the second time as C</dev/sdb>, and
290 so on.
291
292 You don't necessarily need to be root when using libguestfs.  However
293 you obviously do need sufficient permissions to access the filename
294 for whatever operations you want to perform (ie. read access if you
295 just want to read the image or write access if you want to modify the
296 image).
297
298 This is equivalent to the qemu parameter C<-drive file=filename>.");
299
300   ("add_cdrom", (RErr, [String "filename"]), -1, [FishAlias "cdrom"],
301    [],
302    "add a CD-ROM disk image to examine",
303    "\
304 This function adds a virtual CD-ROM disk image to the guest.
305
306 This is equivalent to the qemu parameter C<-cdrom filename>.");
307
308   ("config", (RErr, [String "qemuparam"; OptString "qemuvalue"]), -1, [],
309    [],
310    "add qemu parameters",
311    "\
312 This can be used to add arbitrary qemu command line parameters
313 of the form C<-param value>.  Actually it's not quite arbitrary - we
314 prevent you from setting some parameters which would interfere with
315 parameters that we use.
316
317 The first character of C<param> string must be a C<-> (dash).
318
319 C<value> can be NULL.");
320
321   ("set_qemu", (RErr, [String "qemu"]), -1, [FishAlias "qemu"],
322    [],
323    "set the qemu binary",
324    "\
325 Set the qemu binary that we will use.
326
327 The default is chosen when the library was compiled by the
328 configure script.
329
330 You can also override this by setting the C<LIBGUESTFS_QEMU>
331 environment variable.
332
333 Setting C<qemu> to C<NULL> restores the default qemu binary.");
334
335   ("get_qemu", (RConstString "qemu", []), -1, [],
336    [],
337    "get the qemu binary",
338    "\
339 Return the current qemu binary.
340
341 This is always non-NULL.  If it wasn't set already, then this will
342 return the default qemu binary name.");
343
344   ("set_path", (RErr, [String "path"]), -1, [FishAlias "path"],
345    [],
346    "set the search path",
347    "\
348 Set the path that libguestfs searches for kernel and initrd.img.
349
350 The default is C<$libdir/guestfs> unless overridden by setting
351 C<LIBGUESTFS_PATH> environment variable.
352
353 Setting C<path> to C<NULL> restores the default path.");
354
355   ("get_path", (RConstString "path", []), -1, [],
356    [],
357    "get the search path",
358    "\
359 Return the current search path.
360
361 This is always non-NULL.  If it wasn't set already, then this will
362 return the default path.");
363
364   ("set_append", (RErr, [String "append"]), -1, [FishAlias "append"],
365    [],
366    "add options to kernel command line",
367    "\
368 This function is used to add additional options to the
369 guest kernel command line.
370
371 The default is C<NULL> unless overridden by setting
372 C<LIBGUESTFS_APPEND> environment variable.
373
374 Setting C<append> to C<NULL> means I<no> additional options
375 are passed (libguestfs always adds a few of its own).");
376
377   ("get_append", (RConstString "append", []), -1, [],
378    [],
379    "get the additional kernel options",
380    "\
381 Return the additional kernel options which are added to the
382 guest kernel command line.
383
384 If C<NULL> then no options are added.");
385
386   ("set_autosync", (RErr, [Bool "autosync"]), -1, [FishAlias "autosync"],
387    [],
388    "set autosync mode",
389    "\
390 If C<autosync> is true, this enables autosync.  Libguestfs will make a
391 best effort attempt to run C<guestfs_umount_all> followed by
392 C<guestfs_sync> when the handle is closed
393 (also if the program exits without closing handles).
394
395 This is disabled by default (except in guestfish where it is
396 enabled by default).");
397
398   ("get_autosync", (RBool "autosync", []), -1, [],
399    [],
400    "get autosync mode",
401    "\
402 Get the autosync flag.");
403
404   ("set_verbose", (RErr, [Bool "verbose"]), -1, [FishAlias "verbose"],
405    [],
406    "set verbose mode",
407    "\
408 If C<verbose> is true, this turns on verbose messages (to C<stderr>).
409
410 Verbose messages are disabled unless the environment variable
411 C<LIBGUESTFS_DEBUG> is defined and set to C<1>.");
412
413   ("get_verbose", (RBool "verbose", []), -1, [],
414    [],
415    "get verbose mode",
416    "\
417 This returns the verbose messages flag.");
418
419   ("is_ready", (RBool "ready", []), -1, [],
420    [],
421    "is ready to accept commands",
422    "\
423 This returns true iff this handle is ready to accept commands
424 (in the C<READY> state).
425
426 For more information on states, see L<guestfs(3)>.");
427
428   ("is_config", (RBool "config", []), -1, [],
429    [],
430    "is in configuration state",
431    "\
432 This returns true iff this handle is being configured
433 (in the C<CONFIG> state).
434
435 For more information on states, see L<guestfs(3)>.");
436
437   ("is_launching", (RBool "launching", []), -1, [],
438    [],
439    "is launching subprocess",
440    "\
441 This returns true iff this handle is launching the subprocess
442 (in the C<LAUNCHING> state).
443
444 For more information on states, see L<guestfs(3)>.");
445
446   ("is_busy", (RBool "busy", []), -1, [],
447    [],
448    "is busy processing a command",
449    "\
450 This returns true iff this handle is busy processing a command
451 (in the C<BUSY> state).
452
453 For more information on states, see L<guestfs(3)>.");
454
455   ("get_state", (RInt "state", []), -1, [],
456    [],
457    "get the current state",
458    "\
459 This returns the current state as an opaque integer.  This is
460 only useful for printing debug and internal error messages.
461
462 For more information on states, see L<guestfs(3)>.");
463
464   ("set_busy", (RErr, []), -1, [NotInFish],
465    [],
466    "set state to busy",
467    "\
468 This sets the state to C<BUSY>.  This is only used when implementing
469 actions using the low-level API.
470
471 For more information on states, see L<guestfs(3)>.");
472
473   ("set_ready", (RErr, []), -1, [NotInFish],
474    [],
475    "set state to ready",
476    "\
477 This sets the state to C<READY>.  This is only used when implementing
478 actions using the low-level API.
479
480 For more information on states, see L<guestfs(3)>.");
481
482   ("end_busy", (RErr, []), -1, [NotInFish],
483    [],
484    "leave the busy state",
485    "\
486 This sets the state to C<READY>, or if in C<CONFIG> then it leaves the
487 state as is.  This is only used when implementing
488 actions using the low-level API.
489
490 For more information on states, see L<guestfs(3)>.");
491
492 ]
493
494 let daemon_functions = [
495   ("mount", (RErr, [String "device"; String "mountpoint"]), 1, [],
496    [InitEmpty, Always, TestOutput (
497       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
498        ["mkfs"; "ext2"; "/dev/sda1"];
499        ["mount"; "/dev/sda1"; "/"];
500        ["write_file"; "/new"; "new file contents"; "0"];
501        ["cat"; "/new"]], "new file contents")],
502    "mount a guest disk at a position in the filesystem",
503    "\
504 Mount a guest disk at a position in the filesystem.  Block devices
505 are named C</dev/sda>, C</dev/sdb> and so on, as they were added to
506 the guest.  If those block devices contain partitions, they will have
507 the usual names (eg. C</dev/sda1>).  Also LVM C</dev/VG/LV>-style
508 names can be used.
509
510 The rules are the same as for L<mount(2)>:  A filesystem must
511 first be mounted on C</> before others can be mounted.  Other
512 filesystems can only be mounted on directories which already
513 exist.
514
515 The mounted filesystem is writable, if we have sufficient permissions
516 on the underlying device.
517
518 The filesystem options C<sync> and C<noatime> are set with this
519 call, in order to improve reliability.");
520
521   ("sync", (RErr, []), 2, [],
522    [ InitEmpty, Always, TestRun [["sync"]]],
523    "sync disks, writes are flushed through to the disk image",
524    "\
525 This syncs the disk, so that any writes are flushed through to the
526 underlying disk image.
527
528 You should always call this if you have modified a disk image, before
529 closing the handle.");
530
531   ("touch", (RErr, [String "path"]), 3, [],
532    [InitBasicFS, Always, TestOutputTrue (
533       [["touch"; "/new"];
534        ["exists"; "/new"]])],
535    "update file timestamps or create a new file",
536    "\
537 Touch acts like the L<touch(1)> command.  It can be used to
538 update the timestamps on a file, or, if the file does not exist,
539 to create a new zero-length file.");
540
541   ("cat", (RString "content", [String "path"]), 4, [ProtocolLimitWarning],
542    [InitBasicFS, Always, TestOutput (
543       [["write_file"; "/new"; "new file contents"; "0"];
544        ["cat"; "/new"]], "new file contents")],
545    "list the contents of a file",
546    "\
547 Return the contents of the file named C<path>.
548
549 Note that this function cannot correctly handle binary files
550 (specifically, files containing C<\\0> character which is treated
551 as end of string).  For those you need to use the C<guestfs_download>
552 function which has a more complex interface.");
553
554   ("ll", (RString "listing", [String "directory"]), 5, [],
555    [], (* XXX Tricky to test because it depends on the exact format
556         * of the 'ls -l' command, which changes between F10 and F11.
557         *)
558    "list the files in a directory (long format)",
559    "\
560 List the files in C<directory> (relative to the root directory,
561 there is no cwd) in the format of 'ls -la'.
562
563 This command is mostly useful for interactive sessions.  It
564 is I<not> intended that you try to parse the output string.");
565
566   ("ls", (RStringList "listing", [String "directory"]), 6, [],
567    [InitBasicFS, Always, TestOutputList (
568       [["touch"; "/new"];
569        ["touch"; "/newer"];
570        ["touch"; "/newest"];
571        ["ls"; "/"]], ["lost+found"; "new"; "newer"; "newest"])],
572    "list the files in a directory",
573    "\
574 List the files in C<directory> (relative to the root directory,
575 there is no cwd).  The '.' and '..' entries are not returned, but
576 hidden files are shown.
577
578 This command is mostly useful for interactive sessions.  Programs
579 should probably use C<guestfs_readdir> instead.");
580
581   ("list_devices", (RStringList "devices", []), 7, [],
582    [InitEmpty, Always, TestOutputList (
583       [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"])],
584    "list the block devices",
585    "\
586 List all the block devices.
587
588 The full block device names are returned, eg. C</dev/sda>");
589
590   ("list_partitions", (RStringList "partitions", []), 8, [],
591    [InitBasicFS, Always, TestOutputList (
592       [["list_partitions"]], ["/dev/sda1"]);
593     InitEmpty, Always, TestOutputList (
594       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
595        ["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
596    "list the partitions",
597    "\
598 List all the partitions detected on all block devices.
599
600 The full partition device names are returned, eg. C</dev/sda1>
601
602 This does not return logical volumes.  For that you will need to
603 call C<guestfs_lvs>.");
604
605   ("pvs", (RStringList "physvols", []), 9, [],
606    [InitBasicFSonLVM, Always, TestOutputList (
607       [["pvs"]], ["/dev/sda1"]);
608     InitEmpty, Always, TestOutputList (
609       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
610        ["pvcreate"; "/dev/sda1"];
611        ["pvcreate"; "/dev/sda2"];
612        ["pvcreate"; "/dev/sda3"];
613        ["pvs"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
614    "list the LVM physical volumes (PVs)",
615    "\
616 List all the physical volumes detected.  This is the equivalent
617 of the L<pvs(8)> command.
618
619 This returns a list of just the device names that contain
620 PVs (eg. C</dev/sda2>).
621
622 See also C<guestfs_pvs_full>.");
623
624   ("vgs", (RStringList "volgroups", []), 10, [],
625    [InitBasicFSonLVM, Always, TestOutputList (
626       [["vgs"]], ["VG"]);
627     InitEmpty, Always, TestOutputList (
628       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
629        ["pvcreate"; "/dev/sda1"];
630        ["pvcreate"; "/dev/sda2"];
631        ["pvcreate"; "/dev/sda3"];
632        ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
633        ["vgcreate"; "VG2"; "/dev/sda3"];
634        ["vgs"]], ["VG1"; "VG2"])],
635    "list the LVM volume groups (VGs)",
636    "\
637 List all the volumes groups detected.  This is the equivalent
638 of the L<vgs(8)> command.
639
640 This returns a list of just the volume group names that were
641 detected (eg. C<VolGroup00>).
642
643 See also C<guestfs_vgs_full>.");
644
645   ("lvs", (RStringList "logvols", []), 11, [],
646    [InitBasicFSonLVM, Always, TestOutputList (
647       [["lvs"]], ["/dev/VG/LV"]);
648     InitEmpty, Always, TestOutputList (
649       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
650        ["pvcreate"; "/dev/sda1"];
651        ["pvcreate"; "/dev/sda2"];
652        ["pvcreate"; "/dev/sda3"];
653        ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
654        ["vgcreate"; "VG2"; "/dev/sda3"];
655        ["lvcreate"; "LV1"; "VG1"; "50"];
656        ["lvcreate"; "LV2"; "VG1"; "50"];
657        ["lvcreate"; "LV3"; "VG2"; "50"];
658        ["lvs"]], ["/dev/VG1/LV1"; "/dev/VG1/LV2"; "/dev/VG2/LV3"])],
659    "list the LVM logical volumes (LVs)",
660    "\
661 List all the logical volumes detected.  This is the equivalent
662 of the L<lvs(8)> command.
663
664 This returns a list of the logical volume device names
665 (eg. C</dev/VolGroup00/LogVol00>).
666
667 See also C<guestfs_lvs_full>.");
668
669   ("pvs_full", (RPVList "physvols", []), 12, [],
670    [], (* XXX how to test? *)
671    "list the LVM physical volumes (PVs)",
672    "\
673 List all the physical volumes detected.  This is the equivalent
674 of the L<pvs(8)> command.  The \"full\" version includes all fields.");
675
676   ("vgs_full", (RVGList "volgroups", []), 13, [],
677    [], (* XXX how to test? *)
678    "list the LVM volume groups (VGs)",
679    "\
680 List all the volumes groups detected.  This is the equivalent
681 of the L<vgs(8)> command.  The \"full\" version includes all fields.");
682
683   ("lvs_full", (RLVList "logvols", []), 14, [],
684    [], (* XXX how to test? *)
685    "list the LVM logical volumes (LVs)",
686    "\
687 List all the logical volumes detected.  This is the equivalent
688 of the L<lvs(8)> command.  The \"full\" version includes all fields.");
689
690   ("read_lines", (RStringList "lines", [String "path"]), 15, [],
691    [InitBasicFS, Always, TestOutputList (
692       [["write_file"; "/new"; "line1\r\nline2\nline3"; "0"];
693        ["read_lines"; "/new"]], ["line1"; "line2"; "line3"]);
694     InitBasicFS, Always, TestOutputList (
695       [["write_file"; "/new"; ""; "0"];
696        ["read_lines"; "/new"]], [])],
697    "read file as lines",
698    "\
699 Return the contents of the file named C<path>.
700
701 The file contents are returned as a list of lines.  Trailing
702 C<LF> and C<CRLF> character sequences are I<not> returned.
703
704 Note that this function cannot correctly handle binary files
705 (specifically, files containing C<\\0> character which is treated
706 as end of line).  For those you need to use the C<guestfs_read_file>
707 function which has a more complex interface.");
708
709   ("aug_init", (RErr, [String "root"; Int "flags"]), 16, [],
710    [], (* XXX Augeas code needs tests. *)
711    "create a new Augeas handle",
712    "\
713 Create a new Augeas handle for editing configuration files.
714 If there was any previous Augeas handle associated with this
715 guestfs session, then it is closed.
716
717 You must call this before using any other C<guestfs_aug_*>
718 commands.
719
720 C<root> is the filesystem root.  C<root> must not be NULL,
721 use C</> instead.
722
723 The flags are the same as the flags defined in
724 E<lt>augeas.hE<gt>, the logical I<or> of the following
725 integers:
726
727 =over 4
728
729 =item C<AUG_SAVE_BACKUP> = 1
730
731 Keep the original file with a C<.augsave> extension.
732
733 =item C<AUG_SAVE_NEWFILE> = 2
734
735 Save changes into a file with extension C<.augnew>, and
736 do not overwrite original.  Overrides C<AUG_SAVE_BACKUP>.
737
738 =item C<AUG_TYPE_CHECK> = 4
739
740 Typecheck lenses (can be expensive).
741
742 =item C<AUG_NO_STDINC> = 8
743
744 Do not use standard load path for modules.
745
746 =item C<AUG_SAVE_NOOP> = 16
747
748 Make save a no-op, just record what would have been changed.
749
750 =item C<AUG_NO_LOAD> = 32
751
752 Do not load the tree in C<guestfs_aug_init>.
753
754 =back
755
756 To close the handle, you can call C<guestfs_aug_close>.
757
758 To find out more about Augeas, see L<http://augeas.net/>.");
759
760   ("aug_close", (RErr, []), 26, [],
761    [], (* XXX Augeas code needs tests. *)
762    "close the current Augeas handle",
763    "\
764 Close the current Augeas handle and free up any resources
765 used by it.  After calling this, you have to call
766 C<guestfs_aug_init> again before you can use any other
767 Augeas functions.");
768
769   ("aug_defvar", (RInt "nrnodes", [String "name"; OptString "expr"]), 17, [],
770    [], (* XXX Augeas code needs tests. *)
771    "define an Augeas variable",
772    "\
773 Defines an Augeas variable C<name> whose value is the result
774 of evaluating C<expr>.  If C<expr> is NULL, then C<name> is
775 undefined.
776
777 On success this returns the number of nodes in C<expr>, or
778 C<0> if C<expr> evaluates to something which is not a nodeset.");
779
780   ("aug_defnode", (RIntBool ("nrnodes", "created"), [String "name"; String "expr"; String "val"]), 18, [],
781    [], (* XXX Augeas code needs tests. *)
782    "define an Augeas node",
783    "\
784 Defines a variable C<name> whose value is the result of
785 evaluating C<expr>.
786
787 If C<expr> evaluates to an empty nodeset, a node is created,
788 equivalent to calling C<guestfs_aug_set> C<expr>, C<value>.
789 C<name> will be the nodeset containing that single node.
790
791 On success this returns a pair containing the
792 number of nodes in the nodeset, and a boolean flag
793 if a node was created.");
794
795   ("aug_get", (RString "val", [String "path"]), 19, [],
796    [], (* XXX Augeas code needs tests. *)
797    "look up the value of an Augeas path",
798    "\
799 Look up the value associated with C<path>.  If C<path>
800 matches exactly one node, the C<value> is returned.");
801
802   ("aug_set", (RErr, [String "path"; String "val"]), 20, [],
803    [], (* XXX Augeas code needs tests. *)
804    "set Augeas path to value",
805    "\
806 Set the value associated with C<path> to C<value>.");
807
808   ("aug_insert", (RErr, [String "path"; String "label"; Bool "before"]), 21, [],
809    [], (* XXX Augeas code needs tests. *)
810    "insert a sibling Augeas node",
811    "\
812 Create a new sibling C<label> for C<path>, inserting it into
813 the tree before or after C<path> (depending on the boolean
814 flag C<before>).
815
816 C<path> must match exactly one existing node in the tree, and
817 C<label> must be a label, ie. not contain C</>, C<*> or end
818 with a bracketed index C<[N]>.");
819
820   ("aug_rm", (RInt "nrnodes", [String "path"]), 22, [],
821    [], (* XXX Augeas code needs tests. *)
822    "remove an Augeas path",
823    "\
824 Remove C<path> and all of its children.
825
826 On success this returns the number of entries which were removed.");
827
828   ("aug_mv", (RErr, [String "src"; String "dest"]), 23, [],
829    [], (* XXX Augeas code needs tests. *)
830    "move Augeas node",
831    "\
832 Move the node C<src> to C<dest>.  C<src> must match exactly
833 one node.  C<dest> is overwritten if it exists.");
834
835   ("aug_match", (RStringList "matches", [String "path"]), 24, [],
836    [], (* XXX Augeas code needs tests. *)
837    "return Augeas nodes which match path",
838    "\
839 Returns a list of paths which match the path expression C<path>.
840 The returned paths are sufficiently qualified so that they match
841 exactly one node in the current tree.");
842
843   ("aug_save", (RErr, []), 25, [],
844    [], (* XXX Augeas code needs tests. *)
845    "write all pending Augeas changes to disk",
846    "\
847 This writes all pending changes to disk.
848
849 The flags which were passed to C<guestfs_aug_init> affect exactly
850 how files are saved.");
851
852   ("aug_load", (RErr, []), 27, [],
853    [], (* XXX Augeas code needs tests. *)
854    "load files into the tree",
855    "\
856 Load files into the tree.
857
858 See C<aug_load> in the Augeas documentation for the full gory
859 details.");
860
861   ("aug_ls", (RStringList "matches", [String "path"]), 28, [],
862    [], (* XXX Augeas code needs tests. *)
863    "list Augeas nodes under a path",
864    "\
865 This is just a shortcut for listing C<guestfs_aug_match>
866 C<path/*> and sorting the resulting nodes into alphabetical order.");
867
868   ("rm", (RErr, [String "path"]), 29, [],
869    [InitBasicFS, Always, TestRun
870       [["touch"; "/new"];
871        ["rm"; "/new"]];
872     InitBasicFS, Always, TestLastFail
873       [["rm"; "/new"]];
874     InitBasicFS, Always, TestLastFail
875       [["mkdir"; "/new"];
876        ["rm"; "/new"]]],
877    "remove a file",
878    "\
879 Remove the single file C<path>.");
880
881   ("rmdir", (RErr, [String "path"]), 30, [],
882    [InitBasicFS, Always, TestRun
883       [["mkdir"; "/new"];
884        ["rmdir"; "/new"]];
885     InitBasicFS, Always, TestLastFail
886       [["rmdir"; "/new"]];
887     InitBasicFS, Always, TestLastFail
888       [["touch"; "/new"];
889        ["rmdir"; "/new"]]],
890    "remove a directory",
891    "\
892 Remove the single directory C<path>.");
893
894   ("rm_rf", (RErr, [String "path"]), 31, [],
895    [InitBasicFS, Always, TestOutputFalse
896       [["mkdir"; "/new"];
897        ["mkdir"; "/new/foo"];
898        ["touch"; "/new/foo/bar"];
899        ["rm_rf"; "/new"];
900        ["exists"; "/new"]]],
901    "remove a file or directory recursively",
902    "\
903 Remove the file or directory C<path>, recursively removing the
904 contents if its a directory.  This is like the C<rm -rf> shell
905 command.");
906
907   ("mkdir", (RErr, [String "path"]), 32, [],
908    [InitBasicFS, Always, TestOutputTrue
909       [["mkdir"; "/new"];
910        ["is_dir"; "/new"]];
911     InitBasicFS, Always, TestLastFail
912       [["mkdir"; "/new/foo/bar"]]],
913    "create a directory",
914    "\
915 Create a directory named C<path>.");
916
917   ("mkdir_p", (RErr, [String "path"]), 33, [],
918    [InitBasicFS, Always, TestOutputTrue
919       [["mkdir_p"; "/new/foo/bar"];
920        ["is_dir"; "/new/foo/bar"]];
921     InitBasicFS, Always, TestOutputTrue
922       [["mkdir_p"; "/new/foo/bar"];
923        ["is_dir"; "/new/foo"]];
924     InitBasicFS, Always, TestOutputTrue
925       [["mkdir_p"; "/new/foo/bar"];
926        ["is_dir"; "/new"]]],
927    "create a directory and parents",
928    "\
929 Create a directory named C<path>, creating any parent directories
930 as necessary.  This is like the C<mkdir -p> shell command.");
931
932   ("chmod", (RErr, [Int "mode"; String "path"]), 34, [],
933    [], (* XXX Need stat command to test *)
934    "change file mode",
935    "\
936 Change the mode (permissions) of C<path> to C<mode>.  Only
937 numeric modes are supported.");
938
939   ("chown", (RErr, [Int "owner"; Int "group"; String "path"]), 35, [],
940    [], (* XXX Need stat command to test *)
941    "change file owner and group",
942    "\
943 Change the file owner to C<owner> and group to C<group>.
944
945 Only numeric uid and gid are supported.  If you want to use
946 names, you will need to locate and parse the password file
947 yourself (Augeas support makes this relatively easy).");
948
949   ("exists", (RBool "existsflag", [String "path"]), 36, [],
950    [InitBasicFS, Always, TestOutputTrue (
951       [["touch"; "/new"];
952        ["exists"; "/new"]]);
953     InitBasicFS, Always, TestOutputTrue (
954       [["mkdir"; "/new"];
955        ["exists"; "/new"]])],
956    "test if file or directory exists",
957    "\
958 This returns C<true> if and only if there is a file, directory
959 (or anything) with the given C<path> name.
960
961 See also C<guestfs_is_file>, C<guestfs_is_dir>, C<guestfs_stat>.");
962
963   ("is_file", (RBool "fileflag", [String "path"]), 37, [],
964    [InitBasicFS, Always, TestOutputTrue (
965       [["touch"; "/new"];
966        ["is_file"; "/new"]]);
967     InitBasicFS, Always, TestOutputFalse (
968       [["mkdir"; "/new"];
969        ["is_file"; "/new"]])],
970    "test if file exists",
971    "\
972 This returns C<true> if and only if there is a file
973 with the given C<path> name.  Note that it returns false for
974 other objects like directories.
975
976 See also C<guestfs_stat>.");
977
978   ("is_dir", (RBool "dirflag", [String "path"]), 38, [],
979    [InitBasicFS, Always, TestOutputFalse (
980       [["touch"; "/new"];
981        ["is_dir"; "/new"]]);
982     InitBasicFS, Always, TestOutputTrue (
983       [["mkdir"; "/new"];
984        ["is_dir"; "/new"]])],
985    "test if file exists",
986    "\
987 This returns C<true> if and only if there is a directory
988 with the given C<path> name.  Note that it returns false for
989 other objects like files.
990
991 See also C<guestfs_stat>.");
992
993   ("pvcreate", (RErr, [String "device"]), 39, [],
994    [InitEmpty, Always, TestOutputList (
995       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
996        ["pvcreate"; "/dev/sda1"];
997        ["pvcreate"; "/dev/sda2"];
998        ["pvcreate"; "/dev/sda3"];
999        ["pvs"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
1000    "create an LVM physical volume",
1001    "\
1002 This creates an LVM physical volume on the named C<device>,
1003 where C<device> should usually be a partition name such
1004 as C</dev/sda1>.");
1005
1006   ("vgcreate", (RErr, [String "volgroup"; StringList "physvols"]), 40, [],
1007    [InitEmpty, Always, TestOutputList (
1008       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
1009        ["pvcreate"; "/dev/sda1"];
1010        ["pvcreate"; "/dev/sda2"];
1011        ["pvcreate"; "/dev/sda3"];
1012        ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
1013        ["vgcreate"; "VG2"; "/dev/sda3"];
1014        ["vgs"]], ["VG1"; "VG2"])],
1015    "create an LVM volume group",
1016    "\
1017 This creates an LVM volume group called C<volgroup>
1018 from the non-empty list of physical volumes C<physvols>.");
1019
1020   ("lvcreate", (RErr, [String "logvol"; String "volgroup"; Int "mbytes"]), 41, [],
1021    [InitEmpty, Always, TestOutputList (
1022       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
1023        ["pvcreate"; "/dev/sda1"];
1024        ["pvcreate"; "/dev/sda2"];
1025        ["pvcreate"; "/dev/sda3"];
1026        ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
1027        ["vgcreate"; "VG2"; "/dev/sda3"];
1028        ["lvcreate"; "LV1"; "VG1"; "50"];
1029        ["lvcreate"; "LV2"; "VG1"; "50"];
1030        ["lvcreate"; "LV3"; "VG2"; "50"];
1031        ["lvcreate"; "LV4"; "VG2"; "50"];
1032        ["lvcreate"; "LV5"; "VG2"; "50"];
1033        ["lvs"]],
1034       ["/dev/VG1/LV1"; "/dev/VG1/LV2";
1035        "/dev/VG2/LV3"; "/dev/VG2/LV4"; "/dev/VG2/LV5"])],
1036    "create an LVM volume group",
1037    "\
1038 This creates an LVM volume group called C<logvol>
1039 on the volume group C<volgroup>, with C<size> megabytes.");
1040
1041   ("mkfs", (RErr, [String "fstype"; String "device"]), 42, [],
1042    [InitEmpty, Always, TestOutput (
1043       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
1044        ["mkfs"; "ext2"; "/dev/sda1"];
1045        ["mount"; "/dev/sda1"; "/"];
1046        ["write_file"; "/new"; "new file contents"; "0"];
1047        ["cat"; "/new"]], "new file contents")],
1048    "make a filesystem",
1049    "\
1050 This creates a filesystem on C<device> (usually a partition
1051 or LVM logical volume).  The filesystem type is C<fstype>, for
1052 example C<ext3>.");
1053
1054   ("sfdisk", (RErr, [String "device";
1055                      Int "cyls"; Int "heads"; Int "sectors";
1056                      StringList "lines"]), 43, [DangerWillRobinson],
1057    [],
1058    "create partitions on a block device",
1059    "\
1060 This is a direct interface to the L<sfdisk(8)> program for creating
1061 partitions on block devices.
1062
1063 C<device> should be a block device, for example C</dev/sda>.
1064
1065 C<cyls>, C<heads> and C<sectors> are the number of cylinders, heads
1066 and sectors on the device, which are passed directly to sfdisk as
1067 the I<-C>, I<-H> and I<-S> parameters.  If you pass C<0> for any
1068 of these, then the corresponding parameter is omitted.  Usually for
1069 'large' disks, you can just pass C<0> for these, but for small
1070 (floppy-sized) disks, sfdisk (or rather, the kernel) cannot work
1071 out the right geometry and you will need to tell it.
1072
1073 C<lines> is a list of lines that we feed to C<sfdisk>.  For more
1074 information refer to the L<sfdisk(8)> manpage.
1075
1076 To create a single partition occupying the whole disk, you would
1077 pass C<lines> as a single element list, when the single element being
1078 the string C<,> (comma).
1079
1080 See also: C<guestfs_sfdisk_l>, C<guestfs_sfdisk_N>");
1081
1082   ("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
1083    [InitBasicFS, Always, TestOutput (
1084       [["write_file"; "/new"; "new file contents"; "0"];
1085        ["cat"; "/new"]], "new file contents");
1086     InitBasicFS, Always, TestOutput (
1087       [["write_file"; "/new"; "\nnew file contents\n"; "0"];
1088        ["cat"; "/new"]], "\nnew file contents\n");
1089     InitBasicFS, Always, TestOutput (
1090       [["write_file"; "/new"; "\n\n"; "0"];
1091        ["cat"; "/new"]], "\n\n");
1092     InitBasicFS, Always, TestOutput (
1093       [["write_file"; "/new"; ""; "0"];
1094        ["cat"; "/new"]], "");
1095     InitBasicFS, Always, TestOutput (
1096       [["write_file"; "/new"; "\n\n\n"; "0"];
1097        ["cat"; "/new"]], "\n\n\n");
1098     InitBasicFS, Always, TestOutput (
1099       [["write_file"; "/new"; "\n"; "0"];
1100        ["cat"; "/new"]], "\n")],
1101    "create a file",
1102    "\
1103 This call creates a file called C<path>.  The contents of the
1104 file is the string C<content> (which can contain any 8 bit data),
1105 with length C<size>.
1106
1107 As a special case, if C<size> is C<0>
1108 then the length is calculated using C<strlen> (so in this case
1109 the content cannot contain embedded ASCII NULs).
1110
1111 I<NB.> Owing to a bug, writing content containing ASCII NUL
1112 characters does I<not> work, even if the length is specified.
1113 We hope to resolve this bug in a future version.  In the meantime
1114 use C<guestfs_upload>.");
1115
1116   ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
1117    [InitEmpty, Always, TestOutputList (
1118       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
1119        ["mkfs"; "ext2"; "/dev/sda1"];
1120        ["mount"; "/dev/sda1"; "/"];
1121        ["mounts"]], ["/dev/sda1"]);
1122     InitEmpty, Always, TestOutputList (
1123       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
1124        ["mkfs"; "ext2"; "/dev/sda1"];
1125        ["mount"; "/dev/sda1"; "/"];
1126        ["umount"; "/"];
1127        ["mounts"]], [])],
1128    "unmount a filesystem",
1129    "\
1130 This unmounts the given filesystem.  The filesystem may be
1131 specified either by its mountpoint (path) or the device which
1132 contains the filesystem.");
1133
1134   ("mounts", (RStringList "devices", []), 46, [],
1135    [InitBasicFS, Always, TestOutputList (
1136       [["mounts"]], ["/dev/sda1"])],
1137    "show mounted filesystems",
1138    "\
1139 This returns the list of currently mounted filesystems.  It returns
1140 the list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).
1141
1142 Some internal mounts are not shown.");
1143
1144   ("umount_all", (RErr, []), 47, [FishAlias "unmount-all"],
1145    [InitBasicFS, Always, TestOutputList (
1146       [["umount_all"];
1147        ["mounts"]], []);
1148     (* check that umount_all can unmount nested mounts correctly: *)
1149     InitEmpty, Always, TestOutputList (
1150       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
1151        ["mkfs"; "ext2"; "/dev/sda1"];
1152        ["mkfs"; "ext2"; "/dev/sda2"];
1153        ["mkfs"; "ext2"; "/dev/sda3"];
1154        ["mount"; "/dev/sda1"; "/"];
1155        ["mkdir"; "/mp1"];
1156        ["mount"; "/dev/sda2"; "/mp1"];
1157        ["mkdir"; "/mp1/mp2"];
1158        ["mount"; "/dev/sda3"; "/mp1/mp2"];
1159        ["mkdir"; "/mp1/mp2/mp3"];
1160        ["umount_all"];
1161        ["mounts"]], [])],
1162    "unmount all filesystems",
1163    "\
1164 This unmounts all mounted filesystems.
1165
1166 Some internal mounts are not unmounted by this call.");
1167
1168   ("lvm_remove_all", (RErr, []), 48, [DangerWillRobinson],
1169    [],
1170    "remove all LVM LVs, VGs and PVs",
1171    "\
1172 This command removes all LVM logical volumes, volume groups
1173 and physical volumes.");
1174
1175   ("file", (RString "description", [String "path"]), 49, [],
1176    [InitBasicFS, Always, TestOutput (
1177       [["touch"; "/new"];
1178        ["file"; "/new"]], "empty");
1179     InitBasicFS, Always, TestOutput (
1180       [["write_file"; "/new"; "some content\n"; "0"];
1181        ["file"; "/new"]], "ASCII text");
1182     InitBasicFS, Always, TestLastFail (
1183       [["file"; "/nofile"]])],
1184    "determine file type",
1185    "\
1186 This call uses the standard L<file(1)> command to determine
1187 the type or contents of the file.  This also works on devices,
1188 for example to find out whether a partition contains a filesystem.
1189
1190 The exact command which runs is C<file -bsL path>.  Note in
1191 particular that the filename is not prepended to the output
1192 (the C<-b> option).");
1193
1194   ("command", (RString "output", [StringList "arguments"]), 50, [ProtocolLimitWarning],
1195    [InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1196       [["upload"; "test-command"; "/test-command"];
1197        ["chmod"; "493"; "/test-command"];
1198        ["command"; "/test-command 1"]], "Result1");
1199     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1200       [["upload"; "test-command"; "/test-command"];
1201        ["chmod"; "493"; "/test-command"];
1202        ["command"; "/test-command 2"]], "Result2\n");
1203     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1204       [["upload"; "test-command"; "/test-command"];
1205        ["chmod"; "493"; "/test-command"];
1206        ["command"; "/test-command 3"]], "\nResult3");
1207     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1208       [["upload"; "test-command"; "/test-command"];
1209        ["chmod"; "493"; "/test-command"];
1210        ["command"; "/test-command 4"]], "\nResult4\n");
1211     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1212       [["upload"; "test-command"; "/test-command"];
1213        ["chmod"; "493"; "/test-command"];
1214        ["command"; "/test-command 5"]], "\nResult5\n\n");
1215     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1216       [["upload"; "test-command"; "/test-command"];
1217        ["chmod"; "493"; "/test-command"];
1218        ["command"; "/test-command 6"]], "\n\nResult6\n\n");
1219     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1220       [["upload"; "test-command"; "/test-command"];
1221        ["chmod"; "493"; "/test-command"];
1222        ["command"; "/test-command 7"]], "");
1223     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1224       [["upload"; "test-command"; "/test-command"];
1225        ["chmod"; "493"; "/test-command"];
1226        ["command"; "/test-command 8"]], "\n");
1227     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1228       [["upload"; "test-command"; "/test-command"];
1229        ["chmod"; "493"; "/test-command"];
1230        ["command"; "/test-command 9"]], "\n\n");
1231     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1232       [["upload"; "test-command"; "/test-command"];
1233        ["chmod"; "493"; "/test-command"];
1234        ["command"; "/test-command 10"]], "Result10-1\nResult10-2\n");
1235     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutput (
1236       [["upload"; "test-command"; "/test-command"];
1237        ["chmod"; "493"; "/test-command"];
1238        ["command"; "/test-command 11"]], "Result11-1\nResult11-2");
1239     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestLastFail (
1240       [["upload"; "test-command"; "/test-command"];
1241        ["chmod"; "493"; "/test-command"];
1242        ["command"; "/test-command"]])],
1243    "run a command from the guest filesystem",
1244    "\
1245 This call runs a command from the guest filesystem.  The
1246 filesystem must be mounted, and must contain a compatible
1247 operating system (ie. something Linux, with the same
1248 or compatible processor architecture).
1249
1250 The single parameter is an argv-style list of arguments.
1251 The first element is the name of the program to run.
1252 Subsequent elements are parameters.  The list must be
1253 non-empty (ie. must contain a program name).
1254
1255 The return value is anything printed to I<stdout> by
1256 the command.
1257
1258 If the command returns a non-zero exit status, then
1259 this function returns an error message.  The error message
1260 string is the content of I<stderr> from the command.
1261
1262 The C<$PATH> environment variable will contain at least
1263 C</usr/bin> and C</bin>.  If you require a program from
1264 another location, you should provide the full path in the
1265 first parameter.
1266
1267 Shared libraries and data files required by the program
1268 must be available on filesystems which are mounted in the
1269 correct places.  It is the caller's responsibility to ensure
1270 all filesystems that are needed are mounted at the right
1271 locations.");
1272
1273   ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [ProtocolLimitWarning],
1274    [InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1275       [["upload"; "test-command"; "/test-command"];
1276        ["chmod"; "493"; "/test-command"];
1277        ["command_lines"; "/test-command 1"]], ["Result1"]);
1278     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1279       [["upload"; "test-command"; "/test-command"];
1280        ["chmod"; "493"; "/test-command"];
1281        ["command_lines"; "/test-command 2"]], ["Result2"]);
1282     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1283       [["upload"; "test-command"; "/test-command"];
1284        ["chmod"; "493"; "/test-command"];
1285        ["command_lines"; "/test-command 3"]], ["";"Result3"]);
1286     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1287       [["upload"; "test-command"; "/test-command"];
1288        ["chmod"; "493"; "/test-command"];
1289        ["command_lines"; "/test-command 4"]], ["";"Result4"]);
1290     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1291       [["upload"; "test-command"; "/test-command"];
1292        ["chmod"; "493"; "/test-command"];
1293        ["command_lines"; "/test-command 5"]], ["";"Result5";""]);
1294     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1295       [["upload"; "test-command"; "/test-command"];
1296        ["chmod"; "493"; "/test-command"];
1297        ["command_lines"; "/test-command 6"]], ["";"";"Result6";""]);
1298     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1299       [["upload"; "test-command"; "/test-command"];
1300        ["chmod"; "493"; "/test-command"];
1301        ["command_lines"; "/test-command 7"]], []);
1302     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1303       [["upload"; "test-command"; "/test-command"];
1304        ["chmod"; "493"; "/test-command"];
1305        ["command_lines"; "/test-command 8"]], [""]);
1306     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1307       [["upload"; "test-command"; "/test-command"];
1308        ["chmod"; "493"; "/test-command"];
1309        ["command_lines"; "/test-command 9"]], ["";""]);
1310     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1311       [["upload"; "test-command"; "/test-command"];
1312        ["chmod"; "493"; "/test-command"];
1313        ["command_lines"; "/test-command 10"]], ["Result10-1";"Result10-2"]);
1314     InitBasicFS, Unless (env_is_true "SKIP_TEST_COMMAND"), TestOutputList (
1315       [["upload"; "test-command"; "/test-command"];
1316        ["chmod"; "493"; "/test-command"];
1317        ["command_lines"; "/test-command 11"]], ["Result11-1";"Result11-2"])],
1318    "run a command, returning lines",
1319    "\
1320 This is the same as C<guestfs_command>, but splits the
1321 result into a list of lines.");
1322
1323   ("stat", (RStat "statbuf", [String "path"]), 52, [],
1324    [InitBasicFS, Always, TestOutputStruct (
1325       [["touch"; "/new"];
1326        ["stat"; "/new"]], [CompareWithInt ("size", 0)])],
1327    "get file information",
1328    "\
1329 Returns file information for the given C<path>.
1330
1331 This is the same as the C<stat(2)> system call.");
1332
1333   ("lstat", (RStat "statbuf", [String "path"]), 53, [],
1334    [InitBasicFS, Always, TestOutputStruct (
1335       [["touch"; "/new"];
1336        ["lstat"; "/new"]], [CompareWithInt ("size", 0)])],
1337    "get file information for a symbolic link",
1338    "\
1339 Returns file information for the given C<path>.
1340
1341 This is the same as C<guestfs_stat> except that if C<path>
1342 is a symbolic link, then the link is stat-ed, not the file it
1343 refers to.
1344
1345 This is the same as the C<lstat(2)> system call.");
1346
1347   ("statvfs", (RStatVFS "statbuf", [String "path"]), 54, [],
1348    [InitBasicFS, Always, TestOutputStruct (
1349       [["statvfs"; "/"]], [CompareWithInt ("bfree", 487702);
1350                            CompareWithInt ("blocks", 490020);
1351                            CompareWithInt ("bsize", 1024)])],
1352    "get file system statistics",
1353    "\
1354 Returns file system statistics for any mounted file system.
1355 C<path> should be a file or directory in the mounted file system
1356 (typically it is the mount point itself, but it doesn't need to be).
1357
1358 This is the same as the C<statvfs(2)> system call.");
1359
1360   ("tune2fs_l", (RHashtable "superblock", [String "device"]), 55, [],
1361    [], (* XXX test *)
1362    "get ext2/ext3/ext4 superblock details",
1363    "\
1364 This returns the contents of the ext2, ext3 or ext4 filesystem
1365 superblock on C<device>.
1366
1367 It is the same as running C<tune2fs -l device>.  See L<tune2fs(8)>
1368 manpage for more details.  The list of fields returned isn't
1369 clearly defined, and depends on both the version of C<tune2fs>
1370 that libguestfs was built against, and the filesystem itself.");
1371
1372   ("blockdev_setro", (RErr, [String "device"]), 56, [],
1373    [InitEmpty, Always, TestOutputTrue (
1374       [["blockdev_setro"; "/dev/sda"];
1375        ["blockdev_getro"; "/dev/sda"]])],
1376    "set block device to read-only",
1377    "\
1378 Sets the block device named C<device> to read-only.
1379
1380 This uses the L<blockdev(8)> command.");
1381
1382   ("blockdev_setrw", (RErr, [String "device"]), 57, [],
1383    [InitEmpty, Always, TestOutputFalse (
1384       [["blockdev_setrw"; "/dev/sda"];
1385        ["blockdev_getro"; "/dev/sda"]])],
1386    "set block device to read-write",
1387    "\
1388 Sets the block device named C<device> to read-write.
1389
1390 This uses the L<blockdev(8)> command.");
1391
1392   ("blockdev_getro", (RBool "ro", [String "device"]), 58, [],
1393    [InitEmpty, Always, TestOutputTrue (
1394       [["blockdev_setro"; "/dev/sda"];
1395        ["blockdev_getro"; "/dev/sda"]])],
1396    "is block device set to read-only",
1397    "\
1398 Returns a boolean indicating if the block device is read-only
1399 (true if read-only, false if not).
1400
1401 This uses the L<blockdev(8)> command.");
1402
1403   ("blockdev_getss", (RInt "sectorsize", [String "device"]), 59, [],
1404    [InitEmpty, Always, TestOutputInt (
1405       [["blockdev_getss"; "/dev/sda"]], 512)],
1406    "get sectorsize of block device",
1407    "\
1408 This returns the size of sectors on a block device.
1409 Usually 512, but can be larger for modern devices.
1410
1411 (Note, this is not the size in sectors, use C<guestfs_blockdev_getsz>
1412 for that).
1413
1414 This uses the L<blockdev(8)> command.");
1415
1416   ("blockdev_getbsz", (RInt "blocksize", [String "device"]), 60, [],
1417    [InitEmpty, Always, TestOutputInt (
1418       [["blockdev_getbsz"; "/dev/sda"]], 4096)],
1419    "get blocksize of block device",
1420    "\
1421 This returns the block size of a device.
1422
1423 (Note this is different from both I<size in blocks> and
1424 I<filesystem block size>).
1425
1426 This uses the L<blockdev(8)> command.");
1427
1428   ("blockdev_setbsz", (RErr, [String "device"; Int "blocksize"]), 61, [],
1429    [], (* XXX test *)
1430    "set blocksize of block device",
1431    "\
1432 This sets the block size of a device.
1433
1434 (Note this is different from both I<size in blocks> and
1435 I<filesystem block size>).
1436
1437 This uses the L<blockdev(8)> command.");
1438
1439   ("blockdev_getsz", (RInt64 "sizeinsectors", [String "device"]), 62, [],
1440    [InitEmpty, Always, TestOutputInt (
1441       [["blockdev_getsz"; "/dev/sda"]], 1024000)],
1442    "get total size of device in 512-byte sectors",
1443    "\
1444 This returns the size of the device in units of 512-byte sectors
1445 (even if the sectorsize isn't 512 bytes ... weird).
1446
1447 See also C<guestfs_blockdev_getss> for the real sector size of
1448 the device, and C<guestfs_blockdev_getsize64> for the more
1449 useful I<size in bytes>.
1450
1451 This uses the L<blockdev(8)> command.");
1452
1453   ("blockdev_getsize64", (RInt64 "sizeinbytes", [String "device"]), 63, [],
1454    [InitEmpty, Always, TestOutputInt (
1455       [["blockdev_getsize64"; "/dev/sda"]], 524288000)],
1456    "get total size of device in bytes",
1457    "\
1458 This returns the size of the device in bytes.
1459
1460 See also C<guestfs_blockdev_getsz>.
1461
1462 This uses the L<blockdev(8)> command.");
1463
1464   ("blockdev_flushbufs", (RErr, [String "device"]), 64, [],
1465    [InitEmpty, Always, TestRun
1466       [["blockdev_flushbufs"; "/dev/sda"]]],
1467    "flush device buffers",
1468    "\
1469 This tells the kernel to flush internal buffers associated
1470 with C<device>.
1471
1472 This uses the L<blockdev(8)> command.");
1473
1474   ("blockdev_rereadpt", (RErr, [String "device"]), 65, [],
1475    [InitEmpty, Always, TestRun
1476       [["blockdev_rereadpt"; "/dev/sda"]]],
1477    "reread partition table",
1478    "\
1479 Reread the partition table on C<device>.
1480
1481 This uses the L<blockdev(8)> command.");
1482
1483   ("upload", (RErr, [FileIn "filename"; String "remotefilename"]), 66, [],
1484    [InitBasicFS, Always, TestOutput (
1485       (* Pick a file from cwd which isn't likely to change. *)
1486     [["upload"; "COPYING.LIB"; "/COPYING.LIB"];
1487      ["checksum"; "md5"; "/COPYING.LIB"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
1488    "upload a file from the local machine",
1489    "\
1490 Upload local file C<filename> to C<remotefilename> on the
1491 filesystem.
1492
1493 C<filename> can also be a named pipe.
1494
1495 See also C<guestfs_download>.");
1496
1497   ("download", (RErr, [String "remotefilename"; FileOut "filename"]), 67, [],
1498    [InitBasicFS, Always, TestOutput (
1499       (* Pick a file from cwd which isn't likely to change. *)
1500     [["upload"; "COPYING.LIB"; "/COPYING.LIB"];
1501      ["download"; "/COPYING.LIB"; "testdownload.tmp"];
1502      ["upload"; "testdownload.tmp"; "/upload"];
1503      ["checksum"; "md5"; "/upload"]], "e3eda01d9815f8d24aae2dbd89b68b06")],
1504    "download a file to the local machine",
1505    "\
1506 Download file C<remotefilename> and save it as C<filename>
1507 on the local machine.
1508
1509 C<filename> can also be a named pipe.
1510
1511 See also C<guestfs_upload>, C<guestfs_cat>.");
1512
1513   ("checksum", (RString "checksum", [String "csumtype"; String "path"]), 68, [],
1514    [InitBasicFS, Always, TestOutput (
1515       [["write_file"; "/new"; "test\n"; "0"];
1516        ["checksum"; "crc"; "/new"]], "935282863");
1517     InitBasicFS, Always, TestLastFail (
1518       [["checksum"; "crc"; "/new"]]);
1519     InitBasicFS, Always, TestOutput (
1520       [["write_file"; "/new"; "test\n"; "0"];
1521        ["checksum"; "md5"; "/new"]], "d8e8fca2dc0f896fd7cb4cb0031ba249");
1522     InitBasicFS, Always, TestOutput (
1523       [["write_file"; "/new"; "test\n"; "0"];
1524        ["checksum"; "sha1"; "/new"]], "4e1243bd22c66e76c2ba9eddc1f91394e57f9f83");
1525     InitBasicFS, Always, TestOutput (
1526       [["write_file"; "/new"; "test\n"; "0"];
1527        ["checksum"; "sha224"; "/new"]], "52f1bf093f4b7588726035c176c0cdb4376cfea53819f1395ac9e6ec");
1528     InitBasicFS, Always, TestOutput (
1529       [["write_file"; "/new"; "test\n"; "0"];
1530        ["checksum"; "sha256"; "/new"]], "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2");
1531     InitBasicFS, Always, TestOutput (
1532       [["write_file"; "/new"; "test\n"; "0"];
1533        ["checksum"; "sha384"; "/new"]], "109bb6b5b6d5547c1ce03c7a8bd7d8f80c1cb0957f50c4f7fda04692079917e4f9cad52b878f3d8234e1a170b154b72d");
1534     InitBasicFS, Always, TestOutput (
1535       [["write_file"; "/new"; "test\n"; "0"];
1536        ["checksum"; "sha512"; "/new"]], "0e3e75234abc68f4378a86b3f4b32a198ba301845b0cd6e50106e874345700cc6663a86c1ea125dc5e92be17c98f9a0f85ca9d5f595db2012f7cc3571945c123")],
1537    "compute MD5, SHAx or CRC checksum of file",
1538    "\
1539 This call computes the MD5, SHAx or CRC checksum of the
1540 file named C<path>.
1541
1542 The type of checksum to compute is given by the C<csumtype>
1543 parameter which must have one of the following values:
1544
1545 =over 4
1546
1547 =item C<crc>
1548
1549 Compute the cyclic redundancy check (CRC) specified by POSIX
1550 for the C<cksum> command.
1551
1552 =item C<md5>
1553
1554 Compute the MD5 hash (using the C<md5sum> program).
1555
1556 =item C<sha1>
1557
1558 Compute the SHA1 hash (using the C<sha1sum> program).
1559
1560 =item C<sha224>
1561
1562 Compute the SHA224 hash (using the C<sha224sum> program).
1563
1564 =item C<sha256>
1565
1566 Compute the SHA256 hash (using the C<sha256sum> program).
1567
1568 =item C<sha384>
1569
1570 Compute the SHA384 hash (using the C<sha384sum> program).
1571
1572 =item C<sha512>
1573
1574 Compute the SHA512 hash (using the C<sha512sum> program).
1575
1576 =back
1577
1578 The checksum is returned as a printable string.");
1579
1580   ("tar_in", (RErr, [FileIn "tarfile"; String "directory"]), 69, [],
1581    [InitBasicFS, Always, TestOutput (
1582       [["tar_in"; "images/helloworld.tar"; "/"];
1583        ["cat"; "/hello"]], "hello\n")],
1584    "unpack tarfile to directory",
1585    "\
1586 This command uploads and unpacks local file C<tarfile> (an
1587 I<uncompressed> tar file) into C<directory>.
1588
1589 To upload a compressed tarball, use C<guestfs_tgz_in>.");
1590
1591   ("tar_out", (RErr, [String "directory"; FileOut "tarfile"]), 70, [],
1592    [],
1593    "pack directory into tarfile",
1594    "\
1595 This command packs the contents of C<directory> and downloads
1596 it to local file C<tarfile>.
1597
1598 To download a compressed tarball, use C<guestfs_tgz_out>.");
1599
1600   ("tgz_in", (RErr, [FileIn "tarball"; String "directory"]), 71, [],
1601    [InitBasicFS, Always, TestOutput (
1602       [["tgz_in"; "images/helloworld.tar.gz"; "/"];
1603        ["cat"; "/hello"]], "hello\n")],
1604    "unpack compressed tarball to directory",
1605    "\
1606 This command uploads and unpacks local file C<tarball> (a
1607 I<gzip compressed> tar file) into C<directory>.
1608
1609 To upload an uncompressed tarball, use C<guestfs_tar_in>.");
1610
1611   ("tgz_out", (RErr, [String "directory"; FileOut "tarball"]), 72, [],
1612    [],
1613    "pack directory into compressed tarball",
1614    "\
1615 This command packs the contents of C<directory> and downloads
1616 it to local file C<tarball>.
1617
1618 To download an uncompressed tarball, use C<guestfs_tar_out>.");
1619
1620   ("mount_ro", (RErr, [String "device"; String "mountpoint"]), 73, [],
1621    [InitBasicFS, Always, TestLastFail (
1622       [["umount"; "/"];
1623        ["mount_ro"; "/dev/sda1"; "/"];
1624        ["touch"; "/new"]]);
1625     InitBasicFS, Always, TestOutput (
1626       [["write_file"; "/new"; "data"; "0"];
1627        ["umount"; "/"];
1628        ["mount_ro"; "/dev/sda1"; "/"];
1629        ["cat"; "/new"]], "data")],
1630    "mount a guest disk, read-only",
1631    "\
1632 This is the same as the C<guestfs_mount> command, but it
1633 mounts the filesystem with the read-only (I<-o ro>) flag.");
1634
1635   ("mount_options", (RErr, [String "options"; String "device"; String "mountpoint"]), 74, [],
1636    [],
1637    "mount a guest disk with mount options",
1638    "\
1639 This is the same as the C<guestfs_mount> command, but it
1640 allows you to set the mount options as for the
1641 L<mount(8)> I<-o> flag.");
1642
1643   ("mount_vfs", (RErr, [String "options"; String "vfstype"; String "device"; String "mountpoint"]), 75, [],
1644    [],
1645    "mount a guest disk with mount options and vfstype",
1646    "\
1647 This is the same as the C<guestfs_mount> command, but it
1648 allows you to set both the mount options and the vfstype
1649 as for the L<mount(8)> I<-o> and I<-t> flags.");
1650
1651   ("debug", (RString "result", [String "subcmd"; StringList "extraargs"]), 76, [],
1652    [],
1653    "debugging and internals",
1654    "\
1655 The C<guestfs_debug> command exposes some internals of
1656 C<guestfsd> (the guestfs daemon) that runs inside the
1657 qemu subprocess.
1658
1659 There is no comprehensive help for this command.  You have
1660 to look at the file C<daemon/debug.c> in the libguestfs source
1661 to find out what you can do.");
1662
1663   ("lvremove", (RErr, [String "device"]), 77, [],
1664    [InitEmpty, Always, TestOutputList (
1665       [["pvcreate"; "/dev/sda"];
1666        ["vgcreate"; "VG"; "/dev/sda"];
1667        ["lvcreate"; "LV1"; "VG"; "50"];
1668        ["lvcreate"; "LV2"; "VG"; "50"];
1669        ["lvremove"; "/dev/VG/LV1"];
1670        ["lvs"]], ["/dev/VG/LV2"]);
1671     InitEmpty, Always, TestOutputList (
1672       [["pvcreate"; "/dev/sda"];
1673        ["vgcreate"; "VG"; "/dev/sda"];
1674        ["lvcreate"; "LV1"; "VG"; "50"];
1675        ["lvcreate"; "LV2"; "VG"; "50"];
1676        ["lvremove"; "/dev/VG"];
1677        ["lvs"]], []);
1678     InitEmpty, Always, TestOutputList (
1679       [["pvcreate"; "/dev/sda"];
1680        ["vgcreate"; "VG"; "/dev/sda"];
1681        ["lvcreate"; "LV1"; "VG"; "50"];
1682        ["lvcreate"; "LV2"; "VG"; "50"];
1683        ["lvremove"; "/dev/VG"];
1684        ["vgs"]], ["VG"])],
1685    "remove an LVM logical volume",
1686    "\
1687 Remove an LVM logical volume C<device>, where C<device> is
1688 the path to the LV, such as C</dev/VG/LV>.
1689
1690 You can also remove all LVs in a volume group by specifying
1691 the VG name, C</dev/VG>.");
1692
1693   ("vgremove", (RErr, [String "vgname"]), 78, [],
1694    [InitEmpty, Always, TestOutputList (
1695       [["pvcreate"; "/dev/sda"];
1696        ["vgcreate"; "VG"; "/dev/sda"];
1697        ["lvcreate"; "LV1"; "VG"; "50"];
1698        ["lvcreate"; "LV2"; "VG"; "50"];
1699        ["vgremove"; "VG"];
1700        ["lvs"]], []);
1701     InitEmpty, Always, TestOutputList (
1702       [["pvcreate"; "/dev/sda"];
1703        ["vgcreate"; "VG"; "/dev/sda"];
1704        ["lvcreate"; "LV1"; "VG"; "50"];
1705        ["lvcreate"; "LV2"; "VG"; "50"];
1706        ["vgremove"; "VG"];
1707        ["vgs"]], [])],
1708    "remove an LVM volume group",
1709    "\
1710 Remove an LVM volume group C<vgname>, (for example C<VG>).
1711
1712 This also forcibly removes all logical volumes in the volume
1713 group (if any).");
1714
1715   ("pvremove", (RErr, [String "device"]), 79, [],
1716    [InitEmpty, Always, TestOutputList (
1717       [["pvcreate"; "/dev/sda"];
1718        ["vgcreate"; "VG"; "/dev/sda"];
1719        ["lvcreate"; "LV1"; "VG"; "50"];
1720        ["lvcreate"; "LV2"; "VG"; "50"];
1721        ["vgremove"; "VG"];
1722        ["pvremove"; "/dev/sda"];
1723        ["lvs"]], []);
1724     InitEmpty, Always, TestOutputList (
1725       [["pvcreate"; "/dev/sda"];
1726        ["vgcreate"; "VG"; "/dev/sda"];
1727        ["lvcreate"; "LV1"; "VG"; "50"];
1728        ["lvcreate"; "LV2"; "VG"; "50"];
1729        ["vgremove"; "VG"];
1730        ["pvremove"; "/dev/sda"];
1731        ["vgs"]], []);
1732     InitEmpty, Always, TestOutputList (
1733       [["pvcreate"; "/dev/sda"];
1734        ["vgcreate"; "VG"; "/dev/sda"];
1735        ["lvcreate"; "LV1"; "VG"; "50"];
1736        ["lvcreate"; "LV2"; "VG"; "50"];
1737        ["vgremove"; "VG"];
1738        ["pvremove"; "/dev/sda"];
1739        ["pvs"]], [])],
1740    "remove an LVM physical volume",
1741    "\
1742 This wipes a physical volume C<device> so that LVM will no longer
1743 recognise it.
1744
1745 The implementation uses the C<pvremove> command which refuses to
1746 wipe physical volumes that contain any volume groups, so you have
1747 to remove those first.");
1748
1749   ("set_e2label", (RErr, [String "device"; String "label"]), 80, [],
1750    [InitBasicFS, Always, TestOutput (
1751       [["set_e2label"; "/dev/sda1"; "testlabel"];
1752        ["get_e2label"; "/dev/sda1"]], "testlabel")],
1753    "set the ext2/3/4 filesystem label",
1754    "\
1755 This sets the ext2/3/4 filesystem label of the filesystem on
1756 C<device> to C<label>.  Filesystem labels are limited to
1757 16 characters.
1758
1759 You can use either C<guestfs_tune2fs_l> or C<guestfs_get_e2label>
1760 to return the existing label on a filesystem.");
1761
1762   ("get_e2label", (RString "label", [String "device"]), 81, [],
1763    [],
1764    "get the ext2/3/4 filesystem label",
1765    "\
1766 This returns the ext2/3/4 filesystem label of the filesystem on
1767 C<device>.");
1768
1769   ("set_e2uuid", (RErr, [String "device"; String "uuid"]), 82, [],
1770    [InitBasicFS, Always, TestOutput (
1771       [["set_e2uuid"; "/dev/sda1"; "a3a61220-882b-4f61-89f4-cf24dcc7297d"];
1772        ["get_e2uuid"; "/dev/sda1"]], "a3a61220-882b-4f61-89f4-cf24dcc7297d");
1773     InitBasicFS, Always, TestOutput (
1774       [["set_e2uuid"; "/dev/sda1"; "clear"];
1775        ["get_e2uuid"; "/dev/sda1"]], "");
1776     (* We can't predict what UUIDs will be, so just check the commands run. *)
1777     InitBasicFS, Always, TestRun (
1778       [["set_e2uuid"; "/dev/sda1"; "random"]]);
1779     InitBasicFS, Always, TestRun (
1780       [["set_e2uuid"; "/dev/sda1"; "time"]])],
1781    "set the ext2/3/4 filesystem UUID",
1782    "\
1783 This sets the ext2/3/4 filesystem UUID of the filesystem on
1784 C<device> to C<uuid>.  The format of the UUID and alternatives
1785 such as C<clear>, C<random> and C<time> are described in the
1786 L<tune2fs(8)> manpage.
1787
1788 You can use either C<guestfs_tune2fs_l> or C<guestfs_get_e2uuid>
1789 to return the existing UUID of a filesystem.");
1790
1791   ("get_e2uuid", (RString "uuid", [String "device"]), 83, [],
1792    [],
1793    "get the ext2/3/4 filesystem UUID",
1794    "\
1795 This returns the ext2/3/4 filesystem UUID of the filesystem on
1796 C<device>.");
1797
1798   ("fsck", (RInt "status", [String "fstype"; String "device"]), 84, [],
1799    [InitBasicFS, Always, TestOutputInt (
1800       [["umount"; "/dev/sda1"];
1801        ["fsck"; "ext2"; "/dev/sda1"]], 0);
1802     InitBasicFS, Always, TestOutputInt (
1803       [["umount"; "/dev/sda1"];
1804        ["zero"; "/dev/sda1"];
1805        ["fsck"; "ext2"; "/dev/sda1"]], 8)],
1806    "run the filesystem checker",
1807    "\
1808 This runs the filesystem checker (fsck) on C<device> which
1809 should have filesystem type C<fstype>.
1810
1811 The returned integer is the status.  See L<fsck(8)> for the
1812 list of status codes from C<fsck>.
1813
1814 Notes:
1815
1816 =over 4
1817
1818 =item *
1819
1820 Multiple status codes can be summed together.
1821
1822 =item *
1823
1824 A non-zero return code can mean \"success\", for example if
1825 errors have been corrected on the filesystem.
1826
1827 =item *
1828
1829 Checking or repairing NTFS volumes is not supported
1830 (by linux-ntfs).
1831
1832 =back
1833
1834 This command is entirely equivalent to running C<fsck -a -t fstype device>.");
1835
1836   ("zero", (RErr, [String "device"]), 85, [],
1837    [InitBasicFS, Always, TestOutput (
1838       [["umount"; "/dev/sda1"];
1839        ["zero"; "/dev/sda1"];
1840        ["file"; "/dev/sda1"]], "data")],
1841    "write zeroes to the device",
1842    "\
1843 This command writes zeroes over the first few blocks of C<device>.
1844
1845 How many blocks are zeroed isn't specified (but it's I<not> enough
1846 to securely wipe the device).  It should be sufficient to remove
1847 any partition tables, filesystem superblocks and so on.");
1848
1849   ("grub_install", (RErr, [String "root"; String "device"]), 86, [],
1850    [InitBasicFS, Always, TestOutputTrue (
1851       [["grub_install"; "/"; "/dev/sda1"];
1852        ["is_dir"; "/boot"]])],
1853    "install GRUB",
1854    "\
1855 This command installs GRUB (the Grand Unified Bootloader) on
1856 C<device>, with the root directory being C<root>.");
1857
1858   ("cp", (RErr, [String "src"; String "dest"]), 87, [],
1859    [InitBasicFS, Always, TestOutput (
1860       [["write_file"; "/old"; "file content"; "0"];
1861        ["cp"; "/old"; "/new"];
1862        ["cat"; "/new"]], "file content");
1863     InitBasicFS, Always, TestOutputTrue (
1864       [["write_file"; "/old"; "file content"; "0"];
1865        ["cp"; "/old"; "/new"];
1866        ["is_file"; "/old"]]);
1867     InitBasicFS, Always, TestOutput (
1868       [["write_file"; "/old"; "file content"; "0"];
1869        ["mkdir"; "/dir"];
1870        ["cp"; "/old"; "/dir/new"];
1871        ["cat"; "/dir/new"]], "file content")],
1872    "copy a file",
1873    "\
1874 This copies a file from C<src> to C<dest> where C<dest> is
1875 either a destination filename or destination directory.");
1876
1877   ("cp_a", (RErr, [String "src"; String "dest"]), 88, [],
1878    [InitBasicFS, Always, TestOutput (
1879       [["mkdir"; "/olddir"];
1880        ["mkdir"; "/newdir"];
1881        ["write_file"; "/olddir/file"; "file content"; "0"];
1882        ["cp_a"; "/olddir"; "/newdir"];
1883        ["cat"; "/newdir/olddir/file"]], "file content")],
1884    "copy a file or directory recursively",
1885    "\
1886 This copies a file or directory from C<src> to C<dest>
1887 recursively using the C<cp -a> command.");
1888
1889   ("mv", (RErr, [String "src"; String "dest"]), 89, [],
1890    [InitBasicFS, Always, TestOutput (
1891       [["write_file"; "/old"; "file content"; "0"];
1892        ["mv"; "/old"; "/new"];
1893        ["cat"; "/new"]], "file content");
1894     InitBasicFS, Always, TestOutputFalse (
1895       [["write_file"; "/old"; "file content"; "0"];
1896        ["mv"; "/old"; "/new"];
1897        ["is_file"; "/old"]])],
1898    "move a file",
1899    "\
1900 This moves a file from C<src> to C<dest> where C<dest> is
1901 either a destination filename or destination directory.");
1902
1903   ("drop_caches", (RErr, [Int "whattodrop"]), 90, [],
1904    [InitEmpty, Always, TestRun (
1905       [["drop_caches"; "3"]])],
1906    "drop kernel page cache, dentries and inodes",
1907    "\
1908 This instructs the guest kernel to drop its page cache,
1909 and/or dentries and inode caches.  The parameter C<whattodrop>
1910 tells the kernel what precisely to drop, see
1911 L<http://linux-mm.org/Drop_Caches>
1912
1913 Setting C<whattodrop> to 3 should drop everything.
1914
1915 This automatically calls L<sync(2)> before the operation,
1916 so that the maximum guest memory is freed.");
1917
1918   ("dmesg", (RString "kmsgs", []), 91, [],
1919    [InitEmpty, Always, TestRun (
1920       [["dmesg"]])],
1921    "return kernel messages",
1922    "\
1923 This returns the kernel messages (C<dmesg> output) from
1924 the guest kernel.  This is sometimes useful for extended
1925 debugging of problems.
1926
1927 Another way to get the same information is to enable
1928 verbose messages with C<guestfs_set_verbose> or by setting
1929 the environment variable C<LIBGUESTFS_DEBUG=1> before
1930 running the program.");
1931
1932   ("ping_daemon", (RErr, []), 92, [],
1933    [InitEmpty, Always, TestRun (
1934       [["ping_daemon"]])],
1935    "ping the guest daemon",
1936    "\
1937 This is a test probe into the guestfs daemon running inside
1938 the qemu subprocess.  Calling this function checks that the
1939 daemon responds to the ping message, without affecting the daemon
1940 or attached block device(s) in any other way.");
1941
1942   ("equal", (RBool "equality", [String "file1"; String "file2"]), 93, [],
1943    [InitBasicFS, Always, TestOutputTrue (
1944       [["write_file"; "/file1"; "contents of a file"; "0"];
1945        ["cp"; "/file1"; "/file2"];
1946        ["equal"; "/file1"; "/file2"]]);
1947     InitBasicFS, Always, TestOutputFalse (
1948       [["write_file"; "/file1"; "contents of a file"; "0"];
1949        ["write_file"; "/file2"; "contents of another file"; "0"];
1950        ["equal"; "/file1"; "/file2"]]);
1951     InitBasicFS, Always, TestLastFail (
1952       [["equal"; "/file1"; "/file2"]])],
1953    "test if two files have equal contents",
1954    "\
1955 This compares the two files C<file1> and C<file2> and returns
1956 true if their content is exactly equal, or false otherwise.
1957
1958 The external L<cmp(1)> program is used for the comparison.");
1959
1960   ("strings", (RStringList "stringsout", [String "path"]), 94, [ProtocolLimitWarning],
1961    [InitBasicFS, Always, TestOutputList (
1962       [["write_file"; "/new"; "hello\nworld\n"; "0"];
1963        ["strings"; "/new"]], ["hello"; "world"]);
1964     InitBasicFS, Always, TestOutputList (
1965       [["touch"; "/new"];
1966        ["strings"; "/new"]], [])],
1967    "print the printable strings in a file",
1968    "\
1969 This runs the L<strings(1)> command on a file and returns
1970 the list of printable strings found.");
1971
1972   ("strings_e", (RStringList "stringsout", [String "encoding"; String "path"]), 95, [ProtocolLimitWarning],
1973    [InitBasicFS, Always, TestOutputList (
1974       [["write_file"; "/new"; "hello\nworld\n"; "0"];
1975        ["strings_e"; "b"; "/new"]], []);
1976     InitBasicFS, Disabled, TestOutputList (
1977       [["write_file"; "/new"; "\000h\000e\000l\000l\000o\000\n\000w\000o\000r\000l\000d\000\n"; "24"];
1978        ["strings_e"; "b"; "/new"]], ["hello"; "world"])],
1979    "print the printable strings in a file",
1980    "\
1981 This is like the C<guestfs_strings> command, but allows you to
1982 specify the encoding.
1983
1984 See the L<strings(1)> manpage for the full list of encodings.
1985
1986 Commonly useful encodings are C<l> (lower case L) which will
1987 show strings inside Windows/x86 files.
1988
1989 The returned strings are transcoded to UTF-8.");
1990
1991   ("hexdump", (RString "dump", [String "path"]), 96, [ProtocolLimitWarning],
1992    [InitBasicFS, Always, TestOutput (
1993       [["write_file"; "/new"; "hello\nworld\n"; "12"];
1994        ["hexdump"; "/new"]], "00000000  68 65 6c 6c 6f 0a 77 6f  72 6c 64 0a              |hello.world.|\n0000000c\n")],
1995    "dump a file in hexadecimal",
1996    "\
1997 This runs C<hexdump -C> on the given C<path>.  The result is
1998 the human-readable, canonical hex dump of the file.");
1999
2000   ("zerofree", (RErr, [String "device"]), 97, [],
2001    [InitNone, Always, TestOutput (
2002       [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
2003        ["mkfs"; "ext3"; "/dev/sda1"];
2004        ["mount"; "/dev/sda1"; "/"];
2005        ["write_file"; "/new"; "test file"; "0"];
2006        ["umount"; "/dev/sda1"];
2007        ["zerofree"; "/dev/sda1"];
2008        ["mount"; "/dev/sda1"; "/"];
2009        ["cat"; "/new"]], "test file")],
2010    "zero unused inodes and disk blocks on ext2/3 filesystem",
2011    "\
2012 This runs the I<zerofree> program on C<device>.  This program
2013 claims to zero unused inodes and disk blocks on an ext2/3
2014 filesystem, thus making it possible to compress the filesystem
2015 more effectively.
2016
2017 You should B<not> run this program if the filesystem is
2018 mounted.
2019
2020 It is possible that using this program can damage the filesystem
2021 or data on the filesystem.");
2022
2023   ("pvresize", (RErr, [String "device"]), 98, [],
2024    [],
2025    "resize an LVM physical volume",
2026    "\
2027 This resizes (expands or shrinks) an existing LVM physical
2028 volume to match the new size of the underlying device.");
2029
2030   ("sfdisk_N", (RErr, [String "device"; Int "n";
2031                        Int "cyls"; Int "heads"; Int "sectors";
2032                        String "line"]), 99, [DangerWillRobinson],
2033    [],
2034    "modify a single partition on a block device",
2035    "\
2036 This runs L<sfdisk(8)> option to modify just the single
2037 partition C<n> (note: C<n> counts from 1).
2038
2039 For other parameters, see C<guestfs_sfdisk>.  You should usually
2040 pass C<0> for the cyls/heads/sectors parameters.");
2041
2042   ("sfdisk_l", (RString "partitions", [String "device"]), 100, [],
2043    [],
2044    "display the partition table",
2045    "\
2046 This displays the partition table on C<device>, in the
2047 human-readable output of the L<sfdisk(8)> command.  It is
2048 not intended to be parsed.");
2049
2050   ("sfdisk_kernel_geometry", (RString "partitions", [String "device"]), 101, [],
2051    [],
2052    "display the kernel geometry",
2053    "\
2054 This displays the kernel's idea of the geometry of C<device>.
2055
2056 The result is in human-readable format, and not designed to
2057 be parsed.");
2058
2059   ("sfdisk_disk_geometry", (RString "partitions", [String "device"]), 102, [],
2060    [],
2061    "display the disk geometry from the partition table",
2062    "\
2063 This displays the disk geometry of C<device> read from the
2064 partition table.  Especially in the case where the underlying
2065 block device has been resized, this can be different from the
2066 kernel's idea of the geometry (see C<guestfs_sfdisk_kernel_geometry>).
2067
2068 The result is in human-readable format, and not designed to
2069 be parsed.");
2070
2071   ("vg_activate_all", (RErr, [Bool "activate"]), 103, [],
2072    [],
2073    "activate or deactivate all volume groups",
2074    "\
2075 This command activates or (if C<activate> is false) deactivates
2076 all logical volumes in all volume groups.
2077 If activated, then they are made known to the
2078 kernel, ie. they appear as C</dev/mapper> devices.  If deactivated,
2079 then those devices disappear.
2080
2081 This command is the same as running C<vgchange -a y|n>");
2082
2083   ("vg_activate", (RErr, [Bool "activate"; StringList "volgroups"]), 104, [],
2084    [],
2085    "activate or deactivate some volume groups",
2086    "\
2087 This command activates or (if C<activate> is false) deactivates
2088 all logical volumes in the listed volume groups C<volgroups>.
2089 If activated, then they are made known to the
2090 kernel, ie. they appear as C</dev/mapper> devices.  If deactivated,
2091 then those devices disappear.
2092
2093 This command is the same as running C<vgchange -a y|n volgroups...>
2094
2095 Note that if C<volgroups> is an empty list then B<all> volume groups
2096 are activated or deactivated.");
2097
2098   ("lvresize", (RErr, [String "device"; Int "mbytes"]), 105, [],
2099    [InitNone, Always, TestOutput (
2100     [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
2101      ["pvcreate"; "/dev/sda1"];
2102      ["vgcreate"; "VG"; "/dev/sda1"];
2103      ["lvcreate"; "LV"; "VG"; "10"];
2104      ["mkfs"; "ext2"; "/dev/VG/LV"];
2105      ["mount"; "/dev/VG/LV"; "/"];
2106      ["write_file"; "/new"; "test content"; "0"];
2107      ["umount"; "/"];
2108      ["lvresize"; "/dev/VG/LV"; "20"];
2109      ["resize2fs"; "/dev/VG/LV"];
2110      ["mount"; "/dev/VG/LV"; "/"];
2111      ["cat"; "/new"]], "test content")],
2112    "resize an LVM logical volume",
2113    "\
2114 This resizes (expands or shrinks) an existing LVM logical
2115 volume to C<mbytes>.  When reducing, data in the reduced part
2116 is lost.");
2117
2118   ("resize2fs", (RErr, [String "device"]), 106, [],
2119    [], (* lvresize tests this *)
2120    "resize an ext2/ext3 filesystem",
2121    "\
2122 This resizes an ext2 or ext3 filesystem to match the size of
2123 the underlying device.");
2124
2125   ("find", (RStringList "names", [String "directory"]), 107, [],
2126    [InitBasicFS, Always, TestOutputList (
2127       [["find"; "/"]], ["lost+found"]);
2128     InitBasicFS, Always, TestOutputList (
2129       [["touch"; "/a"];
2130        ["mkdir"; "/b"];
2131        ["touch"; "/b/c"];
2132        ["find"; "/"]], ["a"; "b"; "b/c"; "lost+found"]);
2133     InitBasicFS, Always, TestOutputList (
2134       [["mkdir_p"; "/a/b/c"];
2135        ["touch"; "/a/b/c/d"];
2136        ["find"; "/a/b/"]], ["c"; "c/d"])],
2137    "find all files and directories",
2138    "\
2139 This command lists out all files and directories, recursively,
2140 starting at C<directory>.  It is essentially equivalent to
2141 running the shell command C<find directory -print> but some
2142 post-processing happens on the output, described below.
2143
2144 This returns a list of strings I<without any prefix>.  Thus
2145 if the directory structure was:
2146
2147  /tmp/a
2148  /tmp/b
2149  /tmp/c/d
2150
2151 then the returned list from C<guestfs_find> C</tmp> would be
2152 4 elements:
2153
2154  a
2155  b
2156  c
2157  c/d
2158
2159 If C<directory> is not a directory, then this command returns
2160 an error.
2161
2162 The returned list is sorted.");
2163
2164 ]
2165
2166 let all_functions = non_daemon_functions @ daemon_functions
2167
2168 (* In some places we want the functions to be displayed sorted
2169  * alphabetically, so this is useful:
2170  *)
2171 let all_functions_sorted =
2172   List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) ->
2173                compare n1 n2) all_functions
2174
2175 (* Column names and types from LVM PVs/VGs/LVs. *)
2176 let pv_cols = [
2177   "pv_name", `String;
2178   "pv_uuid", `UUID;
2179   "pv_fmt", `String;
2180   "pv_size", `Bytes;
2181   "dev_size", `Bytes;
2182   "pv_free", `Bytes;
2183   "pv_used", `Bytes;
2184   "pv_attr", `String (* XXX *);
2185   "pv_pe_count", `Int;
2186   "pv_pe_alloc_count", `Int;
2187   "pv_tags", `String;
2188   "pe_start", `Bytes;
2189   "pv_mda_count", `Int;
2190   "pv_mda_free", `Bytes;
2191 (* Not in Fedora 10:
2192   "pv_mda_size", `Bytes;
2193 *)
2194 ]
2195 let vg_cols = [
2196   "vg_name", `String;
2197   "vg_uuid", `UUID;
2198   "vg_fmt", `String;
2199   "vg_attr", `String (* XXX *);
2200   "vg_size", `Bytes;
2201   "vg_free", `Bytes;
2202   "vg_sysid", `String;
2203   "vg_extent_size", `Bytes;
2204   "vg_extent_count", `Int;
2205   "vg_free_count", `Int;
2206   "max_lv", `Int;
2207   "max_pv", `Int;
2208   "pv_count", `Int;
2209   "lv_count", `Int;
2210   "snap_count", `Int;
2211   "vg_seqno", `Int;
2212   "vg_tags", `String;
2213   "vg_mda_count", `Int;
2214   "vg_mda_free", `Bytes;
2215 (* Not in Fedora 10:
2216   "vg_mda_size", `Bytes;
2217 *)
2218 ]
2219 let lv_cols = [
2220   "lv_name", `String;
2221   "lv_uuid", `UUID;
2222   "lv_attr", `String (* XXX *);
2223   "lv_major", `Int;
2224   "lv_minor", `Int;
2225   "lv_kernel_major", `Int;
2226   "lv_kernel_minor", `Int;
2227   "lv_size", `Bytes;
2228   "seg_count", `Int;
2229   "origin", `String;
2230   "snap_percent", `OptPercent;
2231   "copy_percent", `OptPercent;
2232   "move_pv", `String;
2233   "lv_tags", `String;
2234   "mirror_log", `String;
2235   "modules", `String;
2236 ]
2237
2238 (* Column names and types from stat structures.
2239  * NB. Can't use things like 'st_atime' because glibc header files
2240  * define some of these as macros.  Ugh.
2241  *)
2242 let stat_cols = [
2243   "dev", `Int;
2244   "ino", `Int;
2245   "mode", `Int;
2246   "nlink", `Int;
2247   "uid", `Int;
2248   "gid", `Int;
2249   "rdev", `Int;
2250   "size", `Int;
2251   "blksize", `Int;
2252   "blocks", `Int;
2253   "atime", `Int;
2254   "mtime", `Int;
2255   "ctime", `Int;
2256 ]
2257 let statvfs_cols = [
2258   "bsize", `Int;
2259   "frsize", `Int;
2260   "blocks", `Int;
2261   "bfree", `Int;
2262   "bavail", `Int;
2263   "files", `Int;
2264   "ffree", `Int;
2265   "favail", `Int;
2266   "fsid", `Int;
2267   "flag", `Int;
2268   "namemax", `Int;
2269 ]
2270
2271 (* Useful functions.
2272  * Note we don't want to use any external OCaml libraries which
2273  * makes this a bit harder than it should be.
2274  *)
2275 let failwithf fs = ksprintf failwith fs
2276
2277 let replace_char s c1 c2 =
2278   let s2 = String.copy s in
2279   let r = ref false in
2280   for i = 0 to String.length s2 - 1 do
2281     if String.unsafe_get s2 i = c1 then (
2282       String.unsafe_set s2 i c2;
2283       r := true
2284     )
2285   done;
2286   if not !r then s else s2
2287
2288 let isspace c =
2289   c = ' '
2290   (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
2291
2292 let triml ?(test = isspace) str =
2293   let i = ref 0 in
2294   let n = ref (String.length str) in
2295   while !n > 0 && test str.[!i]; do
2296     decr n;
2297     incr i
2298   done;
2299   if !i = 0 then str
2300   else String.sub str !i !n
2301
2302 let trimr ?(test = isspace) str =
2303   let n = ref (String.length str) in
2304   while !n > 0 && test str.[!n-1]; do
2305     decr n
2306   done;
2307   if !n = String.length str then str
2308   else String.sub str 0 !n
2309
2310 let trim ?(test = isspace) str =
2311   trimr ~test (triml ~test str)
2312
2313 let rec find s sub =
2314   let len = String.length s in
2315   let sublen = String.length sub in
2316   let rec loop i =
2317     if i <= len-sublen then (
2318       let rec loop2 j =
2319         if j < sublen then (
2320           if s.[i+j] = sub.[j] then loop2 (j+1)
2321           else -1
2322         ) else
2323           i (* found *)
2324       in
2325       let r = loop2 0 in
2326       if r = -1 then loop (i+1) else r
2327     ) else
2328       -1 (* not found *)
2329   in
2330   loop 0
2331
2332 let rec replace_str s s1 s2 =
2333   let len = String.length s in
2334   let sublen = String.length s1 in
2335   let i = find s s1 in
2336   if i = -1 then s
2337   else (
2338     let s' = String.sub s 0 i in
2339     let s'' = String.sub s (i+sublen) (len-i-sublen) in
2340     s' ^ s2 ^ replace_str s'' s1 s2
2341   )
2342
2343 let rec string_split sep str =
2344   let len = String.length str in
2345   let seplen = String.length sep in
2346   let i = find str sep in
2347   if i = -1 then [str]
2348   else (
2349     let s' = String.sub str 0 i in
2350     let s'' = String.sub str (i+seplen) (len-i-seplen) in
2351     s' :: string_split sep s''
2352   )
2353
2354 let files_equal n1 n2 =
2355   let cmd = sprintf "cmp -s %s %s" (Filename.quote n1) (Filename.quote n2) in
2356   match Sys.command cmd with
2357   | 0 -> true
2358   | 1 -> false
2359   | i -> failwithf "%s: failed with error code %d" cmd i
2360
2361 let rec find_map f = function
2362   | [] -> raise Not_found
2363   | x :: xs ->
2364       match f x with
2365       | Some y -> y
2366       | None -> find_map f xs
2367
2368 let iteri f xs =
2369   let rec loop i = function
2370     | [] -> ()
2371     | x :: xs -> f i x; loop (i+1) xs
2372   in
2373   loop 0 xs
2374
2375 let mapi f xs =
2376   let rec loop i = function
2377     | [] -> []
2378     | x :: xs -> let r = f i x in r :: loop (i+1) xs
2379   in
2380   loop 0 xs
2381
2382 let name_of_argt = function
2383   | String n | OptString n | StringList n | Bool n | Int n
2384   | FileIn n | FileOut n -> n
2385
2386 let seq_of_test = function
2387   | TestRun s | TestOutput (s, _) | TestOutputList (s, _)
2388   | TestOutputInt (s, _) | TestOutputTrue s | TestOutputFalse s
2389   | TestOutputLength (s, _) | TestOutputStruct (s, _)
2390   | TestLastFail s -> s
2391
2392 (* Check function names etc. for consistency. *)
2393 let check_functions () =
2394   let contains_uppercase str =
2395     let len = String.length str in
2396     let rec loop i =
2397       if i >= len then false
2398       else (
2399         let c = str.[i] in
2400         if c >= 'A' && c <= 'Z' then true
2401         else loop (i+1)
2402       )
2403     in
2404     loop 0
2405   in
2406
2407   (* Check function names. *)
2408   List.iter (
2409     fun (name, _, _, _, _, _, _) ->
2410       if String.length name >= 7 && String.sub name 0 7 = "guestfs" then
2411         failwithf "function name %s does not need 'guestfs' prefix" name;
2412       if name = "" then
2413         failwithf "function name is empty";
2414       if name.[0] < 'a' || name.[0] > 'z' then
2415         failwithf "function name %s must start with lowercase a-z" name;
2416       if String.contains name '-' then
2417         failwithf "function name %s should not contain '-', use '_' instead."
2418           name
2419   ) all_functions;
2420
2421   (* Check function parameter/return names. *)
2422   List.iter (
2423     fun (name, style, _, _, _, _, _) ->
2424       let check_arg_ret_name n =
2425         if contains_uppercase n then
2426           failwithf "%s param/ret %s should not contain uppercase chars"
2427             name n;
2428         if String.contains n '-' || String.contains n '_' then
2429           failwithf "%s param/ret %s should not contain '-' or '_'"
2430             name n;
2431         if n = "value" then
2432           failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" n;
2433         if n = "argv" || n = "args" then
2434           failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
2435       in
2436
2437       (match fst style with
2438        | RErr -> ()
2439        | RInt n | RInt64 n | RBool n | RConstString n | RString n
2440        | RStringList n | RPVList n | RVGList n | RLVList n
2441        | RStat n | RStatVFS n
2442        | RHashtable n ->
2443            check_arg_ret_name n
2444        | RIntBool (n,m) ->
2445            check_arg_ret_name n;
2446            check_arg_ret_name m
2447       );
2448       List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) (snd style)
2449   ) all_functions;
2450
2451   (* Check short descriptions. *)
2452   List.iter (
2453     fun (name, _, _, _, _, shortdesc, _) ->
2454       if shortdesc.[0] <> Char.lowercase shortdesc.[0] then
2455         failwithf "short description of %s should begin with lowercase." name;
2456       let c = shortdesc.[String.length shortdesc-1] in
2457       if c = '\n' || c = '.' then
2458         failwithf "short description of %s should not end with . or \\n." name
2459   ) all_functions;
2460
2461   (* Check long dscriptions. *)
2462   List.iter (
2463     fun (name, _, _, _, _, _, longdesc) ->
2464       if longdesc.[String.length longdesc-1] = '\n' then
2465         failwithf "long description of %s should not end with \\n." name
2466   ) all_functions;
2467
2468   (* Check proc_nrs. *)
2469   List.iter (
2470     fun (name, _, proc_nr, _, _, _, _) ->
2471       if proc_nr <= 0 then
2472         failwithf "daemon function %s should have proc_nr > 0" name
2473   ) daemon_functions;
2474
2475   List.iter (
2476     fun (name, _, proc_nr, _, _, _, _) ->
2477       if proc_nr <> -1 then
2478         failwithf "non-daemon function %s should have proc_nr -1" name
2479   ) non_daemon_functions;
2480
2481   let proc_nrs =
2482     List.map (fun (name, _, proc_nr, _, _, _, _) -> name, proc_nr)
2483       daemon_functions in
2484   let proc_nrs =
2485     List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
2486   let rec loop = function
2487     | [] -> ()
2488     | [_] -> ()
2489     | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
2490         loop rest
2491     | (name1,nr1) :: (name2,nr2) :: _ ->
2492         failwithf "%s and %s have conflicting procedure numbers (%d, %d)"
2493           name1 name2 nr1 nr2
2494   in
2495   loop proc_nrs;
2496
2497   (* Check tests. *)
2498   List.iter (
2499     function
2500       (* Ignore functions that have no tests.  We generate a
2501        * warning when the user does 'make check' instead.
2502        *)
2503     | name, _, _, _, [], _, _ -> ()
2504     | name, _, _, _, tests, _, _ ->
2505         let funcs =
2506           List.map (
2507             fun (_, _, test) ->
2508               match seq_of_test test with
2509               | [] ->
2510                   failwithf "%s has a test containing an empty sequence" name
2511               | cmds -> List.map List.hd cmds
2512           ) tests in
2513         let funcs = List.flatten funcs in
2514
2515         let tested = List.mem name funcs in
2516
2517         if not tested then
2518           failwithf "function %s has tests but does not test itself" name
2519   ) all_functions
2520
2521 (* 'pr' prints to the current output file. *)
2522 let chan = ref stdout
2523 let pr fs = ksprintf (output_string !chan) fs
2524
2525 (* Generate a header block in a number of standard styles. *)
2526 type comment_style = CStyle | HashStyle | OCamlStyle | HaskellStyle
2527 type license = GPLv2 | LGPLv2
2528
2529 let generate_header comment license =
2530   let c = match comment with
2531     | CStyle ->     pr "/* "; " *"
2532     | HashStyle ->  pr "# ";  "#"
2533     | OCamlStyle -> pr "(* "; " *"
2534     | HaskellStyle -> pr "{- "; "  " in
2535   pr "libguestfs generated file\n";
2536   pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
2537   pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
2538   pr "%s\n" c;
2539   pr "%s Copyright (C) 2009 Red Hat Inc.\n" c;
2540   pr "%s\n" c;
2541   (match license with
2542    | GPLv2 ->
2543        pr "%s This program is free software; you can redistribute it and/or modify\n" c;
2544        pr "%s it under the terms of the GNU General Public License as published by\n" c;
2545        pr "%s the Free Software Foundation; either version 2 of the License, or\n" c;
2546        pr "%s (at your option) any later version.\n" c;
2547        pr "%s\n" c;
2548        pr "%s This program is distributed in the hope that it will be useful,\n" c;
2549        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
2550        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" c;
2551        pr "%s GNU General Public License for more details.\n" c;
2552        pr "%s\n" c;
2553        pr "%s You should have received a copy of the GNU General Public License along\n" c;
2554        pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c;
2555        pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c;
2556
2557    | LGPLv2 ->
2558        pr "%s This library is free software; you can redistribute it and/or\n" c;
2559        pr "%s modify it under the terms of the GNU Lesser General Public\n" c;
2560        pr "%s License as published by the Free Software Foundation; either\n" c;
2561        pr "%s version 2 of the License, or (at your option) any later version.\n" c;
2562        pr "%s\n" c;
2563        pr "%s This library is distributed in the hope that it will be useful,\n" c;
2564        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
2565        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n" c;
2566        pr "%s Lesser General Public License for more details.\n" c;
2567        pr "%s\n" c;
2568        pr "%s You should have received a copy of the GNU Lesser General Public\n" c;
2569        pr "%s License along with this library; if not, write to the Free Software\n" c;
2570        pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c;
2571   );
2572   (match comment with
2573    | CStyle -> pr " */\n"
2574    | HashStyle -> ()
2575    | OCamlStyle -> pr " *)\n"
2576    | HaskellStyle -> pr "-}\n"
2577   );
2578   pr "\n"
2579
2580 (* Start of main code generation functions below this line. *)
2581
2582 (* Generate the pod documentation for the C API. *)
2583 let rec generate_actions_pod () =
2584   List.iter (
2585     fun (shortname, style, _, flags, _, _, longdesc) ->
2586       let name = "guestfs_" ^ shortname in
2587       pr "=head2 %s\n\n" name;
2588       pr " ";
2589       generate_prototype ~extern:false ~handle:"handle" name style;
2590       pr "\n\n";
2591       pr "%s\n\n" longdesc;
2592       (match fst style with
2593        | RErr ->
2594            pr "This function returns 0 on success or -1 on error.\n\n"
2595        | RInt _ ->
2596            pr "On error this function returns -1.\n\n"
2597        | RInt64 _ ->
2598            pr "On error this function returns -1.\n\n"
2599        | RBool _ ->
2600            pr "This function returns a C truth value on success or -1 on error.\n\n"
2601        | RConstString _ ->
2602            pr "This function returns a string, or NULL on error.
2603 The string is owned by the guest handle and must I<not> be freed.\n\n"
2604        | RString _ ->
2605            pr "This function returns a string, or NULL on error.
2606 I<The caller must free the returned string after use>.\n\n"
2607        | RStringList _ ->
2608            pr "This function returns a NULL-terminated array of strings
2609 (like L<environ(3)>), or NULL if there was an error.
2610 I<The caller must free the strings and the array after use>.\n\n"
2611        | RIntBool _ ->
2612            pr "This function returns a C<struct guestfs_int_bool *>,
2613 or NULL if there was an error.
2614 I<The caller must call C<guestfs_free_int_bool> after use>.\n\n"
2615        | RPVList _ ->
2616            pr "This function returns a C<struct guestfs_lvm_pv_list *>
2617 (see E<lt>guestfs-structs.hE<gt>),
2618 or NULL if there was an error.
2619 I<The caller must call C<guestfs_free_lvm_pv_list> after use>.\n\n"
2620        | RVGList _ ->
2621            pr "This function returns a C<struct guestfs_lvm_vg_list *>
2622 (see E<lt>guestfs-structs.hE<gt>),
2623 or NULL if there was an error.
2624 I<The caller must call C<guestfs_free_lvm_vg_list> after use>.\n\n"
2625        | RLVList _ ->
2626            pr "This function returns a C<struct guestfs_lvm_lv_list *>
2627 (see E<lt>guestfs-structs.hE<gt>),
2628 or NULL if there was an error.
2629 I<The caller must call C<guestfs_free_lvm_lv_list> after use>.\n\n"
2630        | RStat _ ->
2631            pr "This function returns a C<struct guestfs_stat *>
2632 (see L<stat(2)> and E<lt>guestfs-structs.hE<gt>),
2633 or NULL if there was an error.
2634 I<The caller must call C<free> after use>.\n\n"
2635        | RStatVFS _ ->
2636            pr "This function returns a C<struct guestfs_statvfs *>
2637 (see L<statvfs(2)> and E<lt>guestfs-structs.hE<gt>),
2638 or NULL if there was an error.
2639 I<The caller must call C<free> after use>.\n\n"
2640        | RHashtable _ ->
2641            pr "This function returns a NULL-terminated array of
2642 strings, or NULL if there was an error.
2643 The array of strings will always have length C<2n+1>, where
2644 C<n> keys and values alternate, followed by the trailing NULL entry.
2645 I<The caller must free the strings and the array after use>.\n\n"
2646       );
2647       if List.mem ProtocolLimitWarning flags then
2648         pr "%s\n\n" protocol_limit_warning;
2649       if List.mem DangerWillRobinson flags then
2650         pr "%s\n\n" danger_will_robinson;
2651   ) all_functions_sorted
2652
2653 and generate_structs_pod () =
2654   (* LVM structs documentation. *)
2655   List.iter (
2656     fun (typ, cols) ->
2657       pr "=head2 guestfs_lvm_%s\n" typ;
2658       pr "\n";
2659       pr " struct guestfs_lvm_%s {\n" typ;
2660       List.iter (
2661         function
2662         | name, `String -> pr "  char *%s;\n" name
2663         | name, `UUID ->
2664             pr "  /* The next field is NOT nul-terminated, be careful when printing it: */\n";
2665             pr "  char %s[32];\n" name
2666         | name, `Bytes -> pr "  uint64_t %s;\n" name
2667         | name, `Int -> pr "  int64_t %s;\n" name
2668         | name, `OptPercent ->
2669             pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
2670             pr "  float %s;\n" name
2671       ) cols;
2672       pr " \n";
2673       pr " struct guestfs_lvm_%s_list {\n" typ;
2674       pr "   uint32_t len; /* Number of elements in list. */\n";
2675       pr "   struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
2676       pr " };\n";
2677       pr " \n";
2678       pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
2679         typ typ;
2680       pr "\n"
2681   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
2682
2683 (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
2684  * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'.
2685  *
2686  * We have to use an underscore instead of a dash because otherwise
2687  * rpcgen generates incorrect code.
2688  *
2689  * This header is NOT exported to clients, but see also generate_structs_h.
2690  *)
2691 and generate_xdr () =
2692   generate_header CStyle LGPLv2;
2693
2694   (* This has to be defined to get around a limitation in Sun's rpcgen. *)
2695   pr "typedef string str<>;\n";
2696   pr "\n";
2697
2698   (* LVM internal structures. *)
2699   List.iter (
2700     function
2701     | typ, cols ->
2702         pr "struct guestfs_lvm_int_%s {\n" typ;
2703         List.iter (function
2704                    | name, `String -> pr "  string %s<>;\n" name
2705                    | name, `UUID -> pr "  opaque %s[32];\n" name
2706                    | name, `Bytes -> pr "  hyper %s;\n" name
2707                    | name, `Int -> pr "  hyper %s;\n" name
2708                    | name, `OptPercent -> pr "  float %s;\n" name
2709                   ) cols;
2710         pr "};\n";
2711         pr "\n";
2712         pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
2713         pr "\n";
2714   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
2715
2716   (* Stat internal structures. *)
2717   List.iter (
2718     function
2719     | typ, cols ->
2720         pr "struct guestfs_int_%s {\n" typ;
2721         List.iter (function
2722                    | name, `Int -> pr "  hyper %s;\n" name
2723                   ) cols;
2724         pr "};\n";
2725         pr "\n";
2726   ) ["stat", stat_cols; "statvfs", statvfs_cols];
2727
2728   List.iter (
2729     fun (shortname, style, _, _, _, _, _) ->
2730       let name = "guestfs_" ^ shortname in
2731
2732       (match snd style with
2733        | [] -> ()
2734        | args ->
2735            pr "struct %s_args {\n" name;
2736            List.iter (
2737              function
2738              | String n -> pr "  string %s<>;\n" n
2739              | OptString n -> pr "  str *%s;\n" n
2740              | StringList n -> pr "  str %s<>;\n" n
2741              | Bool n -> pr "  bool %s;\n" n
2742              | Int n -> pr "  int %s;\n" n
2743              | FileIn _ | FileOut _ -> ()
2744            ) args;
2745            pr "};\n\n"
2746       );
2747       (match fst style with
2748        | RErr -> ()
2749        | RInt n ->
2750            pr "struct %s_ret {\n" name;
2751            pr "  int %s;\n" n;
2752            pr "};\n\n"
2753        | RInt64 n ->
2754            pr "struct %s_ret {\n" name;
2755            pr "  hyper %s;\n" n;
2756            pr "};\n\n"
2757        | RBool n ->
2758            pr "struct %s_ret {\n" name;
2759            pr "  bool %s;\n" n;
2760            pr "};\n\n"
2761        | RConstString _ ->
2762            failwithf "RConstString cannot be returned from a daemon function"
2763        | RString n ->
2764            pr "struct %s_ret {\n" name;
2765            pr "  string %s<>;\n" n;
2766            pr "};\n\n"
2767        | RStringList n ->
2768            pr "struct %s_ret {\n" name;
2769            pr "  str %s<>;\n" n;
2770            pr "};\n\n"
2771        | RIntBool (n,m) ->
2772            pr "struct %s_ret {\n" name;
2773            pr "  int %s;\n" n;
2774            pr "  bool %s;\n" m;
2775            pr "};\n\n"
2776        | RPVList n ->
2777            pr "struct %s_ret {\n" name;
2778            pr "  guestfs_lvm_int_pv_list %s;\n" n;
2779            pr "};\n\n"
2780        | RVGList n ->
2781            pr "struct %s_ret {\n" name;
2782            pr "  guestfs_lvm_int_vg_list %s;\n" n;
2783            pr "};\n\n"
2784        | RLVList n ->
2785            pr "struct %s_ret {\n" name;
2786            pr "  guestfs_lvm_int_lv_list %s;\n" n;
2787            pr "};\n\n"
2788        | RStat n ->
2789            pr "struct %s_ret {\n" name;
2790            pr "  guestfs_int_stat %s;\n" n;
2791            pr "};\n\n"
2792        | RStatVFS n ->
2793            pr "struct %s_ret {\n" name;
2794            pr "  guestfs_int_statvfs %s;\n" n;
2795            pr "};\n\n"
2796        | RHashtable n ->
2797            pr "struct %s_ret {\n" name;
2798            pr "  str %s<>;\n" n;
2799            pr "};\n\n"
2800       );
2801   ) daemon_functions;
2802
2803   (* Table of procedure numbers. *)
2804   pr "enum guestfs_procedure {\n";
2805   List.iter (
2806     fun (shortname, _, proc_nr, _, _, _, _) ->
2807       pr "  GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
2808   ) daemon_functions;
2809   pr "  GUESTFS_PROC_NR_PROCS\n";
2810   pr "};\n";
2811   pr "\n";
2812
2813   (* Having to choose a maximum message size is annoying for several
2814    * reasons (it limits what we can do in the API), but it (a) makes
2815    * the protocol a lot simpler, and (b) provides a bound on the size
2816    * of the daemon which operates in limited memory space.  For large
2817    * file transfers you should use FTP.
2818    *)
2819   pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
2820   pr "\n";
2821
2822   (* Message header, etc. *)
2823   pr "\
2824 /* The communication protocol is now documented in the guestfs(3)
2825  * manpage.
2826  */
2827
2828 const GUESTFS_PROGRAM = 0x2000F5F5;
2829 const GUESTFS_PROTOCOL_VERSION = 1;
2830
2831 /* These constants must be larger than any possible message length. */
2832 const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5;
2833 const GUESTFS_CANCEL_FLAG = 0xffffeeee;
2834
2835 enum guestfs_message_direction {
2836   GUESTFS_DIRECTION_CALL = 0,        /* client -> daemon */
2837   GUESTFS_DIRECTION_REPLY = 1        /* daemon -> client */
2838 };
2839
2840 enum guestfs_message_status {
2841   GUESTFS_STATUS_OK = 0,
2842   GUESTFS_STATUS_ERROR = 1
2843 };
2844
2845 const GUESTFS_ERROR_LEN = 256;
2846
2847 struct guestfs_message_error {
2848   string error_message<GUESTFS_ERROR_LEN>;
2849 };
2850
2851 struct guestfs_message_header {
2852   unsigned prog;                     /* GUESTFS_PROGRAM */
2853   unsigned vers;                     /* GUESTFS_PROTOCOL_VERSION */
2854   guestfs_procedure proc;            /* GUESTFS_PROC_x */
2855   guestfs_message_direction direction;
2856   unsigned serial;                   /* message serial number */
2857   guestfs_message_status status;
2858 };
2859
2860 const GUESTFS_MAX_CHUNK_SIZE = 8192;
2861
2862 struct guestfs_chunk {
2863   int cancel;                        /* if non-zero, transfer is cancelled */
2864   /* data size is 0 bytes if the transfer has finished successfully */
2865   opaque data<GUESTFS_MAX_CHUNK_SIZE>;
2866 };
2867 "
2868
2869 (* Generate the guestfs-structs.h file. *)
2870 and generate_structs_h () =
2871   generate_header CStyle LGPLv2;
2872
2873   (* This is a public exported header file containing various
2874    * structures.  The structures are carefully written to have
2875    * exactly the same in-memory format as the XDR structures that
2876    * we use on the wire to the daemon.  The reason for creating
2877    * copies of these structures here is just so we don't have to
2878    * export the whole of guestfs_protocol.h (which includes much
2879    * unrelated and XDR-dependent stuff that we don't want to be
2880    * public, or required by clients).
2881    *
2882    * To reiterate, we will pass these structures to and from the
2883    * client with a simple assignment or memcpy, so the format
2884    * must be identical to what rpcgen / the RFC defines.
2885    *)
2886
2887   (* guestfs_int_bool structure. *)
2888   pr "struct guestfs_int_bool {\n";
2889   pr "  int32_t i;\n";
2890   pr "  int32_t b;\n";
2891   pr "};\n";
2892   pr "\n";
2893
2894   (* LVM public structures. *)
2895   List.iter (
2896     function
2897     | typ, cols ->
2898         pr "struct guestfs_lvm_%s {\n" typ;
2899         List.iter (
2900           function
2901           | name, `String -> pr "  char *%s;\n" name
2902           | name, `UUID -> pr "  char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
2903           | name, `Bytes -> pr "  uint64_t %s;\n" name
2904           | name, `Int -> pr "  int64_t %s;\n" name
2905           | name, `OptPercent -> pr "  float %s; /* [0..100] or -1 */\n" name
2906         ) cols;
2907         pr "};\n";
2908         pr "\n";
2909         pr "struct guestfs_lvm_%s_list {\n" typ;
2910         pr "  uint32_t len;\n";
2911         pr "  struct guestfs_lvm_%s *val;\n" typ;
2912         pr "};\n";
2913         pr "\n"
2914   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
2915
2916   (* Stat structures. *)
2917   List.iter (
2918     function
2919     | typ, cols ->
2920         pr "struct guestfs_%s {\n" typ;
2921         List.iter (
2922           function
2923           | name, `Int -> pr "  int64_t %s;\n" name
2924         ) cols;
2925         pr "};\n";
2926         pr "\n"
2927   ) ["stat", stat_cols; "statvfs", statvfs_cols]
2928
2929 (* Generate the guestfs-actions.h file. *)
2930 and generate_actions_h () =
2931   generate_header CStyle LGPLv2;
2932   List.iter (
2933     fun (shortname, style, _, _, _, _, _) ->
2934       let name = "guestfs_" ^ shortname in
2935       generate_prototype ~single_line:true ~newline:true ~handle:"handle"
2936         name style
2937   ) all_functions
2938
2939 (* Generate the client-side dispatch stubs. *)
2940 and generate_client_actions () =
2941   generate_header CStyle LGPLv2;
2942
2943   pr "\
2944 #include <stdio.h>
2945 #include <stdlib.h>
2946
2947 #include \"guestfs.h\"
2948 #include \"guestfs_protocol.h\"
2949
2950 #define error guestfs_error
2951 #define perrorf guestfs_perrorf
2952 #define safe_malloc guestfs_safe_malloc
2953 #define safe_realloc guestfs_safe_realloc
2954 #define safe_strdup guestfs_safe_strdup
2955 #define safe_memdup guestfs_safe_memdup
2956
2957 /* Check the return message from a call for validity. */
2958 static int
2959 check_reply_header (guestfs_h *g,
2960                     const struct guestfs_message_header *hdr,
2961                     int proc_nr, int serial)
2962 {
2963   if (hdr->prog != GUESTFS_PROGRAM) {
2964     error (g, \"wrong program (%%d/%%d)\", hdr->prog, GUESTFS_PROGRAM);
2965     return -1;
2966   }
2967   if (hdr->vers != GUESTFS_PROTOCOL_VERSION) {
2968     error (g, \"wrong protocol version (%%d/%%d)\",
2969            hdr->vers, GUESTFS_PROTOCOL_VERSION);
2970     return -1;
2971   }
2972   if (hdr->direction != GUESTFS_DIRECTION_REPLY) {
2973     error (g, \"unexpected message direction (%%d/%%d)\",
2974            hdr->direction, GUESTFS_DIRECTION_REPLY);
2975     return -1;
2976   }
2977   if (hdr->proc != proc_nr) {
2978     error (g, \"unexpected procedure number (%%d/%%d)\", hdr->proc, proc_nr);
2979     return -1;
2980   }
2981   if (hdr->serial != serial) {
2982     error (g, \"unexpected serial (%%d/%%d)\", hdr->serial, serial);
2983     return -1;
2984   }
2985
2986   return 0;
2987 }
2988
2989 /* Check we are in the right state to run a high-level action. */
2990 static int
2991 check_state (guestfs_h *g, const char *caller)
2992 {
2993   if (!guestfs_is_ready (g)) {
2994     if (guestfs_is_config (g))
2995       error (g, \"%%s: call launch() before using this function\",
2996         caller);
2997     else if (guestfs_is_launching (g))
2998       error (g, \"%%s: call wait_ready() before using this function\",
2999         caller);
3000     else
3001       error (g, \"%%s called from the wrong state, %%d != READY\",
3002         caller, guestfs_get_state (g));
3003     return -1;
3004   }
3005   return 0;
3006 }
3007
3008 ";
3009
3010   (* Client-side stubs for each function. *)
3011   List.iter (
3012     fun (shortname, style, _, _, _, _, _) ->
3013       let name = "guestfs_" ^ shortname in
3014
3015       (* Generate the context struct which stores the high-level
3016        * state between callback functions.
3017        *)
3018       pr "struct %s_ctx {\n" shortname;
3019       pr "  /* This flag is set by the callbacks, so we know we've done\n";
3020       pr "   * the callbacks as expected, and in the right sequence.\n";
3021       pr "   * 0 = not called, 1 = reply_cb called.\n";
3022       pr "   */\n";
3023       pr "  int cb_sequence;\n";
3024       pr "  struct guestfs_message_header hdr;\n";
3025       pr "  struct guestfs_message_error err;\n";
3026       (match fst style with
3027        | RErr -> ()
3028        | RConstString _ ->
3029            failwithf "RConstString cannot be returned from a daemon function"
3030        | RInt _ | RInt64 _
3031        | RBool _ | RString _ | RStringList _
3032        | RIntBool _
3033        | RPVList _ | RVGList _ | RLVList _
3034        | RStat _ | RStatVFS _
3035        | RHashtable _ ->
3036            pr "  struct %s_ret ret;\n" name
3037       );
3038       pr "};\n";
3039       pr "\n";
3040
3041       (* Generate the reply callback function. *)
3042       pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
3043       pr "{\n";
3044       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
3045       pr "  struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
3046       pr "\n";
3047       pr "  /* This should definitely not happen. */\n";
3048       pr "  if (ctx->cb_sequence != 0) {\n";
3049       pr "    ctx->cb_sequence = 9999;\n";
3050       pr "    error (g, \"%%s: internal error: reply callback called twice\", \"%s\");\n" name;
3051       pr "    return;\n";
3052       pr "  }\n";
3053       pr "\n";
3054       pr "  ml->main_loop_quit (ml, g);\n";
3055       pr "\n";
3056       pr "  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {\n";
3057       pr "    error (g, \"%%s: failed to parse reply header\", \"%s\");\n" name;
3058       pr "    return;\n";
3059       pr "  }\n";
3060       pr "  if (ctx->hdr.status == GUESTFS_STATUS_ERROR) {\n";
3061       pr "    if (!xdr_guestfs_message_error (xdr, &ctx->err)) {\n";
3062       pr "      error (g, \"%%s: failed to parse reply error\", \"%s\");\n"
3063         name;
3064       pr "      return;\n";
3065       pr "    }\n";
3066       pr "    goto done;\n";
3067       pr "  }\n";
3068
3069       (match fst style with
3070        | RErr -> ()
3071        | RConstString _ ->
3072            failwithf "RConstString cannot be returned from a daemon function"
3073        | RInt _ | RInt64 _
3074        | RBool _ | RString _ | RStringList _
3075        | RIntBool _
3076        | RPVList _ | RVGList _ | RLVList _
3077        | RStat _ | RStatVFS _
3078        | RHashtable _ ->
3079             pr "  if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name;
3080             pr "    error (g, \"%%s: failed to parse reply\", \"%s\");\n" name;
3081             pr "    return;\n";
3082             pr "  }\n";
3083       );
3084
3085       pr " done:\n";
3086       pr "  ctx->cb_sequence = 1;\n";
3087       pr "}\n\n";
3088
3089       (* Generate the action stub. *)
3090       generate_prototype ~extern:false ~semicolon:false ~newline:true
3091         ~handle:"g" name style;
3092
3093       let error_code =
3094         match fst style with
3095         | RErr | RInt _ | RInt64 _ | RBool _ -> "-1"
3096         | RConstString _ ->
3097             failwithf "RConstString cannot be returned from a daemon function"
3098         | RString _ | RStringList _ | RIntBool _
3099         | RPVList _ | RVGList _ | RLVList _
3100         | RStat _ | RStatVFS _
3101         | RHashtable _ ->
3102             "NULL" in
3103
3104       pr "{\n";
3105
3106       (match snd style with
3107        | [] -> ()
3108        | _ -> pr "  struct %s_args args;\n" name
3109       );
3110
3111       pr "  struct %s_ctx ctx;\n" shortname;
3112       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
3113       pr "  int serial;\n";
3114       pr "\n";
3115       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
3116       pr "  guestfs_set_busy (g);\n";
3117       pr "\n";
3118       pr "  memset (&ctx, 0, sizeof ctx);\n";
3119       pr "\n";
3120
3121       (* Send the main header and arguments. *)
3122       (match snd style with
3123        | [] ->
3124            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s, NULL, NULL);\n"
3125              (String.uppercase shortname)
3126        | args ->
3127            List.iter (
3128              function
3129              | String n ->
3130                  pr "  args.%s = (char *) %s;\n" n n
3131              | OptString n ->
3132                  pr "  args.%s = %s ? (char **) &%s : NULL;\n" n n n
3133              | StringList n ->
3134                  pr "  args.%s.%s_val = (char **) %s;\n" n n n;
3135                  pr "  for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
3136              | Bool n ->
3137                  pr "  args.%s = %s;\n" n n
3138              | Int n ->
3139                  pr "  args.%s = %s;\n" n n
3140              | FileIn _ | FileOut _ -> ()
3141            ) args;
3142            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s,\n"
3143              (String.uppercase shortname);
3144            pr "        (xdrproc_t) xdr_%s_args, (char *) &args);\n"
3145              name;
3146       );
3147       pr "  if (serial == -1) {\n";
3148       pr "    guestfs_end_busy (g);\n";
3149       pr "    return %s;\n" error_code;
3150       pr "  }\n";
3151       pr "\n";
3152
3153       (* Send any additional files (FileIn) requested. *)
3154       let need_read_reply_label = ref false in
3155       List.iter (
3156         function
3157         | FileIn n ->
3158             pr "  {\n";
3159             pr "    int r;\n";
3160             pr "\n";
3161             pr "    r = guestfs__send_file_sync (g, %s);\n" n;
3162             pr "    if (r == -1) {\n";
3163             pr "      guestfs_end_busy (g);\n";
3164             pr "      return %s;\n" error_code;
3165             pr "    }\n";
3166             pr "    if (r == -2) /* daemon cancelled */\n";
3167             pr "      goto read_reply;\n";
3168             need_read_reply_label := true;
3169             pr "  }\n";
3170             pr "\n";
3171         | _ -> ()
3172       ) (snd style);
3173
3174       (* Wait for the reply from the remote end. *)
3175       if !need_read_reply_label then pr " read_reply:\n";
3176       pr "  guestfs__switch_to_receiving (g);\n";
3177       pr "  ctx.cb_sequence = 0;\n";
3178       pr "  guestfs_set_reply_callback (g, %s_reply_cb, &ctx);\n" shortname;
3179       pr "  (void) ml->main_loop_run (ml, g);\n";
3180       pr "  guestfs_set_reply_callback (g, NULL, NULL);\n";
3181       pr "  if (ctx.cb_sequence != 1) {\n";
3182       pr "    error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name;
3183       pr "    guestfs_end_busy (g);\n";
3184       pr "    return %s;\n" error_code;
3185       pr "  }\n";
3186       pr "\n";
3187
3188       pr "  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1) {\n"
3189         (String.uppercase shortname);
3190       pr "    guestfs_end_busy (g);\n";
3191       pr "    return %s;\n" error_code;
3192       pr "  }\n";
3193       pr "\n";
3194
3195       pr "  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {\n";
3196       pr "    error (g, \"%%s\", ctx.err.error_message);\n";
3197       pr "    free (ctx.err.error_message);\n";
3198       pr "    guestfs_end_busy (g);\n";
3199       pr "    return %s;\n" error_code;
3200       pr "  }\n";
3201       pr "\n";
3202
3203       (* Expecting to receive further files (FileOut)? *)
3204       List.iter (
3205         function
3206         | FileOut n ->
3207             pr "  if (guestfs__receive_file_sync (g, %s) == -1) {\n" n;
3208             pr "    guestfs_end_busy (g);\n";
3209             pr "    return %s;\n" error_code;
3210             pr "  }\n";
3211             pr "\n";
3212         | _ -> ()
3213       ) (snd style);
3214
3215       pr "  guestfs_end_busy (g);\n";
3216
3217       (match fst style with
3218        | RErr -> pr "  return 0;\n"
3219        | RInt n | RInt64 n | RBool n ->
3220            pr "  return ctx.ret.%s;\n" n
3221        | RConstString _ ->
3222            failwithf "RConstString cannot be returned from a daemon function"
3223        | RString n ->
3224            pr "  return ctx.ret.%s; /* caller will free */\n" n
3225        | RStringList n | RHashtable n ->
3226            pr "  /* caller will free this, but we need to add a NULL entry */\n";
3227            pr "  ctx.ret.%s.%s_val =\n" n n;
3228            pr "    safe_realloc (g, ctx.ret.%s.%s_val,\n" n n;
3229            pr "                  sizeof (char *) * (ctx.ret.%s.%s_len + 1));\n"
3230              n n;
3231            pr "  ctx.ret.%s.%s_val[ctx.ret.%s.%s_len] = NULL;\n" n n n n;
3232            pr "  return ctx.ret.%s.%s_val;\n" n n
3233        | RIntBool _ ->
3234            pr "  /* caller with free this */\n";
3235            pr "  return safe_memdup (g, &ctx.ret, sizeof (ctx.ret));\n"
3236        | RPVList n | RVGList n | RLVList n
3237        | RStat n | RStatVFS n ->
3238            pr "  /* caller will free this */\n";
3239            pr "  return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n
3240       );
3241
3242       pr "}\n\n"
3243   ) daemon_functions
3244
3245 (* Generate daemon/actions.h. *)
3246 and generate_daemon_actions_h () =
3247   generate_header CStyle GPLv2;
3248
3249   pr "#include \"../src/guestfs_protocol.h\"\n";
3250   pr "\n";
3251
3252   List.iter (
3253     fun (name, style, _, _, _, _, _) ->
3254         generate_prototype
3255           ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
3256           name style;
3257   ) daemon_functions
3258
3259 (* Generate the server-side stubs. *)
3260 and generate_daemon_actions () =
3261   generate_header CStyle GPLv2;
3262
3263   pr "#include <config.h>\n";
3264   pr "\n";
3265   pr "#include <stdio.h>\n";
3266   pr "#include <stdlib.h>\n";
3267   pr "#include <string.h>\n";
3268   pr "#include <inttypes.h>\n";
3269   pr "#include <ctype.h>\n";
3270   pr "#include <rpc/types.h>\n";
3271   pr "#include <rpc/xdr.h>\n";
3272   pr "\n";
3273   pr "#include \"daemon.h\"\n";
3274   pr "#include \"../src/guestfs_protocol.h\"\n";
3275   pr "#include \"actions.h\"\n";
3276   pr "\n";
3277
3278   List.iter (
3279     fun (name, style, _, _, _, _, _) ->
3280       (* Generate server-side stubs. *)
3281       pr "static void %s_stub (XDR *xdr_in)\n" name;
3282       pr "{\n";
3283       let error_code =
3284         match fst style with
3285         | RErr | RInt _ -> pr "  int r;\n"; "-1"
3286         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
3287         | RBool _ -> pr "  int r;\n"; "-1"
3288         | RConstString _ ->
3289             failwithf "RConstString cannot be returned from a daemon function"
3290         | RString _ -> pr "  char *r;\n"; "NULL"
3291         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
3292         | RIntBool _ -> pr "  guestfs_%s_ret *r;\n" name; "NULL"
3293         | RPVList _ -> pr "  guestfs_lvm_int_pv_list *r;\n"; "NULL"
3294         | RVGList _ -> pr "  guestfs_lvm_int_vg_list *r;\n"; "NULL"
3295         | RLVList _ -> pr "  guestfs_lvm_int_lv_list *r;\n"; "NULL"
3296         | RStat _ -> pr "  guestfs_int_stat *r;\n"; "NULL"
3297         | RStatVFS _ -> pr "  guestfs_int_statvfs *r;\n"; "NULL" in
3298
3299       (match snd style with
3300        | [] -> ()
3301        | args ->
3302            pr "  struct guestfs_%s_args args;\n" name;
3303            List.iter (
3304              function
3305              | String n
3306              | OptString n -> pr "  const char *%s;\n" n
3307              | StringList n -> pr "  char **%s;\n" n
3308              | Bool n -> pr "  int %s;\n" n
3309              | Int n -> pr "  int %s;\n" n
3310              | FileIn _ | FileOut _ -> ()
3311            ) args
3312       );
3313       pr "\n";
3314
3315       (match snd style with
3316        | [] -> ()
3317        | args ->
3318            pr "  memset (&args, 0, sizeof args);\n";
3319            pr "\n";
3320            pr "  if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
3321            pr "    reply_with_error (\"%%s: daemon failed to decode procedure arguments\", \"%s\");\n" name;
3322            pr "    return;\n";
3323            pr "  }\n";
3324            List.iter (
3325              function
3326              | String n -> pr "  %s = args.%s;\n" n n
3327              | OptString n -> pr "  %s = args.%s ? *args.%s : NULL;\n" n n n
3328              | StringList n ->
3329                  pr "  %s = realloc (args.%s.%s_val,\n" n n n;
3330                  pr "                sizeof (char *) * (args.%s.%s_len+1));\n" n n;
3331                  pr "  if (%s == NULL) {\n" n;
3332                  pr "    reply_with_perror (\"realloc\");\n";
3333                  pr "    goto done;\n";
3334                  pr "  }\n";
3335                  pr "  %s[args.%s.%s_len] = NULL;\n" n n n;
3336                  pr "  args.%s.%s_val = %s;\n" n n n;
3337              | Bool n -> pr "  %s = args.%s;\n" n n
3338              | Int n -> pr "  %s = args.%s;\n" n n
3339              | FileIn _ | FileOut _ -> ()
3340            ) args;
3341            pr "\n"
3342       );
3343
3344       (* Don't want to call the impl with any FileIn or FileOut
3345        * parameters, since these go "outside" the RPC protocol.
3346        *)
3347       let argsnofile =
3348         List.filter (function FileIn _ | FileOut _ -> false | _ -> true)
3349           (snd style) in
3350       pr "  r = do_%s " name;
3351       generate_call_args argsnofile;
3352       pr ";\n";
3353
3354       pr "  if (r == %s)\n" error_code;
3355       pr "    /* do_%s has already called reply_with_error */\n" name;
3356       pr "    goto done;\n";
3357       pr "\n";
3358
3359       (* If there are any FileOut parameters, then the impl must
3360        * send its own reply.
3361        *)
3362       let no_reply =
3363         List.exists (function FileOut _ -> true | _ -> false) (snd style) in
3364       if no_reply then
3365         pr "  /* do_%s has already sent a reply */\n" name
3366       else (
3367         match fst style with
3368         | RErr -> pr "  reply (NULL, NULL);\n"
3369         | RInt n | RInt64 n | RBool n ->
3370             pr "  struct guestfs_%s_ret ret;\n" name;
3371             pr "  ret.%s = r;\n" n;
3372             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
3373               name
3374         | RConstString _ ->
3375             failwithf "RConstString cannot be returned from a daemon function"
3376         | RString n ->
3377             pr "  struct guestfs_%s_ret ret;\n" name;
3378             pr "  ret.%s = r;\n" n;
3379             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
3380               name;
3381             pr "  free (r);\n"
3382         | RStringList n | RHashtable n ->
3383             pr "  struct guestfs_%s_ret ret;\n" name;
3384             pr "  ret.%s.%s_len = count_strings (r);\n" n n;
3385             pr "  ret.%s.%s_val = r;\n" n n;
3386             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
3387               name;
3388             pr "  free_strings (r);\n"
3389         | RIntBool _ ->
3390             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n"
3391               name;
3392             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name
3393         | RPVList n | RVGList n | RLVList n
3394         | RStat n | RStatVFS n ->
3395             pr "  struct guestfs_%s_ret ret;\n" name;
3396             pr "  ret.%s = *r;\n" n;
3397             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
3398               name;
3399             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
3400               name
3401       );
3402
3403       (* Free the args. *)
3404       (match snd style with
3405        | [] ->
3406            pr "done: ;\n";
3407        | _ ->
3408            pr "done:\n";
3409            pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
3410              name
3411       );
3412
3413       pr "}\n\n";
3414   ) daemon_functions;
3415
3416   (* Dispatch function. *)
3417   pr "void dispatch_incoming_message (XDR *xdr_in)\n";
3418   pr "{\n";
3419   pr "  switch (proc_nr) {\n";
3420
3421   List.iter (
3422     fun (name, style, _, _, _, _, _) ->
3423         pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
3424         pr "      %s_stub (xdr_in);\n" name;
3425         pr "      break;\n"
3426   ) daemon_functions;
3427
3428   pr "    default:\n";
3429   pr "      reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
3430   pr "  }\n";
3431   pr "}\n";
3432   pr "\n";
3433
3434   (* LVM columns and tokenization functions. *)
3435   (* XXX This generates crap code.  We should rethink how we
3436    * do this parsing.
3437    *)
3438   List.iter (
3439     function
3440     | typ, cols ->
3441         pr "static const char *lvm_%s_cols = \"%s\";\n"
3442           typ (String.concat "," (List.map fst cols));
3443         pr "\n";
3444
3445         pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
3446         pr "{\n";
3447         pr "  char *tok, *p, *next;\n";
3448         pr "  int i, j;\n";
3449         pr "\n";
3450         (*
3451         pr "  fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
3452         pr "\n";
3453         *)
3454         pr "  if (!str) {\n";
3455         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
3456         pr "    return -1;\n";
3457         pr "  }\n";
3458         pr "  if (!*str || isspace (*str)) {\n";
3459         pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
3460         pr "    return -1;\n";
3461         pr "  }\n";
3462         pr "  tok = str;\n";
3463         List.iter (
3464           fun (name, coltype) ->
3465             pr "  if (!tok) {\n";
3466             pr "    fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
3467             pr "    return -1;\n";
3468             pr "  }\n";
3469             pr "  p = strchrnul (tok, ',');\n";
3470             pr "  if (*p) next = p+1; else next = NULL;\n";
3471             pr "  *p = '\\0';\n";
3472             (match coltype with
3473              | `String ->
3474                  pr "  r->%s = strdup (tok);\n" name;
3475                  pr "  if (r->%s == NULL) {\n" name;
3476                  pr "    perror (\"strdup\");\n";
3477                  pr "    return -1;\n";
3478                  pr "  }\n"
3479              | `UUID ->
3480                  pr "  for (i = j = 0; i < 32; ++j) {\n";
3481                  pr "    if (tok[j] == '\\0') {\n";
3482                  pr "      fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
3483                  pr "      return -1;\n";
3484                  pr "    } else if (tok[j] != '-')\n";
3485                  pr "      r->%s[i++] = tok[j];\n" name;
3486                  pr "  }\n";
3487              | `Bytes ->
3488                  pr "  if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
3489                  pr "    fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3490                  pr "    return -1;\n";
3491                  pr "  }\n";
3492              | `Int ->
3493                  pr "  if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
3494                  pr "    fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3495                  pr "    return -1;\n";
3496                  pr "  }\n";
3497              | `OptPercent ->
3498                  pr "  if (tok[0] == '\\0')\n";
3499                  pr "    r->%s = -1;\n" name;
3500                  pr "  else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
3501                  pr "    fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3502                  pr "    return -1;\n";
3503                  pr "  }\n";
3504             );
3505             pr "  tok = next;\n";
3506         ) cols;
3507
3508         pr "  if (tok != NULL) {\n";
3509         pr "    fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
3510         pr "    return -1;\n";
3511         pr "  }\n";
3512         pr "  return 0;\n";
3513         pr "}\n";
3514         pr "\n";
3515
3516         pr "guestfs_lvm_int_%s_list *\n" typ;
3517         pr "parse_command_line_%ss (void)\n" typ;
3518         pr "{\n";
3519         pr "  char *out, *err;\n";
3520         pr "  char *p, *pend;\n";
3521         pr "  int r, i;\n";
3522         pr "  guestfs_lvm_int_%s_list *ret;\n" typ;
3523         pr "  void *newp;\n";
3524         pr "\n";
3525         pr "  ret = malloc (sizeof *ret);\n";
3526         pr "  if (!ret) {\n";
3527         pr "    reply_with_perror (\"malloc\");\n";
3528         pr "    return NULL;\n";
3529         pr "  }\n";
3530         pr "\n";
3531         pr "  ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
3532         pr "  ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
3533         pr "\n";
3534         pr "  r = command (&out, &err,\n";
3535         pr "           \"/sbin/lvm\", \"%ss\",\n" typ;
3536         pr "           \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
3537         pr "           \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
3538         pr "  if (r == -1) {\n";
3539         pr "    reply_with_error (\"%%s\", err);\n";
3540         pr "    free (out);\n";
3541         pr "    free (err);\n";
3542         pr "    free (ret);\n";
3543         pr "    return NULL;\n";
3544         pr "  }\n";
3545         pr "\n";
3546         pr "  free (err);\n";
3547         pr "\n";
3548         pr "  /* Tokenize each line of the output. */\n";
3549         pr "  p = out;\n";
3550         pr "  i = 0;\n";
3551         pr "  while (p) {\n";
3552         pr "    pend = strchr (p, '\\n');       /* Get the next line of output. */\n";
3553         pr "    if (pend) {\n";
3554         pr "      *pend = '\\0';\n";
3555         pr "      pend++;\n";
3556         pr "    }\n";
3557         pr "\n";
3558         pr "    while (*p && isspace (*p))      /* Skip any leading whitespace. */\n";
3559         pr "      p++;\n";
3560         pr "\n";
3561         pr "    if (!*p) {                      /* Empty line?  Skip it. */\n";
3562         pr "      p = pend;\n";
3563         pr "      continue;\n";
3564         pr "    }\n";
3565         pr "\n";
3566         pr "    /* Allocate some space to store this next entry. */\n";
3567         pr "    newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
3568         pr "                sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
3569         pr "    if (newp == NULL) {\n";
3570         pr "      reply_with_perror (\"realloc\");\n";
3571         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
3572         pr "      free (ret);\n";
3573         pr "      free (out);\n";
3574         pr "      return NULL;\n";
3575         pr "    }\n";
3576         pr "    ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
3577         pr "\n";
3578         pr "    /* Tokenize the next entry. */\n";
3579         pr "    r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
3580         pr "    if (r == -1) {\n";
3581         pr "      reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
3582         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
3583         pr "      free (ret);\n";
3584         pr "      free (out);\n";
3585         pr "      return NULL;\n";
3586         pr "    }\n";
3587         pr "\n";
3588         pr "    ++i;\n";
3589         pr "    p = pend;\n";
3590         pr "  }\n";
3591         pr "\n";
3592         pr "  ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
3593         pr "\n";
3594         pr "  free (out);\n";
3595         pr "  return ret;\n";
3596         pr "}\n"
3597
3598   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
3599
3600 (* Generate the tests. *)
3601 and generate_tests () =
3602   generate_header CStyle GPLv2;
3603
3604   pr "\
3605 #include <stdio.h>
3606 #include <stdlib.h>
3607 #include <string.h>
3608 #include <unistd.h>
3609 #include <sys/types.h>
3610 #include <fcntl.h>
3611
3612 #include \"guestfs.h\"
3613
3614 static guestfs_h *g;
3615 static int suppress_error = 0;
3616
3617 /* This will be 's' or 'h' depending on whether the guest kernel
3618  * names IDE devices /dev/sd* or /dev/hd*.
3619  */
3620 static char devchar = 's';
3621
3622 static void print_error (guestfs_h *g, void *data, const char *msg)
3623 {
3624   if (!suppress_error)
3625     fprintf (stderr, \"%%s\\n\", msg);
3626 }
3627
3628 static void print_strings (char * const * const argv)
3629 {
3630   int argc;
3631
3632   for (argc = 0; argv[argc] != NULL; ++argc)
3633     printf (\"\\t%%s\\n\", argv[argc]);
3634 }
3635
3636 /*
3637 static void print_table (char * const * const argv)
3638 {
3639   int i;
3640
3641   for (i = 0; argv[i] != NULL; i += 2)
3642     printf (\"%%s: %%s\\n\", argv[i], argv[i+1]);
3643 }
3644 */
3645
3646 static void no_test_warnings (void)
3647 {
3648 ";
3649
3650   List.iter (
3651     function
3652     | name, _, _, _, [], _, _ ->
3653         pr "  fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name
3654     | name, _, _, _, tests, _, _ -> ()
3655   ) all_functions;
3656
3657   pr "}\n";
3658   pr "\n";
3659
3660   (* Generate the actual tests.  Note that we generate the tests
3661    * in reverse order, deliberately, so that (in general) the
3662    * newest tests run first.  This makes it quicker and easier to
3663    * debug them.
3664    *)
3665   let test_names =
3666     List.map (
3667       fun (name, _, _, _, tests, _, _) ->
3668         mapi (generate_one_test name) tests
3669     ) (List.rev all_functions) in
3670   let test_names = List.concat test_names in
3671   let nr_tests = List.length test_names in
3672
3673   pr "\
3674 int main (int argc, char *argv[])
3675 {
3676   char c = 0;
3677   int failed = 0;
3678   const char *srcdir;
3679   const char *filename;
3680   int fd, i;
3681   int nr_tests, test_num = 0;
3682   char **devs;
3683
3684   no_test_warnings ();
3685
3686   g = guestfs_create ();
3687   if (g == NULL) {
3688     printf (\"guestfs_create FAILED\\n\");
3689     exit (1);
3690   }
3691
3692   guestfs_set_error_handler (g, print_error, NULL);
3693
3694   srcdir = getenv (\"srcdir\");
3695   if (!srcdir) srcdir = \".\";
3696   chdir (srcdir);
3697   guestfs_set_path (g, \".\");
3698
3699   filename = \"test1.img\";
3700   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3701   if (fd == -1) {
3702     perror (filename);
3703     exit (1);
3704   }
3705   if (lseek (fd, %d, SEEK_SET) == -1) {
3706     perror (\"lseek\");
3707     close (fd);
3708     unlink (filename);
3709     exit (1);
3710   }
3711   if (write (fd, &c, 1) == -1) {
3712     perror (\"write\");
3713     close (fd);
3714     unlink (filename);
3715     exit (1);
3716   }
3717   if (close (fd) == -1) {
3718     perror (filename);
3719     unlink (filename);
3720     exit (1);
3721   }
3722   if (guestfs_add_drive (g, filename) == -1) {
3723     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3724     exit (1);
3725   }
3726
3727   filename = \"test2.img\";
3728   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3729   if (fd == -1) {
3730     perror (filename);
3731     exit (1);
3732   }
3733   if (lseek (fd, %d, SEEK_SET) == -1) {
3734     perror (\"lseek\");
3735     close (fd);
3736     unlink (filename);
3737     exit (1);
3738   }
3739   if (write (fd, &c, 1) == -1) {
3740     perror (\"write\");
3741     close (fd);
3742     unlink (filename);
3743     exit (1);
3744   }
3745   if (close (fd) == -1) {
3746     perror (filename);
3747     unlink (filename);
3748     exit (1);
3749   }
3750   if (guestfs_add_drive (g, filename) == -1) {
3751     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3752     exit (1);
3753   }
3754
3755   filename = \"test3.img\";
3756   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3757   if (fd == -1) {
3758     perror (filename);
3759     exit (1);
3760   }
3761   if (lseek (fd, %d, SEEK_SET) == -1) {
3762     perror (\"lseek\");
3763     close (fd);
3764     unlink (filename);
3765     exit (1);
3766   }
3767   if (write (fd, &c, 1) == -1) {
3768     perror (\"write\");
3769     close (fd);
3770     unlink (filename);
3771     exit (1);
3772   }
3773   if (close (fd) == -1) {
3774     perror (filename);
3775     unlink (filename);
3776     exit (1);
3777   }
3778   if (guestfs_add_drive (g, filename) == -1) {
3779     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3780     exit (1);
3781   }
3782
3783   if (guestfs_launch (g) == -1) {
3784     printf (\"guestfs_launch FAILED\\n\");
3785     exit (1);
3786   }
3787   if (guestfs_wait_ready (g) == -1) {
3788     printf (\"guestfs_wait_ready FAILED\\n\");
3789     exit (1);
3790   }
3791
3792   /* Detect if the appliance uses /dev/sd* or /dev/hd* in device
3793    * names.  This changed between RHEL 5 and RHEL 6 so we have to
3794    * support both.
3795    */
3796   devs = guestfs_list_devices (g);
3797   if (devs == NULL || devs[0] == NULL) {
3798     printf (\"guestfs_list_devices FAILED\\n\");
3799     exit (1);
3800   }
3801   if (strncmp (devs[0], \"/dev/sd\", 7) == 0)
3802     devchar = 's';
3803   else if (strncmp (devs[0], \"/dev/hd\", 7) == 0)
3804     devchar = 'h';
3805   else {
3806     printf (\"guestfs_list_devices returned unexpected string '%%s'\\n\",
3807             devs[0]);
3808     exit (1);
3809   }
3810   for (i = 0; devs[i] != NULL; ++i)
3811     free (devs[i]);
3812   free (devs);
3813
3814   nr_tests = %d;
3815
3816 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
3817
3818   iteri (
3819     fun i test_name ->
3820       pr "  test_num++;\n";
3821       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
3822       pr "  if (%s () == -1) {\n" test_name;
3823       pr "    printf (\"%s FAILED\\n\");\n" test_name;
3824       pr "    failed++;\n";
3825       pr "  }\n";
3826   ) test_names;
3827   pr "\n";
3828
3829   pr "  guestfs_close (g);\n";
3830   pr "  unlink (\"test1.img\");\n";
3831   pr "  unlink (\"test2.img\");\n";
3832   pr "  unlink (\"test3.img\");\n";
3833   pr "\n";
3834
3835   pr "  if (failed > 0) {\n";
3836   pr "    printf (\"***** %%d / %%d tests FAILED *****\\n\", failed, nr_tests);\n";
3837   pr "    exit (1);\n";
3838   pr "  }\n";
3839   pr "\n";
3840
3841   pr "  exit (0);\n";
3842   pr "}\n"
3843
3844 and generate_one_test name i (init, prereq, test) =
3845   let test_name = sprintf "test_%s_%d" name i in
3846
3847   (match prereq with
3848    | Disabled | Always -> ()
3849    | If code | Unless code ->
3850        pr "static int %s_prereq (void)\n" test_name;
3851        pr "{\n";
3852        pr "  %s\n" code;
3853        pr "}\n";
3854        pr "\n";
3855   );
3856
3857   pr "static int %s (void)\n" test_name;
3858   pr "{\n";
3859
3860   (match prereq with
3861    | Disabled ->
3862        pr "  printf (\"%%s skipped (reason: test disabled in generator)\\n\", \"%s\");\n" test_name
3863    | If _ ->
3864        pr "  if (%s_prereq ()) {\n" test_name;
3865        generate_one_test_body name i test_name init test;
3866        pr "  } else\n";
3867        pr "    printf (\"%%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name
3868    | Unless _ ->
3869        pr "  if (! %s_prereq ()) {\n" test_name;
3870        generate_one_test_body name i test_name init test;
3871        pr "  } else\n";
3872        pr "    printf (\"%%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name
3873    | Always ->
3874        generate_one_test_body name i test_name init test
3875   );
3876
3877   pr "  return 0;\n";
3878   pr "}\n";
3879   pr "\n";
3880   test_name
3881
3882 and generate_one_test_body name i test_name init test =
3883   (match init with
3884    | InitNone
3885    | InitEmpty ->
3886        pr "  /* InitNone|InitEmpty for %s */\n" test_name;
3887        List.iter (generate_test_command_call test_name)
3888          [["blockdev_setrw"; "/dev/sda"];
3889           ["umount_all"];
3890           ["lvm_remove_all"]]
3891    | InitBasicFS ->
3892        pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
3893        List.iter (generate_test_command_call test_name)
3894          [["blockdev_setrw"; "/dev/sda"];
3895           ["umount_all"];
3896           ["lvm_remove_all"];
3897           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
3898           ["mkfs"; "ext2"; "/dev/sda1"];
3899           ["mount"; "/dev/sda1"; "/"]]
3900    | InitBasicFSonLVM ->
3901        pr "  /* InitBasicFSonLVM for %s: create ext2 on /dev/VG/LV */\n"
3902          test_name;
3903        List.iter (generate_test_command_call test_name)
3904          [["blockdev_setrw"; "/dev/sda"];
3905           ["umount_all"];
3906           ["lvm_remove_all"];
3907           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
3908           ["pvcreate"; "/dev/sda1"];
3909           ["vgcreate"; "VG"; "/dev/sda1"];
3910           ["lvcreate"; "LV"; "VG"; "8"];
3911           ["mkfs"; "ext2"; "/dev/VG/LV"];
3912           ["mount"; "/dev/VG/LV"; "/"]]
3913   );
3914
3915   let get_seq_last = function
3916     | [] ->
3917         failwithf "%s: you cannot use [] (empty list) when expecting a command"
3918           test_name
3919     | seq ->
3920         let seq = List.rev seq in
3921         List.rev (List.tl seq), List.hd seq
3922   in
3923
3924   match test with
3925   | TestRun seq ->
3926       pr "  /* TestRun for %s (%d) */\n" name i;
3927       List.iter (generate_test_command_call test_name) seq
3928   | TestOutput (seq, expected) ->
3929       pr "  /* TestOutput for %s (%d) */\n" name i;
3930       pr "  char expected[] = \"%s\";\n" (c_quote expected);
3931       if String.length expected > 7 &&
3932         String.sub expected 0 7 = "/dev/sd" then
3933           pr "  expected[5] = devchar;\n";
3934       let seq, last = get_seq_last seq in
3935       let test () =
3936         pr "    if (strcmp (r, expected) != 0) {\n";
3937         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
3938         pr "      return -1;\n";
3939         pr "    }\n"
3940       in
3941       List.iter (generate_test_command_call test_name) seq;
3942       generate_test_command_call ~test test_name last
3943   | TestOutputList (seq, expected) ->
3944       pr "  /* TestOutputList for %s (%d) */\n" name i;
3945       let seq, last = get_seq_last seq in
3946       let test () =
3947         iteri (
3948           fun i str ->
3949             pr "    if (!r[%d]) {\n" i;
3950             pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
3951             pr "      print_strings (r);\n";
3952             pr "      return -1;\n";
3953             pr "    }\n";
3954             pr "    {\n";
3955             pr "      char expected[] = \"%s\";\n" (c_quote str);
3956             if String.length str > 7 && String.sub str 0 7 = "/dev/sd" then
3957               pr "      expected[5] = devchar;\n";
3958             pr "      if (strcmp (r[%d], expected) != 0) {\n" i;
3959             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
3960             pr "        return -1;\n";
3961             pr "      }\n";
3962             pr "    }\n"
3963         ) expected;
3964         pr "    if (r[%d] != NULL) {\n" (List.length expected);
3965         pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
3966           test_name;
3967         pr "      print_strings (r);\n";
3968         pr "      return -1;\n";
3969         pr "    }\n"
3970       in
3971       List.iter (generate_test_command_call test_name) seq;
3972       generate_test_command_call ~test test_name last
3973   | TestOutputInt (seq, expected) ->
3974       pr "  /* TestOutputInt for %s (%d) */\n" name i;
3975       let seq, last = get_seq_last seq in
3976       let test () =
3977         pr "    if (r != %d) {\n" expected;
3978         pr "      fprintf (stderr, \"%s: expected %d but got %%d\\n\","
3979           test_name expected;
3980         pr "               (int) r);\n";
3981         pr "      return -1;\n";
3982         pr "    }\n"
3983       in
3984       List.iter (generate_test_command_call test_name) seq;
3985       generate_test_command_call ~test test_name last
3986   | TestOutputTrue seq ->
3987       pr "  /* TestOutputTrue for %s (%d) */\n" name i;
3988       let seq, last = get_seq_last seq in
3989       let test () =
3990         pr "    if (!r) {\n";
3991         pr "      fprintf (stderr, \"%s: expected true, got false\\n\");\n"
3992           test_name;
3993         pr "      return -1;\n";
3994         pr "    }\n"
3995       in
3996       List.iter (generate_test_command_call test_name) seq;
3997       generate_test_command_call ~test test_name last
3998   | TestOutputFalse seq ->
3999       pr "  /* TestOutputFalse for %s (%d) */\n" name i;
4000       let seq, last = get_seq_last seq in
4001       let test () =
4002         pr "    if (r) {\n";
4003         pr "      fprintf (stderr, \"%s: expected false, got true\\n\");\n"
4004           test_name;
4005         pr "      return -1;\n";
4006         pr "    }\n"
4007       in
4008       List.iter (generate_test_command_call test_name) seq;
4009       generate_test_command_call ~test test_name last
4010   | TestOutputLength (seq, expected) ->
4011       pr "  /* TestOutputLength for %s (%d) */\n" name i;
4012       let seq, last = get_seq_last seq in
4013       let test () =
4014         pr "    int j;\n";
4015         pr "    for (j = 0; j < %d; ++j)\n" expected;
4016         pr "      if (r[j] == NULL) {\n";
4017         pr "        fprintf (stderr, \"%s: short list returned\\n\");\n"
4018           test_name;
4019         pr "        print_strings (r);\n";
4020         pr "        return -1;\n";
4021         pr "      }\n";
4022         pr "    if (r[j] != NULL) {\n";
4023         pr "      fprintf (stderr, \"%s: long list returned\\n\");\n"
4024           test_name;
4025         pr "      print_strings (r);\n";
4026         pr "      return -1;\n";
4027         pr "    }\n"
4028       in
4029       List.iter (generate_test_command_call test_name) seq;
4030       generate_test_command_call ~test test_name last
4031   | TestOutputStruct (seq, checks) ->
4032       pr "  /* TestOutputStruct for %s (%d) */\n" name i;
4033       let seq, last = get_seq_last seq in
4034       let test () =
4035         List.iter (
4036           function
4037           | CompareWithInt (field, expected) ->
4038               pr "    if (r->%s != %d) {\n" field expected;
4039               pr "      fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
4040                 test_name field expected;
4041               pr "               (int) r->%s);\n" field;
4042               pr "      return -1;\n";
4043               pr "    }\n"
4044           | CompareWithString (field, expected) ->
4045               pr "    if (strcmp (r->%s, \"%s\") != 0) {\n" field expected;
4046               pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
4047                 test_name field expected;
4048               pr "               r->%s);\n" field;
4049               pr "      return -1;\n";
4050               pr "    }\n"
4051           | CompareFieldsIntEq (field1, field2) ->
4052               pr "    if (r->%s != r->%s) {\n" field1 field2;
4053               pr "      fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
4054                 test_name field1 field2;
4055               pr "               (int) r->%s, (int) r->%s);\n" field1 field2;
4056               pr "      return -1;\n";
4057               pr "    }\n"
4058           | CompareFieldsStrEq (field1, field2) ->
4059               pr "    if (strcmp (r->%s, r->%s) != 0) {\n" field1 field2;
4060               pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
4061                 test_name field1 field2;
4062               pr "               r->%s, r->%s);\n" field1 field2;
4063               pr "      return -1;\n";
4064               pr "    }\n"
4065         ) checks
4066       in
4067       List.iter (generate_test_command_call test_name) seq;
4068       generate_test_command_call ~test test_name last
4069   | TestLastFail seq ->
4070       pr "  /* TestLastFail for %s (%d) */\n" name i;
4071       let seq, last = get_seq_last seq in
4072       List.iter (generate_test_command_call test_name) seq;
4073       generate_test_command_call test_name ~expect_error:true last
4074
4075 (* Generate the code to run a command, leaving the result in 'r'.
4076  * If you expect to get an error then you should set expect_error:true.
4077  *)
4078 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
4079   match cmd with
4080   | [] -> assert false
4081   | name :: args ->
4082       (* Look up the command to find out what args/ret it has. *)
4083       let style =
4084         try
4085           let _, style, _, _, _, _, _ =
4086             List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
4087           style
4088         with Not_found ->
4089           failwithf "%s: in test, command %s was not found" test_name name in
4090
4091       if List.length (snd style) <> List.length args then
4092         failwithf "%s: in test, wrong number of args given to %s"
4093           test_name name;
4094
4095       pr "  {\n";
4096
4097       List.iter (
4098         function
4099         | OptString n, "NULL" -> ()
4100         | String n, arg
4101         | OptString n, arg ->
4102             pr "    char %s[] = \"%s\";\n" n (c_quote arg);
4103             if String.length arg > 7 && String.sub arg 0 7 = "/dev/sd" then
4104               pr "    %s[5] = devchar;\n" n
4105         | Int _, _
4106         | Bool _, _
4107         | FileIn _, _ | FileOut _, _ -> ()
4108         | StringList n, arg ->
4109             let strs = string_split " " arg in
4110             iteri (
4111               fun i str ->
4112                 pr "    char %s_%d[] = \"%s\";\n" n i (c_quote str);
4113                 if String.length str > 7 && String.sub str 0 7 = "/dev/sd" then
4114                   pr "    %s_%d[5] = devchar;\n" n i
4115             ) strs;
4116             pr "    char *%s[] = {\n" n;
4117             iteri (
4118               fun i _ -> pr "      %s_%d,\n" n i
4119             ) strs;
4120             pr "      NULL\n";
4121             pr "    };\n";
4122       ) (List.combine (snd style) args);
4123
4124       let error_code =
4125         match fst style with
4126         | RErr | RInt _ | RBool _ -> pr "    int r;\n"; "-1"
4127         | RInt64 _ -> pr "    int64_t r;\n"; "-1"
4128         | RConstString _ -> pr "    const char *r;\n"; "NULL"
4129         | RString _ -> pr "    char *r;\n"; "NULL"
4130         | RStringList _ | RHashtable _ ->
4131             pr "    char **r;\n";
4132             pr "    int i;\n";
4133             "NULL"
4134         | RIntBool _ ->
4135             pr "    struct guestfs_int_bool *r;\n"; "NULL"
4136         | RPVList _ ->
4137             pr "    struct guestfs_lvm_pv_list *r;\n"; "NULL"
4138         | RVGList _ ->
4139             pr "    struct guestfs_lvm_vg_list *r;\n"; "NULL"
4140         | RLVList _ ->
4141             pr "    struct guestfs_lvm_lv_list *r;\n"; "NULL"
4142         | RStat _ ->
4143             pr "    struct guestfs_stat *r;\n"; "NULL"
4144         | RStatVFS _ ->
4145             pr "    struct guestfs_statvfs *r;\n"; "NULL" in
4146
4147       pr "    suppress_error = %d;\n" (if expect_error then 1 else 0);
4148       pr "    r = guestfs_%s (g" name;
4149
4150       (* Generate the parameters. *)
4151       List.iter (
4152         function
4153         | OptString _, "NULL" -> pr ", NULL"
4154         | String n, _
4155         | OptString n, _ ->
4156             pr ", %s" n
4157         | FileIn _, arg | FileOut _, arg ->
4158             pr ", \"%s\"" (c_quote arg)
4159         | StringList n, _ ->
4160             pr ", %s" n
4161         | Int _, arg ->
4162             let i =
4163               try int_of_string arg
4164               with Failure "int_of_string" ->
4165                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
4166             pr ", %d" i
4167         | Bool _, arg ->
4168             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
4169       ) (List.combine (snd style) args);
4170
4171       pr ");\n";
4172       if not expect_error then
4173         pr "    if (r == %s)\n" error_code
4174       else
4175         pr "    if (r != %s)\n" error_code;
4176       pr "      return -1;\n";
4177
4178       (* Insert the test code. *)
4179       (match test with
4180        | None -> ()
4181        | Some f -> f ()
4182       );
4183
4184       (match fst style with
4185        | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _ -> ()
4186        | RString _ -> pr "    free (r);\n"
4187        | RStringList _ | RHashtable _ ->
4188            pr "    for (i = 0; r[i] != NULL; ++i)\n";
4189            pr "      free (r[i]);\n";
4190            pr "    free (r);\n"
4191        | RIntBool _ ->
4192            pr "    guestfs_free_int_bool (r);\n"
4193        | RPVList _ ->
4194            pr "    guestfs_free_lvm_pv_list (r);\n"
4195        | RVGList _ ->
4196            pr "    guestfs_free_lvm_vg_list (r);\n"
4197        | RLVList _ ->
4198            pr "    guestfs_free_lvm_lv_list (r);\n"
4199        | RStat _ | RStatVFS _ ->
4200            pr "    free (r);\n"
4201       );
4202
4203       pr "  }\n"
4204
4205 and c_quote str =
4206   let str = replace_str str "\r" "\\r" in
4207   let str = replace_str str "\n" "\\n" in
4208   let str = replace_str str "\t" "\\t" in
4209   let str = replace_str str "\000" "\\0" in
4210   str
4211
4212 (* Generate a lot of different functions for guestfish. *)
4213 and generate_fish_cmds () =
4214   generate_header CStyle GPLv2;
4215
4216   let all_functions =
4217     List.filter (
4218       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4219     ) all_functions in
4220   let all_functions_sorted =
4221     List.filter (
4222       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4223     ) all_functions_sorted in
4224
4225   pr "#include <stdio.h>\n";
4226   pr "#include <stdlib.h>\n";
4227   pr "#include <string.h>\n";
4228   pr "#include <inttypes.h>\n";
4229   pr "\n";
4230   pr "#include <guestfs.h>\n";
4231   pr "#include \"fish.h\"\n";
4232   pr "\n";
4233
4234   (* list_commands function, which implements guestfish -h *)
4235   pr "void list_commands (void)\n";
4236   pr "{\n";
4237   pr "  printf (\"    %%-16s     %%s\\n\", \"Command\", \"Description\");\n";
4238   pr "  list_builtin_commands ();\n";
4239   List.iter (
4240     fun (name, _, _, flags, _, shortdesc, _) ->
4241       let name = replace_char name '_' '-' in
4242       pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
4243         name shortdesc
4244   ) all_functions_sorted;
4245   pr "  printf (\"    Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
4246   pr "}\n";
4247   pr "\n";
4248
4249   (* display_command function, which implements guestfish -h cmd *)
4250   pr "void display_command (const char *cmd)\n";
4251   pr "{\n";
4252   List.iter (
4253     fun (name, style, _, flags, _, shortdesc, longdesc) ->
4254       let name2 = replace_char name '_' '-' in
4255       let alias =
4256         try find_map (function FishAlias n -> Some n | _ -> None) flags
4257         with Not_found -> name in
4258       let longdesc = replace_str longdesc "C<guestfs_" "C<" in
4259       let synopsis =
4260         match snd style with
4261         | [] -> name2
4262         | args ->
4263             sprintf "%s <%s>"
4264               name2 (String.concat "> <" (List.map name_of_argt args)) in
4265
4266       let warnings =
4267         if List.mem ProtocolLimitWarning flags then
4268           ("\n\n" ^ protocol_limit_warning)
4269         else "" in
4270
4271       (* For DangerWillRobinson commands, we should probably have
4272        * guestfish prompt before allowing you to use them (especially
4273        * in interactive mode). XXX
4274        *)
4275       let warnings =
4276         warnings ^
4277           if List.mem DangerWillRobinson flags then
4278             ("\n\n" ^ danger_will_robinson)
4279           else "" in
4280
4281       let describe_alias =
4282         if name <> alias then
4283           sprintf "\n\nYou can use '%s' as an alias for this command." alias
4284         else "" in
4285
4286       pr "  if (";
4287       pr "strcasecmp (cmd, \"%s\") == 0" name;
4288       if name <> name2 then
4289         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
4290       if name <> alias then
4291         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
4292       pr ")\n";
4293       pr "    pod2text (\"%s - %s\", %S);\n"
4294         name2 shortdesc
4295         (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings ^ describe_alias);
4296       pr "  else\n"
4297   ) all_functions;
4298   pr "    display_builtin_command (cmd);\n";
4299   pr "}\n";
4300   pr "\n";
4301
4302   (* print_{pv,vg,lv}_list functions *)
4303   List.iter (
4304     function
4305     | typ, cols ->
4306         pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
4307         pr "{\n";
4308         pr "  int i;\n";
4309         pr "\n";
4310         List.iter (
4311           function
4312           | name, `String ->
4313               pr "  printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
4314           | name, `UUID ->
4315               pr "  printf (\"%s: \");\n" name;
4316               pr "  for (i = 0; i < 32; ++i)\n";
4317               pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
4318               pr "  printf (\"\\n\");\n"
4319           | name, `Bytes ->
4320               pr "  printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
4321           | name, `Int ->
4322               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
4323           | name, `OptPercent ->
4324               pr "  if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
4325                 typ name name typ name;
4326               pr "  else printf (\"%s: \\n\");\n" name
4327         ) cols;
4328         pr "}\n";
4329         pr "\n";
4330         pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
4331           typ typ typ;
4332         pr "{\n";
4333         pr "  int i;\n";
4334         pr "\n";
4335         pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
4336         pr "    print_%s (&%ss->val[i]);\n" typ typ;
4337         pr "}\n";
4338         pr "\n";
4339   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
4340
4341   (* print_{stat,statvfs} functions *)
4342   List.iter (
4343     function
4344     | typ, cols ->
4345         pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
4346         pr "{\n";
4347         List.iter (
4348           function
4349           | name, `Int ->
4350               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
4351         ) cols;
4352         pr "}\n";
4353         pr "\n";
4354   ) ["stat", stat_cols; "statvfs", statvfs_cols];
4355
4356   (* run_<action> actions *)
4357   List.iter (
4358     fun (name, style, _, flags, _, _, _) ->
4359       pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
4360       pr "{\n";
4361       (match fst style with
4362        | RErr
4363        | RInt _
4364        | RBool _ -> pr "  int r;\n"
4365        | RInt64 _ -> pr "  int64_t r;\n"
4366        | RConstString _ -> pr "  const char *r;\n"
4367        | RString _ -> pr "  char *r;\n"
4368        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
4369        | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"
4370        | RPVList _ -> pr "  struct guestfs_lvm_pv_list *r;\n"
4371        | RVGList _ -> pr "  struct guestfs_lvm_vg_list *r;\n"
4372        | RLVList _ -> pr "  struct guestfs_lvm_lv_list *r;\n"
4373        | RStat _ -> pr "  struct guestfs_stat *r;\n"
4374        | RStatVFS _ -> pr "  struct guestfs_statvfs *r;\n"
4375       );
4376       List.iter (
4377         function
4378         | String n
4379         | OptString n
4380         | FileIn n
4381         | FileOut n -> pr "  const char *%s;\n" n
4382         | StringList n -> pr "  char **%s;\n" n
4383         | Bool n -> pr "  int %s;\n" n
4384         | Int n -> pr "  int %s;\n" n
4385       ) (snd style);
4386
4387       (* Check and convert parameters. *)
4388       let argc_expected = List.length (snd style) in
4389       pr "  if (argc != %d) {\n" argc_expected;
4390       pr "    fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
4391         argc_expected;
4392       pr "    fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
4393       pr "    return -1;\n";
4394       pr "  }\n";
4395       iteri (
4396         fun i ->
4397           function
4398           | String name -> pr "  %s = argv[%d];\n" name i
4399           | OptString name ->
4400               pr "  %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
4401                 name i i
4402           | FileIn name ->
4403               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdin\";\n"
4404                 name i i
4405           | FileOut name ->
4406               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdout\";\n"
4407                 name i i
4408           | StringList name ->
4409               pr "  %s = parse_string_list (argv[%d]);\n" name i
4410           | Bool name ->
4411               pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
4412           | Int name ->
4413               pr "  %s = atoi (argv[%d]);\n" name i
4414       ) (snd style);
4415
4416       (* Call C API function. *)
4417       let fn =
4418         try find_map (function FishAction n -> Some n | _ -> None) flags
4419         with Not_found -> sprintf "guestfs_%s" name in
4420       pr "  r = %s " fn;
4421       generate_call_args ~handle:"g" (snd style);
4422       pr ";\n";
4423
4424       (* Check return value for errors and display command results. *)
4425       (match fst style with
4426        | RErr -> pr "  return r;\n"
4427        | RInt _ ->
4428            pr "  if (r == -1) return -1;\n";
4429            pr "  printf (\"%%d\\n\", r);\n";
4430            pr "  return 0;\n"
4431        | RInt64 _ ->
4432            pr "  if (r == -1) return -1;\n";
4433            pr "  printf (\"%%\" PRIi64 \"\\n\", r);\n";
4434            pr "  return 0;\n"
4435        | RBool _ ->
4436            pr "  if (r == -1) return -1;\n";
4437            pr "  if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
4438            pr "  return 0;\n"
4439        | RConstString _ ->
4440            pr "  if (r == NULL) return -1;\n";
4441            pr "  printf (\"%%s\\n\", r);\n";
4442            pr "  return 0;\n"
4443        | RString _ ->
4444            pr "  if (r == NULL) return -1;\n";
4445            pr "  printf (\"%%s\\n\", r);\n";
4446            pr "  free (r);\n";
4447            pr "  return 0;\n"
4448        | RStringList _ ->
4449            pr "  if (r == NULL) return -1;\n";
4450            pr "  print_strings (r);\n";
4451            pr "  free_strings (r);\n";
4452            pr "  return 0;\n"
4453        | RIntBool _ ->
4454            pr "  if (r == NULL) return -1;\n";
4455            pr "  printf (\"%%d, %%s\\n\", r->i,\n";
4456            pr "    r->b ? \"true\" : \"false\");\n";
4457            pr "  guestfs_free_int_bool (r);\n";
4458            pr "  return 0;\n"
4459        | RPVList _ ->
4460            pr "  if (r == NULL) return -1;\n";
4461            pr "  print_pv_list (r);\n";
4462            pr "  guestfs_free_lvm_pv_list (r);\n";
4463            pr "  return 0;\n"
4464        | RVGList _ ->
4465            pr "  if (r == NULL) return -1;\n";
4466            pr "  print_vg_list (r);\n";
4467            pr "  guestfs_free_lvm_vg_list (r);\n";
4468            pr "  return 0;\n"
4469        | RLVList _ ->
4470            pr "  if (r == NULL) return -1;\n";
4471            pr "  print_lv_list (r);\n";
4472            pr "  guestfs_free_lvm_lv_list (r);\n";
4473            pr "  return 0;\n"
4474        | RStat _ ->
4475            pr "  if (r == NULL) return -1;\n";
4476            pr "  print_stat (r);\n";
4477            pr "  free (r);\n";
4478            pr "  return 0;\n"
4479        | RStatVFS _ ->
4480            pr "  if (r == NULL) return -1;\n";
4481            pr "  print_statvfs (r);\n";
4482            pr "  free (r);\n";
4483            pr "  return 0;\n"
4484        | RHashtable _ ->
4485            pr "  if (r == NULL) return -1;\n";
4486            pr "  print_table (r);\n";
4487            pr "  free_strings (r);\n";
4488            pr "  return 0;\n"
4489       );
4490       pr "}\n";
4491       pr "\n"
4492   ) all_functions;
4493
4494   (* run_action function *)
4495   pr "int run_action (const char *cmd, int argc, char *argv[])\n";
4496   pr "{\n";
4497   List.iter (
4498     fun (name, _, _, flags, _, _, _) ->
4499       let name2 = replace_char name '_' '-' in
4500       let alias =
4501         try find_map (function FishAlias n -> Some n | _ -> None) flags
4502         with Not_found -> name in
4503       pr "  if (";
4504       pr "strcasecmp (cmd, \"%s\") == 0" name;
4505       if name <> name2 then
4506         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
4507       if name <> alias then
4508         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
4509       pr ")\n";
4510       pr "    return run_%s (cmd, argc, argv);\n" name;
4511       pr "  else\n";
4512   ) all_functions;
4513   pr "    {\n";
4514   pr "      fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
4515   pr "      return -1;\n";
4516   pr "    }\n";
4517   pr "  return 0;\n";
4518   pr "}\n";
4519   pr "\n"
4520
4521 (* Readline completion for guestfish. *)
4522 and generate_fish_completion () =
4523   generate_header CStyle GPLv2;
4524
4525   let all_functions =
4526     List.filter (
4527       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4528     ) all_functions in
4529
4530   pr "\
4531 #include <config.h>
4532
4533 #include <stdio.h>
4534 #include <stdlib.h>
4535 #include <string.h>
4536
4537 #ifdef HAVE_LIBREADLINE
4538 #include <readline/readline.h>
4539 #endif
4540
4541 #include \"fish.h\"
4542
4543 #ifdef HAVE_LIBREADLINE
4544
4545 static const char *const commands[] = {
4546 ";
4547
4548   (* Get the commands and sort them, including the aliases. *)
4549   let commands =
4550     List.map (
4551       fun (name, _, _, flags, _, _, _) ->
4552         let name2 = replace_char name '_' '-' in
4553         let alias =
4554           try find_map (function FishAlias n -> Some n | _ -> None) flags
4555           with Not_found -> name in
4556
4557         if name <> alias then [name2; alias] else [name2]
4558     ) all_functions in
4559   let commands = List.flatten commands in
4560   let commands = List.sort compare commands in
4561
4562   List.iter (pr "  \"%s\",\n") commands;
4563
4564   pr "  NULL
4565 };
4566
4567 static char *
4568 generator (const char *text, int state)
4569 {
4570   static int index, len;
4571   const char *name;
4572
4573   if (!state) {
4574     index = 0;
4575     len = strlen (text);
4576   }
4577
4578   while ((name = commands[index]) != NULL) {
4579     index++;
4580     if (strncasecmp (name, text, len) == 0)
4581       return strdup (name);
4582   }
4583
4584   return NULL;
4585 }
4586
4587 #endif /* HAVE_LIBREADLINE */
4588
4589 char **do_completion (const char *text, int start, int end)
4590 {
4591   char **matches = NULL;
4592
4593 #ifdef HAVE_LIBREADLINE
4594   if (start == 0)
4595     matches = rl_completion_matches (text, generator);
4596 #endif
4597
4598   return matches;
4599 }
4600 ";
4601
4602 (* Generate the POD documentation for guestfish. *)
4603 and generate_fish_actions_pod () =
4604   let all_functions_sorted =
4605     List.filter (
4606       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4607     ) all_functions_sorted in
4608
4609   let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
4610
4611   List.iter (
4612     fun (name, style, _, flags, _, _, longdesc) ->
4613       let longdesc =
4614         Str.global_substitute rex (
4615           fun s ->
4616             let sub =
4617               try Str.matched_group 1 s
4618               with Not_found ->
4619                 failwithf "error substituting C<guestfs_...> in longdesc of function %s" name in
4620             "C<" ^ replace_char sub '_' '-' ^ ">"
4621         ) longdesc in
4622       let name = replace_char name '_' '-' in
4623       let alias =
4624         try find_map (function FishAlias n -> Some n | _ -> None) flags
4625         with Not_found -> name in
4626
4627       pr "=head2 %s" name;
4628       if name <> alias then
4629         pr " | %s" alias;
4630       pr "\n";
4631       pr "\n";
4632       pr " %s" name;
4633       List.iter (
4634         function
4635         | String n -> pr " %s" n
4636         | OptString n -> pr " %s" n
4637         | StringList n -> pr " '%s ...'" n
4638         | Bool _ -> pr " true|false"
4639         | Int n -> pr " %s" n
4640         | FileIn n | FileOut n -> pr " (%s|-)" n
4641       ) (snd style);
4642       pr "\n";
4643       pr "\n";
4644       pr "%s\n\n" longdesc;
4645
4646       if List.exists (function FileIn _ | FileOut _ -> true
4647                       | _ -> false) (snd style) then
4648         pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
4649
4650       if List.mem ProtocolLimitWarning flags then
4651         pr "%s\n\n" protocol_limit_warning;
4652
4653       if List.mem DangerWillRobinson flags then
4654         pr "%s\n\n" danger_will_robinson
4655   ) all_functions_sorted
4656
4657 (* Generate a C function prototype. *)
4658 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
4659     ?(single_line = false) ?(newline = false) ?(in_daemon = false)
4660     ?(prefix = "")
4661     ?handle name style =
4662   if extern then pr "extern ";
4663   if static then pr "static ";
4664   (match fst style with
4665    | RErr -> pr "int "
4666    | RInt _ -> pr "int "
4667    | RInt64 _ -> pr "int64_t "
4668    | RBool _ -> pr "int "
4669    | RConstString _ -> pr "const char *"
4670    | RString _ -> pr "char *"
4671    | RStringList _ | RHashtable _ -> pr "char **"
4672    | RIntBool _ ->
4673        if not in_daemon then pr "struct guestfs_int_bool *"
4674        else pr "guestfs_%s_ret *" name
4675    | RPVList _ ->
4676        if not in_daemon then pr "struct guestfs_lvm_pv_list *"
4677        else pr "guestfs_lvm_int_pv_list *"
4678    | RVGList _ ->
4679        if not in_daemon then pr "struct guestfs_lvm_vg_list *"
4680        else pr "guestfs_lvm_int_vg_list *"
4681    | RLVList _ ->
4682        if not in_daemon then pr "struct guestfs_lvm_lv_list *"
4683        else pr "guestfs_lvm_int_lv_list *"
4684    | RStat _ ->
4685        if not in_daemon then pr "struct guestfs_stat *"
4686        else pr "guestfs_int_stat *"
4687    | RStatVFS _ ->
4688        if not in_daemon then pr "struct guestfs_statvfs *"
4689        else pr "guestfs_int_statvfs *"
4690   );
4691   pr "%s%s (" prefix name;
4692   if handle = None && List.length (snd style) = 0 then
4693     pr "void"
4694   else (
4695     let comma = ref false in
4696     (match handle with
4697      | None -> ()
4698      | Some handle -> pr "guestfs_h *%s" handle; comma := true
4699     );
4700     let next () =
4701       if !comma then (
4702         if single_line then pr ", " else pr ",\n\t\t"
4703       );
4704       comma := true
4705     in
4706     List.iter (
4707       function
4708       | String n
4709       | OptString n -> next (); pr "const char *%s" n
4710       | StringList n -> next (); pr "char * const* const %s" n
4711       | Bool n -> next (); pr "int %s" n
4712       | Int n -> next (); pr "int %s" n
4713       | FileIn n
4714       | FileOut n ->
4715           if not in_daemon then (next (); pr "const char *%s" n)
4716     ) (snd style);
4717   );
4718   pr ")";
4719   if semicolon then pr ";";
4720   if newline then pr "\n"
4721
4722 (* Generate C call arguments, eg "(handle, foo, bar)" *)
4723 and generate_call_args ?handle args =
4724   pr "(";
4725   let comma = ref false in
4726   (match handle with
4727    | None -> ()
4728    | Some handle -> pr "%s" handle; comma := true
4729   );
4730   List.iter (
4731     fun arg ->
4732       if !comma then pr ", ";
4733       comma := true;
4734       pr "%s" (name_of_argt arg)
4735   ) args;
4736   pr ")"
4737
4738 (* Generate the OCaml bindings interface. *)
4739 and generate_ocaml_mli () =
4740   generate_header OCamlStyle LGPLv2;
4741
4742   pr "\
4743 (** For API documentation you should refer to the C API
4744     in the guestfs(3) manual page.  The OCaml API uses almost
4745     exactly the same calls. *)
4746
4747 type t
4748 (** A [guestfs_h] handle. *)
4749
4750 exception Error of string
4751 (** This exception is raised when there is an error. *)
4752
4753 val create : unit -> t
4754
4755 val close : t -> unit
4756 (** Handles are closed by the garbage collector when they become
4757     unreferenced, but callers can also call this in order to
4758     provide predictable cleanup. *)
4759
4760 ";
4761   generate_ocaml_lvm_structure_decls ();
4762
4763   generate_ocaml_stat_structure_decls ();
4764
4765   (* The actions. *)
4766   List.iter (
4767     fun (name, style, _, _, _, shortdesc, _) ->
4768       generate_ocaml_prototype name style;
4769       pr "(** %s *)\n" shortdesc;
4770       pr "\n"
4771   ) all_functions
4772
4773 (* Generate the OCaml bindings implementation. *)
4774 and generate_ocaml_ml () =
4775   generate_header OCamlStyle LGPLv2;
4776
4777   pr "\
4778 type t
4779 exception Error of string
4780 external create : unit -> t = \"ocaml_guestfs_create\"
4781 external close : t -> unit = \"ocaml_guestfs_close\"
4782
4783 let () =
4784   Callback.register_exception \"ocaml_guestfs_error\" (Error \"\")
4785
4786 ";
4787
4788   generate_ocaml_lvm_structure_decls ();
4789
4790   generate_ocaml_stat_structure_decls ();
4791
4792   (* The actions. *)
4793   List.iter (
4794     fun (name, style, _, _, _, shortdesc, _) ->
4795       generate_ocaml_prototype ~is_external:true name style;
4796   ) all_functions
4797
4798 (* Generate the OCaml bindings C implementation. *)
4799 and generate_ocaml_c () =
4800   generate_header CStyle LGPLv2;
4801
4802   pr "\
4803 #include <stdio.h>
4804 #include <stdlib.h>
4805 #include <string.h>
4806
4807 #include <caml/config.h>
4808 #include <caml/alloc.h>
4809 #include <caml/callback.h>
4810 #include <caml/fail.h>
4811 #include <caml/memory.h>
4812 #include <caml/mlvalues.h>
4813 #include <caml/signals.h>
4814
4815 #include <guestfs.h>
4816
4817 #include \"guestfs_c.h\"
4818
4819 /* Copy a hashtable of string pairs into an assoc-list.  We return
4820  * the list in reverse order, but hashtables aren't supposed to be
4821  * ordered anyway.
4822  */
4823 static CAMLprim value
4824 copy_table (char * const * argv)
4825 {
4826   CAMLparam0 ();
4827   CAMLlocal5 (rv, pairv, kv, vv, cons);
4828   int i;
4829
4830   rv = Val_int (0);
4831   for (i = 0; argv[i] != NULL; i += 2) {
4832     kv = caml_copy_string (argv[i]);
4833     vv = caml_copy_string (argv[i+1]);
4834     pairv = caml_alloc (2, 0);
4835     Store_field (pairv, 0, kv);
4836     Store_field (pairv, 1, vv);
4837     cons = caml_alloc (2, 0);
4838     Store_field (cons, 1, rv);
4839     rv = cons;
4840     Store_field (cons, 0, pairv);
4841   }
4842
4843   CAMLreturn (rv);
4844 }
4845
4846 ";
4847
4848   (* LVM struct copy functions. *)
4849   List.iter (
4850     fun (typ, cols) ->
4851       let has_optpercent_col =
4852         List.exists (function (_, `OptPercent) -> true | _ -> false) cols in
4853
4854       pr "static CAMLprim value\n";
4855       pr "copy_lvm_%s (const struct guestfs_lvm_%s *%s)\n" typ typ typ;
4856       pr "{\n";
4857       pr "  CAMLparam0 ();\n";
4858       if has_optpercent_col then
4859         pr "  CAMLlocal3 (rv, v, v2);\n"
4860       else
4861         pr "  CAMLlocal2 (rv, v);\n";
4862       pr "\n";
4863       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
4864       iteri (
4865         fun i col ->
4866           (match col with
4867            | name, `String ->
4868                pr "  v = caml_copy_string (%s->%s);\n" typ name
4869            | name, `UUID ->
4870                pr "  v = caml_alloc_string (32);\n";
4871                pr "  memcpy (String_val (v), %s->%s, 32);\n" typ name
4872            | name, `Bytes
4873            | name, `Int ->
4874                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
4875            | name, `OptPercent ->
4876                pr "  if (%s->%s >= 0) { /* Some %s */\n" typ name name;
4877                pr "    v2 = caml_copy_double (%s->%s);\n" typ name;
4878                pr "    v = caml_alloc (1, 0);\n";
4879                pr "    Store_field (v, 0, v2);\n";
4880                pr "  } else /* None */\n";
4881                pr "    v = Val_int (0);\n";
4882           );
4883           pr "  Store_field (rv, %d, v);\n" i
4884       ) cols;
4885       pr "  CAMLreturn (rv);\n";
4886       pr "}\n";
4887       pr "\n";
4888
4889       pr "static CAMLprim value\n";
4890       pr "copy_lvm_%s_list (const struct guestfs_lvm_%s_list *%ss)\n"
4891         typ typ typ;
4892       pr "{\n";
4893       pr "  CAMLparam0 ();\n";
4894       pr "  CAMLlocal2 (rv, v);\n";
4895       pr "  int i;\n";
4896       pr "\n";
4897       pr "  if (%ss->len == 0)\n" typ;
4898       pr "    CAMLreturn (Atom (0));\n";
4899       pr "  else {\n";
4900       pr "    rv = caml_alloc (%ss->len, 0);\n" typ;
4901       pr "    for (i = 0; i < %ss->len; ++i) {\n" typ;
4902       pr "      v = copy_lvm_%s (&%ss->val[i]);\n" typ typ;
4903       pr "      caml_modify (&Field (rv, i), v);\n";
4904       pr "    }\n";
4905       pr "    CAMLreturn (rv);\n";
4906       pr "  }\n";
4907       pr "}\n";
4908       pr "\n";
4909   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
4910
4911   (* Stat copy functions. *)
4912   List.iter (
4913     fun (typ, cols) ->
4914       pr "static CAMLprim value\n";
4915       pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ;
4916       pr "{\n";
4917       pr "  CAMLparam0 ();\n";
4918       pr "  CAMLlocal2 (rv, v);\n";
4919       pr "\n";
4920       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
4921       iteri (
4922         fun i col ->
4923           (match col with
4924            | name, `Int ->
4925                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
4926           );
4927           pr "  Store_field (rv, %d, v);\n" i
4928       ) cols;
4929       pr "  CAMLreturn (rv);\n";
4930       pr "}\n";
4931       pr "\n";
4932   ) ["stat", stat_cols; "statvfs", statvfs_cols];
4933
4934   (* The wrappers. *)
4935   List.iter (
4936     fun (name, style, _, _, _, _, _) ->
4937       let params =
4938         "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
4939
4940       pr "CAMLprim value\n";
4941       pr "ocaml_guestfs_%s (value %s" name (List.hd params);
4942       List.iter (pr ", value %s") (List.tl params);
4943       pr ")\n";
4944       pr "{\n";
4945
4946       (match params with
4947        | [p1; p2; p3; p4; p5] ->
4948            pr "  CAMLparam5 (%s);\n" (String.concat ", " params)
4949        | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
4950            pr "  CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
4951            pr "  CAMLxparam%d (%s);\n"
4952              (List.length rest) (String.concat ", " rest)
4953        | ps ->
4954            pr "  CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
4955       );
4956       pr "  CAMLlocal1 (rv);\n";
4957       pr "\n";
4958
4959       pr "  guestfs_h *g = Guestfs_val (gv);\n";
4960       pr "  if (g == NULL)\n";
4961       pr "    caml_failwith (\"%s: used handle after closing it\");\n" name;
4962       pr "\n";
4963
4964       List.iter (
4965         function
4966         | String n
4967         | FileIn n
4968         | FileOut n ->
4969             pr "  const char *%s = String_val (%sv);\n" n n
4970         | OptString n ->
4971             pr "  const char *%s =\n" n;
4972             pr "    %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n"
4973               n n
4974         | StringList n ->
4975             pr "  char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n
4976         | Bool n ->
4977             pr "  int %s = Bool_val (%sv);\n" n n
4978         | Int n ->
4979             pr "  int %s = Int_val (%sv);\n" n n
4980       ) (snd style);
4981       let error_code =
4982         match fst style with
4983         | RErr -> pr "  int r;\n"; "-1"
4984         | RInt _ -> pr "  int r;\n"; "-1"
4985         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
4986         | RBool _ -> pr "  int r;\n"; "-1"
4987         | RConstString _ -> pr "  const char *r;\n"; "NULL"
4988         | RString _ -> pr "  char *r;\n"; "NULL"
4989         | RStringList _ ->
4990             pr "  int i;\n";
4991             pr "  char **r;\n";
4992             "NULL"
4993         | RIntBool _ ->
4994             pr "  struct guestfs_int_bool *r;\n"; "NULL"
4995         | RPVList _ ->
4996             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
4997         | RVGList _ ->
4998             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
4999         | RLVList _ ->
5000             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
5001         | RStat _ ->
5002             pr "  struct guestfs_stat *r;\n"; "NULL"
5003         | RStatVFS _ ->
5004             pr "  struct guestfs_statvfs *r;\n"; "NULL"
5005         | RHashtable _ ->
5006             pr "  int i;\n";
5007             pr "  char **r;\n";
5008             "NULL" in
5009       pr "\n";
5010
5011       pr "  caml_enter_blocking_section ();\n";
5012       pr "  r = guestfs_%s " name;
5013       generate_call_args ~handle:"g" (snd style);
5014       pr ";\n";
5015       pr "  caml_leave_blocking_section ();\n";
5016
5017       List.iter (
5018         function
5019         | StringList n ->
5020             pr "  ocaml_guestfs_free_strings (%s);\n" n;
5021         | String _ | OptString _ | Bool _ | Int _ | FileIn _ | FileOut _ -> ()
5022       ) (snd style);
5023
5024       pr "  if (r == %s)\n" error_code;
5025       pr "    ocaml_guestfs_raise_error (g, \"%s\");\n" name;
5026       pr "\n";
5027
5028       (match fst style with
5029        | RErr -> pr "  rv = Val_unit;\n"
5030        | RInt _ -> pr "  rv = Val_int (r);\n"
5031        | RInt64 _ ->
5032            pr "  rv = caml_copy_int64 (r);\n"
5033        | RBool _ -> pr "  rv = Val_bool (r);\n"
5034        | RConstString _ -> pr "  rv = caml_copy_string (r);\n"
5035        | RString _ ->
5036            pr "  rv = caml_copy_string (r);\n";
5037            pr "  free (r);\n"
5038        | RStringList _ ->
5039            pr "  rv = caml_copy_string_array ((const char **) r);\n";
5040            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
5041            pr "  free (r);\n"
5042        | RIntBool _ ->
5043            pr "  rv = caml_alloc (2, 0);\n";
5044            pr "  Store_field (rv, 0, Val_int (r->i));\n";
5045            pr "  Store_field (rv, 1, Val_bool (r->b));\n";
5046            pr "  guestfs_free_int_bool (r);\n";
5047        | RPVList _ ->
5048            pr "  rv = copy_lvm_pv_list (r);\n";
5049            pr "  guestfs_free_lvm_pv_list (r);\n";
5050        | RVGList _ ->
5051            pr "  rv = copy_lvm_vg_list (r);\n";
5052            pr "  guestfs_free_lvm_vg_list (r);\n";
5053        | RLVList _ ->
5054            pr "  rv = copy_lvm_lv_list (r);\n";
5055            pr "  guestfs_free_lvm_lv_list (r);\n";
5056        | RStat _ ->
5057            pr "  rv = copy_stat (r);\n";
5058            pr "  free (r);\n";
5059        | RStatVFS _ ->
5060            pr "  rv = copy_statvfs (r);\n";
5061            pr "  free (r);\n";
5062        | RHashtable _ ->
5063            pr "  rv = copy_table (r);\n";
5064            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
5065            pr "  free (r);\n";
5066       );
5067
5068       pr "  CAMLreturn (rv);\n";
5069       pr "}\n";
5070       pr "\n";
5071
5072       if List.length params > 5 then (
5073         pr "CAMLprim value\n";
5074         pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
5075         pr "{\n";
5076         pr "  return ocaml_guestfs_%s (argv[0]" name;
5077         iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
5078         pr ");\n";
5079         pr "}\n";
5080         pr "\n"
5081       )
5082   ) all_functions
5083
5084 and generate_ocaml_lvm_structure_decls () =
5085   List.iter (
5086     fun (typ, cols) ->
5087       pr "type lvm_%s = {\n" typ;
5088       List.iter (
5089         function
5090         | name, `String -> pr "  %s : string;\n" name
5091         | name, `UUID -> pr "  %s : string;\n" name
5092         | name, `Bytes -> pr "  %s : int64;\n" name
5093         | name, `Int -> pr "  %s : int64;\n" name
5094         | name, `OptPercent -> pr "  %s : float option;\n" name
5095       ) cols;
5096       pr "}\n";
5097       pr "\n"
5098   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
5099
5100 and generate_ocaml_stat_structure_decls () =
5101   List.iter (
5102     fun (typ, cols) ->
5103       pr "type %s = {\n" typ;
5104       List.iter (
5105         function
5106         | name, `Int -> pr "  %s : int64;\n" name
5107       ) cols;
5108       pr "}\n";
5109       pr "\n"
5110   ) ["stat", stat_cols; "statvfs", statvfs_cols]
5111
5112 and generate_ocaml_prototype ?(is_external = false) name style =
5113   if is_external then pr "external " else pr "val ";
5114   pr "%s : t -> " name;
5115   List.iter (
5116     function
5117     | String _ | FileIn _ | FileOut _ -> pr "string -> "
5118     | OptString _ -> pr "string option -> "
5119     | StringList _ -> pr "string array -> "
5120     | Bool _ -> pr "bool -> "
5121     | Int _ -> pr "int -> "
5122   ) (snd style);
5123   (match fst style with
5124    | RErr -> pr "unit" (* all errors are turned into exceptions *)
5125    | RInt _ -> pr "int"
5126    | RInt64 _ -> pr "int64"
5127    | RBool _ -> pr "bool"
5128    | RConstString _ -> pr "string"
5129    | RString _ -> pr "string"
5130    | RStringList _ -> pr "string array"
5131    | RIntBool _ -> pr "int * bool"
5132    | RPVList _ -> pr "lvm_pv array"
5133    | RVGList _ -> pr "lvm_vg array"
5134    | RLVList _ -> pr "lvm_lv array"
5135    | RStat _ -> pr "stat"
5136    | RStatVFS _ -> pr "statvfs"
5137    | RHashtable _ -> pr "(string * string) list"
5138   );
5139   if is_external then (
5140     pr " = ";
5141     if List.length (snd style) + 1 > 5 then
5142       pr "\"ocaml_guestfs_%s_byte\" " name;
5143     pr "\"ocaml_guestfs_%s\"" name
5144   );
5145   pr "\n"
5146
5147 (* Generate Perl xs code, a sort of crazy variation of C with macros. *)
5148 and generate_perl_xs () =
5149   generate_header CStyle LGPLv2;
5150
5151   pr "\
5152 #include \"EXTERN.h\"
5153 #include \"perl.h\"
5154 #include \"XSUB.h\"
5155
5156 #include <guestfs.h>
5157
5158 #ifndef PRId64
5159 #define PRId64 \"lld\"
5160 #endif
5161
5162 static SV *
5163 my_newSVll(long long val) {
5164 #ifdef USE_64_BIT_ALL
5165   return newSViv(val);
5166 #else
5167   char buf[100];
5168   int len;
5169   len = snprintf(buf, 100, \"%%\" PRId64, val);
5170   return newSVpv(buf, len);
5171 #endif
5172 }
5173
5174 #ifndef PRIu64
5175 #define PRIu64 \"llu\"
5176 #endif
5177
5178 static SV *
5179 my_newSVull(unsigned long long val) {
5180 #ifdef USE_64_BIT_ALL
5181   return newSVuv(val);
5182 #else
5183   char buf[100];
5184   int len;
5185   len = snprintf(buf, 100, \"%%\" PRIu64, val);
5186   return newSVpv(buf, len);
5187 #endif
5188 }
5189
5190 /* http://www.perlmonks.org/?node_id=680842 */
5191 static char **
5192 XS_unpack_charPtrPtr (SV *arg) {
5193   char **ret;
5194   AV *av;
5195   I32 i;
5196
5197   if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV)
5198     croak (\"array reference expected\");
5199
5200   av = (AV *)SvRV (arg);
5201   ret = malloc ((av_len (av) + 1 + 1) * sizeof (char *));
5202   if (!ret)
5203     croak (\"malloc failed\");
5204
5205   for (i = 0; i <= av_len (av); i++) {
5206     SV **elem = av_fetch (av, i, 0);
5207
5208     if (!elem || !*elem)
5209       croak (\"missing element in list\");
5210
5211     ret[i] = SvPV_nolen (*elem);
5212   }
5213
5214   ret[i] = NULL;
5215
5216   return ret;
5217 }
5218
5219 MODULE = Sys::Guestfs  PACKAGE = Sys::Guestfs
5220
5221 PROTOTYPES: ENABLE
5222
5223 guestfs_h *
5224 _create ()
5225    CODE:
5226       RETVAL = guestfs_create ();
5227       if (!RETVAL)
5228         croak (\"could not create guestfs handle\");
5229       guestfs_set_error_handler (RETVAL, NULL, NULL);
5230  OUTPUT:
5231       RETVAL
5232
5233 void
5234 DESTROY (g)
5235       guestfs_h *g;
5236  PPCODE:
5237       guestfs_close (g);
5238
5239 ";
5240
5241   List.iter (
5242     fun (name, style, _, _, _, _, _) ->
5243       (match fst style with
5244        | RErr -> pr "void\n"
5245        | RInt _ -> pr "SV *\n"
5246        | RInt64 _ -> pr "SV *\n"
5247        | RBool _ -> pr "SV *\n"
5248        | RConstString _ -> pr "SV *\n"
5249        | RString _ -> pr "SV *\n"
5250        | RStringList _
5251        | RIntBool _
5252        | RPVList _ | RVGList _ | RLVList _
5253        | RStat _ | RStatVFS _
5254        | RHashtable _ ->
5255            pr "void\n" (* all lists returned implictly on the stack *)
5256       );
5257       (* Call and arguments. *)
5258       pr "%s " name;
5259       generate_call_args ~handle:"g" (snd style);
5260       pr "\n";
5261       pr "      guestfs_h *g;\n";
5262       List.iter (
5263         function
5264         | String n | FileIn n | FileOut n -> pr "      char *%s;\n" n
5265         | OptString n -> pr "      char *%s;\n" n
5266         | StringList n -> pr "      char **%s;\n" n
5267         | Bool n -> pr "      int %s;\n" n
5268         | Int n -> pr "      int %s;\n" n
5269       ) (snd style);
5270
5271       let do_cleanups () =
5272         List.iter (
5273           function
5274           | String _ | OptString _ | Bool _ | Int _
5275           | FileIn _ | FileOut _ -> ()
5276           | StringList n -> pr "      free (%s);\n" n
5277         ) (snd style)
5278       in
5279
5280       (* Code. *)
5281       (match fst style with
5282        | RErr ->
5283            pr "PREINIT:\n";
5284            pr "      int r;\n";
5285            pr " PPCODE:\n";
5286            pr "      r = guestfs_%s " name;
5287            generate_call_args ~handle:"g" (snd style);
5288            pr ";\n";
5289            do_cleanups ();
5290            pr "      if (r == -1)\n";
5291            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5292        | RInt n
5293        | RBool n ->
5294            pr "PREINIT:\n";
5295            pr "      int %s;\n" n;
5296            pr "   CODE:\n";
5297            pr "      %s = guestfs_%s " n name;
5298            generate_call_args ~handle:"g" (snd style);
5299            pr ";\n";
5300            do_cleanups ();
5301            pr "      if (%s == -1)\n" n;
5302            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5303            pr "      RETVAL = newSViv (%s);\n" n;
5304            pr " OUTPUT:\n";
5305            pr "      RETVAL\n"
5306        | RInt64 n ->
5307            pr "PREINIT:\n";
5308            pr "      int64_t %s;\n" n;
5309            pr "   CODE:\n";
5310            pr "      %s = guestfs_%s " n name;
5311            generate_call_args ~handle:"g" (snd style);
5312            pr ";\n";
5313            do_cleanups ();
5314            pr "      if (%s == -1)\n" n;
5315            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5316            pr "      RETVAL = my_newSVll (%s);\n" n;
5317            pr " OUTPUT:\n";
5318            pr "      RETVAL\n"
5319        | RConstString n ->
5320            pr "PREINIT:\n";
5321            pr "      const char *%s;\n" n;
5322            pr "   CODE:\n";
5323            pr "      %s = guestfs_%s " n name;
5324            generate_call_args ~handle:"g" (snd style);
5325            pr ";\n";
5326            do_cleanups ();
5327            pr "      if (%s == NULL)\n" n;
5328            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5329            pr "      RETVAL = newSVpv (%s, 0);\n" n;
5330            pr " OUTPUT:\n";
5331            pr "      RETVAL\n"
5332        | RString n ->
5333            pr "PREINIT:\n";
5334            pr "      char *%s;\n" n;
5335            pr "   CODE:\n";
5336            pr "      %s = guestfs_%s " n name;
5337            generate_call_args ~handle:"g" (snd style);
5338            pr ";\n";
5339            do_cleanups ();
5340            pr "      if (%s == NULL)\n" n;
5341            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5342            pr "      RETVAL = newSVpv (%s, 0);\n" n;
5343            pr "      free (%s);\n" n;
5344            pr " OUTPUT:\n";
5345            pr "      RETVAL\n"
5346        | RStringList n | RHashtable n ->
5347            pr "PREINIT:\n";
5348            pr "      char **%s;\n" n;
5349            pr "      int i, n;\n";
5350            pr " PPCODE:\n";
5351            pr "      %s = guestfs_%s " n name;
5352            generate_call_args ~handle:"g" (snd style);
5353            pr ";\n";
5354            do_cleanups ();
5355            pr "      if (%s == NULL)\n" n;
5356            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5357            pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
5358            pr "      EXTEND (SP, n);\n";
5359            pr "      for (i = 0; i < n; ++i) {\n";
5360            pr "        PUSHs (sv_2mortal (newSVpv (%s[i], 0)));\n" n;
5361            pr "        free (%s[i]);\n" n;
5362            pr "      }\n";
5363            pr "      free (%s);\n" n;
5364        | RIntBool _ ->
5365            pr "PREINIT:\n";
5366            pr "      struct guestfs_int_bool *r;\n";
5367            pr " PPCODE:\n";
5368            pr "      r = guestfs_%s " name;
5369            generate_call_args ~handle:"g" (snd style);
5370            pr ";\n";
5371            do_cleanups ();
5372            pr "      if (r == NULL)\n";
5373            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5374            pr "      EXTEND (SP, 2);\n";
5375            pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
5376            pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
5377            pr "      guestfs_free_int_bool (r);\n";
5378        | RPVList n ->
5379            generate_perl_lvm_code "pv" pv_cols name style n do_cleanups
5380        | RVGList n ->
5381            generate_perl_lvm_code "vg" vg_cols name style n do_cleanups
5382        | RLVList n ->
5383            generate_perl_lvm_code "lv" lv_cols name style n do_cleanups
5384        | RStat n ->
5385            generate_perl_stat_code "stat" stat_cols name style n do_cleanups
5386        | RStatVFS n ->
5387            generate_perl_stat_code
5388              "statvfs" statvfs_cols name style n do_cleanups
5389       );
5390
5391       pr "\n"
5392   ) all_functions
5393
5394 and generate_perl_lvm_code typ cols name style n do_cleanups =
5395   pr "PREINIT:\n";
5396   pr "      struct guestfs_lvm_%s_list *%s;\n" typ n;
5397   pr "      int i;\n";
5398   pr "      HV *hv;\n";
5399   pr " PPCODE:\n";
5400   pr "      %s = guestfs_%s " n name;
5401   generate_call_args ~handle:"g" (snd style);
5402   pr ";\n";
5403   do_cleanups ();
5404   pr "      if (%s == NULL)\n" n;
5405   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5406   pr "      EXTEND (SP, %s->len);\n" n;
5407   pr "      for (i = 0; i < %s->len; ++i) {\n" n;
5408   pr "        hv = newHV ();\n";
5409   List.iter (
5410     function
5411     | name, `String ->
5412         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 0), 0);\n"
5413           name (String.length name) n name
5414     | name, `UUID ->
5415         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 32), 0);\n"
5416           name (String.length name) n name
5417     | name, `Bytes ->
5418         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n"
5419           name (String.length name) n name
5420     | name, `Int ->
5421         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVll (%s->val[i].%s), 0);\n"
5422           name (String.length name) n name
5423     | name, `OptPercent ->
5424         pr "        (void) hv_store (hv, \"%s\", %d, newSVnv (%s->val[i].%s), 0);\n"
5425           name (String.length name) n name
5426   ) cols;
5427   pr "        PUSHs (sv_2mortal ((SV *) hv));\n";
5428   pr "      }\n";
5429   pr "      guestfs_free_lvm_%s_list (%s);\n" typ n
5430
5431 and generate_perl_stat_code typ cols name style n do_cleanups =
5432   pr "PREINIT:\n";
5433   pr "      struct guestfs_%s *%s;\n" typ n;
5434   pr " PPCODE:\n";
5435   pr "      %s = guestfs_%s " n name;
5436   generate_call_args ~handle:"g" (snd style);
5437   pr ";\n";
5438   do_cleanups ();
5439   pr "      if (%s == NULL)\n" n;
5440   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5441   pr "      EXTEND (SP, %d);\n" (List.length cols);
5442   List.iter (
5443     function
5444     | name, `Int ->
5445         pr "      PUSHs (sv_2mortal (my_newSVll (%s->%s)));\n" n name
5446   ) cols;
5447   pr "      free (%s);\n" n
5448
5449 (* Generate Sys/Guestfs.pm. *)
5450 and generate_perl_pm () =
5451   generate_header HashStyle LGPLv2;
5452
5453   pr "\
5454 =pod
5455
5456 =head1 NAME
5457
5458 Sys::Guestfs - Perl bindings for libguestfs
5459
5460 =head1 SYNOPSIS
5461
5462  use Sys::Guestfs;
5463  
5464  my $h = Sys::Guestfs->new ();
5465  $h->add_drive ('guest.img');
5466  $h->launch ();
5467  $h->wait_ready ();
5468  $h->mount ('/dev/sda1', '/');
5469  $h->touch ('/hello');
5470  $h->sync ();
5471
5472 =head1 DESCRIPTION
5473
5474 The C<Sys::Guestfs> module provides a Perl XS binding to the
5475 libguestfs API for examining and modifying virtual machine
5476 disk images.
5477
5478 Amongst the things this is good for: making batch configuration
5479 changes to guests, getting disk used/free statistics (see also:
5480 virt-df), migrating between virtualization systems (see also:
5481 virt-p2v), performing partial backups, performing partial guest
5482 clones, cloning guests and changing registry/UUID/hostname info, and
5483 much else besides.
5484
5485 Libguestfs uses Linux kernel and qemu code, and can access any type of
5486 guest filesystem that Linux and qemu can, including but not limited
5487 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
5488 schemes, qcow, qcow2, vmdk.
5489
5490 Libguestfs provides ways to enumerate guest storage (eg. partitions,
5491 LVs, what filesystem is in each LV, etc.).  It can also run commands
5492 in the context of the guest.  Also you can access filesystems over FTP.
5493
5494 =head1 ERRORS
5495
5496 All errors turn into calls to C<croak> (see L<Carp(3)>).
5497
5498 =head1 METHODS
5499
5500 =over 4
5501
5502 =cut
5503
5504 package Sys::Guestfs;
5505
5506 use strict;
5507 use warnings;
5508
5509 require XSLoader;
5510 XSLoader::load ('Sys::Guestfs');
5511
5512 =item $h = Sys::Guestfs->new ();
5513
5514 Create a new guestfs handle.
5515
5516 =cut
5517
5518 sub new {
5519   my $proto = shift;
5520   my $class = ref ($proto) || $proto;
5521
5522   my $self = Sys::Guestfs::_create ();
5523   bless $self, $class;
5524   return $self;
5525 }
5526
5527 ";
5528
5529   (* Actions.  We only need to print documentation for these as
5530    * they are pulled in from the XS code automatically.
5531    *)
5532   List.iter (
5533     fun (name, style, _, flags, _, _, longdesc) ->
5534       let longdesc = replace_str longdesc "C<guestfs_" "C<$h-E<gt>" in
5535       pr "=item ";
5536       generate_perl_prototype name style;
5537       pr "\n\n";
5538       pr "%s\n\n" longdesc;
5539       if List.mem ProtocolLimitWarning flags then
5540         pr "%s\n\n" protocol_limit_warning;
5541       if List.mem DangerWillRobinson flags then
5542         pr "%s\n\n" danger_will_robinson
5543   ) all_functions_sorted;
5544
5545   (* End of file. *)
5546   pr "\
5547 =cut
5548
5549 1;
5550
5551 =back
5552
5553 =head1 COPYRIGHT
5554
5555 Copyright (C) 2009 Red Hat Inc.
5556
5557 =head1 LICENSE
5558
5559 Please see the file COPYING.LIB for the full license.
5560
5561 =head1 SEE ALSO
5562
5563 L<guestfs(3)>, L<guestfish(1)>.
5564
5565 =cut
5566 "
5567
5568 and generate_perl_prototype name style =
5569   (match fst style with
5570    | RErr -> ()
5571    | RBool n
5572    | RInt n
5573    | RInt64 n
5574    | RConstString n
5575    | RString n -> pr "$%s = " n
5576    | RIntBool (n, m) -> pr "($%s, $%s) = " n m
5577    | RStringList n
5578    | RPVList n
5579    | RVGList n
5580    | RLVList n -> pr "@%s = " n
5581    | RStat n
5582    | RStatVFS n
5583    | RHashtable n -> pr "%%%s = " n
5584   );
5585   pr "$h->%s (" name;
5586   let comma = ref false in
5587   List.iter (
5588     fun arg ->
5589       if !comma then pr ", ";
5590       comma := true;
5591       match arg with
5592       | String n | OptString n | Bool n | Int n | FileIn n | FileOut n ->
5593           pr "$%s" n
5594       | StringList n ->
5595           pr "\\@%s" n
5596   ) (snd style);
5597   pr ");"
5598
5599 (* Generate Python C module. *)
5600 and generate_python_c () =
5601   generate_header CStyle LGPLv2;
5602
5603   pr "\
5604 #include <stdio.h>
5605 #include <stdlib.h>
5606 #include <assert.h>
5607
5608 #include <Python.h>
5609
5610 #include \"guestfs.h\"
5611
5612 typedef struct {
5613   PyObject_HEAD
5614   guestfs_h *g;
5615 } Pyguestfs_Object;
5616
5617 static guestfs_h *
5618 get_handle (PyObject *obj)
5619 {
5620   assert (obj);
5621   assert (obj != Py_None);
5622   return ((Pyguestfs_Object *) obj)->g;
5623 }
5624
5625 static PyObject *
5626 put_handle (guestfs_h *g)
5627 {
5628   assert (g);
5629   return
5630     PyCObject_FromVoidPtrAndDesc ((void *) g, (char *) \"guestfs_h\", NULL);
5631 }
5632
5633 /* This list should be freed (but not the strings) after use. */
5634 static const char **
5635 get_string_list (PyObject *obj)
5636 {
5637   int i, len;
5638   const char **r;
5639
5640   assert (obj);
5641
5642   if (!PyList_Check (obj)) {
5643     PyErr_SetString (PyExc_RuntimeError, \"expecting a list parameter\");
5644     return NULL;
5645   }
5646
5647   len = PyList_Size (obj);
5648   r = malloc (sizeof (char *) * (len+1));
5649   if (r == NULL) {
5650     PyErr_SetString (PyExc_RuntimeError, \"get_string_list: out of memory\");
5651     return NULL;
5652   }
5653
5654   for (i = 0; i < len; ++i)
5655     r[i] = PyString_AsString (PyList_GetItem (obj, i));
5656   r[len] = NULL;
5657
5658   return r;
5659 }
5660
5661 static PyObject *
5662 put_string_list (char * const * const argv)
5663 {
5664   PyObject *list;
5665   int argc, i;
5666
5667   for (argc = 0; argv[argc] != NULL; ++argc)
5668     ;
5669
5670   list = PyList_New (argc);
5671   for (i = 0; i < argc; ++i)
5672     PyList_SetItem (list, i, PyString_FromString (argv[i]));
5673
5674   return list;
5675 }
5676
5677 static PyObject *
5678 put_table (char * const * const argv)
5679 {
5680   PyObject *list, *item;
5681   int argc, i;
5682
5683   for (argc = 0; argv[argc] != NULL; ++argc)
5684     ;
5685
5686   list = PyList_New (argc >> 1);
5687   for (i = 0; i < argc; i += 2) {
5688     item = PyTuple_New (2);
5689     PyTuple_SetItem (item, 0, PyString_FromString (argv[i]));
5690     PyTuple_SetItem (item, 1, PyString_FromString (argv[i+1]));
5691     PyList_SetItem (list, i >> 1, item);
5692   }
5693
5694   return list;
5695 }
5696
5697 static void
5698 free_strings (char **argv)
5699 {
5700   int argc;
5701
5702   for (argc = 0; argv[argc] != NULL; ++argc)
5703     free (argv[argc]);
5704   free (argv);
5705 }
5706
5707 static PyObject *
5708 py_guestfs_create (PyObject *self, PyObject *args)
5709 {
5710   guestfs_h *g;
5711
5712   g = guestfs_create ();
5713   if (g == NULL) {
5714     PyErr_SetString (PyExc_RuntimeError,
5715                      \"guestfs.create: failed to allocate handle\");
5716     return NULL;
5717   }
5718   guestfs_set_error_handler (g, NULL, NULL);
5719   return put_handle (g);
5720 }
5721
5722 static PyObject *
5723 py_guestfs_close (PyObject *self, PyObject *args)
5724 {
5725   PyObject *py_g;
5726   guestfs_h *g;
5727
5728   if (!PyArg_ParseTuple (args, (char *) \"O:guestfs_close\", &py_g))
5729     return NULL;
5730   g = get_handle (py_g);
5731
5732   guestfs_close (g);
5733
5734   Py_INCREF (Py_None);
5735   return Py_None;
5736 }
5737
5738 ";
5739
5740   (* LVM structures, turned into Python dictionaries. *)
5741   List.iter (
5742     fun (typ, cols) ->
5743       pr "static PyObject *\n";
5744       pr "put_lvm_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
5745       pr "{\n";
5746       pr "  PyObject *dict;\n";
5747       pr "\n";
5748       pr "  dict = PyDict_New ();\n";
5749       List.iter (
5750         function
5751         | name, `String ->
5752             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5753             pr "                        PyString_FromString (%s->%s));\n"
5754               typ name
5755         | name, `UUID ->
5756             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5757             pr "                        PyString_FromStringAndSize (%s->%s, 32));\n"
5758               typ name
5759         | name, `Bytes ->
5760             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5761             pr "                        PyLong_FromUnsignedLongLong (%s->%s));\n"
5762               typ name
5763         | name, `Int ->
5764             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5765             pr "                        PyLong_FromLongLong (%s->%s));\n"
5766               typ name
5767         | name, `OptPercent ->
5768             pr "  if (%s->%s >= 0)\n" typ name;
5769             pr "    PyDict_SetItemString (dict, \"%s\",\n" name;
5770             pr "                          PyFloat_FromDouble ((double) %s->%s));\n"
5771               typ name;
5772             pr "  else {\n";
5773             pr "    Py_INCREF (Py_None);\n";
5774             pr "    PyDict_SetItemString (dict, \"%s\", Py_None);" name;
5775             pr "  }\n"
5776       ) cols;
5777       pr "  return dict;\n";
5778       pr "};\n";
5779       pr "\n";
5780
5781       pr "static PyObject *\n";
5782       pr "put_lvm_%s_list (struct guestfs_lvm_%s_list *%ss)\n" typ typ typ;
5783       pr "{\n";
5784       pr "  PyObject *list;\n";
5785       pr "  int i;\n";
5786       pr "\n";
5787       pr "  list = PyList_New (%ss->len);\n" typ;
5788       pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
5789       pr "    PyList_SetItem (list, i, put_lvm_%s (&%ss->val[i]));\n" typ typ;
5790       pr "  return list;\n";
5791       pr "};\n";
5792       pr "\n"
5793   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
5794
5795   (* Stat structures, turned into Python dictionaries. *)
5796   List.iter (
5797     fun (typ, cols) ->
5798       pr "static PyObject *\n";
5799       pr "put_%s (struct guestfs_%s *%s)\n" typ typ typ;
5800       pr "{\n";
5801       pr "  PyObject *dict;\n";
5802       pr "\n";
5803       pr "  dict = PyDict_New ();\n";
5804       List.iter (
5805         function
5806         | name, `Int ->
5807             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5808             pr "                        PyLong_FromLongLong (%s->%s));\n"
5809               typ name
5810       ) cols;
5811       pr "  return dict;\n";
5812       pr "};\n";
5813       pr "\n";
5814   ) ["stat", stat_cols; "statvfs", statvfs_cols];
5815
5816   (* Python wrapper functions. *)
5817   List.iter (
5818     fun (name, style, _, _, _, _, _) ->
5819       pr "static PyObject *\n";
5820       pr "py_guestfs_%s (PyObject *self, PyObject *args)\n" name;
5821       pr "{\n";
5822
5823       pr "  PyObject *py_g;\n";
5824       pr "  guestfs_h *g;\n";
5825       pr "  PyObject *py_r;\n";
5826
5827       let error_code =
5828         match fst style with
5829         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
5830         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
5831         | RConstString _ -> pr "  const char *r;\n"; "NULL"
5832         | RString _ -> pr "  char *r;\n"; "NULL"
5833         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
5834         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
5835         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
5836         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
5837         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
5838         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
5839         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
5840
5841       List.iter (
5842         function
5843         | String n | FileIn n | FileOut n -> pr "  const char *%s;\n" n
5844         | OptString n -> pr "  const char *%s;\n" n
5845         | StringList n ->
5846             pr "  PyObject *py_%s;\n" n;
5847             pr "  const char **%s;\n" n
5848         | Bool n -> pr "  int %s;\n" n
5849         | Int n -> pr "  int %s;\n" n
5850       ) (snd style);
5851
5852       pr "\n";
5853
5854       (* Convert the parameters. *)
5855       pr "  if (!PyArg_ParseTuple (args, (char *) \"O";
5856       List.iter (
5857         function
5858         | String _ | FileIn _ | FileOut _ -> pr "s"
5859         | OptString _ -> pr "z"
5860         | StringList _ -> pr "O"
5861         | Bool _ -> pr "i" (* XXX Python has booleans? *)
5862         | Int _ -> pr "i"
5863       ) (snd style);
5864       pr ":guestfs_%s\",\n" name;
5865       pr "                         &py_g";
5866       List.iter (
5867         function
5868         | String n | FileIn n | FileOut n -> pr ", &%s" n
5869         | OptString n -> pr ", &%s" n
5870         | StringList n -> pr ", &py_%s" n
5871         | Bool n -> pr ", &%s" n
5872         | Int n -> pr ", &%s" n
5873       ) (snd style);
5874
5875       pr "))\n";
5876       pr "    return NULL;\n";
5877
5878       pr "  g = get_handle (py_g);\n";
5879       List.iter (
5880         function
5881         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5882         | StringList n ->
5883             pr "  %s = get_string_list (py_%s);\n" n n;
5884             pr "  if (!%s) return NULL;\n" n
5885       ) (snd style);
5886
5887       pr "\n";
5888
5889       pr "  r = guestfs_%s " name;
5890       generate_call_args ~handle:"g" (snd style);
5891       pr ";\n";
5892
5893       List.iter (
5894         function
5895         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5896         | StringList n ->
5897             pr "  free (%s);\n" n
5898       ) (snd style);
5899
5900       pr "  if (r == %s) {\n" error_code;
5901       pr "    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));\n";
5902       pr "    return NULL;\n";
5903       pr "  }\n";
5904       pr "\n";
5905
5906       (match fst style with
5907        | RErr ->
5908            pr "  Py_INCREF (Py_None);\n";
5909            pr "  py_r = Py_None;\n"
5910        | RInt _
5911        | RBool _ -> pr "  py_r = PyInt_FromLong ((long) r);\n"
5912        | RInt64 _ -> pr "  py_r = PyLong_FromLongLong (r);\n"
5913        | RConstString _ -> pr "  py_r = PyString_FromString (r);\n"
5914        | RString _ ->
5915            pr "  py_r = PyString_FromString (r);\n";
5916            pr "  free (r);\n"
5917        | RStringList _ ->
5918            pr "  py_r = put_string_list (r);\n";
5919            pr "  free_strings (r);\n"
5920        | RIntBool _ ->
5921            pr "  py_r = PyTuple_New (2);\n";
5922            pr "  PyTuple_SetItem (py_r, 0, PyInt_FromLong ((long) r->i));\n";
5923            pr "  PyTuple_SetItem (py_r, 1, PyInt_FromLong ((long) r->b));\n";
5924            pr "  guestfs_free_int_bool (r);\n"
5925        | RPVList n ->
5926            pr "  py_r = put_lvm_pv_list (r);\n";
5927            pr "  guestfs_free_lvm_pv_list (r);\n"
5928        | RVGList n ->
5929            pr "  py_r = put_lvm_vg_list (r);\n";
5930            pr "  guestfs_free_lvm_vg_list (r);\n"
5931        | RLVList n ->
5932            pr "  py_r = put_lvm_lv_list (r);\n";
5933            pr "  guestfs_free_lvm_lv_list (r);\n"
5934        | RStat n ->
5935            pr "  py_r = put_stat (r);\n";
5936            pr "  free (r);\n"
5937        | RStatVFS n ->
5938            pr "  py_r = put_statvfs (r);\n";
5939            pr "  free (r);\n"
5940        | RHashtable n ->
5941            pr "  py_r = put_table (r);\n";
5942            pr "  free_strings (r);\n"
5943       );
5944
5945       pr "  return py_r;\n";
5946       pr "}\n";
5947       pr "\n"
5948   ) all_functions;
5949
5950   (* Table of functions. *)
5951   pr "static PyMethodDef methods[] = {\n";
5952   pr "  { (char *) \"create\", py_guestfs_create, METH_VARARGS, NULL },\n";
5953   pr "  { (char *) \"close\", py_guestfs_close, METH_VARARGS, NULL },\n";
5954   List.iter (
5955     fun (name, _, _, _, _, _, _) ->
5956       pr "  { (char *) \"%s\", py_guestfs_%s, METH_VARARGS, NULL },\n"
5957         name name
5958   ) all_functions;
5959   pr "  { NULL, NULL, 0, NULL }\n";
5960   pr "};\n";
5961   pr "\n";
5962
5963   (* Init function. *)
5964   pr "\
5965 void
5966 initlibguestfsmod (void)
5967 {
5968   static int initialized = 0;
5969
5970   if (initialized) return;
5971   Py_InitModule ((char *) \"libguestfsmod\", methods);
5972   initialized = 1;
5973 }
5974 "
5975
5976 (* Generate Python module. *)
5977 and generate_python_py () =
5978   generate_header HashStyle LGPLv2;
5979
5980   pr "\
5981 u\"\"\"Python bindings for libguestfs
5982
5983 import guestfs
5984 g = guestfs.GuestFS ()
5985 g.add_drive (\"guest.img\")
5986 g.launch ()
5987 g.wait_ready ()
5988 parts = g.list_partitions ()
5989
5990 The guestfs module provides a Python binding to the libguestfs API
5991 for examining and modifying virtual machine disk images.
5992
5993 Amongst the things this is good for: making batch configuration
5994 changes to guests, getting disk used/free statistics (see also:
5995 virt-df), migrating between virtualization systems (see also:
5996 virt-p2v), performing partial backups, performing partial guest
5997 clones, cloning guests and changing registry/UUID/hostname info, and
5998 much else besides.
5999
6000 Libguestfs uses Linux kernel and qemu code, and can access any type of
6001 guest filesystem that Linux and qemu can, including but not limited
6002 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
6003 schemes, qcow, qcow2, vmdk.
6004
6005 Libguestfs provides ways to enumerate guest storage (eg. partitions,
6006 LVs, what filesystem is in each LV, etc.).  It can also run commands
6007 in the context of the guest.  Also you can access filesystems over FTP.
6008
6009 Errors which happen while using the API are turned into Python
6010 RuntimeError exceptions.
6011
6012 To create a guestfs handle you usually have to perform the following
6013 sequence of calls:
6014
6015 # Create the handle, call add_drive at least once, and possibly
6016 # several times if the guest has multiple block devices:
6017 g = guestfs.GuestFS ()
6018 g.add_drive (\"guest.img\")
6019
6020 # Launch the qemu subprocess and wait for it to become ready:
6021 g.launch ()
6022 g.wait_ready ()
6023
6024 # Now you can issue commands, for example:
6025 logvols = g.lvs ()
6026
6027 \"\"\"
6028
6029 import libguestfsmod
6030
6031 class GuestFS:
6032     \"\"\"Instances of this class are libguestfs API handles.\"\"\"
6033
6034     def __init__ (self):
6035         \"\"\"Create a new libguestfs handle.\"\"\"
6036         self._o = libguestfsmod.create ()
6037
6038     def __del__ (self):
6039         libguestfsmod.close (self._o)
6040
6041 ";
6042
6043   List.iter (
6044     fun (name, style, _, flags, _, _, longdesc) ->
6045       let doc = replace_str longdesc "C<guestfs_" "C<g." in
6046       let doc =
6047         match fst style with
6048         | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _
6049         | RString _ -> doc
6050         | RStringList _ ->
6051             doc ^ "\n\nThis function returns a list of strings."
6052         | RIntBool _ ->
6053             doc ^ "\n\nThis function returns a tuple (int, bool).\n"
6054         | RPVList _ ->
6055             doc ^ "\n\nThis function returns a list of PVs.  Each PV is represented as a dictionary."
6056         | RVGList _ ->
6057             doc ^ "\n\nThis function returns a list of VGs.  Each VG is represented as a dictionary."
6058         | RLVList _ ->
6059             doc ^ "\n\nThis function returns a list of LVs.  Each LV is represented as a dictionary."
6060         | RStat _ ->
6061             doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the stat structure."
6062        | RStatVFS _ ->
6063             doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure."
6064        | RHashtable _ ->
6065             doc ^ "\n\nThis function returns a dictionary." in
6066       let doc =
6067         if List.mem ProtocolLimitWarning flags then
6068           doc ^ "\n\n" ^ protocol_limit_warning
6069         else doc in
6070       let doc =
6071         if List.mem DangerWillRobinson flags then
6072           doc ^ "\n\n" ^ danger_will_robinson
6073         else doc in
6074       let doc = pod2text ~width:60 name doc in
6075       let doc = List.map (fun line -> replace_str line "\\" "\\\\") doc in
6076       let doc = String.concat "\n        " doc in
6077
6078       pr "    def %s " name;
6079       generate_call_args ~handle:"self" (snd style);
6080       pr ":\n";
6081       pr "        u\"\"\"%s\"\"\"\n" doc;
6082       pr "        return libguestfsmod.%s " name;
6083       generate_call_args ~handle:"self._o" (snd style);
6084       pr "\n";
6085       pr "\n";
6086   ) all_functions
6087
6088 (* Useful if you need the longdesc POD text as plain text.  Returns a
6089  * list of lines.
6090  *
6091  * This is the slowest thing about autogeneration.
6092  *)
6093 and pod2text ~width name longdesc =
6094   let filename, chan = Filename.open_temp_file "gen" ".tmp" in
6095   fprintf chan "=head1 %s\n\n%s\n" name longdesc;
6096   close_out chan;
6097   let cmd = sprintf "pod2text -w %d %s" width (Filename.quote filename) in
6098   let chan = Unix.open_process_in cmd in
6099   let lines = ref [] in
6100   let rec loop i =
6101     let line = input_line chan in
6102     if i = 1 then               (* discard the first line of output *)
6103       loop (i+1)
6104     else (
6105       let line = triml line in
6106       lines := line :: !lines;
6107       loop (i+1)
6108     ) in
6109   let lines = try loop 1 with End_of_file -> List.rev !lines in
6110   Unix.unlink filename;
6111   match Unix.close_process_in chan with
6112   | Unix.WEXITED 0 -> lines
6113   | Unix.WEXITED i ->
6114       failwithf "pod2text: process exited with non-zero status (%d)" i
6115   | Unix.WSIGNALED i | Unix.WSTOPPED i ->
6116       failwithf "pod2text: process signalled or stopped by signal %d" i
6117
6118 (* Generate ruby bindings. *)
6119 and generate_ruby_c () =
6120   generate_header CStyle LGPLv2;
6121
6122   pr "\
6123 #include <stdio.h>
6124 #include <stdlib.h>
6125
6126 #include <ruby.h>
6127
6128 #include \"guestfs.h\"
6129
6130 #include \"extconf.h\"
6131
6132 /* For Ruby < 1.9 */
6133 #ifndef RARRAY_LEN
6134 #define RARRAY_LEN(r) (RARRAY((r))->len)
6135 #endif
6136
6137 static VALUE m_guestfs;                 /* guestfs module */
6138 static VALUE c_guestfs;                 /* guestfs_h handle */
6139 static VALUE e_Error;                   /* used for all errors */
6140
6141 static void ruby_guestfs_free (void *p)
6142 {
6143   if (!p) return;
6144   guestfs_close ((guestfs_h *) p);
6145 }
6146
6147 static VALUE ruby_guestfs_create (VALUE m)
6148 {
6149   guestfs_h *g;
6150
6151   g = guestfs_create ();
6152   if (!g)
6153     rb_raise (e_Error, \"failed to create guestfs handle\");
6154
6155   /* Don't print error messages to stderr by default. */
6156   guestfs_set_error_handler (g, NULL, NULL);
6157
6158   /* Wrap it, and make sure the close function is called when the
6159    * handle goes away.
6160    */
6161   return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g);
6162 }
6163
6164 static VALUE ruby_guestfs_close (VALUE gv)
6165 {
6166   guestfs_h *g;
6167   Data_Get_Struct (gv, guestfs_h, g);
6168
6169   ruby_guestfs_free (g);
6170   DATA_PTR (gv) = NULL;
6171
6172   return Qnil;
6173 }
6174
6175 ";
6176
6177   List.iter (
6178     fun (name, style, _, _, _, _, _) ->
6179       pr "static VALUE ruby_guestfs_%s (VALUE gv" name;
6180       List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) (snd style);
6181       pr ")\n";
6182       pr "{\n";
6183       pr "  guestfs_h *g;\n";
6184       pr "  Data_Get_Struct (gv, guestfs_h, g);\n";
6185       pr "  if (!g)\n";
6186       pr "    rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n"
6187         name;
6188       pr "\n";
6189
6190       List.iter (
6191         function
6192         | String n | FileIn n | FileOut n ->
6193             pr "  const char *%s = StringValueCStr (%sv);\n" n n;
6194             pr "  if (!%s)\n" n;
6195             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
6196             pr "              \"%s\", \"%s\");\n" n name
6197         | OptString n ->
6198             pr "  const char *%s = StringValueCStr (%sv);\n" n n
6199         | StringList n ->
6200             pr "  char **%s;" n;
6201             pr "  {\n";
6202             pr "    int i, len;\n";
6203             pr "    len = RARRAY_LEN (%sv);\n" n;
6204             pr "    %s = guestfs_safe_malloc (g, sizeof (char *) * (len+1));\n"
6205               n;
6206             pr "    for (i = 0; i < len; ++i) {\n";
6207             pr "      VALUE v = rb_ary_entry (%sv, i);\n" n;
6208             pr "      %s[i] = StringValueCStr (v);\n" n;
6209             pr "    }\n";
6210             pr "    %s[len] = NULL;\n" n;
6211             pr "  }\n";
6212         | Bool n
6213         | Int n ->
6214             pr "  int %s = NUM2INT (%sv);\n" n n
6215       ) (snd style);
6216       pr "\n";
6217
6218       let error_code =
6219         match fst style with
6220         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
6221         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
6222         | RConstString _ -> pr "  const char *r;\n"; "NULL"
6223         | RString _ -> pr "  char *r;\n"; "NULL"
6224         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
6225         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
6226         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
6227         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
6228         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
6229         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
6230         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
6231       pr "\n";
6232
6233       pr "  r = guestfs_%s " name;
6234       generate_call_args ~handle:"g" (snd style);
6235       pr ";\n";
6236
6237       List.iter (
6238         function
6239         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
6240         | StringList n ->
6241             pr "  free (%s);\n" n
6242       ) (snd style);
6243
6244       pr "  if (r == %s)\n" error_code;
6245       pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n";
6246       pr "\n";
6247
6248       (match fst style with
6249        | RErr ->
6250            pr "  return Qnil;\n"
6251        | RInt _ | RBool _ ->
6252            pr "  return INT2NUM (r);\n"
6253        | RInt64 _ ->
6254            pr "  return ULL2NUM (r);\n"
6255        | RConstString _ ->
6256            pr "  return rb_str_new2 (r);\n";
6257        | RString _ ->
6258            pr "  VALUE rv = rb_str_new2 (r);\n";
6259            pr "  free (r);\n";
6260            pr "  return rv;\n";
6261        | RStringList _ ->
6262            pr "  int i, len = 0;\n";
6263            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
6264            pr "  VALUE rv = rb_ary_new2 (len);\n";
6265            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
6266            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
6267            pr "    free (r[i]);\n";
6268            pr "  }\n";
6269            pr "  free (r);\n";
6270            pr "  return rv;\n"
6271        | RIntBool _ ->
6272            pr "  VALUE rv = rb_ary_new2 (2);\n";
6273            pr "  rb_ary_push (rv, INT2NUM (r->i));\n";
6274            pr "  rb_ary_push (rv, INT2NUM (r->b));\n";
6275            pr "  guestfs_free_int_bool (r);\n";
6276            pr "  return rv;\n"
6277        | RPVList n ->
6278            generate_ruby_lvm_code "pv" pv_cols
6279        | RVGList n ->
6280            generate_ruby_lvm_code "vg" vg_cols
6281        | RLVList n ->
6282            generate_ruby_lvm_code "lv" lv_cols
6283        | RStat n ->
6284            pr "  VALUE rv = rb_hash_new ();\n";
6285            List.iter (
6286              function
6287              | name, `Int ->
6288                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
6289            ) stat_cols;
6290            pr "  free (r);\n";
6291            pr "  return rv;\n"
6292        | RStatVFS n ->
6293            pr "  VALUE rv = rb_hash_new ();\n";
6294            List.iter (
6295              function
6296              | name, `Int ->
6297                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
6298            ) statvfs_cols;
6299            pr "  free (r);\n";
6300            pr "  return rv;\n"
6301        | RHashtable _ ->
6302            pr "  VALUE rv = rb_hash_new ();\n";
6303            pr "  int i;\n";
6304            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
6305            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
6306            pr "    free (r[i]);\n";
6307            pr "    free (r[i+1]);\n";
6308            pr "  }\n";
6309            pr "  free (r);\n";
6310            pr "  return rv;\n"
6311       );
6312
6313       pr "}\n";
6314       pr "\n"
6315   ) all_functions;
6316
6317   pr "\
6318 /* Initialize the module. */
6319 void Init__guestfs ()
6320 {
6321   m_guestfs = rb_define_module (\"Guestfs\");
6322   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
6323   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
6324
6325   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
6326   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
6327
6328 ";
6329   (* Define the rest of the methods. *)
6330   List.iter (
6331     fun (name, style, _, _, _, _, _) ->
6332       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
6333       pr "        ruby_guestfs_%s, %d);\n" name (List.length (snd style))
6334   ) all_functions;
6335
6336   pr "}\n"
6337
6338 (* Ruby code to return an LVM struct list. *)
6339 and generate_ruby_lvm_code typ cols =
6340   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
6341   pr "  int i;\n";
6342   pr "  for (i = 0; i < r->len; ++i) {\n";
6343   pr "    VALUE hv = rb_hash_new ();\n";
6344   List.iter (
6345     function
6346     | name, `String ->
6347         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
6348     | name, `UUID ->
6349         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
6350     | name, `Bytes
6351     | name, `Int ->
6352         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
6353     | name, `OptPercent ->
6354         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
6355   ) cols;
6356   pr "    rb_ary_push (rv, hv);\n";
6357   pr "  }\n";
6358   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
6359   pr "  return rv;\n"
6360
6361 (* Generate Java bindings GuestFS.java file. *)
6362 and generate_java_java () =
6363   generate_header CStyle LGPLv2;
6364
6365   pr "\
6366 package com.redhat.et.libguestfs;
6367
6368 import java.util.HashMap;
6369 import com.redhat.et.libguestfs.LibGuestFSException;
6370 import com.redhat.et.libguestfs.PV;
6371 import com.redhat.et.libguestfs.VG;
6372 import com.redhat.et.libguestfs.LV;
6373 import com.redhat.et.libguestfs.Stat;
6374 import com.redhat.et.libguestfs.StatVFS;
6375 import com.redhat.et.libguestfs.IntBool;
6376
6377 /**
6378  * The GuestFS object is a libguestfs handle.
6379  *
6380  * @author rjones
6381  */
6382 public class GuestFS {
6383   // Load the native code.
6384   static {
6385     System.loadLibrary (\"guestfs_jni\");
6386   }
6387
6388   /**
6389    * The native guestfs_h pointer.
6390    */
6391   long g;
6392
6393   /**
6394    * Create a libguestfs handle.
6395    *
6396    * @throws LibGuestFSException
6397    */
6398   public GuestFS () throws LibGuestFSException
6399   {
6400     g = _create ();
6401   }
6402   private native long _create () throws LibGuestFSException;
6403
6404   /**
6405    * Close a libguestfs handle.
6406    *
6407    * You can also leave handles to be collected by the garbage
6408    * collector, but this method ensures that the resources used
6409    * by the handle are freed up immediately.  If you call any
6410    * other methods after closing the handle, you will get an
6411    * exception.
6412    *
6413    * @throws LibGuestFSException
6414    */
6415   public void close () throws LibGuestFSException
6416   {
6417     if (g != 0)
6418       _close (g);
6419     g = 0;
6420   }
6421   private native void _close (long g) throws LibGuestFSException;
6422
6423   public void finalize () throws LibGuestFSException
6424   {
6425     close ();
6426   }
6427
6428 ";
6429
6430   List.iter (
6431     fun (name, style, _, flags, _, shortdesc, longdesc) ->
6432       let doc = replace_str longdesc "C<guestfs_" "C<g." in
6433       let doc =
6434         if List.mem ProtocolLimitWarning flags then
6435           doc ^ "\n\n" ^ protocol_limit_warning
6436         else doc in
6437       let doc =
6438         if List.mem DangerWillRobinson flags then
6439           doc ^ "\n\n" ^ danger_will_robinson
6440         else doc in
6441       let doc = pod2text ~width:60 name doc in
6442       let doc = String.concat "\n   * " doc in
6443
6444       pr "  /**\n";
6445       pr "   * %s\n" shortdesc;
6446       pr "   *\n";
6447       pr "   * %s\n" doc;
6448       pr "   * @throws LibGuestFSException\n";
6449       pr "   */\n";
6450       pr "  ";
6451       generate_java_prototype ~public:true ~semicolon:false name style;
6452       pr "\n";
6453       pr "  {\n";
6454       pr "    if (g == 0)\n";
6455       pr "      throw new LibGuestFSException (\"%s: handle is closed\");\n"
6456         name;
6457       pr "    ";
6458       if fst style <> RErr then pr "return ";
6459       pr "_%s " name;
6460       generate_call_args ~handle:"g" (snd style);
6461       pr ";\n";
6462       pr "  }\n";
6463       pr "  ";
6464       generate_java_prototype ~privat:true ~native:true name style;
6465       pr "\n";
6466       pr "\n";
6467   ) all_functions;
6468
6469   pr "}\n"
6470
6471 and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false)
6472     ?(semicolon=true) name style =
6473   if privat then pr "private ";
6474   if public then pr "public ";
6475   if native then pr "native ";
6476
6477   (* return type *)
6478   (match fst style with
6479    | RErr -> pr "void ";
6480    | RInt _ -> pr "int ";
6481    | RInt64 _ -> pr "long ";
6482    | RBool _ -> pr "boolean ";
6483    | RConstString _ | RString _ -> pr "String ";
6484    | RStringList _ -> pr "String[] ";
6485    | RIntBool _ -> pr "IntBool ";
6486    | RPVList _ -> pr "PV[] ";
6487    | RVGList _ -> pr "VG[] ";
6488    | RLVList _ -> pr "LV[] ";
6489    | RStat _ -> pr "Stat ";
6490    | RStatVFS _ -> pr "StatVFS ";
6491    | RHashtable _ -> pr "HashMap<String,String> ";
6492   );
6493
6494   if native then pr "_%s " name else pr "%s " name;
6495   pr "(";
6496   let needs_comma = ref false in
6497   if native then (
6498     pr "long g";
6499     needs_comma := true
6500   );
6501
6502   (* args *)
6503   List.iter (
6504     fun arg ->
6505       if !needs_comma then pr ", ";
6506       needs_comma := true;
6507
6508       match arg with
6509       | String n
6510       | OptString n
6511       | FileIn n
6512       | FileOut n ->
6513           pr "String %s" n
6514       | StringList n ->
6515           pr "String[] %s" n
6516       | Bool n ->
6517           pr "boolean %s" n
6518       | Int n ->
6519           pr "int %s" n
6520   ) (snd style);
6521
6522   pr ")\n";
6523   pr "    throws LibGuestFSException";
6524   if semicolon then pr ";"
6525
6526 and generate_java_struct typ cols =
6527   generate_header CStyle LGPLv2;
6528
6529   pr "\
6530 package com.redhat.et.libguestfs;
6531
6532 /**
6533  * Libguestfs %s structure.
6534  *
6535  * @author rjones
6536  * @see GuestFS
6537  */
6538 public class %s {
6539 " typ typ;
6540
6541   List.iter (
6542     function
6543     | name, `String
6544     | name, `UUID -> pr "  public String %s;\n" name
6545     | name, `Bytes
6546     | name, `Int -> pr "  public long %s;\n" name
6547     | name, `OptPercent ->
6548         pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
6549         pr "  public float %s;\n" name
6550   ) cols;
6551
6552   pr "}\n"
6553
6554 and generate_java_c () =
6555   generate_header CStyle LGPLv2;
6556
6557   pr "\
6558 #include <stdio.h>
6559 #include <stdlib.h>
6560 #include <string.h>
6561
6562 #include \"com_redhat_et_libguestfs_GuestFS.h\"
6563 #include \"guestfs.h\"
6564
6565 /* Note that this function returns.  The exception is not thrown
6566  * until after the wrapper function returns.
6567  */
6568 static void
6569 throw_exception (JNIEnv *env, const char *msg)
6570 {
6571   jclass cl;
6572   cl = (*env)->FindClass (env,
6573                           \"com/redhat/et/libguestfs/LibGuestFSException\");
6574   (*env)->ThrowNew (env, cl, msg);
6575 }
6576
6577 JNIEXPORT jlong JNICALL
6578 Java_com_redhat_et_libguestfs_GuestFS__1create
6579   (JNIEnv *env, jobject obj)
6580 {
6581   guestfs_h *g;
6582
6583   g = guestfs_create ();
6584   if (g == NULL) {
6585     throw_exception (env, \"GuestFS.create: failed to allocate handle\");
6586     return 0;
6587   }
6588   guestfs_set_error_handler (g, NULL, NULL);
6589   return (jlong) (long) g;
6590 }
6591
6592 JNIEXPORT void JNICALL
6593 Java_com_redhat_et_libguestfs_GuestFS__1close
6594   (JNIEnv *env, jobject obj, jlong jg)
6595 {
6596   guestfs_h *g = (guestfs_h *) (long) jg;
6597   guestfs_close (g);
6598 }
6599
6600 ";
6601
6602   List.iter (
6603     fun (name, style, _, _, _, _, _) ->
6604       pr "JNIEXPORT ";
6605       (match fst style with
6606        | RErr -> pr "void ";
6607        | RInt _ -> pr "jint ";
6608        | RInt64 _ -> pr "jlong ";
6609        | RBool _ -> pr "jboolean ";
6610        | RConstString _ | RString _ -> pr "jstring ";
6611        | RIntBool _ | RStat _ | RStatVFS _ | RHashtable _ ->
6612            pr "jobject ";
6613        | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
6614            pr "jobjectArray ";
6615       );
6616       pr "JNICALL\n";
6617       pr "Java_com_redhat_et_libguestfs_GuestFS_";
6618       pr "%s" (replace_str ("_" ^ name) "_" "_1");
6619       pr "\n";
6620       pr "  (JNIEnv *env, jobject obj, jlong jg";
6621       List.iter (
6622         function
6623         | String n
6624         | OptString n
6625         | FileIn n
6626         | FileOut n ->
6627             pr ", jstring j%s" n
6628         | StringList n ->
6629             pr ", jobjectArray j%s" n
6630         | Bool n ->
6631             pr ", jboolean j%s" n
6632         | Int n ->
6633             pr ", jint j%s" n
6634       ) (snd style);
6635       pr ")\n";
6636       pr "{\n";
6637       pr "  guestfs_h *g = (guestfs_h *) (long) jg;\n";
6638       let error_code, no_ret =
6639         match fst style with
6640         | RErr -> pr "  int r;\n"; "-1", ""
6641         | RBool _
6642         | RInt _ -> pr "  int r;\n"; "-1", "0"
6643         | RInt64 _ -> pr "  int64_t r;\n"; "-1", "0"
6644         | RConstString _ -> pr "  const char *r;\n"; "NULL", "NULL"
6645         | RString _ ->
6646             pr "  jstring jr;\n";
6647             pr "  char *r;\n"; "NULL", "NULL"
6648         | RStringList _ ->
6649             pr "  jobjectArray jr;\n";
6650             pr "  int r_len;\n";
6651             pr "  jclass cl;\n";
6652             pr "  jstring jstr;\n";
6653             pr "  char **r;\n"; "NULL", "NULL"
6654         | RIntBool _ ->
6655             pr "  jobject jr;\n";
6656             pr "  jclass cl;\n";
6657             pr "  jfieldID fl;\n";
6658             pr "  struct guestfs_int_bool *r;\n"; "NULL", "NULL"
6659         | RStat _ ->
6660             pr "  jobject jr;\n";
6661             pr "  jclass cl;\n";
6662             pr "  jfieldID fl;\n";
6663             pr "  struct guestfs_stat *r;\n"; "NULL", "NULL"
6664         | RStatVFS _ ->
6665             pr "  jobject jr;\n";
6666             pr "  jclass cl;\n";
6667             pr "  jfieldID fl;\n";
6668             pr "  struct guestfs_statvfs *r;\n"; "NULL", "NULL"
6669         | RPVList _ ->
6670             pr "  jobjectArray jr;\n";
6671             pr "  jclass cl;\n";
6672             pr "  jfieldID fl;\n";
6673             pr "  jobject jfl;\n";
6674             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL", "NULL"
6675         | RVGList _ ->
6676             pr "  jobjectArray jr;\n";
6677             pr "  jclass cl;\n";
6678             pr "  jfieldID fl;\n";
6679             pr "  jobject jfl;\n";
6680             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL", "NULL"
6681         | RLVList _ ->
6682             pr "  jobjectArray jr;\n";
6683             pr "  jclass cl;\n";
6684             pr "  jfieldID fl;\n";
6685             pr "  jobject jfl;\n";
6686             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL", "NULL"
6687         | RHashtable _ -> pr "  char **r;\n"; "NULL", "NULL" in
6688       List.iter (
6689         function
6690         | String n
6691         | OptString n
6692         | FileIn n
6693         | FileOut n ->
6694             pr "  const char *%s;\n" n
6695         | StringList n ->
6696             pr "  int %s_len;\n" n;
6697             pr "  const char **%s;\n" n
6698         | Bool n
6699         | Int n ->
6700             pr "  int %s;\n" n
6701       ) (snd style);
6702
6703       let needs_i =
6704         (match fst style with
6705          | RStringList _ | RPVList _ | RVGList _ | RLVList _ -> true
6706          | RErr | RBool _ | RInt _ | RInt64 _ | RConstString _
6707          | RString _ | RIntBool _ | RStat _ | RStatVFS _
6708          | RHashtable _ -> false) ||
6709         List.exists (function StringList _ -> true | _ -> false) (snd style) in
6710       if needs_i then
6711         pr "  int i;\n";
6712
6713       pr "\n";
6714
6715       (* Get the parameters. *)
6716       List.iter (
6717         function
6718         | String n
6719         | OptString n
6720         | FileIn n
6721         | FileOut n ->
6722             pr "  %s = (*env)->GetStringUTFChars (env, j%s, NULL);\n" n n
6723         | StringList n ->
6724             pr "  %s_len = (*env)->GetArrayLength (env, j%s);\n" n n;
6725             pr "  %s = guestfs_safe_malloc (g, sizeof (char *) * (%s_len+1));\n" n n;
6726             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
6727             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
6728               n;
6729             pr "    %s[i] = (*env)->GetStringUTFChars (env, o, NULL);\n" n;
6730             pr "  }\n";
6731             pr "  %s[%s_len] = NULL;\n" n n;
6732         | Bool n
6733         | Int n ->
6734             pr "  %s = j%s;\n" n n
6735       ) (snd style);
6736
6737       (* Make the call. *)
6738       pr "  r = guestfs_%s " name;
6739       generate_call_args ~handle:"g" (snd style);
6740       pr ";\n";
6741
6742       (* Release the parameters. *)
6743       List.iter (
6744         function
6745         | String n
6746         | OptString n
6747         | FileIn n
6748         | FileOut n ->
6749             pr "  (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
6750         | StringList n ->
6751             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
6752             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
6753               n;
6754             pr "    (*env)->ReleaseStringUTFChars (env, o, %s[i]);\n" n;
6755             pr "  }\n";
6756             pr "  free (%s);\n" n
6757         | Bool n
6758         | Int n -> ()
6759       ) (snd style);
6760
6761       (* Check for errors. *)
6762       pr "  if (r == %s) {\n" error_code;
6763       pr "    throw_exception (env, guestfs_last_error (g));\n";
6764       pr "    return %s;\n" no_ret;
6765       pr "  }\n";
6766
6767       (* Return value. *)
6768       (match fst style with
6769        | RErr -> ()
6770        | RInt _ -> pr "  return (jint) r;\n"
6771        | RBool _ -> pr "  return (jboolean) r;\n"
6772        | RInt64 _ -> pr "  return (jlong) r;\n"
6773        | RConstString _ -> pr "  return (*env)->NewStringUTF (env, r);\n"
6774        | RString _ ->
6775            pr "  jr = (*env)->NewStringUTF (env, r);\n";
6776            pr "  free (r);\n";
6777            pr "  return jr;\n"
6778        | RStringList _ ->
6779            pr "  for (r_len = 0; r[r_len] != NULL; ++r_len) ;\n";
6780            pr "  cl = (*env)->FindClass (env, \"java/lang/String\");\n";
6781            pr "  jstr = (*env)->NewStringUTF (env, \"\");\n";
6782            pr "  jr = (*env)->NewObjectArray (env, r_len, cl, jstr);\n";
6783            pr "  for (i = 0; i < r_len; ++i) {\n";
6784            pr "    jstr = (*env)->NewStringUTF (env, r[i]);\n";
6785            pr "    (*env)->SetObjectArrayElement (env, jr, i, jstr);\n";
6786            pr "    free (r[i]);\n";
6787            pr "  }\n";
6788            pr "  free (r);\n";
6789            pr "  return jr;\n"
6790        | RIntBool _ ->
6791            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/IntBool\");\n";
6792            pr "  jr = (*env)->AllocObject (env, cl);\n";
6793            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"I\");\n";
6794            pr "  (*env)->SetIntField (env, jr, fl, r->i);\n";
6795            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"Z\");\n";
6796            pr "  (*env)->SetBooleanField (env, jr, fl, r->b);\n";
6797            pr "  guestfs_free_int_bool (r);\n";
6798            pr "  return jr;\n"
6799        | RStat _ ->
6800            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/Stat\");\n";
6801            pr "  jr = (*env)->AllocObject (env, cl);\n";
6802            List.iter (
6803              function
6804              | name, `Int ->
6805                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
6806                    name;
6807                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
6808            ) stat_cols;
6809            pr "  free (r);\n";
6810            pr "  return jr;\n"
6811        | RStatVFS _ ->
6812            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/StatVFS\");\n";
6813            pr "  jr = (*env)->AllocObject (env, cl);\n";
6814            List.iter (
6815              function
6816              | name, `Int ->
6817                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
6818                    name;
6819                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
6820            ) statvfs_cols;
6821            pr "  free (r);\n";
6822            pr "  return jr;\n"
6823        | RPVList _ ->
6824            generate_java_lvm_return "pv" "PV" pv_cols
6825        | RVGList _ ->
6826            generate_java_lvm_return "vg" "VG" vg_cols
6827        | RLVList _ ->
6828            generate_java_lvm_return "lv" "LV" lv_cols
6829        | RHashtable _ ->
6830            (* XXX *)
6831            pr "  throw_exception (env, \"%s: internal error: please let us know how to make a Java HashMap from JNI bindings!\");\n" name;
6832            pr "  return NULL;\n"
6833       );
6834
6835       pr "}\n";
6836       pr "\n"
6837   ) all_functions
6838
6839 and generate_java_lvm_return typ jtyp cols =
6840   pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/%s\");\n" jtyp;
6841   pr "  jr = (*env)->NewObjectArray (env, r->len, cl, NULL);\n";
6842   pr "  for (i = 0; i < r->len; ++i) {\n";
6843   pr "    jfl = (*env)->AllocObject (env, cl);\n";
6844   List.iter (
6845     function
6846     | name, `String ->
6847         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
6848         pr "    (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, r->val[i].%s));\n" name;
6849     | name, `UUID ->
6850         pr "    {\n";
6851         pr "      char s[33];\n";
6852         pr "      memcpy (s, r->val[i].%s, 32);\n" name;
6853         pr "      s[32] = 0;\n";
6854         pr "      fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
6855         pr "      (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
6856         pr "    }\n";
6857     | name, (`Bytes|`Int) ->
6858         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
6859         pr "    (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
6860     | name, `OptPercent ->
6861         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"F\");\n" name;
6862         pr "    (*env)->SetFloatField (env, jfl, fl, r->val[i].%s);\n" name;
6863   ) cols;
6864   pr "    (*env)->SetObjectArrayElement (env, jfl, i, jfl);\n";
6865   pr "  }\n";
6866   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
6867   pr "  return jr;\n"
6868
6869 and generate_haskell_hs () =
6870   generate_header HaskellStyle LGPLv2;
6871
6872   (* XXX We only know how to generate partial FFI for Haskell
6873    * at the moment.  Please help out!
6874    *)
6875   let can_generate style =
6876     let check_no_bad_args =
6877       List.for_all (function Bool _ | Int _ -> false | _ -> true)
6878     in
6879     match style with
6880     | RErr, args -> check_no_bad_args args
6881     | RBool _, _
6882     | RInt _, _
6883     | RInt64 _, _
6884     | RConstString _, _
6885     | RString _, _
6886     | RStringList _, _
6887     | RIntBool _, _
6888     | RPVList _, _
6889     | RVGList _, _
6890     | RLVList _, _
6891     | RStat _, _
6892     | RStatVFS _, _
6893     | RHashtable _, _ -> false in
6894
6895   pr "\
6896 {-# INCLUDE <guestfs.h> #-}
6897 {-# LANGUAGE ForeignFunctionInterface #-}
6898
6899 module Guestfs (
6900   create";
6901
6902   (* List out the names of the actions we want to export. *)
6903   List.iter (
6904     fun (name, style, _, _, _, _, _) ->
6905       if can_generate style then pr ",\n  %s" name
6906   ) all_functions;
6907
6908   pr "
6909   ) where
6910 import Foreign
6911 import Foreign.C
6912 import IO
6913 import Control.Exception
6914 import Data.Typeable
6915
6916 data GuestfsS = GuestfsS            -- represents the opaque C struct
6917 type GuestfsP = Ptr GuestfsS        -- guestfs_h *
6918 type GuestfsH = ForeignPtr GuestfsS -- guestfs_h * with attached finalizer
6919
6920 -- XXX define properly later XXX
6921 data PV = PV
6922 data VG = VG
6923 data LV = LV
6924 data IntBool = IntBool
6925 data Stat = Stat
6926 data StatVFS = StatVFS
6927 data Hashtable = Hashtable
6928
6929 foreign import ccall unsafe \"guestfs_create\" c_create
6930   :: IO GuestfsP
6931 foreign import ccall unsafe \"&guestfs_close\" c_close
6932   :: FunPtr (GuestfsP -> IO ())
6933 foreign import ccall unsafe \"guestfs_set_error_handler\" c_set_error_handler
6934   :: GuestfsP -> Ptr CInt -> Ptr CInt -> IO ()
6935
6936 create :: IO GuestfsH
6937 create = do
6938   p <- c_create
6939   c_set_error_handler p nullPtr nullPtr
6940   h <- newForeignPtr c_close p
6941   return h
6942
6943 foreign import ccall unsafe \"guestfs_last_error\" c_last_error
6944   :: GuestfsP -> IO CString
6945
6946 -- last_error :: GuestfsH -> IO (Maybe String)
6947 -- last_error h = do
6948 --   str <- withForeignPtr h (\\p -> c_last_error p)
6949 --   maybePeek peekCString str
6950
6951 last_error :: GuestfsH -> IO (String)
6952 last_error h = do
6953   str <- withForeignPtr h (\\p -> c_last_error p)
6954   if (str == nullPtr)
6955     then return \"no error\"
6956     else peekCString str
6957
6958 ";
6959
6960   (* Generate wrappers for each foreign function. *)
6961   List.iter (
6962     fun (name, style, _, _, _, _, _) ->
6963       if can_generate style then (
6964         pr "foreign import ccall unsafe \"guestfs_%s\" c_%s\n" name name;
6965         pr "  :: ";
6966         generate_haskell_prototype ~handle:"GuestfsP" style;
6967         pr "\n";
6968         pr "\n";
6969         pr "%s :: " name;
6970         generate_haskell_prototype ~handle:"GuestfsH" ~hs:true style;
6971         pr "\n";
6972         pr "%s %s = do\n" name
6973           (String.concat " " ("h" :: List.map name_of_argt (snd style)));
6974         pr "  r <- ";
6975         List.iter (
6976           function
6977           | FileIn n
6978           | FileOut n
6979           | String n -> pr "withCString %s $ \\%s -> " n n
6980           | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n
6981           | StringList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n
6982           | Bool n ->
6983               (* XXX this doesn't work *)
6984               pr "      let\n";
6985               pr "        %s = case %s of\n" n n;
6986               pr "          False -> 0\n";
6987               pr "          True -> 1\n";
6988               pr "      in fromIntegral %s $ \\%s ->\n" n n
6989           | Int n -> pr "fromIntegral %s $ \\%s -> " n n
6990         ) (snd style);
6991         pr "withForeignPtr h (\\p -> c_%s %s)\n" name
6992           (String.concat " " ("p" :: List.map name_of_argt (snd style)));
6993         (match fst style with
6994          | RErr | RInt _ | RInt64 _ | RBool _ ->
6995              pr "  if (r == -1)\n";
6996              pr "    then do\n";
6997              pr "      err <- last_error h\n";
6998              pr "      fail err\n";
6999          | RConstString _ | RString _ | RStringList _ | RIntBool _
7000          | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _
7001          | RHashtable _ ->
7002              pr "  if (r == nullPtr)\n";
7003              pr "    then do\n";
7004              pr "      err <- last_error h\n";
7005              pr "      fail err\n";
7006         );
7007         (match fst style with
7008          | RErr ->
7009              pr "    else return ()\n"
7010          | RInt _ ->
7011              pr "    else return (fromIntegral r)\n"
7012          | RInt64 _ ->
7013              pr "    else return (fromIntegral r)\n"
7014          | RBool _ ->
7015              pr "    else return (toBool r)\n"
7016          | RConstString _
7017          | RString _
7018          | RStringList _
7019          | RIntBool _
7020          | RPVList _
7021          | RVGList _
7022          | RLVList _
7023          | RStat _
7024          | RStatVFS _
7025          | RHashtable _ ->
7026              pr "    else return ()\n" (* XXXXXXXXXXXXXXXXXXXX *)
7027         );
7028         pr "\n";
7029       )
7030   ) all_functions
7031
7032 and generate_haskell_prototype ~handle ?(hs = false) style =
7033   pr "%s -> " handle;
7034   let string = if hs then "String" else "CString" in
7035   let int = if hs then "Int" else "CInt" in
7036   let bool = if hs then "Bool" else "CInt" in
7037   let int64 = if hs then "Integer" else "Int64" in
7038   List.iter (
7039     fun arg ->
7040       (match arg with
7041        | String _ -> pr "%s" string
7042        | OptString _ -> if hs then pr "Maybe String" else pr "CString"
7043        | StringList _ -> if hs then pr "[String]" else pr "Ptr CString"
7044        | Bool _ -> pr "%s" bool
7045        | Int _ -> pr "%s" int
7046        | FileIn _ -> pr "%s" string
7047        | FileOut _ -> pr "%s" string
7048       );
7049       pr " -> ";
7050   ) (snd style);
7051   pr "IO (";
7052   (match fst style with
7053    | RErr -> if not hs then pr "CInt"
7054    | RInt _ -> pr "%s" int
7055    | RInt64 _ -> pr "%s" int64
7056    | RBool _ -> pr "%s" bool
7057    | RConstString _ -> pr "%s" string
7058    | RString _ -> pr "%s" string
7059    | RStringList _ -> pr "[%s]" string
7060    | RIntBool _ -> pr "IntBool"
7061    | RPVList _ -> pr "[PV]"
7062    | RVGList _ -> pr "[VG]"
7063    | RLVList _ -> pr "[LV]"
7064    | RStat _ -> pr "Stat"
7065    | RStatVFS _ -> pr "StatVFS"
7066    | RHashtable _ -> pr "Hashtable"
7067   );
7068   pr ")"
7069
7070 let output_to filename =
7071   let filename_new = filename ^ ".new" in
7072   chan := open_out filename_new;
7073   let close () =
7074     close_out !chan;
7075     chan := stdout;
7076
7077     (* Is the new file different from the current file? *)
7078     if Sys.file_exists filename && files_equal filename filename_new then
7079       Unix.unlink filename_new          (* same, so skip it *)
7080     else (
7081       (* different, overwrite old one *)
7082       (try Unix.chmod filename 0o644 with Unix.Unix_error _ -> ());
7083       Unix.rename filename_new filename;
7084       Unix.chmod filename 0o444;
7085       printf "written %s\n%!" filename;
7086     )
7087   in
7088   close
7089
7090 (* Main program. *)
7091 let () =
7092   check_functions ();
7093
7094   if not (Sys.file_exists "configure.ac") then (
7095     eprintf "\
7096 You are probably running this from the wrong directory.
7097 Run it from the top source directory using the command
7098   src/generator.ml
7099 ";
7100     exit 1
7101   );
7102
7103   let close = output_to "src/guestfs_protocol.x" in
7104   generate_xdr ();
7105   close ();
7106
7107   let close = output_to "src/guestfs-structs.h" in
7108   generate_structs_h ();
7109   close ();
7110
7111   let close = output_to "src/guestfs-actions.h" in
7112   generate_actions_h ();
7113   close ();
7114
7115   let close = output_to "src/guestfs-actions.c" in
7116   generate_client_actions ();
7117   close ();
7118
7119   let close = output_to "daemon/actions.h" in
7120   generate_daemon_actions_h ();
7121   close ();
7122
7123   let close = output_to "daemon/stubs.c" in
7124   generate_daemon_actions ();
7125   close ();
7126
7127   let close = output_to "tests.c" in
7128   generate_tests ();
7129   close ();
7130
7131   let close = output_to "fish/cmds.c" in
7132   generate_fish_cmds ();
7133   close ();
7134
7135   let close = output_to "fish/completion.c" in
7136   generate_fish_completion ();
7137   close ();
7138
7139   let close = output_to "guestfs-structs.pod" in
7140   generate_structs_pod ();
7141   close ();
7142
7143   let close = output_to "guestfs-actions.pod" in
7144   generate_actions_pod ();
7145   close ();
7146
7147   let close = output_to "guestfish-actions.pod" in
7148   generate_fish_actions_pod ();
7149   close ();
7150
7151   let close = output_to "ocaml/guestfs.mli" in
7152   generate_ocaml_mli ();
7153   close ();
7154
7155   let close = output_to "ocaml/guestfs.ml" in
7156   generate_ocaml_ml ();
7157   close ();
7158
7159   let close = output_to "ocaml/guestfs_c_actions.c" in
7160   generate_ocaml_c ();
7161   close ();
7162
7163   let close = output_to "perl/Guestfs.xs" in
7164   generate_perl_xs ();
7165   close ();
7166
7167   let close = output_to "perl/lib/Sys/Guestfs.pm" in
7168   generate_perl_pm ();
7169   close ();
7170
7171   let close = output_to "python/guestfs-py.c" in
7172   generate_python_c ();
7173   close ();
7174
7175   let close = output_to "python/guestfs.py" in
7176   generate_python_py ();
7177   close ();
7178
7179   let close = output_to "ruby/ext/guestfs/_guestfs.c" in
7180   generate_ruby_c ();
7181   close ();
7182
7183   let close = output_to "java/com/redhat/et/libguestfs/GuestFS.java" in
7184   generate_java_java ();
7185   close ();
7186
7187   let close = output_to "java/com/redhat/et/libguestfs/PV.java" in
7188   generate_java_struct "PV" pv_cols;
7189   close ();
7190
7191   let close = output_to "java/com/redhat/et/libguestfs/VG.java" in
7192   generate_java_struct "VG" vg_cols;
7193   close ();
7194
7195   let close = output_to "java/com/redhat/et/libguestfs/LV.java" in
7196   generate_java_struct "LV" lv_cols;
7197   close ();
7198
7199   let close = output_to "java/com/redhat/et/libguestfs/Stat.java" in
7200   generate_java_struct "Stat" stat_cols;
7201   close ();
7202
7203   let close = output_to "java/com/redhat/et/libguestfs/StatVFS.java" in
7204   generate_java_struct "StatVFS" statvfs_cols;
7205   close ();
7206
7207   let close = output_to "java/com_redhat_et_libguestfs_GuestFS.c" in
7208   generate_java_c ();
7209   close ();
7210
7211   let close = output_to "haskell/Guestfs.hs" in
7212   generate_haskell_hs ();
7213   close ();