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