Add 'set_memsize'/'get_memsize' calls.
[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"; "493"; "/test-command"];
1347        ["command"; "/test-command 1"]], "Result1");
1348     InitBasicFS, Always, TestOutput (
1349       [["upload"; "test-command"; "/test-command"];
1350        ["chmod"; "493"; "/test-command"];
1351        ["command"; "/test-command 2"]], "Result2\n");
1352     InitBasicFS, Always, TestOutput (
1353       [["upload"; "test-command"; "/test-command"];
1354        ["chmod"; "493"; "/test-command"];
1355        ["command"; "/test-command 3"]], "\nResult3");
1356     InitBasicFS, Always, TestOutput (
1357       [["upload"; "test-command"; "/test-command"];
1358        ["chmod"; "493"; "/test-command"];
1359        ["command"; "/test-command 4"]], "\nResult4\n");
1360     InitBasicFS, Always, TestOutput (
1361       [["upload"; "test-command"; "/test-command"];
1362        ["chmod"; "493"; "/test-command"];
1363        ["command"; "/test-command 5"]], "\nResult5\n\n");
1364     InitBasicFS, Always, TestOutput (
1365       [["upload"; "test-command"; "/test-command"];
1366        ["chmod"; "493"; "/test-command"];
1367        ["command"; "/test-command 6"]], "\n\nResult6\n\n");
1368     InitBasicFS, Always, TestOutput (
1369       [["upload"; "test-command"; "/test-command"];
1370        ["chmod"; "493"; "/test-command"];
1371        ["command"; "/test-command 7"]], "");
1372     InitBasicFS, Always, TestOutput (
1373       [["upload"; "test-command"; "/test-command"];
1374        ["chmod"; "493"; "/test-command"];
1375        ["command"; "/test-command 8"]], "\n");
1376     InitBasicFS, Always, TestOutput (
1377       [["upload"; "test-command"; "/test-command"];
1378        ["chmod"; "493"; "/test-command"];
1379        ["command"; "/test-command 9"]], "\n\n");
1380     InitBasicFS, Always, TestOutput (
1381       [["upload"; "test-command"; "/test-command"];
1382        ["chmod"; "493"; "/test-command"];
1383        ["command"; "/test-command 10"]], "Result10-1\nResult10-2\n");
1384     InitBasicFS, Always, TestOutput (
1385       [["upload"; "test-command"; "/test-command"];
1386        ["chmod"; "493"; "/test-command"];
1387        ["command"; "/test-command 11"]], "Result11-1\nResult11-2");
1388     InitBasicFS, Always, TestLastFail (
1389       [["upload"; "test-command"; "/test-command"];
1390        ["chmod"; "493"; "/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"; "493"; "/test-command"];
1428        ["command_lines"; "/test-command 1"]], ["Result1"]);
1429     InitBasicFS, Always, TestOutputList (
1430       [["upload"; "test-command"; "/test-command"];
1431        ["chmod"; "493"; "/test-command"];
1432        ["command_lines"; "/test-command 2"]], ["Result2"]);
1433     InitBasicFS, Always, TestOutputList (
1434       [["upload"; "test-command"; "/test-command"];
1435        ["chmod"; "493"; "/test-command"];
1436        ["command_lines"; "/test-command 3"]], ["";"Result3"]);
1437     InitBasicFS, Always, TestOutputList (
1438       [["upload"; "test-command"; "/test-command"];
1439        ["chmod"; "493"; "/test-command"];
1440        ["command_lines"; "/test-command 4"]], ["";"Result4"]);
1441     InitBasicFS, Always, TestOutputList (
1442       [["upload"; "test-command"; "/test-command"];
1443        ["chmod"; "493"; "/test-command"];
1444        ["command_lines"; "/test-command 5"]], ["";"Result5";""]);
1445     InitBasicFS, Always, TestOutputList (
1446       [["upload"; "test-command"; "/test-command"];
1447        ["chmod"; "493"; "/test-command"];
1448        ["command_lines"; "/test-command 6"]], ["";"";"Result6";""]);
1449     InitBasicFS, Always, TestOutputList (
1450       [["upload"; "test-command"; "/test-command"];
1451        ["chmod"; "493"; "/test-command"];
1452        ["command_lines"; "/test-command 7"]], []);
1453     InitBasicFS, Always, TestOutputList (
1454       [["upload"; "test-command"; "/test-command"];
1455        ["chmod"; "493"; "/test-command"];
1456        ["command_lines"; "/test-command 8"]], [""]);
1457     InitBasicFS, Always, TestOutputList (
1458       [["upload"; "test-command"; "/test-command"];
1459        ["chmod"; "493"; "/test-command"];
1460        ["command_lines"; "/test-command 9"]], ["";""]);
1461     InitBasicFS, Always, TestOutputList (
1462       [["upload"; "test-command"; "/test-command"];
1463        ["chmod"; "493"; "/test-command"];
1464        ["command_lines"; "/test-command 10"]], ["Result10-1";"Result10-2"]);
1465     InitBasicFS, Always, TestOutputList (
1466       [["upload"; "test-command"; "/test-command"];
1467        ["chmod"; "493"; "/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 ]
2680
2681 let all_functions = non_daemon_functions @ daemon_functions
2682
2683 (* In some places we want the functions to be displayed sorted
2684  * alphabetically, so this is useful:
2685  *)
2686 let all_functions_sorted =
2687   List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) ->
2688                compare n1 n2) all_functions
2689
2690 (* Column names and types from LVM PVs/VGs/LVs. *)
2691 let pv_cols = [
2692   "pv_name", `String;
2693   "pv_uuid", `UUID;
2694   "pv_fmt", `String;
2695   "pv_size", `Bytes;
2696   "dev_size", `Bytes;
2697   "pv_free", `Bytes;
2698   "pv_used", `Bytes;
2699   "pv_attr", `String (* XXX *);
2700   "pv_pe_count", `Int;
2701   "pv_pe_alloc_count", `Int;
2702   "pv_tags", `String;
2703   "pe_start", `Bytes;
2704   "pv_mda_count", `Int;
2705   "pv_mda_free", `Bytes;
2706 (* Not in Fedora 10:
2707   "pv_mda_size", `Bytes;
2708 *)
2709 ]
2710 let vg_cols = [
2711   "vg_name", `String;
2712   "vg_uuid", `UUID;
2713   "vg_fmt", `String;
2714   "vg_attr", `String (* XXX *);
2715   "vg_size", `Bytes;
2716   "vg_free", `Bytes;
2717   "vg_sysid", `String;
2718   "vg_extent_size", `Bytes;
2719   "vg_extent_count", `Int;
2720   "vg_free_count", `Int;
2721   "max_lv", `Int;
2722   "max_pv", `Int;
2723   "pv_count", `Int;
2724   "lv_count", `Int;
2725   "snap_count", `Int;
2726   "vg_seqno", `Int;
2727   "vg_tags", `String;
2728   "vg_mda_count", `Int;
2729   "vg_mda_free", `Bytes;
2730 (* Not in Fedora 10:
2731   "vg_mda_size", `Bytes;
2732 *)
2733 ]
2734 let lv_cols = [
2735   "lv_name", `String;
2736   "lv_uuid", `UUID;
2737   "lv_attr", `String (* XXX *);
2738   "lv_major", `Int;
2739   "lv_minor", `Int;
2740   "lv_kernel_major", `Int;
2741   "lv_kernel_minor", `Int;
2742   "lv_size", `Bytes;
2743   "seg_count", `Int;
2744   "origin", `String;
2745   "snap_percent", `OptPercent;
2746   "copy_percent", `OptPercent;
2747   "move_pv", `String;
2748   "lv_tags", `String;
2749   "mirror_log", `String;
2750   "modules", `String;
2751 ]
2752
2753 (* Column names and types from stat structures.
2754  * NB. Can't use things like 'st_atime' because glibc header files
2755  * define some of these as macros.  Ugh.
2756  *)
2757 let stat_cols = [
2758   "dev", `Int;
2759   "ino", `Int;
2760   "mode", `Int;
2761   "nlink", `Int;
2762   "uid", `Int;
2763   "gid", `Int;
2764   "rdev", `Int;
2765   "size", `Int;
2766   "blksize", `Int;
2767   "blocks", `Int;
2768   "atime", `Int;
2769   "mtime", `Int;
2770   "ctime", `Int;
2771 ]
2772 let statvfs_cols = [
2773   "bsize", `Int;
2774   "frsize", `Int;
2775   "blocks", `Int;
2776   "bfree", `Int;
2777   "bavail", `Int;
2778   "files", `Int;
2779   "ffree", `Int;
2780   "favail", `Int;
2781   "fsid", `Int;
2782   "flag", `Int;
2783   "namemax", `Int;
2784 ]
2785
2786 (* Used for testing language bindings. *)
2787 type callt =
2788   | CallString of string
2789   | CallOptString of string option
2790   | CallStringList of string list
2791   | CallInt of int
2792   | CallBool of bool
2793
2794 (* Useful functions.
2795  * Note we don't want to use any external OCaml libraries which
2796  * makes this a bit harder than it should be.
2797  *)
2798 let failwithf fs = ksprintf failwith fs
2799
2800 let replace_char s c1 c2 =
2801   let s2 = String.copy s in
2802   let r = ref false in
2803   for i = 0 to String.length s2 - 1 do
2804     if String.unsafe_get s2 i = c1 then (
2805       String.unsafe_set s2 i c2;
2806       r := true
2807     )
2808   done;
2809   if not !r then s else s2
2810
2811 let isspace c =
2812   c = ' '
2813   (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
2814
2815 let triml ?(test = isspace) str =
2816   let i = ref 0 in
2817   let n = ref (String.length str) in
2818   while !n > 0 && test str.[!i]; do
2819     decr n;
2820     incr i
2821   done;
2822   if !i = 0 then str
2823   else String.sub str !i !n
2824
2825 let trimr ?(test = isspace) str =
2826   let n = ref (String.length str) in
2827   while !n > 0 && test str.[!n-1]; do
2828     decr n
2829   done;
2830   if !n = String.length str then str
2831   else String.sub str 0 !n
2832
2833 let trim ?(test = isspace) str =
2834   trimr ~test (triml ~test str)
2835
2836 let rec find s sub =
2837   let len = String.length s in
2838   let sublen = String.length sub in
2839   let rec loop i =
2840     if i <= len-sublen then (
2841       let rec loop2 j =
2842         if j < sublen then (
2843           if s.[i+j] = sub.[j] then loop2 (j+1)
2844           else -1
2845         ) else
2846           i (* found *)
2847       in
2848       let r = loop2 0 in
2849       if r = -1 then loop (i+1) else r
2850     ) else
2851       -1 (* not found *)
2852   in
2853   loop 0
2854
2855 let rec replace_str s s1 s2 =
2856   let len = String.length s in
2857   let sublen = String.length s1 in
2858   let i = find s s1 in
2859   if i = -1 then s
2860   else (
2861     let s' = String.sub s 0 i in
2862     let s'' = String.sub s (i+sublen) (len-i-sublen) in
2863     s' ^ s2 ^ replace_str s'' s1 s2
2864   )
2865
2866 let rec string_split sep str =
2867   let len = String.length str in
2868   let seplen = String.length sep in
2869   let i = find str sep in
2870   if i = -1 then [str]
2871   else (
2872     let s' = String.sub str 0 i in
2873     let s'' = String.sub str (i+seplen) (len-i-seplen) in
2874     s' :: string_split sep s''
2875   )
2876
2877 let files_equal n1 n2 =
2878   let cmd = sprintf "cmp -s %s %s" (Filename.quote n1) (Filename.quote n2) in
2879   match Sys.command cmd with
2880   | 0 -> true
2881   | 1 -> false
2882   | i -> failwithf "%s: failed with error code %d" cmd i
2883
2884 let rec find_map f = function
2885   | [] -> raise Not_found
2886   | x :: xs ->
2887       match f x with
2888       | Some y -> y
2889       | None -> find_map f xs
2890
2891 let iteri f xs =
2892   let rec loop i = function
2893     | [] -> ()
2894     | x :: xs -> f i x; loop (i+1) xs
2895   in
2896   loop 0 xs
2897
2898 let mapi f xs =
2899   let rec loop i = function
2900     | [] -> []
2901     | x :: xs -> let r = f i x in r :: loop (i+1) xs
2902   in
2903   loop 0 xs
2904
2905 let name_of_argt = function
2906   | String n | OptString n | StringList n | Bool n | Int n
2907   | FileIn n | FileOut n -> n
2908
2909 let seq_of_test = function
2910   | TestRun s | TestOutput (s, _) | TestOutputList (s, _)
2911   | TestOutputListOfDevices (s, _)
2912   | TestOutputInt (s, _) | TestOutputTrue s | TestOutputFalse s
2913   | TestOutputLength (s, _) | TestOutputStruct (s, _)
2914   | TestLastFail s -> s
2915
2916 (* Check function names etc. for consistency. *)
2917 let check_functions () =
2918   let contains_uppercase str =
2919     let len = String.length str in
2920     let rec loop i =
2921       if i >= len then false
2922       else (
2923         let c = str.[i] in
2924         if c >= 'A' && c <= 'Z' then true
2925         else loop (i+1)
2926       )
2927     in
2928     loop 0
2929   in
2930
2931   (* Check function names. *)
2932   List.iter (
2933     fun (name, _, _, _, _, _, _) ->
2934       if String.length name >= 7 && String.sub name 0 7 = "guestfs" then
2935         failwithf "function name %s does not need 'guestfs' prefix" name;
2936       if name = "" then
2937         failwithf "function name is empty";
2938       if name.[0] < 'a' || name.[0] > 'z' then
2939         failwithf "function name %s must start with lowercase a-z" name;
2940       if String.contains name '-' then
2941         failwithf "function name %s should not contain '-', use '_' instead."
2942           name
2943   ) all_functions;
2944
2945   (* Check function parameter/return names. *)
2946   List.iter (
2947     fun (name, style, _, _, _, _, _) ->
2948       let check_arg_ret_name n =
2949         if contains_uppercase n then
2950           failwithf "%s param/ret %s should not contain uppercase chars"
2951             name n;
2952         if String.contains n '-' || String.contains n '_' then
2953           failwithf "%s param/ret %s should not contain '-' or '_'"
2954             name n;
2955         if n = "value" then
2956           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;
2957         if n = "int" || n = "char" || n = "short" || n = "long" then
2958           failwithf "%s has a param/ret which conflicts with a C type (eg. 'int', 'char' etc.)" name;
2959         if n = "i" || n = "n" then
2960           failwithf "%s has a param/ret called 'i' or 'n', which will cause some conflicts in the generated code" name;
2961         if n = "argv" || n = "args" then
2962           failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" name
2963       in
2964
2965       (match fst style with
2966        | RErr -> ()
2967        | RInt n | RInt64 n | RBool n | RConstString n | RString n
2968        | RStringList n | RPVList n | RVGList n | RLVList n
2969        | RStat n | RStatVFS n
2970        | RHashtable n ->
2971            check_arg_ret_name n
2972        | RIntBool (n,m) ->
2973            check_arg_ret_name n;
2974            check_arg_ret_name m
2975       );
2976       List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) (snd style)
2977   ) all_functions;
2978
2979   (* Check short descriptions. *)
2980   List.iter (
2981     fun (name, _, _, _, _, shortdesc, _) ->
2982       if shortdesc.[0] <> Char.lowercase shortdesc.[0] then
2983         failwithf "short description of %s should begin with lowercase." name;
2984       let c = shortdesc.[String.length shortdesc-1] in
2985       if c = '\n' || c = '.' then
2986         failwithf "short description of %s should not end with . or \\n." name
2987   ) all_functions;
2988
2989   (* Check long dscriptions. *)
2990   List.iter (
2991     fun (name, _, _, _, _, _, longdesc) ->
2992       if longdesc.[String.length longdesc-1] = '\n' then
2993         failwithf "long description of %s should not end with \\n." name
2994   ) all_functions;
2995
2996   (* Check proc_nrs. *)
2997   List.iter (
2998     fun (name, _, proc_nr, _, _, _, _) ->
2999       if proc_nr <= 0 then
3000         failwithf "daemon function %s should have proc_nr > 0" name
3001   ) daemon_functions;
3002
3003   List.iter (
3004     fun (name, _, proc_nr, _, _, _, _) ->
3005       if proc_nr <> -1 then
3006         failwithf "non-daemon function %s should have proc_nr -1" name
3007   ) non_daemon_functions;
3008
3009   let proc_nrs =
3010     List.map (fun (name, _, proc_nr, _, _, _, _) -> name, proc_nr)
3011       daemon_functions in
3012   let proc_nrs =
3013     List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
3014   let rec loop = function
3015     | [] -> ()
3016     | [_] -> ()
3017     | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
3018         loop rest
3019     | (name1,nr1) :: (name2,nr2) :: _ ->
3020         failwithf "%s and %s have conflicting procedure numbers (%d, %d)"
3021           name1 name2 nr1 nr2
3022   in
3023   loop proc_nrs;
3024
3025   (* Check tests. *)
3026   List.iter (
3027     function
3028       (* Ignore functions that have no tests.  We generate a
3029        * warning when the user does 'make check' instead.
3030        *)
3031     | name, _, _, _, [], _, _ -> ()
3032     | name, _, _, _, tests, _, _ ->
3033         let funcs =
3034           List.map (
3035             fun (_, _, test) ->
3036               match seq_of_test test with
3037               | [] ->
3038                   failwithf "%s has a test containing an empty sequence" name
3039               | cmds -> List.map List.hd cmds
3040           ) tests in
3041         let funcs = List.flatten funcs in
3042
3043         let tested = List.mem name funcs in
3044
3045         if not tested then
3046           failwithf "function %s has tests but does not test itself" name
3047   ) all_functions
3048
3049 (* 'pr' prints to the current output file. *)
3050 let chan = ref stdout
3051 let pr fs = ksprintf (output_string !chan) fs
3052
3053 (* Generate a header block in a number of standard styles. *)
3054 type comment_style = CStyle | HashStyle | OCamlStyle | HaskellStyle
3055 type license = GPLv2 | LGPLv2
3056
3057 let generate_header comment license =
3058   let c = match comment with
3059     | CStyle ->     pr "/* "; " *"
3060     | HashStyle ->  pr "# ";  "#"
3061     | OCamlStyle -> pr "(* "; " *"
3062     | HaskellStyle -> pr "{- "; "  " in
3063   pr "libguestfs generated file\n";
3064   pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
3065   pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
3066   pr "%s\n" c;
3067   pr "%s Copyright (C) 2009 Red Hat Inc.\n" c;
3068   pr "%s\n" c;
3069   (match license with
3070    | GPLv2 ->
3071        pr "%s This program is free software; you can redistribute it and/or modify\n" c;
3072        pr "%s it under the terms of the GNU General Public License as published by\n" c;
3073        pr "%s the Free Software Foundation; either version 2 of the License, or\n" c;
3074        pr "%s (at your option) any later version.\n" c;
3075        pr "%s\n" c;
3076        pr "%s This program is distributed in the hope that it will be useful,\n" c;
3077        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
3078        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" c;
3079        pr "%s GNU General Public License for more details.\n" c;
3080        pr "%s\n" c;
3081        pr "%s You should have received a copy of the GNU General Public License along\n" c;
3082        pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c;
3083        pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c;
3084
3085    | LGPLv2 ->
3086        pr "%s This library is free software; you can redistribute it and/or\n" c;
3087        pr "%s modify it under the terms of the GNU Lesser General Public\n" c;
3088        pr "%s License as published by the Free Software Foundation; either\n" c;
3089        pr "%s version 2 of the License, or (at your option) any later version.\n" c;
3090        pr "%s\n" c;
3091        pr "%s This library is distributed in the hope that it will be useful,\n" c;
3092        pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
3093        pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n" c;
3094        pr "%s Lesser General Public License for more details.\n" c;
3095        pr "%s\n" c;
3096        pr "%s You should have received a copy of the GNU Lesser General Public\n" c;
3097        pr "%s License along with this library; if not, write to the Free Software\n" c;
3098        pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c;
3099   );
3100   (match comment with
3101    | CStyle -> pr " */\n"
3102    | HashStyle -> ()
3103    | OCamlStyle -> pr " *)\n"
3104    | HaskellStyle -> pr "-}\n"
3105   );
3106   pr "\n"
3107
3108 (* Start of main code generation functions below this line. *)
3109
3110 (* Generate the pod documentation for the C API. *)
3111 let rec generate_actions_pod () =
3112   List.iter (
3113     fun (shortname, style, _, flags, _, _, longdesc) ->
3114       if not (List.mem NotInDocs flags) then (
3115         let name = "guestfs_" ^ shortname in
3116         pr "=head2 %s\n\n" name;
3117         pr " ";
3118         generate_prototype ~extern:false ~handle:"handle" name style;
3119         pr "\n\n";
3120         pr "%s\n\n" longdesc;
3121         (match fst style with
3122          | RErr ->
3123              pr "This function returns 0 on success or -1 on error.\n\n"
3124          | RInt _ ->
3125              pr "On error this function returns -1.\n\n"
3126          | RInt64 _ ->
3127              pr "On error this function returns -1.\n\n"
3128          | RBool _ ->
3129              pr "This function returns a C truth value on success or -1 on error.\n\n"
3130          | RConstString _ ->
3131              pr "This function returns a string, or NULL on error.
3132 The string is owned by the guest handle and must I<not> be freed.\n\n"
3133          | RString _ ->
3134              pr "This function returns a string, or NULL on error.
3135 I<The caller must free the returned string after use>.\n\n"
3136          | RStringList _ ->
3137              pr "This function returns a NULL-terminated array of strings
3138 (like L<environ(3)>), or NULL if there was an error.
3139 I<The caller must free the strings and the array after use>.\n\n"
3140          | RIntBool _ ->
3141              pr "This function returns a C<struct guestfs_int_bool *>,
3142 or NULL if there was an error.
3143 I<The caller must call C<guestfs_free_int_bool> after use>.\n\n"
3144          | RPVList _ ->
3145              pr "This function returns a C<struct guestfs_lvm_pv_list *>
3146 (see E<lt>guestfs-structs.hE<gt>),
3147 or NULL if there was an error.
3148 I<The caller must call C<guestfs_free_lvm_pv_list> after use>.\n\n"
3149          | RVGList _ ->
3150              pr "This function returns a C<struct guestfs_lvm_vg_list *>
3151 (see E<lt>guestfs-structs.hE<gt>),
3152 or NULL if there was an error.
3153 I<The caller must call C<guestfs_free_lvm_vg_list> after use>.\n\n"
3154          | RLVList _ ->
3155              pr "This function returns a C<struct guestfs_lvm_lv_list *>
3156 (see E<lt>guestfs-structs.hE<gt>),
3157 or NULL if there was an error.
3158 I<The caller must call C<guestfs_free_lvm_lv_list> after use>.\n\n"
3159          | RStat _ ->
3160              pr "This function returns a C<struct guestfs_stat *>
3161 (see L<stat(2)> and E<lt>guestfs-structs.hE<gt>),
3162 or NULL if there was an error.
3163 I<The caller must call C<free> after use>.\n\n"
3164          | RStatVFS _ ->
3165              pr "This function returns a C<struct guestfs_statvfs *>
3166 (see L<statvfs(2)> and E<lt>guestfs-structs.hE<gt>),
3167 or NULL if there was an error.
3168 I<The caller must call C<free> after use>.\n\n"
3169          | RHashtable _ ->
3170              pr "This function returns a NULL-terminated array of
3171 strings, or NULL if there was an error.
3172 The array of strings will always have length C<2n+1>, where
3173 C<n> keys and values alternate, followed by the trailing NULL entry.
3174 I<The caller must free the strings and the array after use>.\n\n"
3175         );
3176         if List.mem ProtocolLimitWarning flags then
3177           pr "%s\n\n" protocol_limit_warning;
3178         if List.mem DangerWillRobinson flags then
3179           pr "%s\n\n" danger_will_robinson
3180       )
3181   ) all_functions_sorted
3182
3183 and generate_structs_pod () =
3184   (* LVM structs documentation. *)
3185   List.iter (
3186     fun (typ, cols) ->
3187       pr "=head2 guestfs_lvm_%s\n" typ;
3188       pr "\n";
3189       pr " struct guestfs_lvm_%s {\n" typ;
3190       List.iter (
3191         function
3192         | name, `String -> pr "  char *%s;\n" name
3193         | name, `UUID ->
3194             pr "  /* The next field is NOT nul-terminated, be careful when printing it: */\n";
3195             pr "  char %s[32];\n" name
3196         | name, `Bytes -> pr "  uint64_t %s;\n" name
3197         | name, `Int -> pr "  int64_t %s;\n" name
3198         | name, `OptPercent ->
3199             pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
3200             pr "  float %s;\n" name
3201       ) cols;
3202       pr " \n";
3203       pr " struct guestfs_lvm_%s_list {\n" typ;
3204       pr "   uint32_t len; /* Number of elements in list. */\n";
3205       pr "   struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
3206       pr " };\n";
3207       pr " \n";
3208       pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
3209         typ typ;
3210       pr "\n"
3211   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
3212
3213 (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
3214  * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'.
3215  *
3216  * We have to use an underscore instead of a dash because otherwise
3217  * rpcgen generates incorrect code.
3218  *
3219  * This header is NOT exported to clients, but see also generate_structs_h.
3220  *)
3221 and generate_xdr () =
3222   generate_header CStyle LGPLv2;
3223
3224   (* This has to be defined to get around a limitation in Sun's rpcgen. *)
3225   pr "typedef string str<>;\n";
3226   pr "\n";
3227
3228   (* LVM internal structures. *)
3229   List.iter (
3230     function
3231     | typ, cols ->
3232         pr "struct guestfs_lvm_int_%s {\n" typ;
3233         List.iter (function
3234                    | name, `String -> pr "  string %s<>;\n" name
3235                    | name, `UUID -> pr "  opaque %s[32];\n" name
3236                    | name, `Bytes -> pr "  hyper %s;\n" name
3237                    | name, `Int -> pr "  hyper %s;\n" name
3238                    | name, `OptPercent -> pr "  float %s;\n" name
3239                   ) cols;
3240         pr "};\n";
3241         pr "\n";
3242         pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
3243         pr "\n";
3244   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
3245
3246   (* Stat internal structures. *)
3247   List.iter (
3248     function
3249     | typ, cols ->
3250         pr "struct guestfs_int_%s {\n" typ;
3251         List.iter (function
3252                    | name, `Int -> pr "  hyper %s;\n" name
3253                   ) cols;
3254         pr "};\n";
3255         pr "\n";
3256   ) ["stat", stat_cols; "statvfs", statvfs_cols];
3257
3258   List.iter (
3259     fun (shortname, style, _, _, _, _, _) ->
3260       let name = "guestfs_" ^ shortname in
3261
3262       (match snd style with
3263        | [] -> ()
3264        | args ->
3265            pr "struct %s_args {\n" name;
3266            List.iter (
3267              function
3268              | String n -> pr "  string %s<>;\n" n
3269              | OptString n -> pr "  str *%s;\n" n
3270              | StringList n -> pr "  str %s<>;\n" n
3271              | Bool n -> pr "  bool %s;\n" n
3272              | Int n -> pr "  int %s;\n" n
3273              | FileIn _ | FileOut _ -> ()
3274            ) args;
3275            pr "};\n\n"
3276       );
3277       (match fst style with
3278        | RErr -> ()
3279        | RInt n ->
3280            pr "struct %s_ret {\n" name;
3281            pr "  int %s;\n" n;
3282            pr "};\n\n"
3283        | RInt64 n ->
3284            pr "struct %s_ret {\n" name;
3285            pr "  hyper %s;\n" n;
3286            pr "};\n\n"
3287        | RBool n ->
3288            pr "struct %s_ret {\n" name;
3289            pr "  bool %s;\n" n;
3290            pr "};\n\n"
3291        | RConstString _ ->
3292            failwithf "RConstString cannot be returned from a daemon function"
3293        | RString n ->
3294            pr "struct %s_ret {\n" name;
3295            pr "  string %s<>;\n" n;
3296            pr "};\n\n"
3297        | RStringList n ->
3298            pr "struct %s_ret {\n" name;
3299            pr "  str %s<>;\n" n;
3300            pr "};\n\n"
3301        | RIntBool (n,m) ->
3302            pr "struct %s_ret {\n" name;
3303            pr "  int %s;\n" n;
3304            pr "  bool %s;\n" m;
3305            pr "};\n\n"
3306        | RPVList n ->
3307            pr "struct %s_ret {\n" name;
3308            pr "  guestfs_lvm_int_pv_list %s;\n" n;
3309            pr "};\n\n"
3310        | RVGList n ->
3311            pr "struct %s_ret {\n" name;
3312            pr "  guestfs_lvm_int_vg_list %s;\n" n;
3313            pr "};\n\n"
3314        | RLVList n ->
3315            pr "struct %s_ret {\n" name;
3316            pr "  guestfs_lvm_int_lv_list %s;\n" n;
3317            pr "};\n\n"
3318        | RStat n ->
3319            pr "struct %s_ret {\n" name;
3320            pr "  guestfs_int_stat %s;\n" n;
3321            pr "};\n\n"
3322        | RStatVFS n ->
3323            pr "struct %s_ret {\n" name;
3324            pr "  guestfs_int_statvfs %s;\n" n;
3325            pr "};\n\n"
3326        | RHashtable n ->
3327            pr "struct %s_ret {\n" name;
3328            pr "  str %s<>;\n" n;
3329            pr "};\n\n"
3330       );
3331   ) daemon_functions;
3332
3333   (* Table of procedure numbers. *)
3334   pr "enum guestfs_procedure {\n";
3335   List.iter (
3336     fun (shortname, _, proc_nr, _, _, _, _) ->
3337       pr "  GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
3338   ) daemon_functions;
3339   pr "  GUESTFS_PROC_NR_PROCS\n";
3340   pr "};\n";
3341   pr "\n";
3342
3343   (* Having to choose a maximum message size is annoying for several
3344    * reasons (it limits what we can do in the API), but it (a) makes
3345    * the protocol a lot simpler, and (b) provides a bound on the size
3346    * of the daemon which operates in limited memory space.  For large
3347    * file transfers you should use FTP.
3348    *)
3349   pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
3350   pr "\n";
3351
3352   (* Message header, etc. *)
3353   pr "\
3354 /* The communication protocol is now documented in the guestfs(3)
3355  * manpage.
3356  */
3357
3358 const GUESTFS_PROGRAM = 0x2000F5F5;
3359 const GUESTFS_PROTOCOL_VERSION = 1;
3360
3361 /* These constants must be larger than any possible message length. */
3362 const GUESTFS_LAUNCH_FLAG = 0xf5f55ff5;
3363 const GUESTFS_CANCEL_FLAG = 0xffffeeee;
3364
3365 enum guestfs_message_direction {
3366   GUESTFS_DIRECTION_CALL = 0,        /* client -> daemon */
3367   GUESTFS_DIRECTION_REPLY = 1        /* daemon -> client */
3368 };
3369
3370 enum guestfs_message_status {
3371   GUESTFS_STATUS_OK = 0,
3372   GUESTFS_STATUS_ERROR = 1
3373 };
3374
3375 const GUESTFS_ERROR_LEN = 256;
3376
3377 struct guestfs_message_error {
3378   string error_message<GUESTFS_ERROR_LEN>;
3379 };
3380
3381 struct guestfs_message_header {
3382   unsigned prog;                     /* GUESTFS_PROGRAM */
3383   unsigned vers;                     /* GUESTFS_PROTOCOL_VERSION */
3384   guestfs_procedure proc;            /* GUESTFS_PROC_x */
3385   guestfs_message_direction direction;
3386   unsigned serial;                   /* message serial number */
3387   guestfs_message_status status;
3388 };
3389
3390 const GUESTFS_MAX_CHUNK_SIZE = 8192;
3391
3392 struct guestfs_chunk {
3393   int cancel;                        /* if non-zero, transfer is cancelled */
3394   /* data size is 0 bytes if the transfer has finished successfully */
3395   opaque data<GUESTFS_MAX_CHUNK_SIZE>;
3396 };
3397 "
3398
3399 (* Generate the guestfs-structs.h file. *)
3400 and generate_structs_h () =
3401   generate_header CStyle LGPLv2;
3402
3403   (* This is a public exported header file containing various
3404    * structures.  The structures are carefully written to have
3405    * exactly the same in-memory format as the XDR structures that
3406    * we use on the wire to the daemon.  The reason for creating
3407    * copies of these structures here is just so we don't have to
3408    * export the whole of guestfs_protocol.h (which includes much
3409    * unrelated and XDR-dependent stuff that we don't want to be
3410    * public, or required by clients).
3411    *
3412    * To reiterate, we will pass these structures to and from the
3413    * client with a simple assignment or memcpy, so the format
3414    * must be identical to what rpcgen / the RFC defines.
3415    *)
3416
3417   (* guestfs_int_bool structure. *)
3418   pr "struct guestfs_int_bool {\n";
3419   pr "  int32_t i;\n";
3420   pr "  int32_t b;\n";
3421   pr "};\n";
3422   pr "\n";
3423
3424   (* LVM public structures. *)
3425   List.iter (
3426     function
3427     | typ, cols ->
3428         pr "struct guestfs_lvm_%s {\n" typ;
3429         List.iter (
3430           function
3431           | name, `String -> pr "  char *%s;\n" name
3432           | name, `UUID -> pr "  char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
3433           | name, `Bytes -> pr "  uint64_t %s;\n" name
3434           | name, `Int -> pr "  int64_t %s;\n" name
3435           | name, `OptPercent -> pr "  float %s; /* [0..100] or -1 */\n" name
3436         ) cols;
3437         pr "};\n";
3438         pr "\n";
3439         pr "struct guestfs_lvm_%s_list {\n" typ;
3440         pr "  uint32_t len;\n";
3441         pr "  struct guestfs_lvm_%s *val;\n" typ;
3442         pr "};\n";
3443         pr "\n"
3444   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
3445
3446   (* Stat structures. *)
3447   List.iter (
3448     function
3449     | typ, cols ->
3450         pr "struct guestfs_%s {\n" typ;
3451         List.iter (
3452           function
3453           | name, `Int -> pr "  int64_t %s;\n" name
3454         ) cols;
3455         pr "};\n";
3456         pr "\n"
3457   ) ["stat", stat_cols; "statvfs", statvfs_cols]
3458
3459 (* Generate the guestfs-actions.h file. *)
3460 and generate_actions_h () =
3461   generate_header CStyle LGPLv2;
3462   List.iter (
3463     fun (shortname, style, _, _, _, _, _) ->
3464       let name = "guestfs_" ^ shortname in
3465       generate_prototype ~single_line:true ~newline:true ~handle:"handle"
3466         name style
3467   ) all_functions
3468
3469 (* Generate the client-side dispatch stubs. *)
3470 and generate_client_actions () =
3471   generate_header CStyle LGPLv2;
3472
3473   pr "\
3474 #include <stdio.h>
3475 #include <stdlib.h>
3476
3477 #include \"guestfs.h\"
3478 #include \"guestfs_protocol.h\"
3479
3480 #define error guestfs_error
3481 #define perrorf guestfs_perrorf
3482 #define safe_malloc guestfs_safe_malloc
3483 #define safe_realloc guestfs_safe_realloc
3484 #define safe_strdup guestfs_safe_strdup
3485 #define safe_memdup guestfs_safe_memdup
3486
3487 /* Check the return message from a call for validity. */
3488 static int
3489 check_reply_header (guestfs_h *g,
3490                     const struct guestfs_message_header *hdr,
3491                     int proc_nr, int serial)
3492 {
3493   if (hdr->prog != GUESTFS_PROGRAM) {
3494     error (g, \"wrong program (%%d/%%d)\", hdr->prog, GUESTFS_PROGRAM);
3495     return -1;
3496   }
3497   if (hdr->vers != GUESTFS_PROTOCOL_VERSION) {
3498     error (g, \"wrong protocol version (%%d/%%d)\",
3499            hdr->vers, GUESTFS_PROTOCOL_VERSION);
3500     return -1;
3501   }
3502   if (hdr->direction != GUESTFS_DIRECTION_REPLY) {
3503     error (g, \"unexpected message direction (%%d/%%d)\",
3504            hdr->direction, GUESTFS_DIRECTION_REPLY);
3505     return -1;
3506   }
3507   if (hdr->proc != proc_nr) {
3508     error (g, \"unexpected procedure number (%%d/%%d)\", hdr->proc, proc_nr);
3509     return -1;
3510   }
3511   if (hdr->serial != serial) {
3512     error (g, \"unexpected serial (%%d/%%d)\", hdr->serial, serial);
3513     return -1;
3514   }
3515
3516   return 0;
3517 }
3518
3519 /* Check we are in the right state to run a high-level action. */
3520 static int
3521 check_state (guestfs_h *g, const char *caller)
3522 {
3523   if (!guestfs_is_ready (g)) {
3524     if (guestfs_is_config (g))
3525       error (g, \"%%s: call launch() before using this function\",
3526         caller);
3527     else if (guestfs_is_launching (g))
3528       error (g, \"%%s: call wait_ready() before using this function\",
3529         caller);
3530     else
3531       error (g, \"%%s called from the wrong state, %%d != READY\",
3532         caller, guestfs_get_state (g));
3533     return -1;
3534   }
3535   return 0;
3536 }
3537
3538 ";
3539
3540   (* Client-side stubs for each function. *)
3541   List.iter (
3542     fun (shortname, style, _, _, _, _, _) ->
3543       let name = "guestfs_" ^ shortname in
3544
3545       (* Generate the context struct which stores the high-level
3546        * state between callback functions.
3547        *)
3548       pr "struct %s_ctx {\n" shortname;
3549       pr "  /* This flag is set by the callbacks, so we know we've done\n";
3550       pr "   * the callbacks as expected, and in the right sequence.\n";
3551       pr "   * 0 = not called, 1 = reply_cb called.\n";
3552       pr "   */\n";
3553       pr "  int cb_sequence;\n";
3554       pr "  struct guestfs_message_header hdr;\n";
3555       pr "  struct guestfs_message_error err;\n";
3556       (match fst style with
3557        | RErr -> ()
3558        | RConstString _ ->
3559            failwithf "RConstString cannot be returned from a daemon function"
3560        | RInt _ | RInt64 _
3561        | RBool _ | RString _ | RStringList _
3562        | RIntBool _
3563        | RPVList _ | RVGList _ | RLVList _
3564        | RStat _ | RStatVFS _
3565        | RHashtable _ ->
3566            pr "  struct %s_ret ret;\n" name
3567       );
3568       pr "};\n";
3569       pr "\n";
3570
3571       (* Generate the reply callback function. *)
3572       pr "static void %s_reply_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
3573       pr "{\n";
3574       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
3575       pr "  struct %s_ctx *ctx = (struct %s_ctx *) data;\n" shortname shortname;
3576       pr "\n";
3577       pr "  /* This should definitely not happen. */\n";
3578       pr "  if (ctx->cb_sequence != 0) {\n";
3579       pr "    ctx->cb_sequence = 9999;\n";
3580       pr "    error (g, \"%%s: internal error: reply callback called twice\", \"%s\");\n" name;
3581       pr "    return;\n";
3582       pr "  }\n";
3583       pr "\n";
3584       pr "  ml->main_loop_quit (ml, g);\n";
3585       pr "\n";
3586       pr "  if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) {\n";
3587       pr "    error (g, \"%%s: failed to parse reply header\", \"%s\");\n" name;
3588       pr "    return;\n";
3589       pr "  }\n";
3590       pr "  if (ctx->hdr.status == GUESTFS_STATUS_ERROR) {\n";
3591       pr "    if (!xdr_guestfs_message_error (xdr, &ctx->err)) {\n";
3592       pr "      error (g, \"%%s: failed to parse reply error\", \"%s\");\n"
3593         name;
3594       pr "      return;\n";
3595       pr "    }\n";
3596       pr "    goto done;\n";
3597       pr "  }\n";
3598
3599       (match fst style with
3600        | RErr -> ()
3601        | RConstString _ ->
3602            failwithf "RConstString cannot be returned from a daemon function"
3603        | RInt _ | RInt64 _
3604        | RBool _ | RString _ | RStringList _
3605        | RIntBool _
3606        | RPVList _ | RVGList _ | RLVList _
3607        | RStat _ | RStatVFS _
3608        | RHashtable _ ->
3609             pr "  if (!xdr_%s_ret (xdr, &ctx->ret)) {\n" name;
3610             pr "    error (g, \"%%s: failed to parse reply\", \"%s\");\n" name;
3611             pr "    return;\n";
3612             pr "  }\n";
3613       );
3614
3615       pr " done:\n";
3616       pr "  ctx->cb_sequence = 1;\n";
3617       pr "}\n\n";
3618
3619       (* Generate the action stub. *)
3620       generate_prototype ~extern:false ~semicolon:false ~newline:true
3621         ~handle:"g" name style;
3622
3623       let error_code =
3624         match fst style with
3625         | RErr | RInt _ | RInt64 _ | RBool _ -> "-1"
3626         | RConstString _ ->
3627             failwithf "RConstString cannot be returned from a daemon function"
3628         | RString _ | RStringList _ | RIntBool _
3629         | RPVList _ | RVGList _ | RLVList _
3630         | RStat _ | RStatVFS _
3631         | RHashtable _ ->
3632             "NULL" in
3633
3634       pr "{\n";
3635
3636       (match snd style with
3637        | [] -> ()
3638        | _ -> pr "  struct %s_args args;\n" name
3639       );
3640
3641       pr "  struct %s_ctx ctx;\n" shortname;
3642       pr "  guestfs_main_loop *ml = guestfs_get_main_loop (g);\n";
3643       pr "  int serial;\n";
3644       pr "\n";
3645       pr "  if (check_state (g, \"%s\") == -1) return %s;\n" name error_code;
3646       pr "  guestfs_set_busy (g);\n";
3647       pr "\n";
3648       pr "  memset (&ctx, 0, sizeof ctx);\n";
3649       pr "\n";
3650
3651       (* Send the main header and arguments. *)
3652       (match snd style with
3653        | [] ->
3654            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s, NULL, NULL);\n"
3655              (String.uppercase shortname)
3656        | args ->
3657            List.iter (
3658              function
3659              | String n ->
3660                  pr "  args.%s = (char *) %s;\n" n n
3661              | OptString n ->
3662                  pr "  args.%s = %s ? (char **) &%s : NULL;\n" n n n
3663              | StringList n ->
3664                  pr "  args.%s.%s_val = (char **) %s;\n" n n n;
3665                  pr "  for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
3666              | Bool n ->
3667                  pr "  args.%s = %s;\n" n n
3668              | Int n ->
3669                  pr "  args.%s = %s;\n" n n
3670              | FileIn _ | FileOut _ -> ()
3671            ) args;
3672            pr "  serial = guestfs__send_sync (g, GUESTFS_PROC_%s,\n"
3673              (String.uppercase shortname);
3674            pr "        (xdrproc_t) xdr_%s_args, (char *) &args);\n"
3675              name;
3676       );
3677       pr "  if (serial == -1) {\n";
3678       pr "    guestfs_end_busy (g);\n";
3679       pr "    return %s;\n" error_code;
3680       pr "  }\n";
3681       pr "\n";
3682
3683       (* Send any additional files (FileIn) requested. *)
3684       let need_read_reply_label = ref false in
3685       List.iter (
3686         function
3687         | FileIn n ->
3688             pr "  {\n";
3689             pr "    int r;\n";
3690             pr "\n";
3691             pr "    r = guestfs__send_file_sync (g, %s);\n" n;
3692             pr "    if (r == -1) {\n";
3693             pr "      guestfs_end_busy (g);\n";
3694             pr "      return %s;\n" error_code;
3695             pr "    }\n";
3696             pr "    if (r == -2) /* daemon cancelled */\n";
3697             pr "      goto read_reply;\n";
3698             need_read_reply_label := true;
3699             pr "  }\n";
3700             pr "\n";
3701         | _ -> ()
3702       ) (snd style);
3703
3704       (* Wait for the reply from the remote end. *)
3705       if !need_read_reply_label then pr " read_reply:\n";
3706       pr "  guestfs__switch_to_receiving (g);\n";
3707       pr "  ctx.cb_sequence = 0;\n";
3708       pr "  guestfs_set_reply_callback (g, %s_reply_cb, &ctx);\n" shortname;
3709       pr "  (void) ml->main_loop_run (ml, g);\n";
3710       pr "  guestfs_set_reply_callback (g, NULL, NULL);\n";
3711       pr "  if (ctx.cb_sequence != 1) {\n";
3712       pr "    error (g, \"%%s reply failed, see earlier error messages\", \"%s\");\n" name;
3713       pr "    guestfs_end_busy (g);\n";
3714       pr "    return %s;\n" error_code;
3715       pr "  }\n";
3716       pr "\n";
3717
3718       pr "  if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_%s, serial) == -1) {\n"
3719         (String.uppercase shortname);
3720       pr "    guestfs_end_busy (g);\n";
3721       pr "    return %s;\n" error_code;
3722       pr "  }\n";
3723       pr "\n";
3724
3725       pr "  if (ctx.hdr.status == GUESTFS_STATUS_ERROR) {\n";
3726       pr "    error (g, \"%%s\", ctx.err.error_message);\n";
3727       pr "    free (ctx.err.error_message);\n";
3728       pr "    guestfs_end_busy (g);\n";
3729       pr "    return %s;\n" error_code;
3730       pr "  }\n";
3731       pr "\n";
3732
3733       (* Expecting to receive further files (FileOut)? *)
3734       List.iter (
3735         function
3736         | FileOut n ->
3737             pr "  if (guestfs__receive_file_sync (g, %s) == -1) {\n" n;
3738             pr "    guestfs_end_busy (g);\n";
3739             pr "    return %s;\n" error_code;
3740             pr "  }\n";
3741             pr "\n";
3742         | _ -> ()
3743       ) (snd style);
3744
3745       pr "  guestfs_end_busy (g);\n";
3746
3747       (match fst style with
3748        | RErr -> pr "  return 0;\n"
3749        | RInt n | RInt64 n | RBool n ->
3750            pr "  return ctx.ret.%s;\n" n
3751        | RConstString _ ->
3752            failwithf "RConstString cannot be returned from a daemon function"
3753        | RString n ->
3754            pr "  return ctx.ret.%s; /* caller will free */\n" n
3755        | RStringList n | RHashtable n ->
3756            pr "  /* caller will free this, but we need to add a NULL entry */\n";
3757            pr "  ctx.ret.%s.%s_val =\n" n n;
3758            pr "    safe_realloc (g, ctx.ret.%s.%s_val,\n" n n;
3759            pr "                  sizeof (char *) * (ctx.ret.%s.%s_len + 1));\n"
3760              n n;
3761            pr "  ctx.ret.%s.%s_val[ctx.ret.%s.%s_len] = NULL;\n" n n n n;
3762            pr "  return ctx.ret.%s.%s_val;\n" n n
3763        | RIntBool _ ->
3764            pr "  /* caller with free this */\n";
3765            pr "  return safe_memdup (g, &ctx.ret, sizeof (ctx.ret));\n"
3766        | RPVList n | RVGList n | RLVList n
3767        | RStat n | RStatVFS n ->
3768            pr "  /* caller will free this */\n";
3769            pr "  return safe_memdup (g, &ctx.ret.%s, sizeof (ctx.ret.%s));\n" n n
3770       );
3771
3772       pr "}\n\n"
3773   ) daemon_functions
3774
3775 (* Generate daemon/actions.h. *)
3776 and generate_daemon_actions_h () =
3777   generate_header CStyle GPLv2;
3778
3779   pr "#include \"../src/guestfs_protocol.h\"\n";
3780   pr "\n";
3781
3782   List.iter (
3783     fun (name, style, _, _, _, _, _) ->
3784         generate_prototype
3785           ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
3786           name style;
3787   ) daemon_functions
3788
3789 (* Generate the server-side stubs. *)
3790 and generate_daemon_actions () =
3791   generate_header CStyle GPLv2;
3792
3793   pr "#include <config.h>\n";
3794   pr "\n";
3795   pr "#include <stdio.h>\n";
3796   pr "#include <stdlib.h>\n";
3797   pr "#include <string.h>\n";
3798   pr "#include <inttypes.h>\n";
3799   pr "#include <ctype.h>\n";
3800   pr "#include <rpc/types.h>\n";
3801   pr "#include <rpc/xdr.h>\n";
3802   pr "\n";
3803   pr "#include \"daemon.h\"\n";
3804   pr "#include \"../src/guestfs_protocol.h\"\n";
3805   pr "#include \"actions.h\"\n";
3806   pr "\n";
3807
3808   List.iter (
3809     fun (name, style, _, _, _, _, _) ->
3810       (* Generate server-side stubs. *)
3811       pr "static void %s_stub (XDR *xdr_in)\n" name;
3812       pr "{\n";
3813       let error_code =
3814         match fst style with
3815         | RErr | RInt _ -> pr "  int r;\n"; "-1"
3816         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
3817         | RBool _ -> pr "  int r;\n"; "-1"
3818         | RConstString _ ->
3819             failwithf "RConstString cannot be returned from a daemon function"
3820         | RString _ -> pr "  char *r;\n"; "NULL"
3821         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
3822         | RIntBool _ -> pr "  guestfs_%s_ret *r;\n" name; "NULL"
3823         | RPVList _ -> pr "  guestfs_lvm_int_pv_list *r;\n"; "NULL"
3824         | RVGList _ -> pr "  guestfs_lvm_int_vg_list *r;\n"; "NULL"
3825         | RLVList _ -> pr "  guestfs_lvm_int_lv_list *r;\n"; "NULL"
3826         | RStat _ -> pr "  guestfs_int_stat *r;\n"; "NULL"
3827         | RStatVFS _ -> pr "  guestfs_int_statvfs *r;\n"; "NULL" in
3828
3829       (match snd style with
3830        | [] -> ()
3831        | args ->
3832            pr "  struct guestfs_%s_args args;\n" name;
3833            List.iter (
3834              function
3835                (* Note we allow the string to be writable, in order to
3836                 * allow device name translation.  This is safe because
3837                 * we can modify the string (passed from RPC).
3838                 *)
3839              | String n
3840              | OptString n -> pr "  char *%s;\n" n
3841              | StringList n -> pr "  char **%s;\n" n
3842              | Bool n -> pr "  int %s;\n" n
3843              | Int n -> pr "  int %s;\n" n
3844              | FileIn _ | FileOut _ -> ()
3845            ) args
3846       );
3847       pr "\n";
3848
3849       (match snd style with
3850        | [] -> ()
3851        | args ->
3852            pr "  memset (&args, 0, sizeof args);\n";
3853            pr "\n";
3854            pr "  if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
3855            pr "    reply_with_error (\"%%s: daemon failed to decode procedure arguments\", \"%s\");\n" name;
3856            pr "    return;\n";
3857            pr "  }\n";
3858            List.iter (
3859              function
3860              | String n -> pr "  %s = args.%s;\n" n n
3861              | OptString n -> pr "  %s = args.%s ? *args.%s : NULL;\n" n n n
3862              | StringList n ->
3863                  pr "  %s = realloc (args.%s.%s_val,\n" n n n;
3864                  pr "                sizeof (char *) * (args.%s.%s_len+1));\n" n n;
3865                  pr "  if (%s == NULL) {\n" n;
3866                  pr "    reply_with_perror (\"realloc\");\n";
3867                  pr "    goto done;\n";
3868                  pr "  }\n";
3869                  pr "  %s[args.%s.%s_len] = NULL;\n" n n n;
3870                  pr "  args.%s.%s_val = %s;\n" n n n;
3871              | Bool n -> pr "  %s = args.%s;\n" n n
3872              | Int n -> pr "  %s = args.%s;\n" n n
3873              | FileIn _ | FileOut _ -> ()
3874            ) args;
3875            pr "\n"
3876       );
3877
3878       (* Don't want to call the impl with any FileIn or FileOut
3879        * parameters, since these go "outside" the RPC protocol.
3880        *)
3881       let argsnofile =
3882         List.filter (function FileIn _ | FileOut _ -> false | _ -> true)
3883           (snd style) in
3884       pr "  r = do_%s " name;
3885       generate_call_args argsnofile;
3886       pr ";\n";
3887
3888       pr "  if (r == %s)\n" error_code;
3889       pr "    /* do_%s has already called reply_with_error */\n" name;
3890       pr "    goto done;\n";
3891       pr "\n";
3892
3893       (* If there are any FileOut parameters, then the impl must
3894        * send its own reply.
3895        *)
3896       let no_reply =
3897         List.exists (function FileOut _ -> true | _ -> false) (snd style) in
3898       if no_reply then
3899         pr "  /* do_%s has already sent a reply */\n" name
3900       else (
3901         match fst style with
3902         | RErr -> pr "  reply (NULL, NULL);\n"
3903         | RInt n | RInt64 n | RBool n ->
3904             pr "  struct guestfs_%s_ret ret;\n" name;
3905             pr "  ret.%s = r;\n" n;
3906             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
3907               name
3908         | RConstString _ ->
3909             failwithf "RConstString cannot be returned from a daemon function"
3910         | RString n ->
3911             pr "  struct guestfs_%s_ret ret;\n" name;
3912             pr "  ret.%s = r;\n" n;
3913             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
3914               name;
3915             pr "  free (r);\n"
3916         | RStringList n | RHashtable n ->
3917             pr "  struct guestfs_%s_ret ret;\n" name;
3918             pr "  ret.%s.%s_len = count_strings (r);\n" n n;
3919             pr "  ret.%s.%s_val = r;\n" n n;
3920             pr "  reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
3921               name;
3922             pr "  free_strings (r);\n"
3923         | RIntBool _ ->
3924             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n"
3925               name;
3926             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name
3927         | RPVList n | RVGList n | RLVList n
3928         | RStat n | RStatVFS n ->
3929             pr "  struct guestfs_%s_ret ret;\n" name;
3930             pr "  ret.%s = *r;\n" n;
3931             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
3932               name;
3933             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
3934               name
3935       );
3936
3937       (* Free the args. *)
3938       (match snd style with
3939        | [] ->
3940            pr "done: ;\n";
3941        | _ ->
3942            pr "done:\n";
3943            pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
3944              name
3945       );
3946
3947       pr "}\n\n";
3948   ) daemon_functions;
3949
3950   (* Dispatch function. *)
3951   pr "void dispatch_incoming_message (XDR *xdr_in)\n";
3952   pr "{\n";
3953   pr "  switch (proc_nr) {\n";
3954
3955   List.iter (
3956     fun (name, style, _, _, _, _, _) ->
3957         pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
3958         pr "      %s_stub (xdr_in);\n" name;
3959         pr "      break;\n"
3960   ) daemon_functions;
3961
3962   pr "    default:\n";
3963   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";
3964   pr "  }\n";
3965   pr "}\n";
3966   pr "\n";
3967
3968   (* LVM columns and tokenization functions. *)
3969   (* XXX This generates crap code.  We should rethink how we
3970    * do this parsing.
3971    *)
3972   List.iter (
3973     function
3974     | typ, cols ->
3975         pr "static const char *lvm_%s_cols = \"%s\";\n"
3976           typ (String.concat "," (List.map fst cols));
3977         pr "\n";
3978
3979         pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
3980         pr "{\n";
3981         pr "  char *tok, *p, *next;\n";
3982         pr "  int i, j;\n";
3983         pr "\n";
3984         (*
3985         pr "  fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
3986         pr "\n";
3987         *)
3988         pr "  if (!str) {\n";
3989         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
3990         pr "    return -1;\n";
3991         pr "  }\n";
3992         pr "  if (!*str || isspace (*str)) {\n";
3993         pr "    fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
3994         pr "    return -1;\n";
3995         pr "  }\n";
3996         pr "  tok = str;\n";
3997         List.iter (
3998           fun (name, coltype) ->
3999             pr "  if (!tok) {\n";
4000             pr "    fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
4001             pr "    return -1;\n";
4002             pr "  }\n";
4003             pr "  p = strchrnul (tok, ',');\n";
4004             pr "  if (*p) next = p+1; else next = NULL;\n";
4005             pr "  *p = '\\0';\n";
4006             (match coltype with
4007              | `String ->
4008                  pr "  r->%s = strdup (tok);\n" name;
4009                  pr "  if (r->%s == NULL) {\n" name;
4010                  pr "    perror (\"strdup\");\n";
4011                  pr "    return -1;\n";
4012                  pr "  }\n"
4013              | `UUID ->
4014                  pr "  for (i = j = 0; i < 32; ++j) {\n";
4015                  pr "    if (tok[j] == '\\0') {\n";
4016                  pr "      fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
4017                  pr "      return -1;\n";
4018                  pr "    } else if (tok[j] != '-')\n";
4019                  pr "      r->%s[i++] = tok[j];\n" name;
4020                  pr "  }\n";
4021              | `Bytes ->
4022                  pr "  if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
4023                  pr "    fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
4024                  pr "    return -1;\n";
4025                  pr "  }\n";
4026              | `Int ->
4027                  pr "  if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
4028                  pr "    fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
4029                  pr "    return -1;\n";
4030                  pr "  }\n";
4031              | `OptPercent ->
4032                  pr "  if (tok[0] == '\\0')\n";
4033                  pr "    r->%s = -1;\n" name;
4034                  pr "  else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
4035                  pr "    fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
4036                  pr "    return -1;\n";
4037                  pr "  }\n";
4038             );
4039             pr "  tok = next;\n";
4040         ) cols;
4041
4042         pr "  if (tok != NULL) {\n";
4043         pr "    fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
4044         pr "    return -1;\n";
4045         pr "  }\n";
4046         pr "  return 0;\n";
4047         pr "}\n";
4048         pr "\n";
4049
4050         pr "guestfs_lvm_int_%s_list *\n" typ;
4051         pr "parse_command_line_%ss (void)\n" typ;
4052         pr "{\n";
4053         pr "  char *out, *err;\n";
4054         pr "  char *p, *pend;\n";
4055         pr "  int r, i;\n";
4056         pr "  guestfs_lvm_int_%s_list *ret;\n" typ;
4057         pr "  void *newp;\n";
4058         pr "\n";
4059         pr "  ret = malloc (sizeof *ret);\n";
4060         pr "  if (!ret) {\n";
4061         pr "    reply_with_perror (\"malloc\");\n";
4062         pr "    return NULL;\n";
4063         pr "  }\n";
4064         pr "\n";
4065         pr "  ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
4066         pr "  ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
4067         pr "\n";
4068         pr "  r = command (&out, &err,\n";
4069         pr "           \"/sbin/lvm\", \"%ss\",\n" typ;
4070         pr "           \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
4071         pr "           \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
4072         pr "  if (r == -1) {\n";
4073         pr "    reply_with_error (\"%%s\", err);\n";
4074         pr "    free (out);\n";
4075         pr "    free (err);\n";
4076         pr "    free (ret);\n";
4077         pr "    return NULL;\n";
4078         pr "  }\n";
4079         pr "\n";
4080         pr "  free (err);\n";
4081         pr "\n";
4082         pr "  /* Tokenize each line of the output. */\n";
4083         pr "  p = out;\n";
4084         pr "  i = 0;\n";
4085         pr "  while (p) {\n";
4086         pr "    pend = strchr (p, '\\n');       /* Get the next line of output. */\n";
4087         pr "    if (pend) {\n";
4088         pr "      *pend = '\\0';\n";
4089         pr "      pend++;\n";
4090         pr "    }\n";
4091         pr "\n";
4092         pr "    while (*p && isspace (*p))      /* Skip any leading whitespace. */\n";
4093         pr "      p++;\n";
4094         pr "\n";
4095         pr "    if (!*p) {                      /* Empty line?  Skip it. */\n";
4096         pr "      p = pend;\n";
4097         pr "      continue;\n";
4098         pr "    }\n";
4099         pr "\n";
4100         pr "    /* Allocate some space to store this next entry. */\n";
4101         pr "    newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
4102         pr "                sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
4103         pr "    if (newp == NULL) {\n";
4104         pr "      reply_with_perror (\"realloc\");\n";
4105         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
4106         pr "      free (ret);\n";
4107         pr "      free (out);\n";
4108         pr "      return NULL;\n";
4109         pr "    }\n";
4110         pr "    ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
4111         pr "\n";
4112         pr "    /* Tokenize the next entry. */\n";
4113         pr "    r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
4114         pr "    if (r == -1) {\n";
4115         pr "      reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
4116         pr "      free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
4117         pr "      free (ret);\n";
4118         pr "      free (out);\n";
4119         pr "      return NULL;\n";
4120         pr "    }\n";
4121         pr "\n";
4122         pr "    ++i;\n";
4123         pr "    p = pend;\n";
4124         pr "  }\n";
4125         pr "\n";
4126         pr "  ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
4127         pr "\n";
4128         pr "  free (out);\n";
4129         pr "  return ret;\n";
4130         pr "}\n"
4131
4132   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
4133
4134 (* Generate the tests. *)
4135 and generate_tests () =
4136   generate_header CStyle GPLv2;
4137
4138   pr "\
4139 #include <stdio.h>
4140 #include <stdlib.h>
4141 #include <string.h>
4142 #include <unistd.h>
4143 #include <sys/types.h>
4144 #include <fcntl.h>
4145
4146 #include \"guestfs.h\"
4147
4148 static guestfs_h *g;
4149 static int suppress_error = 0;
4150
4151 static void print_error (guestfs_h *g, void *data, const char *msg)
4152 {
4153   if (!suppress_error)
4154     fprintf (stderr, \"%%s\\n\", msg);
4155 }
4156
4157 static void print_strings (char * const * const argv)
4158 {
4159   int argc;
4160
4161   for (argc = 0; argv[argc] != NULL; ++argc)
4162     printf (\"\\t%%s\\n\", argv[argc]);
4163 }
4164
4165 /*
4166 static void print_table (char * const * const argv)
4167 {
4168   int i;
4169
4170   for (i = 0; argv[i] != NULL; i += 2)
4171     printf (\"%%s: %%s\\n\", argv[i], argv[i+1]);
4172 }
4173 */
4174
4175 static void no_test_warnings (void)
4176 {
4177 ";
4178
4179   List.iter (
4180     function
4181     | name, _, _, _, [], _, _ ->
4182         pr "  fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name
4183     | name, _, _, _, tests, _, _ -> ()
4184   ) all_functions;
4185
4186   pr "}\n";
4187   pr "\n";
4188
4189   (* Generate the actual tests.  Note that we generate the tests
4190    * in reverse order, deliberately, so that (in general) the
4191    * newest tests run first.  This makes it quicker and easier to
4192    * debug them.
4193    *)
4194   let test_names =
4195     List.map (
4196       fun (name, _, _, _, tests, _, _) ->
4197         mapi (generate_one_test name) tests
4198     ) (List.rev all_functions) in
4199   let test_names = List.concat test_names in
4200   let nr_tests = List.length test_names in
4201
4202   pr "\
4203 int main (int argc, char *argv[])
4204 {
4205   char c = 0;
4206   int failed = 0;
4207   const char *filename;
4208   int fd;
4209   int nr_tests, test_num = 0;
4210
4211   no_test_warnings ();
4212
4213   g = guestfs_create ();
4214   if (g == NULL) {
4215     printf (\"guestfs_create FAILED\\n\");
4216     exit (1);
4217   }
4218
4219   guestfs_set_error_handler (g, print_error, NULL);
4220
4221   guestfs_set_path (g, \"../appliance\");
4222
4223   filename = \"test1.img\";
4224   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
4225   if (fd == -1) {
4226     perror (filename);
4227     exit (1);
4228   }
4229   if (lseek (fd, %d, SEEK_SET) == -1) {
4230     perror (\"lseek\");
4231     close (fd);
4232     unlink (filename);
4233     exit (1);
4234   }
4235   if (write (fd, &c, 1) == -1) {
4236     perror (\"write\");
4237     close (fd);
4238     unlink (filename);
4239     exit (1);
4240   }
4241   if (close (fd) == -1) {
4242     perror (filename);
4243     unlink (filename);
4244     exit (1);
4245   }
4246   if (guestfs_add_drive (g, filename) == -1) {
4247     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
4248     exit (1);
4249   }
4250
4251   filename = \"test2.img\";
4252   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
4253   if (fd == -1) {
4254     perror (filename);
4255     exit (1);
4256   }
4257   if (lseek (fd, %d, SEEK_SET) == -1) {
4258     perror (\"lseek\");
4259     close (fd);
4260     unlink (filename);
4261     exit (1);
4262   }
4263   if (write (fd, &c, 1) == -1) {
4264     perror (\"write\");
4265     close (fd);
4266     unlink (filename);
4267     exit (1);
4268   }
4269   if (close (fd) == -1) {
4270     perror (filename);
4271     unlink (filename);
4272     exit (1);
4273   }
4274   if (guestfs_add_drive (g, filename) == -1) {
4275     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
4276     exit (1);
4277   }
4278
4279   filename = \"test3.img\";
4280   fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
4281   if (fd == -1) {
4282     perror (filename);
4283     exit (1);
4284   }
4285   if (lseek (fd, %d, SEEK_SET) == -1) {
4286     perror (\"lseek\");
4287     close (fd);
4288     unlink (filename);
4289     exit (1);
4290   }
4291   if (write (fd, &c, 1) == -1) {
4292     perror (\"write\");
4293     close (fd);
4294     unlink (filename);
4295     exit (1);
4296   }
4297   if (close (fd) == -1) {
4298     perror (filename);
4299     unlink (filename);
4300     exit (1);
4301   }
4302   if (guestfs_add_drive (g, filename) == -1) {
4303     printf (\"guestfs_add_drive %%s FAILED\\n\", filename);
4304     exit (1);
4305   }
4306
4307   if (guestfs_add_drive_ro (g, \"../images/test.sqsh\") == -1) {
4308     printf (\"guestfs_add_drive_ro ../images/test.sqsh FAILED\\n\");
4309     exit (1);
4310   }
4311
4312   if (guestfs_launch (g) == -1) {
4313     printf (\"guestfs_launch FAILED\\n\");
4314     exit (1);
4315   }
4316
4317   /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */
4318   alarm (600);
4319
4320   if (guestfs_wait_ready (g) == -1) {
4321     printf (\"guestfs_wait_ready FAILED\\n\");
4322     exit (1);
4323   }
4324
4325   /* Cancel previous alarm. */
4326   alarm (0);
4327
4328   nr_tests = %d;
4329
4330 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
4331
4332   iteri (
4333     fun i test_name ->
4334       pr "  test_num++;\n";
4335       pr "  printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
4336       pr "  if (%s () == -1) {\n" test_name;
4337       pr "    printf (\"%s FAILED\\n\");\n" test_name;
4338       pr "    failed++;\n";
4339       pr "  }\n";
4340   ) test_names;
4341   pr "\n";
4342
4343   pr "  guestfs_close (g);\n";
4344   pr "  unlink (\"test1.img\");\n";
4345   pr "  unlink (\"test2.img\");\n";
4346   pr "  unlink (\"test3.img\");\n";
4347   pr "\n";
4348
4349   pr "  if (failed > 0) {\n";
4350   pr "    printf (\"***** %%d / %%d tests FAILED *****\\n\", failed, nr_tests);\n";
4351   pr "    exit (1);\n";
4352   pr "  }\n";
4353   pr "\n";
4354
4355   pr "  exit (0);\n";
4356   pr "}\n"
4357
4358 and generate_one_test name i (init, prereq, test) =
4359   let test_name = sprintf "test_%s_%d" name i in
4360
4361   pr "\
4362 static int %s_skip (void)
4363 {
4364   const char *str;
4365
4366   str = getenv (\"TEST_ONLY\");
4367   if (str)
4368     return strstr (str, \"%s\") == NULL;
4369   str = getenv (\"SKIP_%s\");
4370   if (str && strcmp (str, \"1\") == 0) return 1;
4371   str = getenv (\"SKIP_TEST_%s\");
4372   if (str && strcmp (str, \"1\") == 0) return 1;
4373   return 0;
4374 }
4375
4376 " test_name name (String.uppercase test_name) (String.uppercase name);
4377
4378   (match prereq with
4379    | Disabled | Always -> ()
4380    | If code | Unless code ->
4381        pr "static int %s_prereq (void)\n" test_name;
4382        pr "{\n";
4383        pr "  %s\n" code;
4384        pr "}\n";
4385        pr "\n";
4386   );
4387
4388   pr "\
4389 static int %s (void)
4390 {
4391   if (%s_skip ()) {
4392     printf (\"%%s skipped (reason: environment variable set)\\n\", \"%s\");
4393     return 0;
4394   }
4395
4396 " test_name test_name test_name;
4397
4398   (match prereq with
4399    | Disabled ->
4400        pr "  printf (\"%%s skipped (reason: test disabled in generator)\\n\", \"%s\");\n" test_name
4401    | If _ ->
4402        pr "  if (! %s_prereq ()) {\n" test_name;
4403        pr "    printf (\"%%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
4404        pr "    return 0;\n";
4405        pr "  }\n";
4406        pr "\n";
4407        generate_one_test_body name i test_name init test;
4408    | Unless _ ->
4409        pr "  if (%s_prereq ()) {\n" test_name;
4410        pr "    printf (\"%%s skipped (reason: test prerequisite)\\n\", \"%s\");\n" test_name;
4411        pr "    return 0;\n";
4412        pr "  }\n";
4413        pr "\n";
4414        generate_one_test_body name i test_name init test;
4415    | Always ->
4416        generate_one_test_body name i test_name init test
4417   );
4418
4419   pr "  return 0;\n";
4420   pr "}\n";
4421   pr "\n";
4422   test_name
4423
4424 and generate_one_test_body name i test_name init test =
4425   (match init with
4426    | InitNone
4427    | InitEmpty ->
4428        pr "  /* InitNone|InitEmpty for %s */\n" test_name;
4429        List.iter (generate_test_command_call test_name)
4430          [["blockdev_setrw"; "/dev/sda"];
4431           ["umount_all"];
4432           ["lvm_remove_all"]]
4433    | InitBasicFS ->
4434        pr "  /* InitBasicFS for %s: create ext2 on /dev/sda1 */\n" test_name;
4435        List.iter (generate_test_command_call test_name)
4436          [["blockdev_setrw"; "/dev/sda"];
4437           ["umount_all"];
4438           ["lvm_remove_all"];
4439           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
4440           ["mkfs"; "ext2"; "/dev/sda1"];
4441           ["mount"; "/dev/sda1"; "/"]]
4442    | InitBasicFSonLVM ->
4443        pr "  /* InitBasicFSonLVM for %s: create ext2 on /dev/VG/LV */\n"
4444          test_name;
4445        List.iter (generate_test_command_call test_name)
4446          [["blockdev_setrw"; "/dev/sda"];
4447           ["umount_all"];
4448           ["lvm_remove_all"];
4449           ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
4450           ["pvcreate"; "/dev/sda1"];
4451           ["vgcreate"; "VG"; "/dev/sda1"];
4452           ["lvcreate"; "LV"; "VG"; "8"];
4453           ["mkfs"; "ext2"; "/dev/VG/LV"];
4454           ["mount"; "/dev/VG/LV"; "/"]]
4455   );
4456
4457   let get_seq_last = function
4458     | [] ->
4459         failwithf "%s: you cannot use [] (empty list) when expecting a command"
4460           test_name
4461     | seq ->
4462         let seq = List.rev seq in
4463         List.rev (List.tl seq), List.hd seq
4464   in
4465
4466   match test with
4467   | TestRun seq ->
4468       pr "  /* TestRun for %s (%d) */\n" name i;
4469       List.iter (generate_test_command_call test_name) seq
4470   | TestOutput (seq, expected) ->
4471       pr "  /* TestOutput for %s (%d) */\n" name i;
4472       pr "  char expected[] = \"%s\";\n" (c_quote expected);
4473       let seq, last = get_seq_last seq in
4474       let test () =
4475         pr "    if (strcmp (r, expected) != 0) {\n";
4476         pr "      fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r);\n" test_name;
4477         pr "      return -1;\n";
4478         pr "    }\n"
4479       in
4480       List.iter (generate_test_command_call test_name) seq;
4481       generate_test_command_call ~test test_name last
4482   | TestOutputList (seq, expected) ->
4483       pr "  /* TestOutputList for %s (%d) */\n" name i;
4484       let seq, last = get_seq_last seq in
4485       let test () =
4486         iteri (
4487           fun i str ->
4488             pr "    if (!r[%d]) {\n" i;
4489             pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
4490             pr "      print_strings (r);\n";
4491             pr "      return -1;\n";
4492             pr "    }\n";
4493             pr "    {\n";
4494             pr "      char expected[] = \"%s\";\n" (c_quote str);
4495             pr "      if (strcmp (r[%d], expected) != 0) {\n" i;
4496             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
4497             pr "        return -1;\n";
4498             pr "      }\n";
4499             pr "    }\n"
4500         ) expected;
4501         pr "    if (r[%d] != NULL) {\n" (List.length expected);
4502         pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
4503           test_name;
4504         pr "      print_strings (r);\n";
4505         pr "      return -1;\n";
4506         pr "    }\n"
4507       in
4508       List.iter (generate_test_command_call test_name) seq;
4509       generate_test_command_call ~test test_name last
4510   | TestOutputListOfDevices (seq, expected) ->
4511       pr "  /* TestOutputListOfDevices for %s (%d) */\n" name i;
4512       let seq, last = get_seq_last seq in
4513       let test () =
4514         iteri (
4515           fun i str ->
4516             pr "    if (!r[%d]) {\n" i;
4517             pr "      fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
4518             pr "      print_strings (r);\n";
4519             pr "      return -1;\n";
4520             pr "    }\n";
4521             pr "    {\n";
4522             pr "      char expected[] = \"%s\";\n" (c_quote str);
4523             pr "      r[%d][5] = 's';\n" i;
4524             pr "      if (strcmp (r[%d], expected) != 0) {\n" i;
4525             pr "        fprintf (stderr, \"%s: expected \\\"%%s\\\" but got \\\"%%s\\\"\\n\", expected, r[%d]);\n" test_name i;
4526             pr "        return -1;\n";
4527             pr "      }\n";
4528             pr "    }\n"
4529         ) expected;
4530         pr "    if (r[%d] != NULL) {\n" (List.length expected);
4531         pr "      fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
4532           test_name;
4533         pr "      print_strings (r);\n";
4534         pr "      return -1;\n";
4535         pr "    }\n"
4536       in
4537       List.iter (generate_test_command_call test_name) seq;
4538       generate_test_command_call ~test test_name last
4539   | TestOutputInt (seq, expected) ->
4540       pr "  /* TestOutputInt for %s (%d) */\n" name i;
4541       let seq, last = get_seq_last seq in
4542       let test () =
4543         pr "    if (r != %d) {\n" expected;
4544         pr "      fprintf (stderr, \"%s: expected %d but got %%d\\n\","
4545           test_name expected;
4546         pr "               (int) r);\n";
4547         pr "      return -1;\n";
4548         pr "    }\n"
4549       in
4550       List.iter (generate_test_command_call test_name) seq;
4551       generate_test_command_call ~test test_name last
4552   | TestOutputTrue seq ->
4553       pr "  /* TestOutputTrue for %s (%d) */\n" name i;
4554       let seq, last = get_seq_last seq in
4555       let test () =
4556         pr "    if (!r) {\n";
4557         pr "      fprintf (stderr, \"%s: expected true, got false\\n\");\n"
4558           test_name;
4559         pr "      return -1;\n";
4560         pr "    }\n"
4561       in
4562       List.iter (generate_test_command_call test_name) seq;
4563       generate_test_command_call ~test test_name last
4564   | TestOutputFalse seq ->
4565       pr "  /* TestOutputFalse for %s (%d) */\n" name i;
4566       let seq, last = get_seq_last seq in
4567       let test () =
4568         pr "    if (r) {\n";
4569         pr "      fprintf (stderr, \"%s: expected false, got true\\n\");\n"
4570           test_name;
4571         pr "      return -1;\n";
4572         pr "    }\n"
4573       in
4574       List.iter (generate_test_command_call test_name) seq;
4575       generate_test_command_call ~test test_name last
4576   | TestOutputLength (seq, expected) ->
4577       pr "  /* TestOutputLength for %s (%d) */\n" name i;
4578       let seq, last = get_seq_last seq in
4579       let test () =
4580         pr "    int j;\n";
4581         pr "    for (j = 0; j < %d; ++j)\n" expected;
4582         pr "      if (r[j] == NULL) {\n";
4583         pr "        fprintf (stderr, \"%s: short list returned\\n\");\n"
4584           test_name;
4585         pr "        print_strings (r);\n";
4586         pr "        return -1;\n";
4587         pr "      }\n";
4588         pr "    if (r[j] != NULL) {\n";
4589         pr "      fprintf (stderr, \"%s: long list returned\\n\");\n"
4590           test_name;
4591         pr "      print_strings (r);\n";
4592         pr "      return -1;\n";
4593         pr "    }\n"
4594       in
4595       List.iter (generate_test_command_call test_name) seq;
4596       generate_test_command_call ~test test_name last
4597   | TestOutputStruct (seq, checks) ->
4598       pr "  /* TestOutputStruct for %s (%d) */\n" name i;
4599       let seq, last = get_seq_last seq in
4600       let test () =
4601         List.iter (
4602           function
4603           | CompareWithInt (field, expected) ->
4604               pr "    if (r->%s != %d) {\n" field expected;
4605               pr "      fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
4606                 test_name field expected;
4607               pr "               (int) r->%s);\n" field;
4608               pr "      return -1;\n";
4609               pr "    }\n"
4610           | CompareWithString (field, expected) ->
4611               pr "    if (strcmp (r->%s, \"%s\") != 0) {\n" field expected;
4612               pr "      fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
4613                 test_name field expected;
4614               pr "               r->%s);\n" field;
4615               pr "      return -1;\n";
4616               pr "    }\n"
4617           | CompareFieldsIntEq (field1, field2) ->
4618               pr "    if (r->%s != r->%s) {\n" field1 field2;
4619               pr "      fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
4620                 test_name field1 field2;
4621               pr "               (int) r->%s, (int) r->%s);\n" field1 field2;
4622               pr "      return -1;\n";
4623               pr "    }\n"
4624           | CompareFieldsStrEq (field1, field2) ->
4625               pr "    if (strcmp (r->%s, r->%s) != 0) {\n" field1 field2;
4626               pr "      fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
4627                 test_name field1 field2;
4628               pr "               r->%s, r->%s);\n" field1 field2;
4629               pr "      return -1;\n";
4630               pr "    }\n"
4631         ) checks
4632       in
4633       List.iter (generate_test_command_call test_name) seq;
4634       generate_test_command_call ~test test_name last
4635   | TestLastFail seq ->
4636       pr "  /* TestLastFail for %s (%d) */\n" name i;
4637       let seq, last = get_seq_last seq in
4638       List.iter (generate_test_command_call test_name) seq;
4639       generate_test_command_call test_name ~expect_error:true last
4640
4641 (* Generate the code to run a command, leaving the result in 'r'.
4642  * If you expect to get an error then you should set expect_error:true.
4643  *)
4644 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
4645   match cmd with
4646   | [] -> assert false
4647   | name :: args ->
4648       (* Look up the command to find out what args/ret it has. *)
4649       let style =
4650         try
4651           let _, style, _, _, _, _, _ =
4652             List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
4653           style
4654         with Not_found ->
4655           failwithf "%s: in test, command %s was not found" test_name name in
4656
4657       if List.length (snd style) <> List.length args then
4658         failwithf "%s: in test, wrong number of args given to %s"
4659           test_name name;
4660
4661       pr "  {\n";
4662
4663       List.iter (
4664         function
4665         | OptString n, "NULL" -> ()
4666         | String n, arg
4667         | OptString n, arg ->
4668             pr "    char %s[] = \"%s\";\n" n (c_quote arg);
4669         | Int _, _
4670         | Bool _, _
4671         | FileIn _, _ | FileOut _, _ -> ()
4672         | StringList n, arg ->
4673             let strs = string_split " " arg in
4674             iteri (
4675               fun i str ->
4676                 pr "    char %s_%d[] = \"%s\";\n" n i (c_quote str);
4677             ) strs;
4678             pr "    char *%s[] = {\n" n;
4679             iteri (
4680               fun i _ -> pr "      %s_%d,\n" n i
4681             ) strs;
4682             pr "      NULL\n";
4683             pr "    };\n";
4684       ) (List.combine (snd style) args);
4685
4686       let error_code =
4687         match fst style with
4688         | RErr | RInt _ | RBool _ -> pr "    int r;\n"; "-1"
4689         | RInt64 _ -> pr "    int64_t r;\n"; "-1"
4690         | RConstString _ -> pr "    const char *r;\n"; "NULL"
4691         | RString _ -> pr "    char *r;\n"; "NULL"
4692         | RStringList _ | RHashtable _ ->
4693             pr "    char **r;\n";
4694             pr "    int i;\n";
4695             "NULL"
4696         | RIntBool _ ->
4697             pr "    struct guestfs_int_bool *r;\n"; "NULL"
4698         | RPVList _ ->
4699             pr "    struct guestfs_lvm_pv_list *r;\n"; "NULL"
4700         | RVGList _ ->
4701             pr "    struct guestfs_lvm_vg_list *r;\n"; "NULL"
4702         | RLVList _ ->
4703             pr "    struct guestfs_lvm_lv_list *r;\n"; "NULL"
4704         | RStat _ ->
4705             pr "    struct guestfs_stat *r;\n"; "NULL"
4706         | RStatVFS _ ->
4707             pr "    struct guestfs_statvfs *r;\n"; "NULL" in
4708
4709       pr "    suppress_error = %d;\n" (if expect_error then 1 else 0);
4710       pr "    r = guestfs_%s (g" name;
4711
4712       (* Generate the parameters. *)
4713       List.iter (
4714         function
4715         | OptString _, "NULL" -> pr ", NULL"
4716         | String n, _
4717         | OptString n, _ ->
4718             pr ", %s" n
4719         | FileIn _, arg | FileOut _, arg ->
4720             pr ", \"%s\"" (c_quote arg)
4721         | StringList n, _ ->
4722             pr ", %s" n
4723         | Int _, arg ->
4724             let i =
4725               try int_of_string arg
4726               with Failure "int_of_string" ->
4727                 failwithf "%s: expecting an int, but got '%s'" test_name arg in
4728             pr ", %d" i
4729         | Bool _, arg ->
4730             let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
4731       ) (List.combine (snd style) args);
4732
4733       pr ");\n";
4734       if not expect_error then
4735         pr "    if (r == %s)\n" error_code
4736       else
4737         pr "    if (r != %s)\n" error_code;
4738       pr "      return -1;\n";
4739
4740       (* Insert the test code. *)
4741       (match test with
4742        | None -> ()
4743        | Some f -> f ()
4744       );
4745
4746       (match fst style with
4747        | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _ -> ()
4748        | RString _ -> pr "    free (r);\n"
4749        | RStringList _ | RHashtable _ ->
4750            pr "    for (i = 0; r[i] != NULL; ++i)\n";
4751            pr "      free (r[i]);\n";
4752            pr "    free (r);\n"
4753        | RIntBool _ ->
4754            pr "    guestfs_free_int_bool (r);\n"
4755        | RPVList _ ->
4756            pr "    guestfs_free_lvm_pv_list (r);\n"
4757        | RVGList _ ->
4758            pr "    guestfs_free_lvm_vg_list (r);\n"
4759        | RLVList _ ->
4760            pr "    guestfs_free_lvm_lv_list (r);\n"
4761        | RStat _ | RStatVFS _ ->
4762            pr "    free (r);\n"
4763       );
4764
4765       pr "  }\n"
4766
4767 and c_quote str =
4768   let str = replace_str str "\r" "\\r" in
4769   let str = replace_str str "\n" "\\n" in
4770   let str = replace_str str "\t" "\\t" in
4771   let str = replace_str str "\000" "\\0" in
4772   str
4773
4774 (* Generate a lot of different functions for guestfish. *)
4775 and generate_fish_cmds () =
4776   generate_header CStyle GPLv2;
4777
4778   let all_functions =
4779     List.filter (
4780       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4781     ) all_functions in
4782   let all_functions_sorted =
4783     List.filter (
4784       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
4785     ) all_functions_sorted in
4786
4787   pr "#include <stdio.h>\n";
4788   pr "#include <stdlib.h>\n";
4789   pr "#include <string.h>\n";
4790   pr "#include <inttypes.h>\n";
4791   pr "\n";
4792   pr "#include <guestfs.h>\n";
4793   pr "#include \"fish.h\"\n";
4794   pr "\n";
4795
4796   (* list_commands function, which implements guestfish -h *)
4797   pr "void list_commands (void)\n";
4798   pr "{\n";
4799   pr "  printf (\"    %%-16s     %%s\\n\", \"Command\", \"Description\");\n";
4800   pr "  list_builtin_commands ();\n";
4801   List.iter (
4802     fun (name, _, _, flags, _, shortdesc, _) ->
4803       let name = replace_char name '_' '-' in
4804       pr "  printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
4805         name shortdesc
4806   ) all_functions_sorted;
4807   pr "  printf (\"    Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
4808   pr "}\n";
4809   pr "\n";
4810
4811   (* display_command function, which implements guestfish -h cmd *)
4812   pr "void display_command (const char *cmd)\n";
4813   pr "{\n";
4814   List.iter (
4815     fun (name, style, _, flags, _, shortdesc, longdesc) ->
4816       let name2 = replace_char name '_' '-' in
4817       let alias =
4818         try find_map (function FishAlias n -> Some n | _ -> None) flags
4819         with Not_found -> name in
4820       let longdesc = replace_str longdesc "C<guestfs_" "C<" in
4821       let synopsis =
4822         match snd style with
4823         | [] -> name2
4824         | args ->
4825             sprintf "%s <%s>"
4826               name2 (String.concat "> <" (List.map name_of_argt args)) in
4827
4828       let warnings =
4829         if List.mem ProtocolLimitWarning flags then
4830           ("\n\n" ^ protocol_limit_warning)
4831         else "" in
4832
4833       (* For DangerWillRobinson commands, we should probably have
4834        * guestfish prompt before allowing you to use them (especially
4835        * in interactive mode). XXX
4836        *)
4837       let warnings =
4838         warnings ^
4839           if List.mem DangerWillRobinson flags then
4840             ("\n\n" ^ danger_will_robinson)
4841           else "" in
4842
4843       let describe_alias =
4844         if name <> alias then
4845           sprintf "\n\nYou can use '%s' as an alias for this command." alias
4846         else "" in
4847
4848       pr "  if (";
4849       pr "strcasecmp (cmd, \"%s\") == 0" name;
4850       if name <> name2 then
4851         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
4852       if name <> alias then
4853         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
4854       pr ")\n";
4855       pr "    pod2text (\"%s - %s\", %S);\n"
4856         name2 shortdesc
4857         (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings ^ describe_alias);
4858       pr "  else\n"
4859   ) all_functions;
4860   pr "    display_builtin_command (cmd);\n";
4861   pr "}\n";
4862   pr "\n";
4863
4864   (* print_{pv,vg,lv}_list functions *)
4865   List.iter (
4866     function
4867     | typ, cols ->
4868         pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
4869         pr "{\n";
4870         pr "  int i;\n";
4871         pr "\n";
4872         List.iter (
4873           function
4874           | name, `String ->
4875               pr "  printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
4876           | name, `UUID ->
4877               pr "  printf (\"%s: \");\n" name;
4878               pr "  for (i = 0; i < 32; ++i)\n";
4879               pr "    printf (\"%%c\", %s->%s[i]);\n" typ name;
4880               pr "  printf (\"\\n\");\n"
4881           | name, `Bytes ->
4882               pr "  printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
4883           | name, `Int ->
4884               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
4885           | name, `OptPercent ->
4886               pr "  if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
4887                 typ name name typ name;
4888               pr "  else printf (\"%s: \\n\");\n" name
4889         ) cols;
4890         pr "}\n";
4891         pr "\n";
4892         pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
4893           typ typ typ;
4894         pr "{\n";
4895         pr "  int i;\n";
4896         pr "\n";
4897         pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
4898         pr "    print_%s (&%ss->val[i]);\n" typ typ;
4899         pr "}\n";
4900         pr "\n";
4901   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
4902
4903   (* print_{stat,statvfs} functions *)
4904   List.iter (
4905     function
4906     | typ, cols ->
4907         pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
4908         pr "{\n";
4909         List.iter (
4910           function
4911           | name, `Int ->
4912               pr "  printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
4913         ) cols;
4914         pr "}\n";
4915         pr "\n";
4916   ) ["stat", stat_cols; "statvfs", statvfs_cols];
4917
4918   (* run_<action> actions *)
4919   List.iter (
4920     fun (name, style, _, flags, _, _, _) ->
4921       pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
4922       pr "{\n";
4923       (match fst style with
4924        | RErr
4925        | RInt _
4926        | RBool _ -> pr "  int r;\n"
4927        | RInt64 _ -> pr "  int64_t r;\n"
4928        | RConstString _ -> pr "  const char *r;\n"
4929        | RString _ -> pr "  char *r;\n"
4930        | RStringList _ | RHashtable _ -> pr "  char **r;\n"
4931        | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"
4932        | RPVList _ -> pr "  struct guestfs_lvm_pv_list *r;\n"
4933        | RVGList _ -> pr "  struct guestfs_lvm_vg_list *r;\n"
4934        | RLVList _ -> pr "  struct guestfs_lvm_lv_list *r;\n"
4935        | RStat _ -> pr "  struct guestfs_stat *r;\n"
4936        | RStatVFS _ -> pr "  struct guestfs_statvfs *r;\n"
4937       );
4938       List.iter (
4939         function
4940         | String n
4941         | OptString n
4942         | FileIn n
4943         | FileOut n -> pr "  const char *%s;\n" n
4944         | StringList n -> pr "  char **%s;\n" n
4945         | Bool n -> pr "  int %s;\n" n
4946         | Int n -> pr "  int %s;\n" n
4947       ) (snd style);
4948
4949       (* Check and convert parameters. *)
4950       let argc_expected = List.length (snd style) in
4951       pr "  if (argc != %d) {\n" argc_expected;
4952       pr "    fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
4953         argc_expected;
4954       pr "    fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
4955       pr "    return -1;\n";
4956       pr "  }\n";
4957       iteri (
4958         fun i ->
4959           function
4960           | String name -> pr "  %s = argv[%d];\n" name i
4961           | OptString name ->
4962               pr "  %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
4963                 name i i
4964           | FileIn name ->
4965               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdin\";\n"
4966                 name i i
4967           | FileOut name ->
4968               pr "  %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdout\";\n"
4969                 name i i
4970           | StringList name ->
4971               pr "  %s = parse_string_list (argv[%d]);\n" name i
4972           | Bool name ->
4973               pr "  %s = is_true (argv[%d]) ? 1 : 0;\n" name i
4974           | Int name ->
4975               pr "  %s = atoi (argv[%d]);\n" name i
4976       ) (snd style);
4977
4978       (* Call C API function. *)
4979       let fn =
4980         try find_map (function FishAction n -> Some n | _ -> None) flags
4981         with Not_found -> sprintf "guestfs_%s" name in
4982       pr "  r = %s " fn;
4983       generate_call_args ~handle:"g" (snd style);
4984       pr ";\n";
4985
4986       (* Check return value for errors and display command results. *)
4987       (match fst style with
4988        | RErr -> pr "  return r;\n"
4989        | RInt _ ->
4990            pr "  if (r == -1) return -1;\n";
4991            pr "  printf (\"%%d\\n\", r);\n";
4992            pr "  return 0;\n"
4993        | RInt64 _ ->
4994            pr "  if (r == -1) return -1;\n";
4995            pr "  printf (\"%%\" PRIi64 \"\\n\", r);\n";
4996            pr "  return 0;\n"
4997        | RBool _ ->
4998            pr "  if (r == -1) return -1;\n";
4999            pr "  if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
5000            pr "  return 0;\n"
5001        | RConstString _ ->
5002            pr "  if (r == NULL) return -1;\n";
5003            pr "  printf (\"%%s\\n\", r);\n";
5004            pr "  return 0;\n"
5005        | RString _ ->
5006            pr "  if (r == NULL) return -1;\n";
5007            pr "  printf (\"%%s\\n\", r);\n";
5008            pr "  free (r);\n";
5009            pr "  return 0;\n"
5010        | RStringList _ ->
5011            pr "  if (r == NULL) return -1;\n";
5012            pr "  print_strings (r);\n";
5013            pr "  free_strings (r);\n";
5014            pr "  return 0;\n"
5015        | RIntBool _ ->
5016            pr "  if (r == NULL) return -1;\n";
5017            pr "  printf (\"%%d, %%s\\n\", r->i,\n";
5018            pr "    r->b ? \"true\" : \"false\");\n";
5019            pr "  guestfs_free_int_bool (r);\n";
5020            pr "  return 0;\n"
5021        | RPVList _ ->
5022            pr "  if (r == NULL) return -1;\n";
5023            pr "  print_pv_list (r);\n";
5024            pr "  guestfs_free_lvm_pv_list (r);\n";
5025            pr "  return 0;\n"
5026        | RVGList _ ->
5027            pr "  if (r == NULL) return -1;\n";
5028            pr "  print_vg_list (r);\n";
5029            pr "  guestfs_free_lvm_vg_list (r);\n";
5030            pr "  return 0;\n"
5031        | RLVList _ ->
5032            pr "  if (r == NULL) return -1;\n";
5033            pr "  print_lv_list (r);\n";
5034            pr "  guestfs_free_lvm_lv_list (r);\n";
5035            pr "  return 0;\n"
5036        | RStat _ ->
5037            pr "  if (r == NULL) return -1;\n";
5038            pr "  print_stat (r);\n";
5039            pr "  free (r);\n";
5040            pr "  return 0;\n"
5041        | RStatVFS _ ->
5042            pr "  if (r == NULL) return -1;\n";
5043            pr "  print_statvfs (r);\n";
5044            pr "  free (r);\n";
5045            pr "  return 0;\n"
5046        | RHashtable _ ->
5047            pr "  if (r == NULL) return -1;\n";
5048            pr "  print_table (r);\n";
5049            pr "  free_strings (r);\n";
5050            pr "  return 0;\n"
5051       );
5052       pr "}\n";
5053       pr "\n"
5054   ) all_functions;
5055
5056   (* run_action function *)
5057   pr "int run_action (const char *cmd, int argc, char *argv[])\n";
5058   pr "{\n";
5059   List.iter (
5060     fun (name, _, _, flags, _, _, _) ->
5061       let name2 = replace_char name '_' '-' in
5062       let alias =
5063         try find_map (function FishAlias n -> Some n | _ -> None) flags
5064         with Not_found -> name in
5065       pr "  if (";
5066       pr "strcasecmp (cmd, \"%s\") == 0" name;
5067       if name <> name2 then
5068         pr " || strcasecmp (cmd, \"%s\") == 0" name2;
5069       if name <> alias then
5070         pr " || strcasecmp (cmd, \"%s\") == 0" alias;
5071       pr ")\n";
5072       pr "    return run_%s (cmd, argc, argv);\n" name;
5073       pr "  else\n";
5074   ) all_functions;
5075   pr "    {\n";
5076   pr "      fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
5077   pr "      return -1;\n";
5078   pr "    }\n";
5079   pr "  return 0;\n";
5080   pr "}\n";
5081   pr "\n"
5082
5083 (* Readline completion for guestfish. *)
5084 and generate_fish_completion () =
5085   generate_header CStyle GPLv2;
5086
5087   let all_functions =
5088     List.filter (
5089       fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
5090     ) all_functions in
5091
5092   pr "\
5093 #include <config.h>
5094
5095 #include <stdio.h>
5096 #include <stdlib.h>
5097 #include <string.h>
5098
5099 #ifdef HAVE_LIBREADLINE
5100 #include <readline/readline.h>
5101 #endif
5102
5103 #include \"fish.h\"
5104
5105 #ifdef HAVE_LIBREADLINE
5106
5107 static const char *const commands[] = {
5108   BUILTIN_COMMANDS_FOR_COMPLETION,
5109 ";
5110
5111   (* Get the commands, including the aliases.  They don't need to be
5112    * sorted - the generator() function just does a dumb linear search.
5113    *)
5114   let commands =
5115     List.map (
5116       fun (name, _, _, flags, _, _, _) ->
5117         let name2 = replace_char name '_' '-' in
5118         let alias =
5119           try find_map (function FishAlias n -> Some n | _ -> None) flags
5120           with Not_found -> name in
5121
5122         if name <> alias then [name2; alias] else [name2]
5123     ) all_functions in
5124   let commands = List.flatten commands in
5125
5126   List.iter (pr "  \"%s\",\n") commands;
5127
5128   pr "  NULL
5129 };
5130
5131 static char *
5132 generator (const char *text, int state)
5133 {
5134   static int index, len;
5135   const char *name;
5136
5137   if (!state) {
5138     index = 0;
5139     len = strlen (text);
5140   }
5141
5142   rl_attempted_completion_over = 1;
5143
5144   while ((name = commands[index]) != NULL) {
5145     index++;
5146     if (strncasecmp (name, text, len) == 0)
5147       return strdup (name);
5148   }
5149
5150   return NULL;
5151 }
5152
5153 #endif /* HAVE_LIBREADLINE */
5154
5155 char **do_completion (const char *text, int start, int end)
5156 {
5157   char **matches = NULL;
5158
5159 #ifdef HAVE_LIBREADLINE
5160   rl_completion_append_character = ' ';
5161
5162   if (start == 0)
5163     matches = rl_completion_matches (text, generator);
5164   else if (complete_dest_paths)
5165     matches = rl_completion_matches (text, complete_dest_paths_generator);
5166 #endif
5167
5168   return matches;
5169 }
5170 ";
5171
5172 (* Generate the POD documentation for guestfish. *)
5173 and generate_fish_actions_pod () =
5174   let all_functions_sorted =
5175     List.filter (
5176       fun (_, _, _, flags, _, _, _) ->
5177         not (List.mem NotInFish flags || List.mem NotInDocs flags)
5178     ) all_functions_sorted in
5179
5180   let rex = Str.regexp "C<guestfs_\\([^>]+\\)>" in
5181
5182   List.iter (
5183     fun (name, style, _, flags, _, _, longdesc) ->
5184       let longdesc =
5185         Str.global_substitute rex (
5186           fun s ->
5187             let sub =
5188               try Str.matched_group 1 s
5189               with Not_found ->
5190                 failwithf "error substituting C<guestfs_...> in longdesc of function %s" name in
5191             "C<" ^ replace_char sub '_' '-' ^ ">"
5192         ) longdesc in
5193       let name = replace_char name '_' '-' in
5194       let alias =
5195         try find_map (function FishAlias n -> Some n | _ -> None) flags
5196         with Not_found -> name in
5197
5198       pr "=head2 %s" name;
5199       if name <> alias then
5200         pr " | %s" alias;
5201       pr "\n";
5202       pr "\n";
5203       pr " %s" name;
5204       List.iter (
5205         function
5206         | String n -> pr " %s" n
5207         | OptString n -> pr " %s" n
5208         | StringList n -> pr " '%s ...'" n
5209         | Bool _ -> pr " true|false"
5210         | Int n -> pr " %s" n
5211         | FileIn n | FileOut n -> pr " (%s|-)" n
5212       ) (snd style);
5213       pr "\n";
5214       pr "\n";
5215       pr "%s\n\n" longdesc;
5216
5217       if List.exists (function FileIn _ | FileOut _ -> true
5218                       | _ -> false) (snd style) then
5219         pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
5220
5221       if List.mem ProtocolLimitWarning flags then
5222         pr "%s\n\n" protocol_limit_warning;
5223
5224       if List.mem DangerWillRobinson flags then
5225         pr "%s\n\n" danger_will_robinson
5226   ) all_functions_sorted
5227
5228 (* Generate a C function prototype. *)
5229 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
5230     ?(single_line = false) ?(newline = false) ?(in_daemon = false)
5231     ?(prefix = "")
5232     ?handle name style =
5233   if extern then pr "extern ";
5234   if static then pr "static ";
5235   (match fst style with
5236    | RErr -> pr "int "
5237    | RInt _ -> pr "int "
5238    | RInt64 _ -> pr "int64_t "
5239    | RBool _ -> pr "int "
5240    | RConstString _ -> pr "const char *"
5241    | RString _ -> pr "char *"
5242    | RStringList _ | RHashtable _ -> pr "char **"
5243    | RIntBool _ ->
5244        if not in_daemon then pr "struct guestfs_int_bool *"
5245        else pr "guestfs_%s_ret *" name
5246    | RPVList _ ->
5247        if not in_daemon then pr "struct guestfs_lvm_pv_list *"
5248        else pr "guestfs_lvm_int_pv_list *"
5249    | RVGList _ ->
5250        if not in_daemon then pr "struct guestfs_lvm_vg_list *"
5251        else pr "guestfs_lvm_int_vg_list *"
5252    | RLVList _ ->
5253        if not in_daemon then pr "struct guestfs_lvm_lv_list *"
5254        else pr "guestfs_lvm_int_lv_list *"
5255    | RStat _ ->
5256        if not in_daemon then pr "struct guestfs_stat *"
5257        else pr "guestfs_int_stat *"
5258    | RStatVFS _ ->
5259        if not in_daemon then pr "struct guestfs_statvfs *"
5260        else pr "guestfs_int_statvfs *"
5261   );
5262   pr "%s%s (" prefix name;
5263   if handle = None && List.length (snd style) = 0 then
5264     pr "void"
5265   else (
5266     let comma = ref false in
5267     (match handle with
5268      | None -> ()
5269      | Some handle -> pr "guestfs_h *%s" handle; comma := true
5270     );
5271     let next () =
5272       if !comma then (
5273         if single_line then pr ", " else pr ",\n\t\t"
5274       );
5275       comma := true
5276     in
5277     List.iter (
5278       function
5279       | String n
5280       | OptString n ->
5281           next ();
5282           if not in_daemon then pr "const char *%s" n
5283           else pr "char *%s" n
5284       | StringList n ->
5285           next ();
5286           if not in_daemon then pr "char * const* const %s" n
5287           else pr "char **%s" n
5288       | Bool n -> next (); pr "int %s" n
5289       | Int n -> next (); pr "int %s" n
5290       | FileIn n
5291       | FileOut n ->
5292           if not in_daemon then (next (); pr "const char *%s" n)
5293     ) (snd style);
5294   );
5295   pr ")";
5296   if semicolon then pr ";";
5297   if newline then pr "\n"
5298
5299 (* Generate C call arguments, eg "(handle, foo, bar)" *)
5300 and generate_call_args ?handle args =
5301   pr "(";
5302   let comma = ref false in
5303   (match handle with
5304    | None -> ()
5305    | Some handle -> pr "%s" handle; comma := true
5306   );
5307   List.iter (
5308     fun arg ->
5309       if !comma then pr ", ";
5310       comma := true;
5311       pr "%s" (name_of_argt arg)
5312   ) args;
5313   pr ")"
5314
5315 (* Generate the OCaml bindings interface. *)
5316 and generate_ocaml_mli () =
5317   generate_header OCamlStyle LGPLv2;
5318
5319   pr "\
5320 (** For API documentation you should refer to the C API
5321     in the guestfs(3) manual page.  The OCaml API uses almost
5322     exactly the same calls. *)
5323
5324 type t
5325 (** A [guestfs_h] handle. *)
5326
5327 exception Error of string
5328 (** This exception is raised when there is an error. *)
5329
5330 val create : unit -> t
5331
5332 val close : t -> unit
5333 (** Handles are closed by the garbage collector when they become
5334     unreferenced, but callers can also call this in order to
5335     provide predictable cleanup. *)
5336
5337 ";
5338   generate_ocaml_lvm_structure_decls ();
5339
5340   generate_ocaml_stat_structure_decls ();
5341
5342   (* The actions. *)
5343   List.iter (
5344     fun (name, style, _, _, _, shortdesc, _) ->
5345       generate_ocaml_prototype name style;
5346       pr "(** %s *)\n" shortdesc;
5347       pr "\n"
5348   ) all_functions
5349
5350 (* Generate the OCaml bindings implementation. *)
5351 and generate_ocaml_ml () =
5352   generate_header OCamlStyle LGPLv2;
5353
5354   pr "\
5355 type t
5356 exception Error of string
5357 external create : unit -> t = \"ocaml_guestfs_create\"
5358 external close : t -> unit = \"ocaml_guestfs_close\"
5359
5360 let () =
5361   Callback.register_exception \"ocaml_guestfs_error\" (Error \"\")
5362
5363 ";
5364
5365   generate_ocaml_lvm_structure_decls ();
5366
5367   generate_ocaml_stat_structure_decls ();
5368
5369   (* The actions. *)
5370   List.iter (
5371     fun (name, style, _, _, _, shortdesc, _) ->
5372       generate_ocaml_prototype ~is_external:true name style;
5373   ) all_functions
5374
5375 (* Generate the OCaml bindings C implementation. *)
5376 and generate_ocaml_c () =
5377   generate_header CStyle LGPLv2;
5378
5379   pr "\
5380 #include <stdio.h>
5381 #include <stdlib.h>
5382 #include <string.h>
5383
5384 #include <caml/config.h>
5385 #include <caml/alloc.h>
5386 #include <caml/callback.h>
5387 #include <caml/fail.h>
5388 #include <caml/memory.h>
5389 #include <caml/mlvalues.h>
5390 #include <caml/signals.h>
5391
5392 #include <guestfs.h>
5393
5394 #include \"guestfs_c.h\"
5395
5396 /* Copy a hashtable of string pairs into an assoc-list.  We return
5397  * the list in reverse order, but hashtables aren't supposed to be
5398  * ordered anyway.
5399  */
5400 static CAMLprim value
5401 copy_table (char * const * argv)
5402 {
5403   CAMLparam0 ();
5404   CAMLlocal5 (rv, pairv, kv, vv, cons);
5405   int i;
5406
5407   rv = Val_int (0);
5408   for (i = 0; argv[i] != NULL; i += 2) {
5409     kv = caml_copy_string (argv[i]);
5410     vv = caml_copy_string (argv[i+1]);
5411     pairv = caml_alloc (2, 0);
5412     Store_field (pairv, 0, kv);
5413     Store_field (pairv, 1, vv);
5414     cons = caml_alloc (2, 0);
5415     Store_field (cons, 1, rv);
5416     rv = cons;
5417     Store_field (cons, 0, pairv);
5418   }
5419
5420   CAMLreturn (rv);
5421 }
5422
5423 ";
5424
5425   (* LVM struct copy functions. *)
5426   List.iter (
5427     fun (typ, cols) ->
5428       let has_optpercent_col =
5429         List.exists (function (_, `OptPercent) -> true | _ -> false) cols in
5430
5431       pr "static CAMLprim value\n";
5432       pr "copy_lvm_%s (const struct guestfs_lvm_%s *%s)\n" typ typ typ;
5433       pr "{\n";
5434       pr "  CAMLparam0 ();\n";
5435       if has_optpercent_col then
5436         pr "  CAMLlocal3 (rv, v, v2);\n"
5437       else
5438         pr "  CAMLlocal2 (rv, v);\n";
5439       pr "\n";
5440       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
5441       iteri (
5442         fun i col ->
5443           (match col with
5444            | name, `String ->
5445                pr "  v = caml_copy_string (%s->%s);\n" typ name
5446            | name, `UUID ->
5447                pr "  v = caml_alloc_string (32);\n";
5448                pr "  memcpy (String_val (v), %s->%s, 32);\n" typ name
5449            | name, `Bytes
5450            | name, `Int ->
5451                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
5452            | name, `OptPercent ->
5453                pr "  if (%s->%s >= 0) { /* Some %s */\n" typ name name;
5454                pr "    v2 = caml_copy_double (%s->%s);\n" typ name;
5455                pr "    v = caml_alloc (1, 0);\n";
5456                pr "    Store_field (v, 0, v2);\n";
5457                pr "  } else /* None */\n";
5458                pr "    v = Val_int (0);\n";
5459           );
5460           pr "  Store_field (rv, %d, v);\n" i
5461       ) cols;
5462       pr "  CAMLreturn (rv);\n";
5463       pr "}\n";
5464       pr "\n";
5465
5466       pr "static CAMLprim value\n";
5467       pr "copy_lvm_%s_list (const struct guestfs_lvm_%s_list *%ss)\n"
5468         typ typ typ;
5469       pr "{\n";
5470       pr "  CAMLparam0 ();\n";
5471       pr "  CAMLlocal2 (rv, v);\n";
5472       pr "  int i;\n";
5473       pr "\n";
5474       pr "  if (%ss->len == 0)\n" typ;
5475       pr "    CAMLreturn (Atom (0));\n";
5476       pr "  else {\n";
5477       pr "    rv = caml_alloc (%ss->len, 0);\n" typ;
5478       pr "    for (i = 0; i < %ss->len; ++i) {\n" typ;
5479       pr "      v = copy_lvm_%s (&%ss->val[i]);\n" typ typ;
5480       pr "      caml_modify (&Field (rv, i), v);\n";
5481       pr "    }\n";
5482       pr "    CAMLreturn (rv);\n";
5483       pr "  }\n";
5484       pr "}\n";
5485       pr "\n";
5486   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
5487
5488   (* Stat copy functions. *)
5489   List.iter (
5490     fun (typ, cols) ->
5491       pr "static CAMLprim value\n";
5492       pr "copy_%s (const struct guestfs_%s *%s)\n" typ typ typ;
5493       pr "{\n";
5494       pr "  CAMLparam0 ();\n";
5495       pr "  CAMLlocal2 (rv, v);\n";
5496       pr "\n";
5497       pr "  rv = caml_alloc (%d, 0);\n" (List.length cols);
5498       iteri (
5499         fun i col ->
5500           (match col with
5501            | name, `Int ->
5502                pr "  v = caml_copy_int64 (%s->%s);\n" typ name
5503           );
5504           pr "  Store_field (rv, %d, v);\n" i
5505       ) cols;
5506       pr "  CAMLreturn (rv);\n";
5507       pr "}\n";
5508       pr "\n";
5509   ) ["stat", stat_cols; "statvfs", statvfs_cols];
5510
5511   (* The wrappers. *)
5512   List.iter (
5513     fun (name, style, _, _, _, _, _) ->
5514       let params =
5515         "gv" :: List.map (fun arg -> name_of_argt arg ^ "v") (snd style) in
5516
5517       pr "CAMLprim value\n";
5518       pr "ocaml_guestfs_%s (value %s" name (List.hd params);
5519       List.iter (pr ", value %s") (List.tl params);
5520       pr ")\n";
5521       pr "{\n";
5522
5523       (match params with
5524        | [p1; p2; p3; p4; p5] ->
5525            pr "  CAMLparam5 (%s);\n" (String.concat ", " params)
5526        | p1 :: p2 :: p3 :: p4 :: p5 :: rest ->
5527            pr "  CAMLparam5 (%s);\n" (String.concat ", " [p1; p2; p3; p4; p5]);
5528            pr "  CAMLxparam%d (%s);\n"
5529              (List.length rest) (String.concat ", " rest)
5530        | ps ->
5531            pr "  CAMLparam%d (%s);\n" (List.length ps) (String.concat ", " ps)
5532       );
5533       pr "  CAMLlocal1 (rv);\n";
5534       pr "\n";
5535
5536       pr "  guestfs_h *g = Guestfs_val (gv);\n";
5537       pr "  if (g == NULL)\n";
5538       pr "    caml_failwith (\"%s: used handle after closing it\");\n" name;
5539       pr "\n";
5540
5541       List.iter (
5542         function
5543         | String n
5544         | FileIn n
5545         | FileOut n ->
5546             pr "  const char *%s = String_val (%sv);\n" n n
5547         | OptString n ->
5548             pr "  const char *%s =\n" n;
5549             pr "    %sv != Val_int (0) ? String_val (Field (%sv, 0)) : NULL;\n"
5550               n n
5551         | StringList n ->
5552             pr "  char **%s = ocaml_guestfs_strings_val (g, %sv);\n" n n
5553         | Bool n ->
5554             pr "  int %s = Bool_val (%sv);\n" n n
5555         | Int n ->
5556             pr "  int %s = Int_val (%sv);\n" n n
5557       ) (snd style);
5558       let error_code =
5559         match fst style with
5560         | RErr -> pr "  int r;\n"; "-1"
5561         | RInt _ -> pr "  int r;\n"; "-1"
5562         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
5563         | RBool _ -> pr "  int r;\n"; "-1"
5564         | RConstString _ -> pr "  const char *r;\n"; "NULL"
5565         | RString _ -> pr "  char *r;\n"; "NULL"
5566         | RStringList _ ->
5567             pr "  int i;\n";
5568             pr "  char **r;\n";
5569             "NULL"
5570         | RIntBool _ ->
5571             pr "  struct guestfs_int_bool *r;\n"; "NULL"
5572         | RPVList _ ->
5573             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
5574         | RVGList _ ->
5575             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
5576         | RLVList _ ->
5577             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
5578         | RStat _ ->
5579             pr "  struct guestfs_stat *r;\n"; "NULL"
5580         | RStatVFS _ ->
5581             pr "  struct guestfs_statvfs *r;\n"; "NULL"
5582         | RHashtable _ ->
5583             pr "  int i;\n";
5584             pr "  char **r;\n";
5585             "NULL" in
5586       pr "\n";
5587
5588       pr "  caml_enter_blocking_section ();\n";
5589       pr "  r = guestfs_%s " name;
5590       generate_call_args ~handle:"g" (snd style);
5591       pr ";\n";
5592       pr "  caml_leave_blocking_section ();\n";
5593
5594       List.iter (
5595         function
5596         | StringList n ->
5597             pr "  ocaml_guestfs_free_strings (%s);\n" n;
5598         | String _ | OptString _ | Bool _ | Int _ | FileIn _ | FileOut _ -> ()
5599       ) (snd style);
5600
5601       pr "  if (r == %s)\n" error_code;
5602       pr "    ocaml_guestfs_raise_error (g, \"%s\");\n" name;
5603       pr "\n";
5604
5605       (match fst style with
5606        | RErr -> pr "  rv = Val_unit;\n"
5607        | RInt _ -> pr "  rv = Val_int (r);\n"
5608        | RInt64 _ ->
5609            pr "  rv = caml_copy_int64 (r);\n"
5610        | RBool _ -> pr "  rv = Val_bool (r);\n"
5611        | RConstString _ -> pr "  rv = caml_copy_string (r);\n"
5612        | RString _ ->
5613            pr "  rv = caml_copy_string (r);\n";
5614            pr "  free (r);\n"
5615        | RStringList _ ->
5616            pr "  rv = caml_copy_string_array ((const char **) r);\n";
5617            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
5618            pr "  free (r);\n"
5619        | RIntBool _ ->
5620            pr "  rv = caml_alloc (2, 0);\n";
5621            pr "  Store_field (rv, 0, Val_int (r->i));\n";
5622            pr "  Store_field (rv, 1, Val_bool (r->b));\n";
5623            pr "  guestfs_free_int_bool (r);\n";
5624        | RPVList _ ->
5625            pr "  rv = copy_lvm_pv_list (r);\n";
5626            pr "  guestfs_free_lvm_pv_list (r);\n";
5627        | RVGList _ ->
5628            pr "  rv = copy_lvm_vg_list (r);\n";
5629            pr "  guestfs_free_lvm_vg_list (r);\n";
5630        | RLVList _ ->
5631            pr "  rv = copy_lvm_lv_list (r);\n";
5632            pr "  guestfs_free_lvm_lv_list (r);\n";
5633        | RStat _ ->
5634            pr "  rv = copy_stat (r);\n";
5635            pr "  free (r);\n";
5636        | RStatVFS _ ->
5637            pr "  rv = copy_statvfs (r);\n";
5638            pr "  free (r);\n";
5639        | RHashtable _ ->
5640            pr "  rv = copy_table (r);\n";
5641            pr "  for (i = 0; r[i] != NULL; ++i) free (r[i]);\n";
5642            pr "  free (r);\n";
5643       );
5644
5645       pr "  CAMLreturn (rv);\n";
5646       pr "}\n";
5647       pr "\n";
5648
5649       if List.length params > 5 then (
5650         pr "CAMLprim value\n";
5651         pr "ocaml_guestfs_%s_byte (value *argv, int argn)\n" name;
5652         pr "{\n";
5653         pr "  return ocaml_guestfs_%s (argv[0]" name;
5654         iteri (fun i _ -> pr ", argv[%d]" i) (List.tl params);
5655         pr ");\n";
5656         pr "}\n";
5657         pr "\n"
5658       )
5659   ) all_functions
5660
5661 and generate_ocaml_lvm_structure_decls () =
5662   List.iter (
5663     fun (typ, cols) ->
5664       pr "type lvm_%s = {\n" typ;
5665       List.iter (
5666         function
5667         | name, `String -> pr "  %s : string;\n" name
5668         | name, `UUID -> pr "  %s : string;\n" name
5669         | name, `Bytes -> pr "  %s : int64;\n" name
5670         | name, `Int -> pr "  %s : int64;\n" name
5671         | name, `OptPercent -> pr "  %s : float option;\n" name
5672       ) cols;
5673       pr "}\n";
5674       pr "\n"
5675   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
5676
5677 and generate_ocaml_stat_structure_decls () =
5678   List.iter (
5679     fun (typ, cols) ->
5680       pr "type %s = {\n" typ;
5681       List.iter (
5682         function
5683         | name, `Int -> pr "  %s : int64;\n" name
5684       ) cols;
5685       pr "}\n";
5686       pr "\n"
5687   ) ["stat", stat_cols; "statvfs", statvfs_cols]
5688
5689 and generate_ocaml_prototype ?(is_external = false) name style =
5690   if is_external then pr "external " else pr "val ";
5691   pr "%s : t -> " name;
5692   List.iter (
5693     function
5694     | String _ | FileIn _ | FileOut _ -> pr "string -> "
5695     | OptString _ -> pr "string option -> "
5696     | StringList _ -> pr "string array -> "
5697     | Bool _ -> pr "bool -> "
5698     | Int _ -> pr "int -> "
5699   ) (snd style);
5700   (match fst style with
5701    | RErr -> pr "unit" (* all errors are turned into exceptions *)
5702    | RInt _ -> pr "int"
5703    | RInt64 _ -> pr "int64"
5704    | RBool _ -> pr "bool"
5705    | RConstString _ -> pr "string"
5706    | RString _ -> pr "string"
5707    | RStringList _ -> pr "string array"
5708    | RIntBool _ -> pr "int * bool"
5709    | RPVList _ -> pr "lvm_pv array"
5710    | RVGList _ -> pr "lvm_vg array"
5711    | RLVList _ -> pr "lvm_lv array"
5712    | RStat _ -> pr "stat"
5713    | RStatVFS _ -> pr "statvfs"
5714    | RHashtable _ -> pr "(string * string) list"
5715   );
5716   if is_external then (
5717     pr " = ";
5718     if List.length (snd style) + 1 > 5 then
5719       pr "\"ocaml_guestfs_%s_byte\" " name;
5720     pr "\"ocaml_guestfs_%s\"" name
5721   );
5722   pr "\n"
5723
5724 (* Generate Perl xs code, a sort of crazy variation of C with macros. *)
5725 and generate_perl_xs () =
5726   generate_header CStyle LGPLv2;
5727
5728   pr "\
5729 #include \"EXTERN.h\"
5730 #include \"perl.h\"
5731 #include \"XSUB.h\"
5732
5733 #include <guestfs.h>
5734
5735 #ifndef PRId64
5736 #define PRId64 \"lld\"
5737 #endif
5738
5739 static SV *
5740 my_newSVll(long long val) {
5741 #ifdef USE_64_BIT_ALL
5742   return newSViv(val);
5743 #else
5744   char buf[100];
5745   int len;
5746   len = snprintf(buf, 100, \"%%\" PRId64, val);
5747   return newSVpv(buf, len);
5748 #endif
5749 }
5750
5751 #ifndef PRIu64
5752 #define PRIu64 \"llu\"
5753 #endif
5754
5755 static SV *
5756 my_newSVull(unsigned long long val) {
5757 #ifdef USE_64_BIT_ALL
5758   return newSVuv(val);
5759 #else
5760   char buf[100];
5761   int len;
5762   len = snprintf(buf, 100, \"%%\" PRIu64, val);
5763   return newSVpv(buf, len);
5764 #endif
5765 }
5766
5767 /* http://www.perlmonks.org/?node_id=680842 */
5768 static char **
5769 XS_unpack_charPtrPtr (SV *arg) {
5770   char **ret;
5771   AV *av;
5772   I32 i;
5773
5774   if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV)
5775     croak (\"array reference expected\");
5776
5777   av = (AV *)SvRV (arg);
5778   ret = malloc ((av_len (av) + 1 + 1) * sizeof (char *));
5779   if (!ret)
5780     croak (\"malloc failed\");
5781
5782   for (i = 0; i <= av_len (av); i++) {
5783     SV **elem = av_fetch (av, i, 0);
5784
5785     if (!elem || !*elem)
5786       croak (\"missing element in list\");
5787
5788     ret[i] = SvPV_nolen (*elem);
5789   }
5790
5791   ret[i] = NULL;
5792
5793   return ret;
5794 }
5795
5796 MODULE = Sys::Guestfs  PACKAGE = Sys::Guestfs
5797
5798 PROTOTYPES: ENABLE
5799
5800 guestfs_h *
5801 _create ()
5802    CODE:
5803       RETVAL = guestfs_create ();
5804       if (!RETVAL)
5805         croak (\"could not create guestfs handle\");
5806       guestfs_set_error_handler (RETVAL, NULL, NULL);
5807  OUTPUT:
5808       RETVAL
5809
5810 void
5811 DESTROY (g)
5812       guestfs_h *g;
5813  PPCODE:
5814       guestfs_close (g);
5815
5816 ";
5817
5818   List.iter (
5819     fun (name, style, _, _, _, _, _) ->
5820       (match fst style with
5821        | RErr -> pr "void\n"
5822        | RInt _ -> pr "SV *\n"
5823        | RInt64 _ -> pr "SV *\n"
5824        | RBool _ -> pr "SV *\n"
5825        | RConstString _ -> pr "SV *\n"
5826        | RString _ -> pr "SV *\n"
5827        | RStringList _
5828        | RIntBool _
5829        | RPVList _ | RVGList _ | RLVList _
5830        | RStat _ | RStatVFS _
5831        | RHashtable _ ->
5832            pr "void\n" (* all lists returned implictly on the stack *)
5833       );
5834       (* Call and arguments. *)
5835       pr "%s " name;
5836       generate_call_args ~handle:"g" (snd style);
5837       pr "\n";
5838       pr "      guestfs_h *g;\n";
5839       iteri (
5840         fun i ->
5841           function
5842           | String n | FileIn n | FileOut n -> pr "      char *%s;\n" n
5843           | OptString n ->
5844               (* http://www.perlmonks.org/?node_id=554277
5845                * Note that the implicit handle argument means we have
5846                * to add 1 to the ST(x) operator.
5847                *)
5848               pr "      char *%s = SvOK(ST(%d)) ? SvPV_nolen(ST(%d)) : NULL;\n" n (i+1) (i+1)
5849           | StringList n -> pr "      char **%s;\n" n
5850           | Bool n -> pr "      int %s;\n" n
5851           | Int n -> pr "      int %s;\n" n
5852       ) (snd style);
5853
5854       let do_cleanups () =
5855         List.iter (
5856           function
5857           | String _ | OptString _ | Bool _ | Int _
5858           | FileIn _ | FileOut _ -> ()
5859           | StringList n -> pr "      free (%s);\n" n
5860         ) (snd style)
5861       in
5862
5863       (* Code. *)
5864       (match fst style with
5865        | RErr ->
5866            pr "PREINIT:\n";
5867            pr "      int r;\n";
5868            pr " PPCODE:\n";
5869            pr "      r = guestfs_%s " name;
5870            generate_call_args ~handle:"g" (snd style);
5871            pr ";\n";
5872            do_cleanups ();
5873            pr "      if (r == -1)\n";
5874            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5875        | RInt n
5876        | RBool n ->
5877            pr "PREINIT:\n";
5878            pr "      int %s;\n" n;
5879            pr "   CODE:\n";
5880            pr "      %s = guestfs_%s " n name;
5881            generate_call_args ~handle:"g" (snd style);
5882            pr ";\n";
5883            do_cleanups ();
5884            pr "      if (%s == -1)\n" n;
5885            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5886            pr "      RETVAL = newSViv (%s);\n" n;
5887            pr " OUTPUT:\n";
5888            pr "      RETVAL\n"
5889        | RInt64 n ->
5890            pr "PREINIT:\n";
5891            pr "      int64_t %s;\n" n;
5892            pr "   CODE:\n";
5893            pr "      %s = guestfs_%s " n name;
5894            generate_call_args ~handle:"g" (snd style);
5895            pr ";\n";
5896            do_cleanups ();
5897            pr "      if (%s == -1)\n" n;
5898            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5899            pr "      RETVAL = my_newSVll (%s);\n" n;
5900            pr " OUTPUT:\n";
5901            pr "      RETVAL\n"
5902        | RConstString n ->
5903            pr "PREINIT:\n";
5904            pr "      const char *%s;\n" n;
5905            pr "   CODE:\n";
5906            pr "      %s = guestfs_%s " n name;
5907            generate_call_args ~handle:"g" (snd style);
5908            pr ";\n";
5909            do_cleanups ();
5910            pr "      if (%s == NULL)\n" n;
5911            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5912            pr "      RETVAL = newSVpv (%s, 0);\n" n;
5913            pr " OUTPUT:\n";
5914            pr "      RETVAL\n"
5915        | RString n ->
5916            pr "PREINIT:\n";
5917            pr "      char *%s;\n" n;
5918            pr "   CODE:\n";
5919            pr "      %s = guestfs_%s " n name;
5920            generate_call_args ~handle:"g" (snd style);
5921            pr ";\n";
5922            do_cleanups ();
5923            pr "      if (%s == NULL)\n" n;
5924            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5925            pr "      RETVAL = newSVpv (%s, 0);\n" n;
5926            pr "      free (%s);\n" n;
5927            pr " OUTPUT:\n";
5928            pr "      RETVAL\n"
5929        | RStringList n | RHashtable n ->
5930            pr "PREINIT:\n";
5931            pr "      char **%s;\n" n;
5932            pr "      int i, n;\n";
5933            pr " PPCODE:\n";
5934            pr "      %s = guestfs_%s " n name;
5935            generate_call_args ~handle:"g" (snd style);
5936            pr ";\n";
5937            do_cleanups ();
5938            pr "      if (%s == NULL)\n" n;
5939            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5940            pr "      for (n = 0; %s[n] != NULL; ++n) /**/;\n" n;
5941            pr "      EXTEND (SP, n);\n";
5942            pr "      for (i = 0; i < n; ++i) {\n";
5943            pr "        PUSHs (sv_2mortal (newSVpv (%s[i], 0)));\n" n;
5944            pr "        free (%s[i]);\n" n;
5945            pr "      }\n";
5946            pr "      free (%s);\n" n;
5947        | RIntBool _ ->
5948            pr "PREINIT:\n";
5949            pr "      struct guestfs_int_bool *r;\n";
5950            pr " PPCODE:\n";
5951            pr "      r = guestfs_%s " name;
5952            generate_call_args ~handle:"g" (snd style);
5953            pr ";\n";
5954            do_cleanups ();
5955            pr "      if (r == NULL)\n";
5956            pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5957            pr "      EXTEND (SP, 2);\n";
5958            pr "      PUSHs (sv_2mortal (newSViv (r->i)));\n";
5959            pr "      PUSHs (sv_2mortal (newSViv (r->b)));\n";
5960            pr "      guestfs_free_int_bool (r);\n";
5961        | RPVList n ->
5962            generate_perl_lvm_code "pv" pv_cols name style n do_cleanups
5963        | RVGList n ->
5964            generate_perl_lvm_code "vg" vg_cols name style n do_cleanups
5965        | RLVList n ->
5966            generate_perl_lvm_code "lv" lv_cols name style n do_cleanups
5967        | RStat n ->
5968            generate_perl_stat_code "stat" stat_cols name style n do_cleanups
5969        | RStatVFS n ->
5970            generate_perl_stat_code
5971              "statvfs" statvfs_cols name style n do_cleanups
5972       );
5973
5974       pr "\n"
5975   ) all_functions
5976
5977 and generate_perl_lvm_code typ cols name style n do_cleanups =
5978   pr "PREINIT:\n";
5979   pr "      struct guestfs_lvm_%s_list *%s;\n" typ n;
5980   pr "      int i;\n";
5981   pr "      HV *hv;\n";
5982   pr " PPCODE:\n";
5983   pr "      %s = guestfs_%s " n name;
5984   generate_call_args ~handle:"g" (snd style);
5985   pr ";\n";
5986   do_cleanups ();
5987   pr "      if (%s == NULL)\n" n;
5988   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
5989   pr "      EXTEND (SP, %s->len);\n" n;
5990   pr "      for (i = 0; i < %s->len; ++i) {\n" n;
5991   pr "        hv = newHV ();\n";
5992   List.iter (
5993     function
5994     | name, `String ->
5995         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 0), 0);\n"
5996           name (String.length name) n name
5997     | name, `UUID ->
5998         pr "        (void) hv_store (hv, \"%s\", %d, newSVpv (%s->val[i].%s, 32), 0);\n"
5999           name (String.length name) n name
6000     | name, `Bytes ->
6001         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVull (%s->val[i].%s), 0);\n"
6002           name (String.length name) n name
6003     | name, `Int ->
6004         pr "        (void) hv_store (hv, \"%s\", %d, my_newSVll (%s->val[i].%s), 0);\n"
6005           name (String.length name) n name
6006     | name, `OptPercent ->
6007         pr "        (void) hv_store (hv, \"%s\", %d, newSVnv (%s->val[i].%s), 0);\n"
6008           name (String.length name) n name
6009   ) cols;
6010   pr "        PUSHs (sv_2mortal ((SV *) hv));\n";
6011   pr "      }\n";
6012   pr "      guestfs_free_lvm_%s_list (%s);\n" typ n
6013
6014 and generate_perl_stat_code typ cols name style n do_cleanups =
6015   pr "PREINIT:\n";
6016   pr "      struct guestfs_%s *%s;\n" typ n;
6017   pr " PPCODE:\n";
6018   pr "      %s = guestfs_%s " n name;
6019   generate_call_args ~handle:"g" (snd style);
6020   pr ";\n";
6021   do_cleanups ();
6022   pr "      if (%s == NULL)\n" n;
6023   pr "        croak (\"%s: %%s\", guestfs_last_error (g));\n" name;
6024   pr "      EXTEND (SP, %d);\n" (List.length cols);
6025   List.iter (
6026     function
6027     | name, `Int ->
6028         pr "      PUSHs (sv_2mortal (my_newSVll (%s->%s)));\n" n name
6029   ) cols;
6030   pr "      free (%s);\n" n
6031
6032 (* Generate Sys/Guestfs.pm. *)
6033 and generate_perl_pm () =
6034   generate_header HashStyle LGPLv2;
6035
6036   pr "\
6037 =pod
6038
6039 =head1 NAME
6040
6041 Sys::Guestfs - Perl bindings for libguestfs
6042
6043 =head1 SYNOPSIS
6044
6045  use Sys::Guestfs;
6046  
6047  my $h = Sys::Guestfs->new ();
6048  $h->add_drive ('guest.img');
6049  $h->launch ();
6050  $h->wait_ready ();
6051  $h->mount ('/dev/sda1', '/');
6052  $h->touch ('/hello');
6053  $h->sync ();
6054
6055 =head1 DESCRIPTION
6056
6057 The C<Sys::Guestfs> module provides a Perl XS binding to the
6058 libguestfs API for examining and modifying virtual machine
6059 disk images.
6060
6061 Amongst the things this is good for: making batch configuration
6062 changes to guests, getting disk used/free statistics (see also:
6063 virt-df), migrating between virtualization systems (see also:
6064 virt-p2v), performing partial backups, performing partial guest
6065 clones, cloning guests and changing registry/UUID/hostname info, and
6066 much else besides.
6067
6068 Libguestfs uses Linux kernel and qemu code, and can access any type of
6069 guest filesystem that Linux and qemu can, including but not limited
6070 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
6071 schemes, qcow, qcow2, vmdk.
6072
6073 Libguestfs provides ways to enumerate guest storage (eg. partitions,
6074 LVs, what filesystem is in each LV, etc.).  It can also run commands
6075 in the context of the guest.  Also you can access filesystems over FTP.
6076
6077 =head1 ERRORS
6078
6079 All errors turn into calls to C<croak> (see L<Carp(3)>).
6080
6081 =head1 METHODS
6082
6083 =over 4
6084
6085 =cut
6086
6087 package Sys::Guestfs;
6088
6089 use strict;
6090 use warnings;
6091
6092 require XSLoader;
6093 XSLoader::load ('Sys::Guestfs');
6094
6095 =item $h = Sys::Guestfs->new ();
6096
6097 Create a new guestfs handle.
6098
6099 =cut
6100
6101 sub new {
6102   my $proto = shift;
6103   my $class = ref ($proto) || $proto;
6104
6105   my $self = Sys::Guestfs::_create ();
6106   bless $self, $class;
6107   return $self;
6108 }
6109
6110 ";
6111
6112   (* Actions.  We only need to print documentation for these as
6113    * they are pulled in from the XS code automatically.
6114    *)
6115   List.iter (
6116     fun (name, style, _, flags, _, _, longdesc) ->
6117       if not (List.mem NotInDocs flags) then (
6118         let longdesc = replace_str longdesc "C<guestfs_" "C<$h-E<gt>" in
6119         pr "=item ";
6120         generate_perl_prototype name style;
6121         pr "\n\n";
6122         pr "%s\n\n" longdesc;
6123         if List.mem ProtocolLimitWarning flags then
6124           pr "%s\n\n" protocol_limit_warning;
6125         if List.mem DangerWillRobinson flags then
6126           pr "%s\n\n" danger_will_robinson
6127       )
6128   ) all_functions_sorted;
6129
6130   (* End of file. *)
6131   pr "\
6132 =cut
6133
6134 1;
6135
6136 =back
6137
6138 =head1 COPYRIGHT
6139
6140 Copyright (C) 2009 Red Hat Inc.
6141
6142 =head1 LICENSE
6143
6144 Please see the file COPYING.LIB for the full license.
6145
6146 =head1 SEE ALSO
6147
6148 L<guestfs(3)>, L<guestfish(1)>.
6149
6150 =cut
6151 "
6152
6153 and generate_perl_prototype name style =
6154   (match fst style with
6155    | RErr -> ()
6156    | RBool n
6157    | RInt n
6158    | RInt64 n
6159    | RConstString n
6160    | RString n -> pr "$%s = " n
6161    | RIntBool (n, m) -> pr "($%s, $%s) = " n m
6162    | RStringList n
6163    | RPVList n
6164    | RVGList n
6165    | RLVList n -> pr "@%s = " n
6166    | RStat n
6167    | RStatVFS n
6168    | RHashtable n -> pr "%%%s = " n
6169   );
6170   pr "$h->%s (" name;
6171   let comma = ref false in
6172   List.iter (
6173     fun arg ->
6174       if !comma then pr ", ";
6175       comma := true;
6176       match arg with
6177       | String n | OptString n | Bool n | Int n | FileIn n | FileOut n ->
6178           pr "$%s" n
6179       | StringList n ->
6180           pr "\\@%s" n
6181   ) (snd style);
6182   pr ");"
6183
6184 (* Generate Python C module. *)
6185 and generate_python_c () =
6186   generate_header CStyle LGPLv2;
6187
6188   pr "\
6189 #include <stdio.h>
6190 #include <stdlib.h>
6191 #include <assert.h>
6192
6193 #include <Python.h>
6194
6195 #include \"guestfs.h\"
6196
6197 typedef struct {
6198   PyObject_HEAD
6199   guestfs_h *g;
6200 } Pyguestfs_Object;
6201
6202 static guestfs_h *
6203 get_handle (PyObject *obj)
6204 {
6205   assert (obj);
6206   assert (obj != Py_None);
6207   return ((Pyguestfs_Object *) obj)->g;
6208 }
6209
6210 static PyObject *
6211 put_handle (guestfs_h *g)
6212 {
6213   assert (g);
6214   return
6215     PyCObject_FromVoidPtrAndDesc ((void *) g, (char *) \"guestfs_h\", NULL);
6216 }
6217
6218 /* This list should be freed (but not the strings) after use. */
6219 static const char **
6220 get_string_list (PyObject *obj)
6221 {
6222   int i, len;
6223   const char **r;
6224
6225   assert (obj);
6226
6227   if (!PyList_Check (obj)) {
6228     PyErr_SetString (PyExc_RuntimeError, \"expecting a list parameter\");
6229     return NULL;
6230   }
6231
6232   len = PyList_Size (obj);
6233   r = malloc (sizeof (char *) * (len+1));
6234   if (r == NULL) {
6235     PyErr_SetString (PyExc_RuntimeError, \"get_string_list: out of memory\");
6236     return NULL;
6237   }
6238
6239   for (i = 0; i < len; ++i)
6240     r[i] = PyString_AsString (PyList_GetItem (obj, i));
6241   r[len] = NULL;
6242
6243   return r;
6244 }
6245
6246 static PyObject *
6247 put_string_list (char * const * const argv)
6248 {
6249   PyObject *list;
6250   int argc, i;
6251
6252   for (argc = 0; argv[argc] != NULL; ++argc)
6253     ;
6254
6255   list = PyList_New (argc);
6256   for (i = 0; i < argc; ++i)
6257     PyList_SetItem (list, i, PyString_FromString (argv[i]));
6258
6259   return list;
6260 }
6261
6262 static PyObject *
6263 put_table (char * const * const argv)
6264 {
6265   PyObject *list, *item;
6266   int argc, i;
6267
6268   for (argc = 0; argv[argc] != NULL; ++argc)
6269     ;
6270
6271   list = PyList_New (argc >> 1);
6272   for (i = 0; i < argc; i += 2) {
6273     item = PyTuple_New (2);
6274     PyTuple_SetItem (item, 0, PyString_FromString (argv[i]));
6275     PyTuple_SetItem (item, 1, PyString_FromString (argv[i+1]));
6276     PyList_SetItem (list, i >> 1, item);
6277   }
6278
6279   return list;
6280 }
6281
6282 static void
6283 free_strings (char **argv)
6284 {
6285   int argc;
6286
6287   for (argc = 0; argv[argc] != NULL; ++argc)
6288     free (argv[argc]);
6289   free (argv);
6290 }
6291
6292 static PyObject *
6293 py_guestfs_create (PyObject *self, PyObject *args)
6294 {
6295   guestfs_h *g;
6296
6297   g = guestfs_create ();
6298   if (g == NULL) {
6299     PyErr_SetString (PyExc_RuntimeError,
6300                      \"guestfs.create: failed to allocate handle\");
6301     return NULL;
6302   }
6303   guestfs_set_error_handler (g, NULL, NULL);
6304   return put_handle (g);
6305 }
6306
6307 static PyObject *
6308 py_guestfs_close (PyObject *self, PyObject *args)
6309 {
6310   PyObject *py_g;
6311   guestfs_h *g;
6312
6313   if (!PyArg_ParseTuple (args, (char *) \"O:guestfs_close\", &py_g))
6314     return NULL;
6315   g = get_handle (py_g);
6316
6317   guestfs_close (g);
6318
6319   Py_INCREF (Py_None);
6320   return Py_None;
6321 }
6322
6323 ";
6324
6325   (* LVM structures, turned into Python dictionaries. *)
6326   List.iter (
6327     fun (typ, cols) ->
6328       pr "static PyObject *\n";
6329       pr "put_lvm_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
6330       pr "{\n";
6331       pr "  PyObject *dict;\n";
6332       pr "\n";
6333       pr "  dict = PyDict_New ();\n";
6334       List.iter (
6335         function
6336         | name, `String ->
6337             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
6338             pr "                        PyString_FromString (%s->%s));\n"
6339               typ name
6340         | name, `UUID ->
6341             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
6342             pr "                        PyString_FromStringAndSize (%s->%s, 32));\n"
6343               typ name
6344         | name, `Bytes ->
6345             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
6346             pr "                        PyLong_FromUnsignedLongLong (%s->%s));\n"
6347               typ name
6348         | name, `Int ->
6349             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
6350             pr "                        PyLong_FromLongLong (%s->%s));\n"
6351               typ name
6352         | name, `OptPercent ->
6353             pr "  if (%s->%s >= 0)\n" typ name;
6354             pr "    PyDict_SetItemString (dict, \"%s\",\n" name;
6355             pr "                          PyFloat_FromDouble ((double) %s->%s));\n"
6356               typ name;
6357             pr "  else {\n";
6358             pr "    Py_INCREF (Py_None);\n";
6359             pr "    PyDict_SetItemString (dict, \"%s\", Py_None);" name;
6360             pr "  }\n"
6361       ) cols;
6362       pr "  return dict;\n";
6363       pr "};\n";
6364       pr "\n";
6365
6366       pr "static PyObject *\n";
6367       pr "put_lvm_%s_list (struct guestfs_lvm_%s_list *%ss)\n" typ typ typ;
6368       pr "{\n";
6369       pr "  PyObject *list;\n";
6370       pr "  int i;\n";
6371       pr "\n";
6372       pr "  list = PyList_New (%ss->len);\n" typ;
6373       pr "  for (i = 0; i < %ss->len; ++i)\n" typ;
6374       pr "    PyList_SetItem (list, i, put_lvm_%s (&%ss->val[i]));\n" typ typ;
6375       pr "  return list;\n";
6376       pr "};\n";
6377       pr "\n"
6378   ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
6379
6380   (* Stat structures, turned into Python dictionaries. *)
6381   List.iter (
6382     fun (typ, cols) ->
6383       pr "static PyObject *\n";
6384       pr "put_%s (struct guestfs_%s *%s)\n" typ typ typ;
6385       pr "{\n";
6386       pr "  PyObject *dict;\n";
6387       pr "\n";
6388       pr "  dict = PyDict_New ();\n";
6389       List.iter (
6390         function
6391         | name, `Int ->
6392             pr "  PyDict_SetItemString (dict, \"%s\",\n" name;
6393             pr "                        PyLong_FromLongLong (%s->%s));\n"
6394               typ name
6395       ) cols;
6396       pr "  return dict;\n";
6397       pr "};\n";
6398       pr "\n";
6399   ) ["stat", stat_cols; "statvfs", statvfs_cols];
6400
6401   (* Python wrapper functions. *)
6402   List.iter (
6403     fun (name, style, _, _, _, _, _) ->
6404       pr "static PyObject *\n";
6405       pr "py_guestfs_%s (PyObject *self, PyObject *args)\n" name;
6406       pr "{\n";
6407
6408       pr "  PyObject *py_g;\n";
6409       pr "  guestfs_h *g;\n";
6410       pr "  PyObject *py_r;\n";
6411
6412       let error_code =
6413         match fst style with
6414         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
6415         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
6416         | RConstString _ -> pr "  const char *r;\n"; "NULL"
6417         | RString _ -> pr "  char *r;\n"; "NULL"
6418         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
6419         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
6420         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
6421         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
6422         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
6423         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
6424         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
6425
6426       List.iter (
6427         function
6428         | String n | FileIn n | FileOut n -> pr "  const char *%s;\n" n
6429         | OptString n -> pr "  const char *%s;\n" n
6430         | StringList n ->
6431             pr "  PyObject *py_%s;\n" n;
6432             pr "  const char **%s;\n" n
6433         | Bool n -> pr "  int %s;\n" n
6434         | Int n -> pr "  int %s;\n" n
6435       ) (snd style);
6436
6437       pr "\n";
6438
6439       (* Convert the parameters. *)
6440       pr "  if (!PyArg_ParseTuple (args, (char *) \"O";
6441       List.iter (
6442         function
6443         | String _ | FileIn _ | FileOut _ -> pr "s"
6444         | OptString _ -> pr "z"
6445         | StringList _ -> pr "O"
6446         | Bool _ -> pr "i" (* XXX Python has booleans? *)
6447         | Int _ -> pr "i"
6448       ) (snd style);
6449       pr ":guestfs_%s\",\n" name;
6450       pr "                         &py_g";
6451       List.iter (
6452         function
6453         | String n | FileIn n | FileOut n -> pr ", &%s" n
6454         | OptString n -> pr ", &%s" n
6455         | StringList n -> pr ", &py_%s" n
6456         | Bool n -> pr ", &%s" n
6457         | Int n -> pr ", &%s" n
6458       ) (snd style);
6459
6460       pr "))\n";
6461       pr "    return NULL;\n";
6462
6463       pr "  g = get_handle (py_g);\n";
6464       List.iter (
6465         function
6466         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
6467         | StringList n ->
6468             pr "  %s = get_string_list (py_%s);\n" n n;
6469             pr "  if (!%s) return NULL;\n" n
6470       ) (snd style);
6471
6472       pr "\n";
6473
6474       pr "  r = guestfs_%s " name;
6475       generate_call_args ~handle:"g" (snd style);
6476       pr ";\n";
6477
6478       List.iter (
6479         function
6480         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
6481         | StringList n ->
6482             pr "  free (%s);\n" n
6483       ) (snd style);
6484
6485       pr "  if (r == %s) {\n" error_code;
6486       pr "    PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g));\n";
6487       pr "    return NULL;\n";
6488       pr "  }\n";
6489       pr "\n";
6490
6491       (match fst style with
6492        | RErr ->
6493            pr "  Py_INCREF (Py_None);\n";
6494            pr "  py_r = Py_None;\n"
6495        | RInt _
6496        | RBool _ -> pr "  py_r = PyInt_FromLong ((long) r);\n"
6497        | RInt64 _ -> pr "  py_r = PyLong_FromLongLong (r);\n"
6498        | RConstString _ -> pr "  py_r = PyString_FromString (r);\n"
6499        | RString _ ->
6500            pr "  py_r = PyString_FromString (r);\n";
6501            pr "  free (r);\n"
6502        | RStringList _ ->
6503            pr "  py_r = put_string_list (r);\n";
6504            pr "  free_strings (r);\n"
6505        | RIntBool _ ->
6506            pr "  py_r = PyTuple_New (2);\n";
6507            pr "  PyTuple_SetItem (py_r, 0, PyInt_FromLong ((long) r->i));\n";
6508            pr "  PyTuple_SetItem (py_r, 1, PyInt_FromLong ((long) r->b));\n";
6509            pr "  guestfs_free_int_bool (r);\n"
6510        | RPVList n ->
6511            pr "  py_r = put_lvm_pv_list (r);\n";
6512            pr "  guestfs_free_lvm_pv_list (r);\n"
6513        | RVGList n ->
6514            pr "  py_r = put_lvm_vg_list (r);\n";
6515            pr "  guestfs_free_lvm_vg_list (r);\n"
6516        | RLVList n ->
6517            pr "  py_r = put_lvm_lv_list (r);\n";
6518            pr "  guestfs_free_lvm_lv_list (r);\n"
6519        | RStat n ->
6520            pr "  py_r = put_stat (r);\n";
6521            pr "  free (r);\n"
6522        | RStatVFS n ->
6523            pr "  py_r = put_statvfs (r);\n";
6524            pr "  free (r);\n"
6525        | RHashtable n ->
6526            pr "  py_r = put_table (r);\n";
6527            pr "  free_strings (r);\n"
6528       );
6529
6530       pr "  return py_r;\n";
6531       pr "}\n";
6532       pr "\n"
6533   ) all_functions;
6534
6535   (* Table of functions. *)
6536   pr "static PyMethodDef methods[] = {\n";
6537   pr "  { (char *) \"create\", py_guestfs_create, METH_VARARGS, NULL },\n";
6538   pr "  { (char *) \"close\", py_guestfs_close, METH_VARARGS, NULL },\n";
6539   List.iter (
6540     fun (name, _, _, _, _, _, _) ->
6541       pr "  { (char *) \"%s\", py_guestfs_%s, METH_VARARGS, NULL },\n"
6542         name name
6543   ) all_functions;
6544   pr "  { NULL, NULL, 0, NULL }\n";
6545   pr "};\n";
6546   pr "\n";
6547
6548   (* Init function. *)
6549   pr "\
6550 void
6551 initlibguestfsmod (void)
6552 {
6553   static int initialized = 0;
6554
6555   if (initialized) return;
6556   Py_InitModule ((char *) \"libguestfsmod\", methods);
6557   initialized = 1;
6558 }
6559 "
6560
6561 (* Generate Python module. *)
6562 and generate_python_py () =
6563   generate_header HashStyle LGPLv2;
6564
6565   pr "\
6566 u\"\"\"Python bindings for libguestfs
6567
6568 import guestfs
6569 g = guestfs.GuestFS ()
6570 g.add_drive (\"guest.img\")
6571 g.launch ()
6572 g.wait_ready ()
6573 parts = g.list_partitions ()
6574
6575 The guestfs module provides a Python binding to the libguestfs API
6576 for examining and modifying virtual machine disk images.
6577
6578 Amongst the things this is good for: making batch configuration
6579 changes to guests, getting disk used/free statistics (see also:
6580 virt-df), migrating between virtualization systems (see also:
6581 virt-p2v), performing partial backups, performing partial guest
6582 clones, cloning guests and changing registry/UUID/hostname info, and
6583 much else besides.
6584
6585 Libguestfs uses Linux kernel and qemu code, and can access any type of
6586 guest filesystem that Linux and qemu can, including but not limited
6587 to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
6588 schemes, qcow, qcow2, vmdk.
6589
6590 Libguestfs provides ways to enumerate guest storage (eg. partitions,
6591 LVs, what filesystem is in each LV, etc.).  It can also run commands
6592 in the context of the guest.  Also you can access filesystems over FTP.
6593
6594 Errors which happen while using the API are turned into Python
6595 RuntimeError exceptions.
6596
6597 To create a guestfs handle you usually have to perform the following
6598 sequence of calls:
6599
6600 # Create the handle, call add_drive at least once, and possibly
6601 # several times if the guest has multiple block devices:
6602 g = guestfs.GuestFS ()
6603 g.add_drive (\"guest.img\")
6604
6605 # Launch the qemu subprocess and wait for it to become ready:
6606 g.launch ()
6607 g.wait_ready ()
6608
6609 # Now you can issue commands, for example:
6610 logvols = g.lvs ()
6611
6612 \"\"\"
6613
6614 import libguestfsmod
6615
6616 class GuestFS:
6617     \"\"\"Instances of this class are libguestfs API handles.\"\"\"
6618
6619     def __init__ (self):
6620         \"\"\"Create a new libguestfs handle.\"\"\"
6621         self._o = libguestfsmod.create ()
6622
6623     def __del__ (self):
6624         libguestfsmod.close (self._o)
6625
6626 ";
6627
6628   List.iter (
6629     fun (name, style, _, flags, _, _, longdesc) ->
6630       pr "    def %s " name;
6631       generate_call_args ~handle:"self" (snd style);
6632       pr ":\n";
6633
6634       if not (List.mem NotInDocs flags) then (
6635         let doc = replace_str longdesc "C<guestfs_" "C<g." in
6636         let doc =
6637           match fst style with
6638           | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _
6639           | RString _ -> doc
6640           | RStringList _ ->
6641               doc ^ "\n\nThis function returns a list of strings."
6642           | RIntBool _ ->
6643               doc ^ "\n\nThis function returns a tuple (int, bool).\n"
6644           | RPVList _ ->
6645               doc ^ "\n\nThis function returns a list of PVs.  Each PV is represented as a dictionary."
6646           | RVGList _ ->
6647               doc ^ "\n\nThis function returns a list of VGs.  Each VG is represented as a dictionary."
6648           | RLVList _ ->
6649               doc ^ "\n\nThis function returns a list of LVs.  Each LV is represented as a dictionary."
6650           | RStat _ ->
6651               doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the stat structure."
6652           | RStatVFS _ ->
6653               doc ^ "\n\nThis function returns a dictionary, with keys matching the various fields in the statvfs structure."
6654           | RHashtable _ ->
6655               doc ^ "\n\nThis function returns a dictionary." in
6656         let doc =
6657           if List.mem ProtocolLimitWarning flags then
6658             doc ^ "\n\n" ^ protocol_limit_warning
6659           else doc in
6660         let doc =
6661           if List.mem DangerWillRobinson flags then
6662             doc ^ "\n\n" ^ danger_will_robinson
6663           else doc in
6664         let doc = pod2text ~width:60 name doc in
6665         let doc = List.map (fun line -> replace_str line "\\" "\\\\") doc in
6666         let doc = String.concat "\n        " doc in
6667         pr "        u\"\"\"%s\"\"\"\n" doc;
6668       );
6669       pr "        return libguestfsmod.%s " name;
6670       generate_call_args ~handle:"self._o" (snd style);
6671       pr "\n";
6672       pr "\n";
6673   ) all_functions
6674
6675 (* Useful if you need the longdesc POD text as plain text.  Returns a
6676  * list of lines.
6677  *
6678  * This is the slowest thing about autogeneration.
6679  *)
6680 and pod2text ~width name longdesc =
6681   let filename, chan = Filename.open_temp_file "gen" ".tmp" in
6682   fprintf chan "=head1 %s\n\n%s\n" name longdesc;
6683   close_out chan;
6684   let cmd = sprintf "pod2text -w %d %s" width (Filename.quote filename) in
6685   let chan = Unix.open_process_in cmd in
6686   let lines = ref [] in
6687   let rec loop i =
6688     let line = input_line chan in
6689     if i = 1 then               (* discard the first line of output *)
6690       loop (i+1)
6691     else (
6692       let line = triml line in
6693       lines := line :: !lines;
6694       loop (i+1)
6695     ) in
6696   let lines = try loop 1 with End_of_file -> List.rev !lines in
6697   Unix.unlink filename;
6698   match Unix.close_process_in chan with
6699   | Unix.WEXITED 0 -> lines
6700   | Unix.WEXITED i ->
6701       failwithf "pod2text: process exited with non-zero status (%d)" i
6702   | Unix.WSIGNALED i | Unix.WSTOPPED i ->
6703       failwithf "pod2text: process signalled or stopped by signal %d" i
6704
6705 (* Generate ruby bindings. *)
6706 and generate_ruby_c () =
6707   generate_header CStyle LGPLv2;
6708
6709   pr "\
6710 #include <stdio.h>
6711 #include <stdlib.h>
6712
6713 #include <ruby.h>
6714
6715 #include \"guestfs.h\"
6716
6717 #include \"extconf.h\"
6718
6719 /* For Ruby < 1.9 */
6720 #ifndef RARRAY_LEN
6721 #define RARRAY_LEN(r) (RARRAY((r))->len)
6722 #endif
6723
6724 static VALUE m_guestfs;                 /* guestfs module */
6725 static VALUE c_guestfs;                 /* guestfs_h handle */
6726 static VALUE e_Error;                   /* used for all errors */
6727
6728 static void ruby_guestfs_free (void *p)
6729 {
6730   if (!p) return;
6731   guestfs_close ((guestfs_h *) p);
6732 }
6733
6734 static VALUE ruby_guestfs_create (VALUE m)
6735 {
6736   guestfs_h *g;
6737
6738   g = guestfs_create ();
6739   if (!g)
6740     rb_raise (e_Error, \"failed to create guestfs handle\");
6741
6742   /* Don't print error messages to stderr by default. */
6743   guestfs_set_error_handler (g, NULL, NULL);
6744
6745   /* Wrap it, and make sure the close function is called when the
6746    * handle goes away.
6747    */
6748   return Data_Wrap_Struct (c_guestfs, NULL, ruby_guestfs_free, g);
6749 }
6750
6751 static VALUE ruby_guestfs_close (VALUE gv)
6752 {
6753   guestfs_h *g;
6754   Data_Get_Struct (gv, guestfs_h, g);
6755
6756   ruby_guestfs_free (g);
6757   DATA_PTR (gv) = NULL;
6758
6759   return Qnil;
6760 }
6761
6762 ";
6763
6764   List.iter (
6765     fun (name, style, _, _, _, _, _) ->
6766       pr "static VALUE ruby_guestfs_%s (VALUE gv" name;
6767       List.iter (fun arg -> pr ", VALUE %sv" (name_of_argt arg)) (snd style);
6768       pr ")\n";
6769       pr "{\n";
6770       pr "  guestfs_h *g;\n";
6771       pr "  Data_Get_Struct (gv, guestfs_h, g);\n";
6772       pr "  if (!g)\n";
6773       pr "    rb_raise (rb_eArgError, \"%%s: used handle after closing it\", \"%s\");\n"
6774         name;
6775       pr "\n";
6776
6777       List.iter (
6778         function
6779         | String n | FileIn n | FileOut n ->
6780             pr "  Check_Type (%sv, T_STRING);\n" n;
6781             pr "  const char *%s = StringValueCStr (%sv);\n" n n;
6782             pr "  if (!%s)\n" n;
6783             pr "    rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n";
6784             pr "              \"%s\", \"%s\");\n" n name
6785         | OptString n ->
6786             pr "  const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n
6787         | StringList n ->
6788             pr "  char **%s;\n" n;
6789             pr "  Check_Type (%sv, T_ARRAY);\n" n;
6790             pr "  {\n";
6791             pr "    int i, len;\n";
6792             pr "    len = RARRAY_LEN (%sv);\n" n;
6793             pr "    %s = guestfs_safe_malloc (g, sizeof (char *) * (len+1));\n"
6794               n;
6795             pr "    for (i = 0; i < len; ++i) {\n";
6796             pr "      VALUE v = rb_ary_entry (%sv, i);\n" n;
6797             pr "      %s[i] = StringValueCStr (v);\n" n;
6798             pr "    }\n";
6799             pr "    %s[len] = NULL;\n" n;
6800             pr "  }\n";
6801         | Bool n ->
6802             pr "  int %s = RTEST (%sv);\n" n n
6803         | Int n ->
6804             pr "  int %s = NUM2INT (%sv);\n" n n
6805       ) (snd style);
6806       pr "\n";
6807
6808       let error_code =
6809         match fst style with
6810         | RErr | RInt _ | RBool _ -> pr "  int r;\n"; "-1"
6811         | RInt64 _ -> pr "  int64_t r;\n"; "-1"
6812         | RConstString _ -> pr "  const char *r;\n"; "NULL"
6813         | RString _ -> pr "  char *r;\n"; "NULL"
6814         | RStringList _ | RHashtable _ -> pr "  char **r;\n"; "NULL"
6815         | RIntBool _ -> pr "  struct guestfs_int_bool *r;\n"; "NULL"
6816         | RPVList n -> pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL"
6817         | RVGList n -> pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL"
6818         | RLVList n -> pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL"
6819         | RStat n -> pr "  struct guestfs_stat *r;\n"; "NULL"
6820         | RStatVFS n -> pr "  struct guestfs_statvfs *r;\n"; "NULL" in
6821       pr "\n";
6822
6823       pr "  r = guestfs_%s " name;
6824       generate_call_args ~handle:"g" (snd style);
6825       pr ";\n";
6826
6827       List.iter (
6828         function
6829         | String _ | FileIn _ | FileOut _ | OptString _ | Bool _ | Int _ -> ()
6830         | StringList n ->
6831             pr "  free (%s);\n" n
6832       ) (snd style);
6833
6834       pr "  if (r == %s)\n" error_code;
6835       pr "    rb_raise (e_Error, \"%%s\", guestfs_last_error (g));\n";
6836       pr "\n";
6837
6838       (match fst style with
6839        | RErr ->
6840            pr "  return Qnil;\n"
6841        | RInt _ | RBool _ ->
6842            pr "  return INT2NUM (r);\n"
6843        | RInt64 _ ->
6844            pr "  return ULL2NUM (r);\n"
6845        | RConstString _ ->
6846            pr "  return rb_str_new2 (r);\n";
6847        | RString _ ->
6848            pr "  VALUE rv = rb_str_new2 (r);\n";
6849            pr "  free (r);\n";
6850            pr "  return rv;\n";
6851        | RStringList _ ->
6852            pr "  int i, len = 0;\n";
6853            pr "  for (i = 0; r[i] != NULL; ++i) len++;\n";
6854            pr "  VALUE rv = rb_ary_new2 (len);\n";
6855            pr "  for (i = 0; r[i] != NULL; ++i) {\n";
6856            pr "    rb_ary_push (rv, rb_str_new2 (r[i]));\n";
6857            pr "    free (r[i]);\n";
6858            pr "  }\n";
6859            pr "  free (r);\n";
6860            pr "  return rv;\n"
6861        | RIntBool _ ->
6862            pr "  VALUE rv = rb_ary_new2 (2);\n";
6863            pr "  rb_ary_push (rv, INT2NUM (r->i));\n";
6864            pr "  rb_ary_push (rv, INT2NUM (r->b));\n";
6865            pr "  guestfs_free_int_bool (r);\n";
6866            pr "  return rv;\n"
6867        | RPVList n ->
6868            generate_ruby_lvm_code "pv" pv_cols
6869        | RVGList n ->
6870            generate_ruby_lvm_code "vg" vg_cols
6871        | RLVList n ->
6872            generate_ruby_lvm_code "lv" lv_cols
6873        | RStat n ->
6874            pr "  VALUE rv = rb_hash_new ();\n";
6875            List.iter (
6876              function
6877              | name, `Int ->
6878                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
6879            ) stat_cols;
6880            pr "  free (r);\n";
6881            pr "  return rv;\n"
6882        | RStatVFS n ->
6883            pr "  VALUE rv = rb_hash_new ();\n";
6884            List.iter (
6885              function
6886              | name, `Int ->
6887                  pr "  rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->%s));\n" name name
6888            ) statvfs_cols;
6889            pr "  free (r);\n";
6890            pr "  return rv;\n"
6891        | RHashtable _ ->
6892            pr "  VALUE rv = rb_hash_new ();\n";
6893            pr "  int i;\n";
6894            pr "  for (i = 0; r[i] != NULL; i+=2) {\n";
6895            pr "    rb_hash_aset (rv, rb_str_new2 (r[i]), rb_str_new2 (r[i+1]));\n";
6896            pr "    free (r[i]);\n";
6897            pr "    free (r[i+1]);\n";
6898            pr "  }\n";
6899            pr "  free (r);\n";
6900            pr "  return rv;\n"
6901       );
6902
6903       pr "}\n";
6904       pr "\n"
6905   ) all_functions;
6906
6907   pr "\
6908 /* Initialize the module. */
6909 void Init__guestfs ()
6910 {
6911   m_guestfs = rb_define_module (\"Guestfs\");
6912   c_guestfs = rb_define_class_under (m_guestfs, \"Guestfs\", rb_cObject);
6913   e_Error = rb_define_class_under (m_guestfs, \"Error\", rb_eStandardError);
6914
6915   rb_define_module_function (m_guestfs, \"create\", ruby_guestfs_create, 0);
6916   rb_define_method (c_guestfs, \"close\", ruby_guestfs_close, 0);
6917
6918 ";
6919   (* Define the rest of the methods. *)
6920   List.iter (
6921     fun (name, style, _, _, _, _, _) ->
6922       pr "  rb_define_method (c_guestfs, \"%s\",\n" name;
6923       pr "        ruby_guestfs_%s, %d);\n" name (List.length (snd style))
6924   ) all_functions;
6925
6926   pr "}\n"
6927
6928 (* Ruby code to return an LVM struct list. *)
6929 and generate_ruby_lvm_code typ cols =
6930   pr "  VALUE rv = rb_ary_new2 (r->len);\n";
6931   pr "  int i;\n";
6932   pr "  for (i = 0; i < r->len; ++i) {\n";
6933   pr "    VALUE hv = rb_hash_new ();\n";
6934   List.iter (
6935     function
6936     | name, `String ->
6937         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
6938     | name, `UUID ->
6939         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, 32));\n" name name
6940     | name, `Bytes
6941     | name, `Int ->
6942         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), ULL2NUM (r->val[i].%s));\n" name name
6943     | name, `OptPercent ->
6944         pr "    rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_dbl2big (r->val[i].%s));\n" name name
6945   ) cols;
6946   pr "    rb_ary_push (rv, hv);\n";
6947   pr "  }\n";
6948   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
6949   pr "  return rv;\n"
6950
6951 (* Generate Java bindings GuestFS.java file. *)
6952 and generate_java_java () =
6953   generate_header CStyle LGPLv2;
6954
6955   pr "\
6956 package com.redhat.et.libguestfs;
6957
6958 import java.util.HashMap;
6959 import com.redhat.et.libguestfs.LibGuestFSException;
6960 import com.redhat.et.libguestfs.PV;
6961 import com.redhat.et.libguestfs.VG;
6962 import com.redhat.et.libguestfs.LV;
6963 import com.redhat.et.libguestfs.Stat;
6964 import com.redhat.et.libguestfs.StatVFS;
6965 import com.redhat.et.libguestfs.IntBool;
6966
6967 /**
6968  * The GuestFS object is a libguestfs handle.
6969  *
6970  * @author rjones
6971  */
6972 public class GuestFS {
6973   // Load the native code.
6974   static {
6975     System.loadLibrary (\"guestfs_jni\");
6976   }
6977
6978   /**
6979    * The native guestfs_h pointer.
6980    */
6981   long g;
6982
6983   /**
6984    * Create a libguestfs handle.
6985    *
6986    * @throws LibGuestFSException
6987    */
6988   public GuestFS () throws LibGuestFSException
6989   {
6990     g = _create ();
6991   }
6992   private native long _create () throws LibGuestFSException;
6993
6994   /**
6995    * Close a libguestfs handle.
6996    *
6997    * You can also leave handles to be collected by the garbage
6998    * collector, but this method ensures that the resources used
6999    * by the handle are freed up immediately.  If you call any
7000    * other methods after closing the handle, you will get an
7001    * exception.
7002    *
7003    * @throws LibGuestFSException
7004    */
7005   public void close () throws LibGuestFSException
7006   {
7007     if (g != 0)
7008       _close (g);
7009     g = 0;
7010   }
7011   private native void _close (long g) throws LibGuestFSException;
7012
7013   public void finalize () throws LibGuestFSException
7014   {
7015     close ();
7016   }
7017
7018 ";
7019
7020   List.iter (
7021     fun (name, style, _, flags, _, shortdesc, longdesc) ->
7022       if not (List.mem NotInDocs flags); then (
7023         let doc = replace_str longdesc "C<guestfs_" "C<g." in
7024         let doc =
7025           if List.mem ProtocolLimitWarning flags then
7026             doc ^ "\n\n" ^ protocol_limit_warning
7027           else doc in
7028         let doc =
7029           if List.mem DangerWillRobinson flags then
7030             doc ^ "\n\n" ^ danger_will_robinson
7031           else doc in
7032         let doc = pod2text ~width:60 name doc in
7033         let doc = List.map (            (* RHBZ#501883 *)
7034           function
7035           | "" -> "<p>"
7036           | nonempty -> nonempty
7037         ) doc in
7038         let doc = String.concat "\n   * " doc in
7039
7040         pr "  /**\n";
7041         pr "   * %s\n" shortdesc;
7042         pr "   * <p>\n";
7043         pr "   * %s\n" doc;
7044         pr "   * @throws LibGuestFSException\n";
7045         pr "   */\n";
7046         pr "  ";
7047       );
7048       generate_java_prototype ~public:true ~semicolon:false name style;
7049       pr "\n";
7050       pr "  {\n";
7051       pr "    if (g == 0)\n";
7052       pr "      throw new LibGuestFSException (\"%s: handle is closed\");\n"
7053         name;
7054       pr "    ";
7055       if fst style <> RErr then pr "return ";
7056       pr "_%s " name;
7057       generate_call_args ~handle:"g" (snd style);
7058       pr ";\n";
7059       pr "  }\n";
7060       pr "  ";
7061       generate_java_prototype ~privat:true ~native:true name style;
7062       pr "\n";
7063       pr "\n";
7064   ) all_functions;
7065
7066   pr "}\n"
7067
7068 and generate_java_prototype ?(public=false) ?(privat=false) ?(native=false)
7069     ?(semicolon=true) name style =
7070   if privat then pr "private ";
7071   if public then pr "public ";
7072   if native then pr "native ";
7073
7074   (* return type *)
7075   (match fst style with
7076    | RErr -> pr "void ";
7077    | RInt _ -> pr "int ";
7078    | RInt64 _ -> pr "long ";
7079    | RBool _ -> pr "boolean ";
7080    | RConstString _ | RString _ -> pr "String ";
7081    | RStringList _ -> pr "String[] ";
7082    | RIntBool _ -> pr "IntBool ";
7083    | RPVList _ -> pr "PV[] ";
7084    | RVGList _ -> pr "VG[] ";
7085    | RLVList _ -> pr "LV[] ";
7086    | RStat _ -> pr "Stat ";
7087    | RStatVFS _ -> pr "StatVFS ";
7088    | RHashtable _ -> pr "HashMap<String,String> ";
7089   );
7090
7091   if native then pr "_%s " name else pr "%s " name;
7092   pr "(";
7093   let needs_comma = ref false in
7094   if native then (
7095     pr "long g";
7096     needs_comma := true
7097   );
7098
7099   (* args *)
7100   List.iter (
7101     fun arg ->
7102       if !needs_comma then pr ", ";
7103       needs_comma := true;
7104
7105       match arg with
7106       | String n
7107       | OptString n
7108       | FileIn n
7109       | FileOut n ->
7110           pr "String %s" n
7111       | StringList n ->
7112           pr "String[] %s" n
7113       | Bool n ->
7114           pr "boolean %s" n
7115       | Int n ->
7116           pr "int %s" n
7117   ) (snd style);
7118
7119   pr ")\n";
7120   pr "    throws LibGuestFSException";
7121   if semicolon then pr ";"
7122
7123 and generate_java_struct typ cols =
7124   generate_header CStyle LGPLv2;
7125
7126   pr "\
7127 package com.redhat.et.libguestfs;
7128
7129 /**
7130  * Libguestfs %s structure.
7131  *
7132  * @author rjones
7133  * @see GuestFS
7134  */
7135 public class %s {
7136 " typ typ;
7137
7138   List.iter (
7139     function
7140     | name, `String
7141     | name, `UUID -> pr "  public String %s;\n" name
7142     | name, `Bytes
7143     | name, `Int -> pr "  public long %s;\n" name
7144     | name, `OptPercent ->
7145         pr "  /* The next field is [0..100] or -1 meaning 'not present': */\n";
7146         pr "  public float %s;\n" name
7147   ) cols;
7148
7149   pr "}\n"
7150
7151 and generate_java_c () =
7152   generate_header CStyle LGPLv2;
7153
7154   pr "\
7155 #include <stdio.h>
7156 #include <stdlib.h>
7157 #include <string.h>
7158
7159 #include \"com_redhat_et_libguestfs_GuestFS.h\"
7160 #include \"guestfs.h\"
7161
7162 /* Note that this function returns.  The exception is not thrown
7163  * until after the wrapper function returns.
7164  */
7165 static void
7166 throw_exception (JNIEnv *env, const char *msg)
7167 {
7168   jclass cl;
7169   cl = (*env)->FindClass (env,
7170                           \"com/redhat/et/libguestfs/LibGuestFSException\");
7171   (*env)->ThrowNew (env, cl, msg);
7172 }
7173
7174 JNIEXPORT jlong JNICALL
7175 Java_com_redhat_et_libguestfs_GuestFS__1create
7176   (JNIEnv *env, jobject obj)
7177 {
7178   guestfs_h *g;
7179
7180   g = guestfs_create ();
7181   if (g == NULL) {
7182     throw_exception (env, \"GuestFS.create: failed to allocate handle\");
7183     return 0;
7184   }
7185   guestfs_set_error_handler (g, NULL, NULL);
7186   return (jlong) (long) g;
7187 }
7188
7189 JNIEXPORT void JNICALL
7190 Java_com_redhat_et_libguestfs_GuestFS__1close
7191   (JNIEnv *env, jobject obj, jlong jg)
7192 {
7193   guestfs_h *g = (guestfs_h *) (long) jg;
7194   guestfs_close (g);
7195 }
7196
7197 ";
7198
7199   List.iter (
7200     fun (name, style, _, _, _, _, _) ->
7201       pr "JNIEXPORT ";
7202       (match fst style with
7203        | RErr -> pr "void ";
7204        | RInt _ -> pr "jint ";
7205        | RInt64 _ -> pr "jlong ";
7206        | RBool _ -> pr "jboolean ";
7207        | RConstString _ | RString _ -> pr "jstring ";
7208        | RIntBool _ | RStat _ | RStatVFS _ | RHashtable _ ->
7209            pr "jobject ";
7210        | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
7211            pr "jobjectArray ";
7212       );
7213       pr "JNICALL\n";
7214       pr "Java_com_redhat_et_libguestfs_GuestFS_";
7215       pr "%s" (replace_str ("_" ^ name) "_" "_1");
7216       pr "\n";
7217       pr "  (JNIEnv *env, jobject obj, jlong jg";
7218       List.iter (
7219         function
7220         | String n
7221         | OptString n
7222         | FileIn n
7223         | FileOut n ->
7224             pr ", jstring j%s" n
7225         | StringList n ->
7226             pr ", jobjectArray j%s" n
7227         | Bool n ->
7228             pr ", jboolean j%s" n
7229         | Int n ->
7230             pr ", jint j%s" n
7231       ) (snd style);
7232       pr ")\n";
7233       pr "{\n";
7234       pr "  guestfs_h *g = (guestfs_h *) (long) jg;\n";
7235       let error_code, no_ret =
7236         match fst style with
7237         | RErr -> pr "  int r;\n"; "-1", ""
7238         | RBool _
7239         | RInt _ -> pr "  int r;\n"; "-1", "0"
7240         | RInt64 _ -> pr "  int64_t r;\n"; "-1", "0"
7241         | RConstString _ -> pr "  const char *r;\n"; "NULL", "NULL"
7242         | RString _ ->
7243             pr "  jstring jr;\n";
7244             pr "  char *r;\n"; "NULL", "NULL"
7245         | RStringList _ ->
7246             pr "  jobjectArray jr;\n";
7247             pr "  int r_len;\n";
7248             pr "  jclass cl;\n";
7249             pr "  jstring jstr;\n";
7250             pr "  char **r;\n"; "NULL", "NULL"
7251         | RIntBool _ ->
7252             pr "  jobject jr;\n";
7253             pr "  jclass cl;\n";
7254             pr "  jfieldID fl;\n";
7255             pr "  struct guestfs_int_bool *r;\n"; "NULL", "NULL"
7256         | RStat _ ->
7257             pr "  jobject jr;\n";
7258             pr "  jclass cl;\n";
7259             pr "  jfieldID fl;\n";
7260             pr "  struct guestfs_stat *r;\n"; "NULL", "NULL"
7261         | RStatVFS _ ->
7262             pr "  jobject jr;\n";
7263             pr "  jclass cl;\n";
7264             pr "  jfieldID fl;\n";
7265             pr "  struct guestfs_statvfs *r;\n"; "NULL", "NULL"
7266         | RPVList _ ->
7267             pr "  jobjectArray jr;\n";
7268             pr "  jclass cl;\n";
7269             pr "  jfieldID fl;\n";
7270             pr "  jobject jfl;\n";
7271             pr "  struct guestfs_lvm_pv_list *r;\n"; "NULL", "NULL"
7272         | RVGList _ ->
7273             pr "  jobjectArray jr;\n";
7274             pr "  jclass cl;\n";
7275             pr "  jfieldID fl;\n";
7276             pr "  jobject jfl;\n";
7277             pr "  struct guestfs_lvm_vg_list *r;\n"; "NULL", "NULL"
7278         | RLVList _ ->
7279             pr "  jobjectArray jr;\n";
7280             pr "  jclass cl;\n";
7281             pr "  jfieldID fl;\n";
7282             pr "  jobject jfl;\n";
7283             pr "  struct guestfs_lvm_lv_list *r;\n"; "NULL", "NULL"
7284         | RHashtable _ -> pr "  char **r;\n"; "NULL", "NULL" in
7285       List.iter (
7286         function
7287         | String n
7288         | OptString n
7289         | FileIn n
7290         | FileOut n ->
7291             pr "  const char *%s;\n" n
7292         | StringList n ->
7293             pr "  int %s_len;\n" n;
7294             pr "  const char **%s;\n" n
7295         | Bool n
7296         | Int n ->
7297             pr "  int %s;\n" n
7298       ) (snd style);
7299
7300       let needs_i =
7301         (match fst style with
7302          | RStringList _ | RPVList _ | RVGList _ | RLVList _ -> true
7303          | RErr | RBool _ | RInt _ | RInt64 _ | RConstString _
7304          | RString _ | RIntBool _ | RStat _ | RStatVFS _
7305          | RHashtable _ -> false) ||
7306         List.exists (function StringList _ -> true | _ -> false) (snd style) in
7307       if needs_i then
7308         pr "  int i;\n";
7309
7310       pr "\n";
7311
7312       (* Get the parameters. *)
7313       List.iter (
7314         function
7315         | String n
7316         | FileIn n
7317         | FileOut n ->
7318             pr "  %s = (*env)->GetStringUTFChars (env, j%s, NULL);\n" n n
7319         | OptString n ->
7320             (* This is completely undocumented, but Java null becomes
7321              * a NULL parameter.
7322              *)
7323             pr "  %s = j%s ? (*env)->GetStringUTFChars (env, j%s, NULL) : NULL;\n" n n n
7324         | StringList n ->
7325             pr "  %s_len = (*env)->GetArrayLength (env, j%s);\n" n n;
7326             pr "  %s = guestfs_safe_malloc (g, sizeof (char *) * (%s_len+1));\n" n n;
7327             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
7328             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
7329               n;
7330             pr "    %s[i] = (*env)->GetStringUTFChars (env, o, NULL);\n" n;
7331             pr "  }\n";
7332             pr "  %s[%s_len] = NULL;\n" n n;
7333         | Bool n
7334         | Int n ->
7335             pr "  %s = j%s;\n" n n
7336       ) (snd style);
7337
7338       (* Make the call. *)
7339       pr "  r = guestfs_%s " name;
7340       generate_call_args ~handle:"g" (snd style);
7341       pr ";\n";
7342
7343       (* Release the parameters. *)
7344       List.iter (
7345         function
7346         | String n
7347         | FileIn n
7348         | FileOut n ->
7349             pr "  (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
7350         | OptString n ->
7351             pr "  if (j%s)\n" n;
7352             pr "    (*env)->ReleaseStringUTFChars (env, j%s, %s);\n" n n
7353         | StringList n ->
7354             pr "  for (i = 0; i < %s_len; ++i) {\n" n;
7355             pr "    jobject o = (*env)->GetObjectArrayElement (env, j%s, i);\n"
7356               n;
7357             pr "    (*env)->ReleaseStringUTFChars (env, o, %s[i]);\n" n;
7358             pr "  }\n";
7359             pr "  free (%s);\n" n
7360         | Bool n
7361         | Int n -> ()
7362       ) (snd style);
7363
7364       (* Check for errors. *)
7365       pr "  if (r == %s) {\n" error_code;
7366       pr "    throw_exception (env, guestfs_last_error (g));\n";
7367       pr "    return %s;\n" no_ret;
7368       pr "  }\n";
7369
7370       (* Return value. *)
7371       (match fst style with
7372        | RErr -> ()
7373        | RInt _ -> pr "  return (jint) r;\n"
7374        | RBool _ -> pr "  return (jboolean) r;\n"
7375        | RInt64 _ -> pr "  return (jlong) r;\n"
7376        | RConstString _ -> pr "  return (*env)->NewStringUTF (env, r);\n"
7377        | RString _ ->
7378            pr "  jr = (*env)->NewStringUTF (env, r);\n";
7379            pr "  free (r);\n";
7380            pr "  return jr;\n"
7381        | RStringList _ ->
7382            pr "  for (r_len = 0; r[r_len] != NULL; ++r_len) ;\n";
7383            pr "  cl = (*env)->FindClass (env, \"java/lang/String\");\n";
7384            pr "  jstr = (*env)->NewStringUTF (env, \"\");\n";
7385            pr "  jr = (*env)->NewObjectArray (env, r_len, cl, jstr);\n";
7386            pr "  for (i = 0; i < r_len; ++i) {\n";
7387            pr "    jstr = (*env)->NewStringUTF (env, r[i]);\n";
7388            pr "    (*env)->SetObjectArrayElement (env, jr, i, jstr);\n";
7389            pr "    free (r[i]);\n";
7390            pr "  }\n";
7391            pr "  free (r);\n";
7392            pr "  return jr;\n"
7393        | RIntBool _ ->
7394            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/IntBool\");\n";
7395            pr "  jr = (*env)->AllocObject (env, cl);\n";
7396            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"I\");\n";
7397            pr "  (*env)->SetIntField (env, jr, fl, r->i);\n";
7398            pr "  fl = (*env)->GetFieldID (env, cl, \"i\", \"Z\");\n";
7399            pr "  (*env)->SetBooleanField (env, jr, fl, r->b);\n";
7400            pr "  guestfs_free_int_bool (r);\n";
7401            pr "  return jr;\n"
7402        | RStat _ ->
7403            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/Stat\");\n";
7404            pr "  jr = (*env)->AllocObject (env, cl);\n";
7405            List.iter (
7406              function
7407              | name, `Int ->
7408                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
7409                    name;
7410                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
7411            ) stat_cols;
7412            pr "  free (r);\n";
7413            pr "  return jr;\n"
7414        | RStatVFS _ ->
7415            pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/StatVFS\");\n";
7416            pr "  jr = (*env)->AllocObject (env, cl);\n";
7417            List.iter (
7418              function
7419              | name, `Int ->
7420                  pr "  fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n"
7421                    name;
7422                  pr "  (*env)->SetLongField (env, jr, fl, r->%s);\n" name;
7423            ) statvfs_cols;
7424            pr "  free (r);\n";
7425            pr "  return jr;\n"
7426        | RPVList _ ->
7427            generate_java_lvm_return "pv" "PV" pv_cols
7428        | RVGList _ ->
7429            generate_java_lvm_return "vg" "VG" vg_cols
7430        | RLVList _ ->
7431            generate_java_lvm_return "lv" "LV" lv_cols
7432        | RHashtable _ ->
7433            (* XXX *)
7434            pr "  throw_exception (env, \"%s: internal error: please let us know how to make a Java HashMap from JNI bindings!\");\n" name;
7435            pr "  return NULL;\n"
7436       );
7437
7438       pr "}\n";
7439       pr "\n"
7440   ) all_functions
7441
7442 and generate_java_lvm_return typ jtyp cols =
7443   pr "  cl = (*env)->FindClass (env, \"com/redhat/et/libguestfs/%s\");\n" jtyp;
7444   pr "  jr = (*env)->NewObjectArray (env, r->len, cl, NULL);\n";
7445   pr "  for (i = 0; i < r->len; ++i) {\n";
7446   pr "    jfl = (*env)->AllocObject (env, cl);\n";
7447   List.iter (
7448     function
7449     | name, `String ->
7450         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
7451         pr "    (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, r->val[i].%s));\n" name;
7452     | name, `UUID ->
7453         pr "    {\n";
7454         pr "      char s[33];\n";
7455         pr "      memcpy (s, r->val[i].%s, 32);\n" name;
7456         pr "      s[32] = 0;\n";
7457         pr "      fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
7458         pr "      (*env)->SetObjectField (env, jfl, fl, (*env)->NewStringUTF (env, s));\n";
7459         pr "    }\n";
7460     | name, (`Bytes|`Int) ->
7461         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"J\");\n" name;
7462         pr "    (*env)->SetLongField (env, jfl, fl, r->val[i].%s);\n" name;
7463     | name, `OptPercent ->
7464         pr "    fl = (*env)->GetFieldID (env, cl, \"%s\", \"F\");\n" name;
7465         pr "    (*env)->SetFloatField (env, jfl, fl, r->val[i].%s);\n" name;
7466   ) cols;
7467   pr "    (*env)->SetObjectArrayElement (env, jfl, i, jfl);\n";
7468   pr "  }\n";
7469   pr "  guestfs_free_lvm_%s_list (r);\n" typ;
7470   pr "  return jr;\n"
7471
7472 and generate_haskell_hs () =
7473   generate_header HaskellStyle LGPLv2;
7474
7475   (* XXX We only know how to generate partial FFI for Haskell
7476    * at the moment.  Please help out!
7477    *)
7478   let can_generate style =
7479     match style with
7480     | RErr, _
7481     | RInt _, _
7482     | RInt64 _, _ -> true
7483     | RBool _, _
7484     | RConstString _, _
7485     | RString _, _
7486     | RStringList _, _
7487     | RIntBool _, _
7488     | RPVList _, _
7489     | RVGList _, _
7490     | RLVList _, _
7491     | RStat _, _
7492     | RStatVFS _, _
7493     | RHashtable _, _ -> false in
7494
7495   pr "\
7496 {-# INCLUDE <guestfs.h> #-}
7497 {-# LANGUAGE ForeignFunctionInterface #-}
7498
7499 module Guestfs (
7500   create";
7501
7502   (* List out the names of the actions we want to export. *)
7503   List.iter (
7504     fun (name, style, _, _, _, _, _) ->
7505       if can_generate style then pr ",\n  %s" name
7506   ) all_functions;
7507
7508   pr "
7509   ) where
7510 import Foreign
7511 import Foreign.C
7512 import Foreign.C.Types
7513 import IO
7514 import Control.Exception
7515 import Data.Typeable
7516
7517 data GuestfsS = GuestfsS            -- represents the opaque C struct
7518 type GuestfsP = Ptr GuestfsS        -- guestfs_h *
7519 type GuestfsH = ForeignPtr GuestfsS -- guestfs_h * with attached finalizer
7520
7521 -- XXX define properly later XXX
7522 data PV = PV
7523 data VG = VG
7524 data LV = LV
7525 data IntBool = IntBool
7526 data Stat = Stat
7527 data StatVFS = StatVFS
7528 data Hashtable = Hashtable
7529
7530 foreign import ccall unsafe \"guestfs_create\" c_create
7531   :: IO GuestfsP
7532 foreign import ccall unsafe \"&guestfs_close\" c_close
7533   :: FunPtr (GuestfsP -> IO ())
7534 foreign import ccall unsafe \"guestfs_set_error_handler\" c_set_error_handler
7535   :: GuestfsP -> Ptr CInt -> Ptr CInt -> IO ()
7536
7537 create :: IO GuestfsH
7538 create = do
7539   p <- c_create
7540   c_set_error_handler p nullPtr nullPtr
7541   h <- newForeignPtr c_close p
7542   return h
7543
7544 foreign import ccall unsafe \"guestfs_last_error\" c_last_error
7545   :: GuestfsP -> IO CString
7546
7547 -- last_error :: GuestfsH -> IO (Maybe String)
7548 -- last_error h = do
7549 --   str <- withForeignPtr h (\\p -> c_last_error p)
7550 --   maybePeek peekCString str
7551
7552 last_error :: GuestfsH -> IO (String)
7553 last_error h = do
7554   str <- withForeignPtr h (\\p -> c_last_error p)
7555   if (str == nullPtr)
7556     then return \"no error\"
7557     else peekCString str
7558
7559 ";
7560
7561   (* Generate wrappers for each foreign function. *)
7562   List.iter (
7563     fun (name, style, _, _, _, _, _) ->
7564       if can_generate style then (
7565         pr "foreign import ccall unsafe \"guestfs_%s\" c_%s\n" name name;
7566         pr "  :: ";
7567         generate_haskell_prototype ~handle:"GuestfsP" style;
7568         pr "\n";
7569         pr "\n";
7570         pr "%s :: " name;
7571         generate_haskell_prototype ~handle:"GuestfsH" ~hs:true style;
7572         pr "\n";
7573         pr "%s %s = do\n" name
7574           (String.concat " " ("h" :: List.map name_of_argt (snd style)));
7575         pr "  r <- ";
7576         (* Convert pointer arguments using with* functions. *)
7577         List.iter (
7578           function
7579           | FileIn n
7580           | FileOut n
7581           | String n -> pr "withCString %s $ \\%s -> " n n
7582           | OptString n -> pr "maybeWith withCString %s $ \\%s -> " n n
7583           | StringList n -> pr "withMany withCString %s $ \\%s -> withArray0 nullPtr %s $ \\%s -> " n n n n
7584           | Bool _ | Int _ -> ()
7585         ) (snd style);
7586         (* Convert integer arguments. *)
7587         let args =
7588           List.map (
7589             function
7590             | Bool n -> sprintf "(fromBool %s)" n
7591             | Int n -> sprintf "(fromIntegral %s)" n
7592             | FileIn n | FileOut n | String n | OptString n | StringList n -> n
7593           ) (snd style) in
7594         pr "withForeignPtr h (\\p -> c_%s %s)\n" name
7595           (String.concat " " ("p" :: args));
7596         (match fst style with
7597          | RErr | RInt _ | RInt64 _ | RBool _ ->
7598              pr "  if (r == -1)\n";
7599              pr "    then do\n";
7600              pr "      err <- last_error h\n";
7601              pr "      fail err\n";
7602          | RConstString _ | RString _ | RStringList _ | RIntBool _
7603          | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _
7604          | RHashtable _ ->
7605              pr "  if (r == nullPtr)\n";
7606              pr "    then do\n";
7607              pr "      err <- last_error h\n";
7608              pr "      fail err\n";
7609         );
7610         (match fst style with
7611          | RErr ->
7612              pr "    else return ()\n"
7613          | RInt _ ->
7614              pr "    else return (fromIntegral r)\n"
7615          | RInt64 _ ->
7616              pr "    else return (fromIntegral r)\n"
7617          | RBool _ ->
7618              pr "    else return (toBool r)\n"
7619          | RConstString _
7620          | RString _
7621          | RStringList _
7622          | RIntBool _
7623          | RPVList _
7624          | RVGList _
7625          | RLVList _
7626          | RStat _
7627          | RStatVFS _
7628          | RHashtable _ ->
7629              pr "    else return ()\n" (* XXXXXXXXXXXXXXXXXXXX *)
7630         );
7631         pr "\n";
7632       )
7633   ) all_functions
7634
7635 and generate_haskell_prototype ~handle ?(hs = false) style =
7636   pr "%s -> " handle;
7637   let string = if hs then "String" else "CString" in
7638   let int = if hs then "Int" else "CInt" in
7639   let bool = if hs then "Bool" else "CInt" in
7640   let int64 = if hs then "Integer" else "Int64" in
7641   List.iter (
7642     fun arg ->
7643       (match arg with
7644        | String _ -> pr "%s" string
7645        | OptString _ -> if hs then pr "Maybe String" else pr "CString"
7646        | StringList _ -> if hs then pr "[String]" else pr "Ptr CString"
7647        | Bool _ -> pr "%s" bool
7648        | Int _ -> pr "%s" int
7649        | FileIn _ -> pr "%s" string
7650        | FileOut _ -> pr "%s" string
7651       );
7652       pr " -> ";
7653   ) (snd style);
7654   pr "IO (";
7655   (match fst style with
7656    | RErr -> if not hs then pr "CInt"
7657    | RInt _ -> pr "%s" int
7658    | RInt64 _ -> pr "%s" int64
7659    | RBool _ -> pr "%s" bool
7660    | RConstString _ -> pr "%s" string
7661    | RString _ -> pr "%s" string
7662    | RStringList _ -> pr "[%s]" string
7663    | RIntBool _ -> pr "IntBool"
7664    | RPVList _ -> pr "[PV]"
7665    | RVGList _ -> pr "[VG]"
7666    | RLVList _ -> pr "[LV]"
7667    | RStat _ -> pr "Stat"
7668    | RStatVFS _ -> pr "StatVFS"
7669    | RHashtable _ -> pr "Hashtable"
7670   );
7671   pr ")"
7672
7673 and generate_bindtests () =
7674   generate_header CStyle LGPLv2;
7675
7676   pr "\
7677 #include <stdio.h>
7678 #include <stdlib.h>
7679 #include <inttypes.h>
7680 #include <string.h>
7681
7682 #include \"guestfs.h\"
7683 #include \"guestfs_protocol.h\"
7684
7685 #define error guestfs_error
7686
7687 static void
7688 print_strings (char * const* const argv)
7689 {
7690   int argc;
7691
7692   printf (\"[\");
7693   for (argc = 0; argv[argc] != NULL; ++argc) {
7694     if (argc > 0) printf (\", \");
7695     printf (\"\\\"%%s\\\"\", argv[argc]);
7696   }
7697   printf (\"]\\n\");
7698 }
7699
7700 /* The test0 function prints its parameters to stdout. */
7701 ";
7702
7703   let test0, tests =
7704     match test_functions with
7705     | [] -> assert false
7706     | test0 :: tests -> test0, tests in
7707
7708   let () =
7709     let (name, style, _, _, _, _, _) = test0 in
7710     generate_prototype ~extern:false ~semicolon:false ~newline:true
7711       ~handle:"g" ~prefix:"guestfs_" name style;
7712     pr "{\n";
7713     List.iter (
7714       function
7715       | String n
7716       | FileIn n
7717       | FileOut n -> pr "  printf (\"%%s\\n\", %s);\n" n
7718       | OptString n -> pr "  printf (\"%%s\\n\", %s ? %s : \"null\");\n" n n
7719       | StringList n -> pr "  print_strings (%s);\n" n
7720       | Bool n -> pr "  printf (\"%%s\\n\", %s ? \"true\" : \"false\");\n" n
7721       | Int n -> pr "  printf (\"%%d\\n\", %s);\n" n
7722     ) (snd style);
7723     pr "  /* Java changes stdout line buffering so we need this: */\n";
7724     pr "  fflush (stdout);\n";
7725     pr "  return 0;\n";
7726     pr "}\n";
7727     pr "\n" in
7728
7729   List.iter (
7730     fun (name, style, _, _, _, _, _) ->
7731       if String.sub name (String.length name - 3) 3 <> "err" then (
7732         pr "/* Test normal return. */\n";
7733         generate_prototype ~extern:false ~semicolon:false ~newline:true
7734           ~handle:"g" ~prefix:"guestfs_" name style;
7735         pr "{\n";
7736         (match fst style with
7737          | RErr ->
7738              pr "  return 0;\n"
7739          | RInt _ ->
7740              pr "  int r;\n";
7741              pr "  sscanf (val, \"%%d\", &r);\n";
7742              pr "  return r;\n"
7743          | RInt64 _ ->
7744              pr "  int64_t r;\n";
7745              pr "  sscanf (val, \"%%\" SCNi64, &r);\n";
7746              pr "  return r;\n"
7747          | RBool _ ->
7748              pr "  return strcmp (val, \"true\") == 0;\n"
7749          | RConstString _ ->
7750              (* Can't return the input string here.  Return a static
7751               * string so we ensure we get a segfault if the caller
7752               * tries to free it.
7753               *)
7754              pr "  return \"static string\";\n"
7755          | RString _ ->
7756              pr "  return strdup (val);\n"
7757          | RStringList _ ->
7758              pr "  char **strs;\n";
7759              pr "  int n, i;\n";
7760              pr "  sscanf (val, \"%%d\", &n);\n";
7761              pr "  strs = malloc ((n+1) * sizeof (char *));\n";
7762              pr "  for (i = 0; i < n; ++i) {\n";
7763              pr "    strs[i] = malloc (16);\n";
7764              pr "    snprintf (strs[i], 16, \"%%d\", i);\n";
7765              pr "  }\n";
7766              pr "  strs[n] = NULL;\n";
7767              pr "  return strs;\n"
7768          | RIntBool _ ->
7769              pr "  struct guestfs_int_bool *r;\n";
7770              pr "  r = malloc (sizeof (struct guestfs_int_bool));\n";
7771              pr "  sscanf (val, \"%%\" SCNi32, &r->i);\n";
7772              pr "  r->b = 0;\n";
7773              pr "  return r;\n"
7774          | RPVList _ ->
7775              pr "  struct guestfs_lvm_pv_list *r;\n";
7776              pr "  int i;\n";
7777              pr "  r = malloc (sizeof (struct guestfs_lvm_pv_list));\n";
7778              pr "  sscanf (val, \"%%d\", &r->len);\n";
7779              pr "  r->val = calloc (r->len, sizeof (struct guestfs_lvm_pv));\n";
7780              pr "  for (i = 0; i < r->len; ++i) {\n";
7781              pr "    r->val[i].pv_name = malloc (16);\n";
7782              pr "    snprintf (r->val[i].pv_name, 16, \"%%d\", i);\n";
7783              pr "  }\n";
7784              pr "  return r;\n"
7785          | RVGList _ ->
7786              pr "  struct guestfs_lvm_vg_list *r;\n";
7787              pr "  int i;\n";
7788              pr "  r = malloc (sizeof (struct guestfs_lvm_vg_list));\n";
7789              pr "  sscanf (val, \"%%d\", &r->len);\n";
7790              pr "  r->val = calloc (r->len, sizeof (struct guestfs_lvm_vg));\n";
7791              pr "  for (i = 0; i < r->len; ++i) {\n";
7792              pr "    r->val[i].vg_name = malloc (16);\n";
7793              pr "    snprintf (r->val[i].vg_name, 16, \"%%d\", i);\n";
7794              pr "  }\n";
7795              pr "  return r;\n"
7796          | RLVList _ ->
7797              pr "  struct guestfs_lvm_lv_list *r;\n";
7798              pr "  int i;\n";
7799              pr "  r = malloc (sizeof (struct guestfs_lvm_lv_list));\n";
7800              pr "  sscanf (val, \"%%d\", &r->len);\n";
7801              pr "  r->val = calloc (r->len, sizeof (struct guestfs_lvm_lv));\n";
7802              pr "  for (i = 0; i < r->len; ++i) {\n";
7803              pr "    r->val[i].lv_name = malloc (16);\n";
7804              pr "    snprintf (r->val[i].lv_name, 16, \"%%d\", i);\n";
7805              pr "  }\n";
7806              pr "  return r;\n"
7807          | RStat _ ->
7808              pr "  struct guestfs_stat *r;\n";
7809              pr "  r = calloc (1, sizeof (*r));\n";
7810              pr "  sscanf (val, \"%%\" SCNi64, &r->dev);\n";
7811              pr "  return r;\n"
7812          | RStatVFS _ ->
7813              pr "  struct guestfs_statvfs *r;\n";
7814              pr "  r = calloc (1, sizeof (*r));\n";
7815              pr "  sscanf (val, \"%%\" SCNi64, &r->bsize);\n";
7816              pr "  return r;\n"
7817          | RHashtable _ ->
7818              pr "  char **strs;\n";
7819              pr "  int n, i;\n";
7820              pr "  sscanf (val, \"%%d\", &n);\n";
7821              pr "  strs = malloc ((n*2+1) * sizeof (char *));\n";
7822              pr "  for (i = 0; i < n; ++i) {\n";
7823              pr "    strs[i*2] = malloc (16);\n";
7824              pr "    strs[i*2+1] = malloc (16);\n";
7825              pr "    snprintf (strs[i*2], 16, \"%%d\", i);\n";
7826              pr "    snprintf (strs[i*2+1], 16, \"%%d\", i);\n";
7827              pr "  }\n";
7828              pr "  strs[n*2] = NULL;\n";
7829              pr "  return strs;\n"
7830         );
7831         pr "}\n";
7832         pr "\n"
7833       ) else (
7834         pr "/* Test error return. */\n";
7835         generate_prototype ~extern:false ~semicolon:false ~newline:true
7836           ~handle:"g" ~prefix:"guestfs_" name style;
7837         pr "{\n";
7838         pr "  error (g, \"error\");\n";
7839         (match fst style with
7840          | RErr | RInt _ | RInt64 _ | RBool _ ->
7841              pr "  return -1;\n"
7842          | RConstString _
7843          | RString _ | RStringList _ | RIntBool _
7844          | RPVList _ | RVGList _ | RLVList _ | RStat _ | RStatVFS _
7845          | RHashtable _ ->
7846              pr "  return NULL;\n"
7847         );
7848         pr "}\n";
7849         pr "\n"
7850       )
7851   ) tests
7852
7853 and generate_ocaml_bindtests () =
7854   generate_header OCamlStyle GPLv2;
7855
7856   pr "\
7857 let () =
7858   let g = Guestfs.create () in
7859 ";
7860
7861   let mkargs args =
7862     String.concat " " (
7863       List.map (
7864         function
7865         | CallString s -> "\"" ^ s ^ "\""
7866         | CallOptString None -> "None"
7867         | CallOptString (Some s) -> sprintf "(Some \"%s\")" s
7868         | CallStringList xs ->
7869             "[|" ^ String.concat ";" (List.map (sprintf "\"%s\"") xs) ^ "|]"
7870         | CallInt i when i >= 0 -> string_of_int i
7871         | CallInt i (* when i < 0 *) -> "(" ^ string_of_int i ^ ")"
7872         | CallBool b -> string_of_bool b
7873       ) args
7874     )
7875   in
7876
7877   generate_lang_bindtests (
7878     fun f args -> pr "  Guestfs.%s g %s;\n" f (mkargs args)
7879   );
7880
7881   pr "print_endline \"EOF\"\n"
7882
7883 and generate_perl_bindtests () =
7884   pr "#!/usr/bin/perl -w\n";
7885   generate_header HashStyle GPLv2;
7886
7887   pr "\
7888 use strict;
7889
7890 use Sys::Guestfs;
7891
7892 my $g = Sys::Guestfs->new ();
7893 ";
7894
7895   let mkargs args =
7896     String.concat ", " (
7897       List.map (
7898         function
7899         | CallString s -> "\"" ^ s ^ "\""
7900         | CallOptString None -> "undef"
7901         | CallOptString (Some s) -> sprintf "\"%s\"" s
7902         | CallStringList xs ->
7903             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
7904         | CallInt i -> string_of_int i
7905         | CallBool b -> if b then "1" else "0"
7906       ) args
7907     )
7908   in
7909
7910   generate_lang_bindtests (
7911     fun f args -> pr "$g->%s (%s);\n" f (mkargs args)
7912   );
7913
7914   pr "print \"EOF\\n\"\n"
7915
7916 and generate_python_bindtests () =
7917   generate_header HashStyle GPLv2;
7918
7919   pr "\
7920 import guestfs
7921
7922 g = guestfs.GuestFS ()
7923 ";
7924
7925   let mkargs args =
7926     String.concat ", " (
7927       List.map (
7928         function
7929         | CallString s -> "\"" ^ s ^ "\""
7930         | CallOptString None -> "None"
7931         | CallOptString (Some s) -> sprintf "\"%s\"" s
7932         | CallStringList xs ->
7933             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
7934         | CallInt i -> string_of_int i
7935         | CallBool b -> if b then "1" else "0"
7936       ) args
7937     )
7938   in
7939
7940   generate_lang_bindtests (
7941     fun f args -> pr "g.%s (%s)\n" f (mkargs args)
7942   );
7943
7944   pr "print \"EOF\"\n"
7945
7946 and generate_ruby_bindtests () =
7947   generate_header HashStyle GPLv2;
7948
7949   pr "\
7950 require 'guestfs'
7951
7952 g = Guestfs::create()
7953 ";
7954
7955   let mkargs args =
7956     String.concat ", " (
7957       List.map (
7958         function
7959         | CallString s -> "\"" ^ s ^ "\""
7960         | CallOptString None -> "nil"
7961         | CallOptString (Some s) -> sprintf "\"%s\"" s
7962         | CallStringList xs ->
7963             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
7964         | CallInt i -> string_of_int i
7965         | CallBool b -> string_of_bool b
7966       ) args
7967     )
7968   in
7969
7970   generate_lang_bindtests (
7971     fun f args -> pr "g.%s(%s)\n" f (mkargs args)
7972   );
7973
7974   pr "print \"EOF\\n\"\n"
7975
7976 and generate_java_bindtests () =
7977   generate_header CStyle GPLv2;
7978
7979   pr "\
7980 import com.redhat.et.libguestfs.*;
7981
7982 public class Bindtests {
7983     public static void main (String[] argv)
7984     {
7985         try {
7986             GuestFS g = new GuestFS ();
7987 ";
7988
7989   let mkargs args =
7990     String.concat ", " (
7991       List.map (
7992         function
7993         | CallString s -> "\"" ^ s ^ "\""
7994         | CallOptString None -> "null"
7995         | CallOptString (Some s) -> sprintf "\"%s\"" s
7996         | CallStringList xs ->
7997             "new String[]{" ^
7998               String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "}"
7999         | CallInt i -> string_of_int i
8000         | CallBool b -> string_of_bool b
8001       ) args
8002     )
8003   in
8004
8005   generate_lang_bindtests (
8006     fun f args -> pr "            g.%s (%s);\n" f (mkargs args)
8007   );
8008
8009   pr "
8010             System.out.println (\"EOF\");
8011         }
8012         catch (Exception exn) {
8013             System.err.println (exn);
8014             System.exit (1);
8015         }
8016     }
8017 }
8018 "
8019
8020 and generate_haskell_bindtests () =
8021   generate_header HaskellStyle GPLv2;
8022
8023   pr "\
8024 module Bindtests where
8025 import qualified Guestfs
8026
8027 main = do
8028   g <- Guestfs.create
8029 ";
8030
8031   let mkargs args =
8032     String.concat " " (
8033       List.map (
8034         function
8035         | CallString s -> "\"" ^ s ^ "\""
8036         | CallOptString None -> "Nothing"
8037         | CallOptString (Some s) -> sprintf "(Just \"%s\")" s
8038         | CallStringList xs ->
8039             "[" ^ String.concat "," (List.map (sprintf "\"%s\"") xs) ^ "]"
8040         | CallInt i when i < 0 -> "(" ^ string_of_int i ^ ")"
8041         | CallInt i -> string_of_int i
8042         | CallBool true -> "True"
8043         | CallBool false -> "False"
8044       ) args
8045     )
8046   in
8047
8048   generate_lang_bindtests (
8049     fun f args -> pr "  Guestfs.%s g %s\n" f (mkargs args)
8050   );
8051
8052   pr "  putStrLn \"EOF\"\n"
8053
8054 (* Language-independent bindings tests - we do it this way to
8055  * ensure there is parity in testing bindings across all languages.
8056  *)
8057 and generate_lang_bindtests call =
8058   call "test0" [CallString "abc"; CallOptString (Some "def");
8059                 CallStringList []; CallBool false;
8060                 CallInt 0; CallString "123"; CallString "456"];
8061   call "test0" [CallString "abc"; CallOptString None;
8062                 CallStringList []; CallBool false;
8063                 CallInt 0; CallString "123"; CallString "456"];
8064   call "test0" [CallString ""; CallOptString (Some "def");
8065                 CallStringList []; CallBool false;
8066                 CallInt 0; CallString "123"; CallString "456"];
8067   call "test0" [CallString ""; CallOptString (Some "");
8068                 CallStringList []; CallBool false;
8069                 CallInt 0; CallString "123"; CallString "456"];
8070   call "test0" [CallString "abc"; CallOptString (Some "def");
8071                 CallStringList ["1"]; CallBool false;
8072                 CallInt 0; CallString "123"; CallString "456"];
8073   call "test0" [CallString "abc"; CallOptString (Some "def");
8074                 CallStringList ["1"; "2"]; CallBool false;
8075                 CallInt 0; CallString "123"; CallString "456"];
8076   call "test0" [CallString "abc"; CallOptString (Some "def");
8077                 CallStringList ["1"]; CallBool true;
8078                 CallInt 0; CallString "123"; CallString "456"];
8079   call "test0" [CallString "abc"; CallOptString (Some "def");
8080                 CallStringList ["1"]; CallBool false;
8081                 CallInt (-1); CallString "123"; CallString "456"];
8082   call "test0" [CallString "abc"; CallOptString (Some "def");
8083                 CallStringList ["1"]; CallBool false;
8084                 CallInt (-2); CallString "123"; CallString "456"];
8085   call "test0" [CallString "abc"; CallOptString (Some "def");
8086                 CallStringList ["1"]; CallBool false;
8087                 CallInt 1; CallString "123"; CallString "456"];
8088   call "test0" [CallString "abc"; CallOptString (Some "def");
8089                 CallStringList ["1"]; CallBool false;
8090                 CallInt 2; CallString "123"; CallString "456"];
8091   call "test0" [CallString "abc"; CallOptString (Some "def");
8092                 CallStringList ["1"]; CallBool false;
8093                 CallInt 4095; CallString "123"; CallString "456"];
8094   call "test0" [CallString "abc"; CallOptString (Some "def");
8095                 CallStringList ["1"]; CallBool false;
8096                 CallInt 0; CallString ""; CallString ""]
8097
8098   (* XXX Add here tests of the return and error functions. *)
8099
8100 (* This is used to generate the src/MAX_PROC_NR file which
8101  * contains the maximum procedure number, a surrogate for the
8102  * ABI version number.  See src/Makefile.am for the details.
8103  *)
8104 and generate_max_proc_nr () =
8105   let proc_nrs = List.map (
8106     fun (_, _, proc_nr, _, _, _, _) -> proc_nr
8107   ) daemon_functions in
8108
8109   let max_proc_nr = List.fold_left max 0 proc_nrs in
8110
8111   pr "%d\n" max_proc_nr
8112
8113 let output_to filename =
8114   let filename_new = filename ^ ".new" in
8115   chan := open_out filename_new;
8116   let close () =
8117     close_out !chan;
8118     chan := stdout;
8119
8120     (* Is the new file different from the current file? *)
8121     if Sys.file_exists filename && files_equal filename filename_new then
8122       Unix.unlink filename_new          (* same, so skip it *)
8123     else (
8124       (* different, overwrite old one *)
8125       (try Unix.chmod filename 0o644 with Unix.Unix_error _ -> ());
8126       Unix.rename filename_new filename;
8127       Unix.chmod filename 0o444;
8128       printf "written %s\n%!" filename;
8129     )
8130   in
8131   close
8132
8133 (* Main program. *)
8134 let () =
8135   check_functions ();
8136
8137   if not (Sys.file_exists "configure.ac") then (
8138     eprintf "\
8139 You are probably running this from the wrong directory.
8140 Run it from the top source directory using the command
8141   src/generator.ml
8142 ";
8143     exit 1
8144   );
8145
8146   let close = output_to "src/guestfs_protocol.x" in
8147   generate_xdr ();
8148   close ();
8149
8150   let close = output_to "src/guestfs-structs.h" in
8151   generate_structs_h ();
8152   close ();
8153
8154   let close = output_to "src/guestfs-actions.h" in
8155   generate_actions_h ();
8156   close ();
8157
8158   let close = output_to "src/guestfs-actions.c" in
8159   generate_client_actions ();
8160   close ();
8161
8162   let close = output_to "daemon/actions.h" in
8163   generate_daemon_actions_h ();
8164   close ();
8165
8166   let close = output_to "daemon/stubs.c" in
8167   generate_daemon_actions ();
8168   close ();
8169
8170   let close = output_to "capitests/tests.c" in
8171   generate_tests ();
8172   close ();
8173
8174   let close = output_to "src/guestfs-bindtests.c" in
8175   generate_bindtests ();
8176   close ();
8177
8178   let close = output_to "fish/cmds.c" in
8179   generate_fish_cmds ();
8180   close ();
8181
8182   let close = output_to "fish/completion.c" in
8183   generate_fish_completion ();
8184   close ();
8185
8186   let close = output_to "guestfs-structs.pod" in
8187   generate_structs_pod ();
8188   close ();
8189
8190   let close = output_to "guestfs-actions.pod" in
8191   generate_actions_pod ();
8192   close ();
8193
8194   let close = output_to "guestfish-actions.pod" in
8195   generate_fish_actions_pod ();
8196   close ();
8197
8198   let close = output_to "ocaml/guestfs.mli" in
8199   generate_ocaml_mli ();
8200   close ();
8201
8202   let close = output_to "ocaml/guestfs.ml" in
8203   generate_ocaml_ml ();
8204   close ();
8205
8206   let close = output_to "ocaml/guestfs_c_actions.c" in
8207   generate_ocaml_c ();
8208   close ();
8209
8210   let close = output_to "ocaml/bindtests.ml" in
8211   generate_ocaml_bindtests ();
8212   close ();
8213
8214   let close = output_to "perl/Guestfs.xs" in
8215   generate_perl_xs ();
8216   close ();
8217
8218   let close = output_to "perl/lib/Sys/Guestfs.pm" in
8219   generate_perl_pm ();
8220   close ();
8221
8222   let close = output_to "perl/bindtests.pl" in
8223   generate_perl_bindtests ();
8224   close ();
8225
8226   let close = output_to "python/guestfs-py.c" in
8227   generate_python_c ();
8228   close ();
8229
8230   let close = output_to "python/guestfs.py" in
8231   generate_python_py ();
8232   close ();
8233
8234   let close = output_to "python/bindtests.py" in
8235   generate_python_bindtests ();
8236   close ();
8237
8238   let close = output_to "ruby/ext/guestfs/_guestfs.c" in
8239   generate_ruby_c ();
8240   close ();
8241
8242   let close = output_to "ruby/bindtests.rb" in
8243   generate_ruby_bindtests ();
8244   close ();
8245
8246   let close = output_to "java/com/redhat/et/libguestfs/GuestFS.java" in
8247   generate_java_java ();
8248   close ();
8249
8250   let close = output_to "java/com/redhat/et/libguestfs/PV.java" in
8251   generate_java_struct "PV" pv_cols;
8252   close ();
8253
8254   let close = output_to "java/com/redhat/et/libguestfs/VG.java" in
8255   generate_java_struct "VG" vg_cols;
8256   close ();
8257
8258   let close = output_to "java/com/redhat/et/libguestfs/LV.java" in
8259   generate_java_struct "LV" lv_cols;
8260   close ();
8261
8262   let close = output_to "java/com/redhat/et/libguestfs/Stat.java" in
8263   generate_java_struct "Stat" stat_cols;
8264   close ();
8265
8266   let close = output_to "java/com/redhat/et/libguestfs/StatVFS.java" in
8267   generate_java_struct "StatVFS" statvfs_cols;
8268   close ();
8269
8270   let close = output_to "java/com_redhat_et_libguestfs_GuestFS.c" in
8271   generate_java_c ();
8272   close ();
8273
8274   let close = output_to "java/Bindtests.java" in
8275   generate_java_bindtests ();
8276   close ();
8277
8278   let close = output_to "haskell/Guestfs.hs" in
8279   generate_haskell_hs ();
8280   close ();
8281
8282   let close = output_to "haskell/Bindtests.hs" in
8283   generate_haskell_bindtests ();
8284   close ();
8285
8286   let close = output_to "src/MAX_PROC_NR" in
8287   generate_max_proc_nr ();
8288   close ();