bb550f1d748fab1951e6b16aa4aa75d752392498
[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 of 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
1741 ]
1742
1743 let all_functions = non_daemon_functions @ daemon_functions
1744
1745 (* In some places we want the functions to be displayed sorted
1746  * alphabetically, so this is useful:
1747  *)
1748 let all_functions_sorted =
1749   List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) ->
1750                compare n1 n2) all_functions
1751
1752 (* Column names and types from LVM PVs/VGs/LVs. *)
1753 let pv_cols = [
1754   "pv_name", `String;
1755   "pv_uuid", `UUID;
1756   "pv_fmt", `String;
1757   "pv_size", `Bytes;
1758   "dev_size", `Bytes;
1759   "pv_free", `Bytes;
1760   "pv_used", `Bytes;
1761   "pv_attr", `String (* XXX *);
1762   "pv_pe_count", `Int;
1763   "pv_pe_alloc_count", `Int;
1764   "pv_tags", `String;
1765   "pe_start", `Bytes;
1766   "pv_mda_count", `Int;
1767   "pv_mda_free", `Bytes;
1768 (* Not in Fedora 10:
1769   "pv_mda_size", `Bytes;
1770 *)
1771 ]
1772 let vg_cols = [
1773   "vg_name", `String;
1774   "vg_uuid", `UUID;
1775   "vg_fmt", `String;
1776   "vg_attr", `String (* XXX *);
1777   "vg_size", `Bytes;
1778   "vg_free", `Bytes;
1779   "vg_sysid", `String;
1780   "vg_extent_size", `Bytes;
1781   "vg_extent_count", `Int;
1782   "vg_free_count", `Int;
1783   "max_lv", `Int;
1784   "max_pv", `Int;
1785   "pv_count", `Int;
1786   "lv_count", `Int;
1787   "snap_count", `Int;
1788   "vg_seqno", `Int;
1789   "vg_tags", `String;
1790   "vg_mda_count", `Int;
1791   "vg_mda_free", `Bytes;
1792 (* Not in Fedora 10:
1793   "vg_mda_size", `Bytes;
1794 *)
1795 ]
1796 let lv_cols = [
1797   "lv_name", `String;
1798   "lv_uuid", `UUID;
1799   "lv_attr", `String (* XXX *);
1800   "lv_major", `Int;
1801   "lv_minor", `Int;
1802   "lv_kernel_major", `Int;
1803   "lv_kernel_minor", `Int;
1804   "lv_size", `Bytes;
1805   "seg_count", `Int;
1806   "origin", `String;
1807   "snap_percent", `OptPercent;
1808   "copy_percent", `OptPercent;
1809   "move_pv", `String;
1810   "lv_tags", `String;
1811   "mirror_log", `String;
1812   "modules", `String;
1813 ]
1814
1815 (* Column names and types from stat structures.
1816  * NB. Can't use things like 'st_atime' because glibc header files
1817  * define some of these as macros.  Ugh.
1818  *)
1819 let stat_cols = [
1820   "dev", `Int;
1821   "ino", `Int;
1822   "mode", `Int;
1823   "nlink", `Int;
1824   "uid", `Int;
1825   "gid", `Int;
1826   "rdev", `Int;
1827   "size", `Int;
1828   "blksize", `Int;
1829   "blocks", `Int;
1830   "atime", `Int;
1831   "mtime", `Int;
1832   "ctime", `Int;
1833 ]
1834 let statvfs_cols = [
1835   "bsize", `Int;
1836   "frsize", `Int;
1837   "blocks", `Int;
1838   "bfree", `Int;
1839   "bavail", `Int;
1840   "files", `Int;
1841   "ffree", `Int;
1842   "favail", `Int;
1843   "fsid", `Int;
1844   "flag", `Int;
1845   "namemax", `Int;
1846 ]
1847
1848 (* Useful functions.
1849  * Note we don't want to use any external OCaml libraries which
1850  * makes this a bit harder than it should be.
1851  *)
1852 let failwithf fs = ksprintf failwith fs
1853
1854 let replace_char s c1 c2 =
1855   let s2 = String.copy s in
1856   let r = ref false in
1857   for i = 0 to String.length s2 - 1 do
1858     if String.unsafe_get s2 i = c1 then (
1859       String.unsafe_set s2 i c2;
1860       r := true
1861     )
1862   done;
1863   if not !r then s else s2
1864
1865 let isspace c =
1866   c = ' '
1867   (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
1868
1869 let triml ?(test = isspace) str =
1870   let i = ref 0 in
1871   let n = ref (String.length str) in
1872   while !n > 0 && test str.[!i]; do
1873     decr n;
1874     incr i
1875   done;
1876   if !i = 0 then str
1877   else String.sub str !i !n
1878
1879 let trimr ?(test = isspace) str =
1880   let n = ref (String.length str) in
1881   while !n > 0 && test str.[!n-1]; do
1882     decr n
1883   done;
1884   if !n = String.length str then str
1885   else String.sub str 0 !n
1886
1887 let trim ?(test = isspace) str =
1888   trimr ~test (triml ~test str)
1889
1890 let rec find s sub =
1891   let len = String.length s in
1892   let sublen = String.length sub in
1893   let rec loop i =
1894     if i <= len-sublen then (
1895       let rec loop2 j =
1896         if j < sublen then (
1897           if s.[i+j] = sub.[j] then loop2 (j+1)
1898           else -1
1899         ) else
1900           i (* found *)
1901       in
1902       let r = loop2 0 in
1903       if r = -1 then loop (i+1) else r
1904     ) else
1905       -1 (* not found *)
1906   in
1907   loop 0
1908
1909 let rec replace_str s s1 s2 =
1910   let len = String.length s in
1911   let sublen = String.length s1 in
1912   let i = find s s1 in
1913   if i = -1 then s
1914   else (
1915     let s' = String.sub s 0 i in
1916     let s'' = String.sub s (i+sublen) (len-i-sublen) in
1917     s' ^ s2 ^ replace_str s'' s1 s2
1918   )
1919
1920 let rec string_split sep str =
1921   let len = String.length str in
1922   let seplen = String.length sep in
1923   let i = find str sep in
1924   if i = -1 then [str]
1925   else (
1926     let s' = String.sub str 0 i in
1927     let s'' = String.sub str (i+seplen) (len-i-seplen) in
1928     s' :: string_split sep s''
1929   )
1930
1931 let files_equal n1 n2 =
1932   let cmd = sprintf "cmp -s %s %s" (Filename.quote n1) (Filename.quote n2) in
1933   match Sys.command cmd with
1934   | 0 -> true
1935   | 1 -> false
1936   | i -> failwithf "%s: failed with error code %d" cmd i
1937
1938 let rec find_map f = function
1939   | [] -> raise Not_found
1940   | x :: xs ->
1941       match f x with
1942       | Some y -> y
1943       | None -> find_map f xs
1944
1945 let iteri f xs =
1946   let rec loop i = function
1947     | [] -> ()
1948     | x :: xs -> f i x; loop (i+1) xs
1949   in
1950   loop 0 xs
1951
1952 let mapi f xs =
1953   let rec loop i = function
1954     | [] -> []
1955     | x :: xs -> let r = f i x in r :: loop (i+1) xs
1956   in
1957   loop 0 xs
1958
1959 let name_of_argt = function
1960   | String n | OptString n | StringList n | Bool n | Int n
1961   | FileIn n | FileOut n -> n
1962
1963 let seq_of_test = function
1964   | TestRun s | TestOutput (s, _) | TestOutputList (s, _)
1965   | TestOutputInt (s, _) | TestOutputTrue s | TestOutputFalse s
1966   | TestOutputLength (s, _) | TestOutputStruct (s, _)
1967   | TestLastFail s -> s
1968
1969 (* Check function names etc. for consistency. *)
1970 let check_functions () =
1971   let contains_uppercase str =
1972     let len = String.length str in
1973     let rec loop i =
1974       if i >= len then false
1975       else (
1976         let c = str.[i] in
1977         if c >= 'A' && c <= 'Z' then true
1978         else loop (i+1)
1979       )
1980     in
1981     loop 0
1982   in
1983
1984   (* Check function names. *)
1985   List.iter (
1986     fun (name, _, _, _, _, _, _) ->
1987       if String.length name >= 7 && String.sub name 0 7 = "guestfs" then
1988         failwithf "function name %s does not need 'guestfs' prefix" name;
1989       if contains_uppercase name then
1990         failwithf "function name %s should not contain uppercase chars" name;
1991       if String.contains name '-' then
1992         failwithf "function name %s should not contain '-', use '_' instead."
1993           name
1994   ) all_functions;
1995
1996   (* Check function parameter/return names. *)
1997   List.iter (
1998     fun (name, style, _, _, _, _, _) ->
1999       let check_arg_ret_name n =
2000         if contains_uppercase n then
2001           failwithf "%s param/ret %s should not contain uppercase chars"
2002             name n;
2003         if String.contains n '-' || String.contains n '_' then
2004           failwithf "%s param/ret %s should not contain '-' or '_'"
2005             name n;
2006         if n = "value" then
2007           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;
2008         if n = "argv" || n = "args" then
2009           failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
2010       in
2011
2012       (match fst style with
2013        | RErr -> ()
2014        | RInt n | RInt64 n | RBool n | RConstString n | RString n
2015        | RStringList n | RPVList n | RVGList n | RLVList n
2016        | RStat n | RStatVFS n
2017        | RHashtable n ->
2018            check_arg_ret_name n
2019        | RIntBool (n,m) ->
2020            check_arg_ret_name n;
2021            check_arg_ret_name m
2022       );
2023       List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) (snd style)
2024   ) all_functions;
2025
2026   (* Check short descriptions. *)
2027   List.iter (
2028     fun (name, _, _, _, _, shortdesc, _) ->
2029       if shortdesc.[0] <> Char.lowercase shortdesc.[0] then
2030         failwithf "short description of %s should begin with lowercase." name;
2031       let c = shortdesc.[String.length shortdesc-1] in
2032       if c = '\n' || c = '.' then
2033         failwithf "short description of %s should not end with . or \\n." name
2034   ) all_functions;
2035
2036   (* Check long dscriptions. *)
2037   List.iter (
2038     fun (name, _, _, _, _, _, longdesc) ->
2039       if longdesc.[String.length longdesc-1] = '\n' then
2040         failwithf "long description of %s should not end with \\n." name
2041   ) all_functions;
2042
2043   (* Check proc_nrs. *)
2044   List.iter (
2045     fun (name, _, proc_nr, _, _, _, _) ->
2046       if proc_nr <= 0 then
2047         failwithf "daemon function %s should have proc_nr > 0" name
2048   ) daemon_functions;
2049
2050   List.iter (
2051     fun (name, _, proc_nr, _, _, _, _) ->
2052       if proc_nr <> -1 then
2053         failwithf "non-daemon function %s should have proc_nr -1" name
2054   ) non_daemon_functions;
2055
2056   let proc_nrs =
2057     List.map (fun (name, _, proc_nr, _, _, _, _) -> name, proc_nr)
2058       daemon_functions in
2059   let proc_nrs =
2060     List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
2061   let rec loop = function
2062     | [] -> ()
2063     | [_] -> ()
2064     | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
2065         loop rest
2066     | (name1,nr1) :: (name2,nr2) :: _ ->
2067         failwithf "%s and %s have conflicting procedure numbers (%d, %d)"
2068           name1 name2 nr1 nr2
2069   in
2070   loop proc_nrs;
2071
2072   (* Check tests. *)
2073   List.iter (
2074     function
2075       (* Ignore functions that have no tests.  We generate a
2076        * warning when the user does 'make check' instead.
2077        *)
2078     | name, _, _, _, [], _, _ -> ()
2079     | name, _, _, _, tests, _, _ ->
2080         let funcs =
2081           List.map (
2082             fun (_, test) ->
2083               match seq_of_test test with
2084               | [] ->
2085                   failwithf "%s has a test containing an empty sequence" name
2086               | cmds -> List.map List.hd cmds
2087           ) tests in
2088         let funcs = List.flatten funcs in
2089
2090         let tested = List.mem name funcs in
2091
2092         if not tested then
2093           failwithf "function %s has tests but does not test itself" name
2094   ) all_functions
2095
2096 (* 'pr' prints to the current output file. *)
2097 let chan = ref stdout
2098 let pr fs = ksprintf (output_string !chan) fs
2099
2100 (* Generate a header block in a number of standard styles. *)
2101 type comment_style = CStyle | HashStyle | OCamlStyle
2102 type license = GPLv2 | LGPLv2
2103
2104 let generate_header comment license =
2105   let c = match comment with
2106     | CStyle ->     pr "/* "; " *"
2107     | HashStyle ->  pr "# ";  "#"
2108     | OCamlStyle -> pr "(* "; " *" in
2109   pr "libguestfs generated file\n";
2110   pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
2111   pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
2112   pr "%s\n" c;
2113   pr "%s Copyright (C) 2009 Red Hat Inc.\n" c;
2114   pr "%s\n" c;
2115   (match license with
2116    | GPLv2 ->
2117        pr "%s This program is free software; you can redistribute it and/or modify\n" c;
2118        pr "%s it under the terms of the GNU General Public License as published by\n" c;
2119        pr "%s the Free Software Foundation; either version 2 of the License, or\n" c;
2120        pr "%s (at your option) any later version.\n" c;
2121        pr "%s\n" c;
2122        pr "%s This program is distributed in the hope that it will be useful,\n" c;
2123        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
2124        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" c;
2125        pr "%s GNU General Public License for more details.\n" c;
2126        pr "%s\n" c;
2127        pr "%s You should have received a copy of the GNU General Public License along\n" c;
2128        pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c;
2129        pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c;
2130
2131    | LGPLv2 ->
2132        pr "%s This library is free software; you can redistribute it and/or\n" c;
2133        pr "%s modify it under the terms of the GNU Lesser General Public\n" c;
2134        pr "%s License as published by the Free Software Foundation; either\n" c;
2135        pr "%s version 2 of the License, or (at your option) any later version.\n" c;
2136        pr "%s\n" c;
2137        pr "%s This library is distributed in the hope that it will be useful,\n" c;
2138        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
2139        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n" c;
2140        pr "%s Lesser General Public License for more details.\n" c;
2141        pr "%s\n" c;
2142        pr "%s You should have received a copy of the GNU Lesser General Public\n" c;
2143        pr "%s License along with this library; if not, write to the Free Software\n" c;
2144        pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c;
2145   );
2146   (match comment with
2147    | CStyle -> pr " */\n"
2148    | HashStyle -> ()
2149    | OCamlStyle -> pr " *)\n"
2150   );
2151   pr "\n"
2152
2153 (* Start of main code generation functions below this line. *)
2154
2155 (* Generate the pod documentation for the C API. *)
2156 let rec generate_actions_pod () =
2157   List.iter (
2158     fun (shortname, style, _, flags, _, _, longdesc) ->
2159       let name = "guestfs_" ^ shortname in
2160       pr "=head2 %s\n\n" name;
2161       pr " ";
2162       generate_prototype ~extern:false ~handle:"handle" name style;
2163       pr "\n\n";
2164       pr "%s\n\n" longdesc;
2165       (match fst style with
2166        | RErr ->
2167            pr "This function returns 0 on success or -1 on error.\n\n"
2168        | RInt _ ->
2169            pr "On error this function returns -1.\n\n"
2170        | RInt64 _ ->
2171            pr "On error this function returns -1.\n\n"
2172        | RBool _ ->
2173            pr "This function returns a C truth value on success or -1 on error.\n\n"
2174        | RConstString _ ->
2175            pr "This function returns a string, or NULL on error.
2176 The string is owned by the guest handle and must I<not> be freed.\n\n"
2177        | RString _ ->
2178            pr "This function returns a string, or NULL on error.
2179 I<The caller must free the returned string after use>.\n\n"
2180        | RStringList _ ->
2181            pr "This function returns a NULL-terminated array of strings
2182 (like L<environ(3)>), or NULL if there was an error.
2183 I<The caller must free the strings and the array after use>.\n\n"
2184        | RIntBool _ ->
2185            pr "This function returns a C<struct guestfs_int_bool *>,
2186 or NULL if there was an error.
2187 I<The caller must call C<guestfs_free_int_bool> after use>.\n\n"
2188        | RPVList _ ->
2189            pr "This function returns a C<struct guestfs_lvm_pv_list *>
2190 (see E<lt>guestfs-structs.hE<gt>),
2191 or NULL if there was an error.
2192 I<The caller must call C<guestfs_free_lvm_pv_list> after use>.\n\n"
2193        | RVGList _ ->
2194            pr "This function returns a C<struct guestfs_lvm_vg_list *>
2195 (see E<lt>guestfs-structs.hE<gt>),
2196 or NULL if there was an error.
2197 I<The caller must call C<guestfs_free_lvm_vg_list> after use>.\n\n"
2198        | RLVList _ ->
2199            pr "This function returns a C<struct guestfs_lvm_lv_list *>
2200 (see E<lt>guestfs-structs.hE<gt>),
2201 or NULL if there was an error.
2202 I<The caller must call C<guestfs_free_lvm_lv_list> after use>.\n\n"
2203        | RStat _ ->
2204            pr "This function returns a C<struct guestfs_stat *>
2205 (see L<stat(2)> and E<lt>guestfs-structs.hE<gt>),
2206 or NULL if there was an error.
2207 I<The caller must call C<free> after use>.\n\n"
2208        | RStatVFS _ ->
2209            pr "This function returns a C<struct guestfs_statvfs *>
2210 (see L<statvfs(2)> and E<lt>guestfs-structs.hE<gt>),
2211 or NULL if there was an error.
2212 I<The caller must call C<free> after use>.\n\n"
2213        | RHashtable _ ->
2214            pr "This function returns a NULL-terminated array of
2215 strings, or NULL if there was an error.
2216 The array of strings will always have length C<2n+1>, where
2217 C<n> keys and values alternate, followed by the trailing NULL entry.
2218 I<The caller must free the strings and the array after use>.\n\n"
2219       );
2220       if List.mem ProtocolLimitWarning flags then
2221         pr "%s\n\n" protocol_limit_warning;
2222       if List.mem DangerWillRobinson flags then
2223         pr "%s\n\n" danger_will_robinson;
2224   ) all_functions_sorted
2225
2226 and generate_structs_pod () =
2227   (* LVM structs documentation. *)
2228   List.iter (
2229     fun (typ, cols) ->
2230       pr "=head2 guestfs_lvm_%s\n" typ;
2231       pr "\n";
2232       pr " struct guestfs_lvm_%s {\n" typ;
2233       List.iter (
2234         function
2235         | name, `String -> pr "  char *%s;\n" name
2236         | name, `UUID ->
2237             pr "  /* The next field is NOT nul-terminated, be careful when printing it: */\n";
2238             pr "  char %s[32];\n" name
2239         | name, `Bytes -> pr "  uint64_t %s;\n" name
2240         | name, `Int -> pr "  int64_t %s;\n" name
2241         | name, `OptPercent ->
2242             pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
2243             pr "  float %s;\n" name
2244       ) cols;
2245       pr " \n";
2246       pr " struct guestfs_lvm_%s_list {\n" typ;
2247       pr "   uint32_t len; /* Number of elements in list. */\n";
2248       pr "   struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
2249       pr " };\n";
2250       pr " \n";
2251       pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
2252         typ typ;
2253       pr "\n"
2254   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
2255
2256 (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
2257  * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'.
2258  *
2259  * We have to use an underscore instead of a dash because otherwise
2260  * rpcgen generates incorrect code.
2261  *
2262  * This header is NOT exported to clients, but see also generate_structs_h.
2263  *)
2264 and generate_xdr () =
2265   generate_header CStyle LGPLv2;
2266
2267   (* This has to be defined to get around a limitation in Sun's rpcgen. *)
2268   pr "typedef string str<>;\n";
2269   pr "\n";
2270
2271   (* LVM internal structures. *)
2272   List.iter (
2273     function
2274     | typ, cols ->
2275         pr "struct guestfs_lvm_int_%s {\n" typ;
2276         List.iter (function
2277                    | name, `String -> pr "  string %s<>;\n" name
2278                    | name, `UUID -> pr "  opaque %s[32];\n" name
2279                    | name, `Bytes -> pr "  hyper %s;\n" name
2280                    | name, `Int -> pr "  hyper %s;\n" name
2281                    | name, `OptPercent -> pr "  float %s;\n" name
2282                   ) cols;
2283         pr "};\n";
2284         pr "\n";
2285         pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
2286         pr "\n";
2287   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
2288
2289   (* Stat internal structures. *)
2290   List.iter (
2291     function
2292     | typ, cols ->
2293         pr "struct guestfs_int_%s {\n" typ;
2294         List.iter (function
2295                    | name, `Int -> pr "  hyper %s;\n" name
2296                   ) cols;
2297         pr "};\n";
2298         pr "\n";
2299   ) ["stat", stat_cols; "statvfs", statvfs_cols];
2300
2301   List.iter (
2302     fun (shortname, style, _, _, _, _, _) ->
2303       let name = "guestfs_" ^ shortname in
2304
2305       (match snd style with
2306        | [] -> ()
2307        | args ->
2308            pr "struct %s_args {\n" name;
2309            List.iter (
2310              function
2311              | String n -> pr "  string %s<>;\n" n
2312              | OptString n -> pr "  str *%s;\n" n
2313              | StringList n -> pr "  str %s<>;\n" n
2314              | Bool n -> pr "  bool %s;\n" n
2315              | Int n -> pr "  int %s;\n" n
2316              | FileIn _ | FileOut _ -> ()
2317            ) args;
2318            pr "};\n\n"
2319       );
2320       (match fst style with
2321        | RErr -> ()
2322        | RInt n ->
2323            pr "struct %s_ret {\n" name;
2324            pr "  int %s;\n" n;
2325            pr "};\n\n"
2326        | RInt64 n ->
2327            pr "struct %s_ret {\n" name;
2328            pr "  hyper %s;\n" n;
2329            pr "};\n\n"
2330        | RBool n ->
2331            pr "struct %s_ret {\n" name;
2332            pr "  bool %s;\n" n;
2333            pr "};\n\n"
2334        | RConstString _ ->
2335            failwithf "RConstString cannot be returned from a daemon function"
2336        | RString n ->
2337            pr "struct %s_ret {\n" name;
2338            pr "  string %s<>;\n" n;
2339            pr "};\n\n"
2340        | RStringList n ->
2341            pr "struct %s_ret {\n" name;
2342            pr "  str %s<>;\n" n;
2343            pr "};\n\n"
2344        | RIntBool (n,m) ->
2345            pr "struct %s_ret {\n" name;
2346            pr "  int %s;\n" n;
2347            pr "  bool %s;\n" m;
2348            pr "};\n\n"
2349        | RPVList n ->
2350            pr "struct %s_ret {\n" name;
2351            pr "  guestfs_lvm_int_pv_list %s;\n" n;
2352            pr "};\n\n"
2353        | RVGList n ->
2354            pr "struct %s_ret {\n" name;
2355            pr "  guestfs_lvm_int_vg_list %s;\n" n;
2356            pr "};\n\n"
2357        | RLVList n ->
2358            pr "struct %s_ret {\n" name;
2359            pr "  guestfs_lvm_int_lv_list %s;\n" n;
2360            pr "};\n\n"
2361        | RStat n ->
2362            pr "struct %s_ret {\n" name;
2363            pr "  guestfs_int_stat %s;\n" n;
2364            pr "};\n\n"
2365        | RStatVFS n ->
2366            pr "struct %s_ret {\n" name;
2367            pr "  guestfs_int_statvfs %s;\n" n;
2368            pr "};\n\n"
2369        | RHashtable n ->
2370            pr "struct %s_ret {\n" name;
2371            pr "  str %s<>;\n" n;
2372            pr "};\n\n"
2373       );
2374   ) daemon_functions;
2375
2376   (* Table of procedure numbers. *)
2377   pr "enum guestfs_procedure {\n";
2378   List.iter (
2379     fun (shortname, _, proc_nr, _, _, _, _) ->
2380       pr "  GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
2381   ) daemon_functions;
2382   pr "  GUESTFS_PROC_NR_PROCS\n";
2383   pr "};\n";
2384   pr "\n";
2385
2386   (* Having to choose a maximum message size is annoying for several
2387    * reasons (it limits what we can do in the API), but it (a) makes
2388    * the protocol a lot simpler, and (b) provides a bound on the size
2389    * of the daemon which operates in limited memory space.  For large
2390    * file transfers you should use FTP.
2391    *)
2392   pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
2393   pr "\n";
2394
2395   (* Message header, etc. *)
2396   pr "\
2397 /* The communication protocol is now documented in the guestfs(3)
2398  * manpage.
2399  */
2400
2401 const GUESTFS_PROGRAM = 0x2000F5F5;
2402 const GUESTFS_PROTOCOL_VERSION = 1;
2403
2404 /* These constants must be larger than any possible message length. */
2405 const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5;
2406 const GUESTFS_CANCEL_FLAG = 0xffffeeee;
2407
2408 enum guestfs_message_direction {
2409   GUESTFS_DIRECTION_CALL = 0,        /* client -> daemon */
2410   GUESTFS_DIRECTION_REPLY = 1        /* daemon -> client */
2411 };
2412
2413 enum guestfs_message_status {
2414   GUESTFS_STATUS_OK = 0,
2415   GUESTFS_STATUS_ERROR = 1
2416 };
2417
2418 const GUESTFS_ERROR_LEN = 256;
2419
2420 struct guestfs_message_error {
2421   string error_message<GUESTFS_ERROR_LEN>;
2422 };
2423
2424 struct guestfs_message_header {
2425   unsigned prog;                     /* GUESTFS_PROGRAM */
2426   unsigned vers;                     /* GUESTFS_PROTOCOL_VERSION */
2427   guestfs_procedure proc;            /* GUESTFS_PROC_x */
2428   guestfs_message_direction direction;
2429   unsigned serial;                   /* message serial number */
2430   guestfs_message_status status;
2431 };
2432
2433 const GUESTFS_MAX_CHUNK_SIZE = 8192;
2434
2435 struct guestfs_chunk {
2436   int cancel;                        /* if non-zero, transfer is cancelled */
2437   /* data size is 0 bytes if the transfer has finished successfully */
2438   opaque data<GUESTFS_MAX_CHUNK_SIZE>;
2439 };
2440 "
2441
2442 (* Generate the guestfs-structs.h file. *)
2443 and generate_structs_h () =
2444   generate_header CStyle LGPLv2;
2445
2446   (* This is a public exported header file containing various
2447    * structures.  The structures are carefully written to have
2448    * exactly the same in-memory format as the XDR structures that
2449    * we use on the wire to the daemon.  The reason for creating
2450    * copies of these structures here is just so we don't have to
2451    * export the whole of guestfs_protocol.h (which includes much
2452    * unrelated and XDR-dependent stuff that we don't want to be
2453    * public, or required by clients).
2454    *
2455    * To reiterate, we will pass these structures to and from the
2456    * client with a simple assignment or memcpy, so the format
2457    * must be identical to what rpcgen / the RFC defines.
2458    *)
2459
2460   (* guestfs_int_bool structure. *)
2461   pr "struct guestfs_int_bool {\n";
2462   pr "  int32_t i;\n";
2463   pr "  int32_t b;\n";
2464   pr "};\n";
2465   pr "\n";
2466
2467   (* LVM public structures. *)
2468   List.iter (
2469     function
2470     | typ, cols ->
2471         pr "struct guestfs_lvm_%s {\n" typ;
2472         List.iter (
2473           function
2474           | name, `String -> pr "  char *%s;\n" name
2475           | name, `UUID -> pr "  char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
2476           | name, `Bytes -> pr "  uint64_t %s;\n" name
2477           | name, `Int -> pr "  int64_t %s;\n" name
2478           | name, `OptPercent -> pr "  float %s; /* [0..100] or -1 */\n" name
2479         ) cols;
2480         pr "};\n";
2481         pr "\n";
2482         pr "struct guestfs_lvm_%s_list {\n" typ;
2483         pr "  uint32_t len;\n";
2484         pr "  struct guestfs_lvm_%s *val;\n" typ;
2485         pr "};\n";
2486         pr "\n"
2487   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
2488
2489   (* Stat structures. *)
2490   List.iter (
2491     function
2492     | typ, cols ->
2493         pr "struct guestfs_%s {\n" typ;
2494         List.iter (
2495           function
2496           | name, `Int -> pr "  int64_t %s;\n" name
2497         ) cols;
2498         pr "};\n";
2499         pr "\n"
2500   ) ["stat", stat_cols; "statvfs", statvfs_cols]
2501
2502 (* Generate the guestfs-actions.h file. *)
2503 and generate_actions_h () =
2504   generate_header CStyle LGPLv2;
2505   List.iter (
2506     fun (shortname, style, _, _, _, _, _) ->
2507       let name = "guestfs_" ^ shortname in
2508       generate_prototype ~single_line:true ~newline:true ~handle:"handle"
2509         name style
2510   ) all_functions
2511
2512 (* Generate the client-side dispatch stubs. *)
2513 and generate_client_actions () =
2514   generate_header CStyle LGPLv2;
2515
2516   pr "\
2517 #include <stdio.h>
2518 #include <stdlib.h>
2519
2520 #include \"guestfs.h\"
2521 #include \"guestfs_protocol.h\"
2522
2523 #define error guestfs_error
2524 #define perrorf guestfs_perrorf
2525 #define safe_malloc guestfs_safe_malloc
2526 #define safe_realloc guestfs_safe_realloc
2527 #define safe_strdup guestfs_safe_strdup
2528 #define safe_memdup guestfs_safe_memdup
2529
2530 /* Check the return message from a call for validity. */
2531 static int
2532 check_reply_header (guestfs_h *g,
2533                     const struct guestfs_message_header *hdr,
2534                     int proc_nr, int serial)
2535 {
2536   if (hdr->prog != GUESTFS_PROGRAM) {
2537     error (g, \"wrong program (%%d/%%d)\", hdr->prog, GUESTFS_PROGRAM);
2538     return -1;
2539   }
2540   if (hdr->vers != GUESTFS_PROTOCOL_VERSION) {
2541     error (g, \"wrong protocol version (%%d/%%d)\",
2542            hdr->vers, GUESTFS_PROTOCOL_VERSION);
2543     return -1;
2544   }
2545   if (hdr->direction != GUESTFS_DIRECTION_REPLY) {
2546     error (g, \"unexpected message direction (%%d/%%d)\",
2547            hdr->direction, GUESTFS_DIRECTION_REPLY);
2548     return -1;
2549   }
2550   if (hdr->proc != proc_nr) {
2551     error (g, \"unexpected procedure number (%%d/%%d)\", hdr->proc, proc_nr);
2552     return -1;
2553   }
2554   if (hdr->serial != serial) {
2555     error (g, \"unexpected serial (%%d/%%d)\", hdr->serial, serial);
2556     return -1;
2557   }
2558
2559   return 0;
2560 }
2561
2562 /* Check we are in the right state to run a high-level action. */
2563 static int
2564 check_state (guestfs_h *g, const char *caller)
2565 {
2566   if (!guestfs_is_ready (g)) {
2567     if (guestfs_is_config (g))
2568       error (g, \"%%s: call launch() before using this function\",
2569         caller);
2570     else if (guestfs_is_launching (g))
2571       error (g, \"%%s: call wait_ready() before using this function\",
2572         caller);
2573     else
2574       error (g, \"%%s called from the wrong state, %%d != READY\",
2575         caller, guestfs_get_state (g));
2576     return -1;
2577   }
2578   return 0;
2579 }
2580
2581 ";
2582
2583   (* Client-side stubs for each function. *)
2584   List.iter (
2585     fun (shortname, style, _, _, _, _, _) ->
2586       let name = "guestfs_" ^ shortname in
2587
2588       (* Generate the context struct which stores the high-level
2589        * state between callback functions.
2590        *)
2591       pr "struct %s_ctx {\n" shortname;
2592       pr "  /* This flag is set by the callbacks, so we know we've done\n";
2593       pr "   * the callbacks as expected, and in the right sequence.\n";
2594       pr "   * 0 = not called, 1 = reply_cb called.\n";
2595       pr "   */\n";
2596       pr "  int cb_sequence;\n";
2597       pr "  struct guestfs_message_header hdr;\n";
2598       pr "  struct guestfs_message_error err;\n";
2599       (match fst style with
2600        | RErr -> ()
2601        | RConstString _ ->
2602            failwithf "RConstString cannot be returned from a daemon function"
2603        | RInt _ | RInt64 _
2604        | RBool _ | RString _ | RStringList _
2605        | RIntBool _
2606        | RPVList _ | RVGList _ | RLVList _
2607        | RStat _ | RStatVFS _
2608        | RHashtable _ ->
2609            pr "  struct %s_ret ret;\n" name
2610       );
2611       pr "};\n";
2612       pr "\n";
2613
2614       (* Generate the reply callback function. *)
2615       pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
2616       pr "{\n";
2617       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
2618       pr "  struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
2619       pr "\n";
2620       pr "  /* This should definitely not happen. */\n";
2621       pr "  if (ctx->cb_sequence != 0) {\n";
2622       pr "    ctx->cb_sequence = 9999;\n";
2623       pr "    error (g, \"%%s: internal error: reply callback called twice\", \"%s\");\n" name;
2624       pr "    return;\n";
2625       pr "  }\n";
2626       pr "\n";
2627       pr "  ml->main_loop_quit (ml, g);\n";
2628       pr "\n";
2629       pr "  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {\n";
2630       pr "    error (g, \"%%s: failed to parse reply header\", \"%s\");\n" name;
2631       pr "    return;\n";
2632       pr "  }\n";
2633       pr "  if (ctx->hdr.status == GUESTFS_STATUS_ERROR) {\n";
2634       pr "    if (!xdr_guestfs_message_error (xdr, &ctx->err)) {\n";
2635       pr "      error (g, \"%%s: failed to parse reply error\", \"%s\");\n"
2636         name;
2637       pr "      return;\n";
2638       pr "    }\n";
2639       pr "    goto done;\n";
2640       pr "  }\n";
2641
2642       (match fst style with
2643        | RErr -> ()
2644        | RConstString _ ->
2645            failwithf "RConstString cannot be returned from a daemon function"
2646        | RInt _ | RInt64 _
2647        | RBool _ | RString _ | RStringList _
2648        | RIntBool _
2649        | RPVList _ | RVGList _ | RLVList _
2650        | RStat _ | RStatVFS _
2651        | RHashtable _ ->
2652             pr "  if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name;
2653             pr "    error (g, \"%%s: failed to parse reply\", \"%s\");\n" name;
2654             pr "    return;\n";
2655             pr "  }\n";
2656       );
2657
2658       pr " done:\n";
2659       pr "  ctx->cb_sequence = 1;\n";
2660       pr "}\n\n";
2661
2662       (* Generate the action stub. *)
2663       generate_prototype ~extern:false ~semicolon:false ~newline:true
2664         ~handle:"g" name style;
2665
2666       let error_code =
2667         match fst style with
2668         | RErr | RInt _ | RInt64 _ | RBool _ -> "-1"
2669         | RConstString _ ->
2670             failwithf "RConstString cannot be returned from a daemon function"
2671         | RString _ | RStringList _ | RIntBool _
2672         | RPVList _ | RVGList _ | RLVList _
2673         | RStat _ | RStatVFS _
2674         | RHashtable _ ->
2675             "NULL" in
2676
2677       pr "{\n";
2678
2679       (match snd style with
2680        | [] -> ()
2681        | _ -> pr "  struct %s_args args;\n" name
2682       );
2683
2684       pr "  struct %s_ctx ctx;\n" shortname;
2685       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
2686       pr "  int serial;\n";
2687       pr "\n";
2688       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
2689       pr "  guestfs_set_busy (g);\n";
2690       pr "\n";
2691       pr "  memset (&ctx, 0, sizeof ctx);\n";
2692       pr "\n";
2693
2694       (* Send the main header and arguments. *)
2695       (match snd style with
2696        | [] ->
2697            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s, NULL, NULL);\n"
2698              (String.uppercase shortname)
2699        | args ->
2700            List.iter (
2701              function
2702              | String n ->
2703                  pr "  args.%s = (char *) %s;\n" n n
2704              | OptString n ->
2705                  pr "  args.%s = %s ? (char **) &%s : NULL;\n" n n n
2706              | StringList n ->
2707                  pr "  args.%s.%s_val = (char **) %s;\n" n n n;
2708                  pr "  for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
2709              | Bool n ->
2710                  pr "  args.%s = %s;\n" n n
2711              | Int n ->
2712                  pr "  args.%s = %s;\n" n n
2713              | FileIn _ | FileOut _ -> ()
2714            ) args;
2715            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s,\n"
2716              (String.uppercase shortname);
2717            pr "        (xdrproc_t) xdr_%s_args, (char *) &args);\n"
2718              name;
2719       );
2720       pr "  if (serial == -1) {\n";
2721       pr "    guestfs_set_ready (g);\n";
2722       pr "    return %s;\n" error_code;
2723       pr "  }\n";
2724       pr "\n";
2725
2726       (* Send any additional files (FileIn) requested. *)
2727       let need_read_reply_label = ref false in
2728       List.iter (
2729         function
2730         | FileIn n ->
2731             pr "  {\n";
2732             pr "    int r;\n";
2733             pr "\n";
2734             pr "    r = guestfs__send_file_sync (g, %s);\n" n;
2735             pr "    if (r == -1) {\n";
2736             pr "      guestfs_set_ready (g);\n";
2737             pr "      return %s;\n" error_code;
2738             pr "    }\n";
2739             pr "    if (r == -2) /* daemon cancelled */\n";
2740             pr "      goto read_reply;\n";
2741             need_read_reply_label := true;
2742             pr "  }\n";
2743             pr "\n";
2744         | _ -> ()
2745       ) (snd style);
2746
2747       (* Wait for the reply from the remote end. *)
2748       if !need_read_reply_label then pr " read_reply:\n";
2749       pr "  guestfs__switch_to_receiving (g);\n";
2750       pr "  ctx.cb_sequence = 0;\n";
2751       pr "  guestfs_set_reply_callback (g, %s_reply_cb, &ctx);\n" shortname;
2752       pr "  (void) ml->main_loop_run (ml, g);\n";
2753       pr "  guestfs_set_reply_callback (g, NULL, NULL);\n";
2754       pr "  if (ctx.cb_sequence != 1) {\n";
2755       pr "    error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name;
2756       pr "    guestfs_set_ready (g);\n";
2757       pr "    return %s;\n" error_code;
2758       pr "  }\n";
2759       pr "\n";
2760
2761       pr "  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1) {\n"
2762         (String.uppercase shortname);
2763       pr "    guestfs_set_ready (g);\n";
2764       pr "    return %s;\n" error_code;
2765       pr "  }\n";
2766       pr "\n";
2767
2768       pr "  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {\n";
2769       pr "    error (g, \"%%s\", ctx.err.error_message);\n";
2770       pr "    guestfs_set_ready (g);\n";
2771       pr "    return %s;\n" error_code;
2772       pr "  }\n";
2773       pr "\n";
2774
2775       (* Expecting to receive further files (FileOut)? *)
2776       List.iter (
2777         function
2778         | FileOut n ->
2779             pr "  if (guestfs__receive_file_sync (g, %s) == -1) {\n" n;
2780             pr "    guestfs_set_ready (g);\n";
2781             pr "    return %s;\n" error_code;
2782             pr "  }\n";
2783             pr "\n";
2784         | _ -> ()
2785       ) (snd style);
2786
2787       pr "  guestfs_set_ready (g);\n";
2788
2789       (match fst style with
2790        | RErr -> pr "  return 0;\n"
2791        | RInt n | RInt64 n | RBool n ->
2792            pr "  return ctx.ret.%s;\n" n
2793        | RConstString _ ->
2794            failwithf "RConstString cannot be returned from a daemon function"
2795        | RString n ->
2796            pr "  return ctx.ret.%s; /* caller will free */\n" n
2797        | RStringList n | RHashtable n ->
2798            pr "  /* caller will free this, but we need to add a NULL entry */\n";
2799            pr "  ctx.ret.%s.%s_val =\n" n n;
2800            pr "    safe_realloc (g, ctx.ret.%s.%s_val,\n" n n;
2801            pr "                  sizeof (char *) * (ctx.ret.%s.%s_len + 1));\n"
2802              n n;
2803            pr "  ctx.ret.%s.%s_val[ctx.ret.%s.%s_len] = NULL;\n" n n n n;
2804            pr "  return ctx.ret.%s.%s_val;\n" n n
2805        | RIntBool _ ->
2806            pr "  /* caller with free this */\n";
2807            pr "  return safe_memdup (g, &ctx.ret, sizeof (ctx.ret));\n"
2808        | RPVList n | RVGList n | RLVList n
2809        | RStat n | RStatVFS n ->
2810            pr "  /* caller will free this */\n";
2811            pr "  return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n
2812       );
2813
2814       pr "}\n\n"
2815   ) daemon_functions
2816
2817 (* Generate daemon/actions.h. *)
2818 and generate_daemon_actions_h () =
2819   generate_header CStyle GPLv2;
2820
2821   pr "#include \"../src/guestfs_protocol.h\"\n";
2822   pr "\n";
2823
2824   List.iter (
2825     fun (name, style, _, _, _, _, _) ->
2826         generate_prototype
2827           ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
2828           name style;
2829   ) daemon_functions
2830
2831 (* Generate the server-side stubs. *)
2832 and generate_daemon_actions () =
2833   generate_header CStyle GPLv2;
2834
2835   pr "#include <config.h>\n";
2836   pr "\n";
2837   pr "#include <stdio.h>\n";
2838   pr "#include <stdlib.h>\n";
2839   pr "#include <string.h>\n";
2840   pr "#include <inttypes.h>\n";
2841   pr "#include <ctype.h>\n";
2842   pr "#include <rpc/types.h>\n";
2843   pr "#include <rpc/xdr.h>\n";
2844   pr "\n";
2845   pr "#include \"daemon.h\"\n";
2846   pr "#include \"../src/guestfs_protocol.h\"\n";
2847   pr "#include \"actions.h\"\n";
2848   pr "\n";
2849
2850   List.iter (
2851     fun (name, style, _, _, _, _, _) ->
2852       (* Generate server-side stubs. *)
2853       pr "static void %s_stub (XDR *xdr_in)\n" name;
2854       pr "{\n";
2855       let error_code =
2856         match fst style with
2857         | RErr | RInt _ -> pr "  int r;\n"; "-1"
2858         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
2859         | RBool _ -> pr "  int r;\n"; "-1"
2860         | RConstString _ ->
2861             failwithf "RConstString cannot be returned from a daemon function"
2862         | RString _ -> pr "  char *r;\n"; "NULL"
2863         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
2864         | RIntBool _ -> pr "  guestfs_%s_ret *r;\n" name; "NULL"
2865         | RPVList _ -> pr "  guestfs_lvm_int_pv_list *r;\n"; "NULL"
2866         | RVGList _ -> pr "  guestfs_lvm_int_vg_list *r;\n"; "NULL"
2867         | RLVList _ -> pr "  guestfs_lvm_int_lv_list *r;\n"; "NULL"
2868         | RStat _ -> pr "  guestfs_int_stat *r;\n"; "NULL"
2869         | RStatVFS _ -> pr "  guestfs_int_statvfs *r;\n"; "NULL" in
2870
2871       (match snd style with
2872        | [] -> ()
2873        | args ->
2874            pr "  struct guestfs_%s_args args;\n" name;
2875            List.iter (
2876              function
2877              | String n
2878              | OptString n -> pr "  const char *%s;\n" n
2879              | StringList n -> pr "  char **%s;\n" n
2880              | Bool n -> pr "  int %s;\n" n
2881              | Int n -> pr "  int %s;\n" n
2882              | FileIn _ | FileOut _ -> ()
2883            ) args
2884       );
2885       pr "\n";
2886
2887       (match snd style with
2888        | [] -> ()
2889        | args ->
2890            pr "  memset (&args, 0, sizeof args);\n";
2891            pr "\n";
2892            pr "  if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
2893            pr "    reply_with_error (\"%%s: daemon failed to decode procedure arguments\", \"%s\");\n" name;
2894            pr "    return;\n";
2895            pr "  }\n";
2896            List.iter (
2897              function
2898              | String n -> pr "  %s = args.%s;\n" n n
2899              | OptString n -> pr "  %s = args.%s ? *args.%s : NULL;\n" n n n
2900              | StringList n ->
2901                  pr "  %s = realloc (args.%s.%s_val,\n" n n n;
2902                  pr "                sizeof (char *) * (args.%s.%s_len+1));\n" n n;
2903                  pr "  if (%s == NULL) {\n" n;
2904                  pr "    reply_with_perror (\"realloc\");\n";
2905                  pr "    goto done;\n";
2906                  pr "  }\n";
2907                  pr "  %s[args.%s.%s_len] = NULL;\n" n n n;
2908                  pr "  args.%s.%s_val = %s;\n" n n n;
2909              | Bool n -> pr "  %s = args.%s;\n" n n
2910              | Int n -> pr "  %s = args.%s;\n" n n
2911              | FileIn _ | FileOut _ -> ()
2912            ) args;
2913            pr "\n"
2914       );
2915
2916       (* Don't want to call the impl with any FileIn or FileOut
2917        * parameters, since these go "outside" the RPC protocol.
2918        *)
2919       let argsnofile =
2920         List.filter (function FileIn _ | FileOut _ -> false | _ -> true)
2921           (snd style) in
2922       pr "  r = do_%s " name;
2923       generate_call_args argsnofile;
2924       pr ";\n";
2925
2926       pr "  if (r == %s)\n" error_code;
2927       pr "    /* do_%s has already called reply_with_error */\n" name;
2928       pr "    goto done;\n";
2929       pr "\n";
2930
2931       (* If there are any FileOut parameters, then the impl must
2932        * send its own reply.
2933        *)
2934       let no_reply =
2935         List.exists (function FileOut _ -> true | _ -> false) (snd style) in
2936       if no_reply then
2937         pr "  /* do_%s has already sent a reply */\n" name
2938       else (
2939         match fst style with
2940         | RErr -> pr "  reply (NULL, NULL);\n"
2941         | RInt n | RInt64 n | RBool n ->
2942             pr "  struct guestfs_%s_ret ret;\n" name;
2943             pr "  ret.%s = r;\n" n;
2944             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2945               name
2946         | RConstString _ ->
2947             failwithf "RConstString cannot be returned from a daemon function"
2948         | RString n ->
2949             pr "  struct guestfs_%s_ret ret;\n" name;
2950             pr "  ret.%s = r;\n" n;
2951             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2952               name;
2953             pr "  free (r);\n"
2954         | RStringList n | RHashtable n ->
2955             pr "  struct guestfs_%s_ret ret;\n" name;
2956             pr "  ret.%s.%s_len = count_strings (r);\n" n n;
2957             pr "  ret.%s.%s_val = r;\n" n n;
2958             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2959               name;
2960             pr "  free_strings (r);\n"
2961         | RIntBool _ ->
2962             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n"
2963               name;
2964             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name
2965         | RPVList n | RVGList n | RLVList n
2966         | RStat n | RStatVFS n ->
2967             pr "  struct guestfs_%s_ret ret;\n" name;
2968             pr "  ret.%s = *r;\n" n;
2969             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
2970               name;
2971             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
2972               name
2973       );
2974
2975       (* Free the args. *)
2976       (match snd style with
2977        | [] ->
2978            pr "done: ;\n";
2979        | _ ->
2980            pr "done:\n";
2981            pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
2982              name
2983       );
2984
2985       pr "}\n\n";
2986   ) daemon_functions;
2987
2988   (* Dispatch function. *)
2989   pr "void dispatch_incoming_message (XDR *xdr_in)\n";
2990   pr "{\n";
2991   pr "  switch (proc_nr) {\n";
2992
2993   List.iter (
2994     fun (name, style, _, _, _, _, _) ->
2995         pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
2996         pr "      %s_stub (xdr_in);\n" name;
2997         pr "      break;\n"
2998   ) daemon_functions;
2999
3000   pr "    default:\n";
3001   pr "      reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
3002   pr "  }\n";
3003   pr "}\n";
3004   pr "\n";
3005
3006   (* LVM columns and tokenization functions. *)
3007   (* XXX This generates crap code.  We should rethink how we
3008    * do this parsing.
3009    *)
3010   List.iter (
3011     function
3012     | typ, cols ->
3013         pr "static const char *lvm_%s_cols = \"%s\";\n"
3014           typ (String.concat "," (List.map fst cols));
3015         pr "\n";
3016
3017         pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
3018         pr "{\n";
3019         pr "  char *tok, *p, *next;\n";
3020         pr "  int i, j;\n";
3021         pr "\n";
3022         (*
3023         pr "  fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
3024         pr "\n";
3025         *)
3026         pr "  if (!str) {\n";
3027         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
3028         pr "    return -1;\n";
3029         pr "  }\n";
3030         pr "  if (!*str || isspace (*str)) {\n";
3031         pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
3032         pr "    return -1;\n";
3033         pr "  }\n";
3034         pr "  tok = str;\n";
3035         List.iter (
3036           fun (name, coltype) ->
3037             pr "  if (!tok) {\n";
3038             pr "    fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
3039             pr "    return -1;\n";
3040             pr "  }\n";
3041             pr "  p = strchrnul (tok, ',');\n";
3042             pr "  if (*p) next = p+1; else next = NULL;\n";
3043             pr "  *p = '\\0';\n";
3044             (match coltype with
3045              | `String ->
3046                  pr "  r->%s = strdup (tok);\n" name;
3047                  pr "  if (r->%s == NULL) {\n" name;
3048                  pr "    perror (\"strdup\");\n";
3049                  pr "    return -1;\n";
3050                  pr "  }\n"
3051              | `UUID ->
3052                  pr "  for (i = j = 0; i < 32; ++j) {\n";
3053                  pr "    if (tok[j] == '\\0') {\n";
3054                  pr "      fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
3055                  pr "      return -1;\n";
3056                  pr "    } else if (tok[j] != '-')\n";
3057                  pr "      r->%s[i++] = tok[j];\n" name;
3058                  pr "  }\n";
3059              | `Bytes ->
3060                  pr "  if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
3061                  pr "    fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3062                  pr "    return -1;\n";
3063                  pr "  }\n";
3064              | `Int ->
3065                  pr "  if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
3066                  pr "    fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3067                  pr "    return -1;\n";
3068                  pr "  }\n";
3069              | `OptPercent ->
3070                  pr "  if (tok[0] == '\\0')\n";
3071                  pr "    r->%s = -1;\n" name;
3072                  pr "  else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
3073                  pr "    fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3074                  pr "    return -1;\n";
3075                  pr "  }\n";
3076             );
3077             pr "  tok = next;\n";
3078         ) cols;
3079
3080         pr "  if (tok != NULL) {\n";
3081         pr "    fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
3082         pr "    return -1;\n";
3083         pr "  }\n";
3084         pr "  return 0;\n";
3085         pr "}\n";
3086         pr "\n";
3087
3088         pr "guestfs_lvm_int_%s_list *\n" typ;
3089         pr "parse_command_line_%ss (void)\n" typ;
3090         pr "{\n";
3091         pr "  char *out, *err;\n";
3092         pr "  char *p, *pend;\n";
3093         pr "  int r, i;\n";
3094         pr "  guestfs_lvm_int_%s_list *ret;\n" typ;
3095         pr "  void *newp;\n";
3096         pr "\n";
3097         pr "  ret = malloc (sizeof *ret);\n";
3098         pr "  if (!ret) {\n";
3099         pr "    reply_with_perror (\"malloc\");\n";
3100         pr "    return NULL;\n";
3101         pr "  }\n";
3102         pr "\n";
3103         pr "  ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
3104         pr "  ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
3105         pr "\n";
3106         pr "  r = command (&out, &err,\n";
3107         pr "           \"/sbin/lvm\", \"%ss\",\n" typ;
3108         pr "           \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
3109         pr "           \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
3110         pr "  if (r == -1) {\n";
3111         pr "    reply_with_error (\"%%s\", err);\n";
3112         pr "    free (out);\n";
3113         pr "    free (err);\n";
3114         pr "    free (ret);\n";
3115         pr "    return NULL;\n";
3116         pr "  }\n";
3117         pr "\n";
3118         pr "  free (err);\n";
3119         pr "\n";
3120         pr "  /* Tokenize each line of the output. */\n";
3121         pr "  p = out;\n";
3122         pr "  i = 0;\n";
3123         pr "  while (p) {\n";
3124         pr "    pend = strchr (p, '\\n');       /* Get the next line of output. */\n";
3125         pr "    if (pend) {\n";
3126         pr "      *pend = '\\0';\n";
3127         pr "      pend++;\n";
3128         pr "    }\n";
3129         pr "\n";
3130         pr "    while (*p && isspace (*p))      /* Skip any leading whitespace. */\n";
3131         pr "      p++;\n";
3132         pr "\n";
3133         pr "    if (!*p) {                      /* Empty line?  Skip it. */\n";
3134         pr "      p = pend;\n";
3135         pr "      continue;\n";
3136         pr "    }\n";
3137         pr "\n";
3138         pr "    /* Allocate some space to store this next entry. */\n";
3139         pr "    newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
3140         pr "                sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
3141         pr "    if (newp == NULL) {\n";
3142         pr "      reply_with_perror (\"realloc\");\n";
3143         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
3144         pr "      free (ret);\n";
3145         pr "      free (out);\n";
3146         pr "      return NULL;\n";
3147         pr "    }\n";
3148         pr "    ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
3149         pr "\n";
3150         pr "    /* Tokenize the next entry. */\n";
3151         pr "    r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
3152         pr "    if (r == -1) {\n";
3153         pr "      reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
3154         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
3155         pr "      free (ret);\n";
3156         pr "      free (out);\n";
3157         pr "      return NULL;\n";
3158         pr "    }\n";
3159         pr "\n";
3160         pr "    ++i;\n";
3161         pr "    p = pend;\n";
3162         pr "  }\n";
3163         pr "\n";
3164         pr "  ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
3165         pr "\n";
3166         pr "  free (out);\n";
3167         pr "  return ret;\n";
3168         pr "}\n"
3169
3170   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
3171
3172 (* Generate the tests. *)
3173 and generate_tests () =
3174   generate_header CStyle GPLv2;
3175
3176   pr "\
3177 #include <stdio.h>
3178 #include <stdlib.h>
3179 #include <string.h>
3180 #include <unistd.h>
3181 #include <sys/types.h>
3182 #include <fcntl.h>
3183
3184 #include \"guestfs.h\"
3185
3186 static guestfs_h *g;
3187 static int suppress_error = 0;
3188
3189 static void print_error (guestfs_h *g, void *data, const char *msg)
3190 {
3191   if (!suppress_error)
3192     fprintf (stderr, \"%%s\\n\", msg);
3193 }
3194
3195 static void print_strings (char * const * const argv)
3196 {
3197   int argc;
3198
3199   for (argc = 0; argv[argc] != NULL; ++argc)
3200     printf (\"\\t%%s\\n\", argv[argc]);
3201 }
3202
3203 /*
3204 static void print_table (char * const * const argv)
3205 {
3206   int i;
3207
3208   for (i = 0; argv[i] != NULL; i += 2)
3209     printf (\"%%s: %%s\\n\", argv[i], argv[i+1]);
3210 }
3211 */
3212
3213 static void no_test_warnings (void)
3214 {
3215 ";
3216
3217   List.iter (
3218     function
3219     | name, _, _, _, [], _, _ ->
3220         pr "  fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name
3221     | name, _, _, _, tests, _, _ -> ()
3222   ) all_functions;
3223
3224   pr "}\n";
3225   pr "\n";
3226
3227   (* Generate the actual tests.  Note that we generate the tests
3228    * in reverse order, deliberately, so that (in general) the
3229    * newest tests run first.  This makes it quicker and easier to
3230    * debug them.
3231    *)
3232   let test_names =
3233     List.map (
3234       fun (name, _, _, _, tests, _, _) ->
3235         mapi (generate_one_test name) tests
3236     ) (List.rev all_functions) in
3237   let test_names = List.concat test_names in
3238   let nr_tests = List.length test_names in
3239
3240   pr "\
3241 int main (int argc, char *argv[])
3242 {
3243   char c = 0;
3244   int failed = 0;
3245   const char *srcdir;
3246   const char *filename;
3247   int fd;
3248   int nr_tests, test_num = 0;
3249
3250   no_test_warnings ();
3251
3252   g = guestfs_create ();
3253   if (g == NULL) {
3254     printf (\"guestfs_create FAILED\\n\");
3255     exit (1);
3256   }
3257
3258   guestfs_set_error_handler (g, print_error, NULL);
3259
3260   srcdir = getenv (\"srcdir\");
3261   if (!srcdir) srcdir = \".\";
3262   chdir (srcdir);
3263   guestfs_set_path (g, \".\");
3264
3265   filename = \"test1.img\";
3266   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3267   if (fd == -1) {
3268     perror (filename);
3269     exit (1);
3270   }
3271   if (lseek (fd, %d, SEEK_SET) == -1) {
3272     perror (\"lseek\");
3273     close (fd);
3274     unlink (filename);
3275     exit (1);
3276   }
3277   if (write (fd, &c, 1) == -1) {
3278     perror (\"write\");
3279     close (fd);
3280     unlink (filename);
3281     exit (1);
3282   }
3283   if (close (fd) == -1) {
3284     perror (filename);
3285     unlink (filename);
3286     exit (1);
3287   }
3288   if (guestfs_add_drive (g, filename) == -1) {
3289     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3290     exit (1);
3291   }
3292
3293   filename = \"test2.img\";
3294   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3295   if (fd == -1) {
3296     perror (filename);
3297     exit (1);
3298   }
3299   if (lseek (fd, %d, SEEK_SET) == -1) {
3300     perror (\"lseek\");
3301     close (fd);
3302     unlink (filename);
3303     exit (1);
3304   }
3305   if (write (fd, &c, 1) == -1) {
3306     perror (\"write\");
3307     close (fd);
3308     unlink (filename);
3309     exit (1);
3310   }
3311   if (close (fd) == -1) {
3312     perror (filename);
3313     unlink (filename);
3314     exit (1);
3315   }
3316   if (guestfs_add_drive (g, filename) == -1) {
3317     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3318     exit (1);
3319   }
3320
3321   filename = \"test3.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   if (guestfs_launch (g) == -1) {
3350     printf (\"guestfs_launch FAILED\\n\");
3351     exit (1);
3352   }
3353   if (guestfs_wait_ready (g) == -1) {
3354     printf (\"guestfs_wait_ready FAILED\\n\");
3355     exit (1);
3356   }
3357
3358   nr_tests = %d;
3359
3360 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
3361
3362   iteri (
3363     fun i test_name ->
3364       pr "  test_num++;\n";
3365       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
3366       pr "  if (%s () == -1) {\n" test_name;
3367       pr "    printf (\"%s FAILED\\n\");\n" test_name;
3368       pr "    failed++;\n";
3369       pr "  }\n";
3370   ) test_names;
3371   pr "\n";
3372
3373   pr "  guestfs_close (g);\n";
3374   pr "  unlink (\"test1.img\");\n";
3375   pr "  unlink (\"test2.img\");\n";
3376   pr "  unlink (\"test3.img\");\n";
3377   pr "\n";
3378
3379   pr "  if (failed > 0) {\n";
3380   pr "    printf (\"***** %%d / %%d tests FAILED *****\\n\", failed, nr_tests);\n";
3381   pr "    exit (1);\n";
3382   pr "  }\n";
3383   pr "\n";
3384
3385   pr "  exit (0);\n";
3386   pr "}\n"
3387
3388 and generate_one_test name i (init, test) =
3389   let test_name = sprintf "test_%s_%d" name i in
3390
3391   pr "static int %s (void)\n" test_name;
3392   pr "{\n";
3393
3394   (match init with
3395    | InitNone -> ()
3396    | InitEmpty ->
3397        pr "  /* InitEmpty for %s (%d) */\n" name i;
3398        List.iter (generate_test_command_call test_name)
3399          [["umount_all"];
3400           ["lvm_remove_all"]]
3401    | InitBasicFS ->
3402        pr "  /* InitBasicFS for %s (%d): create ext2 on /dev/sda1 */\n" name i;
3403        List.iter (generate_test_command_call test_name)
3404          [["umount_all"];
3405           ["lvm_remove_all"];
3406           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
3407           ["mkfs"; "ext2"; "/dev/sda1"];
3408           ["mount"; "/dev/sda1"; "/"]]
3409    | InitBasicFSonLVM ->
3410        pr "  /* InitBasicFSonLVM for %s (%d): create ext2 on /dev/VG/LV */\n"
3411          name i;
3412        List.iter (generate_test_command_call test_name)
3413          [["umount_all"];
3414           ["lvm_remove_all"];
3415           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
3416           ["pvcreate"; "/dev/sda1"];
3417           ["vgcreate"; "VG"; "/dev/sda1"];
3418           ["lvcreate"; "LV"; "VG"; "8"];
3419           ["mkfs"; "ext2"; "/dev/VG/LV"];
3420           ["mount"; "/dev/VG/LV"; "/"]]
3421   );
3422
3423   let get_seq_last = function
3424     | [] ->
3425         failwithf "%s: you cannot use [] (empty list) when expecting a command"
3426           test_name
3427     | seq ->
3428         let seq = List.rev seq in
3429         List.rev (List.tl seq), List.hd seq
3430   in
3431
3432   (match test with
3433    | TestRun seq ->
3434        pr "  /* TestRun for %s (%d) */\n" name i;
3435        List.iter (generate_test_command_call test_name) seq
3436    | TestOutput (seq, expected) ->
3437        pr "  /* TestOutput for %s (%d) */\n" name i;
3438        let seq, last = get_seq_last seq in
3439        let test () =
3440          pr "    if (strcmp (r, \"%s\") != 0) {\n" (c_quote expected);
3441          pr "      fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r);\n" test_name (c_quote expected);
3442          pr "      return -1;\n";
3443          pr "    }\n"
3444        in
3445        List.iter (generate_test_command_call test_name) seq;
3446        generate_test_command_call ~test test_name last
3447    | TestOutputList (seq, expected) ->
3448        pr "  /* TestOutputList for %s (%d) */\n" name i;
3449        let seq, last = get_seq_last seq in
3450        let test () =
3451          iteri (
3452            fun i str ->
3453              pr "    if (!r[%d]) {\n" i;
3454              pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
3455              pr "      print_strings (r);\n";
3456              pr "      return -1;\n";
3457              pr "    }\n";
3458              pr "    if (strcmp (r[%d], \"%s\") != 0) {\n" i (c_quote str);
3459              pr "      fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r[%d]);\n" test_name (c_quote str) i;
3460              pr "      return -1;\n";
3461              pr "    }\n"
3462          ) expected;
3463          pr "    if (r[%d] != NULL) {\n" (List.length expected);
3464          pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
3465            test_name;
3466          pr "      print_strings (r);\n";
3467          pr "      return -1;\n";
3468          pr "    }\n"
3469        in
3470        List.iter (generate_test_command_call test_name) seq;
3471        generate_test_command_call ~test test_name last
3472    | TestOutputInt (seq, expected) ->
3473        pr "  /* TestOutputInt for %s (%d) */\n" name i;
3474        let seq, last = get_seq_last seq in
3475        let test () =
3476          pr "    if (r != %d) {\n" expected;
3477          pr "      fprintf (stderr, \"%s: expected %d but got %%d\\n\","
3478            test_name expected;
3479          pr "               (int) r);\n";
3480          pr "      return -1;\n";
3481          pr "    }\n"
3482        in
3483        List.iter (generate_test_command_call test_name) seq;
3484        generate_test_command_call ~test test_name last
3485    | TestOutputTrue seq ->
3486        pr "  /* TestOutputTrue for %s (%d) */\n" name i;
3487        let seq, last = get_seq_last seq in
3488        let test () =
3489          pr "    if (!r) {\n";
3490          pr "      fprintf (stderr, \"%s: expected true, got false\\n\");\n"
3491            test_name;
3492          pr "      return -1;\n";
3493          pr "    }\n"
3494        in
3495        List.iter (generate_test_command_call test_name) seq;
3496        generate_test_command_call ~test test_name last
3497    | TestOutputFalse seq ->
3498        pr "  /* TestOutputFalse for %s (%d) */\n" name i;
3499        let seq, last = get_seq_last seq in
3500        let test () =
3501          pr "    if (r) {\n";
3502          pr "      fprintf (stderr, \"%s: expected false, got true\\n\");\n"
3503            test_name;
3504          pr "      return -1;\n";
3505          pr "    }\n"
3506        in
3507        List.iter (generate_test_command_call test_name) seq;
3508        generate_test_command_call ~test test_name last
3509    | TestOutputLength (seq, expected) ->
3510        pr "  /* TestOutputLength for %s (%d) */\n" name i;
3511        let seq, last = get_seq_last seq in
3512        let test () =
3513          pr "    int j;\n";
3514          pr "    for (j = 0; j < %d; ++j)\n" expected;
3515          pr "      if (r[j] == NULL) {\n";
3516          pr "        fprintf (stderr, \"%s: short list returned\\n\");\n"
3517            test_name;
3518          pr "        print_strings (r);\n";
3519          pr "        return -1;\n";
3520          pr "      }\n";
3521          pr "    if (r[j] != NULL) {\n";
3522          pr "      fprintf (stderr, \"%s: long list returned\\n\");\n"
3523            test_name;
3524          pr "      print_strings (r);\n";
3525          pr "      return -1;\n";
3526          pr "    }\n"
3527        in
3528        List.iter (generate_test_command_call test_name) seq;
3529        generate_test_command_call ~test test_name last
3530    | TestOutputStruct (seq, checks) ->
3531        pr "  /* TestOutputStruct for %s (%d) */\n" name i;
3532        let seq, last = get_seq_last seq in
3533        let test () =
3534          List.iter (
3535            function
3536            | CompareWithInt (field, expected) ->
3537                pr "    if (r->%s != %d) {\n" field expected;
3538                pr "      fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
3539                  test_name field expected;
3540                pr "               (int) r->%s);\n" field;
3541                pr "      return -1;\n";
3542                pr "    }\n"
3543            | CompareWithString (field, expected) ->
3544                pr "    if (strcmp (r->%s, \"%s\") != 0) {\n" field expected;
3545                pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
3546                  test_name field expected;
3547                pr "               r->%s);\n" field;
3548                pr "      return -1;\n";
3549                pr "    }\n"
3550            | CompareFieldsIntEq (field1, field2) ->
3551                pr "    if (r->%s != r->%s) {\n" field1 field2;
3552                pr "      fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
3553                  test_name field1 field2;
3554                pr "               (int) r->%s, (int) r->%s);\n" field1 field2;
3555                pr "      return -1;\n";
3556                pr "    }\n"
3557            | CompareFieldsStrEq (field1, field2) ->
3558                pr "    if (strcmp (r->%s, r->%s) != 0) {\n" field1 field2;
3559                pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
3560                  test_name field1 field2;
3561                pr "               r->%s, r->%s);\n" field1 field2;
3562                pr "      return -1;\n";
3563                pr "    }\n"
3564          ) checks
3565        in
3566        List.iter (generate_test_command_call test_name) seq;
3567        generate_test_command_call ~test test_name last
3568    | TestLastFail seq ->
3569        pr "  /* TestLastFail for %s (%d) */\n" name i;
3570        let seq, last = get_seq_last seq in
3571        List.iter (generate_test_command_call test_name) seq;
3572        generate_test_command_call test_name ~expect_error:true last
3573   );
3574
3575   pr "  return 0;\n";
3576   pr "}\n";
3577   pr "\n";
3578   test_name
3579
3580 (* Generate the code to run a command, leaving the result in 'r'.
3581  * If you expect to get an error then you should set expect_error:true.
3582  *)
3583 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
3584   match cmd with
3585   | [] -> assert false
3586   | name :: args ->
3587       (* Look up the command to find out what args/ret it has. *)
3588       let style =
3589         try
3590           let _, style, _, _, _, _, _ =
3591             List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
3592           style
3593         with Not_found ->
3594           failwithf "%s: in test, command %s was not found" test_name name in
3595
3596       if List.length (snd style) <> List.length args then
3597         failwithf "%s: in test, wrong number of args given to %s"
3598           test_name name;
3599
3600       pr "  {\n";
3601
3602       List.iter (
3603         function
3604         | String _, _
3605         | OptString _, _
3606         | Int _, _
3607         | Bool _, _ -> ()
3608         | FileIn _, _ | FileOut _, _ -> ()
3609         | StringList n, arg ->
3610             pr "    char *%s[] = {\n" n;
3611             let strs = string_split " " arg in
3612             List.iter (
3613               fun str -> pr "      \"%s\",\n" (c_quote str)
3614             ) strs;
3615             pr "      NULL\n";
3616             pr "    };\n";
3617       ) (List.combine (snd style) args);
3618
3619       let error_code =
3620         match fst style with
3621         | RErr | RInt _ | RBool _ -> pr "    int r;\n"; "-1"
3622         | RInt64 _ -> pr "    int64_t r;\n"; "-1"
3623         | RConstString _ -> pr "    const char *r;\n"; "NULL"
3624         | RString _ -> pr "    char *r;\n"; "NULL"
3625         | RStringList _ | RHashtable _ ->
3626             pr "    char **r;\n";
3627             pr "    int i;\n";
3628             "NULL"
3629         | RIntBool _ ->
3630             pr "    struct guestfs_int_bool *r;\n"; "NULL"
3631         | RPVList _ ->
3632             pr "    struct guestfs_lvm_pv_list *r;\n"; "NULL"
3633         | RVGList _ ->
3634             pr "    struct guestfs_lvm_vg_list *r;\n"; "NULL"
3635         | RLVList _ ->
3636             pr "    struct guestfs_lvm_lv_list *r;\n"; "NULL"
3637         | RStat _ ->
3638             pr "    struct guestfs_stat *r;\n"; "NULL"
3639         | RStatVFS _ ->
3640             pr "    struct guestfs_statvfs *r;\n"; "NULL" in
3641
3642       pr "    suppress_error = %d;\n" (if expect_error then 1 else 0);
3643       pr "    r = guestfs_%s (g" name;
3644
3645       (* Generate the parameters. *)
3646       List.iter (
3647         function
3648         | String _, arg
3649         | FileIn _, arg | FileOut _, arg ->
3650             pr ", \"%s\"" (c_quote arg)
3651         | OptString _, arg ->
3652             if arg = "NULL" then pr ", NULL" else pr ", \"%s\"" (c_quote arg)
3653         | StringList n, _ ->
3654             pr ", %s" n
3655         | Int _, arg ->
3656             let i =
3657               try int_of_string arg
3658               with Failure "int_of_string" ->
3659                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
3660             pr ", %d" i
3661         | Bool _, arg ->
3662             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
3663       ) (List.combine (snd style) args);
3664
3665       pr ");\n";
3666       if not expect_error then
3667         pr "    if (r == %s)\n" error_code
3668       else
3669         pr "    if (r != %s)\n" error_code;
3670       pr "      return -1;\n";
3671
3672       (* Insert the test code. *)
3673       (match test with
3674        | None -> ()
3675        | Some f -> f ()
3676       );
3677
3678       (match fst style with
3679        | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _ -> ()
3680        | RString _ -> pr "    free (r);\n"
3681        | RStringList _ | RHashtable _ ->
3682            pr "    for (i = 0; r[i] != NULL; ++i)\n";
3683            pr "      free (r[i]);\n";
3684            pr "    free (r);\n"
3685        | RIntBool _ ->
3686            pr "    guestfs_free_int_bool (r);\n"
3687        | RPVList _ ->
3688            pr "    guestfs_free_lvm_pv_list (r);\n"
3689        | RVGList _ ->
3690            pr "    guestfs_free_lvm_vg_list (r);\n"
3691        | RLVList _ ->
3692            pr "    guestfs_free_lvm_lv_list (r);\n"
3693        | RStat _ | RStatVFS _ ->
3694            pr "    free (r);\n"
3695       );
3696
3697       pr "  }\n"
3698
3699 and c_quote str =
3700   let str = replace_str str "\r" "\\r" in
3701   let str = replace_str str "\n" "\\n" in
3702   let str = replace_str str "\t" "\\t" in
3703   str
3704
3705 (* Generate a lot of different functions for guestfish. *)
3706 and generate_fish_cmds () =
3707   generate_header CStyle GPLv2;
3708
3709   let all_functions =
3710     List.filter (
3711       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3712     ) all_functions in
3713   let all_functions_sorted =
3714     List.filter (
3715       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3716     ) all_functions_sorted in
3717
3718   pr "#include <stdio.h>\n";
3719   pr "#include <stdlib.h>\n";
3720   pr "#include <string.h>\n";
3721   pr "#include <inttypes.h>\n";
3722   pr "\n";
3723   pr "#include <guestfs.h>\n";
3724   pr "#include \"fish.h\"\n";
3725   pr "\n";
3726
3727   (* list_commands function, which implements guestfish -h *)
3728   pr "void list_commands (void)\n";
3729   pr "{\n";
3730   pr "  printf (\"    %%-16s     %%s\\n\", \"Command\", \"Description\");\n";
3731   pr "  list_builtin_commands ();\n";
3732   List.iter (
3733     fun (name, _, _, flags, _, shortdesc, _) ->
3734       let name = replace_char name '_' '-' in
3735       pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
3736         name shortdesc
3737   ) all_functions_sorted;
3738   pr "  printf (\"    Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
3739   pr "}\n";
3740   pr "\n";
3741
3742   (* display_command function, which implements guestfish -h cmd *)
3743   pr "void display_command (const char *cmd)\n";
3744   pr "{\n";
3745   List.iter (
3746     fun (name, style, _, flags, _, shortdesc, longdesc) ->
3747       let name2 = replace_char name '_' '-' in
3748       let alias =
3749         try find_map (function FishAlias n -> Some n | _ -> None) flags
3750         with Not_found -> name in
3751       let longdesc = replace_str longdesc "C<guestfs_" "C<" in
3752       let synopsis =
3753         match snd style with
3754         | [] -> name2
3755         | args ->
3756             sprintf "%s <%s>"
3757               name2 (String.concat "> <" (List.map name_of_argt args)) in
3758
3759       let warnings =
3760         if List.mem ProtocolLimitWarning flags then
3761           ("\n\n" ^ protocol_limit_warning)
3762         else "" in
3763
3764       (* For DangerWillRobinson commands, we should probably have
3765        * guestfish prompt before allowing you to use them (especially
3766        * in interactive mode). XXX
3767        *)
3768       let warnings =
3769         warnings ^
3770           if List.mem DangerWillRobinson flags then
3771             ("\n\n" ^ danger_will_robinson)
3772           else "" in
3773
3774       let describe_alias =
3775         if name <> alias then
3776           sprintf "\n\nYou can use '%s' as an alias for this command." alias
3777         else "" in
3778
3779       pr "  if (";
3780       pr "strcasecmp (cmd, \"%s\") == 0" name;
3781       if name <> name2 then
3782         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
3783       if name <> alias then
3784         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
3785       pr ")\n";
3786       pr "    pod2text (\"%s - %s\", %S);\n"
3787         name2 shortdesc
3788         (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings ^ describe_alias);
3789       pr "  else\n"
3790   ) all_functions;
3791   pr "    display_builtin_command (cmd);\n";
3792   pr "}\n";
3793   pr "\n";
3794
3795   (* print_{pv,vg,lv}_list functions *)
3796   List.iter (
3797     function
3798     | typ, cols ->
3799         pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
3800         pr "{\n";
3801         pr "  int i;\n";
3802         pr "\n";
3803         List.iter (
3804           function
3805           | name, `String ->
3806               pr "  printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
3807           | name, `UUID ->
3808               pr "  printf (\"%s: \");\n" name;
3809               pr "  for (i = 0; i < 32; ++i)\n";
3810               pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
3811               pr "  printf (\"\\n\");\n"
3812           | name, `Bytes ->
3813               pr "  printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
3814           | name, `Int ->
3815               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
3816           | name, `OptPercent ->
3817               pr "  if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
3818                 typ name name typ name;
3819               pr "  else printf (\"%s: \\n\");\n" name
3820         ) cols;
3821         pr "}\n";
3822         pr "\n";
3823         pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
3824           typ typ typ;
3825         pr "{\n";
3826         pr "  int i;\n";
3827         pr "\n";
3828         pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
3829         pr "    print_%s (&%ss->val[i]);\n" typ typ;
3830         pr "}\n";
3831         pr "\n";
3832   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
3833
3834   (* print_{stat,statvfs} functions *)
3835   List.iter (
3836     function
3837     | typ, cols ->
3838         pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
3839         pr "{\n";
3840         List.iter (
3841           function
3842           | name, `Int ->
3843               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
3844         ) cols;
3845         pr "}\n";
3846         pr "\n";
3847   ) ["stat", stat_cols; "statvfs", statvfs_cols];
3848
3849   (* run_<action> actions *)
3850   List.iter (
3851     fun (name, style, _, flags, _, _, _) ->
3852       pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
3853       pr "{\n";
3854       (match fst style with
3855        | RErr
3856        | RInt _
3857        | RBool _ -> pr "  int r;\n"
3858        | RInt64 _ -> pr "  int64_t r;\n"
3859        | RConstString _ -> pr "  const char *r;\n"
3860        | RString _ -> pr "  char *r;\n"
3861        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
3862        | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"
3863        | RPVList _ -> pr "  struct guestfs_lvm_pv_list *r;\n"
3864        | RVGList _ -> pr "  struct guestfs_lvm_vg_list *r;\n"
3865        | RLVList _ -> pr "  struct guestfs_lvm_lv_list *r;\n"
3866        | RStat _ -> pr "  struct guestfs_stat *r;\n"
3867        | RStatVFS _ -> pr "  struct guestfs_statvfs *r;\n"
3868       );
3869       List.iter (
3870         function
3871         | String n
3872         | OptString n
3873         | FileIn n
3874         | FileOut n -> pr "  const char *%s;\n" n
3875         | StringList n -> pr "  char **%s;\n" n
3876         | Bool n -> pr "  int %s;\n" n
3877         | Int n -> pr "  int %s;\n" n
3878       ) (snd style);
3879
3880       (* Check and convert parameters. *)
3881       let argc_expected = List.length (snd style) in
3882       pr "  if (argc != %d) {\n" argc_expected;
3883       pr "    fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
3884         argc_expected;
3885       pr "    fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
3886       pr "    return -1;\n";
3887       pr "  }\n";
3888       iteri (
3889         fun i ->
3890           function
3891           | String name -> pr "  %s = argv[%d];\n" name i
3892           | OptString name ->
3893               pr "  %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
3894                 name i i
3895           | FileIn name ->
3896               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdin\";\n"
3897                 name i i
3898           | FileOut name ->
3899               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdout\";\n"
3900                 name i i
3901           | StringList name ->
3902               pr "  %s = parse_string_list (argv[%d]);\n" name i
3903           | Bool name ->
3904               pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
3905           | Int name ->
3906               pr "  %s = atoi (argv[%d]);\n" name i
3907       ) (snd style);
3908
3909       (* Call C API function. *)
3910       let fn =
3911         try find_map (function FishAction n -> Some n | _ -> None) flags
3912         with Not_found -> sprintf "guestfs_%s" name in
3913       pr "  r = %s " fn;
3914       generate_call_args ~handle:"g" (snd style);
3915       pr ";\n";
3916
3917       (* Check return value for errors and display command results. *)
3918       (match fst style with
3919        | RErr -> pr "  return r;\n"
3920        | RInt _ ->
3921            pr "  if (r == -1) return -1;\n";
3922            pr "  printf (\"%%d\\n\", r);\n";
3923            pr "  return 0;\n"
3924        | RInt64 _ ->
3925            pr "  if (r == -1) return -1;\n";
3926            pr "  printf (\"%%\" PRIi64 \"\\n\", r);\n";
3927            pr "  return 0;\n"
3928        | RBool _ ->
3929            pr "  if (r == -1) return -1;\n";
3930            pr "  if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
3931            pr "  return 0;\n"
3932        | RConstString _ ->
3933            pr "  if (r == NULL) return -1;\n";
3934            pr "  printf (\"%%s\\n\", r);\n";
3935            pr "  return 0;\n"
3936        | RString _ ->
3937            pr "  if (r == NULL) return -1;\n";
3938            pr "  printf (\"%%s\\n\", r);\n";
3939            pr "  free (r);\n";
3940            pr "  return 0;\n"
3941        | RStringList _ ->
3942            pr "  if (r == NULL) return -1;\n";
3943            pr "  print_strings (r);\n";
3944            pr "  free_strings (r);\n";
3945            pr "  return 0;\n"
3946        | RIntBool _ ->
3947            pr "  if (r == NULL) return -1;\n";
3948            pr "  printf (\"%%d, %%s\\n\", r->i,\n";
3949            pr "    r->b ? \"true\" : \"false\");\n";
3950            pr "  guestfs_free_int_bool (r);\n";
3951            pr "  return 0;\n"
3952        | RPVList _ ->
3953            pr "  if (r == NULL) return -1;\n";
3954            pr "  print_pv_list (r);\n";
3955            pr "  guestfs_free_lvm_pv_list (r);\n";
3956            pr "  return 0;\n"
3957        | RVGList _ ->
3958            pr "  if (r == NULL) return -1;\n";
3959            pr "  print_vg_list (r);\n";
3960            pr "  guestfs_free_lvm_vg_list (r);\n";
3961            pr "  return 0;\n"
3962        | RLVList _ ->
3963            pr "  if (r == NULL) return -1;\n";
3964            pr "  print_lv_list (r);\n";
3965            pr "  guestfs_free_lvm_lv_list (r);\n";
3966            pr "  return 0;\n"
3967        | RStat _ ->
3968            pr "  if (r == NULL) return -1;\n";
3969            pr "  print_stat (r);\n";
3970            pr "  free (r);\n";
3971            pr "  return 0;\n"
3972        | RStatVFS _ ->
3973            pr "  if (r == NULL) return -1;\n";
3974            pr "  print_statvfs (r);\n";
3975            pr "  free (r);\n";
3976            pr "  return 0;\n"
3977        | RHashtable _ ->
3978            pr "  if (r == NULL) return -1;\n";
3979            pr "  print_table (r);\n";
3980            pr "  free_strings (r);\n";
3981            pr "  return 0;\n"
3982       );
3983       pr "}\n";
3984       pr "\n"
3985   ) all_functions;
3986
3987   (* run_action function *)
3988   pr "int run_action (const char *cmd, int argc, char *argv[])\n";
3989   pr "{\n";
3990   List.iter (
3991     fun (name, _, _, flags, _, _, _) ->
3992       let name2 = replace_char name '_' '-' in
3993       let alias =
3994         try find_map (function FishAlias n -> Some n | _ -> None) flags
3995         with Not_found -> name in
3996       pr "  if (";
3997       pr "strcasecmp (cmd, \"%s\") == 0" name;
3998       if name <> name2 then
3999         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
4000       if name <> alias then
4001         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
4002       pr ")\n";
4003       pr "    return run_%s (cmd, argc, argv);\n" name;
4004       pr "  else\n";
4005   ) all_functions;
4006   pr "    {\n";
4007   pr "      fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
4008   pr "      return -1;\n";
4009   pr "    }\n";
4010   pr "  return 0;\n";
4011   pr "}\n";
4012   pr "\n"
4013
4014 (* Readline completion for guestfish. *)
4015 and generate_fish_completion () =
4016   generate_header CStyle GPLv2;
4017
4018   let all_functions =
4019     List.filter (
4020       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4021     ) all_functions in
4022
4023   pr "\
4024 #include <config.h>
4025
4026 #include <stdio.h>
4027 #include <stdlib.h>
4028 #include <string.h>
4029
4030 #ifdef HAVE_LIBREADLINE
4031 #include <readline/readline.h>
4032 #endif
4033
4034 #include \"fish.h\"
4035
4036 #ifdef HAVE_LIBREADLINE
4037
4038 static const char *const commands[] = {
4039 ";
4040
4041   (* Get the commands and sort them, including the aliases. *)
4042   let commands =
4043     List.map (
4044       fun (name, _, _, flags, _, _, _) ->
4045         let name2 = replace_char name '_' '-' in
4046         let alias =
4047           try find_map (function FishAlias n -> Some n | _ -> None) flags
4048           with Not_found -> name in
4049
4050         if name <> alias then [name2; alias] else [name2]
4051     ) all_functions in
4052   let commands = List.flatten commands in
4053   let commands = List.sort compare commands in
4054
4055   List.iter (pr "  \"%s\",\n") commands;
4056
4057   pr "  NULL
4058 };
4059
4060 static char *
4061 generator (const char *text, int state)
4062 {
4063   static int index, len;
4064   const char *name;
4065
4066   if (!state) {
4067     index = 0;
4068     len = strlen (text);
4069   }
4070
4071   while ((name = commands[index]) != NULL) {
4072     index++;
4073     if (strncasecmp (name, text, len) == 0)
4074       return strdup (name);
4075   }
4076
4077   return NULL;
4078 }
4079
4080 #endif /* HAVE_LIBREADLINE */
4081
4082 char **do_completion (const char *text, int start, int end)
4083 {
4084   char **matches = NULL;
4085
4086 #ifdef HAVE_LIBREADLINE
4087   if (start == 0)
4088     matches = rl_completion_matches (text, generator);
4089 #endif
4090
4091   return matches;
4092 }
4093 ";
4094
4095 (* Generate the POD documentation for guestfish. *)
4096 and generate_fish_actions_pod () =
4097   let all_functions_sorted =
4098     List.filter (
4099       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4100     ) all_functions_sorted in
4101
4102   let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
4103
4104   List.iter (
4105     fun (name, style, _, flags, _, _, longdesc) ->
4106       let longdesc =
4107         Str.global_substitute rex (
4108           fun s ->
4109             let sub =
4110               try Str.matched_group 1 s
4111               with Not_found ->
4112                 failwithf "error substituting C<guestfs_...> in longdesc of function %s" name in
4113             "C<" ^ replace_char sub '_' '-' ^ ">"
4114         ) longdesc in
4115       let name = replace_char name '_' '-' in
4116       let alias =
4117         try find_map (function FishAlias n -> Some n | _ -> None) flags
4118         with Not_found -> name in
4119
4120       pr "=head2 %s" name;
4121       if name <> alias then
4122         pr " | %s" alias;
4123       pr "\n";
4124       pr "\n";
4125       pr " %s" name;
4126       List.iter (
4127         function
4128         | String n -> pr " %s" n
4129         | OptString n -> pr " %s" n
4130         | StringList n -> pr " '%s ...'" n
4131         | Bool _ -> pr " true|false"
4132         | Int n -> pr " %s" n
4133         | FileIn n | FileOut n -> pr " (%s|-)" n
4134       ) (snd style);
4135       pr "\n";
4136       pr "\n";
4137       pr "%s\n\n" longdesc;
4138
4139       if List.exists (function FileIn _ | FileOut _ -> true
4140                       | _ -> false) (snd style) then
4141         pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
4142
4143       if List.mem ProtocolLimitWarning flags then
4144         pr "%s\n\n" protocol_limit_warning;
4145
4146       if List.mem DangerWillRobinson flags then
4147         pr "%s\n\n" danger_will_robinson
4148   ) all_functions_sorted
4149
4150 (* Generate a C function prototype. *)
4151 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
4152     ?(single_line = false) ?(newline = false) ?(in_daemon = false)
4153     ?(prefix = "")
4154     ?handle name style =
4155   if extern then pr "extern ";
4156   if static then pr "static ";
4157   (match fst style with
4158    | RErr -> pr "int "
4159    | RInt _ -> pr "int "
4160    | RInt64 _ -> pr "int64_t "
4161    | RBool _ -> pr "int "
4162    | RConstString _ -> pr "const char *"
4163    | RString _ -> pr "char *"
4164    | RStringList _ | RHashtable _ -> pr "char **"
4165    | RIntBool _ ->
4166        if not in_daemon then pr "struct guestfs_int_bool *"
4167        else pr "guestfs_%s_ret *" name
4168    | RPVList _ ->
4169        if not in_daemon then pr "struct guestfs_lvm_pv_list *"
4170        else pr "guestfs_lvm_int_pv_list *"
4171    | RVGList _ ->
4172        if not in_daemon then pr "struct guestfs_lvm_vg_list *"
4173        else pr "guestfs_lvm_int_vg_list *"
4174    | RLVList _ ->
4175        if not in_daemon then pr "struct guestfs_lvm_lv_list *"
4176        else pr "guestfs_lvm_int_lv_list *"
4177    | RStat _ ->
4178        if not in_daemon then pr "struct guestfs_stat *"
4179        else pr "guestfs_int_stat *"
4180    | RStatVFS _ ->
4181        if not in_daemon then pr "struct guestfs_statvfs *"
4182        else pr "guestfs_int_statvfs *"
4183   );
4184   pr "%s%s (" prefix name;
4185   if handle = None && List.length (snd style) = 0 then
4186     pr "void"
4187   else (
4188     let comma = ref false in
4189     (match handle with
4190      | None -> ()
4191      | Some handle -> pr "guestfs_h *%s" handle; comma := true
4192     );
4193     let next () =
4194       if !comma then (
4195         if single_line then pr ", " else pr ",\n\t\t"
4196       );
4197       comma := true
4198     in
4199     List.iter (
4200       function
4201       | String n
4202       | OptString n -> next (); pr "const char *%s" n
4203       | StringList n -> next (); pr "char * const* const %s" n
4204       | Bool n -> next (); pr "int %s" n
4205       | Int n -> next (); pr "int %s" n
4206       | FileIn n
4207       | FileOut n ->
4208           if not in_daemon then (next (); pr "const char *%s" n)
4209     ) (snd style);
4210   );
4211   pr ")";
4212   if semicolon then pr ";";
4213   if newline then pr "\n"
4214
4215 (* Generate C call arguments, eg "(handle, foo, bar)" *)
4216 and generate_call_args ?handle args =
4217   pr "(";
4218   let comma = ref false in
4219   (match handle with
4220    | None -> ()
4221    | Some handle -> pr "%s" handle; comma := true
4222   );
4223   List.iter (
4224     fun arg ->
4225       if !comma then pr ", ";
4226       comma := true;
4227       pr "%s" (name_of_argt arg)
4228   ) args;
4229   pr ")"
4230
4231 (* Generate the OCaml bindings interface. *)
4232 and generate_ocaml_mli () =
4233   generate_header OCamlStyle LGPLv2;
4234
4235   pr "\
4236 (** For API documentation you should refer to the C API
4237     in the guestfs(3) manual page.  The OCaml API uses almost
4238     exactly the same calls. *)
4239
4240 type t
4241 (** A [guestfs_h] handle. *)
4242
4243 exception Error of string
4244 (** This exception is raised when there is an error. *)
4245
4246 val create : unit -> t
4247
4248 val close : t -> unit
4249 (** Handles are closed by the garbage collector when they become
4250     unreferenced, but callers can also call this in order to
4251     provide predictable cleanup. *)
4252
4253 ";
4254   generate_ocaml_lvm_structure_decls ();
4255
4256   generate_ocaml_stat_structure_decls ();
4257
4258   (* The actions. *)
4259   List.iter (
4260     fun (name, style, _, _, _, shortdesc, _) ->
4261       generate_ocaml_prototype name style;
4262       pr "(** %s *)\n" shortdesc;
4263       pr "\n"
4264   ) all_functions
4265
4266 (* Generate the OCaml bindings implementation. *)
4267 and generate_ocaml_ml () =
4268   generate_header OCamlStyle LGPLv2;
4269
4270   pr "\
4271 type t
4272 exception Error of string
4273 external create : unit -> t = \"ocaml_guestfs_create\"
4274 external close : t -> unit = \"ocaml_guestfs_close\"
4275
4276 let () =
4277   Callback.register_exception \"ocaml_guestfs_error\" (Error \"\")
4278
4279 ";
4280
4281   generate_ocaml_lvm_structure_decls ();
4282
4283   generate_ocaml_stat_structure_decls ();
4284
4285   (* The actions. *)
4286   List.iter (
4287     fun (name, style, _, _, _, shortdesc, _) ->
4288       generate_ocaml_prototype ~is_external:true name style;
4289   ) all_functions
4290
4291 (* Generate the OCaml bindings C implementation. *)
4292 and generate_ocaml_c () =
4293   generate_header CStyle LGPLv2;
4294
4295   pr "\
4296 #include <stdio.h>
4297 #include <stdlib.h>
4298 #include <string.h>
4299
4300 #include <caml/config.h>
4301 #include <caml/alloc.h>
4302 #include <caml/callback.h>
4303 #include <caml/fail.h>
4304 #include <caml/memory.h>
4305 #include <caml/mlvalues.h>
4306 #include <caml/signals.h>
4307
4308 #include <guestfs.h>
4309
4310 #include \"guestfs_c.h\"
4311
4312 /* Copy a hashtable of string pairs into an assoc-list.  We return
4313  * the list in reverse order, but hashtables aren't supposed to be
4314  * ordered anyway.
4315  */
4316 static CAMLprim value
4317 copy_table (char * const * argv)
4318 {
4319   CAMLparam0 ();
4320   CAMLlocal5 (rv, pairv, kv, vv, cons);
4321   int i;
4322
4323   rv = Val_int (0);
4324   for (i = 0; argv[i] != NULL; i += 2) {
4325     kv = caml_copy_string (argv[i]);
4326     vv = caml_copy_string (argv[i+1]);
4327     pairv = caml_alloc (2, 0);
4328     Store_field (pairv, 0, kv);
4329     Store_field (pairv, 1, vv);
4330     cons = caml_alloc (2, 0);
4331     Store_field (cons, 1, rv);
4332     rv = cons;
4333     Store_field (cons, 0, pairv);
4334   }
4335
4336   CAMLreturn (rv);
4337 }
4338
4339 ";
4340
4341   (* LVM struct copy functions. *)
4342   List.iter (
4343     fun (typ, cols) ->
4344       let has_optpercent_col =
4345         List.exists (function (_, `OptPercent) -> true | _ -> false) cols in
4346
4347       pr "static CAMLprim value\n";
4348       pr "copy_lvm_%s (const struct guestfs_lvm_%s *%s)\n" typ typ typ;
4349       pr "{\n";
4350       pr "  CAMLparam0 ();\n";
4351       if has_optpercent_col then
4352         pr "  CAMLlocal3 (rv, v, v2);\n"
4353       else
4354         pr "  CAMLlocal2 (rv, v);\n";
4355       pr "\n";
4356       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
4357       iteri (
4358         fun i col ->
4359           (match col with
4360            | name, `String ->
4361                pr "  v = caml_copy_string (%s->%s);\n" typ name
4362            | name, `UUID ->
4363                pr "  v = caml_alloc_string (32);\n";
4364                pr "  memcpy (String_val (v), %s->%s, 32);\n" typ name
4365            | name, `Bytes
4366            | name, `Int ->
4367                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
4368            | name, `OptPercent ->
4369                pr "  if (%s->%s >= 0) { /* Some %s */\n" typ name name;
4370                pr "    v2 = caml_copy_double (%s->%s);\n" typ name;
4371                pr "    v = caml_alloc (1, 0);\n";
4372                pr "    Store_field (v, 0, v2);\n";
4373                pr "  } else /* None */\n";
4374                pr "    v = Val_int (0);\n";
4375           );
4376           pr "  Store_field (rv, %d, v);\n" i
4377       ) cols;
4378       pr "  CAMLreturn (rv);\n";
4379       pr "}\n";
4380       pr "\n";
4381
4382       pr "static CAMLprim value\n";
4383       pr "copy_lvm_%s_list (const struct guestfs_lvm_%s_list *%ss)\n"
4384         typ typ typ;
4385       pr "{\n";
4386       pr "  CAMLparam0 ();\n";
4387       pr "  CAMLlocal2 (rv, v);\n";
4388       pr "  int i;\n";
4389       pr "\n";
4390       pr "  if (%ss->len == 0)\n" typ;
4391       pr "    CAMLreturn (Atom (0));\n";
4392       pr "  else {\n";
4393       pr "    rv = caml_alloc (%ss->len, 0);\n" typ;
4394       pr "    for (i = 0; i < %ss->len; ++i) {\n" typ;
4395       pr "      v = copy_lvm_%s (&%ss->val[i]);\n" typ typ;
4396       pr "      caml_modify (&Field (rv, i), v);\n";
4397       pr "    }\n";
4398       pr "    CAMLreturn (rv);\n";
4399       pr "  }\n";
4400       pr "}\n";
4401       pr "\n";
4402   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
4403
4404   (* Stat copy functions. *)
4405   List.iter (
4406     fun (typ, cols) ->
4407       pr "static CAMLprim value\n";
4408       pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ;
4409       pr "{\n";
4410       pr "  CAMLparam0 ();\n";
4411       pr "  CAMLlocal2 (rv, v);\n";
4412       pr "\n";
4413       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
4414       iteri (
4415         fun i col ->
4416           (match col with
4417            | name, `Int ->
4418                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
4419           );
4420           pr "  Store_field (rv, %d, v);\n" i
4421       ) cols;
4422       pr "  CAMLreturn (rv);\n";
4423       pr "}\n";
4424       pr "\n";
4425   ) ["stat", stat_cols; "statvfs", statvfs_cols];
4426
4427   (* The wrappers. *)
4428   List.iter (
4429     fun (name, style, _, _, _, _, _) ->
4430       let params =
4431         "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
4432
4433       pr "CAMLprim value\n";
4434       pr "ocaml_guestfs_%s (value %s" name (List.hd params);
4435       List.iter (pr ", value %s") (List.tl params);
4436       pr ")\n";
4437       pr "{\n";
4438
4439       (match params with
4440        | [p1; p2; p3; p4; p5] ->
4441            pr "  CAMLparam5 (%s);\n" (String.concat ", " params)
4442        | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
4443            pr "  CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
4444            pr "  CAMLxparam%d (%s);\n"
4445              (List.length rest) (String.concat ", " rest)
4446        | ps ->
4447            pr "  CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
4448       );
4449       pr "  CAMLlocal1 (rv);\n";
4450       pr "\n";
4451
4452       pr "  guestfs_h *g = Guestfs_val (gv);\n";
4453       pr "  if (g == NULL)\n";
4454       pr "    caml_failwith (\"%s: used handle after closing it\");\n" name;
4455       pr "\n";
4456
4457       List.iter (
4458         function
4459         | String n
4460         | FileIn n
4461         | FileOut n ->
4462             pr "  const char *%s = String_val (%sv);\n" n n
4463         | OptString n ->
4464             pr "  const char *%s =\n" n;
4465             pr "    %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n"
4466               n n
4467         | StringList n ->
4468             pr "  char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n
4469         | Bool n ->
4470             pr "  int %s = Bool_val (%sv);\n" n n
4471         | Int n ->
4472             pr "  int %s = Int_val (%sv);\n" n n
4473       ) (snd style);
4474       let error_code =
4475         match fst style with
4476         | RErr -> pr "  int r;\n"; "-1"
4477         | RInt _ -> pr "  int r;\n"; "-1"
4478         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
4479         | RBool _ -> pr "  int r;\n"; "-1"
4480         | RConstString _ -> pr "  const char *r;\n"; "NULL"
4481         | RString _ -> pr "  char *r;\n"; "NULL"
4482         | RStringList _ ->
4483             pr "  int i;\n";
4484             pr "  char **r;\n";
4485             "NULL"
4486         | RIntBool _ ->
4487             pr "  struct guestfs_int_bool *r;\n"; "NULL"
4488         | RPVList _ ->
4489             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
4490         | RVGList _ ->
4491             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
4492         | RLVList _ ->
4493             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
4494         | RStat _ ->
4495             pr "  struct guestfs_stat *r;\n"; "NULL"
4496         | RStatVFS _ ->
4497             pr "  struct guestfs_statvfs *r;\n"; "NULL"
4498         | RHashtable _ ->
4499             pr "  int i;\n";
4500             pr "  char **r;\n";
4501             "NULL" in
4502       pr "\n";
4503
4504       pr "  caml_enter_blocking_section ();\n";
4505       pr "  r = guestfs_%s " name;
4506       generate_call_args ~handle:"g" (snd style);
4507       pr ";\n";
4508       pr "  caml_leave_blocking_section ();\n";
4509
4510       List.iter (
4511         function
4512         | StringList n ->
4513             pr "  ocaml_guestfs_free_strings (%s);\n" n;
4514         | String _ | OptString _ | Bool _ | Int _ | FileIn _ | FileOut _ -> ()
4515       ) (snd style);
4516
4517       pr "  if (r == %s)\n" error_code;
4518       pr "    ocaml_guestfs_raise_error (g, \"%s\");\n" name;
4519       pr "\n";
4520
4521       (match fst style with
4522        | RErr -> pr "  rv = Val_unit;\n"
4523        | RInt _ -> pr "  rv = Val_int (r);\n"
4524        | RInt64 _ ->
4525            pr "  rv = caml_copy_int64 (r);\n"
4526        | RBool _ -> pr "  rv = Val_bool (r);\n"
4527        | RConstString _ -> pr "  rv = caml_copy_string (r);\n"
4528        | RString _ ->
4529            pr "  rv = caml_copy_string (r);\n";
4530            pr "  free (r);\n"
4531        | RStringList _ ->
4532            pr "  rv = caml_copy_string_array ((const char **) r);\n";
4533            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
4534            pr "  free (r);\n"
4535        | RIntBool _ ->
4536            pr "  rv = caml_alloc (2, 0);\n";
4537            pr "  Store_field (rv, 0, Val_int (r->i));\n";
4538            pr "  Store_field (rv, 1, Val_bool (r->b));\n";
4539            pr "  guestfs_free_int_bool (r);\n";
4540        | RPVList _ ->
4541            pr "  rv = copy_lvm_pv_list (r);\n";
4542            pr "  guestfs_free_lvm_pv_list (r);\n";
4543        | RVGList _ ->
4544            pr "  rv = copy_lvm_vg_list (r);\n";
4545            pr "  guestfs_free_lvm_vg_list (r);\n";
4546        | RLVList _ ->
4547            pr "  rv = copy_lvm_lv_list (r);\n";
4548            pr "  guestfs_free_lvm_lv_list (r);\n";
4549        | RStat _ ->
4550            pr "  rv = copy_stat (r);\n";
4551            pr "  free (r);\n";
4552        | RStatVFS _ ->
4553            pr "  rv = copy_statvfs (r);\n";
4554            pr "  free (r);\n";
4555        | RHashtable _ ->
4556            pr "  rv = copy_table (r);\n";
4557            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
4558            pr "  free (r);\n";
4559       );
4560
4561       pr "  CAMLreturn (rv);\n";
4562       pr "}\n";
4563       pr "\n";
4564
4565       if List.length params > 5 then (
4566         pr "CAMLprim value\n";
4567         pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
4568         pr "{\n";
4569         pr "  return ocaml_guestfs_%s (argv[0]" name;
4570         iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
4571         pr ");\n";
4572         pr "}\n";
4573         pr "\n"
4574       )
4575   ) all_functions
4576
4577 and generate_ocaml_lvm_structure_decls () =
4578   List.iter (
4579     fun (typ, cols) ->
4580       pr "type lvm_%s = {\n" typ;
4581       List.iter (
4582         function
4583         | name, `String -> pr "  %s : string;\n" name
4584         | name, `UUID -> pr "  %s : string;\n" name
4585         | name, `Bytes -> pr "  %s : int64;\n" name
4586         | name, `Int -> pr "  %s : int64;\n" name
4587         | name, `OptPercent -> pr "  %s : float option;\n" name
4588       ) cols;
4589       pr "}\n";
4590       pr "\n"
4591   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
4592
4593 and generate_ocaml_stat_structure_decls () =
4594   List.iter (
4595     fun (typ, cols) ->
4596       pr "type %s = {\n" typ;
4597       List.iter (
4598         function
4599         | name, `Int -> pr "  %s : int64;\n" name
4600       ) cols;
4601       pr "}\n";
4602       pr "\n"
4603   ) ["stat", stat_cols; "statvfs", statvfs_cols]
4604
4605 and generate_ocaml_prototype ?(is_external = false) name style =
4606   if is_external then pr "external " else pr "val ";
4607   pr "%s : t -> " name;
4608   List.iter (
4609     function
4610     | String _ | FileIn _ | FileOut _ -> pr "string -> "
4611     | OptString _ -> pr "string option -> "
4612     | StringList _ -> pr "string array -> "
4613     | Bool _ -> pr "bool -> "
4614     | Int _ -> pr "int -> "
4615   ) (snd style);
4616   (match fst style with
4617    | RErr -> pr "unit" (* all errors are turned into exceptions *)
4618    | RInt _ -> pr "int"
4619    | RInt64 _ -> pr "int64"
4620    | RBool _ -> pr "bool"
4621    | RConstString _ -> pr "string"
4622    | RString _ -> pr "string"
4623    | RStringList _ -> pr "string array"
4624    | RIntBool _ -> pr "int * bool"
4625    | RPVList _ -> pr "lvm_pv array"
4626    | RVGList _ -> pr "lvm_vg array"
4627    | RLVList _ -> pr "lvm_lv array"
4628    | RStat _ -> pr "stat"
4629    | RStatVFS _ -> pr "statvfs"
4630    | RHashtable _ -> pr "(string * string) list"
4631   );
4632   if is_external then (
4633     pr " = ";
4634     if List.length (snd style) + 1 > 5 then
4635       pr "\"ocaml_guestfs_%s_byte\" " name;
4636     pr "\"ocaml_guestfs_%s\"" name
4637   );
4638   pr "\n"
4639
4640 (* Generate Perl xs code, a sort of crazy variation of C with macros. *)
4641 and generate_perl_xs () =
4642   generate_header CStyle LGPLv2;
4643
4644   pr "\
4645 #include \"EXTERN.h\"
4646 #include \"perl.h\"
4647 #include \"XSUB.h\"
4648
4649 #include <guestfs.h>
4650
4651 #ifndef PRId64
4652 #define PRId64 \"lld\"
4653 #endif
4654
4655 static SV *
4656 my_newSVll(long long val) {
4657 #ifdef USE_64_BIT_ALL
4658   return newSViv(val);
4659 #else
4660   char buf[100];
4661   int len;
4662   len = snprintf(buf, 100, \"%%\" PRId64, val);
4663   return newSVpv(buf, len);
4664 #endif
4665 }
4666
4667 #ifndef PRIu64
4668 #define PRIu64 \"llu\"
4669 #endif
4670
4671 static SV *
4672 my_newSVull(unsigned long long val) {
4673 #ifdef USE_64_BIT_ALL
4674   return newSVuv(val);
4675 #else
4676   char buf[100];
4677   int len;
4678   len = snprintf(buf, 100, \"%%\" PRIu64, val);
4679   return newSVpv(buf, len);
4680 #endif
4681 }
4682
4683 /* http://www.perlmonks.org/?node_id=680842 */
4684 static char **
4685 XS_unpack_charPtrPtr (SV *arg) {
4686   char **ret;
4687   AV *av;
4688   I32 i;
4689
4690   if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV)
4691     croak (\"array reference expected\");
4692
4693   av = (AV *)SvRV (arg);
4694   ret = malloc (av_len (av) + 1 + 1);
4695   if (!ret)
4696     croak (\"malloc failed\");
4697
4698   for (i = 0; i <= av_len (av); i++) {
4699     SV **elem = av_fetch (av, i, 0);
4700
4701     if (!elem || !*elem)
4702       croak (\"missing element in list\");
4703
4704     ret[i] = SvPV_nolen (*elem);
4705   }
4706
4707   ret[i] = NULL;
4708
4709   return ret;
4710 }
4711
4712 MODULE = Sys::Guestfs  PACKAGE = Sys::Guestfs
4713
4714 guestfs_h *
4715 _create ()
4716    CODE:
4717       RETVAL = guestfs_create ();
4718       if (!RETVAL)
4719         croak (\"could not create guestfs handle\");
4720       guestfs_set_error_handler (RETVAL, NULL, NULL);
4721  OUTPUT:
4722       RETVAL
4723
4724 void
4725 DESTROY (g)
4726       guestfs_h *g;
4727  PPCODE:
4728       guestfs_close (g);
4729
4730 ";
4731
4732   List.iter (
4733     fun (name, style, _, _, _, _, _) ->
4734       (match fst style with
4735        | RErr -> pr "void\n"
4736        | RInt _ -> pr "SV *\n"
4737        | RInt64 _ -> pr "SV *\n"
4738        | RBool _ -> pr "SV *\n"
4739        | RConstString _ -> pr "SV *\n"
4740        | RString _ -> pr "SV *\n"
4741        | RStringList _
4742        | RIntBool _
4743        | RPVList _ | RVGList _ | RLVList _
4744        | RStat _ | RStatVFS _
4745        | RHashtable _ ->
4746            pr "void\n" (* all lists returned implictly on the stack *)
4747       );
4748       (* Call and arguments. *)
4749       pr "%s " name;
4750       generate_call_args ~handle:"g" (snd style);
4751       pr "\n";
4752       pr "      guestfs_h *g;\n";
4753       List.iter (
4754         function
4755         | String n | FileIn n | FileOut n -> pr "      char *%s;\n" n
4756         | OptString n -> pr "      char *%s;\n" n
4757         | StringList n -> pr "      char **%s;\n" n
4758         | Bool n -> pr "      int %s;\n" n
4759         | Int n -> pr "      int %s;\n" n
4760       ) (snd style);
4761
4762       let do_cleanups () =
4763         List.iter (
4764           function
4765           | String _ | OptString _ | Bool _ | Int _
4766           | FileIn _ | FileOut _ -> ()
4767           | StringList n -> pr "      free (%s);\n" n
4768         ) (snd style)
4769       in
4770
4771       (* Code. *)
4772       (match fst style with
4773        | RErr ->
4774            pr "PREINIT:\n";
4775            pr "      int r;\n";
4776            pr " PPCODE:\n";
4777            pr "      r = guestfs_%s " name;
4778            generate_call_args ~handle:"g" (snd style);
4779            pr ";\n";
4780            do_cleanups ();
4781            pr "      if (r == -1)\n";
4782            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4783        | RInt n
4784        | RBool n ->
4785            pr "PREINIT:\n";
4786            pr "      int %s;\n" n;
4787            pr "   CODE:\n";
4788            pr "      %s = guestfs_%s " n name;
4789            generate_call_args ~handle:"g" (snd style);
4790            pr ";\n";
4791            do_cleanups ();
4792            pr "      if (%s == -1)\n" n;
4793            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4794            pr "      RETVAL = newSViv (%s);\n" n;
4795            pr " OUTPUT:\n";
4796            pr "      RETVAL\n"
4797        | RInt64 n ->
4798            pr "PREINIT:\n";
4799            pr "      int64_t %s;\n" n;
4800            pr "   CODE:\n";
4801            pr "      %s = guestfs_%s " n name;
4802            generate_call_args ~handle:"g" (snd style);
4803            pr ";\n";
4804            do_cleanups ();
4805            pr "      if (%s == -1)\n" n;
4806            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4807            pr "      RETVAL = my_newSVll (%s);\n" n;
4808            pr " OUTPUT:\n";
4809            pr "      RETVAL\n"
4810        | RConstString n ->
4811            pr "PREINIT:\n";
4812            pr "      const char *%s;\n" n;
4813            pr "   CODE:\n";
4814            pr "      %s = guestfs_%s " n name;
4815            generate_call_args ~handle:"g" (snd style);
4816            pr ";\n";
4817            do_cleanups ();
4818            pr "      if (%s == NULL)\n" n;
4819            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4820            pr "      RETVAL = newSVpv (%s, 0);\n" n;
4821            pr " OUTPUT:\n";
4822            pr "      RETVAL\n"
4823        | RString n ->
4824            pr "PREINIT:\n";
4825            pr "      char *%s;\n" n;
4826            pr "   CODE:\n";
4827            pr "      %s = guestfs_%s " n name;
4828            generate_call_args ~handle:"g" (snd style);
4829            pr ";\n";
4830            do_cleanups ();
4831            pr "      if (%s == NULL)\n" n;
4832            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4833            pr "      RETVAL = newSVpv (%s, 0);\n" n;
4834            pr "      free (%s);\n" n;
4835            pr " OUTPUT:\n";
4836            pr "      RETVAL\n"
4837        | RStringList n | RHashtable n ->
4838            pr "PREINIT:\n";
4839            pr "      char **%s;\n" n;
4840            pr "      int i, n;\n";
4841            pr " PPCODE:\n";
4842            pr "      %s = guestfs_%s " n name;
4843            generate_call_args ~handle:"g" (snd style);
4844            pr ";\n";
4845            do_cleanups ();
4846            pr "      if (%s == NULL)\n" n;
4847            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4848            pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
4849            pr "      EXTEND (SP, n);\n";
4850            pr "      for (i = 0; i < n; ++i) {\n";
4851            pr "        PUSHs (sv_2mortal (newSVpv (%s[i], 0)));\n" n;
4852            pr "        free (%s[i]);\n" n;
4853            pr "      }\n";
4854            pr "      free (%s);\n" n;
4855        | RIntBool _ ->
4856            pr "PREINIT:\n";
4857            pr "      struct guestfs_int_bool *r;\n";
4858            pr " PPCODE:\n";
4859            pr "      r = guestfs_%s " name;
4860            generate_call_args ~handle:"g" (snd style);
4861            pr ";\n";
4862            do_cleanups ();
4863            pr "      if (r == NULL)\n";
4864            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4865            pr "      EXTEND (SP, 2);\n";
4866            pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
4867            pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
4868            pr "      guestfs_free_int_bool (r);\n";
4869        | RPVList n ->
4870            generate_perl_lvm_code "pv" pv_cols name style n do_cleanups
4871        | RVGList n ->
4872            generate_perl_lvm_code "vg" vg_cols name style n do_cleanups
4873        | RLVList n ->
4874            generate_perl_lvm_code "lv" lv_cols name style n do_cleanups
4875        | RStat n ->
4876            generate_perl_stat_code "stat" stat_cols name style n do_cleanups
4877        | RStatVFS n ->
4878            generate_perl_stat_code
4879              "statvfs" statvfs_cols name style n do_cleanups
4880       );
4881
4882       pr "\n"
4883   ) all_functions
4884
4885 and generate_perl_lvm_code typ cols name style n do_cleanups =
4886   pr "PREINIT:\n";
4887   pr "      struct guestfs_lvm_%s_list *%s;\n" typ n;
4888   pr "      int i;\n";
4889   pr "      HV *hv;\n";
4890   pr " PPCODE:\n";
4891   pr "      %s = guestfs_%s " n name;
4892   generate_call_args ~handle:"g" (snd style);
4893   pr ";\n";
4894   do_cleanups ();
4895   pr "      if (%s == NULL)\n" n;
4896   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4897   pr "      EXTEND (SP, %s->len);\n" n;
4898   pr "      for (i = 0; i < %s->len; ++i) {\n" n;
4899   pr "        hv = newHV ();\n";
4900   List.iter (
4901     function
4902     | name, `String ->
4903         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 0), 0);\n"
4904           name (String.length name) n name
4905     | name, `UUID ->
4906         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 32), 0);\n"
4907           name (String.length name) n name
4908     | name, `Bytes ->
4909         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n"
4910           name (String.length name) n name
4911     | name, `Int ->
4912         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVll (%s->val[i].%s), 0);\n"
4913           name (String.length name) n name
4914     | name, `OptPercent ->
4915         pr "        (void) hv_store (hv, \"%s\", %d, newSVnv (%s->val[i].%s), 0);\n"
4916           name (String.length name) n name
4917   ) cols;
4918   pr "        PUSHs (sv_2mortal ((SV *) hv));\n";
4919   pr "      }\n";
4920   pr "      guestfs_free_lvm_%s_list (%s);\n" typ n
4921
4922 and generate_perl_stat_code typ cols name style n do_cleanups =
4923   pr "PREINIT:\n";
4924   pr "      struct guestfs_%s *%s;\n" typ n;
4925   pr " PPCODE:\n";
4926   pr "      %s = guestfs_%s " n name;
4927   generate_call_args ~handle:"g" (snd style);
4928   pr ";\n";
4929   do_cleanups ();
4930   pr "      if (%s == NULL)\n" n;
4931   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4932   pr "      EXTEND (SP, %d);\n" (List.length cols);
4933   List.iter (
4934     function
4935     | name, `Int ->
4936         pr "      PUSHs (sv_2mortal (my_newSVll (%s->%s)));\n" n name
4937   ) cols;
4938   pr "      free (%s);\n" n
4939
4940 (* Generate Sys/Guestfs.pm. *)
4941 and generate_perl_pm () =
4942   generate_header HashStyle LGPLv2;
4943
4944   pr "\
4945 =pod
4946
4947 =head1 NAME
4948
4949 Sys::Guestfs - Perl bindings for libguestfs
4950
4951 =head1 SYNOPSIS
4952
4953  use Sys::Guestfs;
4954  
4955  my $h = Sys::Guestfs->new ();
4956  $h->add_drive ('guest.img');
4957  $h->launch ();
4958  $h->wait_ready ();
4959  $h->mount ('/dev/sda1', '/');
4960  $h->touch ('/hello');
4961  $h->sync ();
4962
4963 =head1 DESCRIPTION
4964
4965 The C<Sys::Guestfs> module provides a Perl XS binding to the
4966 libguestfs API for examining and modifying virtual machine
4967 disk images.
4968
4969 Amongst the things this is good for: making batch configuration
4970 changes to guests, getting disk used/free statistics (see also:
4971 virt-df), migrating between virtualization systems (see also:
4972 virt-p2v), performing partial backups, performing partial guest
4973 clones, cloning guests and changing registry/UUID/hostname info, and
4974 much else besides.
4975
4976 Libguestfs uses Linux kernel and qemu code, and can access any type of
4977 guest filesystem that Linux and qemu can, including but not limited
4978 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
4979 schemes, qcow, qcow2, vmdk.
4980
4981 Libguestfs provides ways to enumerate guest storage (eg. partitions,
4982 LVs, what filesystem is in each LV, etc.).  It can also run commands
4983 in the context of the guest.  Also you can access filesystems over FTP.
4984
4985 =head1 ERRORS
4986
4987 All errors turn into calls to C<croak> (see L<Carp(3)>).
4988
4989 =head1 METHODS
4990
4991 =over 4
4992
4993 =cut
4994
4995 package Sys::Guestfs;
4996
4997 use strict;
4998 use warnings;
4999
5000 require XSLoader;
5001 XSLoader::load ('Sys::Guestfs');
5002
5003 =item $h = Sys::Guestfs->new ();
5004
5005 Create a new guestfs handle.
5006
5007 =cut
5008
5009 sub new {
5010   my $proto = shift;
5011   my $class = ref ($proto) || $proto;
5012
5013   my $self = Sys::Guestfs::_create ();
5014   bless $self, $class;
5015   return $self;
5016 }
5017
5018 ";
5019
5020   (* Actions.  We only need to print documentation for these as
5021    * they are pulled in from the XS code automatically.
5022    *)
5023   List.iter (
5024     fun (name, style, _, flags, _, _, longdesc) ->
5025       let longdesc = replace_str longdesc "C<guestfs_" "C<$h-E<gt>" in
5026       pr "=item ";
5027       generate_perl_prototype name style;
5028       pr "\n\n";
5029       pr "%s\n\n" longdesc;
5030       if List.mem ProtocolLimitWarning flags then
5031         pr "%s\n\n" protocol_limit_warning;
5032       if List.mem DangerWillRobinson flags then
5033         pr "%s\n\n" danger_will_robinson
5034   ) all_functions_sorted;
5035
5036   (* End of file. *)
5037   pr "\
5038 =cut
5039
5040 1;
5041
5042 =back
5043
5044 =head1 COPYRIGHT
5045
5046 Copyright (C) 2009 Red Hat Inc.
5047
5048 =head1 LICENSE
5049
5050 Please see the file COPYING.LIB for the full license.
5051
5052 =head1 SEE ALSO
5053
5054 L<guestfs(3)>, L<guestfish(1)>.
5055
5056 =cut
5057 "
5058
5059 and generate_perl_prototype name style =
5060   (match fst style with
5061    | RErr -> ()
5062    | RBool n
5063    | RInt n
5064    | RInt64 n
5065    | RConstString n
5066    | RString n -> pr "$%s = " n
5067    | RIntBool (n, m) -> pr "($%s, $%s) = " n m
5068    | RStringList n
5069    | RPVList n
5070    | RVGList n
5071    | RLVList n -> pr "@%s = " n
5072    | RStat n
5073    | RStatVFS n
5074    | RHashtable n -> pr "%%%s = " n
5075   );
5076   pr "$h->%s (" name;
5077   let comma = ref false in
5078   List.iter (
5079     fun arg ->
5080       if !comma then pr ", ";
5081       comma := true;
5082       match arg with
5083       | String n | OptString n | Bool n | Int n | FileIn n | FileOut n ->
5084           pr "$%s" n
5085       | StringList n ->
5086           pr "\\@%s" n
5087   ) (snd style);
5088   pr ");"
5089
5090 (* Generate Python C module. *)
5091 and generate_python_c () =
5092   generate_header CStyle LGPLv2;
5093
5094   pr "\
5095 #include <stdio.h>
5096 #include <stdlib.h>
5097 #include <assert.h>
5098
5099 #include <Python.h>
5100
5101 #include \"guestfs.h\"
5102
5103 typedef struct {
5104   PyObject_HEAD
5105   guestfs_h *g;
5106 } Pyguestfs_Object;
5107
5108 static guestfs_h *
5109 get_handle (PyObject *obj)
5110 {
5111   assert (obj);
5112   assert (obj != Py_None);
5113   return ((Pyguestfs_Object *) obj)->g;
5114 }
5115
5116 static PyObject *
5117 put_handle (guestfs_h *g)
5118 {
5119   assert (g);
5120   return
5121     PyCObject_FromVoidPtrAndDesc ((void *) g, (char *) \"guestfs_h\", NULL);
5122 }
5123
5124 /* This list should be freed (but not the strings) after use. */
5125 static const char **
5126 get_string_list (PyObject *obj)
5127 {
5128   int i, len;
5129   const char **r;
5130
5131   assert (obj);
5132
5133   if (!PyList_Check (obj)) {
5134     PyErr_SetString (PyExc_RuntimeError, \"expecting a list parameter\");
5135     return NULL;
5136   }
5137
5138   len = PyList_Size (obj);
5139   r = malloc (sizeof (char *) * (len+1));
5140   if (r == NULL) {
5141     PyErr_SetString (PyExc_RuntimeError, \"get_string_list: out of memory\");
5142     return NULL;
5143   }
5144
5145   for (i = 0; i < len; ++i)
5146     r[i] = PyString_AsString (PyList_GetItem (obj, i));
5147   r[len] = NULL;
5148
5149   return r;
5150 }
5151
5152 static PyObject *
5153 put_string_list (char * const * const argv)
5154 {
5155   PyObject *list;
5156   int argc, i;
5157
5158   for (argc = 0; argv[argc] != NULL; ++argc)
5159     ;
5160
5161   list = PyList_New (argc);
5162   for (i = 0; i < argc; ++i)
5163     PyList_SetItem (list, i, PyString_FromString (argv[i]));
5164
5165   return list;
5166 }
5167
5168 static PyObject *
5169 put_table (char * const * const argv)
5170 {
5171   PyObject *list, *item;
5172   int argc, i;
5173
5174   for (argc = 0; argv[argc] != NULL; ++argc)
5175     ;
5176
5177   list = PyList_New (argc >> 1);
5178   for (i = 0; i < argc; i += 2) {
5179     item = PyTuple_New (2);
5180     PyTuple_SetItem (item, 0, PyString_FromString (argv[i]));
5181     PyTuple_SetItem (item, 1, PyString_FromString (argv[i+1]));
5182     PyList_SetItem (list, i >> 1, item);
5183   }
5184
5185   return list;
5186 }
5187
5188 static void
5189 free_strings (char **argv)
5190 {
5191   int argc;
5192
5193   for (argc = 0; argv[argc] != NULL; ++argc)
5194     free (argv[argc]);
5195   free (argv);
5196 }
5197
5198 static PyObject *
5199 py_guestfs_create (PyObject *self, PyObject *args)
5200 {
5201   guestfs_h *g;
5202
5203   g = guestfs_create ();
5204   if (g == NULL) {
5205     PyErr_SetString (PyExc_RuntimeError,
5206                      \"guestfs.create: failed to allocate handle\");
5207     return NULL;
5208   }
5209   guestfs_set_error_handler (g, NULL, NULL);
5210   return put_handle (g);
5211 }
5212
5213 static PyObject *
5214 py_guestfs_close (PyObject *self, PyObject *args)
5215 {
5216   PyObject *py_g;
5217   guestfs_h *g;
5218
5219   if (!PyArg_ParseTuple (args, (char *) \"O:guestfs_close\", &py_g))
5220     return NULL;
5221   g = get_handle (py_g);
5222
5223   guestfs_close (g);
5224
5225   Py_INCREF (Py_None);
5226   return Py_None;
5227 }
5228
5229 ";
5230
5231   (* LVM structures, turned into Python dictionaries. *)
5232   List.iter (
5233     fun (typ, cols) ->
5234       pr "static PyObject *\n";
5235       pr "put_lvm_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
5236       pr "{\n";
5237       pr "  PyObject *dict;\n";
5238       pr "\n";
5239       pr "  dict = PyDict_New ();\n";
5240       List.iter (
5241         function
5242         | name, `String ->
5243             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5244             pr "                        PyString_FromString (%s->%s));\n"
5245               typ name
5246         | name, `UUID ->
5247             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5248             pr "                        PyString_FromStringAndSize (%s->%s, 32));\n"
5249               typ name
5250         | name, `Bytes ->
5251             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5252             pr "                        PyLong_FromUnsignedLongLong (%s->%s));\n"
5253               typ name
5254         | name, `Int ->
5255             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5256             pr "                        PyLong_FromLongLong (%s->%s));\n"
5257               typ name
5258         | name, `OptPercent ->
5259             pr "  if (%s->%s >= 0)\n" typ name;
5260             pr "    PyDict_SetItemString (dict, \"%s\",\n" name;
5261             pr "                          PyFloat_FromDouble ((double) %s->%s));\n"
5262               typ name;
5263             pr "  else {\n";
5264             pr "    Py_INCREF (Py_None);\n";
5265             pr "    PyDict_SetItemString (dict, \"%s\", Py_None);" name;
5266             pr "  }\n"
5267       ) cols;
5268       pr "  return dict;\n";
5269       pr "};\n";
5270       pr "\n";
5271
5272       pr "static PyObject *\n";
5273       pr "put_lvm_%s_list (struct guestfs_lvm_%s_list *%ss)\n" typ typ typ;
5274       pr "{\n";
5275       pr "  PyObject *list;\n";
5276       pr "  int i;\n";
5277       pr "\n";
5278       pr "  list = PyList_New (%ss->len);\n" typ;
5279       pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
5280       pr "    PyList_SetItem (list, i, put_lvm_%s (&%ss->val[i]));\n" typ typ;
5281       pr "  return list;\n";
5282       pr "};\n";
5283       pr "\n"
5284   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
5285
5286   (* Stat structures, turned into Python dictionaries. *)
5287   List.iter (
5288     fun (typ, cols) ->
5289       pr "static PyObject *\n";
5290       pr "put_%s (struct guestfs_%s *%s)\n" typ typ typ;
5291       pr "{\n";
5292       pr "  PyObject *dict;\n";
5293       pr "\n";
5294       pr "  dict = PyDict_New ();\n";
5295       List.iter (
5296         function
5297         | name, `Int ->
5298             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5299             pr "                        PyLong_FromLongLong (%s->%s));\n"
5300               typ name
5301       ) cols;
5302       pr "  return dict;\n";
5303       pr "};\n";
5304       pr "\n";
5305   ) ["stat", stat_cols; "statvfs", statvfs_cols];
5306
5307   (* Python wrapper functions. *)
5308   List.iter (
5309     fun (name, style, _, _, _, _, _) ->
5310       pr "static PyObject *\n";
5311       pr "py_guestfs_%s (PyObject *self, PyObject *args)\n" name;
5312       pr "{\n";
5313
5314       pr "  PyObject *py_g;\n";
5315       pr "  guestfs_h *g;\n";
5316       pr "  PyObject *py_r;\n";
5317
5318       let error_code =
5319         match fst style with
5320         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
5321         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
5322         | RConstString _ -> pr "  const char *r;\n"; "NULL"
5323         | RString _ -> pr "  char *r;\n"; "NULL"
5324         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
5325         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
5326         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
5327         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
5328         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
5329         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
5330         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
5331
5332       List.iter (
5333         function
5334         | String n | FileIn n | FileOut n -> pr "  const char *%s;\n" n
5335         | OptString n -> pr "  const char *%s;\n" n
5336         | StringList n ->
5337             pr "  PyObject *py_%s;\n" n;
5338             pr "  const char **%s;\n" n
5339         | Bool n -> pr "  int %s;\n" n
5340         | Int n -> pr "  int %s;\n" n
5341       ) (snd style);
5342
5343       pr "\n";
5344
5345       (* Convert the parameters. *)
5346       pr "  if (!PyArg_ParseTuple (args, (char *) \"O";
5347       List.iter (
5348         function
5349         | String _ | FileIn _ | FileOut _ -> pr "s"
5350         | OptString _ -> pr "z"
5351         | StringList _ -> pr "O"
5352         | Bool _ -> pr "i" (* XXX Python has booleans? *)
5353         | Int _ -> pr "i"
5354       ) (snd style);
5355       pr ":guestfs_%s\",\n" name;
5356       pr "                         &py_g";
5357       List.iter (
5358         function
5359         | String n | FileIn n | FileOut n -> pr ", &%s" n
5360         | OptString n -> pr ", &%s" n
5361         | StringList n -> pr ", &py_%s" n
5362         | Bool n -> pr ", &%s" n
5363         | Int n -> pr ", &%s" n
5364       ) (snd style);
5365
5366       pr "))\n";
5367       pr "    return NULL;\n";
5368
5369       pr "  g = get_handle (py_g);\n";
5370       List.iter (
5371         function
5372         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5373         | StringList n ->
5374             pr "  %s = get_string_list (py_%s);\n" n n;
5375             pr "  if (!%s) return NULL;\n" n
5376       ) (snd style);
5377
5378       pr "\n";
5379
5380       pr "  r = guestfs_%s " name;
5381       generate_call_args ~handle:"g" (snd style);
5382       pr ";\n";
5383
5384       List.iter (
5385         function
5386         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5387         | StringList n ->
5388             pr "  free (%s);\n" n
5389       ) (snd style);
5390
5391       pr "  if (r == %s) {\n" error_code;
5392       pr "    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));\n";
5393       pr "    return NULL;\n";
5394       pr "  }\n";
5395       pr "\n";
5396
5397       (match fst style with
5398        | RErr ->
5399            pr "  Py_INCREF (Py_None);\n";
5400            pr "  py_r = Py_None;\n"
5401        | RInt _
5402        | RBool _ -> pr "  py_r = PyInt_FromLong ((long) r);\n"
5403        | RInt64 _ -> pr "  py_r = PyLong_FromLongLong (r);\n"
5404        | RConstString _ -> pr "  py_r = PyString_FromString (r);\n"
5405        | RString _ ->
5406            pr "  py_r = PyString_FromString (r);\n";
5407            pr "  free (r);\n"
5408        | RStringList _ ->
5409            pr "  py_r = put_string_list (r);\n";
5410            pr "  free_strings (r);\n"
5411        | RIntBool _ ->
5412            pr "  py_r = PyTuple_New (2);\n";
5413            pr "  PyTuple_SetItem (py_r, 0, PyInt_FromLong ((long) r->i));\n";
5414            pr "  PyTuple_SetItem (py_r, 1, PyInt_FromLong ((long) r->b));\n";
5415            pr "  guestfs_free_int_bool (r);\n"
5416        | RPVList n ->
5417            pr "  py_r = put_lvm_pv_list (r);\n";
5418            pr "  guestfs_free_lvm_pv_list (r);\n"
5419        | RVGList n ->
5420            pr "  py_r = put_lvm_vg_list (r);\n";
5421            pr "  guestfs_free_lvm_vg_list (r);\n"
5422        | RLVList n ->
5423            pr "  py_r = put_lvm_lv_list (r);\n";
5424            pr "  guestfs_free_lvm_lv_list (r);\n"
5425        | RStat n ->
5426            pr "  py_r = put_stat (r);\n";
5427            pr "  free (r);\n"
5428        | RStatVFS n ->
5429            pr "  py_r = put_statvfs (r);\n";
5430            pr "  free (r);\n"
5431        | RHashtable n ->
5432            pr "  py_r = put_table (r);\n";
5433            pr "  free_strings (r);\n"
5434       );
5435
5436       pr "  return py_r;\n";
5437       pr "}\n";
5438       pr "\n"
5439   ) all_functions;
5440
5441   (* Table of functions. *)
5442   pr "static PyMethodDef methods[] = {\n";
5443   pr "  { (char *) \"create\", py_guestfs_create, METH_VARARGS, NULL },\n";
5444   pr "  { (char *) \"close\", py_guestfs_close, METH_VARARGS, NULL },\n";
5445   List.iter (
5446     fun (name, _, _, _, _, _, _) ->
5447       pr "  { (char *) \"%s\", py_guestfs_%s, METH_VARARGS, NULL },\n"
5448         name name
5449   ) all_functions;
5450   pr "  { NULL, NULL, 0, NULL }\n";
5451   pr "};\n";
5452   pr "\n";
5453
5454   (* Init function. *)
5455   pr "\
5456 void
5457 initlibguestfsmod (void)
5458 {
5459   static int initialized = 0;
5460
5461   if (initialized) return;
5462   Py_InitModule ((char *) \"libguestfsmod\", methods);
5463   initialized = 1;
5464 }
5465 "
5466
5467 (* Generate Python module. *)
5468 and generate_python_py () =
5469   generate_header HashStyle LGPLv2;
5470
5471   pr "\
5472 u\"\"\"Python bindings for libguestfs
5473
5474 import guestfs
5475 g = guestfs.GuestFS ()
5476 g.add_drive (\"guest.img\")
5477 g.launch ()
5478 g.wait_ready ()
5479 parts = g.list_partitions ()
5480
5481 The guestfs module provides a Python binding to the libguestfs API
5482 for examining and modifying virtual machine disk images.
5483
5484 Amongst the things this is good for: making batch configuration
5485 changes to guests, getting disk used/free statistics (see also:
5486 virt-df), migrating between virtualization systems (see also:
5487 virt-p2v), performing partial backups, performing partial guest
5488 clones, cloning guests and changing registry/UUID/hostname info, and
5489 much else besides.
5490
5491 Libguestfs uses Linux kernel and qemu code, and can access any type of
5492 guest filesystem that Linux and qemu can, including but not limited
5493 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
5494 schemes, qcow, qcow2, vmdk.
5495
5496 Libguestfs provides ways to enumerate guest storage (eg. partitions,
5497 LVs, what filesystem is in each LV, etc.).  It can also run commands
5498 in the context of the guest.  Also you can access filesystems over FTP.
5499
5500 Errors which happen while using the API are turned into Python
5501 RuntimeError exceptions.
5502
5503 To create a guestfs handle you usually have to perform the following
5504 sequence of calls:
5505
5506 # Create the handle, call add_drive at least once, and possibly
5507 # several times if the guest has multiple block devices:
5508 g = guestfs.GuestFS ()
5509 g.add_drive (\"guest.img\")
5510
5511 # Launch the qemu subprocess and wait for it to become ready:
5512 g.launch ()
5513 g.wait_ready ()
5514
5515 # Now you can issue commands, for example:
5516 logvols = g.lvs ()
5517
5518 \"\"\"
5519
5520 import libguestfsmod
5521
5522 class GuestFS:
5523     \"\"\"Instances of this class are libguestfs API handles.\"\"\"
5524
5525     def __init__ (self):
5526         \"\"\"Create a new libguestfs handle.\"\"\"
5527         self._o = libguestfsmod.create ()
5528
5529     def __del__ (self):
5530         libguestfsmod.close (self._o)
5531
5532 ";
5533
5534   List.iter (
5535     fun (name, style, _, flags, _, _, longdesc) ->
5536       let doc = replace_str longdesc "C<guestfs_" "C<g." in
5537       let doc =
5538         match fst style with
5539         | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _
5540         | RString _ -> doc
5541         | RStringList _ ->
5542             doc ^ "\n\nThis function returns a list of strings."
5543         | RIntBool _ ->
5544             doc ^ "\n\nThis function returns a tuple (int, bool).\n"
5545         | RPVList _ ->
5546             doc ^ "\n\nThis function returns a list of PVs.  Each PV is represented as a dictionary."
5547         | RVGList _ ->
5548             doc ^ "\n\nThis function returns a list of VGs.  Each VG is represented as a dictionary."
5549         | RLVList _ ->
5550             doc ^ "\n\nThis function returns a list of LVs.  Each LV is represented as a dictionary."
5551         | RStat _ ->
5552             doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the stat structure."
5553        | RStatVFS _ ->
5554             doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure."
5555        | RHashtable _ ->
5556             doc ^ "\n\nThis function returns a dictionary." in
5557       let doc =
5558         if List.mem ProtocolLimitWarning flags then
5559           doc ^ "\n\n" ^ protocol_limit_warning
5560         else doc in
5561       let doc =
5562         if List.mem DangerWillRobinson flags then
5563           doc ^ "\n\n" ^ danger_will_robinson
5564         else doc in
5565       let doc = pod2text ~width:60 name doc in
5566       let doc = List.map (fun line -> replace_str line "\\" "\\\\") doc in
5567       let doc = String.concat "\n        " doc in
5568
5569       pr "    def %s " name;
5570       generate_call_args ~handle:"self" (snd style);
5571       pr ":\n";
5572       pr "        u\"\"\"%s\"\"\"\n" doc;
5573       pr "        return libguestfsmod.%s " name;
5574       generate_call_args ~handle:"self._o" (snd style);
5575       pr "\n";
5576       pr "\n";
5577   ) all_functions
5578
5579 (* Useful if you need the longdesc POD text as plain text.  Returns a
5580  * list of lines.
5581  *
5582  * This is the slowest thing about autogeneration.
5583  *)
5584 and pod2text ~width name longdesc =
5585   let filename, chan = Filename.open_temp_file "gen" ".tmp" in
5586   fprintf chan "=head1 %s\n\n%s\n" name longdesc;
5587   close_out chan;
5588   let cmd = sprintf "pod2text -w %d %s" width (Filename.quote filename) in
5589   let chan = Unix.open_process_in cmd in
5590   let lines = ref [] in
5591   let rec loop i =
5592     let line = input_line chan in
5593     if i = 1 then               (* discard the first line of output *)
5594       loop (i+1)
5595     else (
5596       let line = triml line in
5597       lines := line :: !lines;
5598       loop (i+1)
5599     ) in
5600   let lines = try loop 1 with End_of_file -> List.rev !lines in
5601   Unix.unlink filename;
5602   match Unix.close_process_in chan with
5603   | Unix.WEXITED 0 -> lines
5604   | Unix.WEXITED i ->
5605       failwithf "pod2text: process exited with non-zero status (%d)" i
5606   | Unix.WSIGNALED i | Unix.WSTOPPED i ->
5607       failwithf "pod2text: process signalled or stopped by signal %d" i
5608
5609 (* Generate ruby bindings. *)
5610 and generate_ruby_c () =
5611   generate_header CStyle LGPLv2;
5612
5613   pr "\
5614 #include <stdio.h>
5615 #include <stdlib.h>
5616
5617 #include <ruby.h>
5618
5619 #include \"guestfs.h\"
5620
5621 #include \"extconf.h\"
5622
5623 static VALUE m_guestfs;                 /* guestfs module */
5624 static VALUE c_guestfs;                 /* guestfs_h handle */
5625 static VALUE e_Error;                   /* used for all errors */
5626
5627 static void ruby_guestfs_free (void *p)
5628 {
5629   if (!p) return;
5630   guestfs_close ((guestfs_h *) p);
5631 }
5632
5633 static VALUE ruby_guestfs_create (VALUE m)
5634 {
5635   guestfs_h *g;
5636
5637   g = guestfs_create ();
5638   if (!g)
5639     rb_raise (e_Error, \"failed to create guestfs handle\");
5640
5641   /* Don't print error messages to stderr by default. */
5642   guestfs_set_error_handler (g, NULL, NULL);
5643
5644   /* Wrap it, and make sure the close function is called when the
5645    * handle goes away.
5646    */
5647   return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g);
5648 }
5649
5650 static VALUE ruby_guestfs_close (VALUE gv)
5651 {
5652   guestfs_h *g;
5653   Data_Get_Struct (gv, guestfs_h, g);
5654
5655   ruby_guestfs_free (g);
5656   DATA_PTR (gv) = NULL;
5657
5658   return Qnil;
5659 }
5660
5661 ";
5662
5663   List.iter (
5664     fun (name, style, _, _, _, _, _) ->
5665       pr "static VALUE ruby_guestfs_%s (VALUE gv" name;
5666       List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) (snd style);
5667       pr ")\n";
5668       pr "{\n";
5669       pr "  guestfs_h *g;\n";
5670       pr "  Data_Get_Struct (gv, guestfs_h, g);\n";
5671       pr "  if (!g)\n";
5672       pr "    rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n"
5673         name;
5674       pr "\n";
5675
5676       List.iter (
5677         function
5678         | String n | FileIn n | FileOut n ->
5679             pr "  const char *%s = StringValueCStr (%sv);\n" n n;
5680             pr "  if (!%s)\n" n;
5681             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
5682             pr "              \"%s\", \"%s\");\n" n name
5683         | OptString n ->
5684             pr "  const char *%s = StringValueCStr (%sv);\n" n n
5685         | StringList n ->
5686             pr "  char **%s;" n;
5687             pr "  {\n";
5688             pr "    int i, len;\n";
5689             pr "    len = RARRAY_LEN (%sv);\n" n;
5690             pr "    %s = guestfs_safe_malloc (g, sizeof (char *) * (len+1));\n"
5691               n;
5692             pr "    for (i = 0; i < len; ++i) {\n";
5693             pr "      VALUE v = rb_ary_entry (%sv, i);\n" n;
5694             pr "      %s[i] = StringValueCStr (v);\n" n;
5695             pr "    }\n";
5696             pr "    %s[len] = NULL;\n" n;
5697             pr "  }\n";
5698         | Bool n
5699         | Int n ->
5700             pr "  int %s = NUM2INT (%sv);\n" n n
5701       ) (snd style);
5702       pr "\n";
5703
5704       let error_code =
5705         match fst style with
5706         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
5707         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
5708         | RConstString _ -> pr "  const char *r;\n"; "NULL"
5709         | RString _ -> pr "  char *r;\n"; "NULL"
5710         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
5711         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
5712         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
5713         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
5714         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
5715         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
5716         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
5717       pr "\n";
5718
5719       pr "  r = guestfs_%s " name;
5720       generate_call_args ~handle:"g" (snd style);
5721       pr ";\n";
5722
5723       List.iter (
5724         function
5725         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5726         | StringList n ->
5727             pr "  free (%s);\n" n
5728       ) (snd style);
5729
5730       pr "  if (r == %s)\n" error_code;
5731       pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n";
5732       pr "\n";
5733
5734       (match fst style with
5735        | RErr ->
5736            pr "  return Qnil;\n"
5737        | RInt _ | RBool _ ->
5738            pr "  return INT2NUM (r);\n"
5739        | RInt64 _ ->
5740            pr "  return ULL2NUM (r);\n"
5741        | RConstString _ ->
5742            pr "  return rb_str_new2 (r);\n";
5743        | RString _ ->
5744            pr "  VALUE rv = rb_str_new2 (r);\n";
5745            pr "  free (r);\n";
5746            pr "  return rv;\n";
5747        | RStringList _ ->
5748            pr "  int i, len = 0;\n";
5749            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
5750            pr "  VALUE rv = rb_ary_new2 (len);\n";
5751            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
5752            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
5753            pr "    free (r[i]);\n";
5754            pr "  }\n";
5755            pr "  free (r);\n";
5756            pr "  return rv;\n"
5757        | RIntBool _ ->
5758            pr "  VALUE rv = rb_ary_new2 (2);\n";
5759            pr "  rb_ary_push (rv, INT2NUM (r->i));\n";
5760            pr "  rb_ary_push (rv, INT2NUM (r->b));\n";
5761            pr "  guestfs_free_int_bool (r);\n";
5762            pr "  return rv;\n"
5763        | RPVList n ->
5764            generate_ruby_lvm_code "pv" pv_cols
5765        | RVGList n ->
5766            generate_ruby_lvm_code "vg" vg_cols
5767        | RLVList n ->
5768            generate_ruby_lvm_code "lv" lv_cols
5769        | RStat n ->
5770            pr "  VALUE rv = rb_hash_new ();\n";
5771            List.iter (
5772              function
5773              | name, `Int ->
5774                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
5775            ) stat_cols;
5776            pr "  free (r);\n";
5777            pr "  return rv;\n"
5778        | RStatVFS n ->
5779            pr "  VALUE rv = rb_hash_new ();\n";
5780            List.iter (
5781              function
5782              | name, `Int ->
5783                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
5784            ) statvfs_cols;
5785            pr "  free (r);\n";
5786            pr "  return rv;\n"
5787        | RHashtable _ ->
5788            pr "  VALUE rv = rb_hash_new ();\n";
5789            pr "  int i;\n";
5790            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
5791            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
5792            pr "    free (r[i]);\n";
5793            pr "    free (r[i+1]);\n";
5794            pr "  }\n";
5795            pr "  free (r);\n";
5796            pr "  return rv;\n"
5797       );
5798
5799       pr "}\n";
5800       pr "\n"
5801   ) all_functions;
5802
5803   pr "\
5804 /* Initialize the module. */
5805 void Init__guestfs ()
5806 {
5807   m_guestfs = rb_define_module (\"Guestfs\");
5808   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
5809   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
5810
5811   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
5812   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
5813
5814 ";
5815   (* Define the rest of the methods. *)
5816   List.iter (
5817     fun (name, style, _, _, _, _, _) ->
5818       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
5819       pr "        ruby_guestfs_%s, %d);\n" name (List.length (snd style))
5820   ) all_functions;
5821
5822   pr "}\n"
5823
5824 (* Ruby code to return an LVM struct list. *)
5825 and generate_ruby_lvm_code typ cols =
5826   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
5827   pr "  int i;\n";
5828   pr "  for (i = 0; i < r->len; ++i) {\n";
5829   pr "    VALUE hv = rb_hash_new ();\n";
5830   List.iter (
5831     function
5832     | name, `String ->
5833         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
5834     | name, `UUID ->
5835         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
5836     | name, `Bytes
5837     | name, `Int ->
5838         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
5839     | name, `OptPercent ->
5840         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
5841   ) cols;
5842   pr "    rb_ary_push (rv, hv);\n";
5843   pr "  }\n";
5844   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
5845   pr "  return rv;\n"
5846
5847 (* Generate Java bindings GuestFS.java file. *)
5848 and generate_java_java () =
5849   generate_header CStyle LGPLv2;
5850
5851   pr "\
5852 package com.redhat.et.libguestfs;
5853
5854 import java.util.HashMap;
5855 import com.redhat.et.libguestfs.LibGuestFSException;
5856 import com.redhat.et.libguestfs.PV;
5857 import com.redhat.et.libguestfs.VG;
5858 import com.redhat.et.libguestfs.LV;
5859 import com.redhat.et.libguestfs.Stat;
5860 import com.redhat.et.libguestfs.StatVFS;
5861 import com.redhat.et.libguestfs.IntBool;
5862
5863 /**
5864  * The GuestFS object is a libguestfs handle.
5865  *
5866  * @author rjones
5867  */
5868 public class GuestFS {
5869   // Load the native code.
5870   static {
5871     System.loadLibrary (\"guestfs_jni\");
5872   }
5873
5874   /**
5875    * The native guestfs_h pointer.
5876    */
5877   long g;
5878
5879   /**
5880    * Create a libguestfs handle.
5881    *
5882    * @throws LibGuestFSException
5883    */
5884   public GuestFS () throws LibGuestFSException
5885   {
5886     g = _create ();
5887   }
5888   private native long _create () throws LibGuestFSException;
5889
5890   /**
5891    * Close a libguestfs handle.
5892    *
5893    * You can also leave handles to be collected by the garbage
5894    * collector, but this method ensures that the resources used
5895    * by the handle are freed up immediately.  If you call any
5896    * other methods after closing the handle, you will get an
5897    * exception.
5898    *
5899    * @throws LibGuestFSException
5900    */
5901   public void close () throws LibGuestFSException
5902   {
5903     if (g != 0)
5904       _close (g);
5905     g = 0;
5906   }
5907   private native void _close (long g) throws LibGuestFSException;
5908
5909   public void finalize () throws LibGuestFSException
5910   {
5911     close ();
5912   }
5913
5914 ";
5915
5916   List.iter (
5917     fun (name, style, _, flags, _, shortdesc, longdesc) ->
5918       let doc = replace_str longdesc "C<guestfs_" "C<g." in
5919       let doc =
5920         if List.mem ProtocolLimitWarning flags then
5921           doc ^ "\n\n" ^ protocol_limit_warning
5922         else doc in
5923       let doc =
5924         if List.mem DangerWillRobinson flags then
5925           doc ^ "\n\n" ^ danger_will_robinson
5926         else doc in
5927       let doc = pod2text ~width:60 name doc in
5928       let doc = String.concat "\n   * " doc in
5929
5930       pr "  /**\n";
5931       pr "   * %s\n" shortdesc;
5932       pr "   *\n";
5933       pr "   * %s\n" doc;
5934       pr "   * @throws LibGuestFSException\n";
5935       pr "   */\n";
5936       pr "  ";
5937       generate_java_prototype ~public:true ~semicolon:false name style;
5938       pr "\n";
5939       pr "  {\n";
5940       pr "    if (g == 0)\n";
5941       pr "      throw new LibGuestFSException (\"%s: handle is closed\");\n"
5942         name;
5943       pr "    ";
5944       if fst style <> RErr then pr "return ";
5945       pr "_%s " name;
5946       generate_call_args ~handle:"g" (snd style);
5947       pr ";\n";
5948       pr "  }\n";
5949       pr "  ";
5950       generate_java_prototype ~privat:true ~native:true name style;
5951       pr "\n";
5952       pr "\n";
5953   ) all_functions;
5954
5955   pr "}\n"
5956
5957 and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false)
5958     ?(semicolon=true) name style =
5959   if privat then pr "private ";
5960   if public then pr "public ";
5961   if native then pr "native ";
5962
5963   (* return type *)
5964   (match fst style with
5965    | RErr -> pr "void ";
5966    | RInt _ -> pr "int ";
5967    | RInt64 _ -> pr "long ";
5968    | RBool _ -> pr "boolean ";
5969    | RConstString _ | RString _ -> pr "String ";
5970    | RStringList _ -> pr "String[] ";
5971    | RIntBool _ -> pr "IntBool ";
5972    | RPVList _ -> pr "PV[] ";
5973    | RVGList _ -> pr "VG[] ";
5974    | RLVList _ -> pr "LV[] ";
5975    | RStat _ -> pr "Stat ";
5976    | RStatVFS _ -> pr "StatVFS ";
5977    | RHashtable _ -> pr "HashMap<String,String> ";
5978   );
5979
5980   if native then pr "_%s " name else pr "%s " name;
5981   pr "(";
5982   let needs_comma = ref false in
5983   if native then (
5984     pr "long g";
5985     needs_comma := true
5986   );
5987
5988   (* args *)
5989   List.iter (
5990     fun arg ->
5991       if !needs_comma then pr ", ";
5992       needs_comma := true;
5993
5994       match arg with
5995       | String n
5996       | OptString n
5997       | FileIn n
5998       | FileOut n ->
5999           pr "String %s" n
6000       | StringList n ->
6001           pr "String[] %s" n
6002       | Bool n ->
6003           pr "boolean %s" n
6004       | Int n ->
6005           pr "int %s" n
6006   ) (snd style);
6007
6008   pr ")\n";
6009   pr "    throws LibGuestFSException";
6010   if semicolon then pr ";"
6011
6012 and generate_java_struct typ cols =
6013   generate_header CStyle LGPLv2;
6014
6015   pr "\
6016 package com.redhat.et.libguestfs;
6017
6018 /**
6019  * Libguestfs %s structure.
6020  *
6021  * @author rjones
6022  * @see GuestFS
6023  */
6024 public class %s {
6025 " typ typ;
6026
6027   List.iter (
6028     function
6029     | name, `String
6030     | name, `UUID -> pr "  public String %s;\n" name
6031     | name, `Bytes
6032     | name, `Int -> pr "  public long %s;\n" name
6033     | name, `OptPercent ->
6034         pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
6035         pr "  public float %s;\n" name
6036   ) cols;
6037
6038   pr "}\n"
6039
6040 and generate_java_c () =
6041   generate_header CStyle LGPLv2;
6042
6043   pr "\
6044 #include <stdio.h>
6045 #include <stdlib.h>
6046 #include <string.h>
6047
6048 #include \"com_redhat_et_libguestfs_GuestFS.h\"
6049 #include \"guestfs.h\"
6050
6051 /* Note that this function returns.  The exception is not thrown
6052  * until after the wrapper function returns.
6053  */
6054 static void
6055 throw_exception (JNIEnv *env, const char *msg)
6056 {
6057   jclass cl;
6058   cl = (*env)->FindClass (env,
6059                           \"com/redhat/et/libguestfs/LibGuestFSException\");
6060   (*env)->ThrowNew (env, cl, msg);
6061 }
6062
6063 JNIEXPORT jlong JNICALL
6064 Java_com_redhat_et_libguestfs_GuestFS__1create
6065   (JNIEnv *env, jobject obj)
6066 {
6067   guestfs_h *g;
6068
6069   g = guestfs_create ();
6070   if (g == NULL) {
6071     throw_exception (env, \"GuestFS.create: failed to allocate handle\");
6072     return 0;
6073   }
6074   guestfs_set_error_handler (g, NULL, NULL);
6075   return (jlong) (long) g;
6076 }
6077
6078 JNIEXPORT void JNICALL
6079 Java_com_redhat_et_libguestfs_GuestFS__1close
6080   (JNIEnv *env, jobject obj, jlong jg)
6081 {
6082   guestfs_h *g = (guestfs_h *) (long) jg;
6083   guestfs_close (g);
6084 }
6085
6086 ";
6087
6088   List.iter (
6089     fun (name, style, _, _, _, _, _) ->
6090       pr "JNIEXPORT ";
6091       (match fst style with
6092        | RErr -> pr "void ";
6093        | RInt _ -> pr "jint ";
6094        | RInt64 _ -> pr "jlong ";
6095        | RBool _ -> pr "jboolean ";
6096        | RConstString _ | RString _ -> pr "jstring ";
6097        | RIntBool _ | RStat _ | RStatVFS _ | RHashtable _ ->
6098            pr "jobject ";
6099        | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
6100            pr "jobjectArray ";
6101       );
6102       pr "JNICALL\n";
6103       pr "Java_com_redhat_et_libguestfs_GuestFS_";
6104       pr "%s" (replace_str ("_" ^ name) "_" "_1");
6105       pr "\n";
6106       pr "  (JNIEnv *env, jobject obj, jlong jg";
6107       List.iter (
6108         function
6109         | String n
6110         | OptString n
6111         | FileIn n
6112         | FileOut n ->
6113             pr ", jstring j%s" n
6114         | StringList n ->
6115             pr ", jobjectArray j%s" n
6116         | Bool n ->
6117             pr ", jboolean j%s" n
6118         | Int n ->
6119             pr ", jint j%s" n
6120       ) (snd style);
6121       pr ")\n";
6122       pr "{\n";
6123       pr "  guestfs_h *g = (guestfs_h *) (long) jg;\n";
6124       let error_code, no_ret =
6125         match fst style with
6126         | RErr -> pr "  int r;\n"; "-1", ""
6127         | RBool _
6128         | RInt _ -> pr "  int r;\n"; "-1", "0"
6129         | RInt64 _ -> pr "  int64_t r;\n"; "-1", "0"
6130         | RConstString _ -> pr "  const char *r;\n"; "NULL", "NULL"
6131         | RString _ ->
6132             pr "  jstring jr;\n";
6133             pr "  char *r;\n"; "NULL", "NULL"
6134         | RStringList _ ->
6135             pr "  jobjectArray jr;\n";
6136             pr "  int r_len;\n";
6137             pr "  jclass cl;\n";
6138             pr "  jstring jstr;\n";
6139             pr "  char **r;\n"; "NULL", "NULL"
6140         | RIntBool _ ->
6141             pr "  jobject jr;\n";
6142             pr "  jclass cl;\n";
6143             pr "  jfieldID fl;\n";
6144             pr "  struct guestfs_int_bool *r;\n"; "NULL", "NULL"
6145         | RStat _ ->
6146             pr "  jobject jr;\n";
6147             pr "  jclass cl;\n";
6148             pr "  jfieldID fl;\n";
6149             pr "  struct guestfs_stat *r;\n"; "NULL", "NULL"
6150         | RStatVFS _ ->
6151             pr "  jobject jr;\n";
6152             pr "  jclass cl;\n";
6153             pr "  jfieldID fl;\n";
6154             pr "  struct guestfs_statvfs *r;\n"; "NULL", "NULL"
6155         | RPVList _ ->
6156             pr "  jobjectArray jr;\n";
6157             pr "  jclass cl;\n";
6158             pr "  jfieldID fl;\n";
6159             pr "  jobject jfl;\n";
6160             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL", "NULL"
6161         | RVGList _ ->
6162             pr "  jobjectArray jr;\n";
6163             pr "  jclass cl;\n";
6164             pr "  jfieldID fl;\n";
6165             pr "  jobject jfl;\n";
6166             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL", "NULL"
6167         | RLVList _ ->
6168             pr "  jobjectArray jr;\n";
6169             pr "  jclass cl;\n";
6170             pr "  jfieldID fl;\n";
6171             pr "  jobject jfl;\n";
6172             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL", "NULL"
6173         | RHashtable _ -> pr "  char **r;\n"; "NULL", "NULL" in
6174       List.iter (
6175         function
6176         | String n
6177         | OptString n
6178         | FileIn n
6179         | FileOut n ->
6180             pr "  const char *%s;\n" n
6181         | StringList n ->
6182             pr "  int %s_len;\n" n;
6183             pr "  const char **%s;\n" n
6184         | Bool n
6185         | Int n ->
6186             pr "  int %s;\n" n
6187       ) (snd style);
6188
6189       let needs_i =
6190         (match fst style with
6191          | RStringList _ | RPVList _ | RVGList _ | RLVList _ -> true
6192          | RErr _ | RBool _ | RInt _ | RInt64 _ | RConstString _
6193          | RString _ | RIntBool _ | RStat _ | RStatVFS _
6194          | RHashtable _ -> false) ||
6195         List.exists (function StringList _ -> true | _ -> false) (snd style) in
6196       if needs_i then
6197         pr "  int i;\n";
6198
6199       pr "\n";
6200
6201       (* Get the parameters. *)
6202       List.iter (
6203         function
6204         | String n
6205         | OptString n
6206         | FileIn n
6207         | FileOut n ->
6208             pr "  %s = (*env)->GetStringUTFChars (env, j%s, NULL);\n" n n
6209         | StringList n ->
6210             pr "  %s_len = (*env)->GetArrayLength (env, j%s);\n" n n;
6211             pr "  %s = guestfs_safe_malloc (g, sizeof (char *) * (%s_len+1));\n" n n;
6212             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
6213             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
6214               n;
6215             pr "    %s[i] = (*env)->GetStringUTFChars (env, o, NULL);\n" n;
6216             pr "  }\n";
6217             pr "  %s[%s_len] = NULL;\n" n n;
6218         | Bool n
6219         | Int n ->
6220             pr "  %s = j%s;\n" n n
6221       ) (snd style);
6222
6223       (* Make the call. *)
6224       pr "  r = guestfs_%s " name;
6225       generate_call_args ~handle:"g" (snd style);
6226       pr ";\n";
6227
6228       (* Release the parameters. *)
6229       List.iter (
6230         function
6231         | String n
6232         | OptString n
6233         | FileIn n
6234         | FileOut n ->
6235             pr "  (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
6236         | StringList n ->
6237             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
6238             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
6239               n;
6240             pr "    (*env)->ReleaseStringUTFChars (env, o, %s[i]);\n" n;
6241             pr "  }\n";
6242             pr "  free (%s);\n" n
6243         | Bool n
6244         | Int n -> ()
6245       ) (snd style);
6246
6247       (* Check for errors. *)
6248       pr "  if (r == %s) {\n" error_code;
6249       pr "    throw_exception (env, guestfs_last_error (g));\n";
6250       pr "    return %s;\n" no_ret;
6251       pr "  }\n";
6252
6253       (* Return value. *)
6254       (match fst style with
6255        | RErr -> ()
6256        | RInt _ -> pr "  return (jint) r;\n"
6257        | RBool _ -> pr "  return (jboolean) r;\n"
6258        | RInt64 _ -> pr "  return (jlong) r;\n"
6259        | RConstString _ -> pr "  return (*env)->NewStringUTF (env, r);\n"
6260        | RString _ ->
6261            pr "  jr = (*env)->NewStringUTF (env, r);\n";
6262            pr "  free (r);\n";
6263            pr "  return jr;\n"
6264        | RStringList _ ->
6265            pr "  for (r_len = 0; r[r_len] != NULL; ++r_len) ;\n";
6266            pr "  cl = (*env)->FindClass (env, \"java/lang/String\");\n";
6267            pr "  jstr = (*env)->NewStringUTF (env, \"\");\n";
6268            pr "  jr = (*env)->NewObjectArray (env, r_len, cl, jstr);\n";
6269            pr "  for (i = 0; i < r_len; ++i) {\n";
6270            pr "    jstr = (*env)->NewStringUTF (env, r[i]);\n";
6271            pr "    (*env)->SetObjectArrayElement (env, jr, i, jstr);\n";
6272            pr "    free (r[i]);\n";
6273            pr "  }\n";
6274            pr "  free (r);\n";
6275            pr "  return jr;\n"
6276        | RIntBool _ ->
6277            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/IntBool\");\n";
6278            pr "  jr = (*env)->AllocObject (env, cl);\n";
6279            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"I\");\n";
6280            pr "  (*env)->SetIntField (env, jr, fl, r->i);\n";
6281            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"Z\");\n";
6282            pr "  (*env)->SetBooleanField (env, jr, fl, r->b);\n";
6283            pr "  guestfs_free_int_bool (r);\n";
6284            pr "  return jr;\n"
6285        | RStat _ ->
6286            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/Stat\");\n";
6287            pr "  jr = (*env)->AllocObject (env, cl);\n";
6288            List.iter (
6289              function
6290              | name, `Int ->
6291                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
6292                    name;
6293                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
6294            ) stat_cols;
6295            pr "  free (r);\n";
6296            pr "  return jr;\n"
6297        | RStatVFS _ ->
6298            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/StatVFS\");\n";
6299            pr "  jr = (*env)->AllocObject (env, cl);\n";
6300            List.iter (
6301              function
6302              | name, `Int ->
6303                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
6304                    name;
6305                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
6306            ) statvfs_cols;
6307            pr "  free (r);\n";
6308            pr "  return jr;\n"
6309        | RPVList _ ->
6310            generate_java_lvm_return "pv" "PV" pv_cols
6311        | RVGList _ ->
6312            generate_java_lvm_return "vg" "VG" vg_cols
6313        | RLVList _ ->
6314            generate_java_lvm_return "lv" "LV" lv_cols
6315        | RHashtable _ ->
6316            (* XXX *)
6317            pr "  throw_exception (env, \"%s: internal error: please let us know how to make a Java HashMap from JNI bindings!\");\n" name;
6318            pr "  return NULL;\n"
6319       );
6320
6321       pr "}\n";
6322       pr "\n"
6323   ) all_functions
6324
6325 and generate_java_lvm_return typ jtyp cols =
6326   pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/%s\");\n" jtyp;
6327   pr "  jr = (*env)->NewObjectArray (env, r->len, cl, NULL);\n";
6328   pr "  for (i = 0; i < r->len; ++i) {\n";
6329   pr "    jfl = (*env)->AllocObject (env, cl);\n";
6330   List.iter (
6331     function
6332     | name, `String ->
6333         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
6334         pr "    (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, r->val[i].%s));\n" name;
6335     | name, `UUID ->
6336         pr "    {\n";
6337         pr "      char s[33];\n";
6338         pr "      memcpy (s, r->val[i].%s, 32);\n" name;
6339         pr "      s[32] = 0;\n";
6340         pr "      fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
6341         pr "      (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
6342         pr "    }\n";
6343     | name, (`Bytes|`Int) ->
6344         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
6345         pr "    (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
6346     | name, `OptPercent ->
6347         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"F\");\n" name;
6348         pr "    (*env)->SetFloatField (env, jfl, fl, r->val[i].%s);\n" name;
6349   ) cols;
6350   pr "    (*env)->SetObjectArrayElement (env, jfl, i, jfl);\n";
6351   pr "  }\n";
6352   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
6353   pr "  return jr;\n"
6354
6355 let output_to filename =
6356   let filename_new = filename ^ ".new" in
6357   chan := open_out filename_new;
6358   let close () =
6359     close_out !chan;
6360     chan := stdout;
6361
6362     (* Is the new file different from the current file? *)
6363     if Sys.file_exists filename && files_equal filename filename_new then
6364       Unix.unlink filename_new          (* same, so skip it *)
6365     else (
6366       (* different, overwrite old one *)
6367       (try Unix.chmod filename 0o644 with Unix.Unix_error _ -> ());
6368       Unix.rename filename_new filename;
6369       Unix.chmod filename 0o444;
6370       printf "written %s\n%!" filename;
6371     )
6372   in
6373   close
6374
6375 (* Main program. *)
6376 let () =
6377   check_functions ();
6378
6379   if not (Sys.file_exists "configure.ac") then (
6380     eprintf "\
6381 You are probably running this from the wrong directory.
6382 Run it from the top source directory using the command
6383   src/generator.ml
6384 ";
6385     exit 1
6386   );
6387
6388   let close = output_to "src/guestfs_protocol.x" in
6389   generate_xdr ();
6390   close ();
6391
6392   let close = output_to "src/guestfs-structs.h" in
6393   generate_structs_h ();
6394   close ();
6395
6396   let close = output_to "src/guestfs-actions.h" in
6397   generate_actions_h ();
6398   close ();
6399
6400   let close = output_to "src/guestfs-actions.c" in
6401   generate_client_actions ();
6402   close ();
6403
6404   let close = output_to "daemon/actions.h" in
6405   generate_daemon_actions_h ();
6406   close ();
6407
6408   let close = output_to "daemon/stubs.c" in
6409   generate_daemon_actions ();
6410   close ();
6411
6412   let close = output_to "tests.c" in
6413   generate_tests ();
6414   close ();
6415
6416   let close = output_to "fish/cmds.c" in
6417   generate_fish_cmds ();
6418   close ();
6419
6420   let close = output_to "fish/completion.c" in
6421   generate_fish_completion ();
6422   close ();
6423
6424   let close = output_to "guestfs-structs.pod" in
6425   generate_structs_pod ();
6426   close ();
6427
6428   let close = output_to "guestfs-actions.pod" in
6429   generate_actions_pod ();
6430   close ();
6431
6432   let close = output_to "guestfish-actions.pod" in
6433   generate_fish_actions_pod ();
6434   close ();
6435
6436   let close = output_to "ocaml/guestfs.mli" in
6437   generate_ocaml_mli ();
6438   close ();
6439
6440   let close = output_to "ocaml/guestfs.ml" in
6441   generate_ocaml_ml ();
6442   close ();
6443
6444   let close = output_to "ocaml/guestfs_c_actions.c" in
6445   generate_ocaml_c ();
6446   close ();
6447
6448   let close = output_to "perl/Guestfs.xs" in
6449   generate_perl_xs ();
6450   close ();
6451
6452   let close = output_to "perl/lib/Sys/Guestfs.pm" in
6453   generate_perl_pm ();
6454   close ();
6455
6456   let close = output_to "python/guestfs-py.c" in
6457   generate_python_c ();
6458   close ();
6459
6460   let close = output_to "python/guestfs.py" in
6461   generate_python_py ();
6462   close ();
6463
6464   let close = output_to "ruby/ext/guestfs/_guestfs.c" in
6465   generate_ruby_c ();
6466   close ();
6467
6468   let close = output_to "java/com/redhat/et/libguestfs/GuestFS.java" in
6469   generate_java_java ();
6470   close ();
6471
6472   let close = output_to "java/com/redhat/et/libguestfs/PV.java" in
6473   generate_java_struct "PV" pv_cols;
6474   close ();
6475
6476   let close = output_to "java/com/redhat/et/libguestfs/VG.java" in
6477   generate_java_struct "VG" vg_cols;
6478   close ();
6479
6480   let close = output_to "java/com/redhat/et/libguestfs/LV.java" in
6481   generate_java_struct "LV" lv_cols;
6482   close ();
6483
6484   let close = output_to "java/com/redhat/et/libguestfs/Stat.java" in
6485   generate_java_struct "Stat" stat_cols;
6486   close ();
6487
6488   let close = output_to "java/com/redhat/et/libguestfs/StatVFS.java" in
6489   generate_java_struct "StatVFS" statvfs_cols;
6490   close ();
6491
6492   let close = output_to "java/com_redhat_et_libguestfs_GuestFS.c" in
6493   generate_java_c ();
6494   close ();