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