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