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