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