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