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