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