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