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>.");
138 ("pvs", (RPVList "physvols", P0), 9, [],
139 "list the LVM physical volumes (PVs)",
141 List all the physical volumes detected. This is the equivalent
142 of the L<pvs(8)> command.");
144 ("vgs", (RVGList "volgroups", P0), 10, [],
145 "list the LVM volume groups (VGs)",
147 List all the volumes groups detected. This is the equivalent
148 of the L<vgs(8)> command.");
150 ("lvs", (RLVList "logvols", P0), 11, [],
151 "list the LVM logical volumes (LVs)",
153 List all the logical volumes detected. This is the equivalent
154 of the L<lvs(8)> command.");
157 (* Column names and types from LVM PVs/VGs/LVs. *)
166 "pv_attr", `String (* XXX *);
168 "pv_pe_alloc_count", `Int;
171 "pv_mda_count", `Int;
172 "pv_mda_free", `Bytes;
174 "pv_mda_size", `Bytes;
181 "vg_attr", `String (* XXX *);
185 "vg_extent_size", `Bytes;
186 "vg_extent_count", `Int;
187 "vg_free_count", `Int;
195 "vg_mda_count", `Int;
196 "vg_mda_free", `Bytes;
198 "vg_mda_size", `Bytes;
204 "lv_attr", `String (* XXX *);
207 "lv_kernel_major", `Int;
208 "lv_kernel_minor", `Int;
212 "snap_percent", `OptPercent;
213 "copy_percent", `OptPercent;
216 "mirror_log", `String;
220 (* In some places we want the functions to be displayed sorted
221 * alphabetically, so this is useful:
223 let sorted_functions =
224 List.sort (fun (n1,_,_,_,_,_) (n2,_,_,_,_,_) -> compare n1 n2) functions
226 (* Useful functions. *)
227 let failwithf fs = ksprintf failwith fs
228 let replace s c1 c2 =
229 let s2 = String.copy s in
231 for i = 0 to String.length s2 - 1 do
232 if String.unsafe_get s2 i = c1 then (
233 String.unsafe_set s2 i c2;
237 if not !r then s else s2
239 (* 'pr' prints to the current output file. *)
240 let chan = ref stdout
241 let pr fs = ksprintf (output_string !chan) fs
243 let iter_args f = function
246 | P2 (arg1, arg2) -> f arg1; f arg2
248 let iteri_args f = function
250 | P1 arg1 -> f 0 arg1
251 | P2 (arg1, arg2) -> f 0 arg1; f 1 arg2
253 let map_args f = function
255 | P1 arg1 -> [f arg1]
256 | P2 (arg1, arg2) -> [f arg1; f arg2]
258 let nr_args = function | P0 -> 0 | P1 _ -> 1 | P2 _ -> 2
260 (* Check function names etc. for consistency. *)
261 let check_functions () =
263 fun (name, _, _, _, _, _) ->
264 if String.contains name '-' then
265 failwithf "Function name '%s' should not contain '-', use '_' instead."
270 List.map (fun (name, _, proc_nr, _, _, _) -> name, proc_nr) functions in
272 List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
273 let rec loop = function
276 | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
278 | (name1,nr1) :: (name2,nr2) :: _ ->
279 failwithf "'%s' and '%s' have conflicting procedure numbers (%d, %d)"
284 type comment_style = CStyle | HashStyle | OCamlStyle
285 type license = GPLv2 | LGPLv2
287 (* Generate a header block in a number of standard styles. *)
288 let rec generate_header comment license =
289 let c = match comment with
290 | CStyle -> pr "/* "; " *"
291 | HashStyle -> pr "# "; "#"
292 | OCamlStyle -> pr "(* "; " *" in
293 pr "libguestfs generated file\n";
294 pr "%s WARNING: THIS FILE IS GENERATED BY 'src/generator.ml'.\n" c;
295 pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
297 pr "%s Copyright (C) 2009 Red Hat Inc.\n" c;
301 pr "%s This program is free software; you can redistribute it and/or modify\n" c;
302 pr "%s it under the terms of the GNU General Public License as published by\n" c;
303 pr "%s the Free Software Foundation; either version 2 of the License, or\n" c;
304 pr "%s (at your option) any later version.\n" c;
306 pr "%s This program is distributed in the hope that it will be useful,\n" c;
307 pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
308 pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" c;
309 pr "%s GNU General Public License for more details.\n" c;
311 pr "%s You should have received a copy of the GNU General Public License along\n" c;
312 pr "%s with this program; if not, write to the Free Software Foundation, Inc.,\n" c;
313 pr "%s 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" c;
316 pr "%s This library is free software; you can redistribute it and/or\n" c;
317 pr "%s modify it under the terms of the GNU Lesser General Public\n" c;
318 pr "%s License as published by the Free Software Foundation; either\n" c;
319 pr "%s version 2 of the License, or (at your option) any later version.\n" c;
321 pr "%s This library is distributed in the hope that it will be useful,\n" c;
322 pr "%s but WITHOUT ANY WARRANTY; without even the implied warranty of\n" c;
323 pr "%s MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" c;
324 pr "%s Lesser General Public License for more details.\n" c;
326 pr "%s You should have received a copy of the GNU Lesser General Public\n" c;
327 pr "%s License along with this library; if not, write to the Free Software\n" c;
328 pr "%s Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" c;
331 | CStyle -> pr " */\n"
333 | OCamlStyle -> pr " *)\n"
337 (* Generate the pod documentation for the C API. *)
338 and generate_actions_pod () =
340 fun (shortname, style, _, flags, _, longdesc) ->
341 let name = "guestfs_" ^ shortname in
342 pr "=head2 %s\n\n" name;
344 generate_prototype ~extern:false ~handle:"handle" name style;
346 pr "%s\n\n" longdesc;
347 (match fst style with
349 pr "This function returns 0 on success or -1 on error.\n\n"
351 pr "This function returns a string or NULL on error.
352 I<The caller must free the returned string after use>.\n\n"
354 pr "This function returns a NULL-terminated array of strings
355 (like L<environ(3)>), or NULL if there was an error.
356 I<The caller must free the strings and the array after use>.\n\n"
358 pr "This function returns a C<struct guestfs_lvm_pv_list>.
359 I<The caller must call C<guestfs_free_lvm_pv_list> after use.>.\n\n"
361 pr "This function returns a C<struct guestfs_lvm_vg_list>.
362 I<The caller must call C<guestfs_free_lvm_vg_list> after use.>.\n\n"
364 pr "This function returns a C<struct guestfs_lvm_lv_list>.
365 I<The caller must call C<guestfs_free_lvm_lv_list> after use.>.\n\n"
367 if List.mem ProtocolLimitWarning flags then
368 pr "Because of the message protocol, there is a transfer limit
369 of somewhere between 2MB and 4MB. To transfer large files you should use
373 and generate_structs_pod () =
374 (* LVM structs documentation. *)
377 pr "=head2 guestfs_lvm_%s\n" typ;
379 pr " struct guestfs_lvm_%s {\n" typ;
382 | name, `String -> pr " char *%s;\n" name
384 pr " /* The next field is NOT nul-terminated, be careful when printing it: */\n";
385 pr " char %s[32];\n" name
386 | name, `Bytes -> pr " uint64_t %s;\n" name
387 | name, `Int -> pr " int64_t %s;\n" name
388 | name, `OptPercent ->
389 pr " /* The next field is [0..100] or -1 meaning 'not present': */\n";
390 pr " float %s;\n" name
393 pr " struct guestfs_lvm_%s_list {\n" typ;
394 pr " uint32_t len; /* Number of elements in list. */\n";
395 pr " struct guestfs_lvm_%s *val; /* Elements. */\n" typ;
398 pr " void guestfs_free_lvm_%s_list (struct guestfs_free_lvm_%s_list *);\n"
401 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
403 (* Generate the protocol (XDR) file, 'guestfs_protocol.x' and
404 * indirectly 'guestfs_protocol.h' and 'guestfs_protocol.c'. We
405 * have to use an underscore instead of a dash because otherwise
406 * rpcgen generates incorrect code.
408 * This header is NOT exported to clients, but see also generate_structs_h.
410 and generate_xdr () =
411 generate_header CStyle LGPLv2;
413 (* This has to be defined to get around a limitation in Sun's rpcgen. *)
414 pr "typedef string str<>;\n";
417 (* LVM internal structures. *)
421 pr "struct guestfs_lvm_int_%s {\n" typ;
423 | name, `String -> pr " string %s<>;\n" name
424 | name, `UUID -> pr " opaque %s[32];\n" name
425 | name, `Bytes -> pr " hyper %s;\n" name
426 | name, `Int -> pr " hyper %s;\n" name
427 | name, `OptPercent -> pr " float %s;\n" name
431 pr "typedef struct guestfs_lvm_int_%s guestfs_lvm_int_%s_list<>;\n" typ typ;
433 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
436 fun (shortname, style, _, _, _, _) ->
437 let name = "guestfs_" ^ shortname in
438 pr "/* %s */\n\n" name;
439 (match snd style with
442 pr "struct %s_args {\n" name;
445 | String name -> pr " string %s<>;\n" name
449 (match fst style with
452 pr "struct %s_ret {\n" name;
453 pr " string %s<>;\n" n;
456 pr "struct %s_ret {\n" name;
460 pr "struct %s_ret {\n" name;
461 pr " guestfs_lvm_int_pv_list %s;\n" n;
464 pr "struct %s_ret {\n" name;
465 pr " guestfs_lvm_int_vg_list %s;\n" n;
468 pr "struct %s_ret {\n" name;
469 pr " guestfs_lvm_int_lv_list %s;\n" n;
474 (* Table of procedure numbers. *)
475 pr "enum guestfs_procedure {\n";
477 fun (shortname, _, proc_nr, _, _, _) ->
478 pr " GUESTFS_PROC_%s = %d,\n" (String.uppercase shortname) proc_nr
480 pr " GUESTFS_PROC_dummy\n"; (* so we don't have a "hanging comma" *)
484 (* Having to choose a maximum message size is annoying for several
485 * reasons (it limits what we can do in the API), but it (a) makes
486 * the protocol a lot simpler, and (b) provides a bound on the size
487 * of the daemon which operates in limited memory space. For large
488 * file transfers you should use FTP.
490 pr "const GUESTFS_MESSAGE_MAX = %d;\n" (4 * 1024 * 1024);
493 (* Message header, etc. *)
495 const GUESTFS_PROGRAM = 0x2000F5F5;
496 const GUESTFS_PROTOCOL_VERSION = 1;
498 enum guestfs_message_direction {
499 GUESTFS_DIRECTION_CALL = 0, /* client -> daemon */
500 GUESTFS_DIRECTION_REPLY = 1 /* daemon -> client */
503 enum guestfs_message_status {
504 GUESTFS_STATUS_OK = 0,
505 GUESTFS_STATUS_ERROR = 1
508 const GUESTFS_ERROR_LEN = 256;
510 struct guestfs_message_error {
511 string error<GUESTFS_ERROR_LEN>; /* error message */
514 struct guestfs_message_header {
515 unsigned prog; /* GUESTFS_PROGRAM */
516 unsigned vers; /* GUESTFS_PROTOCOL_VERSION */
517 guestfs_procedure proc; /* GUESTFS_PROC_x */
518 guestfs_message_direction direction;
519 unsigned serial; /* message serial number */
520 guestfs_message_status status;
524 (* Generate the guestfs-structs.h file. *)
525 and generate_structs_h () =
526 generate_header CStyle LGPLv2;
528 (* This is a public exported header file containing various
529 * structures. The structures are carefully written to have
530 * exactly the same in-memory format as the XDR structures that
531 * we use on the wire to the daemon. The reason for creating
532 * copies of these structures here is just so we don't have to
533 * export the whole of guestfs_protocol.h (which includes much
534 * unrelated and XDR-dependent stuff that we don't want to be
535 * public, or required by clients).
537 * To reiterate, we will pass these structures to and from the
538 * client with a simple assignment or memcpy, so the format
539 * must be identical to what rpcgen / the RFC defines.
542 (* LVM public structures. *)
546 pr "struct guestfs_lvm_%s {\n" typ;
549 | name, `String -> pr " char *%s;\n" name
550 | name, `UUID -> pr " char %s[32]; /* this is NOT nul-terminated, be careful when printing */\n" name
551 | name, `Bytes -> pr " uint64_t %s;\n" name
552 | name, `Int -> pr " int64_t %s;\n" name
553 | name, `OptPercent -> pr " float %s; /* [0..100] or -1 */\n" name
557 pr "struct guestfs_lvm_%s_list {\n" typ;
558 pr " uint32_t len;\n";
559 pr " struct guestfs_lvm_%s *val;\n" typ;
562 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
564 (* Generate the guestfs-actions.h file. *)
565 and generate_actions_h () =
566 generate_header CStyle LGPLv2;
568 fun (shortname, style, _, _, _, _) ->
569 let name = "guestfs_" ^ shortname in
570 generate_prototype ~single_line:true ~newline:true ~handle:"handle"
574 (* Generate the client-side dispatch stubs. *)
575 and generate_client_actions () =
576 generate_header CStyle LGPLv2;
578 (* Client-side stubs for each function. *)
580 fun (shortname, style, _, _, _, _) ->
581 let name = "guestfs_" ^ shortname in
583 (* Generate the return value struct. *)
584 pr "struct %s_rv {\n" shortname;
585 pr " int cb_done; /* flag to indicate callback was called */\n";
586 pr " struct guestfs_message_header hdr;\n";
587 pr " struct guestfs_message_error err;\n";
588 (match fst style with
590 | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
591 pr " struct %s_ret ret;\n" name
595 (* Generate the callback function. *)
596 pr "static void %s_cb (guestfs_h *g, void *data, XDR *xdr)\n" shortname;
598 pr " struct %s_rv *rv = (struct %s_rv *) data;\n" shortname shortname;
600 pr " if (!xdr_guestfs_message_header (xdr, &rv->hdr)) {\n";
601 pr " error (g, \"%s: failed to parse reply header\");\n" name;
604 pr " if (rv->hdr.status == GUESTFS_STATUS_ERROR) {\n";
605 pr " if (!xdr_guestfs_message_error (xdr, &rv->err)) {\n";
606 pr " error (g, \"%s: failed to parse reply error\");\n" name;
612 (match fst style with
614 | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
615 pr " if (!xdr_%s_ret (xdr, &rv->ret)) {\n" name;
616 pr " error (g, \"%s: failed to parse reply\");\n" name;
622 pr " rv->cb_done = 1;\n";
623 pr " main_loop.main_loop_quit (g);\n";
626 (* Generate the action stub. *)
627 generate_prototype ~extern:false ~semicolon:false ~newline:true
628 ~handle:"g" name style;
633 | RString _ | RStringList _ | RPVList _ | RVGList _ | RLVList _ ->
638 (match snd style with
640 | _ -> pr " struct %s_args args;\n" name
643 pr " struct %s_rv rv;\n" shortname;
646 pr " if (g->state != READY) {\n";
647 pr " error (g, \"%s called from the wrong state, %%d != READY\",\n"
650 pr " return %s;\n" error_code;
653 pr " memset (&rv, 0, sizeof rv);\n";
656 (match snd style with
658 pr " serial = dispatch (g, GUESTFS_PROC_%s, NULL, NULL);\n"
659 (String.uppercase shortname)
663 | String name -> pr " args.%s = (char *) %s;\n" name name
665 pr " serial = dispatch (g, GUESTFS_PROC_%s,\n"
666 (String.uppercase shortname);
667 pr " (xdrproc_t) xdr_%s_args, (char *) &args);\n"
670 pr " if (serial == -1)\n";
671 pr " return %s;\n" error_code;
674 pr " rv.cb_done = 0;\n";
675 pr " g->reply_cb_internal = %s_cb;\n" shortname;
676 pr " g->reply_cb_internal_data = &rv;\n";
677 pr " main_loop.main_loop_run (g);\n";
678 pr " g->reply_cb_internal = NULL;\n";
679 pr " g->reply_cb_internal_data = NULL;\n";
680 pr " if (!rv.cb_done) {\n";
681 pr " error (g, \"%s failed, see earlier error messages\");\n" name;
682 pr " return %s;\n" error_code;
686 pr " if (check_reply_header (g, &rv.hdr, GUESTFS_PROC_%s, serial) == -1)\n"
687 (String.uppercase shortname);
688 pr " return %s;\n" error_code;
691 pr " if (rv.hdr.status == GUESTFS_STATUS_ERROR) {\n";
692 pr " error (g, \"%%s\", rv.err.error);\n";
693 pr " return %s;\n" error_code;
697 (match fst style with
698 | Err -> pr " return 0;\n"
700 pr " return rv.ret.%s; /* caller will free */\n" n
702 pr " /* caller will free this, but we need to add a NULL entry */\n";
703 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;
704 pr " rv.ret.%s.%s_val[rv.ret.%s.%s_len] = NULL;\n" n n n n;
705 pr " return rv.ret.%s.%s_val;\n" n n
707 pr " /* caller will free this */\n";
708 pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
710 pr " /* caller will free this */\n";
711 pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
713 pr " /* caller will free this */\n";
714 pr " return safe_memdup (g, &rv.ret.%s, sizeof (rv.ret.%s));\n" n n
720 (* Generate daemon/actions.h. *)
721 and generate_daemon_actions_h () =
722 generate_header CStyle GPLv2;
724 pr "#include \"../src/guestfs_protocol.h\"\n";
728 fun (name, style, _, _, _, _) ->
730 ~single_line:true ~newline:true ~in_daemon:true ("do_" ^ name) style;
733 (* Generate the server-side stubs. *)
734 and generate_daemon_actions () =
735 generate_header CStyle GPLv2;
737 pr "#define _GNU_SOURCE // for strchrnul\n";
739 pr "#include <stdio.h>\n";
740 pr "#include <stdlib.h>\n";
741 pr "#include <string.h>\n";
742 pr "#include <inttypes.h>\n";
743 pr "#include <ctype.h>\n";
744 pr "#include <rpc/types.h>\n";
745 pr "#include <rpc/xdr.h>\n";
747 pr "#include \"daemon.h\"\n";
748 pr "#include \"../src/guestfs_protocol.h\"\n";
749 pr "#include \"actions.h\"\n";
753 fun (name, style, _, _, _, _) ->
754 (* Generate server-side stubs. *)
755 pr "static void %s_stub (XDR *xdr_in)\n" name;
759 | Err -> pr " int r;\n"; "-1"
760 | RString _ -> pr " char *r;\n"; "NULL"
761 | RStringList _ -> pr " char **r;\n"; "NULL"
762 | RPVList _ -> pr " guestfs_lvm_int_pv_list *r;\n"; "NULL"
763 | RVGList _ -> pr " guestfs_lvm_int_vg_list *r;\n"; "NULL"
764 | RLVList _ -> pr " guestfs_lvm_int_lv_list *r;\n"; "NULL" in
766 (match snd style with
769 pr " struct guestfs_%s_args args;\n" name;
772 | String name -> pr " const char *%s;\n" name
777 (match snd style with
780 pr " memset (&args, 0, sizeof args);\n";
782 pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
783 pr " reply_with_error (\"%s: daemon failed to decode procedure arguments\");\n" name;
788 | String name -> pr " %s = args.%s;\n" name name
793 pr " r = do_%s " name;
794 generate_call_args style;
797 pr " if (r == %s)\n" error_code;
798 pr " /* do_%s has already called reply_with_error, so just return */\n" name;
802 (match fst style with
803 | Err -> pr " reply (NULL, NULL);\n"
805 pr " struct guestfs_%s_ret ret;\n" name;
806 pr " ret.%s = r;\n" n;
807 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
810 pr " struct guestfs_%s_ret ret;\n" name;
811 pr " ret.%s.%s_len = count_strings (r);\n" n n;
812 pr " ret.%s.%s_val = r;\n" n n;
813 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
814 pr " free_strings (r);\n"
816 pr " struct guestfs_%s_ret ret;\n" name;
817 pr " ret.%s = *r;\n" n;
818 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
819 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
821 pr " struct guestfs_%s_ret ret;\n" name;
822 pr " ret.%s = *r;\n" n;
823 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
824 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
826 pr " struct guestfs_%s_ret ret;\n" name;
827 pr " ret.%s = *r;\n" n;
828 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name;
829 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n" name
835 (* Dispatch function. *)
836 pr "void dispatch_incoming_message (XDR *xdr_in)\n";
838 pr " switch (proc_nr) {\n";
841 fun (name, style, _, _, _, _) ->
842 pr " case GUESTFS_PROC_%s:\n" (String.uppercase name);
843 pr " %s_stub (xdr_in);\n" name;
848 pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n";
853 (* LVM columns and tokenization functions. *)
854 (* XXX This generates crap code. We should rethink how we
860 pr "static const char *lvm_%s_cols = \"%s\";\n"
861 typ (String.concat "," (List.map fst cols));
864 pr "static int lvm_tokenize_%s (char *str, struct guestfs_lvm_int_%s *r)\n" typ typ;
866 pr " char *tok, *p, *next;\n";
870 pr " fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
874 pr " fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
877 pr " if (!*str || isspace (*str)) {\n";
878 pr " fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
883 fun (name, coltype) ->
885 pr " fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
888 pr " p = strchrnul (tok, ',');\n";
889 pr " if (*p) next = p+1; else next = NULL;\n";
893 pr " r->%s = strdup (tok);\n" name;
894 pr " if (r->%s == NULL) {\n" name;
895 pr " perror (\"strdup\");\n";
899 pr " for (i = j = 0; i < 32; ++j) {\n";
900 pr " if (tok[j] == '\\0') {\n";
901 pr " fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
903 pr " } else if (tok[j] != '-')\n";
904 pr " r->%s[i++] = tok[j];\n" name;
907 pr " if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
908 pr " fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
912 pr " if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
913 pr " fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
917 pr " if (tok[0] == '\\0')\n";
918 pr " r->%s = -1;\n" name;
919 pr " else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
920 pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
927 pr " if (tok != NULL) {\n";
928 pr " fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
935 pr "guestfs_lvm_int_%s_list *\n" typ;
936 pr "parse_command_line_%ss (void)\n" typ;
938 pr " char *out, *err;\n";
939 pr " char *p, *pend;\n";
941 pr " guestfs_lvm_int_%s_list *ret;\n" typ;
944 pr " ret = malloc (sizeof *ret);\n";
946 pr " reply_with_perror (\"malloc\");\n";
947 pr " return NULL;\n";
950 pr " ret->guestfs_lvm_int_%s_list_len = 0;\n" typ;
951 pr " ret->guestfs_lvm_int_%s_list_val = NULL;\n" typ;
953 pr " r = command (&out, &err,\n";
954 pr " \"/sbin/lvm\", \"%ss\",\n" typ;
955 pr " \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
956 pr " \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
957 pr " if (r == -1) {\n";
958 pr " reply_with_error (\"%%s\", err);\n";
961 pr " return NULL;\n";
966 pr " /* Tokenize each line of the output. */\n";
970 pr " pend = strchr (p, '\\n'); /* Get the next line of output. */\n";
972 pr " *pend = '\\0';\n";
976 pr " while (*p && isspace (*p)) /* Skip any leading whitespace. */\n";
979 pr " if (!*p) { /* Empty line? Skip it. */\n";
984 pr " /* Allocate some space to store this next entry. */\n";
985 pr " newp = realloc (ret->guestfs_lvm_int_%s_list_val,\n" typ;
986 pr " sizeof (guestfs_lvm_int_%s) * (i+1));\n" typ;
987 pr " if (newp == NULL) {\n";
988 pr " reply_with_perror (\"realloc\");\n";
989 pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
992 pr " return NULL;\n";
994 pr " ret->guestfs_lvm_int_%s_list_val = newp;\n" typ;
996 pr " /* Tokenize the next entry. */\n";
997 pr " r = lvm_tokenize_%s (p, &ret->guestfs_lvm_int_%s_list_val[i]);\n" typ typ;
998 pr " if (r == -1) {\n";
999 pr " reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
1000 pr " free (ret->guestfs_lvm_int_%s_list_val);\n" typ;
1001 pr " free (ret);\n";
1002 pr " free (out);\n";
1003 pr " return NULL;\n";
1010 pr " ret->guestfs_lvm_int_%s_list_len = i;\n" typ;
1012 pr " free (out);\n";
1013 pr " return ret;\n";
1016 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols]
1018 (* Generate a lot of different functions for guestfish. *)
1019 and generate_fish_cmds () =
1020 generate_header CStyle GPLv2;
1022 pr "#include <stdio.h>\n";
1023 pr "#include <stdlib.h>\n";
1024 pr "#include <string.h>\n";
1025 pr "#include <inttypes.h>\n";
1027 pr "#include <guestfs.h>\n";
1028 pr "#include \"fish.h\"\n";
1031 (* list_commands function, which implements guestfish -h *)
1032 pr "void list_commands (void)\n";
1034 pr " printf (\" %%-16s %%s\\n\", \"Command\", \"Description\");\n";
1035 pr " list_builtin_commands ();\n";
1037 fun (name, _, _, _, shortdesc, _) ->
1038 let name = replace name '_' '-' in
1039 pr " printf (\"%%-20s %%s\\n\", \"%s\", \"%s\");\n"
1042 pr " printf (\" Use -h <cmd> / help <cmd> to show detailed help for a command.\\n\");\n";
1046 (* display_command function, which implements guestfish -h cmd *)
1047 pr "void display_command (const char *cmd)\n";
1050 fun (name, style, _, flags, shortdesc, longdesc) ->
1051 let name2 = replace name '_' '-' in
1053 match snd style with
1058 String.concat "> <" (
1060 | String n -> n) args
1065 if List.mem ProtocolLimitWarning flags then
1066 "\n\nBecause of the message protocol, there is a transfer limit
1067 of somewhere between 2MB and 4MB. To transfer large files you should use
1072 pr "strcasecmp (cmd, \"%s\") == 0" name;
1073 if name <> name2 then
1074 pr " || strcasecmp (cmd, \"%s\") == 0" name2;
1076 pr " pod2text (\"%s - %s\", %S);\n"
1078 (" " ^ synopsis ^ "\n\n" ^ longdesc ^ warnings);
1081 pr " display_builtin_command (cmd);\n";
1085 (* print_{pv,vg,lv}_list functions *)
1089 pr "static void print_%s (struct guestfs_lvm_%s *%s)\n" typ typ typ;
1096 pr " printf (\"%s: %%s\\n\", %s->%s);\n" name typ name
1098 pr " printf (\"%s: \");\n" name;
1099 pr " for (i = 0; i < 32; ++i)\n";
1100 pr " printf (\"%%c\", %s->%s[i]);\n" typ name;
1101 pr " printf (\"\\n\");\n"
1103 pr " printf (\"%s: %%\" PRIu64 \"\\n\", %s->%s);\n" name typ name
1105 pr " printf (\"%s: %%\" PRIi64 \"\\n\", %s->%s);\n" name typ name
1106 | name, `OptPercent ->
1107 pr " if (%s->%s >= 0) printf (\"%s: %%g %%%%\\n\", %s->%s);\n"
1108 typ name name typ name;
1109 pr " else printf (\"%s: \\n\");\n" name
1113 pr "static void print_%s_list (struct guestfs_lvm_%s_list *%ss)\n"
1118 pr " for (i = 0; i < %ss->len; ++i)\n" typ;
1119 pr " print_%s (&%ss->val[i]);\n" typ typ;
1122 ) ["pv", pv_cols; "vg", vg_cols; "lv", lv_cols];
1124 (* run_<action> actions *)
1126 fun (name, style, _, _, _, _) ->
1127 pr "static int run_%s (const char *cmd, int argc, char *argv[])\n" name;
1129 (match fst style with
1130 | Err -> pr " int r;\n"
1131 | RString _ -> pr " char *r;\n"
1132 | RStringList _ -> pr " char **r;\n"
1133 | RPVList _ -> pr " struct guestfs_lvm_pv_list *r;\n"
1134 | RVGList _ -> pr " struct guestfs_lvm_vg_list *r;\n"
1135 | RLVList _ -> pr " struct guestfs_lvm_lv_list *r;\n"
1139 | String name -> pr " const char *%s;\n" name
1142 (* Check and convert parameters. *)
1143 let argc_expected = nr_args (snd style) in
1144 pr " if (argc != %d) {\n" argc_expected;
1145 pr " fprintf (stderr, \"%%s should have %d parameter(s)\\n\", cmd);\n"
1147 pr " fprintf (stderr, \"type 'help %%s' for help on %%s\\n\", cmd, cmd);\n";
1153 | String name -> pr " %s = argv[%d];\n" name i
1156 (* Call C API function. *)
1157 pr " r = guestfs_%s " name;
1158 generate_call_args ~handle:"g" style;
1161 (* Check return value for errors and display command results. *)
1162 (match fst style with
1163 | Err -> pr " return r;\n"
1165 pr " if (r == NULL) return -1;\n";
1166 pr " printf (\"%%s\", r);\n";
1170 pr " if (r == NULL) return -1;\n";
1171 pr " print_strings (r);\n";
1172 pr " free_strings (r);\n";
1175 pr " if (r == NULL) return -1;\n";
1176 pr " print_pv_list (r);\n";
1177 pr " guestfs_free_lvm_pv_list (r);\n";
1180 pr " if (r == NULL) return -1;\n";
1181 pr " print_vg_list (r);\n";
1182 pr " guestfs_free_lvm_vg_list (r);\n";
1185 pr " if (r == NULL) return -1;\n";
1186 pr " print_lv_list (r);\n";
1187 pr " guestfs_free_lvm_lv_list (r);\n";
1194 (* run_action function *)
1195 pr "int run_action (const char *cmd, int argc, char *argv[])\n";
1198 fun (name, _, _, _, _, _) ->
1199 let name2 = replace name '_' '-' in
1201 pr "strcasecmp (cmd, \"%s\") == 0" name;
1202 if name <> name2 then
1203 pr " || strcasecmp (cmd, \"%s\") == 0" name2;
1205 pr " return run_%s (cmd, argc, argv);\n" name;
1209 pr " fprintf (stderr, \"%%s: unknown command\\n\", cmd);\n";
1216 (* Generate a C function prototype. *)
1217 and generate_prototype ?(extern = true) ?(static = false) ?(semicolon = true)
1218 ?(single_line = false) ?(newline = false) ?(in_daemon = false)
1219 ?handle name style =
1220 if extern then pr "extern ";
1221 if static then pr "static ";
1222 (match fst style with
1224 | RString _ -> pr "char *"
1225 | RStringList _ -> pr "char **"
1227 if not in_daemon then pr "struct guestfs_lvm_pv_list *"
1228 else pr "guestfs_lvm_int_pv_list *"
1230 if not in_daemon then pr "struct guestfs_lvm_vg_list *"
1231 else pr "guestfs_lvm_int_vg_list *"
1233 if not in_daemon then pr "struct guestfs_lvm_lv_list *"
1234 else pr "guestfs_lvm_int_lv_list *"
1237 let comma = ref false in
1240 | Some handle -> pr "guestfs_h *%s" handle; comma := true
1244 if single_line then pr ", " else pr ",\n\t\t"
1250 | String name -> next (); pr "const char *%s" name
1253 if semicolon then pr ";";
1254 if newline then pr "\n"
1256 (* Generate C call arguments, eg "(handle, foo, bar)" *)
1257 and generate_call_args ?handle style =
1259 let comma = ref false in
1262 | Some handle -> pr "%s" handle; comma := true
1266 if !comma then pr ", ";
1269 | String name -> pr "%s" name
1273 let output_to filename =
1274 let filename_new = filename ^ ".new" in
1275 chan := open_out filename_new;
1279 Unix.rename filename_new filename;
1280 printf "written %s\n%!" filename;
1288 let close = output_to "src/guestfs_protocol.x" in
1292 let close = output_to "src/guestfs-structs.h" in
1293 generate_structs_h ();
1296 let close = output_to "src/guestfs-actions.h" in
1297 generate_actions_h ();
1300 let close = output_to "src/guestfs-actions.c" in
1301 generate_client_actions ();
1304 let close = output_to "daemon/actions.h" in
1305 generate_daemon_actions_h ();
1308 let close = output_to "daemon/stubs.c" in
1309 generate_daemon_actions ();
1312 let close = output_to "fish/cmds.c" in
1313 generate_fish_cmds ();
1316 let close = output_to "guestfs-structs.pod" in
1317 generate_structs_pod ();
1320 let close = output_to "guestfs-actions.pod" in
1321 generate_actions_pod ();