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