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