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
19 * This script generates a large amount of code and documentation for
20 * all the daemon actions. To add a new action there are only two
21 * files you need to change, this one to describe the interface, and
22 * daemon/<somefile>.c to write the implementation.
29 type style = ret * args
31 (* "Err" as a return value means an int used as a simple error
32 * indication, ie. 0 or -1.
35 (* "RString" and "RStringList" require special treatment because
36 * the caller must free them.
39 | RStringList of string
40 (* LVM PVs, VGs and LVs. *)
45 (* 0 arguments, 1 argument, etc. The guestfs_h param is implicit. *)
50 | String of string (* const char *name, cannot be NULL *)
52 type flags = ProtocolLimitWarning
55 ("mount", (Err, P2 (String "device", String "mountpoint")), 1, [],
56 "mount a guest disk at a position in the filesystem",
58 Mount a guest disk at a position in the filesystem. Block devices
59 are named C</dev/sda>, C</dev/sdb> and so on, as they were added to
60 the guest. If those block devices contain partitions, they will have
61 the usual names (eg. C</dev/sda1>). Also LVM C</dev/VG/LV>-style
64 The rules are the same as for L<mount(2)>: A filesystem must
65 first be mounted on C</> before others can be mounted. Other
66 filesystems can only be mounted on directories which already
69 The mounted filesystem is writable, if we have sufficient permissions
70 on the underlying device.
72 The filesystem options C<sync> and C<noatime> are set with this
73 call, in order to improve reliability.");
75 ("sync", (Err, P0), 2, [],
76 "sync disks, writes are flushed through to the disk image",
78 This syncs the disk, so that any writes are flushed through to the
79 underlying disk image.
81 You should always call this if you have modified a disk image, before
82 calling C<guestfs_close>.");
84 ("touch", (Err, P1 (String "path")), 3, [],
85 "update file timestamps or create a new file",
87 Touch acts like the L<touch(1)> command. It can be used to
88 update the timestamps on a file, or, if the file does not exist,
89 to create a new zero-length file.");
91 ("cat", (RString "content", P1 (String "path")), 4, [ProtocolLimitWarning],
92 "list the contents of a file",
94 Return the contents of the file named C<path>.
96 Note that this function cannot correctly handle binary files
97 (specifically, files containing C<\\0> character which is treated
98 as end of string). For those you need to use the C<guestfs_read_file>
99 function which has a more complex interface.");
101 ("ll", (RString "listing", P1 (String "directory")), 5, [],
102 "list the files in a directory (long format)",
104 List the files in C<directory> (relative to the root directory,
105 there is no cwd) in the format of 'ls -la'.
107 This command is mostly useful for interactive sessions. It
108 is I<not> intended that you try to parse the output string.");
110 ("ls", (RStringList "listing", P1 (String "directory")), 6, [],
111 "list the files in a directory",
113 List the files in C<directory> (relative to the root directory,
114 there is no cwd). The '.' and '..' entries are not returned, but
115 hidden files are shown.
117 This command is mostly useful for interactive sessions. Programs
118 should probably use C<guestfs_readdir> instead.");
120 ("list_devices", (RStringList "devices", P0), 7, [],
121 "list the block devices",
123 List all the block devices.
125 The full block device names are returned, eg. C</dev/sda>
128 ("list_partitions", (RStringList "partitions", P0), 8, [],
129 "list the partitions",
131 List all the partitions detected on all block devices.
133 The full partition device names are returned, eg. C</dev/sda1>
135 This does not return logical volumes. For that you will need to
136 call C<guestfs_lvs>.");
139 ("pvs", (RPVList "physvols", P0), 9, [],
140 "list the LVM physical volumes (PVs)",
142 List all the physical volumes detected. This is the equivalent
143 of the L<pvs(8)> command.");
145 ("vgs", (RVGList "volgroups", P0), 10, [],
146 "list the LVM volume groups (VGs)",
148 List all the volumes groups detected. This is the equivalent
149 of the L<vgs(8)> command.");
151 ("lvs", (RLVList "logvols", P0), 11, [],
152 "list the LVM logical volumes (LVs)",
154 List all the logical volumes detected. This is the equivalent
155 of the L<lvs(8)> command.");
158 ("pvs_full", (RPVList "physvols", P0), 12, [],
159 "list the LVM physical volumes (PVs)",
161 List all the physical volumes detected. This is the equivalent
162 of the L<pvs(8)> command.");
164 ("vgs_full", (RVGList "volgroups", P0), 13, [],
165 "list the LVM volume groups (VGs)",
167 List all the volumes groups detected. This is the equivalent
168 of the L<vgs(8)> command.");
170 ("lvs_full", (RLVList "logvols", P0), 14, [],
171 "list the LVM logical volumes (LVs)",
173 List all the logical volumes detected. This is the equivalent
174 of the L<lvs(8)> command.");
177 (* Column names and types from LVM PVs/VGs/LVs. *)
186 "pv_attr", `String (* XXX *);
188 "pv_pe_alloc_count", `Int;
191 "pv_mda_count", `Int;
192 "pv_mda_free", `Bytes;
194 "pv_mda_size", `Bytes;
201 "vg_attr", `String (* XXX *);
205 "vg_extent_size", `Bytes;
206 "vg_extent_count", `Int;
207 "vg_free_count", `Int;
215 "vg_mda_count", `Int;
216 "vg_mda_free", `Bytes;
218 "vg_mda_size", `Bytes;
224 "lv_attr", `String (* XXX *);
227 "lv_kernel_major", `Int;
228 "lv_kernel_minor", `Int;
232 "snap_percent", `OptPercent;
233 "copy_percent", `OptPercent;
236 "mirror_log", `String;
240 (* In some places we want the functions to be displayed sorted
241 * alphabetically, so this is useful:
243 let sorted_functions =
244 List.sort (fun (n1,_,_,_,_,_) (n2,_,_,_,_,_) -> compare n1 n2) functions
246 (* Useful functions. *)
247 let failwithf fs = ksprintf failwith fs
248 let replace s c1 c2 =
249 let s2 = String.copy s in
251 for i = 0 to String.length s2 - 1 do
252 if String.unsafe_get s2 i = c1 then (
253 String.unsafe_set s2 i c2;
257 if not !r then s else s2
259 (* 'pr' prints to the current output file. *)
260 let chan = ref stdout
261 let pr fs = ksprintf (output_string !chan) fs
263 let iter_args f = function
266 | P2 (arg1, arg2) -> f arg1; f arg2
268 let iteri_args f = function
270 | P1 arg1 -> f 0 arg1
271 | P2 (arg1, arg2) -> f 0 arg1; f 1 arg2
273 let map_args f = function
275 | P1 arg1 -> [f arg1]
276 | P2 (arg1, arg2) -> [f arg1; f arg2]
278 let nr_args = function | P0 -> 0 | P1 _ -> 1 | P2 _ -> 2
280 (* Check function names etc. for consistency. *)
281 let check_functions () =
283 fun (name, _, _, _, _, _) ->
284 if String.contains name '-' then
285 failwithf "Function name '%s' should not contain '-', use '_' instead."
290 List.map (fun (name, _, proc_nr, _, _, _) -> name, proc_nr) functions in
292 List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
293 let rec loop = function
296 | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
298 | (name1,nr1) :: (name2,nr2) :: _ ->
299 failwithf "'%s' and '%s' have conflicting procedure numbers (%d, %d)"
304 type comment_style = CStyle | HashStyle | OCamlStyle
305 type license = GPLv2 | LGPLv2
307 (* Generate a header block in a number of standard styles. *)
308 let rec generate_header comment license =
309 let c = match comment with
310 | CStyle -> pr "/* "; " *"
311 | HashStyle -> pr "# "; "#"
312 | OCamlStyle -> pr "(* "; " *" in
313 pr "libguestfs generated file\n";
314 pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
315 pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
317 pr "%s Copyright (C) 2009 Red Hat Inc.\n" c;
321 pr "%s This program is free software; you can redistribute it and/or modify\n" c;
322 pr "%s it under the terms of the GNU General Public License as published by\n" c;
323 pr "%s the Free Software Foundation; either version 2 of the License, or\n" c;
324 pr "%s (at your option) any later version.\n" c;
326 pr "%s This program is distributed in the hope that it will be useful,\n" c;
327 pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
328 pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" c;
329 pr "%s GNU General Public License for more details.\n" c;
331 pr "%s You should have received a copy of the GNU General Public License along\n" c;
332 pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c;
333 pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c;
336 pr "%s This library is free software; you can redistribute it and/or\n" c;
337 pr "%s modify it under the terms of the GNU Lesser General Public\n" c;
338 pr "%s License as published by the Free Software Foundation; either\n" c;
339 pr "%s version 2 of the License, or (at your option) any later version.\n" c;
341 pr "%s This library is distributed in the hope that it will be useful,\n" c;
342 pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
343 pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" c;
344 pr "%s Lesser General Public License for more details.\n" c;
346 pr "%s You should have received a copy of the GNU Lesser General Public\n" c;
347 pr "%s License along with this library; if not, write to the Free Software\n" c;
348 pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c;
351 | CStyle -> pr " */\n"
353 | OCamlStyle -> pr " *)\n"
357 (* Generate the pod documentation for the C API. *)
358 and generate_actions_pod () =
360 fun (shortname, style, _, flags, _, longdesc) ->
361 let name = "guestfs_" ^ shortname in
362 pr "=head2 %s\n\n" name;
364 generate_prototype ~extern:false ~handle:"handle" name style;
366 pr "%s\n\n" longdesc;
367 (match fst style with
369 pr "This function returns 0 on success or -1 on error.\n\n"
371 pr "This function returns a string or NULL on error.
372 I<The caller must free the returned string after use>.\n\n"
374 pr "This function returns a NULL-terminated array of strings
375 (like L<environ(3)>), or NULL if there was an error.
376 I<The caller must free the strings and the array after use>.\n\n"
378 pr "This function returns a C<struct guestfs_lvm_pv_list>.
379 I<The caller must call C<guestfs_free_lvm_pv_list> after use.>.\n\n"
381 pr "This function returns a C<struct guestfs_lvm_vg_list>.
382 I<The caller must call C<guestfs_free_lvm_vg_list> after use.>.\n\n"
384 pr "This function returns a C<struct guestfs_lvm_lv_list>.
385 I<The caller must call C<guestfs_free_lvm_lv_list> after use.>.\n\n"
387 if List.mem ProtocolLimitWarning flags then
388 pr "Because of the message protocol, there is a transfer limit
389 of somewhere between 2MB and 4MB. To transfer large files you should use
393 and generate_structs_pod () =
394 (* LVM structs documentation. *)
397 pr "=head2 guestfs_lvm_%s\n" typ;
399 pr " struct guestfs_lvm_%s {\n" typ;
402 | name, `String -> pr " char *%s;\n" name
404 pr " /* The next field is NOT nul-terminated, be careful when printing it: */\n";
405 pr " char %s[32];\n" name
406 | name, `Bytes -> pr " uint64_t %s;\n" name
407 | name, `Int -> pr " int64_t %s;\n" name
408 | name, `OptPercent ->
409 pr " /* The next field is [0..100] or -1 meaning 'not present': */\n";
410 pr " float %s;\n" name
413 pr " struct guestfs_lvm_%s_list {\n" typ;
414 pr " uint32_t len; /* Number of elements in list. */\n";
415 pr " struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
418 pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
421 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
423 (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
424 * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'. We
425 * have to use an underscore instead of a dash because otherwise
426 * rpcgen generates incorrect code.
428 * This header is NOT exported to clients, but see also generate_structs_h.
430 and generate_xdr () =
431 generate_header CStyle LGPLv2;
433 (* This has to be defined to get around a limitation in Sun's rpcgen. *)
434 pr "typedef string str<>;\n";
437 (* LVM internal structures. *)
441 pr "struct guestfs_lvm_int_%s {\n" typ;
443 | name, `String -> pr " string %s<>;\n" name
444 | name, `UUID -> pr " opaque %s[32];\n" name
445 | name, `Bytes -> pr " hyper %s;\n" name
446 | name, `Int -> pr " hyper %s;\n" name
447 | name, `OptPercent -> pr " float %s;\n" name
451 pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
453 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
456 fun (shortname, style, _, _, _, _) ->
457 let name = "guestfs_" ^ shortname in
458 pr "/* %s */\n\n" name;
459 (match snd style with
462 pr "struct %s_args {\n" name;
465 | String name -> pr " string %s<>;\n" name
469 (match fst style with
472 pr "struct %s_ret {\n" name;
473 pr " string %s<>;\n" n;
476 pr "struct %s_ret {\n" name;
480 pr "struct %s_ret {\n" name;
481 pr " guestfs_lvm_int_pv_list %s;\n" n;
484 pr "struct %s_ret {\n" name;
485 pr " guestfs_lvm_int_vg_list %s;\n" n;
488 pr "struct %s_ret {\n" name;
489 pr " guestfs_lvm_int_lv_list %s;\n" n;
494 (* Table of procedure numbers. *)
495 pr "enum guestfs_procedure {\n";
497 fun (shortname, _, proc_nr, _, _, _) ->
498 pr " GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
500 pr " GUESTFS_PROC_dummy\n"; (* so we don't have a "hanging comma" *)
504 (* Having to choose a maximum message size is annoying for several
505 * reasons (it limits what we can do in the API), but it (a) makes
506 * the protocol a lot simpler, and (b) provides a bound on the size
507 * of the daemon which operates in limited memory space. For large
508 * file transfers you should use FTP.
510 pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
513 (* Message header, etc. *)
515 const GUESTFS_PROGRAM = 0x2000F5F5;
516 const GUESTFS_PROTOCOL_VERSION = 1;
518 enum guestfs_message_direction {
519 GUESTFS_DIRECTION_CALL = 0, /* client -> daemon */
520 GUESTFS_DIRECTION_REPLY = 1 /* daemon -> client */
523 enum guestfs_message_status {
524 GUESTFS_STATUS_OK = 0,
525 GUESTFS_STATUS_ERROR = 1
528 const GUESTFS_ERROR_LEN = 256;
530 struct guestfs_message_error {
531 string error<GUESTFS_ERROR_LEN>; /* error message */
534 struct guestfs_message_header {
535 unsigned prog; /* GUESTFS_PROGRAM */
536 unsigned vers; /* GUESTFS_PROTOCOL_VERSION */
537 guestfs_procedure proc; /* GUESTFS_PROC_x */
538 guestfs_message_direction direction;
539 unsigned serial; /* message serial number */
540 guestfs_message_status status;
544 (* Generate the guestfs-structs.h file. *)
545 and generate_structs_h () =
546 generate_header CStyle LGPLv2;
548 (* This is a public exported header file containing various
549 * structures. The structures are carefully written to have
550 * exactly the same in-memory format as the XDR structures that
551 * we use on the wire to the daemon. The reason for creating
552 * copies of these structures here is just so we don't have to
553 * export the whole of guestfs_protocol.h (which includes much
554 * unrelated and XDR-dependent stuff that we don't want to be
555 * public, or required by clients).
557 * To reiterate, we will pass these structures to and from the
558 * client with a simple assignment or memcpy, so the format
559 * must be identical to what rpcgen / the RFC defines.
562 (* LVM public structures. *)
566 pr "struct guestfs_lvm_%s {\n" typ;
569 | name, `String -> pr " char *%s;\n" name
570 | name, `UUID -> pr " char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
571 | name, `Bytes -> pr " uint64_t %s;\n" name
572 | name, `Int -> pr " int64_t %s;\n" name
573 | name, `OptPercent -> pr " float %s; /* [0..100] or -1 */\n" name
577 pr "struct guestfs_lvm_%s_list {\n" typ;
578 pr " uint32_t len;\n";
579 pr " struct guestfs_lvm_%s *val;\n" typ;
582 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
584 (* Generate the guestfs-actions.h file. *)
585 and generate_actions_h () =
586 generate_header CStyle LGPLv2;
588 fun (shortname, style, _, _, _, _) ->
589 let name = "guestfs_" ^ shortname in
590 generate_prototype ~single_line:true ~newline:true ~handle:"handle"
594 (* Generate the client-side dispatch stubs. *)
595 and generate_client_actions () =
596 generate_header CStyle LGPLv2;
598 (* Client-side stubs for each function. *)
600 fun (shortname, style, _, _, _, _) ->
601 let name = "guestfs_" ^ shortname in
603 (* Generate the return value struct. *)
604 pr "struct %s_rv {\n" shortname;
605 pr " int cb_done; /* flag to indicate callback was called */\n";
606 pr " struct guestfs_message_header hdr;\n";
607 pr " struct guestfs_message_error err;\n";
608 (match fst style with
610 | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
611 pr " struct %s_ret ret;\n" name
615 (* Generate the callback function. *)
616 pr "static void %s_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
618 pr " struct %s_rv *rv = (struct %s_rv *) data;\n" shortname shortname;
620 pr " if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {\n";
621 pr " error (g, \"%s: failed to parse reply header\");\n" name;
624 pr " if (rv->hdr.status == GUESTFS_STATUS_ERROR) {\n";
625 pr " if (!xdr_guestfs_message_error (xdr, &rv->err)) {\n";
626 pr " error (g, \"%s: failed to parse reply error\");\n" name;
632 (match fst style with
634 | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
635 pr " if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name;
636 pr " error (g, \"%s: failed to parse reply\");\n" name;
642 pr " rv->cb_done = 1;\n";
643 pr " main_loop.main_loop_quit (g);\n";
646 (* Generate the action stub. *)
647 generate_prototype ~extern:false ~semicolon:false ~newline:true
648 ~handle:"g" name style;
653 | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
658 (match snd style with
660 | _ -> pr " struct %s_args args;\n" name
663 pr " struct %s_rv rv;\n" shortname;
666 pr " if (g->state != READY) {\n";
667 pr " error (g, \"%s called from the wrong state, %%d != READY\",\n"
670 pr " return %s;\n" error_code;
673 pr " memset (&rv, 0, sizeof rv);\n";
676 (match snd style with
678 pr " serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
679 (String.uppercase shortname)
683 | String name -> pr " args.%s = (char *) %s;\n" name name
685 pr " serial = dispatch (g, GUESTFS_PROC_%s,\n"
686 (String.uppercase shortname);
687 pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n"
690 pr " if (serial == -1)\n";
691 pr " return %s;\n" error_code;
694 pr " rv.cb_done = 0;\n";
695 pr " g->reply_cb_internal = %s_cb;\n" shortname;
696 pr " g->reply_cb_internal_data = &rv;\n";
697 pr " main_loop.main_loop_run (g);\n";
698 pr " g->reply_cb_internal = NULL;\n";
699 pr " g->reply_cb_internal_data = NULL;\n";
700 pr " if (!rv.cb_done) {\n";
701 pr " error (g, \"%s failed, see earlier error messages\");\n" name;
702 pr " return %s;\n" error_code;
706 pr " if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_%s, serial) == -1)\n"
707 (String.uppercase shortname);
708 pr " return %s;\n" error_code;
711 pr " if (rv.hdr.status == GUESTFS_STATUS_ERROR) {\n";
712 pr " error (g, \"%%s\", rv.err.error);\n";
713 pr " return %s;\n" error_code;
717 (match fst style with
718 | Err -> pr " return 0;\n"
720 pr " return rv.ret.%s; /* caller will free */\n" n
722 pr " /* caller will free this, but we need to add a NULL entry */\n";
723 pr " rv.ret.%s.%s_val = safe_realloc (g, rv.ret.%s.%s_val, rv.ret.%s.%s_len + 1);\n" n n n n n n;
724 pr " rv.ret.%s.%s_val[rv.ret.%s.%s_len] = NULL;\n" n n n n;
725 pr " return rv.ret.%s.%s_val;\n" n n
727 pr " /* caller will free this */\n";
728 pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
730 pr " /* caller will free this */\n";
731 pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
733 pr " /* caller will free this */\n";
734 pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
740 (* Generate daemon/actions.h. *)
741 and generate_daemon_actions_h () =
742 generate_header CStyle GPLv2;
744 pr "#include \"../src/guestfs_protocol.h\"\n";
748 fun (name, style, _, _, _, _) ->
750 ~single_line:true ~newline:true ~in_daemon:true ("do_" ^ name) style;
753 (* Generate the server-side stubs. *)
754 and generate_daemon_actions () =
755 generate_header CStyle GPLv2;
757 pr "#define _GNU_SOURCE // for strchrnul\n";
759 pr "#include <stdio.h>\n";
760 pr "#include <stdlib.h>\n";
761 pr "#include <string.h>\n";
762 pr "#include <inttypes.h>\n";
763 pr "#include <ctype.h>\n";
764 pr "#include <rpc/types.h>\n";
765 pr "#include <rpc/xdr.h>\n";
767 pr "#include \"daemon.h\"\n";
768 pr "#include \"../src/guestfs_protocol.h\"\n";
769 pr "#include \"actions.h\"\n";
773 fun (name, style, _, _, _, _) ->
774 (* Generate server-side stubs. *)
775 pr "static void %s_stub (XDR *xdr_in)\n" name;
779 | Err -> pr " int r;\n"; "-1"
780 | RString _ -> pr " char *r;\n"; "NULL"
781 | RStringList _ -> pr " char **r;\n"; "NULL"
782 | RPVList _ -> pr " guestfs_lvm_int_pv_list *r;\n"; "NULL"
783 | RVGList _ -> pr " guestfs_lvm_int_vg_list *r;\n"; "NULL"
784 | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL" in
786 (match snd style with
789 pr " struct guestfs_%s_args args;\n" name;
792 | String name -> pr " const char *%s;\n" name
797 (match snd style with
800 pr " memset (&args, 0, sizeof args);\n";
802 pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
803 pr " reply_with_error (\"%s: daemon failed to decode procedure arguments\");\n" name;
808 | String name -> pr " %s = args.%s;\n" name name
813 pr " r = do_%s " name;
814 generate_call_args style;
817 pr " if (r == %s)\n" error_code;
818 pr " /* do_%s has already called reply_with_error, so just return */\n" name;
822 (match fst style with
823 | Err -> pr " reply (NULL, NULL);\n"
825 pr " struct guestfs_%s_ret ret;\n" name;
826 pr " ret.%s = r;\n" n;
827 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
830 pr " struct guestfs_%s_ret ret;\n" name;
831 pr " ret.%s.%s_len = count_strings (r);\n" n n;
832 pr " ret.%s.%s_val = r;\n" n n;
833 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
834 pr " free_strings (r);\n"
836 pr " struct guestfs_%s_ret ret;\n" name;
837 pr " ret.%s = *r;\n" n;
838 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
839 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
841 pr " struct guestfs_%s_ret ret;\n" name;
842 pr " ret.%s = *r;\n" n;
843 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
844 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
846 pr " struct guestfs_%s_ret ret;\n" name;
847 pr " ret.%s = *r;\n" n;
848 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
849 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
855 (* Dispatch function. *)
856 pr "void dispatch_incoming_message (XDR *xdr_in)\n";
858 pr " switch (proc_nr) {\n";
861 fun (name, style, _, _, _, _) ->
862 pr " case GUESTFS_PROC_%s:\n" (String.uppercase name);
863 pr " %s_stub (xdr_in);\n" name;
868 pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
873 (* LVM columns and tokenization functions. *)
874 (* XXX This generates crap code. We should rethink how we
880 pr "static const char *lvm_%s_cols = \"%s\";\n"
881 typ (String.concat "," (List.map fst cols));
884 pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
886 pr " char *tok, *p, *next;\n";
890 pr " fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
894 pr " fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
897 pr " if (!*str || isspace (*str)) {\n";
898 pr " fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
903 fun (name, coltype) ->
905 pr " fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
908 pr " p = strchrnul (tok, ',');\n";
909 pr " if (*p) next = p+1; else next = NULL;\n";
913 pr " r->%s = strdup (tok);\n" name;
914 pr " if (r->%s == NULL) {\n" name;
915 pr " perror (\"strdup\");\n";
919 pr " for (i = j = 0; i < 32; ++j) {\n";
920 pr " if (tok[j] == '\\0') {\n";
921 pr " fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
923 pr " } else if (tok[j] != '-')\n";
924 pr " r->%s[i++] = tok[j];\n" name;
927 pr " if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
928 pr " fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
932 pr " if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
933 pr " fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
937 pr " if (tok[0] == '\\0')\n";
938 pr " r->%s = -1;\n" name;
939 pr " else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
940 pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
947 pr " if (tok != NULL) {\n";
948 pr " fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
955 pr "guestfs_lvm_int_%s_list *\n" typ;
956 pr "parse_command_line_%ss (void)\n" typ;
958 pr " char *out, *err;\n";
959 pr " char *p, *pend;\n";
961 pr " guestfs_lvm_int_%s_list *ret;\n" typ;
964 pr " ret = malloc (sizeof *ret);\n";
966 pr " reply_with_perror (\"malloc\");\n";
967 pr " return NULL;\n";
970 pr " ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
971 pr " ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
973 pr " r = command (&out, &err,\n";
974 pr " \"/sbin/lvm\", \"%ss\",\n" typ;
975 pr " \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
976 pr " \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
977 pr " if (r == -1) {\n";
978 pr " reply_with_error (\"%%s\", err);\n";
981 pr " return NULL;\n";
986 pr " /* Tokenize each line of the output. */\n";
990 pr " pend = strchr (p, '\\n'); /* Get the next line of output. */\n";
992 pr " *pend = '\\0';\n";
996 pr " while (*p && isspace (*p)) /* Skip any leading whitespace. */\n";
999 pr " if (!*p) { /* Empty line? Skip it. */\n";
1004 pr " /* Allocate some space to store this next entry. */\n";
1005 pr " newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
1006 pr " sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
1007 pr " if (newp == NULL) {\n";
1008 pr " reply_with_perror (\"realloc\");\n";
1009 pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
1010 pr " free (ret);\n";
1011 pr " free (out);\n";
1012 pr " return NULL;\n";
1014 pr " ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
1016 pr " /* Tokenize the next entry. */\n";
1017 pr " r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
1018 pr " if (r == -1) {\n";
1019 pr " reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
1020 pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
1021 pr " free (ret);\n";
1022 pr " free (out);\n";
1023 pr " return NULL;\n";
1030 pr " ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
1032 pr " free (out);\n";
1033 pr " return ret;\n";
1036 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
1038 (* Generate a lot of different functions for guestfish. *)
1039 and generate_fish_cmds () =
1040 generate_header CStyle GPLv2;
1042 pr "#include <stdio.h>\n";
1043 pr "#include <stdlib.h>\n";
1044 pr "#include <string.h>\n";
1045 pr "#include <inttypes.h>\n";
1047 pr "#include <guestfs.h>\n";
1048 pr "#include \"fish.h\"\n";
1051 (* list_commands function, which implements guestfish -h *)
1052 pr "void list_commands (void)\n";
1054 pr " printf (\" %%-16s %%s\\n\", \"Command\", \"Description\");\n";
1055 pr " list_builtin_commands ();\n";
1057 fun (name, _, _, _, shortdesc, _) ->
1058 let name = replace name '_' '-' in
1059 pr " printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
1062 pr " printf (\" Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
1066 (* display_command function, which implements guestfish -h cmd *)
1067 pr "void display_command (const char *cmd)\n";
1070 fun (name, style, _, flags, shortdesc, longdesc) ->
1071 let name2 = replace name '_' '-' in
1073 match snd style with
1078 String.concat "> <" (
1080 | String n -> n) args
1085 if List.mem ProtocolLimitWarning flags then
1086 "\n\nBecause of the message protocol, there is a transfer limit
1087 of somewhere between 2MB and 4MB. To transfer large files you should use
1092 pr "strcasecmp (cmd, \"%s\") == 0" name;
1093 if name <> name2 then
1094 pr " || strcasecmp (cmd, \"%s\") == 0" name2;
1096 pr " pod2text (\"%s - %s\", %S);\n"
1098 (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings);
1101 pr " display_builtin_command (cmd);\n";
1105 (* print_{pv,vg,lv}_list functions *)
1109 pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
1116 pr " printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
1118 pr " printf (\"%s: \");\n" name;
1119 pr " for (i = 0; i < 32; ++i)\n";
1120 pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
1121 pr " printf (\"\\n\");\n"
1123 pr " printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
1125 pr " printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
1126 | name, `OptPercent ->
1127 pr " if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
1128 typ name name typ name;
1129 pr " else printf (\"%s: \\n\");\n" name
1133 pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
1138 pr " for (i = 0; i < %ss->len; ++i)\n" typ;
1139 pr " print_%s (&%ss->val[i]);\n" typ typ;
1142 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
1144 (* run_<action> actions *)
1146 fun (name, style, _, _, _, _) ->
1147 pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
1149 (match fst style with
1150 | Err -> pr " int r;\n"
1151 | RString _ -> pr " char *r;\n"
1152 | RStringList _ -> pr " char **r;\n"
1153 | RPVList _ -> pr " struct guestfs_lvm_pv_list *r;\n"
1154 | RVGList _ -> pr " struct guestfs_lvm_vg_list *r;\n"
1155 | RLVList _ -> pr " struct guestfs_lvm_lv_list *r;\n"
1159 | String name -> pr " const char *%s;\n" name
1162 (* Check and convert parameters. *)
1163 let argc_expected = nr_args (snd style) in
1164 pr " if (argc != %d) {\n" argc_expected;
1165 pr " fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
1167 pr " fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
1173 | String name -> pr " %s = argv[%d];\n" name i
1176 (* Call C API function. *)
1177 pr " r = guestfs_%s " name;
1178 generate_call_args ~handle:"g" style;
1181 (* Check return value for errors and display command results. *)
1182 (match fst style with
1183 | Err -> pr " return r;\n"
1185 pr " if (r == NULL) return -1;\n";
1186 pr " printf (\"%%s\", r);\n";
1190 pr " if (r == NULL) return -1;\n";
1191 pr " print_strings (r);\n";
1192 pr " free_strings (r);\n";
1195 pr " if (r == NULL) return -1;\n";
1196 pr " print_pv_list (r);\n";
1197 pr " guestfs_free_lvm_pv_list (r);\n";
1200 pr " if (r == NULL) return -1;\n";
1201 pr " print_vg_list (r);\n";
1202 pr " guestfs_free_lvm_vg_list (r);\n";
1205 pr " if (r == NULL) return -1;\n";
1206 pr " print_lv_list (r);\n";
1207 pr " guestfs_free_lvm_lv_list (r);\n";
1214 (* run_action function *)
1215 pr "int run_action (const char *cmd, int argc, char *argv[])\n";
1218 fun (name, _, _, _, _, _) ->
1219 let name2 = replace name '_' '-' in
1221 pr "strcasecmp (cmd, \"%s\") == 0" name;
1222 if name <> name2 then
1223 pr " || strcasecmp (cmd, \"%s\") == 0" name2;
1225 pr " return run_%s (cmd, argc, argv);\n" name;
1229 pr " fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
1236 (* Generate a C function prototype. *)
1237 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
1238 ?(single_line = false) ?(newline = false) ?(in_daemon = false)
1239 ?handle name style =
1240 if extern then pr "extern ";
1241 if static then pr "static ";
1242 (match fst style with
1244 | RString _ -> pr "char *"
1245 | RStringList _ -> pr "char **"
1247 if not in_daemon then pr "struct guestfs_lvm_pv_list *"
1248 else pr "guestfs_lvm_int_pv_list *"
1250 if not in_daemon then pr "struct guestfs_lvm_vg_list *"
1251 else pr "guestfs_lvm_int_vg_list *"
1253 if not in_daemon then pr "struct guestfs_lvm_lv_list *"
1254 else pr "guestfs_lvm_int_lv_list *"
1257 let comma = ref false in
1260 | Some handle -> pr "guestfs_h *%s" handle; comma := true
1264 if single_line then pr ", " else pr ",\n\t\t"
1270 | String name -> next (); pr "const char *%s" name
1273 if semicolon then pr ";";
1274 if newline then pr "\n"
1276 (* Generate C call arguments, eg "(handle, foo, bar)" *)
1277 and generate_call_args ?handle style =
1279 let comma = ref false in
1282 | Some handle -> pr "%s" handle; comma := true
1286 if !comma then pr ", ";
1289 | String name -> pr "%s" name
1293 let output_to filename =
1294 let filename_new = filename ^ ".new" in
1295 chan := open_out filename_new;
1299 Unix.rename filename_new filename;
1300 printf "written %s\n%!" filename;
1308 let close = output_to "src/guestfs_protocol.x" in
1312 let close = output_to "src/guestfs-structs.h" in
1313 generate_structs_h ();
1316 let close = output_to "src/guestfs-actions.h" in
1317 generate_actions_h ();
1320 let close = output_to "src/guestfs-actions.c" in
1321 generate_client_actions ();
1324 let close = output_to "daemon/actions.h" in
1325 generate_daemon_actions_h ();
1328 let close = output_to "daemon/stubs.c" in
1329 generate_daemon_actions ();
1332 let close = output_to "fish/cmds.c" in
1333 generate_fish_cmds ();
1336 let close = output_to "guestfs-structs.pod" in
1337 generate_structs_pod ();
1340 let close = output_to "guestfs-actions.pod" in
1341 generate_actions_pod ();