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