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