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