3 * Copyright (C) 2009 Red Hat Inc.
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.
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.
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
20 (* This script generates a large amount of code and documentation for
21 * all the daemon actions.
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.
27 * After editing this file, run it (./src/generator.ml) to regenerate
28 * all the output files.
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]
39 type style = ret * args
41 (* "RErr" as a return value means an int used as a simple error
42 * indication, ie. 0 or -1.
45 (* "RInt" as a return value means an int which is -1 for error
46 * or any value >= 0 on success. Only use this for smallish
47 * positive ints (0 <= i < 2^30).
50 (* "RInt64" is the same as RInt, but is guaranteed to be able
51 * to return a full 64 bit value, _except_ that -1 means error
52 * (so -1 cannot be a valid, non-error return value).
55 (* "RBool" is a bool return value which can be true/false or
59 (* "RConstString" is a string that refers to a constant value.
60 * Try to avoid using this. In particular you cannot use this
61 * for values returned from the daemon, because there is no
62 * thread-safe way to return them in the C API.
64 | RConstString of string
65 (* "RString" and "RStringList" are caller-frees. *)
67 | RStringList of string
68 (* Some limited tuples are possible: *)
69 | RIntBool of string * string
70 (* LVM PVs, VGs and LVs. *)
77 (* Key-value pairs of untyped strings. Turns into a hashtable or
78 * dictionary in languages which support it. DON'T use this as a
79 * general "bucket" for results. Prefer a stronger typed return
80 * value if one is available, or write a custom struct. Don't use
81 * this if the list could potentially be very long, since it is
82 * inefficient. Keys should be unique. NULLs are not permitted.
84 | RHashtable of string
86 and args = argt list (* Function parameters, guestfs handle is implicit. *)
88 (* Note in future we should allow a "variable args" parameter as
89 * the final parameter, to allow commands like
90 * chmod mode file [file(s)...]
91 * This is not implemented yet, but many commands (such as chmod)
92 * are currently defined with the argument order keeping this future
93 * possibility in mind.
96 | String of string (* const char *name, cannot be NULL *)
97 | OptString of string (* const char *name, may be NULL *)
98 | StringList of string(* list of strings (each string cannot be NULL) *)
99 | Bool of string (* boolean *)
100 | Int of string (* int (smallish ints, signed, <= 31 bits) *)
101 (* These are treated as filenames (simple string parameters) in
102 * the C API and bindings. But in the RPC protocol, we transfer
103 * the actual file content up to or down from the daemon.
104 * FileIn: local machine -> daemon (in request)
105 * FileOut: daemon -> local machine (in reply)
106 * In guestfish (only), the special name "-" means read from
107 * stdin or write to stdout.
113 | ProtocolLimitWarning (* display warning about protocol size limits *)
114 | DangerWillRobinson (* flags particularly dangerous commands *)
115 | FishAlias of string (* provide an alias for this cmd in guestfish *)
116 | FishAction of string (* call this function in guestfish *)
117 | NotInFish (* do not export via guestfish *)
119 let protocol_limit_warning =
120 "Because of the message protocol, there is a transfer limit
121 of somewhere between 2MB and 4MB. To transfer large files you should use
124 let danger_will_robinson =
125 "B<This command is dangerous. Without careful use you
126 can easily destroy all your data>."
128 (* You can supply zero or as many tests as you want per API call.
130 * Note that the test environment has 3 block devices, of size 500MB,
131 * 50MB and 10MB (respectively /dev/sda, /dev/sdb, /dev/sdc).
132 * Note for partitioning purposes, the 500MB device has 63 cylinders.
134 * To be able to run the tests in a reasonable amount of time,
135 * the virtual machine and block devices are reused between tests.
136 * So don't try testing kill_subprocess :-x
138 * Between each test we umount-all and lvm-remove-all (except InitNone).
140 * Don't assume anything about the previous contents of the block
141 * devices. Use 'Init*' to create some initial scenarios.
143 type tests = (test_init * test) list
145 (* Run the command sequence and just expect nothing to fail. *)
147 (* Run the command sequence and expect the output of the final
148 * command to be the string.
150 | TestOutput of seq * string
151 (* Run the command sequence and expect the output of the final
152 * command to be the list of strings.
154 | TestOutputList of seq * string list
155 (* Run the command sequence and expect the output of the final
156 * command to be the integer.
158 | TestOutputInt of seq * int
159 (* Run the command sequence and expect the output of the final
160 * command to be a true value (!= 0 or != NULL).
162 | TestOutputTrue of seq
163 (* Run the command sequence and expect the output of the final
164 * command to be a false value (== 0 or == NULL, but not an error).
166 | TestOutputFalse of seq
167 (* Run the command sequence and expect the output of the final
168 * command to be a list of the given length (but don't care about
171 | TestOutputLength of seq * int
172 (* Run the command sequence and expect the output of the final
173 * command to be a structure.
175 | TestOutputStruct of seq * test_field_compare list
176 (* Run the command sequence and expect the final command (only)
179 | TestLastFail of seq
181 and test_field_compare =
182 | CompareWithInt of string * int
183 | CompareWithString of string * string
184 | CompareFieldsIntEq of string * string
185 | CompareFieldsStrEq of string * string
187 (* Some initial scenarios for testing. *)
189 (* Do nothing, block devices could contain random stuff including
190 * LVM PVs, and some filesystems might be mounted. This is usually
194 (* Block devices are empty and no filesystems are mounted. *)
196 (* /dev/sda contains a single partition /dev/sda1, which is formatted
197 * as ext2, empty [except for lost+found] and mounted on /.
198 * /dev/sdb and /dev/sdc may have random content.
203 * /dev/sda1 (is a PV):
204 * /dev/VG/LV (size 8MB):
205 * formatted as ext2, empty [except for lost+found], mounted on /
206 * /dev/sdb and /dev/sdc may have random content.
210 (* Sequence of commands for testing. *)
212 and cmd = string list
214 (* Note about long descriptions: When referring to another
215 * action, use the format C<guestfs_other> (ie. the full name of
216 * the C function). This will be replaced as appropriate in other
219 * Apart from that, long descriptions are just perldoc paragraphs.
222 let non_daemon_functions = [
223 ("launch", (RErr, []), -1, [FishAlias "run"; FishAction "launch"],
225 "launch the qemu subprocess",
227 Internally libguestfs is implemented by running a virtual machine
230 You should call this after configuring the handle
231 (eg. adding drives) but before performing any actions.");
233 ("wait_ready", (RErr, []), -1, [NotInFish],
235 "wait until the qemu subprocess launches",
237 Internally libguestfs is implemented by running a virtual machine
240 You should call this after C<guestfs_launch> to wait for the launch
243 ("kill_subprocess", (RErr, []), -1, [],
245 "kill the qemu subprocess",
247 This kills the qemu subprocess. You should never need to call this.");
249 ("add_drive", (RErr, [String "filename"]), -1, [FishAlias "add"],
251 "add an image to examine or modify",
253 This function adds a virtual machine disk image C<filename> to the
254 guest. The first time you call this function, the disk appears as IDE
255 disk 0 (C</dev/sda>) in the guest, the second time as C</dev/sdb>, and
258 You don't necessarily need to be root when using libguestfs. However
259 you obviously do need sufficient permissions to access the filename
260 for whatever operations you want to perform (ie. read access if you
261 just want to read the image or write access if you want to modify the
264 This is equivalent to the qemu parameter C<-drive file=filename>.");
266 ("add_cdrom", (RErr, [String "filename"]), -1, [FishAlias "cdrom"],
268 "add a CD-ROM disk image to examine",
270 This function adds a virtual CD-ROM disk image to the guest.
272 This is equivalent to the qemu parameter C<-cdrom filename>.");
274 ("config", (RErr, [String "qemuparam"; OptString "qemuvalue"]), -1, [],
276 "add qemu parameters",
278 This can be used to add arbitrary qemu command line parameters
279 of the form C<-param value>. Actually it's not quite arbitrary - we
280 prevent you from setting some parameters which would interfere with
281 parameters that we use.
283 The first character of C<param> string must be a C<-> (dash).
285 C<value> can be NULL.");
287 ("set_path", (RErr, [String "path"]), -1, [FishAlias "path"],
289 "set the search path",
291 Set the path that libguestfs searches for kernel and initrd.img.
293 The default is C<$libdir/guestfs> unless overridden by setting
294 C<LIBGUESTFS_PATH> environment variable.
296 The string C<path> is stashed in the libguestfs handle, so the caller
297 must make sure it remains valid for the lifetime of the handle.
299 Setting C<path> to C<NULL> restores the default path.");
301 ("get_path", (RConstString "path", []), -1, [],
303 "get the search path",
305 Return the current search path.
307 This is always non-NULL. If it wasn't set already, then this will
308 return the default path.");
310 ("set_autosync", (RErr, [Bool "autosync"]), -1, [FishAlias "autosync"],
314 If C<autosync> is true, this enables autosync. Libguestfs will make a
315 best effort attempt to run C<guestfs_sync> when the handle is closed
316 (also if the program exits without closing handles).");
318 ("get_autosync", (RBool "autosync", []), -1, [],
322 Get the autosync flag.");
324 ("set_verbose", (RErr, [Bool "verbose"]), -1, [FishAlias "verbose"],
328 If C<verbose> is true, this turns on verbose messages (to C<stderr>).
330 Verbose messages are disabled unless the environment variable
331 C<LIBGUESTFS_DEBUG> is defined and set to C<1>.");
333 ("get_verbose", (RBool "verbose", []), -1, [],
337 This returns the verbose messages flag.")
340 let daemon_functions = [
341 ("mount", (RErr, [String "device"; String "mountpoint"]), 1, [],
342 [InitEmpty, TestOutput (
343 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
344 ["mkfs"; "ext2"; "/dev/sda1"];
345 ["mount"; "/dev/sda1"; "/"];
346 ["write_file"; "/new"; "new file contents"; "0"];
347 ["cat"; "/new"]], "new file contents")],
348 "mount a guest disk at a position in the filesystem",
350 Mount a guest disk at a position in the filesystem. Block devices
351 are named C</dev/sda>, C</dev/sdb> and so on, as they were added to
352 the guest. If those block devices contain partitions, they will have
353 the usual names (eg. C</dev/sda1>). Also LVM C</dev/VG/LV>-style
356 The rules are the same as for L<mount(2)>: A filesystem must
357 first be mounted on C</> before others can be mounted. Other
358 filesystems can only be mounted on directories which already
361 The mounted filesystem is writable, if we have sufficient permissions
362 on the underlying device.
364 The filesystem options C<sync> and C<noatime> are set with this
365 call, in order to improve reliability.");
367 ("sync", (RErr, []), 2, [],
368 [ InitEmpty, TestRun [["sync"]]],
369 "sync disks, writes are flushed through to the disk image",
371 This syncs the disk, so that any writes are flushed through to the
372 underlying disk image.
374 You should always call this if you have modified a disk image, before
375 closing the handle.");
377 ("touch", (RErr, [String "path"]), 3, [],
378 [InitBasicFS, TestOutputTrue (
380 ["exists"; "/new"]])],
381 "update file timestamps or create a new file",
383 Touch acts like the L<touch(1)> command. It can be used to
384 update the timestamps on a file, or, if the file does not exist,
385 to create a new zero-length file.");
387 ("cat", (RString "content", [String "path"]), 4, [ProtocolLimitWarning],
388 [InitBasicFS, TestOutput (
389 [["write_file"; "/new"; "new file contents"; "0"];
390 ["cat"; "/new"]], "new file contents")],
391 "list the contents of a file",
393 Return the contents of the file named C<path>.
395 Note that this function cannot correctly handle binary files
396 (specifically, files containing C<\\0> character which is treated
397 as end of string). For those you need to use the C<guestfs_download>
398 function which has a more complex interface.");
400 ("ll", (RString "listing", [String "directory"]), 5, [],
401 [], (* XXX Tricky to test because it depends on the exact format
402 * of the 'ls -l' command, which changes between F10 and F11.
404 "list the files in a directory (long format)",
406 List the files in C<directory> (relative to the root directory,
407 there is no cwd) in the format of 'ls -la'.
409 This command is mostly useful for interactive sessions. It
410 is I<not> intended that you try to parse the output string.");
412 ("ls", (RStringList "listing", [String "directory"]), 6, [],
413 [InitBasicFS, TestOutputList (
416 ["touch"; "/newest"];
417 ["ls"; "/"]], ["lost+found"; "new"; "newer"; "newest"])],
418 "list the files in a directory",
420 List the files in C<directory> (relative to the root directory,
421 there is no cwd). The '.' and '..' entries are not returned, but
422 hidden files are shown.
424 This command is mostly useful for interactive sessions. Programs
425 should probably use C<guestfs_readdir> instead.");
427 ("list_devices", (RStringList "devices", []), 7, [],
428 [InitEmpty, TestOutputList (
429 [["list_devices"]], ["/dev/sda"; "/dev/sdb"; "/dev/sdc"])],
430 "list the block devices",
432 List all the block devices.
434 The full block device names are returned, eg. C</dev/sda>");
436 ("list_partitions", (RStringList "partitions", []), 8, [],
437 [InitBasicFS, TestOutputList (
438 [["list_partitions"]], ["/dev/sda1"]);
439 InitEmpty, TestOutputList (
440 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
441 ["list_partitions"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
442 "list the partitions",
444 List all the partitions detected on all block devices.
446 The full partition device names are returned, eg. C</dev/sda1>
448 This does not return logical volumes. For that you will need to
449 call C<guestfs_lvs>.");
451 ("pvs", (RStringList "physvols", []), 9, [],
452 [InitBasicFSonLVM, TestOutputList (
453 [["pvs"]], ["/dev/sda1"]);
454 InitEmpty, TestOutputList (
455 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
456 ["pvcreate"; "/dev/sda1"];
457 ["pvcreate"; "/dev/sda2"];
458 ["pvcreate"; "/dev/sda3"];
459 ["pvs"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
460 "list the LVM physical volumes (PVs)",
462 List all the physical volumes detected. This is the equivalent
463 of the L<pvs(8)> command.
465 This returns a list of just the device names that contain
466 PVs (eg. C</dev/sda2>).
468 See also C<guestfs_pvs_full>.");
470 ("vgs", (RStringList "volgroups", []), 10, [],
471 [InitBasicFSonLVM, TestOutputList (
473 InitEmpty, TestOutputList (
474 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
475 ["pvcreate"; "/dev/sda1"];
476 ["pvcreate"; "/dev/sda2"];
477 ["pvcreate"; "/dev/sda3"];
478 ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
479 ["vgcreate"; "VG2"; "/dev/sda3"];
480 ["vgs"]], ["VG1"; "VG2"])],
481 "list the LVM volume groups (VGs)",
483 List all the volumes groups detected. This is the equivalent
484 of the L<vgs(8)> command.
486 This returns a list of just the volume group names that were
487 detected (eg. C<VolGroup00>).
489 See also C<guestfs_vgs_full>.");
491 ("lvs", (RStringList "logvols", []), 11, [],
492 [InitBasicFSonLVM, TestOutputList (
493 [["lvs"]], ["/dev/VG/LV"]);
494 InitEmpty, TestOutputList (
495 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
496 ["pvcreate"; "/dev/sda1"];
497 ["pvcreate"; "/dev/sda2"];
498 ["pvcreate"; "/dev/sda3"];
499 ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
500 ["vgcreate"; "VG2"; "/dev/sda3"];
501 ["lvcreate"; "LV1"; "VG1"; "50"];
502 ["lvcreate"; "LV2"; "VG1"; "50"];
503 ["lvcreate"; "LV3"; "VG2"; "50"];
504 ["lvs"]], ["/dev/VG1/LV1"; "/dev/VG1/LV2"; "/dev/VG2/LV3"])],
505 "list the LVM logical volumes (LVs)",
507 List all the logical volumes detected. This is the equivalent
508 of the L<lvs(8)> command.
510 This returns a list of the logical volume device names
511 (eg. C</dev/VolGroup00/LogVol00>).
513 See also C<guestfs_lvs_full>.");
515 ("pvs_full", (RPVList "physvols", []), 12, [],
516 [], (* XXX how to test? *)
517 "list the LVM physical volumes (PVs)",
519 List all the physical volumes detected. This is the equivalent
520 of the L<pvs(8)> command. The \"full\" version includes all fields.");
522 ("vgs_full", (RVGList "volgroups", []), 13, [],
523 [], (* XXX how to test? *)
524 "list the LVM volume groups (VGs)",
526 List all the volumes groups detected. This is the equivalent
527 of the L<vgs(8)> command. The \"full\" version includes all fields.");
529 ("lvs_full", (RLVList "logvols", []), 14, [],
530 [], (* XXX how to test? *)
531 "list the LVM logical volumes (LVs)",
533 List all the logical volumes detected. This is the equivalent
534 of the L<lvs(8)> command. The \"full\" version includes all fields.");
536 ("read_lines", (RStringList "lines", [String "path"]), 15, [],
537 [InitBasicFS, TestOutputList (
538 [["write_file"; "/new"; "line1\r\nline2\nline3"; "0"];
539 ["read_lines"; "/new"]], ["line1"; "line2"; "line3"]);
540 InitBasicFS, TestOutputList (
541 [["write_file"; "/new"; ""; "0"];
542 ["read_lines"; "/new"]], [])],
543 "read file as lines",
545 Return the contents of the file named C<path>.
547 The file contents are returned as a list of lines. Trailing
548 C<LF> and C<CRLF> character sequences are I<not> returned.
550 Note that this function cannot correctly handle binary files
551 (specifically, files containing C<\\0> character which is treated
552 as end of line). For those you need to use the C<guestfs_read_file>
553 function which has a more complex interface.");
555 ("aug_init", (RErr, [String "root"; Int "flags"]), 16, [],
556 [], (* XXX Augeas code needs tests. *)
557 "create a new Augeas handle",
559 Create a new Augeas handle for editing configuration files.
560 If there was any previous Augeas handle associated with this
561 guestfs session, then it is closed.
563 You must call this before using any other C<guestfs_aug_*>
566 C<root> is the filesystem root. C<root> must not be NULL,
569 The flags are the same as the flags defined in
570 E<lt>augeas.hE<gt>, the logical I<or> of the following
575 =item C<AUG_SAVE_BACKUP> = 1
577 Keep the original file with a C<.augsave> extension.
579 =item C<AUG_SAVE_NEWFILE> = 2
581 Save changes into a file with extension C<.augnew>, and
582 do not overwrite original. Overrides C<AUG_SAVE_BACKUP>.
584 =item C<AUG_TYPE_CHECK> = 4
586 Typecheck lenses (can be expensive).
588 =item C<AUG_NO_STDINC> = 8
590 Do not use standard load path for modules.
592 =item C<AUG_SAVE_NOOP> = 16
594 Make save a no-op, just record what would have been changed.
596 =item C<AUG_NO_LOAD> = 32
598 Do not load the tree in C<guestfs_aug_init>.
602 To close the handle, you can call C<guestfs_aug_close>.
604 To find out more about Augeas, see L<http://augeas.net/>.");
606 ("aug_close", (RErr, []), 26, [],
607 [], (* XXX Augeas code needs tests. *)
608 "close the current Augeas handle",
610 Close the current Augeas handle and free up any resources
611 used by it. After calling this, you have to call
612 C<guestfs_aug_init> again before you can use any other
615 ("aug_defvar", (RInt "nrnodes", [String "name"; OptString "expr"]), 17, [],
616 [], (* XXX Augeas code needs tests. *)
617 "define an Augeas variable",
619 Defines an Augeas variable C<name> whose value is the result
620 of evaluating C<expr>. If C<expr> is NULL, then C<name> is
623 On success this returns the number of nodes in C<expr>, or
624 C<0> if C<expr> evaluates to something which is not a nodeset.");
626 ("aug_defnode", (RIntBool ("nrnodes", "created"), [String "name"; String "expr"; String "val"]), 18, [],
627 [], (* XXX Augeas code needs tests. *)
628 "define an Augeas node",
630 Defines a variable C<name> whose value is the result of
633 If C<expr> evaluates to an empty nodeset, a node is created,
634 equivalent to calling C<guestfs_aug_set> C<expr>, C<value>.
635 C<name> will be the nodeset containing that single node.
637 On success this returns a pair containing the
638 number of nodes in the nodeset, and a boolean flag
639 if a node was created.");
641 ("aug_get", (RString "val", [String "path"]), 19, [],
642 [], (* XXX Augeas code needs tests. *)
643 "look up the value of an Augeas path",
645 Look up the value associated with C<path>. If C<path>
646 matches exactly one node, the C<value> is returned.");
648 ("aug_set", (RErr, [String "path"; String "val"]), 20, [],
649 [], (* XXX Augeas code needs tests. *)
650 "set Augeas path to value",
652 Set the value associated with C<path> to C<value>.");
654 ("aug_insert", (RErr, [String "path"; String "label"; Bool "before"]), 21, [],
655 [], (* XXX Augeas code needs tests. *)
656 "insert a sibling Augeas node",
658 Create a new sibling C<label> for C<path>, inserting it into
659 the tree before or after C<path> (depending on the boolean
662 C<path> must match exactly one existing node in the tree, and
663 C<label> must be a label, ie. not contain C</>, C<*> or end
664 with a bracketed index C<[N]>.");
666 ("aug_rm", (RInt "nrnodes", [String "path"]), 22, [],
667 [], (* XXX Augeas code needs tests. *)
668 "remove an Augeas path",
670 Remove C<path> and all of its children.
672 On success this returns the number of entries which were removed.");
674 ("aug_mv", (RErr, [String "src"; String "dest"]), 23, [],
675 [], (* XXX Augeas code needs tests. *)
678 Move the node C<src> to C<dest>. C<src> must match exactly
679 one node. C<dest> is overwritten if it exists.");
681 ("aug_match", (RStringList "matches", [String "path"]), 24, [],
682 [], (* XXX Augeas code needs tests. *)
683 "return Augeas nodes which match path",
685 Returns a list of paths which match the path expression C<path>.
686 The returned paths are sufficiently qualified so that they match
687 exactly one node in the current tree.");
689 ("aug_save", (RErr, []), 25, [],
690 [], (* XXX Augeas code needs tests. *)
691 "write all pending Augeas changes to disk",
693 This writes all pending changes to disk.
695 The flags which were passed to C<guestfs_aug_init> affect exactly
696 how files are saved.");
698 ("aug_load", (RErr, []), 27, [],
699 [], (* XXX Augeas code needs tests. *)
700 "load files into the tree",
702 Load files into the tree.
704 See C<aug_load> in the Augeas documentation for the full gory
707 ("aug_ls", (RStringList "matches", [String "path"]), 28, [],
708 [], (* XXX Augeas code needs tests. *)
709 "list Augeas nodes under a path",
711 This is just a shortcut for listing C<guestfs_aug_match>
712 C<path/*> and sorting the resulting nodes into alphabetical order.");
714 ("rm", (RErr, [String "path"]), 29, [],
715 [InitBasicFS, TestRun
718 InitBasicFS, TestLastFail
720 InitBasicFS, TestLastFail
725 Remove the single file C<path>.");
727 ("rmdir", (RErr, [String "path"]), 30, [],
728 [InitBasicFS, TestRun
731 InitBasicFS, TestLastFail
733 InitBasicFS, TestLastFail
736 "remove a directory",
738 Remove the single directory C<path>.");
740 ("rm_rf", (RErr, [String "path"]), 31, [],
741 [InitBasicFS, TestOutputFalse
743 ["mkdir"; "/new/foo"];
744 ["touch"; "/new/foo/bar"];
746 ["exists"; "/new"]]],
747 "remove a file or directory recursively",
749 Remove the file or directory C<path>, recursively removing the
750 contents if its a directory. This is like the C<rm -rf> shell
753 ("mkdir", (RErr, [String "path"]), 32, [],
754 [InitBasicFS, TestOutputTrue
757 InitBasicFS, TestLastFail
758 [["mkdir"; "/new/foo/bar"]]],
759 "create a directory",
761 Create a directory named C<path>.");
763 ("mkdir_p", (RErr, [String "path"]), 33, [],
764 [InitBasicFS, TestOutputTrue
765 [["mkdir_p"; "/new/foo/bar"];
766 ["is_dir"; "/new/foo/bar"]];
767 InitBasicFS, TestOutputTrue
768 [["mkdir_p"; "/new/foo/bar"];
769 ["is_dir"; "/new/foo"]];
770 InitBasicFS, TestOutputTrue
771 [["mkdir_p"; "/new/foo/bar"];
772 ["is_dir"; "/new"]]],
773 "create a directory and parents",
775 Create a directory named C<path>, creating any parent directories
776 as necessary. This is like the C<mkdir -p> shell command.");
778 ("chmod", (RErr, [Int "mode"; String "path"]), 34, [],
779 [], (* XXX Need stat command to test *)
782 Change the mode (permissions) of C<path> to C<mode>. Only
783 numeric modes are supported.");
785 ("chown", (RErr, [Int "owner"; Int "group"; String "path"]), 35, [],
786 [], (* XXX Need stat command to test *)
787 "change file owner and group",
789 Change the file owner to C<owner> and group to C<group>.
791 Only numeric uid and gid are supported. If you want to use
792 names, you will need to locate and parse the password file
793 yourself (Augeas support makes this relatively easy).");
795 ("exists", (RBool "existsflag", [String "path"]), 36, [],
796 [InitBasicFS, TestOutputTrue (
798 ["exists"; "/new"]]);
799 InitBasicFS, TestOutputTrue (
801 ["exists"; "/new"]])],
802 "test if file or directory exists",
804 This returns C<true> if and only if there is a file, directory
805 (or anything) with the given C<path> name.
807 See also C<guestfs_is_file>, C<guestfs_is_dir>, C<guestfs_stat>.");
809 ("is_file", (RBool "fileflag", [String "path"]), 37, [],
810 [InitBasicFS, TestOutputTrue (
812 ["is_file"; "/new"]]);
813 InitBasicFS, TestOutputFalse (
815 ["is_file"; "/new"]])],
816 "test if file exists",
818 This returns C<true> if and only if there is a file
819 with the given C<path> name. Note that it returns false for
820 other objects like directories.
822 See also C<guestfs_stat>.");
824 ("is_dir", (RBool "dirflag", [String "path"]), 38, [],
825 [InitBasicFS, TestOutputFalse (
827 ["is_dir"; "/new"]]);
828 InitBasicFS, TestOutputTrue (
830 ["is_dir"; "/new"]])],
831 "test if file exists",
833 This returns C<true> if and only if there is a directory
834 with the given C<path> name. Note that it returns false for
835 other objects like files.
837 See also C<guestfs_stat>.");
839 ("pvcreate", (RErr, [String "device"]), 39, [],
840 [InitEmpty, TestOutputList (
841 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
842 ["pvcreate"; "/dev/sda1"];
843 ["pvcreate"; "/dev/sda2"];
844 ["pvcreate"; "/dev/sda3"];
845 ["pvs"]], ["/dev/sda1"; "/dev/sda2"; "/dev/sda3"])],
846 "create an LVM physical volume",
848 This creates an LVM physical volume on the named C<device>,
849 where C<device> should usually be a partition name such
852 ("vgcreate", (RErr, [String "volgroup"; StringList "physvols"]), 40, [],
853 [InitEmpty, TestOutputList (
854 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
855 ["pvcreate"; "/dev/sda1"];
856 ["pvcreate"; "/dev/sda2"];
857 ["pvcreate"; "/dev/sda3"];
858 ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
859 ["vgcreate"; "VG2"; "/dev/sda3"];
860 ["vgs"]], ["VG1"; "VG2"])],
861 "create an LVM volume group",
863 This creates an LVM volume group called C<volgroup>
864 from the non-empty list of physical volumes C<physvols>.");
866 ("lvcreate", (RErr, [String "logvol"; String "volgroup"; Int "mbytes"]), 41, [],
867 [InitEmpty, TestOutputList (
868 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ",10 ,20 ,"];
869 ["pvcreate"; "/dev/sda1"];
870 ["pvcreate"; "/dev/sda2"];
871 ["pvcreate"; "/dev/sda3"];
872 ["vgcreate"; "VG1"; "/dev/sda1 /dev/sda2"];
873 ["vgcreate"; "VG2"; "/dev/sda3"];
874 ["lvcreate"; "LV1"; "VG1"; "50"];
875 ["lvcreate"; "LV2"; "VG1"; "50"];
876 ["lvcreate"; "LV3"; "VG2"; "50"];
877 ["lvcreate"; "LV4"; "VG2"; "50"];
878 ["lvcreate"; "LV5"; "VG2"; "50"];
880 ["/dev/VG1/LV1"; "/dev/VG1/LV2";
881 "/dev/VG2/LV3"; "/dev/VG2/LV4"; "/dev/VG2/LV5"])],
882 "create an LVM volume group",
884 This creates an LVM volume group called C<logvol>
885 on the volume group C<volgroup>, with C<size> megabytes.");
887 ("mkfs", (RErr, [String "fstype"; String "device"]), 42, [],
888 [InitEmpty, TestOutput (
889 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
890 ["mkfs"; "ext2"; "/dev/sda1"];
891 ["mount"; "/dev/sda1"; "/"];
892 ["write_file"; "/new"; "new file contents"; "0"];
893 ["cat"; "/new"]], "new file contents")],
896 This creates a filesystem on C<device> (usually a partition
897 of LVM logical volume). The filesystem type is C<fstype>, for
900 ("sfdisk", (RErr, [String "device";
901 Int "cyls"; Int "heads"; Int "sectors";
902 StringList "lines"]), 43, [DangerWillRobinson],
904 "create partitions on a block device",
906 This is a direct interface to the L<sfdisk(8)> program for creating
907 partitions on block devices.
909 C<device> should be a block device, for example C</dev/sda>.
911 C<cyls>, C<heads> and C<sectors> are the number of cylinders, heads
912 and sectors on the device, which are passed directly to sfdisk as
913 the I<-C>, I<-H> and I<-S> parameters. If you pass C<0> for any
914 of these, then the corresponding parameter is omitted. Usually for
915 'large' disks, you can just pass C<0> for these, but for small
916 (floppy-sized) disks, sfdisk (or rather, the kernel) cannot work
917 out the right geometry and you will need to tell it.
919 C<lines> is a list of lines that we feed to C<sfdisk>. For more
920 information refer to the L<sfdisk(8)> manpage.
922 To create a single partition occupying the whole disk, you would
923 pass C<lines> as a single element list, when the single element being
924 the string C<,> (comma).");
926 ("write_file", (RErr, [String "path"; String "content"; Int "size"]), 44, [ProtocolLimitWarning],
927 [InitBasicFS, TestOutput (
928 [["write_file"; "/new"; "new file contents"; "0"];
929 ["cat"; "/new"]], "new file contents");
930 InitBasicFS, TestOutput (
931 [["write_file"; "/new"; "\nnew file contents\n"; "0"];
932 ["cat"; "/new"]], "\nnew file contents\n");
933 InitBasicFS, TestOutput (
934 [["write_file"; "/new"; "\n\n"; "0"];
935 ["cat"; "/new"]], "\n\n");
936 InitBasicFS, TestOutput (
937 [["write_file"; "/new"; ""; "0"];
938 ["cat"; "/new"]], "");
939 InitBasicFS, TestOutput (
940 [["write_file"; "/new"; "\n\n\n"; "0"];
941 ["cat"; "/new"]], "\n\n\n");
942 InitBasicFS, TestOutput (
943 [["write_file"; "/new"; "\n"; "0"];
944 ["cat"; "/new"]], "\n")],
947 This call creates a file called C<path>. The contents of the
948 file is the string C<content> (which can contain any 8 bit data),
951 As a special case, if C<size> is C<0>
952 then the length is calculated using C<strlen> (so in this case
953 the content cannot contain embedded ASCII NULs).");
955 ("umount", (RErr, [String "pathordevice"]), 45, [FishAlias "unmount"],
956 [InitEmpty, TestOutputList (
957 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
958 ["mkfs"; "ext2"; "/dev/sda1"];
959 ["mount"; "/dev/sda1"; "/"];
960 ["mounts"]], ["/dev/sda1"]);
961 InitEmpty, TestOutputList (
962 [["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
963 ["mkfs"; "ext2"; "/dev/sda1"];
964 ["mount"; "/dev/sda1"; "/"];
967 "unmount a filesystem",
969 This unmounts the given filesystem. The filesystem may be
970 specified either by its mountpoint (path) or the device which
971 contains the filesystem.");
973 ("mounts", (RStringList "devices", []), 46, [],
974 [InitBasicFS, TestOutputList (
975 [["mounts"]], ["/dev/sda1"])],
976 "show mounted filesystems",
978 This returns the list of currently mounted filesystems. It returns
979 the list of devices (eg. C</dev/sda1>, C</dev/VG/LV>).
981 Some internal mounts are not shown.");
983 ("umount_all", (RErr, []), 47, [FishAlias "unmount-all"],
984 [InitBasicFS, TestOutputList (
987 "unmount all filesystems",
989 This unmounts all mounted filesystems.
991 Some internal mounts are not unmounted by this call.");
993 ("lvm_remove_all", (RErr, []), 48, [DangerWillRobinson],
995 "remove all LVM LVs, VGs and PVs",
997 This command removes all LVM logical volumes, volume groups
998 and physical volumes.");
1000 ("file", (RString "description", [String "path"]), 49, [],
1001 [InitBasicFS, TestOutput (
1003 ["file"; "/new"]], "empty");
1004 InitBasicFS, TestOutput (
1005 [["write_file"; "/new"; "some content\n"; "0"];
1006 ["file"; "/new"]], "ASCII text");
1007 InitBasicFS, TestLastFail (
1008 [["file"; "/nofile"]])],
1009 "determine file type",
1011 This call uses the standard L<file(1)> command to determine
1012 the type or contents of the file. This also works on devices,
1013 for example to find out whether a partition contains a filesystem.
1015 The exact command which runs is C<file -bsL path>. Note in
1016 particular that the filename is not prepended to the output
1017 (the C<-b> option).");
1019 ("command", (RString "output", [StringList "arguments"]), 50, [],
1020 [], (* XXX how to test? *)
1021 "run a command from the guest filesystem",
1023 This call runs a command from the guest filesystem. The
1024 filesystem must be mounted, and must contain a compatible
1025 operating system (ie. something Linux, with the same
1026 or compatible processor architecture).
1028 The single parameter is an argv-style list of arguments.
1029 The first element is the name of the program to run.
1030 Subsequent elements are parameters. The list must be
1031 non-empty (ie. must contain a program name).
1033 The C<$PATH> environment variable will contain at least
1034 C</usr/bin> and C</bin>. If you require a program from
1035 another location, you should provide the full path in the
1038 Shared libraries and data files required by the program
1039 must be available on filesystems which are mounted in the
1040 correct places. It is the caller's responsibility to ensure
1041 all filesystems that are needed are mounted at the right
1044 ("command_lines", (RStringList "lines", [StringList "arguments"]), 51, [],
1045 [], (* XXX how to test? *)
1046 "run a command, returning lines",
1048 This is the same as C<guestfs_command>, but splits the
1049 result into a list of lines.");
1051 ("stat", (RStat "statbuf", [String "path"]), 52, [],
1052 [InitBasicFS, TestOutputStruct (
1054 ["stat"; "/new"]], [CompareWithInt ("size", 0)])],
1055 "get file information",
1057 Returns file information for the given C<path>.
1059 This is the same as the C<stat(2)> system call.");
1061 ("lstat", (RStat "statbuf", [String "path"]), 53, [],
1062 [InitBasicFS, TestOutputStruct (
1064 ["lstat"; "/new"]], [CompareWithInt ("size", 0)])],
1065 "get file information for a symbolic link",
1067 Returns file information for the given C<path>.
1069 This is the same as C<guestfs_stat> except that if C<path>
1070 is a symbolic link, then the link is stat-ed, not the file it
1073 This is the same as the C<lstat(2)> system call.");
1075 ("statvfs", (RStatVFS "statbuf", [String "path"]), 54, [],
1076 [InitBasicFS, TestOutputStruct (
1077 [["statvfs"; "/"]], [CompareWithInt ("bfree", 487702);
1078 CompareWithInt ("blocks", 490020);
1079 CompareWithInt ("bsize", 1024)])],
1080 "get file system statistics",
1082 Returns file system statistics for any mounted file system.
1083 C<path> should be a file or directory in the mounted file system
1084 (typically it is the mount point itself, but it doesn't need to be).
1086 This is the same as the C<statvfs(2)> system call.");
1088 ("tune2fs_l", (RHashtable "superblock", [String "device"]), 55, [],
1090 "get ext2/ext3 superblock details",
1092 This returns the contents of the ext2 or ext3 filesystem superblock
1095 It is the same as running C<tune2fs -l device>. See L<tune2fs(8)>
1096 manpage for more details. The list of fields returned isn't
1097 clearly defined, and depends on both the version of C<tune2fs>
1098 that libguestfs was built against, and the filesystem itself.");
1100 ("blockdev_setro", (RErr, [String "device"]), 56, [],
1101 [InitEmpty, TestOutputTrue (
1102 [["blockdev_setro"; "/dev/sda"];
1103 ["blockdev_getro"; "/dev/sda"]])],
1104 "set block device to read-only",
1106 Sets the block device named C<device> to read-only.
1108 This uses the L<blockdev(8)> command.");
1110 ("blockdev_setrw", (RErr, [String "device"]), 57, [],
1111 [InitEmpty, TestOutputFalse (
1112 [["blockdev_setrw"; "/dev/sda"];
1113 ["blockdev_getro"; "/dev/sda"]])],
1114 "set block device to read-write",
1116 Sets the block device named C<device> to read-write.
1118 This uses the L<blockdev(8)> command.");
1120 ("blockdev_getro", (RBool "ro", [String "device"]), 58, [],
1121 [InitEmpty, TestOutputTrue (
1122 [["blockdev_setro"; "/dev/sda"];
1123 ["blockdev_getro"; "/dev/sda"]])],
1124 "is block device set to read-only",
1126 Returns a boolean indicating if the block device is read-only
1127 (true if read-only, false if not).
1129 This uses the L<blockdev(8)> command.");
1131 ("blockdev_getss", (RInt "sectorsize", [String "device"]), 59, [],
1132 [InitEmpty, TestOutputInt (
1133 [["blockdev_getss"; "/dev/sda"]], 512)],
1134 "get sectorsize of block device",
1136 This returns the size of sectors on a block device.
1137 Usually 512, but can be larger for modern devices.
1139 (Note, this is not the size in sectors, use C<guestfs_blockdev_getsz>
1142 This uses the L<blockdev(8)> command.");
1144 ("blockdev_getbsz", (RInt "blocksize", [String "device"]), 60, [],
1145 [InitEmpty, TestOutputInt (
1146 [["blockdev_getbsz"; "/dev/sda"]], 4096)],
1147 "get blocksize of block device",
1149 This returns the block size of a device.
1151 (Note this is different from both I<size in blocks> and
1152 I<filesystem block size>).
1154 This uses the L<blockdev(8)> command.");
1156 ("blockdev_setbsz", (RErr, [String "device"; Int "blocksize"]), 61, [],
1158 "set blocksize of block device",
1160 This sets the block size of a device.
1162 (Note this is different from both I<size in blocks> and
1163 I<filesystem block size>).
1165 This uses the L<blockdev(8)> command.");
1167 ("blockdev_getsz", (RInt64 "sizeinsectors", [String "device"]), 62, [],
1168 [InitEmpty, TestOutputInt (
1169 [["blockdev_getsz"; "/dev/sda"]], 1024000)],
1170 "get total size of device in 512-byte sectors",
1172 This returns the size of the device in units of 512-byte sectors
1173 (even if the sectorsize isn't 512 bytes ... weird).
1175 See also C<guestfs_blockdev_getss> for the real sector size of
1176 the device, and C<guestfs_blockdev_getsize64> for the more
1177 useful I<size in bytes>.
1179 This uses the L<blockdev(8)> command.");
1181 ("blockdev_getsize64", (RInt64 "sizeinbytes", [String "device"]), 63, [],
1182 [InitEmpty, TestOutputInt (
1183 [["blockdev_getsize64"; "/dev/sda"]], 524288000)],
1184 "get total size of device in bytes",
1186 This returns the size of the device in bytes.
1188 See also C<guestfs_blockdev_getsz>.
1190 This uses the L<blockdev(8)> command.");
1192 ("blockdev_flushbufs", (RErr, [String "device"]), 64, [],
1194 [["blockdev_flushbufs"; "/dev/sda"]]],
1195 "flush device buffers",
1197 This tells the kernel to flush internal buffers associated
1200 This uses the L<blockdev(8)> command.");
1202 ("blockdev_rereadpt", (RErr, [String "device"]), 65, [],
1204 [["blockdev_rereadpt"; "/dev/sda"]]],
1205 "reread partition table",
1207 Reread the partition table on C<device>.
1209 This uses the L<blockdev(8)> command.");
1212 ("upload", (RErr, [FileIn "filename"; String "remotefilename"]), 66, [],
1214 "upload a file from the local machine",
1216 Upload local file C<filename> to C<remotefilename> on the
1219 C<filename> can also be a named pipe.
1221 See also C<guestfs_upload>.");
1223 ("download", (RErr, [String "remotefilename"; FileOut "filename"]), 67, [],
1225 "download a file to the local machine",
1227 Download file C<remotefilename> and save it as C<filename>
1228 on the local machine.
1230 C<filename> can also be a named pipe.
1232 See also C<guestfs_download>, C<guestfs_cat>.");
1237 let all_functions = non_daemon_functions @ daemon_functions
1239 (* In some places we want the functions to be displayed sorted
1240 * alphabetically, so this is useful:
1242 let all_functions_sorted =
1243 List.sort (fun (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) ->
1244 compare n1 n2) all_functions
1246 (* Column names and types from LVM PVs/VGs/LVs. *)
1255 "pv_attr", `String (* XXX *);
1256 "pv_pe_count", `Int;
1257 "pv_pe_alloc_count", `Int;
1260 "pv_mda_count", `Int;
1261 "pv_mda_free", `Bytes;
1262 (* Not in Fedora 10:
1263 "pv_mda_size", `Bytes;
1270 "vg_attr", `String (* XXX *);
1273 "vg_sysid", `String;
1274 "vg_extent_size", `Bytes;
1275 "vg_extent_count", `Int;
1276 "vg_free_count", `Int;
1284 "vg_mda_count", `Int;
1285 "vg_mda_free", `Bytes;
1286 (* Not in Fedora 10:
1287 "vg_mda_size", `Bytes;
1293 "lv_attr", `String (* XXX *);
1296 "lv_kernel_major", `Int;
1297 "lv_kernel_minor", `Int;
1301 "snap_percent", `OptPercent;
1302 "copy_percent", `OptPercent;
1305 "mirror_log", `String;
1309 (* Column names and types from stat structures.
1310 * NB. Can't use things like 'st_atime' because glibc header files
1311 * define some of these as macros. Ugh.
1328 let statvfs_cols = [
1342 (* Useful functions.
1343 * Note we don't want to use any external OCaml libraries which
1344 * makes this a bit harder than it should be.
1346 let failwithf fs = ksprintf failwith fs
1348 let replace_char s c1 c2 =
1349 let s2 = String.copy s in
1350 let r = ref false in
1351 for i = 0 to String.length s2 - 1 do
1352 if String.unsafe_get s2 i = c1 then (
1353 String.unsafe_set s2 i c2;
1357 if not !r then s else s2
1361 (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
1363 let triml ?(test = isspace) str =
1365 let n = ref (String.length str) in
1366 while !n > 0 && test str.[!i]; do
1371 else String.sub str !i !n
1373 let trimr ?(test = isspace) str =
1374 let n = ref (String.length str) in
1375 while !n > 0 && test str.[!n-1]; do
1378 if !n = String.length str then str
1379 else String.sub str 0 !n
1381 let trim ?(test = isspace) str =
1382 trimr ~test (triml ~test str)
1384 let rec find s sub =
1385 let len = String.length s in
1386 let sublen = String.length sub in
1388 if i <= len-sublen then (
1390 if j < sublen then (
1391 if s.[i+j] = sub.[j] then loop2 (j+1)
1397 if r = -1 then loop (i+1) else r
1403 let rec replace_str s s1 s2 =
1404 let len = String.length s in
1405 let sublen = String.length s1 in
1406 let i = find s s1 in
1409 let s' = String.sub s 0 i in
1410 let s'' = String.sub s (i+sublen) (len-i-sublen) in
1411 s' ^ s2 ^ replace_str s'' s1 s2
1414 let rec string_split sep str =
1415 let len = String.length str in
1416 let seplen = String.length sep in
1417 let i = find str sep in
1418 if i = -1 then [str]
1420 let s' = String.sub str 0 i in
1421 let s'' = String.sub str (i+seplen) (len-i-seplen) in
1422 s' :: string_split sep s''
1425 let rec find_map f = function
1426 | [] -> raise Not_found
1430 | None -> find_map f xs
1433 let rec loop i = function
1435 | x :: xs -> f i x; loop (i+1) xs
1440 let rec loop i = function
1442 | x :: xs -> let r = f i x in r :: loop (i+1) xs
1446 let name_of_argt = function
1447 | String n | OptString n | StringList n | Bool n | Int n
1448 | FileIn n | FileOut n -> n
1450 let seq_of_test = function
1451 | TestRun s | TestOutput (s, _) | TestOutputList (s, _)
1452 | TestOutputInt (s, _) | TestOutputTrue s | TestOutputFalse s
1453 | TestOutputLength (s, _) | TestOutputStruct (s, _)
1454 | TestLastFail s -> s
1456 (* Check function names etc. for consistency. *)
1457 let check_functions () =
1458 let contains_uppercase str =
1459 let len = String.length str in
1461 if i >= len then false
1464 if c >= 'A' && c <= 'Z' then true
1471 (* Check function names. *)
1473 fun (name, _, _, _, _, _, _) ->
1474 if String.length name >= 7 && String.sub name 0 7 = "guestfs" then
1475 failwithf "function name %s does not need 'guestfs' prefix" name;
1476 if contains_uppercase name then
1477 failwithf "function name %s should not contain uppercase chars" name;
1478 if String.contains name '-' then
1479 failwithf "function name %s should not contain '-', use '_' instead."
1483 (* Check function parameter/return names. *)
1485 fun (name, style, _, _, _, _, _) ->
1486 let check_arg_ret_name n =
1487 if contains_uppercase n then
1488 failwithf "%s param/ret %s should not contain uppercase chars"
1490 if String.contains n '-' || String.contains n '_' then
1491 failwithf "%s param/ret %s should not contain '-' or '_'"
1494 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;
1495 if n = "argv" || n = "args" then
1496 failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" n
1499 (match fst style with
1501 | RInt n | RInt64 n | RBool n | RConstString n | RString n
1502 | RStringList n | RPVList n | RVGList n | RLVList n
1503 | RStat n | RStatVFS n
1505 check_arg_ret_name n
1507 check_arg_ret_name n;
1508 check_arg_ret_name m
1510 List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) (snd style)
1513 (* Check short descriptions. *)
1515 fun (name, _, _, _, _, shortdesc, _) ->
1516 if shortdesc.[0] <> Char.lowercase shortdesc.[0] then
1517 failwithf "short description of %s should begin with lowercase." name;
1518 let c = shortdesc.[String.length shortdesc-1] in
1519 if c = '\n' || c = '.' then
1520 failwithf "short description of %s should not end with . or \\n." name
1523 (* Check long dscriptions. *)
1525 fun (name, _, _, _, _, _, longdesc) ->
1526 if longdesc.[String.length longdesc-1] = '\n' then
1527 failwithf "long description of %s should not end with \\n." name
1530 (* Check proc_nrs. *)
1532 fun (name, _, proc_nr, _, _, _, _) ->
1533 if proc_nr <= 0 then
1534 failwithf "daemon function %s should have proc_nr > 0" name
1538 fun (name, _, proc_nr, _, _, _, _) ->
1539 if proc_nr <> -1 then
1540 failwithf "non-daemon function %s should have proc_nr -1" name
1541 ) non_daemon_functions;
1544 List.map (fun (name, _, proc_nr, _, _, _, _) -> name, proc_nr)
1547 List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
1548 let rec loop = function
1551 | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
1553 | (name1,nr1) :: (name2,nr2) :: _ ->
1554 failwithf "%s and %s have conflicting procedure numbers (%d, %d)"
1562 (* Ignore functions that have no tests. We generate a
1563 * warning when the user does 'make check' instead.
1565 | name, _, _, _, [], _, _ -> ()
1566 | name, _, _, _, tests, _, _ ->
1570 match seq_of_test test with
1572 failwithf "%s has a test containing an empty sequence" name
1573 | cmds -> List.map List.hd cmds
1575 let funcs = List.flatten funcs in
1577 let tested = List.mem name funcs in
1580 failwithf "function %s has tests but does not test itself" name
1583 (* 'pr' prints to the current output file. *)
1584 let chan = ref stdout
1585 let pr fs = ksprintf (output_string !chan) fs
1587 (* Generate a header block in a number of standard styles. *)
1588 type comment_style = CStyle | HashStyle | OCamlStyle
1589 type license = GPLv2 | LGPLv2
1591 let generate_header comment license =
1592 let c = match comment with
1593 | CStyle -> pr "/* "; " *"
1594 | HashStyle -> pr "# "; "#"
1595 | OCamlStyle -> pr "(* "; " *" in
1596 pr "libguestfs generated file\n";
1597 pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
1598 pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
1600 pr "%s Copyright (C) 2009 Red Hat Inc.\n" c;
1604 pr "%s This program is free software; you can redistribute it and/or modify\n" c;
1605 pr "%s it under the terms of the GNU General Public License as published by\n" c;
1606 pr "%s the Free Software Foundation; either version 2 of the License, or\n" c;
1607 pr "%s (at your option) any later version.\n" c;
1609 pr "%s This program is distributed in the hope that it will be useful,\n" c;
1610 pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
1611 pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" c;
1612 pr "%s GNU General Public License for more details.\n" c;
1614 pr "%s You should have received a copy of the GNU General Public License along\n" c;
1615 pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c;
1616 pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c;
1619 pr "%s This library is free software; you can redistribute it and/or\n" c;
1620 pr "%s modify it under the terms of the GNU Lesser General Public\n" c;
1621 pr "%s License as published by the Free Software Foundation; either\n" c;
1622 pr "%s version 2 of the License, or (at your option) any later version.\n" c;
1624 pr "%s This library is distributed in the hope that it will be useful,\n" c;
1625 pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
1626 pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" c;
1627 pr "%s Lesser General Public License for more details.\n" c;
1629 pr "%s You should have received a copy of the GNU Lesser General Public\n" c;
1630 pr "%s License along with this library; if not, write to the Free Software\n" c;
1631 pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c;
1634 | CStyle -> pr " */\n"
1636 | OCamlStyle -> pr " *)\n"
1640 (* Start of main code generation functions below this line. *)
1642 (* Generate the pod documentation for the C API. *)
1643 let rec generate_actions_pod () =
1645 fun (shortname, style, _, flags, _, _, longdesc) ->
1646 let name = "guestfs_" ^ shortname in
1647 pr "=head2 %s\n\n" name;
1649 generate_prototype ~extern:false ~handle:"handle" name style;
1651 pr "%s\n\n" longdesc;
1652 (match fst style with
1654 pr "This function returns 0 on success or -1 on error.\n\n"
1656 pr "On error this function returns -1.\n\n"
1658 pr "On error this function returns -1.\n\n"
1660 pr "This function returns a C truth value on success or -1 on error.\n\n"
1662 pr "This function returns a string, or NULL on error.
1663 The string is owned by the guest handle and must I<not> be freed.\n\n"
1665 pr "This function returns a string, or NULL on error.
1666 I<The caller must free the returned string after use>.\n\n"
1668 pr "This function returns a NULL-terminated array of strings
1669 (like L<environ(3)>), or NULL if there was an error.
1670 I<The caller must free the strings and the array after use>.\n\n"
1672 pr "This function returns a C<struct guestfs_int_bool *>,
1673 or NULL if there was an error.
1674 I<The caller must call C<guestfs_free_int_bool> after use>.\n\n"
1676 pr "This function returns a C<struct guestfs_lvm_pv_list *>
1677 (see E<lt>guestfs-structs.hE<gt>),
1678 or NULL if there was an error.
1679 I<The caller must call C<guestfs_free_lvm_pv_list> after use>.\n\n"
1681 pr "This function returns a C<struct guestfs_lvm_vg_list *>
1682 (see E<lt>guestfs-structs.hE<gt>),
1683 or NULL if there was an error.
1684 I<The caller must call C<guestfs_free_lvm_vg_list> after use>.\n\n"
1686 pr "This function returns a C<struct guestfs_lvm_lv_list *>
1687 (see E<lt>guestfs-structs.hE<gt>),
1688 or NULL if there was an error.
1689 I<The caller must call C<guestfs_free_lvm_lv_list> after use>.\n\n"
1691 pr "This function returns a C<struct guestfs_stat *>
1692 (see L<stat(2)> and E<lt>guestfs-structs.hE<gt>),
1693 or NULL if there was an error.
1694 I<The caller must call C<free> after use>.\n\n"
1696 pr "This function returns a C<struct guestfs_statvfs *>
1697 (see L<statvfs(2)> and E<lt>guestfs-structs.hE<gt>),
1698 or NULL if there was an error.
1699 I<The caller must call C<free> after use>.\n\n"
1701 pr "This function returns a NULL-terminated array of
1702 strings, or NULL if there was an error.
1703 The array of strings will always have length C<2n+1>, where
1704 C<n> keys and values alternate, followed by the trailing NULL entry.
1705 I<The caller must free the strings and the array after use>.\n\n"
1707 if List.mem ProtocolLimitWarning flags then
1708 pr "%s\n\n" protocol_limit_warning;
1709 if List.mem DangerWillRobinson flags then
1710 pr "%s\n\n" danger_will_robinson;
1711 ) all_functions_sorted
1713 and generate_structs_pod () =
1714 (* LVM structs documentation. *)
1717 pr "=head2 guestfs_lvm_%s\n" typ;
1719 pr " struct guestfs_lvm_%s {\n" typ;
1722 | name, `String -> pr " char *%s;\n" name
1724 pr " /* The next field is NOT nul-terminated, be careful when printing it: */\n";
1725 pr " char %s[32];\n" name
1726 | name, `Bytes -> pr " uint64_t %s;\n" name
1727 | name, `Int -> pr " int64_t %s;\n" name
1728 | name, `OptPercent ->
1729 pr " /* The next field is [0..100] or -1 meaning 'not present': */\n";
1730 pr " float %s;\n" name
1733 pr " struct guestfs_lvm_%s_list {\n" typ;
1734 pr " uint32_t len; /* Number of elements in list. */\n";
1735 pr " struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
1738 pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
1741 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
1743 (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
1744 * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'.
1746 * We have to use an underscore instead of a dash because otherwise
1747 * rpcgen generates incorrect code.
1749 * This header is NOT exported to clients, but see also generate_structs_h.
1751 and generate_xdr () =
1752 generate_header CStyle LGPLv2;
1754 (* This has to be defined to get around a limitation in Sun's rpcgen. *)
1755 pr "typedef string str<>;\n";
1758 (* LVM internal structures. *)
1762 pr "struct guestfs_lvm_int_%s {\n" typ;
1764 | name, `String -> pr " string %s<>;\n" name
1765 | name, `UUID -> pr " opaque %s[32];\n" name
1766 | name, `Bytes -> pr " hyper %s;\n" name
1767 | name, `Int -> pr " hyper %s;\n" name
1768 | name, `OptPercent -> pr " float %s;\n" name
1772 pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
1774 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
1776 (* Stat internal structures. *)
1780 pr "struct guestfs_int_%s {\n" typ;
1782 | name, `Int -> pr " hyper %s;\n" name
1786 ) ["stat", stat_cols; "statvfs", statvfs_cols];
1789 fun (shortname, style, _, _, _, _, _) ->
1790 let name = "guestfs_" ^ shortname in
1792 (match snd style with
1795 pr "struct %s_args {\n" name;
1798 | String n -> pr " string %s<>;\n" n
1799 | OptString n -> pr " str *%s;\n" n
1800 | StringList n -> pr " str %s<>;\n" n
1801 | Bool n -> pr " bool %s;\n" n
1802 | Int n -> pr " int %s;\n" n
1803 | FileIn _ | FileOut _ -> ()
1807 (match fst style with
1810 pr "struct %s_ret {\n" name;
1814 pr "struct %s_ret {\n" name;
1815 pr " hyper %s;\n" n;
1818 pr "struct %s_ret {\n" name;
1822 failwithf "RConstString cannot be returned from a daemon function"
1824 pr "struct %s_ret {\n" name;
1825 pr " string %s<>;\n" n;
1828 pr "struct %s_ret {\n" name;
1829 pr " str %s<>;\n" n;
1832 pr "struct %s_ret {\n" name;
1837 pr "struct %s_ret {\n" name;
1838 pr " guestfs_lvm_int_pv_list %s;\n" n;
1841 pr "struct %s_ret {\n" name;
1842 pr " guestfs_lvm_int_vg_list %s;\n" n;
1845 pr "struct %s_ret {\n" name;
1846 pr " guestfs_lvm_int_lv_list %s;\n" n;
1849 pr "struct %s_ret {\n" name;
1850 pr " guestfs_int_stat %s;\n" n;
1853 pr "struct %s_ret {\n" name;
1854 pr " guestfs_int_statvfs %s;\n" n;
1857 pr "struct %s_ret {\n" name;
1858 pr " str %s<>;\n" n;
1863 (* Table of procedure numbers. *)
1864 pr "enum guestfs_procedure {\n";
1866 fun (shortname, _, proc_nr, _, _, _, _) ->
1867 pr " GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
1869 pr " GUESTFS_PROC_NR_PROCS\n";
1873 (* Having to choose a maximum message size is annoying for several
1874 * reasons (it limits what we can do in the API), but it (a) makes
1875 * the protocol a lot simpler, and (b) provides a bound on the size
1876 * of the daemon which operates in limited memory space. For large
1877 * file transfers you should use FTP.
1879 pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
1882 (* Message header, etc. *)
1884 const GUESTFS_PROGRAM = 0x2000F5F5;
1885 const GUESTFS_PROTOCOL_VERSION = 1;
1887 enum guestfs_message_direction {
1888 GUESTFS_DIRECTION_CALL = 0, /* client -> daemon */
1889 GUESTFS_DIRECTION_REPLY = 1 /* daemon -> client */
1892 enum guestfs_message_status {
1893 GUESTFS_STATUS_OK = 0,
1894 GUESTFS_STATUS_ERROR = 1
1897 const GUESTFS_ERROR_LEN = 256;
1899 struct guestfs_message_error {
1900 string error<GUESTFS_ERROR_LEN>; /* error message */
1903 /* For normal requests and replies (not involving any FileIn or
1904 * FileOut parameters), the protocol is:
1907 * total length (header + args, but not including length word itself)
1909 * guestfs_foo_args struct
1911 * total length (as above)
1913 * guestfs_foo_ret struct
1916 struct guestfs_message_header {
1917 unsigned prog; /* GUESTFS_PROGRAM */
1918 unsigned vers; /* GUESTFS_PROTOCOL_VERSION */
1919 guestfs_procedure proc; /* GUESTFS_PROC_x */
1920 guestfs_message_direction direction;
1921 unsigned serial; /* message serial number */
1922 guestfs_message_status status;
1925 /* Chunked encoding used to transfer files, for FileIn and FileOut
1928 * For requests which have >= 1 FileIn parameter:
1929 * length of header + args (but not length word itself, and not chunks)
1931 * guestfs_foo_args struct
1932 * sequence of chunks for FileIn param #0
1933 * sequence of chunks for FileIn param #1 etc
1935 * For replies which have >= 1 FileOut parameter:
1936 * length of header + ret (but not length word itself, and not chunks)
1938 * guestfs_foo_ret struct
1939 * sequence of chunks for FileOut param #0
1940 * sequence of chunks for FileOut param #1 etc
1942 const GUESTFS_MAX_CHUNK_SIZE = 8192;
1944 struct guestfs_chunk {
1945 int cancel; /* if non-zero, transfer is cancelled */
1946 /* data size is 0 bytes if the transfer has finished successfully */
1947 opaque data<GUESTFS_MAX_CHUNK_SIZE>;
1951 (* Generate the guestfs-structs.h file. *)
1952 and generate_structs_h () =
1953 generate_header CStyle LGPLv2;
1955 (* This is a public exported header file containing various
1956 * structures. The structures are carefully written to have
1957 * exactly the same in-memory format as the XDR structures that
1958 * we use on the wire to the daemon. The reason for creating
1959 * copies of these structures here is just so we don't have to
1960 * export the whole of guestfs_protocol.h (which includes much
1961 * unrelated and XDR-dependent stuff that we don't want to be
1962 * public, or required by clients).
1964 * To reiterate, we will pass these structures to and from the
1965 * client with a simple assignment or memcpy, so the format
1966 * must be identical to what rpcgen / the RFC defines.
1969 (* guestfs_int_bool structure. *)
1970 pr "struct guestfs_int_bool {\n";
1976 (* LVM public structures. *)
1980 pr "struct guestfs_lvm_%s {\n" typ;
1983 | name, `String -> pr " char *%s;\n" name
1984 | name, `UUID -> pr " char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
1985 | name, `Bytes -> pr " uint64_t %s;\n" name
1986 | name, `Int -> pr " int64_t %s;\n" name
1987 | name, `OptPercent -> pr " float %s; /* [0..100] or -1 */\n" name
1991 pr "struct guestfs_lvm_%s_list {\n" typ;
1992 pr " uint32_t len;\n";
1993 pr " struct guestfs_lvm_%s *val;\n" typ;
1996 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
1998 (* Stat structures. *)
2002 pr "struct guestfs_%s {\n" typ;
2005 | name, `Int -> pr " int64_t %s;\n" name
2009 ) ["stat", stat_cols; "statvfs", statvfs_cols]
2011 (* Generate the guestfs-actions.h file. *)
2012 and generate_actions_h () =
2013 generate_header CStyle LGPLv2;
2015 fun (shortname, style, _, _, _, _, _) ->
2016 let name = "guestfs_" ^ shortname in
2017 generate_prototype ~single_line:true ~newline:true ~handle:"handle"
2021 (* Generate the client-side dispatch stubs. *)
2022 and generate_client_actions () =
2023 generate_header CStyle LGPLv2;
2025 (* Client-side stubs for each function. *)
2027 fun (shortname, style, _, _, _, _, _) ->
2028 let name = "guestfs_" ^ shortname in
2030 (* Generate the state struct which stores the high-level
2031 * state between callback functions. The callback(s) are:
2032 * <name>_cb_header_sent header was sent
2033 * <name>_cb_file_sent FileIn file was sent
2034 * <name>_cb_reply_received reply received
2036 pr "struct %s_state {\n" shortname;
2037 pr " int cb_done;\n";
2038 pr " struct guestfs_message_header hdr;\n";
2039 pr " struct guestfs_message_error err;\n";
2040 (match fst style with
2043 failwithf "RConstString cannot be returned from a daemon function"
2045 | RBool _ | RString _ | RStringList _
2047 | RPVList _ | RVGList _ | RLVList _
2048 | RStat _ | RStatVFS _
2050 pr " struct %s_ret ret;\n" name
2055 (* Generate the callback function. *)
2056 pr "static void %s_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
2058 pr " struct %s_state *state = (struct %s_state *) data;\n" shortname shortname;
2060 pr " if (!xdr_guestfs_message_header (xdr, &state->hdr)) {\n";
2061 pr " error (g, \"%s: failed to parse reply header\");\n" name;
2064 pr " if (state->hdr.status == GUESTFS_STATUS_ERROR) {\n";
2065 pr " if (!xdr_guestfs_message_error (xdr, &state->err)) {\n";
2066 pr " error (g, \"%s: failed to parse reply error\");\n" name;
2072 (match fst style with
2075 failwithf "RConstString cannot be returned from a daemon function"
2077 | RBool _ | RString _ | RStringList _
2079 | RPVList _ | RVGList _ | RLVList _
2080 | RStat _ | RStatVFS _
2082 pr " if (!xdr_%s_ret (xdr, &state->ret)) {\n" name;
2083 pr " error (g, \"%s: failed to parse reply\");\n" name;
2089 pr " state->cb_done = 1;\n";
2090 pr " g->main_loop->main_loop_quit (g->main_loop, g);\n";
2093 (* Generate the action stub. *)
2094 generate_prototype ~extern:false ~semicolon:false ~newline:true
2095 ~handle:"g" name style;
2098 match fst style with
2099 | RErr | RInt _ | RInt64 _ | RBool _ -> "-1"
2101 failwithf "RConstString cannot be returned from a daemon function"
2102 | RString _ | RStringList _ | RIntBool _
2103 | RPVList _ | RVGList _ | RLVList _
2104 | RStat _ | RStatVFS _
2110 (match snd style with
2112 | _ -> pr " struct %s_args args;\n" name
2115 pr " struct %s_state state;\n" shortname;
2116 pr " int serial;\n";
2118 pr " if (g->state != READY) {\n";
2119 pr " if (g->state == CONFIG)\n";
2120 pr " error (g, \"%%s: call launch() before using this function\",\n";
2121 pr " \"%s\");\n" name;
2122 pr " else if (g->state == LAUNCHING)\n";
2123 pr " error (g, \"%%s: call wait_ready() before using this function\",\n";
2124 pr " \"%s\");\n" name;
2126 pr " error (g, \"%%s called from the wrong state, %%d != READY\",\n";
2127 pr " \"%s\", g->state);\n" name;
2128 pr " return %s;\n" error_code;
2131 pr " memset (&state, 0, sizeof state);\n";
2134 (* Dispatch the main header and arguments. *)
2135 (match snd style with
2137 pr " serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
2138 (String.uppercase shortname)
2143 pr " args.%s = (char *) %s;\n" n n
2145 pr " args.%s = %s ? (char **) &%s : NULL;\n" n n n
2147 pr " args.%s.%s_val = (char **) %s;\n" n n n;
2148 pr " for (args.%s.%s_len = 0; %s[args.%s.%s_len]; args.%s.%s_len++) ;\n" n n n n n n n;
2150 pr " args.%s = %s;\n" n n
2152 pr " args.%s = %s;\n" n n
2153 | FileIn _ | FileOut _ -> ()
2155 pr " serial = dispatch (g, GUESTFS_PROC_%s,\n"
2156 (String.uppercase shortname);
2157 pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n"
2160 pr " if (serial == -1)\n";
2161 pr " return %s;\n" error_code;
2164 (* Send any additional files requested. *)
2168 pr " if (send_file (g, %s) == -1)\n" n;
2169 pr " return %s;\n" error_code;
2174 (* Wait for the reply from the remote end. *)
2175 pr " state.cb_done = 0;\n";
2176 pr " g->reply_cb_internal = %s_cb;\n" shortname;
2177 pr " g->reply_cb_internal_data = &state;\n";
2178 pr " (void) g->main_loop->main_loop_run (g->main_loop, g);\n";
2179 pr " g->reply_cb_internal = NULL;\n";
2180 pr " g->reply_cb_internal_data = NULL;\n";
2181 pr " if (!state.cb_done) {\n";
2182 pr " error (g, \"%s failed, see earlier error messages\");\n" name;
2183 pr " return %s;\n" error_code;
2187 pr " if (check_reply_header (g, &state.hdr, GUESTFS_PROC_%s, serial) == -1)\n"
2188 (String.uppercase shortname);
2189 pr " return %s;\n" error_code;
2192 pr " if (state.hdr.status == GUESTFS_STATUS_ERROR) {\n";
2193 pr " error (g, \"%%s\", state.err.error);\n";
2194 pr " return %s;\n" error_code;
2198 (* Expecting to receive further files (FileOut)? *)
2202 pr " if (receive_file (g, %s) == -1)\n" n;
2203 pr " return %s;\n" error_code;
2208 (match fst style with
2209 | RErr -> pr " return 0;\n"
2210 | RInt n | RInt64 n | RBool n ->
2211 pr " return state.ret.%s;\n" n
2213 failwithf "RConstString cannot be returned from a daemon function"
2215 pr " return state.ret.%s; /* caller will free */\n" n
2216 | RStringList n | RHashtable n ->
2217 pr " /* caller will free this, but we need to add a NULL entry */\n";
2218 pr " state.ret.%s.%s_val =" n n;
2219 pr " safe_realloc (g, state.ret.%s.%s_val,\n" n n;
2220 pr " sizeof (char *) * (state.ret.%s.%s_len + 1));\n"
2222 pr " state.ret.%s.%s_val[state.ret.%s.%s_len] = NULL;\n" n n n n;
2223 pr " return state.ret.%s.%s_val;\n" n n
2225 pr " /* caller with free this */\n";
2226 pr " return safe_memdup (g, &state.ret, sizeof (state.ret));\n"
2227 | RPVList n | RVGList n | RLVList n
2228 | RStat n | RStatVFS n ->
2229 pr " /* caller will free this */\n";
2230 pr " return safe_memdup (g, &state.ret.%s, sizeof (state.ret.%s));\n" n n
2236 (* Generate daemon/actions.h. *)
2237 and generate_daemon_actions_h () =
2238 generate_header CStyle GPLv2;
2240 pr "#include \"../src/guestfs_protocol.h\"\n";
2244 fun (name, style, _, _, _, _, _) ->
2246 ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
2250 (* Generate the server-side stubs. *)
2251 and generate_daemon_actions () =
2252 generate_header CStyle GPLv2;
2254 pr "#define _GNU_SOURCE // for strchrnul\n";
2256 pr "#include <stdio.h>\n";
2257 pr "#include <stdlib.h>\n";
2258 pr "#include <string.h>\n";
2259 pr "#include <inttypes.h>\n";
2260 pr "#include <ctype.h>\n";
2261 pr "#include <rpc/types.h>\n";
2262 pr "#include <rpc/xdr.h>\n";
2264 pr "#include \"daemon.h\"\n";
2265 pr "#include \"../src/guestfs_protocol.h\"\n";
2266 pr "#include \"actions.h\"\n";
2270 fun (name, style, _, _, _, _, _) ->
2271 (* Generate server-side stubs. *)
2272 pr "static void %s_stub (XDR *xdr_in)\n" name;
2275 match fst style with
2276 | RErr | RInt _ -> pr " int r;\n"; "-1"
2277 | RInt64 _ -> pr " int64_t r;\n"; "-1"
2278 | RBool _ -> pr " int r;\n"; "-1"
2280 failwithf "RConstString cannot be returned from a daemon function"
2281 | RString _ -> pr " char *r;\n"; "NULL"
2282 | RStringList _ | RHashtable _ -> pr " char **r;\n"; "NULL"
2283 | RIntBool _ -> pr " guestfs_%s_ret *r;\n" name; "NULL"
2284 | RPVList _ -> pr " guestfs_lvm_int_pv_list *r;\n"; "NULL"
2285 | RVGList _ -> pr " guestfs_lvm_int_vg_list *r;\n"; "NULL"
2286 | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL"
2287 | RStat _ -> pr " guestfs_int_stat *r;\n"; "NULL"
2288 | RStatVFS _ -> pr " guestfs_int_statvfs *r;\n"; "NULL" in
2290 (match snd style with
2293 pr " struct guestfs_%s_args args;\n" name;
2297 | OptString n -> pr " const char *%s;\n" n
2298 | StringList n -> pr " char **%s;\n" n
2299 | Bool n -> pr " int %s;\n" n
2300 | Int n -> pr " int %s;\n" n
2301 | FileIn _ | FileOut _ -> ()
2306 (match snd style with
2309 pr " memset (&args, 0, sizeof args);\n";
2311 pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
2312 pr " reply_with_error (\"%%s: daemon failed to decode procedure arguments\", \"%s\");\n" name;
2317 | String n -> pr " %s = args.%s;\n" n n
2318 | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n
2320 pr " args.%s.%s_val = realloc (args.%s.%s_val, sizeof (char *) * (args.%s.%s_len+1));\n" n n n n n n;
2321 pr " args.%s.%s_val[args.%s.%s_len] = NULL;\n" n n n n;
2322 pr " %s = args.%s.%s_val;\n" n n n
2323 | Bool n -> pr " %s = args.%s;\n" n n
2324 | Int n -> pr " %s = args.%s;\n" n n
2325 | FileIn _ | FileOut _ -> ()
2330 (* Don't want to call the impl with any FileIn or FileOut
2331 * parameters, since these go "outside" the RPC protocol.
2334 List.filter (function FileIn _ | FileOut _ -> false | _ -> true)
2336 pr " r = do_%s " name;
2337 generate_call_args argsnofile;
2340 pr " if (r == %s)\n" error_code;
2341 pr " /* do_%s has already called reply_with_error */\n" name;
2345 (* If there are any FileOut parameters, then the impl must
2346 * send its own reply.
2349 List.exists (function FileOut _ -> true | _ -> false) (snd style) in
2351 pr " /* do_%s has already sent a reply */\n" name
2353 match fst style with
2354 | RErr -> pr " reply (NULL, NULL);\n"
2355 | RInt n | RInt64 n | RBool n ->
2356 pr " struct guestfs_%s_ret ret;\n" name;
2357 pr " ret.%s = r;\n" n;
2358 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2361 failwithf "RConstString cannot be returned from a daemon function"
2363 pr " struct guestfs_%s_ret ret;\n" name;
2364 pr " ret.%s = r;\n" n;
2365 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2368 | RStringList n | RHashtable n ->
2369 pr " struct guestfs_%s_ret ret;\n" name;
2370 pr " ret.%s.%s_len = count_strings (r);\n" n n;
2371 pr " ret.%s.%s_val = r;\n" n n;
2372 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
2374 pr " free_strings (r);\n"
2376 pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n"
2378 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) r);\n" name
2379 | RPVList n | RVGList n | RLVList n
2380 | RStat n | RStatVFS n ->
2381 pr " struct guestfs_%s_ret ret;\n" name;
2382 pr " ret.%s = *r;\n" n;
2383 pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
2385 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
2389 (* Free the args. *)
2390 (match snd style with
2395 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
2402 (* Dispatch function. *)
2403 pr "void dispatch_incoming_message (XDR *xdr_in)\n";
2405 pr " switch (proc_nr) {\n";
2408 fun (name, style, _, _, _, _, _) ->
2409 pr " case GUESTFS_PROC_%s:\n" (String.uppercase name);
2410 pr " %s_stub (xdr_in);\n" name;
2415 pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
2420 (* LVM columns and tokenization functions. *)
2421 (* XXX This generates crap code. We should rethink how we
2427 pr "static const char *lvm_%s_cols = \"%s\";\n"
2428 typ (String.concat "," (List.map fst cols));
2431 pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
2433 pr " char *tok, *p, *next;\n";
2437 pr " fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
2440 pr " if (!str) {\n";
2441 pr " fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
2444 pr " if (!*str || isspace (*str)) {\n";
2445 pr " fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
2450 fun (name, coltype) ->
2451 pr " if (!tok) {\n";
2452 pr " fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
2455 pr " p = strchrnul (tok, ',');\n";
2456 pr " if (*p) next = p+1; else next = NULL;\n";
2457 pr " *p = '\\0';\n";
2460 pr " r->%s = strdup (tok);\n" name;
2461 pr " if (r->%s == NULL) {\n" name;
2462 pr " perror (\"strdup\");\n";
2466 pr " for (i = j = 0; i < 32; ++j) {\n";
2467 pr " if (tok[j] == '\\0') {\n";
2468 pr " fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
2470 pr " } else if (tok[j] != '-')\n";
2471 pr " r->%s[i++] = tok[j];\n" name;
2474 pr " if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
2475 pr " fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
2479 pr " if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
2480 pr " fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
2484 pr " if (tok[0] == '\\0')\n";
2485 pr " r->%s = -1;\n" name;
2486 pr " else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
2487 pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
2491 pr " tok = next;\n";
2494 pr " if (tok != NULL) {\n";
2495 pr " fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
2502 pr "guestfs_lvm_int_%s_list *\n" typ;
2503 pr "parse_command_line_%ss (void)\n" typ;
2505 pr " char *out, *err;\n";
2506 pr " char *p, *pend;\n";
2508 pr " guestfs_lvm_int_%s_list *ret;\n" typ;
2509 pr " void *newp;\n";
2511 pr " ret = malloc (sizeof *ret);\n";
2512 pr " if (!ret) {\n";
2513 pr " reply_with_perror (\"malloc\");\n";
2514 pr " return NULL;\n";
2517 pr " ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
2518 pr " ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
2520 pr " r = command (&out, &err,\n";
2521 pr " \"/sbin/lvm\", \"%ss\",\n" typ;
2522 pr " \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
2523 pr " \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
2524 pr " if (r == -1) {\n";
2525 pr " reply_with_error (\"%%s\", err);\n";
2526 pr " free (out);\n";
2527 pr " free (err);\n";
2528 pr " free (ret);\n";
2529 pr " return NULL;\n";
2532 pr " free (err);\n";
2534 pr " /* Tokenize each line of the output. */\n";
2537 pr " while (p) {\n";
2538 pr " pend = strchr (p, '\\n'); /* Get the next line of output. */\n";
2539 pr " if (pend) {\n";
2540 pr " *pend = '\\0';\n";
2544 pr " while (*p && isspace (*p)) /* Skip any leading whitespace. */\n";
2547 pr " if (!*p) { /* Empty line? Skip it. */\n";
2552 pr " /* Allocate some space to store this next entry. */\n";
2553 pr " newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
2554 pr " sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
2555 pr " if (newp == NULL) {\n";
2556 pr " reply_with_perror (\"realloc\");\n";
2557 pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
2558 pr " free (ret);\n";
2559 pr " free (out);\n";
2560 pr " return NULL;\n";
2562 pr " ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
2564 pr " /* Tokenize the next entry. */\n";
2565 pr " r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
2566 pr " if (r == -1) {\n";
2567 pr " reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
2568 pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
2569 pr " free (ret);\n";
2570 pr " free (out);\n";
2571 pr " return NULL;\n";
2578 pr " ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
2580 pr " free (out);\n";
2581 pr " return ret;\n";
2584 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
2586 (* Generate the tests. *)
2587 and generate_tests () =
2588 generate_header CStyle GPLv2;
2595 #include <sys/types.h>
2598 #include \"guestfs.h\"
2600 static guestfs_h *g;
2601 static int suppress_error = 0;
2603 static void print_error (guestfs_h *g, void *data, const char *msg)
2605 if (!suppress_error)
2606 fprintf (stderr, \"%%s\\n\", msg);
2609 static void print_strings (char * const * const argv)
2613 for (argc = 0; argv[argc] != NULL; ++argc)
2614 printf (\"\\t%%s\\n\", argv[argc]);
2618 static void print_table (char * const * const argv)
2622 for (i = 0; argv[i] != NULL; i += 2)
2623 printf (\"%%s: %%s\\n\", argv[i], argv[i+1]);
2627 static void no_test_warnings (void)
2633 | name, _, _, _, [], _, _ ->
2634 pr " fprintf (stderr, \"warning: \\\"guestfs_%s\\\" has no tests\\n\");\n" name
2635 | name, _, _, _, tests, _, _ -> ()
2641 (* Generate the actual tests. Note that we generate the tests
2642 * in reverse order, deliberately, so that (in general) the
2643 * newest tests run first. This makes it quicker and easier to
2648 fun (name, _, _, _, tests, _, _) ->
2649 mapi (generate_one_test name) tests
2650 ) (List.rev all_functions) in
2651 let test_names = List.concat test_names in
2652 let nr_tests = List.length test_names in
2655 int main (int argc, char *argv[])
2662 int nr_tests, test_num = 0;
2664 no_test_warnings ();
2666 g = guestfs_create ();
2668 printf (\"guestfs_create FAILED\\n\");
2672 guestfs_set_error_handler (g, print_error, NULL);
2674 srcdir = getenv (\"srcdir\");
2675 if (!srcdir) srcdir = \".\";
2676 guestfs_set_path (g, srcdir);
2678 snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir);
2679 fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
2684 if (lseek (fd, %d, SEEK_SET) == -1) {
2690 if (write (fd, &c, 1) == -1) {
2696 if (close (fd) == -1) {
2701 if (guestfs_add_drive (g, buf) == -1) {
2702 printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
2706 snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir);
2707 fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
2712 if (lseek (fd, %d, SEEK_SET) == -1) {
2718 if (write (fd, &c, 1) == -1) {
2724 if (close (fd) == -1) {
2729 if (guestfs_add_drive (g, buf) == -1) {
2730 printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
2734 snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir);
2735 fd = open (buf, O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_TRUNC, 0666);
2740 if (lseek (fd, %d, SEEK_SET) == -1) {
2746 if (write (fd, &c, 1) == -1) {
2752 if (close (fd) == -1) {
2757 if (guestfs_add_drive (g, buf) == -1) {
2758 printf (\"guestfs_add_drive %%s FAILED\\n\", buf);
2762 if (guestfs_launch (g) == -1) {
2763 printf (\"guestfs_launch FAILED\\n\");
2766 if (guestfs_wait_ready (g) == -1) {
2767 printf (\"guestfs_wait_ready FAILED\\n\");
2773 " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests;
2777 pr " test_num++;\n";
2778 pr " printf (\"%%3d/%%3d %s\\n\", test_num, nr_tests);\n" test_name;
2779 pr " if (%s () == -1) {\n" test_name;
2780 pr " printf (\"%s FAILED\\n\");\n" test_name;
2786 pr " guestfs_close (g);\n";
2787 pr " snprintf (buf, sizeof buf, \"%%s/test1.img\", srcdir);\n";
2788 pr " unlink (buf);\n";
2789 pr " snprintf (buf, sizeof buf, \"%%s/test2.img\", srcdir);\n";
2790 pr " unlink (buf);\n";
2791 pr " snprintf (buf, sizeof buf, \"%%s/test3.img\", srcdir);\n";
2792 pr " unlink (buf);\n";
2795 pr " if (failed > 0) {\n";
2796 pr " printf (\"***** %%d / %%d tests FAILED *****\\n\", failed, nr_tests);\n";
2804 and generate_one_test name i (init, test) =
2805 let test_name = sprintf "test_%s_%d" name i in
2807 pr "static int %s (void)\n" test_name;
2813 pr " /* InitEmpty for %s (%d) */\n" name i;
2814 List.iter (generate_test_command_call test_name)
2818 pr " /* InitBasicFS for %s (%d): create ext2 on /dev/sda1 */\n" name i;
2819 List.iter (generate_test_command_call test_name)
2822 ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
2823 ["mkfs"; "ext2"; "/dev/sda1"];
2824 ["mount"; "/dev/sda1"; "/"]]
2825 | InitBasicFSonLVM ->
2826 pr " /* InitBasicFSonLVM for %s (%d): create ext2 on /dev/VG/LV */\n"
2828 List.iter (generate_test_command_call test_name)
2831 ["sfdisk"; "/dev/sda"; "0"; "0"; "0"; ","];
2832 ["pvcreate"; "/dev/sda1"];
2833 ["vgcreate"; "VG"; "/dev/sda1"];
2834 ["lvcreate"; "LV"; "VG"; "8"];
2835 ["mkfs"; "ext2"; "/dev/VG/LV"];
2836 ["mount"; "/dev/VG/LV"; "/"]]
2839 let get_seq_last = function
2841 failwithf "%s: you cannot use [] (empty list) when expecting a command"
2844 let seq = List.rev seq in
2845 List.rev (List.tl seq), List.hd seq
2850 pr " /* TestRun for %s (%d) */\n" name i;
2851 List.iter (generate_test_command_call test_name) seq
2852 | TestOutput (seq, expected) ->
2853 pr " /* TestOutput for %s (%d) */\n" name i;
2854 let seq, last = get_seq_last seq in
2856 pr " if (strcmp (r, \"%s\") != 0) {\n" (c_quote expected);
2857 pr " fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r);\n" test_name (c_quote expected);
2861 List.iter (generate_test_command_call test_name) seq;
2862 generate_test_command_call ~test test_name last
2863 | TestOutputList (seq, expected) ->
2864 pr " /* TestOutputList for %s (%d) */\n" name i;
2865 let seq, last = get_seq_last seq in
2869 pr " if (!r[%d]) {\n" i;
2870 pr " fprintf (stderr, \"%s: short list returned from command\\n\");\n" test_name;
2871 pr " print_strings (r);\n";
2874 pr " if (strcmp (r[%d], \"%s\") != 0) {\n" i (c_quote str);
2875 pr " fprintf (stderr, \"%s: expected \\\"%s\\\" but got \\\"%%s\\\"\\n\", r[%d]);\n" test_name (c_quote str) i;
2879 pr " if (r[%d] != NULL) {\n" (List.length expected);
2880 pr " fprintf (stderr, \"%s: extra elements returned from command\\n\");\n"
2882 pr " print_strings (r);\n";
2886 List.iter (generate_test_command_call test_name) seq;
2887 generate_test_command_call ~test test_name last
2888 | TestOutputInt (seq, expected) ->
2889 pr " /* TestOutputInt for %s (%d) */\n" name i;
2890 let seq, last = get_seq_last seq in
2892 pr " if (r != %d) {\n" expected;
2893 pr " fprintf (stderr, \"%s: expected %d but got %%d\\n\","
2899 List.iter (generate_test_command_call test_name) seq;
2900 generate_test_command_call ~test test_name last
2901 | TestOutputTrue seq ->
2902 pr " /* TestOutputTrue for %s (%d) */\n" name i;
2903 let seq, last = get_seq_last seq in
2906 pr " fprintf (stderr, \"%s: expected true, got false\\n\");\n"
2911 List.iter (generate_test_command_call test_name) seq;
2912 generate_test_command_call ~test test_name last
2913 | TestOutputFalse seq ->
2914 pr " /* TestOutputFalse for %s (%d) */\n" name i;
2915 let seq, last = get_seq_last seq in
2918 pr " fprintf (stderr, \"%s: expected false, got true\\n\");\n"
2923 List.iter (generate_test_command_call test_name) seq;
2924 generate_test_command_call ~test test_name last
2925 | TestOutputLength (seq, expected) ->
2926 pr " /* TestOutputLength for %s (%d) */\n" name i;
2927 let seq, last = get_seq_last seq in
2930 pr " for (j = 0; j < %d; ++j)\n" expected;
2931 pr " if (r[j] == NULL) {\n";
2932 pr " fprintf (stderr, \"%s: short list returned\\n\");\n"
2934 pr " print_strings (r);\n";
2937 pr " if (r[j] != NULL) {\n";
2938 pr " fprintf (stderr, \"%s: long list returned\\n\");\n"
2940 pr " print_strings (r);\n";
2944 List.iter (generate_test_command_call test_name) seq;
2945 generate_test_command_call ~test test_name last
2946 | TestOutputStruct (seq, checks) ->
2947 pr " /* TestOutputStruct for %s (%d) */\n" name i;
2948 let seq, last = get_seq_last seq in
2952 | CompareWithInt (field, expected) ->
2953 pr " if (r->%s != %d) {\n" field expected;
2954 pr " fprintf (stderr, \"%s: %s was %%d, expected %d\\n\",\n"
2955 test_name field expected;
2956 pr " (int) r->%s);\n" field;
2959 | CompareWithString (field, expected) ->
2960 pr " if (strcmp (r->%s, \"%s\") != 0) {\n" field expected;
2961 pr " fprintf (stderr, \"%s: %s was \"%%s\", expected \"%s\"\\n\",\n"
2962 test_name field expected;
2963 pr " r->%s);\n" field;
2966 | CompareFieldsIntEq (field1, field2) ->
2967 pr " if (r->%s != r->%s) {\n" field1 field2;
2968 pr " fprintf (stderr, \"%s: %s (%%d) <> %s (%%d)\\n\",\n"
2969 test_name field1 field2;
2970 pr " (int) r->%s, (int) r->%s);\n" field1 field2;
2973 | CompareFieldsStrEq (field1, field2) ->
2974 pr " if (strcmp (r->%s, r->%s) != 0) {\n" field1 field2;
2975 pr " fprintf (stderr, \"%s: %s (\"%%s\") <> %s (\"%%s\")\\n\",\n"
2976 test_name field1 field2;
2977 pr " r->%s, r->%s);\n" field1 field2;
2982 List.iter (generate_test_command_call test_name) seq;
2983 generate_test_command_call ~test test_name last
2984 | TestLastFail seq ->
2985 pr " /* TestLastFail for %s (%d) */\n" name i;
2986 let seq, last = get_seq_last seq in
2987 List.iter (generate_test_command_call test_name) seq;
2988 generate_test_command_call test_name ~expect_error:true last
2996 (* Generate the code to run a command, leaving the result in 'r'.
2997 * If you expect to get an error then you should set expect_error:true.
2999 and generate_test_command_call ?(expect_error = false) ?test test_name cmd =
3001 | [] -> assert false
3003 (* Look up the command to find out what args/ret it has. *)
3006 let _, style, _, _, _, _, _ =
3007 List.find (fun (n, _, _, _, _, _, _) -> n = name) all_functions in
3010 failwithf "%s: in test, command %s was not found" test_name name in
3012 if List.length (snd style) <> List.length args then
3013 failwithf "%s: in test, wrong number of args given to %s"
3024 | FileIn _, _ | FileOut _, _ -> ()
3025 | StringList n, arg ->
3026 pr " char *%s[] = {\n" n;
3027 let strs = string_split " " arg in
3029 fun str -> pr " \"%s\",\n" (c_quote str)
3033 ) (List.combine (snd style) args);
3036 match fst style with
3037 | RErr | RInt _ | RBool _ -> pr " int r;\n"; "-1"
3038 | RInt64 _ -> pr " int64_t r;\n"; "-1"
3039 | RConstString _ -> pr " const char *r;\n"; "NULL"
3040 | RString _ -> pr " char *r;\n"; "NULL"
3041 | RStringList _ | RHashtable _ ->
3046 pr " struct guestfs_int_bool *r;\n"; "NULL"
3048 pr " struct guestfs_lvm_pv_list *r;\n"; "NULL"
3050 pr " struct guestfs_lvm_vg_list *r;\n"; "NULL"
3052 pr " struct guestfs_lvm_lv_list *r;\n"; "NULL"
3054 pr " struct guestfs_stat *r;\n"; "NULL"
3056 pr " struct guestfs_statvfs *r;\n"; "NULL" in
3058 pr " suppress_error = %d;\n" (if expect_error then 1 else 0);
3059 pr " r = guestfs_%s (g" name;
3061 (* Generate the parameters. *)
3065 | FileIn _, arg | FileOut _, arg ->
3066 pr ", \"%s\"" (c_quote arg)
3067 | OptString _, arg ->
3068 if arg = "NULL" then pr ", NULL" else pr ", \"%s\"" (c_quote arg)
3069 | StringList n, _ ->
3073 try int_of_string arg
3074 with Failure "int_of_string" ->
3075 failwithf "%s: expecting an int, but got '%s'" test_name arg in
3078 let b = bool_of_string arg in pr ", %d" (if b then 1 else 0)
3079 ) (List.combine (snd style) args);
3082 if not expect_error then
3083 pr " if (r == %s)\n" error_code
3085 pr " if (r != %s)\n" error_code;
3088 (* Insert the test code. *)
3094 (match fst style with
3095 | RErr | RInt _ | RInt64 _ | RBool _ | RConstString _ -> ()
3096 | RString _ -> pr " free (r);\n"
3097 | RStringList _ | RHashtable _ ->
3098 pr " for (i = 0; r[i] != NULL; ++i)\n";
3099 pr " free (r[i]);\n";
3102 pr " guestfs_free_int_bool (r);\n"
3104 pr " guestfs_free_lvm_pv_list (r);\n"
3106 pr " guestfs_free_lvm_vg_list (r);\n"
3108 pr " guestfs_free_lvm_lv_list (r);\n"
3109 | RStat _ | RStatVFS _ ->
3116 let str = replace_str str "\r" "\\r" in
3117 let str = replace_str str "\n" "\\n" in
3118 let str = replace_str str "\t" "\\t" in
3121 (* Generate a lot of different functions for guestfish. *)
3122 and generate_fish_cmds () =
3123 generate_header CStyle GPLv2;
3127 fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3129 let all_functions_sorted =
3131 fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3132 ) all_functions_sorted in
3134 pr "#include <stdio.h>\n";
3135 pr "#include <stdlib.h>\n";
3136 pr "#include <string.h>\n";
3137 pr "#include <inttypes.h>\n";
3139 pr "#include <guestfs.h>\n";
3140 pr "#include \"fish.h\"\n";
3143 (* list_commands function, which implements guestfish -h *)
3144 pr "void list_commands (void)\n";
3146 pr " printf (\" %%-16s %%s\\n\", \"Command\", \"Description\");\n";
3147 pr " list_builtin_commands ();\n";
3149 fun (name, _, _, flags, _, shortdesc, _) ->
3150 let name = replace_char name '_' '-' in
3151 pr " printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
3153 ) all_functions_sorted;
3154 pr " printf (\" Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
3158 (* display_command function, which implements guestfish -h cmd *)
3159 pr "void display_command (const char *cmd)\n";
3162 fun (name, style, _, flags, _, shortdesc, longdesc) ->
3163 let name2 = replace_char name '_' '-' in
3165 try find_map (function FishAlias n -> Some n | _ -> None) flags
3166 with Not_found -> name in
3167 let longdesc = replace_str longdesc "C<guestfs_" "C<" in
3169 match snd style with
3173 name2 (String.concat "> <" (List.map name_of_argt args)) in
3176 if List.mem ProtocolLimitWarning flags then
3177 ("\n\n" ^ protocol_limit_warning)
3180 (* For DangerWillRobinson commands, we should probably have
3181 * guestfish prompt before allowing you to use them (especially
3182 * in interactive mode). XXX
3186 if List.mem DangerWillRobinson flags then
3187 ("\n\n" ^ danger_will_robinson)
3190 let describe_alias =
3191 if name <> alias then
3192 sprintf "\n\nYou can use '%s' as an alias for this command." alias
3196 pr "strcasecmp (cmd, \"%s\") == 0" name;
3197 if name <> name2 then
3198 pr " || strcasecmp (cmd, \"%s\") == 0" name2;
3199 if name <> alias then
3200 pr " || strcasecmp (cmd, \"%s\") == 0" alias;
3202 pr " pod2text (\"%s - %s\", %S);\n"
3204 (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings ^ describe_alias);
3207 pr " display_builtin_command (cmd);\n";
3211 (* print_{pv,vg,lv}_list functions *)
3215 pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
3222 pr " printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
3224 pr " printf (\"%s: \");\n" name;
3225 pr " for (i = 0; i < 32; ++i)\n";
3226 pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
3227 pr " printf (\"\\n\");\n"
3229 pr " printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
3231 pr " printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
3232 | name, `OptPercent ->
3233 pr " if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
3234 typ name name typ name;
3235 pr " else printf (\"%s: \\n\");\n" name
3239 pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
3244 pr " for (i = 0; i < %ss->len; ++i)\n" typ;
3245 pr " print_%s (&%ss->val[i]);\n" typ typ;
3248 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
3250 (* print_{stat,statvfs} functions *)
3254 pr "static void print_%s (struct guestfs_%s *%s)\n" typ typ typ;
3259 pr " printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
3263 ) ["stat", stat_cols; "statvfs", statvfs_cols];
3265 (* run_<action> actions *)
3267 fun (name, style, _, flags, _, _, _) ->
3268 pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
3270 (match fst style with
3273 | RBool _ -> pr " int r;\n"
3274 | RInt64 _ -> pr " int64_t r;\n"
3275 | RConstString _ -> pr " const char *r;\n"
3276 | RString _ -> pr " char *r;\n"
3277 | RStringList _ | RHashtable _ -> pr " char **r;\n"
3278 | RIntBool _ -> pr " struct guestfs_int_bool *r;\n"
3279 | RPVList _ -> pr " struct guestfs_lvm_pv_list *r;\n"
3280 | RVGList _ -> pr " struct guestfs_lvm_vg_list *r;\n"
3281 | RLVList _ -> pr " struct guestfs_lvm_lv_list *r;\n"
3282 | RStat _ -> pr " struct guestfs_stat *r;\n"
3283 | RStatVFS _ -> pr " struct guestfs_statvfs *r;\n"
3290 | FileOut n -> pr " const char *%s;\n" n
3291 | StringList n -> pr " char **%s;\n" n
3292 | Bool n -> pr " int %s;\n" n
3293 | Int n -> pr " int %s;\n" n
3296 (* Check and convert parameters. *)
3297 let argc_expected = List.length (snd style) in
3298 pr " if (argc != %d) {\n" argc_expected;
3299 pr " fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
3301 pr " fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
3307 | String name -> pr " %s = argv[%d];\n" name i
3309 pr " %s = strcmp (argv[%d], \"\") != 0 ? argv[%d] : NULL;\n"
3312 pr " %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdin\";\n"
3315 pr " %s = strcmp (argv[%d], \"-\") != 0 ? argv[%d] : \"/dev/stdout\";\n"
3317 | StringList name ->
3318 pr " %s = parse_string_list (argv[%d]);\n" name i
3320 pr " %s = is_true (argv[%d]) ? 1 : 0;\n" name i
3322 pr " %s = atoi (argv[%d]);\n" name i
3325 (* Call C API function. *)
3327 try find_map (function FishAction n -> Some n | _ -> None) flags
3328 with Not_found -> sprintf "guestfs_%s" name in
3330 generate_call_args ~handle:"g" (snd style);
3333 (* Check return value for errors and display command results. *)
3334 (match fst style with
3335 | RErr -> pr " return r;\n"
3337 pr " if (r == -1) return -1;\n";
3338 pr " printf (\"%%d\\n\", r);\n";
3341 pr " if (r == -1) return -1;\n";
3342 pr " printf (\"%%\" PRIi64 \"\\n\", r);\n";
3345 pr " if (r == -1) return -1;\n";
3346 pr " if (r) printf (\"true\\n\"); else printf (\"false\\n\");\n";
3349 pr " if (r == NULL) return -1;\n";
3350 pr " printf (\"%%s\\n\", r);\n";
3353 pr " if (r == NULL) return -1;\n";
3354 pr " printf (\"%%s\\n\", r);\n";
3358 pr " if (r == NULL) return -1;\n";
3359 pr " print_strings (r);\n";
3360 pr " free_strings (r);\n";
3363 pr " if (r == NULL) return -1;\n";
3364 pr " printf (\"%%d, %%s\\n\", r->i,\n";
3365 pr " r->b ? \"true\" : \"false\");\n";
3366 pr " guestfs_free_int_bool (r);\n";
3369 pr " if (r == NULL) return -1;\n";
3370 pr " print_pv_list (r);\n";
3371 pr " guestfs_free_lvm_pv_list (r);\n";
3374 pr " if (r == NULL) return -1;\n";
3375 pr " print_vg_list (r);\n";
3376 pr " guestfs_free_lvm_vg_list (r);\n";
3379 pr " if (r == NULL) return -1;\n";
3380 pr " print_lv_list (r);\n";
3381 pr " guestfs_free_lvm_lv_list (r);\n";
3384 pr " if (r == NULL) return -1;\n";
3385 pr " print_stat (r);\n";
3389 pr " if (r == NULL) return -1;\n";
3390 pr " print_statvfs (r);\n";
3394 pr " if (r == NULL) return -1;\n";
3395 pr " print_table (r);\n";
3396 pr " free_strings (r);\n";
3403 (* run_action function *)
3404 pr "int run_action (const char *cmd, int argc, char *argv[])\n";
3407 fun (name, _, _, flags, _, _, _) ->
3408 let name2 = replace_char name '_' '-' in
3410 try find_map (function FishAlias n -> Some n | _ -> None) flags
3411 with Not_found -> name in
3413 pr "strcasecmp (cmd, \"%s\") == 0" name;
3414 if name <> name2 then
3415 pr " || strcasecmp (cmd, \"%s\") == 0" name2;
3416 if name <> alias then
3417 pr " || strcasecmp (cmd, \"%s\") == 0" alias;
3419 pr " return run_%s (cmd, argc, argv);\n" name;
3423 pr " fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
3430 (* Readline completion for guestfish. *)
3431 and generate_fish_completion () =
3432 generate_header CStyle GPLv2;
3436 fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3446 #ifdef HAVE_LIBREADLINE
3447 #include <readline/readline.h>
3452 #ifdef HAVE_LIBREADLINE
3454 static const char *commands[] = {
3457 (* Get the commands and sort them, including the aliases. *)
3460 fun (name, _, _, flags, _, _, _) ->
3461 let name2 = replace_char name '_' '-' in
3463 try find_map (function FishAlias n -> Some n | _ -> None) flags
3464 with Not_found -> name in
3466 if name <> alias then [name2; alias] else [name2]
3468 let commands = List.flatten commands in
3469 let commands = List.sort compare commands in
3471 List.iter (pr " \"%s\",\n") commands;
3477 generator (const char *text, int state)
3479 static int index, len;
3484 len = strlen (text);
3487 while ((name = commands[index]) != NULL) {
3489 if (strncasecmp (name, text, len) == 0)
3490 return strdup (name);
3496 #endif /* HAVE_LIBREADLINE */
3498 char **do_completion (const char *text, int start, int end)
3500 char **matches = NULL;
3502 #ifdef HAVE_LIBREADLINE
3504 matches = rl_completion_matches (text, generator);
3511 (* Generate the POD documentation for guestfish. *)
3512 and generate_fish_actions_pod () =
3513 let all_functions_sorted =
3515 fun (_, _, _, flags, _, _, _) -> not (List.mem NotInFish flags)
3516 ) all_functions_sorted in
3519 fun (name, style, _, flags, _, _, longdesc) ->
3520 let longdesc = replace_str longdesc "C<guestfs_" "C<" in
3521 let name = replace_char name '_' '-' in
3523 try find_map (function FishAlias n -> Some n | _ -> None) flags
3524 with Not_found -> name in
3526 pr "=head2 %s" name;
3527 if name <> alias then
3534 | String n -> pr " %s" n
3535 | OptString n -> pr " %s" n
3536 | StringList n -> pr " %s,..." n
3537 | Bool _ -> pr " true|false"
3538 | Int n -> pr " %s" n
3539 | FileIn n | FileOut n -> pr " (%s|-)" n
3543 pr "%s\n\n" longdesc;
3545 if List.exists (function FileIn _ | FileOut _ -> true
3546 | _ -> false) (snd style) then
3547 pr "Use C<-> instead of a filename to read/write from stdin/stdout.\n\n";
3549 if List.mem ProtocolLimitWarning flags then
3550 pr "%s\n\n" protocol_limit_warning;
3552 if List.mem DangerWillRobinson flags then
3553 pr "%s\n\n" danger_will_robinson
3554 ) all_functions_sorted
3556 (* Generate a C function prototype. *)
3557 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
3558 ?(single_line = false) ?(newline = false) ?(in_daemon = false)
3560 ?handle name style =
3561 if extern then pr "extern ";
3562 if static then pr "static ";
3563 (match fst style with
3565 | RInt _ -> pr "int "
3566 | RInt64 _ -> pr "int64_t "
3567 | RBool _ -> pr "int "
3568 | RConstString _ -> pr "const char *"
3569 | RString _ -> pr "char *"
3570 | RStringList _ | RHashtable _ -> pr "char **"
3572 if not in_daemon then pr "struct guestfs_int_bool *"
3573 else pr "guestfs_%s_ret *" name
3575 if not in_daemon then pr "struct guestfs_lvm_pv_list *"
3576 else pr "guestfs_lvm_int_pv_list *"
3578 if not in_daemon then pr "struct guestfs_lvm_vg_list *"
3579 else pr "guestfs_lvm_int_vg_list *"
3581 if not in_daemon then pr "struct guestfs_lvm_lv_list *"
3582 else pr "guestfs_lvm_int_lv_list *"
3584 if not in_daemon then pr "struct guestfs_stat *"
3585 else pr "guestfs_int_stat *"
3587 if not in_daemon then pr "struct guestfs_statvfs *"
3588 else pr "guestfs_int_statvfs *"
3590 pr "%s%s (" prefix name;
3591 if handle = None && List.length (snd style) = 0 then
3594 let comma = ref false in
3597 | Some handle -> pr "guestfs_h *%s" handle; comma := true
3601 if single_line then pr ", " else pr ",\n\t\t"
3608 | OptString n -> next (); pr "const char *%s" n
3609 | StringList n -> next (); pr "char * const* const %s" n
3610 | Bool n -> next (); pr "int %s" n
3611 | Int n -> next (); pr "int %s" n