Add grub-install 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, TestOutputInt (
1637       [["umount"; "/dev/sda1"];
1638        ["fsck"; "ext2"; "/dev/sda1"]], 0);
1639     InitBasicFS, TestOutputInt (
1640       [["umount"; "/dev/sda1"];
1641        ["zero"; "/dev/sda1"];
1642        ["fsck"; "ext2"; "/dev/sda1"]], 8)],
1643    "run the filesystem checker",
1644    "\
1645 This runs the filesystem checker (fsck) on C<device> which
1646 should have filesystem type C<fstype>.
1647
1648 The returned integer is the status.  See L<fsck(8)> for the
1649 list of status codes from C<fsck>.
1650
1651 Notes:
1652
1653 =over 4
1654
1655 =item *
1656
1657 Multiple status codes can be summed together.
1658
1659 =item *
1660
1661 A non-zero return code can mean \"success\", for example if
1662 errors have been corrected on the filesystem.
1663
1664 =item *
1665
1666 Checking or repairing NTFS volumes is not supported
1667 (by linux-ntfs).
1668
1669 =back
1670
1671 This command is entirely equivalent to running C<fsck -a -t fstype device>.");
1672
1673   ("zero", (RErr, [String "device"]), 85, [],
1674    [InitBasicFS, TestOutput (
1675       [["umount"; "/dev/sda1"];
1676        ["zero"; "/dev/sda1"];
1677        ["file"; "/dev/sda1"]], "data")],
1678    "write zeroes to the device",
1679    "\
1680 This command writes zeroes over the first few blocks of C<device>.
1681
1682 How many blocks are zeroed isn't specified (but it's I<not> enough
1683 to securely wipe the device).  It should be sufficient to remove
1684 any partition tables, filesystem superblocks and so on.");
1685
1686   ("grub_install", (RErr, [String "root"; String "device"]), 86, [],
1687    [InitBasicFS, TestOutputTrue (
1688       [["grub_install"; "/"; "/dev/sda1"];
1689        ["is_dir"; "/boot"]])],
1690    "install GRUB",
1691    "\
1692 This command installs GRUB (the Grand Unified Bootloader) on
1693 C<device>, with the root directory being C<root>.");
1694
1695
1696 ]
1697
1698 let all_functions = non_daemon_functions @ daemon_functions
1699
1700 (* In some places we want the functions to be displayed sorted
1701  * alphabetically, so this is useful:
1702  *)
1703 let all_functions_sorted =
1704   List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) ->
1705                compare n1 n2) all_functions
1706
1707 (* Column names and types from LVM PVs/VGs/LVs. *)
1708 let pv_cols = [
1709   "pv_name", `String;
1710   "pv_uuid", `UUID;
1711   "pv_fmt", `String;
1712   "pv_size", `Bytes;
1713   "dev_size", `Bytes;
1714   "pv_free", `Bytes;
1715   "pv_used", `Bytes;
1716   "pv_attr", `String (* XXX *);
1717   "pv_pe_count", `Int;
1718   "pv_pe_alloc_count", `Int;
1719   "pv_tags", `String;
1720   "pe_start", `Bytes;
1721   "pv_mda_count", `Int;
1722   "pv_mda_free", `Bytes;
1723 (* Not in Fedora 10:
1724   "pv_mda_size", `Bytes;
1725 *)
1726 ]
1727 let vg_cols = [
1728   "vg_name", `String;
1729   "vg_uuid", `UUID;
1730   "vg_fmt", `String;
1731   "vg_attr", `String (* XXX *);
1732   "vg_size", `Bytes;
1733   "vg_free", `Bytes;
1734   "vg_sysid", `String;
1735   "vg_extent_size", `Bytes;
1736   "vg_extent_count", `Int;
1737   "vg_free_count", `Int;
1738   "max_lv", `Int;
1739   "max_pv", `Int;
1740   "pv_count", `Int;
1741   "lv_count", `Int;
1742   "snap_count", `Int;
1743   "vg_seqno", `Int;
1744   "vg_tags", `String;
1745   "vg_mda_count", `Int;
1746   "vg_mda_free", `Bytes;
1747 (* Not in Fedora 10:
1748   "vg_mda_size", `Bytes;
1749 *)
1750 ]
1751 let lv_cols = [
1752   "lv_name", `String;
1753   "lv_uuid", `UUID;
1754   "lv_attr", `String (* XXX *);
1755   "lv_major", `Int;
1756   "lv_minor", `Int;
1757   "lv_kernel_major", `Int;
1758   "lv_kernel_minor", `Int;
1759   "lv_size", `Bytes;
1760   "seg_count", `Int;
1761   "origin", `String;
1762   "snap_percent", `OptPercent;
1763   "copy_percent", `OptPercent;
1764   "move_pv", `String;
1765   "lv_tags", `String;
1766   "mirror_log", `String;
1767   "modules", `String;
1768 ]
1769
1770 (* Column names and types from stat structures.
1771  * NB. Can't use things like 'st_atime' because glibc header files
1772  * define some of these as macros.  Ugh.
1773  *)
1774 let stat_cols = [
1775   "dev", `Int;
1776   "ino", `Int;
1777   "mode", `Int;
1778   "nlink", `Int;
1779   "uid", `Int;
1780   "gid", `Int;
1781   "rdev", `Int;
1782   "size", `Int;
1783   "blksize", `Int;
1784   "blocks", `Int;
1785   "atime", `Int;
1786   "mtime", `Int;
1787   "ctime", `Int;
1788 ]
1789 let statvfs_cols = [
1790   "bsize", `Int;
1791   "frsize", `Int;
1792   "blocks", `Int;
1793   "bfree", `Int;
1794   "bavail", `Int;
1795   "files", `Int;
1796   "ffree", `Int;
1797   "favail", `Int;
1798   "fsid", `Int;
1799   "flag", `Int;
1800   "namemax", `Int;
1801 ]
1802
1803 (* Useful functions.
1804  * Note we don't want to use any external OCaml libraries which
1805  * makes this a bit harder than it should be.
1806  *)
1807 let failwithf fs = ksprintf failwith fs
1808
1809 let replace_char s c1 c2 =
1810   let s2 = String.copy s in
1811   let r = ref false in
1812   for i = 0 to String.length s2 - 1 do
1813     if String.unsafe_get s2 i = c1 then (
1814       String.unsafe_set s2 i c2;
1815       r := true
1816     )
1817   done;
1818   if not !r then s else s2
1819
1820 let isspace c =
1821   c = ' '
1822   (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
1823
1824 let triml ?(test = isspace) str =
1825   let i = ref 0 in
1826   let n = ref (String.length str) in
1827   while !n > 0 && test str.[!i]; do
1828     decr n;
1829     incr i
1830   done;
1831   if !i = 0 then str
1832   else String.sub str !i !n
1833
1834 let trimr ?(test = isspace) str =
1835   let n = ref (String.length str) in
1836   while !n > 0 && test str.[!n-1]; do
1837     decr n
1838   done;
1839   if !n = String.length str then str
1840   else String.sub str 0 !n
1841
1842 let trim ?(test = isspace) str =
1843   trimr ~test (triml ~test str)
1844
1845 let rec find s sub =
1846   let len = String.length s in
1847   let sublen = String.length sub in
1848   let rec loop i =
1849     if i <= len-sublen then (
1850       let rec loop2 j =
1851         if j < sublen then (
1852           if s.[i+j] = sub.[j] then loop2 (j+1)
1853           else -1
1854         ) else
1855           i (* found *)
1856       in
1857       let r = loop2 0 in
1858       if r = -1 then loop (i+1) else r
1859     ) else
1860       -1 (* not found *)
1861   in
1862   loop 0
1863
1864 let rec replace_str s s1 s2 =
1865   let len = String.length s in
1866   let sublen = String.length s1 in
1867   let i = find s s1 in
1868   if i = -1 then s
1869   else (
1870     let s' = String.sub s 0 i in
1871     let s'' = String.sub s (i+sublen) (len-i-sublen) in
1872     s' ^ s2 ^ replace_str s'' s1 s2
1873   )
1874
1875 let rec string_split sep str =
1876   let len = String.length str in
1877   let seplen = String.length sep in
1878   let i = find str sep in
1879   if i = -1 then [str]
1880   else (
1881     let s' = String.sub str 0 i in
1882     let s'' = String.sub str (i+seplen) (len-i-seplen) in
1883     s' :: string_split sep s''
1884   )
1885
1886 let files_equal n1 n2 =
1887   let cmd = sprintf "cmp -s %s %s" (Filename.quote n1) (Filename.quote n2) in
1888   match Sys.command cmd with
1889   | 0 -> true
1890   | 1 -> false
1891   | i -> failwithf "%s: failed with error code %d" cmd i
1892
1893 let rec find_map f = function
1894   | [] -> raise Not_found
1895   | x :: xs ->
1896       match f x with
1897       | Some y -> y
1898       | None -> find_map f xs
1899
1900 let iteri f xs =
1901   let rec loop i = function
1902     | [] -> ()
1903     | x :: xs -> f i x; loop (i+1) xs
1904   in
1905   loop 0 xs
1906
1907 let mapi f xs =
1908   let rec loop i = function
1909     | [] -> []
1910     | x :: xs -> let r = f i x in r :: loop (i+1) xs
1911   in
1912   loop 0 xs
1913
1914 let name_of_argt = function
1915   | String n | OptString n | StringList n | Bool n | Int n
1916   | FileIn n | FileOut n -> n
1917
1918 let seq_of_test = function
1919   | TestRun s | TestOutput (s, _) | TestOutputList (s, _)
1920   | TestOutputInt (s, _) | TestOutputTrue s | TestOutputFalse s
1921   | TestOutputLength (s, _) | TestOutputStruct (s, _)
1922   | TestLastFail s -> s
1923
1924 (* Check function names etc. for consistency. *)
1925 let check_functions () =
1926   let contains_uppercase str =
1927     let len = String.length str in
1928     let rec loop i =
1929       if i >= len then false
1930       else (
1931         let c = str.[i] in
1932         if c >= 'A' && c <= 'Z' then true
1933         else loop (i+1)
1934       )
1935     in
1936     loop 0
1937   in
1938
1939   (* Check function names. *)
1940   List.iter (
1941     fun (name, _, _, _, _, _, _) ->
1942       if String.length name >= 7 && String.sub name 0 7 = "guestfs" then
1943         failwithf "function name %s does not need 'guestfs' prefix" name;
1944       if contains_uppercase name then
1945         failwithf "function name %s should not contain uppercase chars" name;
1946       if String.contains name '-' then
1947         failwithf "function name %s should not contain '-', use '_' instead."
1948           name
1949   ) all_functions;
1950
1951   (* Check function parameter/return names. *)
1952   List.iter (
1953     fun (name, style, _, _, _, _, _) ->
1954       let check_arg_ret_name n =
1955         if contains_uppercase n then
1956           failwithf "%s param/ret %s should not contain uppercase chars"
1957             name n;
1958         if String.contains n '-' || String.contains n '_' then
1959           failwithf "%s param/ret %s should not contain '-' or '_'"
1960             name n;
1961         if n = "value" then
1962           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;
1963         if n = "argv" || n = "args" then
1964           failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
1965       in
1966
1967       (match fst style with
1968        | RErr -> ()
1969        | RInt n | RInt64 n | RBool n | RConstString n | RString n
1970        | RStringList n | RPVList n | RVGList n | RLVList n
1971        | RStat n | RStatVFS n
1972        | RHashtable n ->
1973            check_arg_ret_name n
1974        | RIntBool (n,m) ->
1975            check_arg_ret_name n;
1976            check_arg_ret_name m
1977       );
1978       List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) (snd style)
1979   ) all_functions;
1980
1981   (* Check short descriptions. *)
1982   List.iter (
1983     fun (name, _, _, _, _, shortdesc, _) ->
1984       if shortdesc.[0] <> Char.lowercase shortdesc.[0] then
1985         failwithf "short description of %s should begin with lowercase." name;
1986       let c = shortdesc.[String.length shortdesc-1] in
1987       if c = '\n' || c = '.' then
1988         failwithf "short description of %s should not end with . or \\n." name
1989   ) all_functions;
1990
1991   (* Check long dscriptions. *)
1992   List.iter (
1993     fun (name, _, _, _, _, _, longdesc) ->
1994       if longdesc.[String.length longdesc-1] = '\n' then
1995         failwithf "long description of %s should not end with \\n." name
1996   ) all_functions;
1997
1998   (* Check proc_nrs. *)
1999   List.iter (
2000     fun (name, _, proc_nr, _, _, _, _) ->
2001       if proc_nr <= 0 then
2002         failwithf "daemon function %s should have proc_nr > 0" name
2003   ) daemon_functions;
2004
2005   List.iter (
2006     fun (name, _, proc_nr, _, _, _, _) ->
2007       if proc_nr <> -1 then
2008         failwithf "non-daemon function %s should have proc_nr -1" name
2009   ) non_daemon_functions;
2010
2011   let proc_nrs =
2012     List.map (fun (name, _, proc_nr, _, _, _, _) -> name, proc_nr)
2013       daemon_functions in
2014   let proc_nrs =
2015     List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
2016   let rec loop = function
2017     | [] -> ()
2018     | [_] -> ()
2019     | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
2020         loop rest
2021     | (name1,nr1) :: (name2,nr2) :: _ ->
2022         failwithf "%s and %s have conflicting procedure numbers (%d, %d)"
2023           name1 name2 nr1 nr2
2024   in
2025   loop proc_nrs;
2026
2027   (* Check tests. *)
2028   List.iter (
2029     function
2030       (* Ignore functions that have no tests.  We generate a
2031        * warning when the user does 'make check' instead.
2032        *)
2033     | name, _, _, _, [], _, _ -> ()
2034     | name, _, _, _, tests, _, _ ->
2035         let funcs =
2036           List.map (
2037             fun (_, test) ->
2038               match seq_of_test test with
2039               | [] ->
2040                   failwithf "%s has a test containing an empty sequence" name
2041               | cmds -> List.map List.hd cmds
2042           ) tests in
2043         let funcs = List.flatten funcs in
2044
2045         let tested = List.mem name funcs in
2046
2047         if not tested then
2048           failwithf "function %s has tests but does not test itself" name
2049   ) all_functions
2050
2051 (* 'pr' prints to the current output file. *)
2052 let chan = ref stdout
2053 let pr fs = ksprintf (output_string !chan) fs
2054
2055 (* Generate a header block in a number of standard styles. *)
2056 type comment_style = CStyle | HashStyle | OCamlStyle
2057 type license = GPLv2 | LGPLv2
2058
2059 let generate_header comment license =
2060   let c = match comment with
2061     | CStyle ->     pr "/* "; " *"
2062     | HashStyle ->  pr "# ";  "#"
2063     | OCamlStyle -> pr "(* "; " *" in
2064   pr "libguestfs generated file\n";
2065   pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
2066   pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
2067   pr "%s\n" c;
2068   pr "%s Copyright (C) 2009 Red Hat Inc.\n" c;
2069   pr "%s\n" c;
2070   (match license with
2071    | GPLv2 ->
2072        pr "%s This program is free software; you can redistribute it and/or modify\n" c;
2073        pr "%s it under the terms of the GNU General Public License as published by\n" c;
2074        pr "%s the Free Software Foundation; either version 2 of the License, or\n" c;
2075        pr "%s (at your option) any later version.\n" c;
2076        pr "%s\n" c;
2077        pr "%s This program is distributed in the hope that it will be useful,\n" c;
2078        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
2079        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" c;
2080        pr "%s GNU General Public License for more details.\n" c;
2081        pr "%s\n" c;
2082        pr "%s You should have received a copy of the GNU General Public License along\n" c;
2083        pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c;
2084        pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c;
2085
2086    | LGPLv2 ->
2087        pr "%s This library is free software; you can redistribute it and/or\n" c;
2088        pr "%s modify it under the terms of the GNU Lesser General Public\n" c;
2089        pr "%s License as published by the Free Software Foundation; either\n" c;
2090        pr "%s version 2 of the License, or (at your option) any later version.\n" c;
2091        pr "%s\n" c;
2092        pr "%s This library is distributed in the hope that it will be useful,\n" c;
2093        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
2094        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n" c;
2095        pr "%s Lesser General Public License for more details.\n" c;
2096        pr "%s\n" c;
2097        pr "%s You should have received a copy of the GNU Lesser General Public\n" c;
2098        pr "%s License along with this library; if not, write to the Free Software\n" c;
2099        pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c;
2100   );
2101   (match comment with
2102    | CStyle -> pr " */\n"
2103    | HashStyle -> ()
2104    | OCamlStyle -> pr " *)\n"
2105   );
2106   pr "\n"
2107
2108 (* Start of main code generation functions below this line. *)
2109
2110 (* Generate the pod documentation for the C API. *)
2111 let rec generate_actions_pod () =
2112   List.iter (
2113     fun (shortname, style, _, flags, _, _, longdesc) ->
2114       let name = "guestfs_" ^ shortname in
2115       pr "=head2 %s\n\n" name;
2116       pr " ";
2117       generate_prototype ~extern:false ~handle:"handle" name style;
2118       pr "\n\n";
2119       pr "%s\n\n" longdesc;
2120       (match fst style with
2121        | RErr ->
2122            pr "This function returns 0 on success or -1 on error.\n\n"
2123        | RInt _ ->
2124            pr "On error this function returns -1.\n\n"
2125        | RInt64 _ ->
2126            pr "On error this function returns -1.\n\n"
2127        | RBool _ ->
2128            pr "This function returns a C truth value on success or -1 on error.\n\n"
2129        | RConstString _ ->
2130            pr "This function returns a string, or NULL on error.
2131 The string is owned by the guest handle and must I<not> be freed.\n\n"
2132        | RString _ ->
2133            pr "This function returns a string, or NULL on error.
2134 I<The caller must free the returned string after use>.\n\n"
2135        | RStringList _ ->
2136            pr "This function returns a NULL-terminated array of strings
2137 (like L<environ(3)>), or NULL if there was an error.
2138 I<The caller must free the strings and the array after use>.\n\n"
2139        | RIntBool _ ->
2140            pr "This function returns a C<struct guestfs_int_bool *>,
2141 or NULL if there was an error.
2142 I<The caller must call C<guestfs_free_int_bool> after use>.\n\n"
2143        | RPVList _ ->
2144            pr "This function returns a C<struct guestfs_lvm_pv_list *>
2145 (see E<lt>guestfs-structs.hE<gt>),
2146 or NULL if there was an error.
2147 I<The caller must call C<guestfs_free_lvm_pv_list> after use>.\n\n"
2148        | RVGList _ ->
2149            pr "This function returns a C<struct guestfs_lvm_vg_list *>
2150 (see E<lt>guestfs-structs.hE<gt>),
2151 or NULL if there was an error.
2152 I<The caller must call C<guestfs_free_lvm_vg_list> after use>.\n\n"
2153        | RLVList _ ->
2154            pr "This function returns a C<struct guestfs_lvm_lv_list *>
2155 (see E<lt>guestfs-structs.hE<gt>),
2156 or NULL if there was an error.
2157 I<The caller must call C<guestfs_free_lvm_lv_list> after use>.\n\n"
2158        | RStat _ ->
2159            pr "This function returns a C<struct guestfs_stat *>
2160 (see L<stat(2)> and E<lt>guestfs-structs.hE<gt>),
2161 or NULL if there was an error.
2162 I<The caller must call C<free> after use>.\n\n"
2163        | RStatVFS _ ->
2164            pr "This function returns a C<struct guestfs_statvfs *>
2165 (see L<statvfs(2)> and E<lt>guestfs-structs.hE<gt>),
2166 or NULL if there was an error.
2167 I<The caller must call C<free> after use>.\n\n"
2168        | RHashtable _ ->
2169            pr "This function returns a NULL-terminated array of
2170 strings, or NULL if there was an error.
2171 The array of strings will always have length C<2n+1>, where
2172 C<n> keys and values alternate, followed by the trailing NULL entry.
2173 I<The caller must free the strings and the array after use>.\n\n"
2174       );
2175       if List.mem ProtocolLimitWarning flags then
2176         pr "%s\n\n" protocol_limit_warning;
2177       if List.mem DangerWillRobinson flags then
2178         pr "%s\n\n" danger_will_robinson;
2179   ) all_functions_sorted
2180
2181 and generate_structs_pod () =
2182   (* LVM structs documentation. *)
2183   List.iter (
2184     fun (typ, cols) ->
2185       pr "=head2 guestfs_lvm_%s\n" typ;
2186       pr "\n";
2187       pr " struct guestfs_lvm_%s {\n" typ;
2188       List.iter (
2189         function
2190         | name, `String -> pr "  char *%s;\n" name
2191         | name, `UUID ->
2192             pr "  /* The next field is NOT nul-terminated, be careful when printing it: */\n";
2193             pr "  char %s[32];\n" name
2194         | name, `Bytes -> pr "  uint64_t %s;\n" name
2195         | name, `Int -> pr "  int64_t %s;\n" name
2196         | name, `OptPercent ->
2197             pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
2198             pr "  float %s;\n" name
2199       ) cols;
2200       pr " \n";
2201       pr " struct guestfs_lvm_%s_list {\n" typ;
2202       pr "   uint32_t len; /* Number of elements in list. */\n";
2203       pr "   struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
2204       pr " };\n";
2205       pr " \n";
2206       pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
2207         typ typ;
2208       pr "\n"
2209   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
2210
2211 (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
2212  * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'.
2213  *
2214  * We have to use an underscore instead of a dash because otherwise
2215  * rpcgen generates incorrect code.
2216  *
2217  * This header is NOT exported to clients, but see also generate_structs_h.
2218  *)
2219 and generate_xdr () =
2220   generate_header CStyle LGPLv2;
2221
2222   (* This has to be defined to get around a limitation in Sun's rpcgen. *)
2223   pr "typedef string str<>;\n";
2224   pr "\n";
2225
2226   (* LVM internal structures. *)
2227   List.iter (
2228     function
2229     | typ, cols ->
2230         pr "struct guestfs_lvm_int_%s {\n" typ;
2231         List.iter (function
2232                    | name, `String -> pr "  string %s<>;\n" name
2233                    | name, `UUID -> pr "  opaque %s[32];\n" name
2234                    | name, `Bytes -> pr "  hyper %s;\n" name
2235                    | name, `Int -> pr "  hyper %s;\n" name
2236                    | name, `OptPercent -> pr "  float %s;\n" name
2237                   ) cols;
2238         pr "};\n";
2239         pr "\n";
2240         pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
2241         pr "\n";
2242   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
2243
2244   (* Stat internal structures. *)
2245   List.iter (
2246     function
2247     | typ, cols ->
2248         pr "struct guestfs_int_%s {\n" typ;
2249         List.iter (function
2250                    | name, `Int -> pr "  hyper %s;\n" name
2251                   ) cols;
2252         pr "};\n";
2253         pr "\n";
2254   ) ["stat", stat_cols; "statvfs", statvfs_cols];
2255
2256   List.iter (
2257     fun (shortname, style, _, _, _, _, _) ->
2258       let name = "guestfs_" ^ shortname in
2259
2260       (match snd style with
2261        | [] -> ()
2262        | args ->
2263            pr "struct %s_args {\n" name;
2264            List.iter (
2265              function
2266              | String n -> pr "  string %s<>;\n" n
2267              | OptString n -> pr "  str *%s;\n" n
2268              | StringList n -> pr "  str %s<>;\n" n
2269              | Bool n -> pr "  bool %s;\n" n
2270              | Int n -> pr "  int %s;\n" n
2271              | FileIn _ | FileOut _ -> ()
2272            ) args;
2273            pr "};\n\n"
2274       );
2275       (match fst style with
2276        | RErr -> ()
2277        | RInt n ->
2278            pr "struct %s_ret {\n" name;
2279            pr "  int %s;\n" n;
2280            pr "};\n\n"
2281        | RInt64 n ->
2282            pr "struct %s_ret {\n" name;
2283            pr "  hyper %s;\n" n;
2284            pr "};\n\n"
2285        | RBool n ->
2286            pr "struct %s_ret {\n" name;
2287            pr "  bool %s;\n" n;
2288            pr "};\n\n"
2289        | RConstString _ ->
2290            failwithf "RConstString cannot be returned from a daemon function"
2291        | RString n ->
2292            pr "struct %s_ret {\n" name;
2293            pr "  string %s<>;\n" n;
2294            pr "};\n\n"
2295        | RStringList n ->
2296            pr "struct %s_ret {\n" name;
2297            pr "  str %s<>;\n" n;
2298            pr "};\n\n"
2299        | RIntBool (n,m) ->
2300            pr "struct %s_ret {\n" name;
2301            pr "  int %s;\n" n;
2302            pr "  bool %s;\n" m;
2303            pr "};\n\n"
2304        | RPVList n ->
2305            pr "struct %s_ret {\n" name;
2306            pr "  guestfs_lvm_int_pv_list %s;\n" n;
2307            pr "};\n\n"
2308        | RVGList n ->
2309            pr "struct %s_ret {\n" name;
2310            pr "  guestfs_lvm_int_vg_list %s;\n" n;
2311            pr "};\n\n"
2312        | RLVList n ->
2313            pr "struct %s_ret {\n" name;
2314            pr "  guestfs_lvm_int_lv_list %s;\n" n;
2315            pr "};\n\n"
2316        | RStat n ->
2317            pr "struct %s_ret {\n" name;
2318            pr "  guestfs_int_stat %s;\n" n;
2319            pr "};\n\n"
2320        | RStatVFS n ->
2321            pr "struct %s_ret {\n" name;
2322            pr "  guestfs_int_statvfs %s;\n" n;
2323            pr "};\n\n"
2324        | RHashtable n ->
2325            pr "struct %s_ret {\n" name;
2326            pr "  str %s<>;\n" n;
2327            pr "};\n\n"
2328       );
2329   ) daemon_functions;
2330
2331   (* Table of procedure numbers. *)
2332   pr "enum guestfs_procedure {\n";
2333   List.iter (
2334     fun (shortname, _, proc_nr, _, _, _, _) ->
2335       pr "  GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
2336   ) daemon_functions;
2337   pr "  GUESTFS_PROC_NR_PROCS\n";
2338   pr "};\n";
2339   pr "\n";
2340
2341   (* Having to choose a maximum message size is annoying for several
2342    * reasons (it limits what we can do in the API), but it (a) makes
2343    * the protocol a lot simpler, and (b) provides a bound on the size
2344    * of the daemon which operates in limited memory space.  For large
2345    * file transfers you should use FTP.
2346    *)
2347   pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
2348   pr "\n";
2349
2350   (* Message header, etc. *)
2351   pr "\
2352 /* The communication protocol is now documented in the guestfs(3)
2353  * manpage.
2354  */
2355
2356 const GUESTFS_PROGRAM = 0x2000F5F5;
2357 const GUESTFS_PROTOCOL_VERSION = 1;
2358
2359 /* These constants must be larger than any possible message length. */
2360 const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5;
2361 const GUESTFS_CANCEL_FLAG = 0xffffeeee;
2362
2363 enum guestfs_message_direction {
2364   GUESTFS_DIRECTION_CALL = 0,        /* client -> daemon */
2365   GUESTFS_DIRECTION_REPLY = 1        /* daemon -> client */
2366 };
2367
2368 enum guestfs_message_status {
2369   GUESTFS_STATUS_OK = 0,
2370   GUESTFS_STATUS_ERROR = 1
2371 };
2372
2373 const GUESTFS_ERROR_LEN = 256;
2374
2375 struct guestfs_message_error {
2376   string error_message<GUESTFS_ERROR_LEN>;
2377 };
2378
2379 struct guestfs_message_header {
2380   unsigned prog;                     /* GUESTFS_PROGRAM */
2381   unsigned vers;                     /* GUESTFS_PROTOCOL_VERSION */
2382   guestfs_procedure proc;            /* GUESTFS_PROC_x */
2383   guestfs_message_direction direction;
2384   unsigned serial;                   /* message serial number */
2385   guestfs_message_status status;
2386 };
2387
2388 const GUESTFS_MAX_CHUNK_SIZE = 8192;
2389
2390 struct guestfs_chunk {
2391   int cancel;                        /* if non-zero, transfer is cancelled */
2392   /* data size is 0 bytes if the transfer has finished successfully */
2393   opaque data<GUESTFS_MAX_CHUNK_SIZE>;
2394 };
2395 "
2396
2397 (* Generate the guestfs-structs.h file. *)
2398 and generate_structs_h () =
2399   generate_header CStyle LGPLv2;
2400
2401   (* This is a public exported header file containing various
2402    * structures.  The structures are carefully written to have
2403    * exactly the same in-memory format as the XDR structures that
2404    * we use on the wire to the daemon.  The reason for creating
2405    * copies of these structures here is just so we don't have to
2406    * export the whole of guestfs_protocol.h (which includes much
2407    * unrelated and XDR-dependent stuff that we don't want to be
2408    * public, or required by clients).
2409    *
2410    * To reiterate, we will pass these structures to and from the
2411    * client with a simple assignment or memcpy, so the format
2412    * must be identical to what rpcgen / the RFC defines.
2413    *)
2414
2415   (* guestfs_int_bool structure. *)
2416   pr "struct guestfs_int_bool {\n";
2417   pr "  int32_t i;\n";
2418   pr "  int32_t b;\n";
2419   pr "};\n";
2420   pr "\n";
2421
2422   (* LVM public structures. *)
2423   List.iter (
2424     function
2425     | typ, cols ->
2426         pr "struct guestfs_lvm_%s {\n" typ;
2427         List.iter (
2428           function
2429           | name, `String -> pr "  char *%s;\n" name
2430           | name, `UUID -> pr "  char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
2431           | name, `Bytes -> pr "  uint64_t %s;\n" name
2432           | name, `Int -> pr "  int64_t %s;\n" name
2433           | name, `OptPercent -> pr "  float %s; /* [0..100] or -1 */\n" name
2434         ) cols;
2435         pr "};\n";
2436         pr "\n";
2437         pr "struct guestfs_lvm_%s_list {\n" typ;
2438         pr "  uint32_t len;\n";
2439         pr "  struct guestfs_lvm_%s *val;\n" typ;
2440         pr "};\n";
2441         pr "\n"
2442   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
2443
2444   (* Stat structures. *)
2445   List.iter (
2446     function
2447     | typ, cols ->
2448         pr "struct guestfs_%s {\n" typ;
2449         List.iter (
2450           function
2451           | name, `Int -> pr "  int64_t %s;\n" name
2452         ) cols;
2453         pr "};\n";
2454         pr "\n"
2455   ) ["stat", stat_cols; "statvfs", statvfs_cols]
2456
2457 (* Generate the guestfs-actions.h file. *)
2458 and generate_actions_h () =
2459   generate_header CStyle LGPLv2;
2460   List.iter (
2461     fun (shortname, style, _, _, _, _, _) ->
2462       let name = "guestfs_" ^ shortname in
2463       generate_prototype ~single_line:true ~newline:true ~handle:"handle"
2464         name style
2465   ) all_functions
2466
2467 (* Generate the client-side dispatch stubs. *)
2468 and generate_client_actions () =
2469   generate_header CStyle LGPLv2;
2470
2471   pr "\
2472 #include <stdio.h>
2473 #include <stdlib.h>
2474
2475 #include \"guestfs.h\"
2476 #include \"guestfs_protocol.h\"
2477
2478 #define error guestfs_error
2479 #define perrorf guestfs_perrorf
2480 #define safe_malloc guestfs_safe_malloc
2481 #define safe_realloc guestfs_safe_realloc
2482 #define safe_strdup guestfs_safe_strdup
2483 #define safe_memdup guestfs_safe_memdup
2484
2485 /* Check the return message from a call for validity. */
2486 static int
2487 check_reply_header (guestfs_h *g,
2488                     const struct guestfs_message_header *hdr,
2489                     int proc_nr, int serial)
2490 {
2491   if (hdr->prog != GUESTFS_PROGRAM) {
2492     error (g, \"wrong program (%%d/%%d)\", hdr->prog, GUESTFS_PROGRAM);
2493     return -1;
2494   }
2495   if (hdr->vers != GUESTFS_PROTOCOL_VERSION) {
2496     error (g, \"wrong protocol version (%%d/%%d)\",
2497            hdr->vers, GUESTFS_PROTOCOL_VERSION);
2498     return -1;
2499   }
2500   if (hdr->direction != GUESTFS_DIRECTION_REPLY) {
2501     error (g, \"unexpected message direction (%%d/%%d)\",
2502            hdr->direction, GUESTFS_DIRECTION_REPLY);
2503     return -1;
2504   }
2505   if (hdr->proc != proc_nr) {
2506     error (g, \"unexpected procedure number (%%d/%%d)\", hdr->proc, proc_nr);
2507     return -1;
2508   }
2509   if (hdr->serial != serial) {
2510     error (g, \"unexpected serial (%%d/%%d)\", hdr->serial, serial);
2511     return -1;
2512   }
2513
2514   return 0;
2515 }
2516
2517 /* Check we are in the right state to run a high-level action. */
2518 static int
2519 check_state (guestfs_h *g, const char *caller)
2520 {
2521   if (!guestfs_is_ready (g)) {
2522     if (guestfs_is_config (g))
2523       error (g, \"%%s: call launch() before using this function\",
2524         caller);
2525     else if (guestfs_is_launching (g))
2526       error (g, \"%%s: call wait_ready() before using this function\",
2527         caller);
2528     else
2529       error (g, \"%%s called from the wrong state, %%d != READY\",
2530         caller, guestfs_get_state (g));
2531     return -1;
2532   }
2533   return 0;
2534 }
2535
2536 ";
2537
2538   (* Client-side stubs for each function. *)
2539   List.iter (
2540     fun (shortname, style, _, _, _, _, _) ->
2541       let name = "guestfs_" ^ shortname in
2542
2543       (* Generate the context struct which stores the high-level
2544        * state between callback functions.
2545        *)
2546       pr "struct %s_ctx {\n" shortname;
2547       pr "  /* This flag is set by the callbacks, so we know we've done\n";
2548       pr "   * the callbacks as expected, and in the right sequence.\n";
2549       pr "   * 0 = not called, 1 = reply_cb called.\n";
2550       pr "   */\n";
2551       pr "  int cb_sequence;\n";
2552       pr "  struct guestfs_message_header hdr;\n";
2553       pr "  struct guestfs_message_error err;\n";
2554       (match fst style with
2555        | RErr -> ()
2556        | RConstString _ ->
2557            failwithf "RConstString cannot be returned from a daemon function"
2558        | RInt _ | RInt64 _
2559        | RBool _ | RString _ | RStringList _
2560        | RIntBool _
2561        | RPVList _ | RVGList _ | RLVList _
2562        | RStat _ | RStatVFS _
2563        | RHashtable _ ->
2564            pr "  struct %s_ret ret;\n" name
2565       );
2566       pr "};\n";
2567       pr "\n";
2568
2569       (* Generate the reply callback function. *)
2570       pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
2571       pr "{\n";
2572       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
2573       pr "  struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
2574       pr "\n";
2575       pr "  /* This should definitely not happen. */\n";
2576       pr "  if (ctx->cb_sequence != 0) {\n";
2577       pr "    ctx->cb_sequence = 9999;\n";
2578       pr "    error (g, \"%%s: internal error: reply callback called twice\", \"%s\");\n" name;
2579       pr "    return;\n";
2580       pr "  }\n";
2581       pr "\n";
2582       pr "  ml->main_loop_quit (ml, g);\n";
2583       pr "\n";
2584       pr "  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {\n";
2585       pr "    error (g, \"%%s: failed to parse reply header\", \"%s\");\n" name;
2586       pr "    return;\n";
2587       pr "  }\n";
2588       pr "  if (ctx->hdr.status == GUESTFS_STATUS_ERROR) {\n";
2589       pr "    if (!xdr_guestfs_message_error (xdr, &ctx->err)) {\n";
2590       pr "      error (g, \"%%s: failed to parse reply error\", \"%s\");\n"
2591         name;
2592       pr "      return;\n";
2593       pr "    }\n";
2594       pr "    goto done;\n";
2595       pr "  }\n";
2596
2597       (match fst style with
2598        | RErr -> ()
2599        | RConstString _ ->
2600            failwithf "RConstString cannot be returned from a daemon function"
2601        | RInt _ | RInt64 _
2602        | RBool _ | RString _ | RStringList _
2603        | RIntBool _
2604        | RPVList _ | RVGList _ | RLVList _
2605        | RStat _ | RStatVFS _
2606        | RHashtable _ ->
2607             pr "  if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name;
2608             pr "    error (g, \"%%s: failed to parse reply\", \"%s\");\n" name;
2609             pr "    return;\n";
2610             pr "  }\n";
2611       );
2612
2613       pr " done:\n";
2614       pr "  ctx->cb_sequence = 1;\n";
2615       pr "}\n\n";
2616
2617       (* Generate the action stub. *)
2618       generate_prototype ~extern:false ~semicolon:false ~newline:true
2619         ~handle:"g" name style;
2620
2621       let error_code =
2622         match fst style with
2623         | RErr | RInt _ | RInt64 _ | RBool _ -> "-1"
2624         | RConstString _ ->
2625             failwithf "RConstString cannot be returned from a daemon function"
2626         | RString _ | RStringList _ | RIntBool _
2627         | RPVList _ | RVGList _ | RLVList _
2628         | RStat _ | RStatVFS _
2629         | RHashtable _ ->
2630             "NULL" in
2631
2632       pr "{\n";
2633
2634       (match snd style with
2635        | [] -> ()
2636        | _ -> pr "  struct %s_args args;\n" name
2637       );
2638
2639       pr "  struct %s_ctx ctx;\n" shortname;
2640       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
2641       pr "  int serial;\n";
2642       pr "\n";
2643       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
2644       pr "  guestfs_set_busy (g);\n";
2645       pr "\n";
2646       pr "  memset (&ctx, 0, sizeof ctx);\n";
2647       pr "\n";
2648
2649       (* Send the main header and arguments. *)
2650       (match snd style with
2651        | [] ->
2652            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s, NULL, NULL);\n"
2653              (String.uppercase shortname)
2654        | args ->
2655            List.iter (
2656              function
2657              | String n ->
2658                  pr "  args.%s = (char *) %s;\n" n n
2659              | OptString n ->
2660                  pr "  args.%s = %s ? (char **) &%s : NULL;\n" n n n
2661              | StringList n ->
2662                  pr "  args.%s.%s_val = (char **) %s;\n" n n n;
2663                  pr "  for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
2664              | Bool n ->
2665                  pr "  args.%s = %s;\n" n n
2666              | Int n ->
2667                  pr "  args.%s = %s;\n" n n
2668              | FileIn _ | FileOut _ -> ()
2669            ) args;
2670            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s,\n"
2671              (String.uppercase shortname);
2672            pr "        (xdrproc_t) xdr_%s_args, (char *) &args);\n"
2673              name;
2674       );
2675       pr "  if (serial == -1) {\n";
2676       pr "    guestfs_set_ready (g);\n";
2677       pr "    return %s;\n" error_code;
2678       pr "  }\n";
2679       pr "\n";
2680
2681       (* Send any additional files (FileIn) requested. *)
2682       let need_read_reply_label = ref false in
2683       List.iter (
2684         function
2685         | FileIn n ->
2686             pr "  {\n";
2687             pr "    int r;\n";
2688             pr "\n";
2689             pr "    r = guestfs__send_file_sync (g, %s);\n" n;
2690             pr "    if (r == -1) {\n";
2691             pr "      guestfs_set_ready (g);\n";
2692             pr "      return %s;\n" error_code;
2693             pr "    }\n";
2694             pr "    if (r == -2) /* daemon cancelled */\n";
2695             pr "      goto read_reply;\n";
2696             need_read_reply_label := true;
2697             pr "  }\n";
2698             pr "\n";
2699         | _ -> ()
2700       ) (snd style);
2701
2702       (* Wait for the reply from the remote end. *)
2703       if !need_read_reply_label then pr " read_reply:\n";
2704       pr "  guestfs__switch_to_receiving (g);\n";
2705       pr "  ctx.cb_sequence = 0;\n";
2706       pr "  guestfs_set_reply_callback (g, %s_reply_cb, &ctx);\n" shortname;
2707       pr "  (void) ml->main_loop_run (ml, g);\n";
2708       pr "  guestfs_set_reply_callback (g, NULL, NULL);\n";
2709       pr "  if (ctx.cb_sequence != 1) {\n";
2710       pr "    error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name;
2711       pr "    guestfs_set_ready (g);\n";
2712       pr "    return %s;\n" error_code;
2713       pr "  }\n";
2714       pr "\n";
2715
2716       pr "  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1) {\n"
2717         (String.uppercase shortname);
2718       pr "    guestfs_set_ready (g);\n";
2719       pr "    return %s;\n" error_code;
2720       pr "  }\n";
2721       pr "\n";
2722
2723       pr "  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {\n";
2724       pr "    error (g, \"%%s\", ctx.err.error_message);\n";
2725       pr "    guestfs_set_ready (g);\n";
2726       pr "    return %s;\n" error_code;
2727       pr "  }\n";
2728       pr "\n";
2729
2730       (* Expecting to receive further files (FileOut)? *)
2731       List.iter (
2732         function
2733         | FileOut n ->
2734             pr "  if (guestfs__receive_file_sync (g, %s) == -1) {\n" n;
2735             pr "    guestfs_set_ready (g);\n";
2736             pr "    return %s;\n" error_code;
2737             pr "  }\n";
2738             pr "\n";
2739         | _ -> ()
2740       ) (snd style);
2741
2742       pr "  guestfs_set_ready (g);\n";
2743
2744       (match fst style with
2745        | RErr -> pr "  return 0;\n"
2746        | RInt n | RInt64 n | RBool n ->
2747            pr "  return ctx.ret.%s;\n" n
2748        | RConstString _ ->
2749            failwithf "RConstString cannot be returned from a daemon function"
2750        | RString n ->
2751            pr "  return ctx.ret.%s; /* caller will free */\n" n
2752        | RStringList n | RHashtable n ->
2753            pr "  /* caller will free this, but we need to add a NULL entry */\n";
2754            pr "  ctx.ret.%s.%s_val =\n" n n;
2755            pr "    safe_realloc (g, ctx.ret.%s.%s_val,\n" n n;
2756            pr "                  sizeof (char *) * (ctx.ret.%s.%s_len + 1));\n"
2757              n n;
2758            pr "  ctx.ret.%s.%s_val[ctx.ret.%s.%s_len] = NULL;\n" n n n n;
2759            pr "  return ctx.ret.%s.%s_val;\n" n n
2760        | RIntBool _ ->
2761            pr "  /* caller with free this */\n";
2762            pr "  return safe_memdup (g, &ctx.ret, sizeof (ctx.ret));\n"
2763        | RPVList n | RVGList n | RLVList n
2764        | RStat n | RStatVFS n ->
2765            pr "  /* caller will free this */\n";
2766            pr "  return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n
2767       );
2768
2769       pr "}\n\n"
2770   ) daemon_functions
2771
2772 (* Generate daemon/actions.h. *)
2773 and generate_daemon_actions_h () =
2774   generate_header CStyle GPLv2;
2775
2776   pr "#include \"../src/guestfs_protocol.h\"\n";
2777   pr "\n";
2778
2779   List.iter (
2780     fun (name, style, _, _, _, _, _) ->
2781         generate_prototype
2782           ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
2783           name style;
2784   ) daemon_functions
2785
2786 (* Generate the server-side stubs. *)
2787 and generate_daemon_actions () =
2788   generate_header CStyle GPLv2;
2789
2790   pr "#include <config.h>\n";
2791   pr "\n";
2792   pr "#include <stdio.h>\n";
2793   pr "#include <stdlib.h>\n";
2794   pr "#include <string.h>\n";
2795   pr "#include <inttypes.h>\n";
2796   pr "#include <ctype.h>\n";
2797   pr "#include <rpc/types.h>\n";
2798   pr "#include <rpc/xdr.h>\n";
2799   pr "\n";
2800   pr "#include \"daemon.h\"\n";
2801   pr "#include \"../src/guestfs_protocol.h\"\n";
2802   pr "#include \"actions.h\"\n";
2803   pr "\n";
2804
2805   List.iter (
2806     fun (name, style, _, _, _, _, _) ->
2807       (* Generate server-side stubs. *)
2808       pr "static void %s_stub (XDR *xdr_in)\n" name;
2809       pr "{\n";
2810       let error_code =
2811         match fst style with
2812         | RErr | RInt _ -> pr "  int r;\n"; "-1"
2813         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
2814         | RBool _ -> pr "  int r;\n"; "-1"
2815         | RConstString _ ->
2816             failwithf "RConstString cannot be returned from a daemon function"
2817         | RString _ -> pr "  char *r;\n"; "NULL"
2818         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
2819         | RIntBool _ -> pr "  guestfs_%s_ret *r;\n" name; "NULL"
2820         | RPVList _ -> pr "  guestfs_lvm_int_pv_list *r;\n"; "NULL"
2821         | RVGList _ -> pr "  guestfs_lvm_int_vg_list *r;\n"; "NULL"
2822         | RLVList _ -> pr "  guestfs_lvm_int_lv_list *r;\n"; "NULL"
2823         | RStat _ -> pr "  guestfs_int_stat *r;\n"; "NULL"
2824         | RStatVFS _ -> pr "  guestfs_int_statvfs *r;\n"; "NULL" in
2825
2826       (match snd style with
2827        | [] -> ()
2828        | args ->
2829            pr "  struct guestfs_%s_args args;\n" name;
2830            List.iter (
2831              function
2832              | String n
2833              | OptString n -> pr "  const char *%s;\n" n
2834              | StringList n -> pr "  char **%s;\n" n
2835              | Bool n -> pr "  int %s;\n" n
2836              | Int n -> pr "  int %s;\n" n
2837              | FileIn _ | FileOut _ -> ()
2838            ) args
2839       );
2840       pr "\n";
2841
2842       (match snd style with
2843        | [] -> ()
2844        | args ->
2845            pr "  memset (&args, 0, sizeof args);\n";
2846            pr "\n";
2847            pr "  if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
2848            pr "    reply_with_error (\"%%s: daemon failed to decode procedure arguments\", \"%s\");\n" name;
2849            pr "    return;\n";
2850            pr "  }\n";
2851            List.iter (
2852              function
2853              | String n -> pr "  %s = args.%s;\n" n n
2854              | OptString n -> pr "  %s = args.%s ? *args.%s : NULL;\n" n n n
2855              | StringList n ->
2856                  pr "  %s = realloc (args.%s.%s_val,\n" n n n;
2857                  pr "                sizeof (char *) * (args.%s.%s_len+1));\n" n n;
2858                  pr "  if (%s == NULL) {\n" n;
2859                  pr "    reply_with_perror (\"realloc\");\n";
2860                  pr "    goto done;\n";
2861                  pr "  }\n";
2862                  pr "  %s[args.%s.%s_len] = NULL;\n" n n n;
2863                  pr "  args.%s.%s_val = %s;\n" n n n;
2864              | Bool n -> pr "  %s = args.%s;\n" n n
2865              | Int n -> pr "  %s = args.%s;\n" n n
2866              | FileIn _ | FileOut _ -> ()
2867            ) args;
2868            pr "\n"
2869       );
2870
2871       (* Don't want to call the impl with any FileIn or FileOut
2872        * parameters, since these go "outside" the RPC protocol.
2873        *)
2874       let argsnofile =
2875         List.filter (function FileIn _ | FileOut _ -> false | _ -> true)
2876           (snd style) in
2877       pr "  r = do_%s " name;
2878       generate_call_args argsnofile;
2879       pr ";\n";
2880
2881       pr "  if (r == %s)\n" error_code;
2882       pr "    /* do_%s has already called reply_with_error */\n" name;
2883       pr "    goto done;\n";
2884       pr "\n";
2885
2886       (* If there are any FileOut parameters, then the impl must
2887        * send its own reply.
2888        *)
2889       let no_reply =
2890         List.exists (function FileOut _ -> true | _ -> false) (snd style) in
2891       if no_reply then
2892         pr "  /* do_%s has already sent a reply */\n" name
2893       else (
2894         match fst style with
2895         | RErr -> pr "  reply (NULL, NULL);\n"
2896         | RInt n | RInt64 n | RBool n ->
2897             pr "  struct guestfs_%s_ret ret;\n" name;
2898             pr "  ret.%s = r;\n" n;
2899             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2900               name
2901         | RConstString _ ->
2902             failwithf "RConstString cannot be returned from a daemon function"
2903         | RString n ->
2904             pr "  struct guestfs_%s_ret ret;\n" name;
2905             pr "  ret.%s = r;\n" n;
2906             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2907               name;
2908             pr "  free (r);\n"
2909         | RStringList n | RHashtable n ->
2910             pr "  struct guestfs_%s_ret ret;\n" name;
2911             pr "  ret.%s.%s_len = count_strings (r);\n" n n;
2912             pr "  ret.%s.%s_val = r;\n" n n;
2913             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2914               name;
2915             pr "  free_strings (r);\n"
2916         | RIntBool _ ->
2917             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n"
2918               name;
2919             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name
2920         | RPVList n | RVGList n | RLVList n
2921         | RStat n | RStatVFS n ->
2922             pr "  struct guestfs_%s_ret ret;\n" name;
2923             pr "  ret.%s = *r;\n" n;
2924             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
2925               name;
2926             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
2927               name
2928       );
2929
2930       (* Free the args. *)
2931       (match snd style with
2932        | [] ->
2933            pr "done: ;\n";
2934        | _ ->
2935            pr "done:\n";
2936            pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
2937              name
2938       );
2939
2940       pr "}\n\n";
2941   ) daemon_functions;
2942
2943   (* Dispatch function. *)
2944   pr "void dispatch_incoming_message (XDR *xdr_in)\n";
2945   pr "{\n";
2946   pr "  switch (proc_nr) {\n";
2947
2948   List.iter (
2949     fun (name, style, _, _, _, _, _) ->
2950         pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
2951         pr "      %s_stub (xdr_in);\n" name;
2952         pr "      break;\n"
2953   ) daemon_functions;
2954
2955   pr "    default:\n";
2956   pr "      reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
2957   pr "  }\n";
2958   pr "}\n";
2959   pr "\n";
2960
2961   (* LVM columns and tokenization functions. *)
2962   (* XXX This generates crap code.  We should rethink how we
2963    * do this parsing.
2964    *)
2965   List.iter (
2966     function
2967     | typ, cols ->
2968         pr "static const char *lvm_%s_cols = \"%s\";\n"
2969           typ (String.concat "," (List.map fst cols));
2970         pr "\n";
2971
2972         pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
2973         pr "{\n";
2974         pr "  char *tok, *p, *next;\n";
2975         pr "  int i, j;\n";
2976         pr "\n";
2977         (*
2978         pr "  fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
2979         pr "\n";
2980         *)
2981         pr "  if (!str) {\n";
2982         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
2983         pr "    return -1;\n";
2984         pr "  }\n";
2985         pr "  if (!*str || isspace (*str)) {\n";
2986         pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
2987         pr "    return -1;\n";
2988         pr "  }\n";
2989         pr "  tok = str;\n";
2990         List.iter (
2991           fun (name, coltype) ->
2992             pr "  if (!tok) {\n";
2993             pr "    fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
2994             pr "    return -1;\n";
2995             pr "  }\n";
2996             pr "  p = strchrnul (tok, ',');\n";
2997             pr "  if (*p) next = p+1; else next = NULL;\n";
2998             pr "  *p = '\\0';\n";
2999             (match coltype with
3000              | `String ->
3001                  pr "  r->%s = strdup (tok);\n" name;
3002                  pr "  if (r->%s == NULL) {\n" name;
3003                  pr "    perror (\"strdup\");\n";
3004                  pr "    return -1;\n";
3005                  pr "  }\n"
3006              | `UUID ->
3007                  pr "  for (i = j = 0; i < 32; ++j) {\n";
3008                  pr "    if (tok[j] == '\\0') {\n";
3009                  pr "      fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
3010                  pr "      return -1;\n";
3011                  pr "    } else if (tok[j] != '-')\n";
3012                  pr "      r->%s[i++] = tok[j];\n" name;
3013                  pr "  }\n";
3014              | `Bytes ->
3015                  pr "  if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
3016                  pr "    fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3017                  pr "    return -1;\n";
3018                  pr "  }\n";
3019              | `Int ->
3020                  pr "  if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
3021                  pr "    fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3022                  pr "    return -1;\n";
3023                  pr "  }\n";
3024              | `OptPercent ->
3025                  pr "  if (tok[0] == '\\0')\n";
3026                  pr "    r->%s = -1;\n" name;
3027                  pr "  else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
3028                  pr "    fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
3029                  pr "    return -1;\n";
3030                  pr "  }\n";
3031             );
3032             pr "  tok = next;\n";
3033         ) cols;
3034
3035         pr "  if (tok != NULL) {\n";
3036         pr "    fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
3037         pr "    return -1;\n";
3038         pr "  }\n";
3039         pr "  return 0;\n";
3040         pr "}\n";
3041         pr "\n";
3042
3043         pr "guestfs_lvm_int_%s_list *\n" typ;
3044         pr "parse_command_line_%ss (void)\n" typ;
3045         pr "{\n";
3046         pr "  char *out, *err;\n";
3047         pr "  char *p, *pend;\n";
3048         pr "  int r, i;\n";
3049         pr "  guestfs_lvm_int_%s_list *ret;\n" typ;
3050         pr "  void *newp;\n";
3051         pr "\n";
3052         pr "  ret = malloc (sizeof *ret);\n";
3053         pr "  if (!ret) {\n";
3054         pr "    reply_with_perror (\"malloc\");\n";
3055         pr "    return NULL;\n";
3056         pr "  }\n";
3057         pr "\n";
3058         pr "  ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
3059         pr "  ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
3060         pr "\n";
3061         pr "  r = command (&out, &err,\n";
3062         pr "           \"/sbin/lvm\", \"%ss\",\n" typ;
3063         pr "           \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
3064         pr "           \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
3065         pr "  if (r == -1) {\n";
3066         pr "    reply_with_error (\"%%s\", err);\n";
3067         pr "    free (out);\n";
3068         pr "    free (err);\n";
3069         pr "    free (ret);\n";
3070         pr "    return NULL;\n";
3071         pr "  }\n";
3072         pr "\n";
3073         pr "  free (err);\n";
3074         pr "\n";
3075         pr "  /* Tokenize each line of the output. */\n";
3076         pr "  p = out;\n";
3077         pr "  i = 0;\n";
3078         pr "  while (p) {\n";
3079         pr "    pend = strchr (p, '\\n');       /* Get the next line of output. */\n";
3080         pr "    if (pend) {\n";
3081         pr "      *pend = '\\0';\n";
3082         pr "      pend++;\n";
3083         pr "    }\n";
3084         pr "\n";
3085         pr "    while (*p && isspace (*p))      /* Skip any leading whitespace. */\n";
3086         pr "      p++;\n";
3087         pr "\n";
3088         pr "    if (!*p) {                      /* Empty line?  Skip it. */\n";
3089         pr "      p = pend;\n";
3090         pr "      continue;\n";
3091         pr "    }\n";
3092         pr "\n";
3093         pr "    /* Allocate some space to store this next entry. */\n";
3094         pr "    newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
3095         pr "                sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
3096         pr "    if (newp == NULL) {\n";
3097         pr "      reply_with_perror (\"realloc\");\n";
3098         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
3099         pr "      free (ret);\n";
3100         pr "      free (out);\n";
3101         pr "      return NULL;\n";
3102         pr "    }\n";
3103         pr "    ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
3104         pr "\n";
3105         pr "    /* Tokenize the next entry. */\n";
3106         pr "    r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
3107         pr "    if (r == -1) {\n";
3108         pr "      reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
3109         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
3110         pr "      free (ret);\n";
3111         pr "      free (out);\n";
3112         pr "      return NULL;\n";
3113         pr "    }\n";
3114         pr "\n";
3115         pr "    ++i;\n";
3116         pr "    p = pend;\n";
3117         pr "  }\n";
3118         pr "\n";
3119         pr "  ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
3120         pr "\n";
3121         pr "  free (out);\n";
3122         pr "  return ret;\n";
3123         pr "}\n"
3124
3125   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
3126
3127 (* Generate the tests. *)
3128 and generate_tests () =
3129   generate_header CStyle GPLv2;
3130
3131   pr "\
3132 #include <stdio.h>
3133 #include <stdlib.h>
3134 #include <string.h>
3135 #include <unistd.h>
3136 #include <sys/types.h>
3137 #include <fcntl.h>
3138
3139 #include \"guestfs.h\"
3140
3141 static guestfs_h *g;
3142 static int suppress_error = 0;
3143
3144 static void print_error (guestfs_h *g, void *data, const char *msg)
3145 {
3146   if (!suppress_error)
3147     fprintf (stderr, \"%%s\\n\", msg);
3148 }
3149
3150 static void print_strings (char * const * const argv)
3151 {
3152   int argc;
3153
3154   for (argc = 0; argv[argc] != NULL; ++argc)
3155     printf (\"\\t%%s\\n\", argv[argc]);
3156 }
3157
3158 /*
3159 static void print_table (char * const * const argv)
3160 {
3161   int i;
3162
3163   for (i = 0; argv[i] != NULL; i += 2)
3164     printf (\"%%s: %%s\\n\", argv[i], argv[i+1]);
3165 }
3166 */
3167
3168 static void no_test_warnings (void)
3169 {
3170 ";
3171
3172   List.iter (
3173     function
3174     | name, _, _, _, [], _, _ ->
3175         pr "  fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name
3176     | name, _, _, _, tests, _, _ -> ()
3177   ) all_functions;
3178
3179   pr "}\n";
3180   pr "\n";
3181
3182   (* Generate the actual tests.  Note that we generate the tests
3183    * in reverse order, deliberately, so that (in general) the
3184    * newest tests run first.  This makes it quicker and easier to
3185    * debug them.
3186    *)
3187   let test_names =
3188     List.map (
3189       fun (name, _, _, _, tests, _, _) ->
3190         mapi (generate_one_test name) tests
3191     ) (List.rev all_functions) in
3192   let test_names = List.concat test_names in
3193   let nr_tests = List.length test_names in
3194
3195   pr "\
3196 int main (int argc, char *argv[])
3197 {
3198   char c = 0;
3199   int failed = 0;
3200   const char *srcdir;
3201   const char *filename;
3202   int fd;
3203   int nr_tests, test_num = 0;
3204
3205   no_test_warnings ();
3206
3207   g = guestfs_create ();
3208   if (g == NULL) {
3209     printf (\"guestfs_create FAILED\\n\");
3210     exit (1);
3211   }
3212
3213   guestfs_set_error_handler (g, print_error, NULL);
3214
3215   srcdir = getenv (\"srcdir\");
3216   if (!srcdir) srcdir = \".\";
3217   chdir (srcdir);
3218   guestfs_set_path (g, \".\");
3219
3220   filename = \"test1.img\";
3221   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3222   if (fd == -1) {
3223     perror (filename);
3224     exit (1);
3225   }
3226   if (lseek (fd, %d, SEEK_SET) == -1) {
3227     perror (\"lseek\");
3228     close (fd);
3229     unlink (filename);
3230     exit (1);
3231   }
3232   if (write (fd, &c, 1) == -1) {
3233     perror (\"write\");
3234     close (fd);
3235     unlink (filename);
3236     exit (1);
3237   }
3238   if (close (fd) == -1) {
3239     perror (filename);
3240     unlink (filename);
3241     exit (1);
3242   }
3243   if (guestfs_add_drive (g, filename) == -1) {
3244     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3245     exit (1);
3246   }
3247
3248   filename = \"test2.img\";
3249   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3250   if (fd == -1) {
3251     perror (filename);
3252     exit (1);
3253   }
3254   if (lseek (fd, %d, SEEK_SET) == -1) {
3255     perror (\"lseek\");
3256     close (fd);
3257     unlink (filename);
3258     exit (1);
3259   }
3260   if (write (fd, &c, 1) == -1) {
3261     perror (\"write\");
3262     close (fd);
3263     unlink (filename);
3264     exit (1);
3265   }
3266   if (close (fd) == -1) {
3267     perror (filename);
3268     unlink (filename);
3269     exit (1);
3270   }
3271   if (guestfs_add_drive (g, filename) == -1) {
3272     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3273     exit (1);
3274   }
3275
3276   filename = \"test3.img\";
3277   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
3278   if (fd == -1) {
3279     perror (filename);
3280     exit (1);
3281   }
3282   if (lseek (fd, %d, SEEK_SET) == -1) {
3283     perror (\"lseek\");
3284     close (fd);
3285     unlink (filename);
3286     exit (1);
3287   }
3288   if (write (fd, &c, 1) == -1) {
3289     perror (\"write\");
3290     close (fd);
3291     unlink (filename);
3292     exit (1);
3293   }
3294   if (close (fd) == -1) {
3295     perror (filename);
3296     unlink (filename);
3297     exit (1);
3298   }
3299   if (guestfs_add_drive (g, filename) == -1) {
3300     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
3301     exit (1);
3302   }
3303
3304   if (guestfs_launch (g) == -1) {
3305     printf (\"guestfs_launch FAILED\\n\");
3306     exit (1);
3307   }
3308   if (guestfs_wait_ready (g) == -1) {
3309     printf (\"guestfs_wait_ready FAILED\\n\");
3310     exit (1);
3311   }
3312
3313   nr_tests = %d;
3314
3315 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
3316
3317   iteri (
3318     fun i test_name ->
3319       pr "  test_num++;\n";
3320       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
3321       pr "  if (%s () == -1) {\n" test_name;
3322       pr "    printf (\"%s FAILED\\n\");\n" test_name;
3323       pr "    failed++;\n";
3324       pr "  }\n";
3325   ) test_names;
3326   pr "\n";
3327
3328   pr "  guestfs_close (g);\n";
3329   pr "  unlink (\"test1.img\");\n";
3330   pr "  unlink (\"test2.img\");\n";
3331   pr "  unlink (\"test3.img\");\n";
3332   pr "\n";
3333
3334   pr "  if (failed > 0) {\n";
3335   pr "    printf (\"***** %%d / %%d tests FAILED *****\\n\", failed, nr_tests);\n";
3336   pr "    exit (1);\n";
3337   pr "  }\n";
3338   pr "\n";
3339
3340   pr "  exit (0);\n";
3341   pr "}\n"
3342
3343 and generate_one_test name i (init, test) =
3344   let test_name = sprintf "test_%s_%d" name i in
3345
3346   pr "static int %s (void)\n" test_name;
3347   pr "{\n";
3348
3349   (match init with
3350    | InitNone -> ()
3351    | InitEmpty ->
3352        pr "  /* InitEmpty for %s (%d) */\n" name i;
3353        List.iter (generate_test_command_call test_name)
3354          [["umount_all"];
3355           ["lvm_remove_all"]]
3356    | InitBasicFS ->
3357        pr "  /* InitBasicFS for %s (%d): create ext2 on /dev/sda1 */\n" name i;
3358        List.iter (generate_test_command_call test_name)
3359          [["umount_all"];
3360           ["lvm_remove_all"];
3361           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
3362           ["mkfs"; "ext2"; "/dev/sda1"];
3363           ["mount"; "/dev/sda1"; "/"]]
3364    | InitBasicFSonLVM ->
3365        pr "  /* InitBasicFSonLVM for %s (%d): create ext2 on /dev/VG/LV */\n"
3366          name i;
3367        List.iter (generate_test_command_call test_name)
3368          [["umount_all"];
3369           ["lvm_remove_all"];
3370           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
3371           ["pvcreate"; "/dev/sda1"];
3372           ["vgcreate"; "VG"; "/dev/sda1"];
3373           ["lvcreate"; "LV"; "VG"; "8"];
3374           ["mkfs"; "ext2"; "/dev/VG/LV"];
3375           ["mount"; "/dev/VG/LV"; "/"]]
3376   );
3377
3378   let get_seq_last = function
3379     | [] ->
3380         failwithf "%s: you cannot use [] (empty list) when expecting a command"
3381           test_name
3382     | seq ->
3383         let seq = List.rev seq in
3384         List.rev (List.tl seq), List.hd seq
3385   in
3386
3387   (match test with
3388    | TestRun seq ->
3389        pr "  /* TestRun for %s (%d) */\n" name i;
3390        List.iter (generate_test_command_call test_name) seq
3391    | TestOutput (seq, expected) ->
3392        pr "  /* TestOutput for %s (%d) */\n" name i;
3393        let seq, last = get_seq_last seq in
3394        let test () =
3395          pr "    if (strcmp (r, \"%s\") != 0) {\n" (c_quote expected);
3396          pr "      fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r);\n" test_name (c_quote expected);
3397          pr "      return -1;\n";
3398          pr "    }\n"
3399        in
3400        List.iter (generate_test_command_call test_name) seq;
3401        generate_test_command_call ~test test_name last
3402    | TestOutputList (seq, expected) ->
3403        pr "  /* TestOutputList for %s (%d) */\n" name i;
3404        let seq, last = get_seq_last seq in
3405        let test () =
3406          iteri (
3407            fun i str ->
3408              pr "    if (!r[%d]) {\n" i;
3409              pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
3410              pr "      print_strings (r);\n";
3411              pr "      return -1;\n";
3412              pr "    }\n";
3413              pr "    if (strcmp (r[%d], \"%s\") != 0) {\n" i (c_quote str);
3414              pr "      fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r[%d]);\n" test_name (c_quote str) i;
3415              pr "      return -1;\n";
3416              pr "    }\n"
3417          ) expected;
3418          pr "    if (r[%d] != NULL) {\n" (List.length expected);
3419          pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
3420            test_name;
3421          pr "      print_strings (r);\n";
3422          pr "      return -1;\n";
3423          pr "    }\n"
3424        in
3425        List.iter (generate_test_command_call test_name) seq;
3426        generate_test_command_call ~test test_name last
3427    | TestOutputInt (seq, expected) ->
3428        pr "  /* TestOutputInt for %s (%d) */\n" name i;
3429        let seq, last = get_seq_last seq in
3430        let test () =
3431          pr "    if (r != %d) {\n" expected;
3432          pr "      fprintf (stderr, \"%s: expected %d but got %%d\\n\","
3433            test_name expected;
3434          pr "               (int) r);\n";
3435          pr "      return -1;\n";
3436          pr "    }\n"
3437        in
3438        List.iter (generate_test_command_call test_name) seq;
3439        generate_test_command_call ~test test_name last
3440    | TestOutputTrue seq ->
3441        pr "  /* TestOutputTrue for %s (%d) */\n" name i;
3442        let seq, last = get_seq_last seq in
3443        let test () =
3444          pr "    if (!r) {\n";
3445          pr "      fprintf (stderr, \"%s: expected true, got false\\n\");\n"
3446            test_name;
3447          pr "      return -1;\n";
3448          pr "    }\n"
3449        in
3450        List.iter (generate_test_command_call test_name) seq;
3451        generate_test_command_call ~test test_name last
3452    | TestOutputFalse seq ->
3453        pr "  /* TestOutputFalse for %s (%d) */\n" name i;
3454        let seq, last = get_seq_last seq in
3455        let test () =
3456          pr "    if (r) {\n";
3457          pr "      fprintf (stderr, \"%s: expected false, got true\\n\");\n"
3458            test_name;
3459          pr "      return -1;\n";
3460          pr "    }\n"
3461        in
3462        List.iter (generate_test_command_call test_name) seq;
3463        generate_test_command_call ~test test_name last
3464    | TestOutputLength (seq, expected) ->
3465        pr "  /* TestOutputLength for %s (%d) */\n" name i;
3466        let seq, last = get_seq_last seq in
3467        let test () =
3468          pr "    int j;\n";
3469          pr "    for (j = 0; j < %d; ++j)\n" expected;
3470          pr "      if (r[j] == NULL) {\n";
3471          pr "        fprintf (stderr, \"%s: short list returned\\n\");\n"
3472            test_name;
3473          pr "        print_strings (r);\n";
3474          pr "        return -1;\n";
3475          pr "      }\n";
3476          pr "    if (r[j] != NULL) {\n";
3477          pr "      fprintf (stderr, \"%s: long list returned\\n\");\n"
3478            test_name;
3479          pr "      print_strings (r);\n";
3480          pr "      return -1;\n";
3481          pr "    }\n"
3482        in
3483        List.iter (generate_test_command_call test_name) seq;
3484        generate_test_command_call ~test test_name last
3485    | TestOutputStruct (seq, checks) ->
3486        pr "  /* TestOutputStruct for %s (%d) */\n" name i;
3487        let seq, last = get_seq_last seq in
3488        let test () =
3489          List.iter (
3490            function
3491            | CompareWithInt (field, expected) ->
3492                pr "    if (r->%s != %d) {\n" field expected;
3493                pr "      fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
3494                  test_name field expected;
3495                pr "               (int) r->%s);\n" field;
3496                pr "      return -1;\n";
3497                pr "    }\n"
3498            | CompareWithString (field, expected) ->
3499                pr "    if (strcmp (r->%s, \"%s\") != 0) {\n" field expected;
3500                pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
3501                  test_name field expected;
3502                pr "               r->%s);\n" field;
3503                pr "      return -1;\n";
3504                pr "    }\n"
3505            | CompareFieldsIntEq (field1, field2) ->
3506                pr "    if (r->%s != r->%s) {\n" field1 field2;
3507                pr "      fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
3508                  test_name field1 field2;
3509                pr "               (int) r->%s, (int) r->%s);\n" field1 field2;
3510                pr "      return -1;\n";
3511                pr "    }\n"
3512            | CompareFieldsStrEq (field1, field2) ->
3513                pr "    if (strcmp (r->%s, r->%s) != 0) {\n" field1 field2;
3514                pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
3515                  test_name field1 field2;
3516                pr "               r->%s, r->%s);\n" field1 field2;
3517                pr "      return -1;\n";
3518                pr "    }\n"
3519          ) checks
3520        in
3521        List.iter (generate_test_command_call test_name) seq;
3522        generate_test_command_call ~test test_name last
3523    | TestLastFail seq ->
3524        pr "  /* TestLastFail for %s (%d) */\n" name i;
3525        let seq, last = get_seq_last seq in
3526        List.iter (generate_test_command_call test_name) seq;
3527        generate_test_command_call test_name ~expect_error:true last
3528   );
3529
3530   pr "  return 0;\n";
3531   pr "}\n";
3532   pr "\n";
3533   test_name
3534
3535 (* Generate the code to run a command, leaving the result in 'r'.
3536  * If you expect to get an error then you should set expect_error:true.
3537  *)
3538 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
3539   match cmd with
3540   | [] -> assert false
3541   | name :: args ->
3542       (* Look up the command to find out what args/ret it has. *)
3543       let style =
3544         try
3545           let _, style, _, _, _, _, _ =
3546             List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
3547           style
3548         with Not_found ->
3549           failwithf "%s: in test, command %s was not found" test_name name in
3550
3551       if List.length (snd style) <> List.length args then
3552         failwithf "%s: in test, wrong number of args given to %s"
3553           test_name name;
3554
3555       pr "  {\n";
3556
3557       List.iter (
3558         function
3559         | String _, _
3560         | OptString _, _
3561         | Int _, _
3562         | Bool _, _ -> ()
3563         | FileIn _, _ | FileOut _, _ -> ()
3564         | StringList n, arg ->
3565             pr "    char *%s[] = {\n" n;
3566             let strs = string_split " " arg in
3567             List.iter (
3568               fun str -> pr "      \"%s\",\n" (c_quote str)
3569             ) strs;
3570             pr "      NULL\n";
3571             pr "    };\n";
3572       ) (List.combine (snd style) args);
3573
3574       let error_code =
3575         match fst style with
3576         | RErr | RInt _ | RBool _ -> pr "    int r;\n"; "-1"
3577         | RInt64 _ -> pr "    int64_t r;\n"; "-1"
3578         | RConstString _ -> pr "    const char *r;\n"; "NULL"
3579         | RString _ -> pr "    char *r;\n"; "NULL"
3580         | RStringList _ | RHashtable _ ->
3581             pr "    char **r;\n";
3582             pr "    int i;\n";
3583             "NULL"
3584         | RIntBool _ ->
3585             pr "    struct guestfs_int_bool *r;\n"; "NULL"
3586         | RPVList _ ->
3587             pr "    struct guestfs_lvm_pv_list *r;\n"; "NULL"
3588         | RVGList _ ->
3589             pr "    struct guestfs_lvm_vg_list *r;\n"; "NULL"
3590         | RLVList _ ->
3591             pr "    struct guestfs_lvm_lv_list *r;\n"; "NULL"
3592         | RStat _ ->
3593             pr "    struct guestfs_stat *r;\n"; "NULL"
3594         | RStatVFS _ ->
3595             pr "    struct guestfs_statvfs *r;\n"; "NULL" in
3596
3597       pr "    suppress_error = %d;\n" (if expect_error then 1 else 0);
3598       pr "    r = guestfs_%s (g" name;
3599
3600       (* Generate the parameters. *)
3601       List.iter (
3602         function
3603         | String _, arg
3604         | FileIn _, arg | FileOut _, arg ->
3605             pr ", \"%s\"" (c_quote arg)
3606         | OptString _, arg ->
3607             if arg = "NULL" then pr ", NULL" else pr ", \"%s\"" (c_quote arg)
3608         | StringList n, _ ->
3609             pr ", %s" n
3610         | Int _, arg ->
3611             let i =
3612               try int_of_string arg
3613               with Failure "int_of_string" ->
3614                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
3615             pr ", %d" i
3616         | Bool _, arg ->
3617             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
3618       ) (List.combine (snd style) args);
3619
3620       pr ");\n";
3621       if not expect_error then
3622         pr "    if (r == %s)\n" error_code
3623       else
3624         pr "    if (r != %s)\n" error_code;
3625       pr "      return -1;\n";
3626
3627       (* Insert the test code. *)
3628       (match test with
3629        | None -> ()
3630        | Some f -> f ()
3631       );
3632
3633       (match fst style with
3634        | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _ -> ()
3635        | RString _ -> pr "    free (r);\n"
3636        | RStringList _ | RHashtable _ ->
3637            pr "    for (i = 0; r[i] != NULL; ++i)\n";
3638            pr "      free (r[i]);\n";
3639            pr "    free (r);\n"
3640        | RIntBool _ ->
3641            pr "    guestfs_free_int_bool (r);\n"
3642        | RPVList _ ->
3643            pr "    guestfs_free_lvm_pv_list (r);\n"
3644        | RVGList _ ->
3645            pr "    guestfs_free_lvm_vg_list (r);\n"
3646        | RLVList _ ->
3647            pr "    guestfs_free_lvm_lv_list (r);\n"
3648        | RStat _ | RStatVFS _ ->
3649            pr "    free (r);\n"
3650       );
3651
3652       pr "  }\n"
3653
3654 and c_quote str =
3655   let str = replace_str str "\r" "\\r" in
3656   let str = replace_str str "\n" "\\n" in
3657   let str = replace_str str "\t" "\\t" in
3658   str
3659
3660 (* Generate a lot of different functions for guestfish. *)
3661 and generate_fish_cmds () =
3662   generate_header CStyle GPLv2;
3663
3664   let all_functions =
3665     List.filter (
3666       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3667     ) all_functions in
3668   let all_functions_sorted =
3669     List.filter (
3670       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3671     ) all_functions_sorted in
3672
3673   pr "#include <stdio.h>\n";
3674   pr "#include <stdlib.h>\n";
3675   pr "#include <string.h>\n";
3676   pr "#include <inttypes.h>\n";
3677   pr "\n";
3678   pr "#include <guestfs.h>\n";
3679   pr "#include \"fish.h\"\n";
3680   pr "\n";
3681
3682   (* list_commands function, which implements guestfish -h *)
3683   pr "void list_commands (void)\n";
3684   pr "{\n";
3685   pr "  printf (\"    %%-16s     %%s\\n\", \"Command\", \"Description\");\n";
3686   pr "  list_builtin_commands ();\n";
3687   List.iter (
3688     fun (name, _, _, flags, _, shortdesc, _) ->
3689       let name = replace_char name '_' '-' in
3690       pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
3691         name shortdesc
3692   ) all_functions_sorted;
3693   pr "  printf (\"    Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
3694   pr "}\n";
3695   pr "\n";
3696
3697   (* display_command function, which implements guestfish -h cmd *)
3698   pr "void display_command (const char *cmd)\n";
3699   pr "{\n";
3700   List.iter (
3701     fun (name, style, _, flags, _, shortdesc, longdesc) ->
3702       let name2 = replace_char name '_' '-' in
3703       let alias =
3704         try find_map (function FishAlias n -> Some n | _ -> None) flags
3705         with Not_found -> name in
3706       let longdesc = replace_str longdesc "C<guestfs_" "C<" in
3707       let synopsis =
3708         match snd style with
3709         | [] -> name2
3710         | args ->
3711             sprintf "%s <%s>"
3712               name2 (String.concat "> <" (List.map name_of_argt args)) in
3713
3714       let warnings =
3715         if List.mem ProtocolLimitWarning flags then
3716           ("\n\n" ^ protocol_limit_warning)
3717         else "" in
3718
3719       (* For DangerWillRobinson commands, we should probably have
3720        * guestfish prompt before allowing you to use them (especially
3721        * in interactive mode). XXX
3722        *)
3723       let warnings =
3724         warnings ^
3725           if List.mem DangerWillRobinson flags then
3726             ("\n\n" ^ danger_will_robinson)
3727           else "" in
3728
3729       let describe_alias =
3730         if name <> alias then
3731           sprintf "\n\nYou can use '%s' as an alias for this command." alias
3732         else "" in
3733
3734       pr "  if (";
3735       pr "strcasecmp (cmd, \"%s\") == 0" name;
3736       if name <> name2 then
3737         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
3738       if name <> alias then
3739         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
3740       pr ")\n";
3741       pr "    pod2text (\"%s - %s\", %S);\n"
3742         name2 shortdesc
3743         (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings ^ describe_alias);
3744       pr "  else\n"
3745   ) all_functions;
3746   pr "    display_builtin_command (cmd);\n";
3747   pr "}\n";
3748   pr "\n";
3749
3750   (* print_{pv,vg,lv}_list functions *)
3751   List.iter (
3752     function
3753     | typ, cols ->
3754         pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
3755         pr "{\n";
3756         pr "  int i;\n";
3757         pr "\n";
3758         List.iter (
3759           function
3760           | name, `String ->
3761               pr "  printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
3762           | name, `UUID ->
3763               pr "  printf (\"%s: \");\n" name;
3764               pr "  for (i = 0; i < 32; ++i)\n";
3765               pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
3766               pr "  printf (\"\\n\");\n"
3767           | name, `Bytes ->
3768               pr "  printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
3769           | name, `Int ->
3770               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
3771           | name, `OptPercent ->
3772               pr "  if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
3773                 typ name name typ name;
3774               pr "  else printf (\"%s: \\n\");\n" name
3775         ) cols;
3776         pr "}\n";
3777         pr "\n";
3778         pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
3779           typ typ typ;
3780         pr "{\n";
3781         pr "  int i;\n";
3782         pr "\n";
3783         pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
3784         pr "    print_%s (&%ss->val[i]);\n" typ typ;
3785         pr "}\n";
3786         pr "\n";
3787   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
3788
3789   (* print_{stat,statvfs} functions *)
3790   List.iter (
3791     function
3792     | typ, cols ->
3793         pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
3794         pr "{\n";
3795         List.iter (
3796           function
3797           | name, `Int ->
3798               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
3799         ) cols;
3800         pr "}\n";
3801         pr "\n";
3802   ) ["stat", stat_cols; "statvfs", statvfs_cols];
3803
3804   (* run_<action> actions *)
3805   List.iter (
3806     fun (name, style, _, flags, _, _, _) ->
3807       pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
3808       pr "{\n";
3809       (match fst style with
3810        | RErr
3811        | RInt _
3812        | RBool _ -> pr "  int r;\n"
3813        | RInt64 _ -> pr "  int64_t r;\n"
3814        | RConstString _ -> pr "  const char *r;\n"
3815        | RString _ -> pr "  char *r;\n"
3816        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
3817        | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"
3818        | RPVList _ -> pr "  struct guestfs_lvm_pv_list *r;\n"
3819        | RVGList _ -> pr "  struct guestfs_lvm_vg_list *r;\n"
3820        | RLVList _ -> pr "  struct guestfs_lvm_lv_list *r;\n"
3821        | RStat _ -> pr "  struct guestfs_stat *r;\n"
3822        | RStatVFS _ -> pr "  struct guestfs_statvfs *r;\n"
3823       );
3824       List.iter (
3825         function
3826         | String n
3827         | OptString n
3828         | FileIn n
3829         | FileOut n -> pr "  const char *%s;\n" n
3830         | StringList n -> pr "  char **%s;\n" n
3831         | Bool n -> pr "  int %s;\n" n
3832         | Int n -> pr "  int %s;\n" n
3833       ) (snd style);
3834
3835       (* Check and convert parameters. *)
3836       let argc_expected = List.length (snd style) in
3837       pr "  if (argc != %d) {\n" argc_expected;
3838       pr "    fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
3839         argc_expected;
3840       pr "    fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
3841       pr "    return -1;\n";
3842       pr "  }\n";
3843       iteri (
3844         fun i ->
3845           function
3846           | String name -> pr "  %s = argv[%d];\n" name i
3847           | OptString name ->
3848               pr "  %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
3849                 name i i
3850           | FileIn name ->
3851               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdin\";\n"
3852                 name i i
3853           | FileOut name ->
3854               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdout\";\n"
3855                 name i i
3856           | StringList name ->
3857               pr "  %s = parse_string_list (argv[%d]);\n" name i
3858           | Bool name ->
3859               pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
3860           | Int name ->
3861               pr "  %s = atoi (argv[%d]);\n" name i
3862       ) (snd style);
3863
3864       (* Call C API function. *)
3865       let fn =
3866         try find_map (function FishAction n -> Some n | _ -> None) flags
3867         with Not_found -> sprintf "guestfs_%s" name in
3868       pr "  r = %s " fn;
3869       generate_call_args ~handle:"g" (snd style);
3870       pr ";\n";
3871
3872       (* Check return value for errors and display command results. *)
3873       (match fst style with
3874        | RErr -> pr "  return r;\n"
3875        | RInt _ ->
3876            pr "  if (r == -1) return -1;\n";
3877            pr "  printf (\"%%d\\n\", r);\n";
3878            pr "  return 0;\n"
3879        | RInt64 _ ->
3880            pr "  if (r == -1) return -1;\n";
3881            pr "  printf (\"%%\" PRIi64 \"\\n\", r);\n";
3882            pr "  return 0;\n"
3883        | RBool _ ->
3884            pr "  if (r == -1) return -1;\n";
3885            pr "  if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
3886            pr "  return 0;\n"
3887        | RConstString _ ->
3888            pr "  if (r == NULL) return -1;\n";
3889            pr "  printf (\"%%s\\n\", r);\n";
3890            pr "  return 0;\n"
3891        | RString _ ->
3892            pr "  if (r == NULL) return -1;\n";
3893            pr "  printf (\"%%s\\n\", r);\n";
3894            pr "  free (r);\n";
3895            pr "  return 0;\n"
3896        | RStringList _ ->
3897            pr "  if (r == NULL) return -1;\n";
3898            pr "  print_strings (r);\n";
3899            pr "  free_strings (r);\n";
3900            pr "  return 0;\n"
3901        | RIntBool _ ->
3902            pr "  if (r == NULL) return -1;\n";
3903            pr "  printf (\"%%d, %%s\\n\", r->i,\n";
3904            pr "    r->b ? \"true\" : \"false\");\n";
3905            pr "  guestfs_free_int_bool (r);\n";
3906            pr "  return 0;\n"
3907        | RPVList _ ->
3908            pr "  if (r == NULL) return -1;\n";
3909            pr "  print_pv_list (r);\n";
3910            pr "  guestfs_free_lvm_pv_list (r);\n";
3911            pr "  return 0;\n"
3912        | RVGList _ ->
3913            pr "  if (r == NULL) return -1;\n";
3914            pr "  print_vg_list (r);\n";
3915            pr "  guestfs_free_lvm_vg_list (r);\n";
3916            pr "  return 0;\n"
3917        | RLVList _ ->
3918            pr "  if (r == NULL) return -1;\n";
3919            pr "  print_lv_list (r);\n";
3920            pr "  guestfs_free_lvm_lv_list (r);\n";
3921            pr "  return 0;\n"
3922        | RStat _ ->
3923            pr "  if (r == NULL) return -1;\n";
3924            pr "  print_stat (r);\n";
3925            pr "  free (r);\n";
3926            pr "  return 0;\n"
3927        | RStatVFS _ ->
3928            pr "  if (r == NULL) return -1;\n";
3929            pr "  print_statvfs (r);\n";
3930            pr "  free (r);\n";
3931            pr "  return 0;\n"
3932        | RHashtable _ ->
3933            pr "  if (r == NULL) return -1;\n";
3934            pr "  print_table (r);\n";
3935            pr "  free_strings (r);\n";
3936            pr "  return 0;\n"
3937       );
3938       pr "}\n";
3939       pr "\n"
3940   ) all_functions;
3941
3942   (* run_action function *)
3943   pr "int run_action (const char *cmd, int argc, char *argv[])\n";
3944   pr "{\n";
3945   List.iter (
3946     fun (name, _, _, flags, _, _, _) ->
3947       let name2 = replace_char name '_' '-' in
3948       let alias =
3949         try find_map (function FishAlias n -> Some n | _ -> None) flags
3950         with Not_found -> name in
3951       pr "  if (";
3952       pr "strcasecmp (cmd, \"%s\") == 0" name;
3953       if name <> name2 then
3954         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
3955       if name <> alias then
3956         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
3957       pr ")\n";
3958       pr "    return run_%s (cmd, argc, argv);\n" name;
3959       pr "  else\n";
3960   ) all_functions;
3961   pr "    {\n";
3962   pr "      fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
3963   pr "      return -1;\n";
3964   pr "    }\n";
3965   pr "  return 0;\n";
3966   pr "}\n";
3967   pr "\n"
3968
3969 (* Readline completion for guestfish. *)
3970 and generate_fish_completion () =
3971   generate_header CStyle GPLv2;
3972
3973   let all_functions =
3974     List.filter (
3975       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3976     ) all_functions in
3977
3978   pr "\
3979 #include <config.h>
3980
3981 #include <stdio.h>
3982 #include <stdlib.h>
3983 #include <string.h>
3984
3985 #ifdef HAVE_LIBREADLINE
3986 #include <readline/readline.h>
3987 #endif
3988
3989 #include \"fish.h\"
3990
3991 #ifdef HAVE_LIBREADLINE
3992
3993 static const char *const commands[] = {
3994 ";
3995
3996   (* Get the commands and sort them, including the aliases. *)
3997   let commands =
3998     List.map (
3999       fun (name, _, _, flags, _, _, _) ->
4000         let name2 = replace_char name '_' '-' in
4001         let alias =
4002           try find_map (function FishAlias n -> Some n | _ -> None) flags
4003           with Not_found -> name in
4004
4005         if name <> alias then [name2; alias] else [name2]
4006     ) all_functions in
4007   let commands = List.flatten commands in
4008   let commands = List.sort compare commands in
4009
4010   List.iter (pr "  \"%s\",\n") commands;
4011
4012   pr "  NULL
4013 };
4014
4015 static char *
4016 generator (const char *text, int state)
4017 {
4018   static int index, len;
4019   const char *name;
4020
4021   if (!state) {
4022     index = 0;
4023     len = strlen (text);
4024   }
4025
4026   while ((name = commands[index]) != NULL) {
4027     index++;
4028     if (strncasecmp (name, text, len) == 0)
4029       return strdup (name);
4030   }
4031
4032   return NULL;
4033 }
4034
4035 #endif /* HAVE_LIBREADLINE */
4036
4037 char **do_completion (const char *text, int start, int end)
4038 {
4039   char **matches = NULL;
4040
4041 #ifdef HAVE_LIBREADLINE
4042   if (start == 0)
4043     matches = rl_completion_matches (text, generator);
4044 #endif
4045
4046   return matches;
4047 }
4048 ";
4049
4050 (* Generate the POD documentation for guestfish. *)
4051 and generate_fish_actions_pod () =
4052   let all_functions_sorted =
4053     List.filter (
4054       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4055     ) all_functions_sorted in
4056
4057   let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
4058
4059   List.iter (
4060     fun (name, style, _, flags, _, _, longdesc) ->
4061       let longdesc =
4062         Str.global_substitute rex (
4063           fun s ->
4064             let sub =
4065               try Str.matched_group 1 s
4066               with Not_found ->
4067                 failwithf "error substituting C<guestfs_...> in longdesc of function %s" name in
4068             "C<" ^ replace_char sub '_' '-' ^ ">"
4069         ) longdesc in
4070       let name = replace_char name '_' '-' in
4071       let alias =
4072         try find_map (function FishAlias n -> Some n | _ -> None) flags
4073         with Not_found -> name in
4074
4075       pr "=head2 %s" name;
4076       if name <> alias then
4077         pr " | %s" alias;
4078       pr "\n";
4079       pr "\n";
4080       pr " %s" name;
4081       List.iter (
4082         function
4083         | String n -> pr " %s" n
4084         | OptString n -> pr " %s" n
4085         | StringList n -> pr " '%s ...'" n
4086         | Bool _ -> pr " true|false"
4087         | Int n -> pr " %s" n
4088         | FileIn n | FileOut n -> pr " (%s|-)" n
4089       ) (snd style);
4090       pr "\n";
4091       pr "\n";
4092       pr "%s\n\n" longdesc;
4093
4094       if List.exists (function FileIn _ | FileOut _ -> true
4095                       | _ -> false) (snd style) then
4096         pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
4097
4098       if List.mem ProtocolLimitWarning flags then
4099         pr "%s\n\n" protocol_limit_warning;
4100
4101       if List.mem DangerWillRobinson flags then
4102         pr "%s\n\n" danger_will_robinson
4103   ) all_functions_sorted
4104
4105 (* Generate a C function prototype. *)
4106 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
4107     ?(single_line = false) ?(newline = false) ?(in_daemon = false)
4108     ?(prefix = "")
4109     ?handle name style =
4110   if extern then pr "extern ";
4111   if static then pr "static ";
4112   (match fst style with
4113    | RErr -> pr "int "
4114    | RInt _ -> pr "int "
4115    | RInt64 _ -> pr "int64_t "
4116    | RBool _ -> pr "int "
4117    | RConstString _ -> pr "const char *"
4118    | RString _ -> pr "char *"
4119    | RStringList _ | RHashtable _ -> pr "char **"
4120    | RIntBool _ ->
4121        if not in_daemon then pr "struct guestfs_int_bool *"
4122        else pr "guestfs_%s_ret *" name
4123    | RPVList _ ->
4124        if not in_daemon then pr "struct guestfs_lvm_pv_list *"
4125        else pr "guestfs_lvm_int_pv_list *"
4126    | RVGList _ ->
4127        if not in_daemon then pr "struct guestfs_lvm_vg_list *"
4128        else pr "guestfs_lvm_int_vg_list *"
4129    | RLVList _ ->
4130        if not in_daemon then pr "struct guestfs_lvm_lv_list *"
4131        else pr "guestfs_lvm_int_lv_list *"
4132    | RStat _ ->
4133        if not in_daemon then pr "struct guestfs_stat *"
4134        else pr "guestfs_int_stat *"
4135    | RStatVFS _ ->
4136        if not in_daemon then pr "struct guestfs_statvfs *"
4137        else pr "guestfs_int_statvfs *"
4138   );
4139   pr "%s%s (" prefix name;
4140   if handle = None && List.length (snd style) = 0 then
4141     pr "void"
4142   else (
4143     let comma = ref false in
4144     (match handle with
4145      | None -> ()
4146      | Some handle -> pr "guestfs_h *%s" handle; comma := true
4147     );
4148     let next () =
4149       if !comma then (
4150         if single_line then pr ", " else pr ",\n\t\t"
4151       );
4152       comma := true
4153     in
4154     List.iter (
4155       function
4156       | String n
4157       | OptString n -> next (); pr "const char *%s" n
4158       | StringList n -> next (); pr "char * const* const %s" n
4159       | Bool n -> next (); pr "int %s" n
4160       | Int n -> next (); pr "int %s" n
4161       | FileIn n
4162       | FileOut n ->
4163           if not in_daemon then (next (); pr "const char *%s" n)
4164     ) (snd style);
4165   );
4166   pr ")";
4167   if semicolon then pr ";";
4168   if newline then pr "\n"
4169
4170 (* Generate C call arguments, eg "(handle, foo, bar)" *)
4171 and generate_call_args ?handle args =
4172   pr "(";
4173   let comma = ref false in
4174   (match handle with
4175    | None -> ()
4176    | Some handle -> pr "%s" handle; comma := true
4177   );
4178   List.iter (
4179     fun arg ->
4180       if !comma then pr ", ";
4181       comma := true;
4182       pr "%s" (name_of_argt arg)
4183   ) args;
4184   pr ")"
4185
4186 (* Generate the OCaml bindings interface. *)
4187 and generate_ocaml_mli () =
4188   generate_header OCamlStyle LGPLv2;
4189
4190   pr "\
4191 (** For API documentation you should refer to the C API
4192     in the guestfs(3) manual page.  The OCaml API uses almost
4193     exactly the same calls. *)
4194
4195 type t
4196 (** A [guestfs_h] handle. *)
4197
4198 exception Error of string
4199 (** This exception is raised when there is an error. *)
4200
4201 val create : unit -> t
4202
4203 val close : t -> unit
4204 (** Handles are closed by the garbage collector when they become
4205     unreferenced, but callers can also call this in order to
4206     provide predictable cleanup. *)
4207
4208 ";
4209   generate_ocaml_lvm_structure_decls ();
4210
4211   generate_ocaml_stat_structure_decls ();
4212
4213   (* The actions. *)
4214   List.iter (
4215     fun (name, style, _, _, _, shortdesc, _) ->
4216       generate_ocaml_prototype name style;
4217       pr "(** %s *)\n" shortdesc;
4218       pr "\n"
4219   ) all_functions
4220
4221 (* Generate the OCaml bindings implementation. *)
4222 and generate_ocaml_ml () =
4223   generate_header OCamlStyle LGPLv2;
4224
4225   pr "\
4226 type t
4227 exception Error of string
4228 external create : unit -> t = \"ocaml_guestfs_create\"
4229 external close : t -> unit = \"ocaml_guestfs_close\"
4230
4231 let () =
4232   Callback.register_exception \"ocaml_guestfs_error\" (Error \"\")
4233
4234 ";
4235
4236   generate_ocaml_lvm_structure_decls ();
4237
4238   generate_ocaml_stat_structure_decls ();
4239
4240   (* The actions. *)
4241   List.iter (
4242     fun (name, style, _, _, _, shortdesc, _) ->
4243       generate_ocaml_prototype ~is_external:true name style;
4244   ) all_functions
4245
4246 (* Generate the OCaml bindings C implementation. *)
4247 and generate_ocaml_c () =
4248   generate_header CStyle LGPLv2;
4249
4250   pr "\
4251 #include <stdio.h>
4252 #include <stdlib.h>
4253 #include <string.h>
4254
4255 #include <caml/config.h>
4256 #include <caml/alloc.h>
4257 #include <caml/callback.h>
4258 #include <caml/fail.h>
4259 #include <caml/memory.h>
4260 #include <caml/mlvalues.h>
4261 #include <caml/signals.h>
4262
4263 #include <guestfs.h>
4264
4265 #include \"guestfs_c.h\"
4266
4267 /* Copy a hashtable of string pairs into an assoc-list.  We return
4268  * the list in reverse order, but hashtables aren't supposed to be
4269  * ordered anyway.
4270  */
4271 static CAMLprim value
4272 copy_table (char * const * argv)
4273 {
4274   CAMLparam0 ();
4275   CAMLlocal5 (rv, pairv, kv, vv, cons);
4276   int i;
4277
4278   rv = Val_int (0);
4279   for (i = 0; argv[i] != NULL; i += 2) {
4280     kv = caml_copy_string (argv[i]);
4281     vv = caml_copy_string (argv[i+1]);
4282     pairv = caml_alloc (2, 0);
4283     Store_field (pairv, 0, kv);
4284     Store_field (pairv, 1, vv);
4285     cons = caml_alloc (2, 0);
4286     Store_field (cons, 1, rv);
4287     rv = cons;
4288     Store_field (cons, 0, pairv);
4289   }
4290
4291   CAMLreturn (rv);
4292 }
4293
4294 ";
4295
4296   (* LVM struct copy functions. *)
4297   List.iter (
4298     fun (typ, cols) ->
4299       let has_optpercent_col =
4300         List.exists (function (_, `OptPercent) -> true | _ -> false) cols in
4301
4302       pr "static CAMLprim value\n";
4303       pr "copy_lvm_%s (const struct guestfs_lvm_%s *%s)\n" typ typ typ;
4304       pr "{\n";
4305       pr "  CAMLparam0 ();\n";
4306       if has_optpercent_col then
4307         pr "  CAMLlocal3 (rv, v, v2);\n"
4308       else
4309         pr "  CAMLlocal2 (rv, v);\n";
4310       pr "\n";
4311       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
4312       iteri (
4313         fun i col ->
4314           (match col with
4315            | name, `String ->
4316                pr "  v = caml_copy_string (%s->%s);\n" typ name
4317            | name, `UUID ->
4318                pr "  v = caml_alloc_string (32);\n";
4319                pr "  memcpy (String_val (v), %s->%s, 32);\n" typ name
4320            | name, `Bytes
4321            | name, `Int ->
4322                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
4323            | name, `OptPercent ->
4324                pr "  if (%s->%s >= 0) { /* Some %s */\n" typ name name;
4325                pr "    v2 = caml_copy_double (%s->%s);\n" typ name;
4326                pr "    v = caml_alloc (1, 0);\n";
4327                pr "    Store_field (v, 0, v2);\n";
4328                pr "  } else /* None */\n";
4329                pr "    v = Val_int (0);\n";
4330           );
4331           pr "  Store_field (rv, %d, v);\n" i
4332       ) cols;
4333       pr "  CAMLreturn (rv);\n";
4334       pr "}\n";
4335       pr "\n";
4336
4337       pr "static CAMLprim value\n";
4338       pr "copy_lvm_%s_list (const struct guestfs_lvm_%s_list *%ss)\n"
4339         typ typ typ;
4340       pr "{\n";
4341       pr "  CAMLparam0 ();\n";
4342       pr "  CAMLlocal2 (rv, v);\n";
4343       pr "  int i;\n";
4344       pr "\n";
4345       pr "  if (%ss->len == 0)\n" typ;
4346       pr "    CAMLreturn (Atom (0));\n";
4347       pr "  else {\n";
4348       pr "    rv = caml_alloc (%ss->len, 0);\n" typ;
4349       pr "    for (i = 0; i < %ss->len; ++i) {\n" typ;
4350       pr "      v = copy_lvm_%s (&%ss->val[i]);\n" typ typ;
4351       pr "      caml_modify (&Field (rv, i), v);\n";
4352       pr "    }\n";
4353       pr "    CAMLreturn (rv);\n";
4354       pr "  }\n";
4355       pr "}\n";
4356       pr "\n";
4357   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
4358
4359   (* Stat copy functions. *)
4360   List.iter (
4361     fun (typ, cols) ->
4362       pr "static CAMLprim value\n";
4363       pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ;
4364       pr "{\n";
4365       pr "  CAMLparam0 ();\n";
4366       pr "  CAMLlocal2 (rv, v);\n";
4367       pr "\n";
4368       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
4369       iteri (
4370         fun i col ->
4371           (match col with
4372            | name, `Int ->
4373                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
4374           );
4375           pr "  Store_field (rv, %d, v);\n" i
4376       ) cols;
4377       pr "  CAMLreturn (rv);\n";
4378       pr "}\n";
4379       pr "\n";
4380   ) ["stat", stat_cols; "statvfs", statvfs_cols];
4381
4382   (* The wrappers. *)
4383   List.iter (
4384     fun (name, style, _, _, _, _, _) ->
4385       let params =
4386         "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
4387
4388       pr "CAMLprim value\n";
4389       pr "ocaml_guestfs_%s (value %s" name (List.hd params);
4390       List.iter (pr ", value %s") (List.tl params);
4391       pr ")\n";
4392       pr "{\n";
4393
4394       (match params with
4395        | [p1; p2; p3; p4; p5] ->
4396            pr "  CAMLparam5 (%s);\n" (String.concat ", " params)
4397        | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
4398            pr "  CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
4399            pr "  CAMLxparam%d (%s);\n"
4400              (List.length rest) (String.concat ", " rest)
4401        | ps ->
4402            pr "  CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
4403       );
4404       pr "  CAMLlocal1 (rv);\n";
4405       pr "\n";
4406
4407       pr "  guestfs_h *g = Guestfs_val (gv);\n";
4408       pr "  if (g == NULL)\n";
4409       pr "    caml_failwith (\"%s: used handle after closing it\");\n" name;
4410       pr "\n";
4411
4412       List.iter (
4413         function
4414         | String n
4415         | FileIn n
4416         | FileOut n ->
4417             pr "  const char *%s = String_val (%sv);\n" n n
4418         | OptString n ->
4419             pr "  const char *%s =\n" n;
4420             pr "    %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n"
4421               n n
4422         | StringList n ->
4423             pr "  char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n
4424         | Bool n ->
4425             pr "  int %s = Bool_val (%sv);\n" n n
4426         | Int n ->
4427             pr "  int %s = Int_val (%sv);\n" n n
4428       ) (snd style);
4429       let error_code =
4430         match fst style with
4431         | RErr -> pr "  int r;\n"; "-1"
4432         | RInt _ -> pr "  int r;\n"; "-1"
4433         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
4434         | RBool _ -> pr "  int r;\n"; "-1"
4435         | RConstString _ -> pr "  const char *r;\n"; "NULL"
4436         | RString _ -> pr "  char *r;\n"; "NULL"
4437         | RStringList _ ->
4438             pr "  int i;\n";
4439             pr "  char **r;\n";
4440             "NULL"
4441         | RIntBool _ ->
4442             pr "  struct guestfs_int_bool *r;\n"; "NULL"
4443         | RPVList _ ->
4444             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
4445         | RVGList _ ->
4446             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
4447         | RLVList _ ->
4448             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
4449         | RStat _ ->
4450             pr "  struct guestfs_stat *r;\n"; "NULL"
4451         | RStatVFS _ ->
4452             pr "  struct guestfs_statvfs *r;\n"; "NULL"
4453         | RHashtable _ ->
4454             pr "  int i;\n";
4455             pr "  char **r;\n";
4456             "NULL" in
4457       pr "\n";
4458
4459       pr "  caml_enter_blocking_section ();\n";
4460       pr "  r = guestfs_%s " name;
4461       generate_call_args ~handle:"g" (snd style);
4462       pr ";\n";
4463       pr "  caml_leave_blocking_section ();\n";
4464
4465       List.iter (
4466         function
4467         | StringList n ->
4468             pr "  ocaml_guestfs_free_strings (%s);\n" n;
4469         | String _ | OptString _ | Bool _ | Int _ | FileIn _ | FileOut _ -> ()
4470       ) (snd style);
4471
4472       pr "  if (r == %s)\n" error_code;
4473       pr "    ocaml_guestfs_raise_error (g, \"%s\");\n" name;
4474       pr "\n";
4475
4476       (match fst style with
4477        | RErr -> pr "  rv = Val_unit;\n"
4478        | RInt _ -> pr "  rv = Val_int (r);\n"
4479        | RInt64 _ ->
4480            pr "  rv = caml_copy_int64 (r);\n"
4481        | RBool _ -> pr "  rv = Val_bool (r);\n"
4482        | RConstString _ -> pr "  rv = caml_copy_string (r);\n"
4483        | RString _ ->
4484            pr "  rv = caml_copy_string (r);\n";
4485            pr "  free (r);\n"
4486        | RStringList _ ->
4487            pr "  rv = caml_copy_string_array ((const char **) r);\n";
4488            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
4489            pr "  free (r);\n"
4490        | RIntBool _ ->
4491            pr "  rv = caml_alloc (2, 0);\n";
4492            pr "  Store_field (rv, 0, Val_int (r->i));\n";
4493            pr "  Store_field (rv, 1, Val_bool (r->b));\n";
4494            pr "  guestfs_free_int_bool (r);\n";
4495        | RPVList _ ->
4496            pr "  rv = copy_lvm_pv_list (r);\n";
4497            pr "  guestfs_free_lvm_pv_list (r);\n";
4498        | RVGList _ ->
4499            pr "  rv = copy_lvm_vg_list (r);\n";
4500            pr "  guestfs_free_lvm_vg_list (r);\n";
4501        | RLVList _ ->
4502            pr "  rv = copy_lvm_lv_list (r);\n";
4503            pr "  guestfs_free_lvm_lv_list (r);\n";
4504        | RStat _ ->
4505            pr "  rv = copy_stat (r);\n";
4506            pr "  free (r);\n";
4507        | RStatVFS _ ->
4508            pr "  rv = copy_statvfs (r);\n";
4509            pr "  free (r);\n";
4510        | RHashtable _ ->
4511            pr "  rv = copy_table (r);\n";
4512            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
4513            pr "  free (r);\n";
4514       );
4515
4516       pr "  CAMLreturn (rv);\n";
4517       pr "}\n";
4518       pr "\n";
4519
4520       if List.length params > 5 then (
4521         pr "CAMLprim value\n";
4522         pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
4523         pr "{\n";
4524         pr "  return ocaml_guestfs_%s (argv[0]" name;
4525         iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
4526         pr ");\n";
4527         pr "}\n";
4528         pr "\n"
4529       )
4530   ) all_functions
4531
4532 and generate_ocaml_lvm_structure_decls () =
4533   List.iter (
4534     fun (typ, cols) ->
4535       pr "type lvm_%s = {\n" typ;
4536       List.iter (
4537         function
4538         | name, `String -> pr "  %s : string;\n" name
4539         | name, `UUID -> pr "  %s : string;\n" name
4540         | name, `Bytes -> pr "  %s : int64;\n" name
4541         | name, `Int -> pr "  %s : int64;\n" name
4542         | name, `OptPercent -> pr "  %s : float option;\n" name
4543       ) cols;
4544       pr "}\n";
4545       pr "\n"
4546   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
4547
4548 and generate_ocaml_stat_structure_decls () =
4549   List.iter (
4550     fun (typ, cols) ->
4551       pr "type %s = {\n" typ;
4552       List.iter (
4553         function
4554         | name, `Int -> pr "  %s : int64;\n" name
4555       ) cols;
4556       pr "}\n";
4557       pr "\n"
4558   ) ["stat", stat_cols; "statvfs", statvfs_cols]
4559
4560 and generate_ocaml_prototype ?(is_external = false) name style =
4561   if is_external then pr "external " else pr "val ";
4562   pr "%s : t -> " name;
4563   List.iter (
4564     function
4565     | String _ | FileIn _ | FileOut _ -> pr "string -> "
4566     | OptString _ -> pr "string option -> "
4567     | StringList _ -> pr "string array -> "
4568     | Bool _ -> pr "bool -> "
4569     | Int _ -> pr "int -> "
4570   ) (snd style);
4571   (match fst style with
4572    | RErr -> pr "unit" (* all errors are turned into exceptions *)
4573    | RInt _ -> pr "int"
4574    | RInt64 _ -> pr "int64"
4575    | RBool _ -> pr "bool"
4576    | RConstString _ -> pr "string"
4577    | RString _ -> pr "string"
4578    | RStringList _ -> pr "string array"
4579    | RIntBool _ -> pr "int * bool"
4580    | RPVList _ -> pr "lvm_pv array"
4581    | RVGList _ -> pr "lvm_vg array"
4582    | RLVList _ -> pr "lvm_lv array"
4583    | RStat _ -> pr "stat"
4584    | RStatVFS _ -> pr "statvfs"
4585    | RHashtable _ -> pr "(string * string) list"
4586   );
4587   if is_external then (
4588     pr " = ";
4589     if List.length (snd style) + 1 > 5 then
4590       pr "\"ocaml_guestfs_%s_byte\" " name;
4591     pr "\"ocaml_guestfs_%s\"" name
4592   );
4593   pr "\n"
4594
4595 (* Generate Perl xs code, a sort of crazy variation of C with macros. *)
4596 and generate_perl_xs () =
4597   generate_header CStyle LGPLv2;
4598
4599   pr "\
4600 #include \"EXTERN.h\"
4601 #include \"perl.h\"
4602 #include \"XSUB.h\"
4603
4604 #include <guestfs.h>
4605
4606 #ifndef PRId64
4607 #define PRId64 \"lld\"
4608 #endif
4609
4610 static SV *
4611 my_newSVll(long long val) {
4612 #ifdef USE_64_BIT_ALL
4613   return newSViv(val);
4614 #else
4615   char buf[100];
4616   int len;
4617   len = snprintf(buf, 100, \"%%\" PRId64, val);
4618   return newSVpv(buf, len);
4619 #endif
4620 }
4621
4622 #ifndef PRIu64
4623 #define PRIu64 \"llu\"
4624 #endif
4625
4626 static SV *
4627 my_newSVull(unsigned long long val) {
4628 #ifdef USE_64_BIT_ALL
4629   return newSVuv(val);
4630 #else
4631   char buf[100];
4632   int len;
4633   len = snprintf(buf, 100, \"%%\" PRIu64, val);
4634   return newSVpv(buf, len);
4635 #endif
4636 }
4637
4638 /* http://www.perlmonks.org/?node_id=680842 */
4639 static char **
4640 XS_unpack_charPtrPtr (SV *arg) {
4641   char **ret;
4642   AV *av;
4643   I32 i;
4644
4645   if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV)
4646     croak (\"array reference expected\");
4647
4648   av = (AV *)SvRV (arg);
4649   ret = malloc (av_len (av) + 1 + 1);
4650   if (!ret)
4651     croak (\"malloc failed\");
4652
4653   for (i = 0; i <= av_len (av); i++) {
4654     SV **elem = av_fetch (av, i, 0);
4655
4656     if (!elem || !*elem)
4657       croak (\"missing element in list\");
4658
4659     ret[i] = SvPV_nolen (*elem);
4660   }
4661
4662   ret[i] = NULL;
4663
4664   return ret;
4665 }
4666
4667 MODULE = Sys::Guestfs  PACKAGE = Sys::Guestfs
4668
4669 guestfs_h *
4670 _create ()
4671    CODE:
4672       RETVAL = guestfs_create ();
4673       if (!RETVAL)
4674         croak (\"could not create guestfs handle\");
4675       guestfs_set_error_handler (RETVAL, NULL, NULL);
4676  OUTPUT:
4677       RETVAL
4678
4679 void
4680 DESTROY (g)
4681       guestfs_h *g;
4682  PPCODE:
4683       guestfs_close (g);
4684
4685 ";
4686
4687   List.iter (
4688     fun (name, style, _, _, _, _, _) ->
4689       (match fst style with
4690        | RErr -> pr "void\n"
4691        | RInt _ -> pr "SV *\n"
4692        | RInt64 _ -> pr "SV *\n"
4693        | RBool _ -> pr "SV *\n"
4694        | RConstString _ -> pr "SV *\n"
4695        | RString _ -> pr "SV *\n"
4696        | RStringList _
4697        | RIntBool _
4698        | RPVList _ | RVGList _ | RLVList _
4699        | RStat _ | RStatVFS _
4700        | RHashtable _ ->
4701            pr "void\n" (* all lists returned implictly on the stack *)
4702       );
4703       (* Call and arguments. *)
4704       pr "%s " name;
4705       generate_call_args ~handle:"g" (snd style);
4706       pr "\n";
4707       pr "      guestfs_h *g;\n";
4708       List.iter (
4709         function
4710         | String n | FileIn n | FileOut n -> pr "      char *%s;\n" n
4711         | OptString n -> pr "      char *%s;\n" n
4712         | StringList n -> pr "      char **%s;\n" n
4713         | Bool n -> pr "      int %s;\n" n
4714         | Int n -> pr "      int %s;\n" n
4715       ) (snd style);
4716
4717       let do_cleanups () =
4718         List.iter (
4719           function
4720           | String _ | OptString _ | Bool _ | Int _
4721           | FileIn _ | FileOut _ -> ()
4722           | StringList n -> pr "      free (%s);\n" n
4723         ) (snd style)
4724       in
4725
4726       (* Code. *)
4727       (match fst style with
4728        | RErr ->
4729            pr "PREINIT:\n";
4730            pr "      int r;\n";
4731            pr " PPCODE:\n";
4732            pr "      r = guestfs_%s " name;
4733            generate_call_args ~handle:"g" (snd style);
4734            pr ";\n";
4735            do_cleanups ();
4736            pr "      if (r == -1)\n";
4737            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4738        | RInt n
4739        | RBool n ->
4740            pr "PREINIT:\n";
4741            pr "      int %s;\n" n;
4742            pr "   CODE:\n";
4743            pr "      %s = guestfs_%s " n name;
4744            generate_call_args ~handle:"g" (snd style);
4745            pr ";\n";
4746            do_cleanups ();
4747            pr "      if (%s == -1)\n" n;
4748            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4749            pr "      RETVAL = newSViv (%s);\n" n;
4750            pr " OUTPUT:\n";
4751            pr "      RETVAL\n"
4752        | RInt64 n ->
4753            pr "PREINIT:\n";
4754            pr "      int64_t %s;\n" n;
4755            pr "   CODE:\n";
4756            pr "      %s = guestfs_%s " n name;
4757            generate_call_args ~handle:"g" (snd style);
4758            pr ";\n";
4759            do_cleanups ();
4760            pr "      if (%s == -1)\n" n;
4761            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4762            pr "      RETVAL = my_newSVll (%s);\n" n;
4763            pr " OUTPUT:\n";
4764            pr "      RETVAL\n"
4765        | RConstString n ->
4766            pr "PREINIT:\n";
4767            pr "      const char *%s;\n" n;
4768            pr "   CODE:\n";
4769            pr "      %s = guestfs_%s " n name;
4770            generate_call_args ~handle:"g" (snd style);
4771            pr ";\n";
4772            do_cleanups ();
4773            pr "      if (%s == NULL)\n" n;
4774            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4775            pr "      RETVAL = newSVpv (%s, 0);\n" n;
4776            pr " OUTPUT:\n";
4777            pr "      RETVAL\n"
4778        | RString n ->
4779            pr "PREINIT:\n";
4780            pr "      char *%s;\n" n;
4781            pr "   CODE:\n";
4782            pr "      %s = guestfs_%s " n name;
4783            generate_call_args ~handle:"g" (snd style);
4784            pr ";\n";
4785            do_cleanups ();
4786            pr "      if (%s == NULL)\n" n;
4787            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4788            pr "      RETVAL = newSVpv (%s, 0);\n" n;
4789            pr "      free (%s);\n" n;
4790            pr " OUTPUT:\n";
4791            pr "      RETVAL\n"
4792        | RStringList n | RHashtable n ->
4793            pr "PREINIT:\n";
4794            pr "      char **%s;\n" n;
4795            pr "      int i, n;\n";
4796            pr " PPCODE:\n";
4797            pr "      %s = guestfs_%s " n name;
4798            generate_call_args ~handle:"g" (snd style);
4799            pr ";\n";
4800            do_cleanups ();
4801            pr "      if (%s == NULL)\n" n;
4802            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4803            pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
4804            pr "      EXTEND (SP, n);\n";
4805            pr "      for (i = 0; i < n; ++i) {\n";
4806            pr "        PUSHs (sv_2mortal (newSVpv (%s[i], 0)));\n" n;
4807            pr "        free (%s[i]);\n" n;
4808            pr "      }\n";
4809            pr "      free (%s);\n" n;
4810        | RIntBool _ ->
4811            pr "PREINIT:\n";
4812            pr "      struct guestfs_int_bool *r;\n";
4813            pr " PPCODE:\n";
4814            pr "      r = guestfs_%s " name;
4815            generate_call_args ~handle:"g" (snd style);
4816            pr ";\n";
4817            do_cleanups ();
4818            pr "      if (r == NULL)\n";
4819            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4820            pr "      EXTEND (SP, 2);\n";
4821            pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
4822            pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
4823            pr "      guestfs_free_int_bool (r);\n";
4824        | RPVList n ->
4825            generate_perl_lvm_code "pv" pv_cols name style n do_cleanups
4826        | RVGList n ->
4827            generate_perl_lvm_code "vg" vg_cols name style n do_cleanups
4828        | RLVList n ->
4829            generate_perl_lvm_code "lv" lv_cols name style n do_cleanups
4830        | RStat n ->
4831            generate_perl_stat_code "stat" stat_cols name style n do_cleanups
4832        | RStatVFS n ->
4833            generate_perl_stat_code
4834              "statvfs" statvfs_cols name style n do_cleanups
4835       );
4836
4837       pr "\n"
4838   ) all_functions
4839
4840 and generate_perl_lvm_code typ cols name style n do_cleanups =
4841   pr "PREINIT:\n";
4842   pr "      struct guestfs_lvm_%s_list *%s;\n" typ n;
4843   pr "      int i;\n";
4844   pr "      HV *hv;\n";
4845   pr " PPCODE:\n";
4846   pr "      %s = guestfs_%s " n name;
4847   generate_call_args ~handle:"g" (snd style);
4848   pr ";\n";
4849   do_cleanups ();
4850   pr "      if (%s == NULL)\n" n;
4851   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4852   pr "      EXTEND (SP, %s->len);\n" n;
4853   pr "      for (i = 0; i < %s->len; ++i) {\n" n;
4854   pr "        hv = newHV ();\n";
4855   List.iter (
4856     function
4857     | name, `String ->
4858         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 0), 0);\n"
4859           name (String.length name) n name
4860     | name, `UUID ->
4861         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 32), 0);\n"
4862           name (String.length name) n name
4863     | name, `Bytes ->
4864         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n"
4865           name (String.length name) n name
4866     | name, `Int ->
4867         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVll (%s->val[i].%s), 0);\n"
4868           name (String.length name) n name
4869     | name, `OptPercent ->
4870         pr "        (void) hv_store (hv, \"%s\", %d, newSVnv (%s->val[i].%s), 0);\n"
4871           name (String.length name) n name
4872   ) cols;
4873   pr "        PUSHs (sv_2mortal ((SV *) hv));\n";
4874   pr "      }\n";
4875   pr "      guestfs_free_lvm_%s_list (%s);\n" typ n
4876
4877 and generate_perl_stat_code typ cols name style n do_cleanups =
4878   pr "PREINIT:\n";
4879   pr "      struct guestfs_%s *%s;\n" typ n;
4880   pr " PPCODE:\n";
4881   pr "      %s = guestfs_%s " n name;
4882   generate_call_args ~handle:"g" (snd style);
4883   pr ";\n";
4884   do_cleanups ();
4885   pr "      if (%s == NULL)\n" n;
4886   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
4887   pr "      EXTEND (SP, %d);\n" (List.length cols);
4888   List.iter (
4889     function
4890     | name, `Int ->
4891         pr "      PUSHs (sv_2mortal (my_newSVll (%s->%s)));\n" n name
4892   ) cols;
4893   pr "      free (%s);\n" n
4894
4895 (* Generate Sys/Guestfs.pm. *)
4896 and generate_perl_pm () =
4897   generate_header HashStyle LGPLv2;
4898
4899   pr "\
4900 =pod
4901
4902 =head1 NAME
4903
4904 Sys::Guestfs - Perl bindings for libguestfs
4905
4906 =head1 SYNOPSIS
4907
4908  use Sys::Guestfs;
4909  
4910  my $h = Sys::Guestfs->new ();
4911  $h->add_drive ('guest.img');
4912  $h->launch ();
4913  $h->wait_ready ();
4914  $h->mount ('/dev/sda1', '/');
4915  $h->touch ('/hello');
4916  $h->sync ();
4917
4918 =head1 DESCRIPTION
4919
4920 The C<Sys::Guestfs> module provides a Perl XS binding to the
4921 libguestfs API for examining and modifying virtual machine
4922 disk images.
4923
4924 Amongst the things this is good for: making batch configuration
4925 changes to guests, getting disk used/free statistics (see also:
4926 virt-df), migrating between virtualization systems (see also:
4927 virt-p2v), performing partial backups, performing partial guest
4928 clones, cloning guests and changing registry/UUID/hostname info, and
4929 much else besides.
4930
4931 Libguestfs uses Linux kernel and qemu code, and can access any type of
4932 guest filesystem that Linux and qemu can, including but not limited
4933 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
4934 schemes, qcow, qcow2, vmdk.
4935
4936 Libguestfs provides ways to enumerate guest storage (eg. partitions,
4937 LVs, what filesystem is in each LV, etc.).  It can also run commands
4938 in the context of the guest.  Also you can access filesystems over FTP.
4939
4940 =head1 ERRORS
4941
4942 All errors turn into calls to C<croak> (see L<Carp(3)>).
4943
4944 =head1 METHODS
4945
4946 =over 4
4947
4948 =cut
4949
4950 package Sys::Guestfs;
4951
4952 use strict;
4953 use warnings;
4954
4955 require XSLoader;
4956 XSLoader::load ('Sys::Guestfs');
4957
4958 =item $h = Sys::Guestfs->new ();
4959
4960 Create a new guestfs handle.
4961
4962 =cut
4963
4964 sub new {
4965   my $proto = shift;
4966   my $class = ref ($proto) || $proto;
4967
4968   my $self = Sys::Guestfs::_create ();
4969   bless $self, $class;
4970   return $self;
4971 }
4972
4973 ";
4974
4975   (* Actions.  We only need to print documentation for these as
4976    * they are pulled in from the XS code automatically.
4977    *)
4978   List.iter (
4979     fun (name, style, _, flags, _, _, longdesc) ->
4980       let longdesc = replace_str longdesc "C<guestfs_" "C<$h-E<gt>" in
4981       pr "=item ";
4982       generate_perl_prototype name style;
4983       pr "\n\n";
4984       pr "%s\n\n" longdesc;
4985       if List.mem ProtocolLimitWarning flags then
4986         pr "%s\n\n" protocol_limit_warning;
4987       if List.mem DangerWillRobinson flags then
4988         pr "%s\n\n" danger_will_robinson
4989   ) all_functions_sorted;
4990
4991   (* End of file. *)
4992   pr "\
4993 =cut
4994
4995 1;
4996
4997 =back
4998
4999 =head1 COPYRIGHT
5000
5001 Copyright (C) 2009 Red Hat Inc.
5002
5003 =head1 LICENSE
5004
5005 Please see the file COPYING.LIB for the full license.
5006
5007 =head1 SEE ALSO
5008
5009 L<guestfs(3)>, L<guestfish(1)>.
5010
5011 =cut
5012 "
5013
5014 and generate_perl_prototype name style =
5015   (match fst style with
5016    | RErr -> ()
5017    | RBool n
5018    | RInt n
5019    | RInt64 n
5020    | RConstString n
5021    | RString n -> pr "$%s = " n
5022    | RIntBool (n, m) -> pr "($%s, $%s) = " n m
5023    | RStringList n
5024    | RPVList n
5025    | RVGList n
5026    | RLVList n -> pr "@%s = " n
5027    | RStat n
5028    | RStatVFS n
5029    | RHashtable n -> pr "%%%s = " n
5030   );
5031   pr "$h->%s (" name;
5032   let comma = ref false in
5033   List.iter (
5034     fun arg ->
5035       if !comma then pr ", ";
5036       comma := true;
5037       match arg with
5038       | String n | OptString n | Bool n | Int n | FileIn n | FileOut n ->
5039           pr "$%s" n
5040       | StringList n ->
5041           pr "\\@%s" n
5042   ) (snd style);
5043   pr ");"
5044
5045 (* Generate Python C module. *)
5046 and generate_python_c () =
5047   generate_header CStyle LGPLv2;
5048
5049   pr "\
5050 #include <stdio.h>
5051 #include <stdlib.h>
5052 #include <assert.h>
5053
5054 #include <Python.h>
5055
5056 #include \"guestfs.h\"
5057
5058 typedef struct {
5059   PyObject_HEAD
5060   guestfs_h *g;
5061 } Pyguestfs_Object;
5062
5063 static guestfs_h *
5064 get_handle (PyObject *obj)
5065 {
5066   assert (obj);
5067   assert (obj != Py_None);
5068   return ((Pyguestfs_Object *) obj)->g;
5069 }
5070
5071 static PyObject *
5072 put_handle (guestfs_h *g)
5073 {
5074   assert (g);
5075   return
5076     PyCObject_FromVoidPtrAndDesc ((void *) g, (char *) \"guestfs_h\", NULL);
5077 }
5078
5079 /* This list should be freed (but not the strings) after use. */
5080 static const char **
5081 get_string_list (PyObject *obj)
5082 {
5083   int i, len;
5084   const char **r;
5085
5086   assert (obj);
5087
5088   if (!PyList_Check (obj)) {
5089     PyErr_SetString (PyExc_RuntimeError, \"expecting a list parameter\");
5090     return NULL;
5091   }
5092
5093   len = PyList_Size (obj);
5094   r = malloc (sizeof (char *) * (len+1));
5095   if (r == NULL) {
5096     PyErr_SetString (PyExc_RuntimeError, \"get_string_list: out of memory\");
5097     return NULL;
5098   }
5099
5100   for (i = 0; i < len; ++i)
5101     r[i] = PyString_AsString (PyList_GetItem (obj, i));
5102   r[len] = NULL;
5103
5104   return r;
5105 }
5106
5107 static PyObject *
5108 put_string_list (char * const * const argv)
5109 {
5110   PyObject *list;
5111   int argc, i;
5112
5113   for (argc = 0; argv[argc] != NULL; ++argc)
5114     ;
5115
5116   list = PyList_New (argc);
5117   for (i = 0; i < argc; ++i)
5118     PyList_SetItem (list, i, PyString_FromString (argv[i]));
5119
5120   return list;
5121 }
5122
5123 static PyObject *
5124 put_table (char * const * const argv)
5125 {
5126   PyObject *list, *item;
5127   int argc, i;
5128
5129   for (argc = 0; argv[argc] != NULL; ++argc)
5130     ;
5131
5132   list = PyList_New (argc >> 1);
5133   for (i = 0; i < argc; i += 2) {
5134     item = PyTuple_New (2);
5135     PyTuple_SetItem (item, 0, PyString_FromString (argv[i]));
5136     PyTuple_SetItem (item, 1, PyString_FromString (argv[i+1]));
5137     PyList_SetItem (list, i >> 1, item);
5138   }
5139
5140   return list;
5141 }
5142
5143 static void
5144 free_strings (char **argv)
5145 {
5146   int argc;
5147
5148   for (argc = 0; argv[argc] != NULL; ++argc)
5149     free (argv[argc]);
5150   free (argv);
5151 }
5152
5153 static PyObject *
5154 py_guestfs_create (PyObject *self, PyObject *args)
5155 {
5156   guestfs_h *g;
5157
5158   g = guestfs_create ();
5159   if (g == NULL) {
5160     PyErr_SetString (PyExc_RuntimeError,
5161                      \"guestfs.create: failed to allocate handle\");
5162     return NULL;
5163   }
5164   guestfs_set_error_handler (g, NULL, NULL);
5165   return put_handle (g);
5166 }
5167
5168 static PyObject *
5169 py_guestfs_close (PyObject *self, PyObject *args)
5170 {
5171   PyObject *py_g;
5172   guestfs_h *g;
5173
5174   if (!PyArg_ParseTuple (args, (char *) \"O:guestfs_close\", &py_g))
5175     return NULL;
5176   g = get_handle (py_g);
5177
5178   guestfs_close (g);
5179
5180   Py_INCREF (Py_None);
5181   return Py_None;
5182 }
5183
5184 ";
5185
5186   (* LVM structures, turned into Python dictionaries. *)
5187   List.iter (
5188     fun (typ, cols) ->
5189       pr "static PyObject *\n";
5190       pr "put_lvm_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
5191       pr "{\n";
5192       pr "  PyObject *dict;\n";
5193       pr "\n";
5194       pr "  dict = PyDict_New ();\n";
5195       List.iter (
5196         function
5197         | name, `String ->
5198             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5199             pr "                        PyString_FromString (%s->%s));\n"
5200               typ name
5201         | name, `UUID ->
5202             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5203             pr "                        PyString_FromStringAndSize (%s->%s, 32));\n"
5204               typ name
5205         | name, `Bytes ->
5206             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5207             pr "                        PyLong_FromUnsignedLongLong (%s->%s));\n"
5208               typ name
5209         | name, `Int ->
5210             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5211             pr "                        PyLong_FromLongLong (%s->%s));\n"
5212               typ name
5213         | name, `OptPercent ->
5214             pr "  if (%s->%s >= 0)\n" typ name;
5215             pr "    PyDict_SetItemString (dict, \"%s\",\n" name;
5216             pr "                          PyFloat_FromDouble ((double) %s->%s));\n"
5217               typ name;
5218             pr "  else {\n";
5219             pr "    Py_INCREF (Py_None);\n";
5220             pr "    PyDict_SetItemString (dict, \"%s\", Py_None);" name;
5221             pr "  }\n"
5222       ) cols;
5223       pr "  return dict;\n";
5224       pr "};\n";
5225       pr "\n";
5226
5227       pr "static PyObject *\n";
5228       pr "put_lvm_%s_list (struct guestfs_lvm_%s_list *%ss)\n" typ typ typ;
5229       pr "{\n";
5230       pr "  PyObject *list;\n";
5231       pr "  int i;\n";
5232       pr "\n";
5233       pr "  list = PyList_New (%ss->len);\n" typ;
5234       pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
5235       pr "    PyList_SetItem (list, i, put_lvm_%s (&%ss->val[i]));\n" typ typ;
5236       pr "  return list;\n";
5237       pr "};\n";
5238       pr "\n"
5239   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
5240
5241   (* Stat structures, turned into Python dictionaries. *)
5242   List.iter (
5243     fun (typ, cols) ->
5244       pr "static PyObject *\n";
5245       pr "put_%s (struct guestfs_%s *%s)\n" typ typ typ;
5246       pr "{\n";
5247       pr "  PyObject *dict;\n";
5248       pr "\n";
5249       pr "  dict = PyDict_New ();\n";
5250       List.iter (
5251         function
5252         | name, `Int ->
5253             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
5254             pr "                        PyLong_FromLongLong (%s->%s));\n"
5255               typ name
5256       ) cols;
5257       pr "  return dict;\n";
5258       pr "};\n";
5259       pr "\n";
5260   ) ["stat", stat_cols; "statvfs", statvfs_cols];
5261
5262   (* Python wrapper functions. *)
5263   List.iter (
5264     fun (name, style, _, _, _, _, _) ->
5265       pr "static PyObject *\n";
5266       pr "py_guestfs_%s (PyObject *self, PyObject *args)\n" name;
5267       pr "{\n";
5268
5269       pr "  PyObject *py_g;\n";
5270       pr "  guestfs_h *g;\n";
5271       pr "  PyObject *py_r;\n";
5272
5273       let error_code =
5274         match fst style with
5275         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
5276         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
5277         | RConstString _ -> pr "  const char *r;\n"; "NULL"
5278         | RString _ -> pr "  char *r;\n"; "NULL"
5279         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
5280         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
5281         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
5282         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
5283         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
5284         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
5285         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
5286
5287       List.iter (
5288         function
5289         | String n | FileIn n | FileOut n -> pr "  const char *%s;\n" n
5290         | OptString n -> pr "  const char *%s;\n" n
5291         | StringList n ->
5292             pr "  PyObject *py_%s;\n" n;
5293             pr "  const char **%s;\n" n
5294         | Bool n -> pr "  int %s;\n" n
5295         | Int n -> pr "  int %s;\n" n
5296       ) (snd style);
5297
5298       pr "\n";
5299
5300       (* Convert the parameters. *)
5301       pr "  if (!PyArg_ParseTuple (args, (char *) \"O";
5302       List.iter (
5303         function
5304         | String _ | FileIn _ | FileOut _ -> pr "s"
5305         | OptString _ -> pr "z"
5306         | StringList _ -> pr "O"
5307         | Bool _ -> pr "i" (* XXX Python has booleans? *)
5308         | Int _ -> pr "i"
5309       ) (snd style);
5310       pr ":guestfs_%s\",\n" name;
5311       pr "                         &py_g";
5312       List.iter (
5313         function
5314         | String n | FileIn n | FileOut n -> pr ", &%s" n
5315         | OptString n -> pr ", &%s" n
5316         | StringList n -> pr ", &py_%s" n
5317         | Bool n -> pr ", &%s" n
5318         | Int n -> pr ", &%s" n
5319       ) (snd style);
5320
5321       pr "))\n";
5322       pr "    return NULL;\n";
5323
5324       pr "  g = get_handle (py_g);\n";
5325       List.iter (
5326         function
5327         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5328         | StringList n ->
5329             pr "  %s = get_string_list (py_%s);\n" n n;
5330             pr "  if (!%s) return NULL;\n" n
5331       ) (snd style);
5332
5333       pr "\n";
5334
5335       pr "  r = guestfs_%s " name;
5336       generate_call_args ~handle:"g" (snd style);
5337       pr ";\n";
5338
5339       List.iter (
5340         function
5341         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5342         | StringList n ->
5343             pr "  free (%s);\n" n
5344       ) (snd style);
5345
5346       pr "  if (r == %s) {\n" error_code;
5347       pr "    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));\n";
5348       pr "    return NULL;\n";
5349       pr "  }\n";
5350       pr "\n";
5351
5352       (match fst style with
5353        | RErr ->
5354            pr "  Py_INCREF (Py_None);\n";
5355            pr "  py_r = Py_None;\n"
5356        | RInt _
5357        | RBool _ -> pr "  py_r = PyInt_FromLong ((long) r);\n"
5358        | RInt64 _ -> pr "  py_r = PyLong_FromLongLong (r);\n"
5359        | RConstString _ -> pr "  py_r = PyString_FromString (r);\n"
5360        | RString _ ->
5361            pr "  py_r = PyString_FromString (r);\n";
5362            pr "  free (r);\n"
5363        | RStringList _ ->
5364            pr "  py_r = put_string_list (r);\n";
5365            pr "  free_strings (r);\n"
5366        | RIntBool _ ->
5367            pr "  py_r = PyTuple_New (2);\n";
5368            pr "  PyTuple_SetItem (py_r, 0, PyInt_FromLong ((long) r->i));\n";
5369            pr "  PyTuple_SetItem (py_r, 1, PyInt_FromLong ((long) r->b));\n";
5370            pr "  guestfs_free_int_bool (r);\n"
5371        | RPVList n ->
5372            pr "  py_r = put_lvm_pv_list (r);\n";
5373            pr "  guestfs_free_lvm_pv_list (r);\n"
5374        | RVGList n ->
5375            pr "  py_r = put_lvm_vg_list (r);\n";
5376            pr "  guestfs_free_lvm_vg_list (r);\n"
5377        | RLVList n ->
5378            pr "  py_r = put_lvm_lv_list (r);\n";
5379            pr "  guestfs_free_lvm_lv_list (r);\n"
5380        | RStat n ->
5381            pr "  py_r = put_stat (r);\n";
5382            pr "  free (r);\n"
5383        | RStatVFS n ->
5384            pr "  py_r = put_statvfs (r);\n";
5385            pr "  free (r);\n"
5386        | RHashtable n ->
5387            pr "  py_r = put_table (r);\n";
5388            pr "  free_strings (r);\n"
5389       );
5390
5391       pr "  return py_r;\n";
5392       pr "}\n";
5393       pr "\n"
5394   ) all_functions;
5395
5396   (* Table of functions. *)
5397   pr "static PyMethodDef methods[] = {\n";
5398   pr "  { (char *) \"create\", py_guestfs_create, METH_VARARGS, NULL },\n";
5399   pr "  { (char *) \"close\", py_guestfs_close, METH_VARARGS, NULL },\n";
5400   List.iter (
5401     fun (name, _, _, _, _, _, _) ->
5402       pr "  { (char *) \"%s\", py_guestfs_%s, METH_VARARGS, NULL },\n"
5403         name name
5404   ) all_functions;
5405   pr "  { NULL, NULL, 0, NULL }\n";
5406   pr "};\n";
5407   pr "\n";
5408
5409   (* Init function. *)
5410   pr "\
5411 void
5412 initlibguestfsmod (void)
5413 {
5414   static int initialized = 0;
5415
5416   if (initialized) return;
5417   Py_InitModule ((char *) \"libguestfsmod\", methods);
5418   initialized = 1;
5419 }
5420 "
5421
5422 (* Generate Python module. *)
5423 and generate_python_py () =
5424   generate_header HashStyle LGPLv2;
5425
5426   pr "\
5427 u\"\"\"Python bindings for libguestfs
5428
5429 import guestfs
5430 g = guestfs.GuestFS ()
5431 g.add_drive (\"guest.img\")
5432 g.launch ()
5433 g.wait_ready ()
5434 parts = g.list_partitions ()
5435
5436 The guestfs module provides a Python binding to the libguestfs API
5437 for examining and modifying virtual machine disk images.
5438
5439 Amongst the things this is good for: making batch configuration
5440 changes to guests, getting disk used/free statistics (see also:
5441 virt-df), migrating between virtualization systems (see also:
5442 virt-p2v), performing partial backups, performing partial guest
5443 clones, cloning guests and changing registry/UUID/hostname info, and
5444 much else besides.
5445
5446 Libguestfs uses Linux kernel and qemu code, and can access any type of
5447 guest filesystem that Linux and qemu can, including but not limited
5448 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
5449 schemes, qcow, qcow2, vmdk.
5450
5451 Libguestfs provides ways to enumerate guest storage (eg. partitions,
5452 LVs, what filesystem is in each LV, etc.).  It can also run commands
5453 in the context of the guest.  Also you can access filesystems over FTP.
5454
5455 Errors which happen while using the API are turned into Python
5456 RuntimeError exceptions.
5457
5458 To create a guestfs handle you usually have to perform the following
5459 sequence of calls:
5460
5461 # Create the handle, call add_drive at least once, and possibly
5462 # several times if the guest has multiple block devices:
5463 g = guestfs.GuestFS ()
5464 g.add_drive (\"guest.img\")
5465
5466 # Launch the qemu subprocess and wait for it to become ready:
5467 g.launch ()
5468 g.wait_ready ()
5469
5470 # Now you can issue commands, for example:
5471 logvols = g.lvs ()
5472
5473 \"\"\"
5474
5475 import libguestfsmod
5476
5477 class GuestFS:
5478     \"\"\"Instances of this class are libguestfs API handles.\"\"\"
5479
5480     def __init__ (self):
5481         \"\"\"Create a new libguestfs handle.\"\"\"
5482         self._o = libguestfsmod.create ()
5483
5484     def __del__ (self):
5485         libguestfsmod.close (self._o)
5486
5487 ";
5488
5489   List.iter (
5490     fun (name, style, _, flags, _, _, longdesc) ->
5491       let doc = replace_str longdesc "C<guestfs_" "C<g." in
5492       let doc =
5493         match fst style with
5494         | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _
5495         | RString _ -> doc
5496         | RStringList _ ->
5497             doc ^ "\n\nThis function returns a list of strings."
5498         | RIntBool _ ->
5499             doc ^ "\n\nThis function returns a tuple (int, bool).\n"
5500         | RPVList _ ->
5501             doc ^ "\n\nThis function returns a list of PVs.  Each PV is represented as a dictionary."
5502         | RVGList _ ->
5503             doc ^ "\n\nThis function returns a list of VGs.  Each VG is represented as a dictionary."
5504         | RLVList _ ->
5505             doc ^ "\n\nThis function returns a list of LVs.  Each LV is represented as a dictionary."
5506         | RStat _ ->
5507             doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the stat structure."
5508        | RStatVFS _ ->
5509             doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure."
5510        | RHashtable _ ->
5511             doc ^ "\n\nThis function returns a dictionary." in
5512       let doc =
5513         if List.mem ProtocolLimitWarning flags then
5514           doc ^ "\n\n" ^ protocol_limit_warning
5515         else doc in
5516       let doc =
5517         if List.mem DangerWillRobinson flags then
5518           doc ^ "\n\n" ^ danger_will_robinson
5519         else doc in
5520       let doc = pod2text ~width:60 name doc in
5521       let doc = List.map (fun line -> replace_str line "\\" "\\\\") doc in
5522       let doc = String.concat "\n        " doc in
5523
5524       pr "    def %s " name;
5525       generate_call_args ~handle:"self" (snd style);
5526       pr ":\n";
5527       pr "        u\"\"\"%s\"\"\"\n" doc;
5528       pr "        return libguestfsmod.%s " name;
5529       generate_call_args ~handle:"self._o" (snd style);
5530       pr "\n";
5531       pr "\n";
5532   ) all_functions
5533
5534 (* Useful if you need the longdesc POD text as plain text.  Returns a
5535  * list of lines.
5536  *
5537  * This is the slowest thing about autogeneration.
5538  *)
5539 and pod2text ~width name longdesc =
5540   let filename, chan = Filename.open_temp_file "gen" ".tmp" in
5541   fprintf chan "=head1 %s\n\n%s\n" name longdesc;
5542   close_out chan;
5543   let cmd = sprintf "pod2text -w %d %s" width (Filename.quote filename) in
5544   let chan = Unix.open_process_in cmd in
5545   let lines = ref [] in
5546   let rec loop i =
5547     let line = input_line chan in
5548     if i = 1 then               (* discard the first line of output *)
5549       loop (i+1)
5550     else (
5551       let line = triml line in
5552       lines := line :: !lines;
5553       loop (i+1)
5554     ) in
5555   let lines = try loop 1 with End_of_file -> List.rev !lines in
5556   Unix.unlink filename;
5557   match Unix.close_process_in chan with
5558   | Unix.WEXITED 0 -> lines
5559   | Unix.WEXITED i ->
5560       failwithf "pod2text: process exited with non-zero status (%d)" i
5561   | Unix.WSIGNALED i | Unix.WSTOPPED i ->
5562       failwithf "pod2text: process signalled or stopped by signal %d" i
5563
5564 (* Generate ruby bindings. *)
5565 and generate_ruby_c () =
5566   generate_header CStyle LGPLv2;
5567
5568   pr "\
5569 #include <stdio.h>
5570 #include <stdlib.h>
5571
5572 #include <ruby.h>
5573
5574 #include \"guestfs.h\"
5575
5576 #include \"extconf.h\"
5577
5578 static VALUE m_guestfs;                 /* guestfs module */
5579 static VALUE c_guestfs;                 /* guestfs_h handle */
5580 static VALUE e_Error;                   /* used for all errors */
5581
5582 static void ruby_guestfs_free (void *p)
5583 {
5584   if (!p) return;
5585   guestfs_close ((guestfs_h *) p);
5586 }
5587
5588 static VALUE ruby_guestfs_create (VALUE m)
5589 {
5590   guestfs_h *g;
5591
5592   g = guestfs_create ();
5593   if (!g)
5594     rb_raise (e_Error, \"failed to create guestfs handle\");
5595
5596   /* Don't print error messages to stderr by default. */
5597   guestfs_set_error_handler (g, NULL, NULL);
5598
5599   /* Wrap it, and make sure the close function is called when the
5600    * handle goes away.
5601    */
5602   return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g);
5603 }
5604
5605 static VALUE ruby_guestfs_close (VALUE gv)
5606 {
5607   guestfs_h *g;
5608   Data_Get_Struct (gv, guestfs_h, g);
5609
5610   ruby_guestfs_free (g);
5611   DATA_PTR (gv) = NULL;
5612
5613   return Qnil;
5614 }
5615
5616 ";
5617
5618   List.iter (
5619     fun (name, style, _, _, _, _, _) ->
5620       pr "static VALUE ruby_guestfs_%s (VALUE gv" name;
5621       List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) (snd style);
5622       pr ")\n";
5623       pr "{\n";
5624       pr "  guestfs_h *g;\n";
5625       pr "  Data_Get_Struct (gv, guestfs_h, g);\n";
5626       pr "  if (!g)\n";
5627       pr "    rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n"
5628         name;
5629       pr "\n";
5630
5631       List.iter (
5632         function
5633         | String n | FileIn n | FileOut n ->
5634             pr "  const char *%s = StringValueCStr (%sv);\n" n n;
5635             pr "  if (!%s)\n" n;
5636             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
5637             pr "              \"%s\", \"%s\");\n" n name
5638         | OptString n ->
5639             pr "  const char *%s = StringValueCStr (%sv);\n" n n
5640         | StringList n ->
5641             pr "  char **%s;" n;
5642             pr "  {\n";
5643             pr "    int i, len;\n";
5644             pr "    len = RARRAY_LEN (%sv);\n" n;
5645             pr "    %s = guestfs_safe_malloc (g, sizeof (char *) * (len+1));\n"
5646               n;
5647             pr "    for (i = 0; i < len; ++i) {\n";
5648             pr "      VALUE v = rb_ary_entry (%sv, i);\n" n;
5649             pr "      %s[i] = StringValueCStr (v);\n" n;
5650             pr "    }\n";
5651             pr "    %s[len] = NULL;\n" n;
5652             pr "  }\n";
5653         | Bool n
5654         | Int n ->
5655             pr "  int %s = NUM2INT (%sv);\n" n n
5656       ) (snd style);
5657       pr "\n";
5658
5659       let error_code =
5660         match fst style with
5661         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
5662         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
5663         | RConstString _ -> pr "  const char *r;\n"; "NULL"
5664         | RString _ -> pr "  char *r;\n"; "NULL"
5665         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
5666         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
5667         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
5668         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
5669         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
5670         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
5671         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
5672       pr "\n";
5673
5674       pr "  r = guestfs_%s " name;
5675       generate_call_args ~handle:"g" (snd style);
5676       pr ";\n";
5677
5678       List.iter (
5679         function
5680         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
5681         | StringList n ->
5682             pr "  free (%s);\n" n
5683       ) (snd style);
5684
5685       pr "  if (r == %s)\n" error_code;
5686       pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n";
5687       pr "\n";
5688
5689       (match fst style with
5690        | RErr ->
5691            pr "  return Qnil;\n"
5692        | RInt _ | RBool _ ->
5693            pr "  return INT2NUM (r);\n"
5694        | RInt64 _ ->
5695            pr "  return ULL2NUM (r);\n"
5696        | RConstString _ ->
5697            pr "  return rb_str_new2 (r);\n";
5698        | RString _ ->
5699            pr "  VALUE rv = rb_str_new2 (r);\n";
5700            pr "  free (r);\n";
5701            pr "  return rv;\n";
5702        | RStringList _ ->
5703            pr "  int i, len = 0;\n";
5704            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
5705            pr "  VALUE rv = rb_ary_new2 (len);\n";
5706            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
5707            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
5708            pr "    free (r[i]);\n";
5709            pr "  }\n";
5710            pr "  free (r);\n";
5711            pr "  return rv;\n"
5712        | RIntBool _ ->
5713            pr "  VALUE rv = rb_ary_new2 (2);\n";
5714            pr "  rb_ary_push (rv, INT2NUM (r->i));\n";
5715            pr "  rb_ary_push (rv, INT2NUM (r->b));\n";
5716            pr "  guestfs_free_int_bool (r);\n";
5717            pr "  return rv;\n"
5718        | RPVList n ->
5719            generate_ruby_lvm_code "pv" pv_cols
5720        | RVGList n ->
5721            generate_ruby_lvm_code "vg" vg_cols
5722        | RLVList n ->
5723            generate_ruby_lvm_code "lv" lv_cols
5724        | RStat n ->
5725            pr "  VALUE rv = rb_hash_new ();\n";
5726            List.iter (
5727              function
5728              | name, `Int ->
5729                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
5730            ) stat_cols;
5731            pr "  free (r);\n";
5732            pr "  return rv;\n"
5733        | RStatVFS n ->
5734            pr "  VALUE rv = rb_hash_new ();\n";
5735            List.iter (
5736              function
5737              | name, `Int ->
5738                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
5739            ) statvfs_cols;
5740            pr "  free (r);\n";
5741            pr "  return rv;\n"
5742        | RHashtable _ ->
5743            pr "  VALUE rv = rb_hash_new ();\n";
5744            pr "  int i;\n";
5745            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
5746            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
5747            pr "    free (r[i]);\n";
5748            pr "    free (r[i+1]);\n";
5749            pr "  }\n";
5750            pr "  free (r);\n";
5751            pr "  return rv;\n"
5752       );
5753
5754       pr "}\n";
5755       pr "\n"
5756   ) all_functions;
5757
5758   pr "\
5759 /* Initialize the module. */
5760 void Init__guestfs ()
5761 {
5762   m_guestfs = rb_define_module (\"Guestfs\");
5763   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
5764   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
5765
5766   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
5767   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
5768
5769 ";
5770   (* Define the rest of the methods. *)
5771   List.iter (
5772     fun (name, style, _, _, _, _, _) ->
5773       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
5774       pr "        ruby_guestfs_%s, %d);\n" name (List.length (snd style))
5775   ) all_functions;
5776
5777   pr "}\n"
5778
5779 (* Ruby code to return an LVM struct list. *)
5780 and generate_ruby_lvm_code typ cols =
5781   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
5782   pr "  int i;\n";
5783   pr "  for (i = 0; i < r->len; ++i) {\n";
5784   pr "    VALUE hv = rb_hash_new ();\n";
5785   List.iter (
5786     function
5787     | name, `String ->
5788         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
5789     | name, `UUID ->
5790         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
5791     | name, `Bytes
5792     | name, `Int ->
5793         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
5794     | name, `OptPercent ->
5795         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
5796   ) cols;
5797   pr "    rb_ary_push (rv, hv);\n";
5798   pr "  }\n";
5799   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
5800   pr "  return rv;\n"
5801
5802 (* Generate Java bindings GuestFS.java file. *)
5803 and generate_java_java () =
5804   generate_header CStyle LGPLv2;
5805
5806   pr "\
5807 package com.redhat.et.libguestfs;
5808
5809 import java.util.HashMap;
5810 import com.redhat.et.libguestfs.LibGuestFSException;
5811 import com.redhat.et.libguestfs.PV;
5812 import com.redhat.et.libguestfs.VG;
5813 import com.redhat.et.libguestfs.LV;
5814 import com.redhat.et.libguestfs.Stat;
5815 import com.redhat.et.libguestfs.StatVFS;
5816 import com.redhat.et.libguestfs.IntBool;
5817
5818 /**
5819  * The GuestFS object is a libguestfs handle.
5820  *
5821  * @author rjones
5822  */
5823 public class GuestFS {
5824   // Load the native code.
5825   static {
5826     System.loadLibrary (\"guestfs_jni\");
5827   }
5828
5829   /**
5830    * The native guestfs_h pointer.
5831    */
5832   long g;
5833
5834   /**
5835    * Create a libguestfs handle.
5836    *
5837    * @throws LibGuestFSException
5838    */
5839   public GuestFS () throws LibGuestFSException
5840   {
5841     g = _create ();
5842   }
5843   private native long _create () throws LibGuestFSException;
5844
5845   /**
5846    * Close a libguestfs handle.
5847    *
5848    * You can also leave handles to be collected by the garbage
5849    * collector, but this method ensures that the resources used
5850    * by the handle are freed up immediately.  If you call any
5851    * other methods after closing the handle, you will get an
5852    * exception.
5853    *
5854    * @throws LibGuestFSException
5855    */
5856   public void close () throws LibGuestFSException
5857   {
5858     if (g != 0)
5859       _close (g);
5860     g = 0;
5861   }
5862   private native void _close (long g) throws LibGuestFSException;
5863
5864   public void finalize () throws LibGuestFSException
5865   {
5866     close ();
5867   }
5868
5869 ";
5870
5871   List.iter (
5872     fun (name, style, _, flags, _, shortdesc, longdesc) ->
5873       let doc = replace_str longdesc "C<guestfs_" "C<g." in
5874       let doc =
5875         if List.mem ProtocolLimitWarning flags then
5876           doc ^ "\n\n" ^ protocol_limit_warning
5877         else doc in
5878       let doc =
5879         if List.mem DangerWillRobinson flags then
5880           doc ^ "\n\n" ^ danger_will_robinson
5881         else doc in
5882       let doc = pod2text ~width:60 name doc in
5883       let doc = String.concat "\n   * " doc in
5884
5885       pr "  /**\n";
5886       pr "   * %s\n" shortdesc;
5887       pr "   *\n";
5888       pr "   * %s\n" doc;
5889       pr "   * @throws LibGuestFSException\n";
5890       pr "   */\n";
5891       pr "  ";
5892       generate_java_prototype ~public:true ~semicolon:false name style;
5893       pr "\n";
5894       pr "  {\n";
5895       pr "    if (g == 0)\n";
5896       pr "      throw new LibGuestFSException (\"%s: handle is closed\");\n"
5897         name;
5898       pr "    ";
5899       if fst style <> RErr then pr "return ";
5900       pr "_%s " name;
5901       generate_call_args ~handle:"g" (snd style);
5902       pr ";\n";
5903       pr "  }\n";
5904       pr "  ";
5905       generate_java_prototype ~privat:true ~native:true name style;
5906       pr "\n";
5907       pr "\n";
5908   ) all_functions;
5909
5910   pr "}\n"
5911
5912 and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false)
5913     ?(semicolon=true) name style =
5914   if privat then pr "private ";
5915   if public then pr "public ";
5916   if native then pr "native ";
5917
5918   (* return type *)
5919   (match fst style with
5920    | RErr -> pr "void ";
5921    | RInt _ -> pr "int ";
5922    | RInt64 _ -> pr "long ";
5923    | RBool _ -> pr "boolean ";
5924    | RConstString _ | RString _ -> pr "String ";
5925    | RStringList _ -> pr "String[] ";
5926    | RIntBool _ -> pr "IntBool ";
5927    | RPVList _ -> pr "PV[] ";
5928    | RVGList _ -> pr "VG[] ";
5929    | RLVList _ -> pr "LV[] ";
5930    | RStat _ -> pr "Stat ";
5931    | RStatVFS _ -> pr "StatVFS ";
5932    | RHashtable _ -> pr "HashMap<String,String> ";
5933   );
5934
5935   if native then pr "_%s " name else pr "%s " name;
5936   pr "(";
5937   let needs_comma = ref false in
5938   if native then (
5939     pr "long g";
5940     needs_comma := true
5941   );
5942
5943   (* args *)
5944   List.iter (
5945     fun arg ->
5946       if !needs_comma then pr ", ";
5947       needs_comma := true;
5948
5949       match arg with
5950       | String n
5951       | OptString n
5952       | FileIn n
5953       | FileOut n ->
5954           pr "String %s" n
5955       | StringList n ->
5956           pr "String[] %s" n
5957       | Bool n ->
5958           pr "boolean %s" n
5959       | Int n ->
5960           pr "int %s" n
5961   ) (snd style);
5962
5963   pr ")\n";
5964   pr "    throws LibGuestFSException";
5965   if semicolon then pr ";"
5966
5967 and generate_java_struct typ cols =
5968   generate_header CStyle LGPLv2;
5969
5970   pr "\
5971 package com.redhat.et.libguestfs;
5972
5973 /**
5974  * Libguestfs %s structure.
5975  *
5976  * @author rjones
5977  * @see GuestFS
5978  */
5979 public class %s {
5980 " typ typ;
5981
5982   List.iter (
5983     function
5984     | name, `String
5985     | name, `UUID -> pr "  public String %s;\n" name
5986     | name, `Bytes
5987     | name, `Int -> pr "  public long %s;\n" name
5988     | name, `OptPercent ->
5989         pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
5990         pr "  public float %s;\n" name
5991   ) cols;
5992
5993   pr "}\n"
5994
5995 and generate_java_c () =
5996   generate_header CStyle LGPLv2;
5997
5998   pr "\
5999 #include <stdio.h>
6000 #include <stdlib.h>
6001 #include <string.h>
6002
6003 #include \"com_redhat_et_libguestfs_GuestFS.h\"
6004 #include \"guestfs.h\"
6005
6006 /* Note that this function returns.  The exception is not thrown
6007  * until after the wrapper function returns.
6008  */
6009 static void
6010 throw_exception (JNIEnv *env, const char *msg)
6011 {
6012   jclass cl;
6013   cl = (*env)->FindClass (env,
6014                           \"com/redhat/et/libguestfs/LibGuestFSException\");
6015   (*env)->ThrowNew (env, cl, msg);
6016 }
6017
6018 JNIEXPORT jlong JNICALL
6019 Java_com_redhat_et_libguestfs_GuestFS__1create
6020   (JNIEnv *env, jobject obj)
6021 {
6022   guestfs_h *g;
6023
6024   g = guestfs_create ();
6025   if (g == NULL) {
6026     throw_exception (env, \"GuestFS.create: failed to allocate handle\");
6027     return 0;
6028   }
6029   guestfs_set_error_handler (g, NULL, NULL);
6030   return (jlong) (long) g;
6031 }
6032
6033 JNIEXPORT void JNICALL
6034 Java_com_redhat_et_libguestfs_GuestFS__1close
6035   (JNIEnv *env, jobject obj, jlong jg)
6036 {
6037   guestfs_h *g = (guestfs_h *) (long) jg;
6038   guestfs_close (g);
6039 }
6040
6041 ";
6042
6043   List.iter (
6044     fun (name, style, _, _, _, _, _) ->
6045       pr "JNIEXPORT ";
6046       (match fst style with
6047        | RErr -> pr "void ";
6048        | RInt _ -> pr "jint ";
6049        | RInt64 _ -> pr "jlong ";
6050        | RBool _ -> pr "jboolean ";
6051        | RConstString _ | RString _ -> pr "jstring ";
6052        | RIntBool _ | RStat _ | RStatVFS _ | RHashtable _ ->
6053            pr "jobject ";
6054        | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
6055            pr "jobjectArray ";
6056       );
6057       pr "JNICALL\n";
6058       pr "Java_com_redhat_et_libguestfs_GuestFS_";
6059       pr "%s" (replace_str ("_" ^ name) "_" "_1");
6060       pr "\n";
6061       pr "  (JNIEnv *env, jobject obj, jlong jg";
6062       List.iter (
6063         function
6064         | String n
6065         | OptString n
6066         | FileIn n
6067         | FileOut n ->
6068             pr ", jstring j%s" n
6069         | StringList n ->
6070             pr ", jobjectArray j%s" n
6071         | Bool n ->
6072             pr ", jboolean j%s" n
6073         | Int n ->
6074             pr ", jint j%s" n
6075       ) (snd style);
6076       pr ")\n";
6077       pr "{\n";
6078       pr "  guestfs_h *g = (guestfs_h *) (long) jg;\n";
6079       let error_code, no_ret =
6080         match fst style with
6081         | RErr -> pr "  int r;\n"; "-1", ""
6082         | RBool _
6083         | RInt _ -> pr "  int r;\n"; "-1", "0"
6084         | RInt64 _ -> pr "  int64_t r;\n"; "-1", "0"
6085         | RConstString _ -> pr "  const char *r;\n"; "NULL", "NULL"
6086         | RString _ ->
6087             pr "  jstring jr;\n";
6088             pr "  char *r;\n"; "NULL", "NULL"
6089         | RStringList _ ->
6090             pr "  jobjectArray jr;\n";
6091             pr "  int r_len;\n";
6092             pr "  jclass cl;\n";
6093             pr "  jstring jstr;\n";
6094             pr "  char **r;\n"; "NULL", "NULL"
6095         | RIntBool _ ->
6096             pr "  jobject jr;\n";
6097             pr "  jclass cl;\n";
6098             pr "  jfieldID fl;\n";
6099             pr "  struct guestfs_int_bool *r;\n"; "NULL", "NULL"
6100         | RStat _ ->
6101             pr "  jobject jr;\n";
6102             pr "  jclass cl;\n";
6103             pr "  jfieldID fl;\n";
6104             pr "  struct guestfs_stat *r;\n"; "NULL", "NULL"
6105         | RStatVFS _ ->
6106             pr "  jobject jr;\n";
6107             pr "  jclass cl;\n";
6108             pr "  jfieldID fl;\n";
6109             pr "  struct guestfs_statvfs *r;\n"; "NULL", "NULL"
6110         | RPVList _ ->
6111             pr "  jobjectArray jr;\n";
6112             pr "  jclass cl;\n";
6113             pr "  jfieldID fl;\n";
6114             pr "  jobject jfl;\n";
6115             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL", "NULL"
6116         | RVGList _ ->
6117             pr "  jobjectArray jr;\n";
6118             pr "  jclass cl;\n";
6119             pr "  jfieldID fl;\n";
6120             pr "  jobject jfl;\n";
6121             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL", "NULL"
6122         | RLVList _ ->
6123             pr "  jobjectArray jr;\n";
6124             pr "  jclass cl;\n";
6125             pr "  jfieldID fl;\n";
6126             pr "  jobject jfl;\n";
6127             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL", "NULL"
6128         | RHashtable _ -> pr "  char **r;\n"; "NULL", "NULL" in
6129       List.iter (
6130         function
6131         | String n
6132         | OptString n
6133         | FileIn n
6134         | FileOut n ->
6135             pr "  const char *%s;\n" n
6136         | StringList n ->
6137             pr "  int %s_len;\n" n;
6138             pr "  const char **%s;\n" n
6139         | Bool n
6140         | Int n ->
6141             pr "  int %s;\n" n
6142       ) (snd style);
6143
6144       let needs_i =
6145         (match fst style with
6146          | RStringList _ | RPVList _ | RVGList _ | RLVList _ -> true
6147          | RErr _ | RBool _ | RInt _ | RInt64 _ | RConstString _
6148          | RString _ | RIntBool _ | RStat _ | RStatVFS _
6149          | RHashtable _ -> false) ||
6150         List.exists (function StringList _ -> true | _ -> false) (snd style) in
6151       if needs_i then
6152         pr "  int i;\n";
6153
6154       pr "\n";
6155
6156       (* Get the parameters. *)
6157       List.iter (
6158         function
6159         | String n
6160         | OptString n
6161         | FileIn n
6162         | FileOut n ->
6163             pr "  %s = (*env)->GetStringUTFChars (env, j%s, NULL);\n" n n
6164         | StringList n ->
6165             pr "  %s_len = (*env)->GetArrayLength (env, j%s);\n" n n;
6166             pr "  %s = guestfs_safe_malloc (g, sizeof (char *) * (%s_len+1));\n" n n;
6167             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
6168             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
6169               n;
6170             pr "    %s[i] = (*env)->GetStringUTFChars (env, o, NULL);\n" n;
6171             pr "  }\n";
6172             pr "  %s[%s_len] = NULL;\n" n n;
6173         | Bool n
6174         | Int n ->
6175             pr "  %s = j%s;\n" n n
6176       ) (snd style);
6177
6178       (* Make the call. *)
6179       pr "  r = guestfs_%s " name;
6180       generate_call_args ~handle:"g" (snd style);
6181       pr ";\n";
6182
6183       (* Release the parameters. *)
6184       List.iter (
6185         function
6186         | String n
6187         | OptString n
6188         | FileIn n
6189         | FileOut n ->
6190             pr "  (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
6191         | StringList n ->
6192             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
6193             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
6194               n;
6195             pr "    (*env)->ReleaseStringUTFChars (env, o, %s[i]);\n" n;
6196             pr "  }\n";
6197             pr "  free (%s);\n" n
6198         | Bool n
6199         | Int n -> ()
6200       ) (snd style);
6201
6202       (* Check for errors. *)
6203       pr "  if (r == %s) {\n" error_code;
6204       pr "    throw_exception (env, guestfs_last_error (g));\n";
6205       pr "    return %s;\n" no_ret;
6206       pr "  }\n";
6207
6208       (* Return value. *)
6209       (match fst style with
6210        | RErr -> ()
6211        | RInt _ -> pr "  return (jint) r;\n"
6212        | RBool _ -> pr "  return (jboolean) r;\n"
6213        | RInt64 _ -> pr "  return (jlong) r;\n"
6214        | RConstString _ -> pr "  return (*env)->NewStringUTF (env, r);\n"
6215        | RString _ ->
6216            pr "  jr = (*env)->NewStringUTF (env, r);\n";
6217            pr "  free (r);\n";
6218            pr "  return jr;\n"
6219        | RStringList _ ->
6220            pr "  for (r_len = 0; r[r_len] != NULL; ++r_len) ;\n";
6221            pr "  cl = (*env)->FindClass (env, \"java/lang/String\");\n";
6222            pr "  jstr = (*env)->NewStringUTF (env, \"\");\n";
6223            pr "  jr = (*env)->NewObjectArray (env, r_len, cl, jstr);\n";
6224            pr "  for (i = 0; i < r_len; ++i) {\n";
6225            pr "    jstr = (*env)->NewStringUTF (env, r[i]);\n";
6226            pr "    (*env)->SetObjectArrayElement (env, jr, i, jstr);\n";
6227            pr "    free (r[i]);\n";
6228            pr "  }\n";
6229            pr "  free (r);\n";
6230            pr "  return jr;\n"
6231        | RIntBool _ ->
6232            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/IntBool\");\n";
6233            pr "  jr = (*env)->AllocObject (env, cl);\n";
6234            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"I\");\n";
6235            pr "  (*env)->SetIntField (env, jr, fl, r->i);\n";
6236            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"Z\");\n";
6237            pr "  (*env)->SetBooleanField (env, jr, fl, r->b);\n";
6238            pr "  guestfs_free_int_bool (r);\n";
6239            pr "  return jr;\n"
6240        | RStat _ ->
6241            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/Stat\");\n";
6242            pr "  jr = (*env)->AllocObject (env, cl);\n";
6243            List.iter (
6244              function
6245              | name, `Int ->
6246                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
6247                    name;
6248                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
6249            ) stat_cols;
6250            pr "  free (r);\n";
6251            pr "  return jr;\n"
6252        | RStatVFS _ ->
6253            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/StatVFS\");\n";
6254            pr "  jr = (*env)->AllocObject (env, cl);\n";
6255            List.iter (
6256              function
6257              | name, `Int ->
6258                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
6259                    name;
6260                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
6261            ) statvfs_cols;
6262            pr "  free (r);\n";
6263            pr "  return jr;\n"
6264        | RPVList _ ->
6265            generate_java_lvm_return "pv" "PV" pv_cols
6266        | RVGList _ ->
6267            generate_java_lvm_return "vg" "VG" vg_cols
6268        | RLVList _ ->
6269            generate_java_lvm_return "lv" "LV" lv_cols
6270        | RHashtable _ ->
6271            (* XXX *)
6272            pr "  throw_exception (env, \"%s: internal error: please let us know how to make a Java HashMap from JNI bindings!\");\n" name;
6273            pr "  return NULL;\n"
6274       );
6275
6276       pr "}\n";
6277       pr "\n"
6278   ) all_functions
6279
6280 and generate_java_lvm_return typ jtyp cols =
6281   pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/%s\");\n" jtyp;
6282   pr "  jr = (*env)->NewObjectArray (env, r->len, cl, NULL);\n";
6283   pr "  for (i = 0; i < r->len; ++i) {\n";
6284   pr "    jfl = (*env)->AllocObject (env, cl);\n";
6285   List.iter (
6286     function
6287     | name, `String ->
6288         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
6289         pr "    (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, r->val[i].%s));\n" name;
6290     | name, `UUID ->
6291         pr "    {\n";
6292         pr "      char s[33];\n";
6293         pr "      memcpy (s, r->val[i].%s, 32);\n" name;
6294         pr "      s[32] = 0;\n";
6295         pr "      fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
6296         pr "      (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
6297         pr "    }\n";
6298     | name, (`Bytes|`Int) ->
6299         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
6300         pr "    (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
6301     | name, `OptPercent ->
6302         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"F\");\n" name;
6303         pr "    (*env)->SetFloatField (env, jfl, fl, r->val[i].%s);\n" name;
6304   ) cols;
6305   pr "    (*env)->SetObjectArrayElement (env, jfl, i, jfl);\n";
6306   pr "  }\n";
6307   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
6308   pr "  return jr;\n"
6309
6310 let output_to filename =
6311   let filename_new = filename ^ ".new" in
6312   chan := open_out filename_new;
6313   let close () =
6314     close_out !chan;
6315     chan := stdout;
6316
6317     (* Is the new file different from the current file? *)
6318     if Sys.file_exists filename && files_equal filename filename_new then
6319       Unix.unlink filename_new          (* same, so skip it *)
6320     else (
6321       (* different, overwrite old one *)
6322       (try Unix.chmod filename 0o644 with Unix.Unix_error _ -> ());
6323       Unix.rename filename_new filename;
6324       Unix.chmod filename 0o444;
6325       printf "written %s\n%!" filename;
6326     )
6327   in
6328   close
6329
6330 (* Main program. *)
6331 let () =
6332   check_functions ();
6333
6334   if not (Sys.file_exists "configure.ac") then (
6335     eprintf "\
6336 You are probably running this from the wrong directory.
6337 Run it from the top source directory using the command
6338   src/generator.ml
6339 ";
6340     exit 1
6341   );
6342
6343   let close = output_to "src/guestfs_protocol.x" in
6344   generate_xdr ();
6345   close ();
6346
6347   let close = output_to "src/guestfs-structs.h" in
6348   generate_structs_h ();
6349   close ();
6350
6351   let close = output_to "src/guestfs-actions.h" in
6352   generate_actions_h ();
6353   close ();
6354
6355   let close = output_to "src/guestfs-actions.c" in
6356   generate_client_actions ();
6357   close ();
6358
6359   let close = output_to "daemon/actions.h" in
6360   generate_daemon_actions_h ();
6361   close ();
6362
6363   let close = output_to "daemon/stubs.c" in
6364   generate_daemon_actions ();
6365   close ();
6366
6367   let close = output_to "tests.c" in
6368   generate_tests ();
6369   close ();
6370
6371   let close = output_to "fish/cmds.c" in
6372   generate_fish_cmds ();
6373   close ();
6374
6375   let close = output_to "fish/completion.c" in
6376   generate_fish_completion ();
6377   close ();
6378
6379   let close = output_to "guestfs-structs.pod" in
6380   generate_structs_pod ();
6381   close ();
6382
6383   let close = output_to "guestfs-actions.pod" in
6384   generate_actions_pod ();
6385   close ();
6386
6387   let close = output_to "guestfish-actions.pod" in
6388   generate_fish_actions_pod ();
6389   close ();
6390
6391   let close = output_to "ocaml/guestfs.mli" in
6392   generate_ocaml_mli ();
6393   close ();
6394
6395   let close = output_to "ocaml/guestfs.ml" in
6396   generate_ocaml_ml ();
6397   close ();
6398
6399   let close = output_to "ocaml/guestfs_c_actions.c" in
6400   generate_ocaml_c ();
6401   close ();
6402
6403   let close = output_to "perl/Guestfs.xs" in
6404   generate_perl_xs ();
6405   close ();
6406
6407   let close = output_to "perl/lib/Sys/Guestfs.pm" in
6408   generate_perl_pm ();
6409   close ();
6410
6411   let close = output_to "python/guestfs-py.c" in
6412   generate_python_c ();
6413   close ();
6414
6415   let close = output_to "python/guestfs.py" in
6416   generate_python_py ();
6417   close ();
6418
6419   let close = output_to "ruby/ext/guestfs/_guestfs.c" in
6420   generate_ruby_c ();
6421   close ();
6422
6423   let close = output_to "java/com/redhat/et/libguestfs/GuestFS.java" in
6424   generate_java_java ();
6425   close ();
6426
6427   let close = output_to "java/com/redhat/et/libguestfs/PV.java" in
6428   generate_java_struct "PV" pv_cols;
6429   close ();
6430
6431   let close = output_to "java/com/redhat/et/libguestfs/VG.java" in
6432   generate_java_struct "VG" vg_cols;
6433   close ();
6434
6435   let close = output_to "java/com/redhat/et/libguestfs/LV.java" in
6436   generate_java_struct "LV" lv_cols;
6437   close ();
6438
6439   let close = output_to "java/com/redhat/et/libguestfs/Stat.java" in
6440   generate_java_struct "Stat" stat_cols;
6441   close ();
6442
6443   let close = output_to "java/com/redhat/et/libguestfs/StatVFS.java" in
6444   generate_java_struct "StatVFS" statvfs_cols;
6445   close ();
6446
6447   let close = output_to "java/com_redhat_et_libguestfs_GuestFS.c" in
6448   generate_java_c ();
6449   close ();