2 * Copyright (C) 2009-2010 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 (* Please read generator/README first. *)
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
32 (* Generate daemon/actions.h. *)
33 let generate_daemon_actions_h () =
34 generate_header CStyle GPLv2plus;
36 pr "#include \"guestfs_protocol.h\"\n";
41 | shortname, (_, _, (_::_ as optargs)), _, _, _, _, _ ->
44 let uc_shortname = String.uppercase shortname in
45 let n = name_of_argt arg in
46 let uc_n = String.uppercase n in
47 pr "#define GUESTFS_%s_%s_BITMASK (UINT64_C(1)<<%d)\n"
54 fun (name, (ret, args, optargs), _, _, _, _, _) ->
55 let style = ret, args @ optargs, [] in
57 ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
61 (* Generate the server-side stubs. *)
62 and generate_daemon_actions () =
63 generate_header CStyle GPLv2plus;
65 pr "#include <config.h>\n";
67 pr "#include <stdio.h>\n";
68 pr "#include <stdlib.h>\n";
69 pr "#include <string.h>\n";
70 pr "#include <inttypes.h>\n";
71 pr "#include <rpc/types.h>\n";
72 pr "#include <rpc/xdr.h>\n";
74 pr "#include \"daemon.h\"\n";
75 pr "#include \"c-ctype.h\"\n";
76 pr "#include \"guestfs_protocol.h\"\n";
77 pr "#include \"actions.h\"\n";
81 fun (name, (ret, args, optargs), _, _, _, _, _) ->
82 (* Generate server-side stubs. *)
83 pr "static void %s_stub (XDR *xdr_in)\n" name;
86 | RErr | RInt _ -> pr " int r;\n"
87 | RInt64 _ -> pr " int64_t r;\n"
88 | RBool _ -> pr " int r;\n"
89 | RConstString _ | RConstOptString _ ->
90 failwithf "RConstString|RConstOptString cannot be used by daemon functions"
91 | RString _ -> pr " char *r;\n"
92 | RStringList _ | RHashtable _ -> pr " char **r;\n"
93 | RStruct (_, typ) -> pr " guestfs_int_%s *r;\n" typ
94 | RStructList (_, typ) -> pr " guestfs_int_%s_list *r;\n" typ
96 pr " size_t size = 1;\n";
100 if args <> [] || optargs <> [] then (
101 pr " struct guestfs_%s_args args;\n" name;
104 | Device n | Dev_or_Path n
108 | OptString n -> pr " char *%s;\n" n
109 | StringList n | DeviceList n -> pr " char **%s;\n" n
110 | Bool n -> pr " int %s;\n" n
111 | Int n -> pr " int %s;\n" n
112 | Int64 n -> pr " int64_t %s;\n" n
113 | FileIn _ | FileOut _ -> ()
115 pr " const char *%s;\n" n;
116 pr " size_t %s_size;\n" n
117 | Pointer _ -> assert false
123 List.exists (function FileIn _ -> true | _ -> false) args in
125 (* Reject unknown optional arguments.
126 * Note this code is included even for calls with no optional
127 * args because the caller must not pass optargs_bitmask != 0
130 if optargs <> [] then (
131 let len = List.length optargs in
132 let mask = Int64.lognot (Int64.pred (Int64.shift_left 1L len)) in
133 pr " if (optargs_bitmask & UINT64_C(0x%Lx)) {\n" mask;
135 pr " cancel_receive ();\n";
136 pr " reply_with_error (\"unknown option in optional arguments bitmask (this can happen if a program is compiled against a newer version of libguestfs, then run against an older version of the daemon)\");\n";
140 pr " if (optargs_bitmask != 0) {\n";
142 pr " cancel_receive ();\n";
143 pr " reply_with_error (\"header optargs_bitmask field must be passed as 0 for calls that don't take optional arguments\");\n";
149 (* Decode arguments. *)
150 if args <> [] || optargs <> [] then (
151 pr " memset (&args, 0, sizeof args);\n";
153 pr " if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
155 pr " cancel_receive ();\n";
156 pr " reply_with_error (\"daemon failed to decode procedure arguments\");\n";
160 pr " char *%s = args.%s;\n" n n
162 let pr_list_handling_code n =
163 pr " %s = realloc (args.%s.%s_val,\n" n n n;
164 pr " sizeof (char *) * (args.%s.%s_len+1));\n" n n;
165 pr " if (%s == NULL) {\n" n;
167 pr " cancel_receive ();\n";
168 pr " reply_with_perror (\"realloc\");\n";
171 pr " %s[args.%s.%s_len] = NULL;\n" n n n;
172 pr " args.%s.%s_val = %s;\n" n n n;
178 pr " ABS_PATH (%s, %s, goto done);\n"
179 n (if is_filein then "cancel_receive ()" else "");
182 pr " RESOLVE_DEVICE (%s, %s, goto done);\n"
183 n (if is_filein then "cancel_receive ()" else "");
186 pr " REQUIRE_ROOT_OR_RESOLVE_DEVICE (%s, %s, goto done);\n"
187 n (if is_filein then "cancel_receive ()" else "");
188 | String n | Key n -> pr_args n
189 | OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n
191 pr_list_handling_code n;
193 pr_list_handling_code n;
194 pr " /* Ensure that each is a device,\n";
195 pr " * and perform device name translation.\n";
199 pr " for (i = 0; %s[i] != NULL; ++i)\n" n;
200 pr " RESOLVE_DEVICE (%s[i], %s, goto done);\n" n
201 (if is_filein then "cancel_receive ()" else "");
203 | Bool n -> pr " %s = args.%s;\n" n n
204 | Int n -> pr " %s = args.%s;\n" n n
205 | Int64 n -> pr " %s = args.%s;\n" n n
206 | FileIn _ | FileOut _ -> ()
208 pr " %s = args.%s.%s_val;\n" n n n;
209 pr " %s_size = args.%s.%s_len;\n" n n n
210 | Pointer _ -> assert false
215 (* this is used at least for do_equal *)
216 if List.exists (function Pathname _ -> true | _ -> false) args then (
217 (* Emit NEED_ROOT just once, even when there are two or
218 more Pathname args *)
219 pr " NEED_ROOT (%s, goto done);\n"
220 (if is_filein then "cancel_receive ()" else "");
223 (* Don't want to call the impl with any FileIn or FileOut
224 * parameters, since these go "outside" the RPC protocol.
229 (function FileIn _ | FileOut _ -> false | _ -> true) args in
230 let style = ret, args' @ optargs, [] in
231 pr " r = do_%s " name;
232 generate_c_call_args style;
236 | RConstOptString _ -> assert false
237 | RErr | RInt _ | RInt64 _ | RBool _
239 | RString _ | RStringList _ | RHashtable _
240 | RStruct (_, _) | RStructList (_, _) ->
242 match errcode_of_ret ret with
243 | `CannotReturnError -> assert false
244 | (`ErrorIsMinusOne | `ErrorIsNULL) as e -> e in
245 pr " if (r == %s)\n" (string_of_errcode errcode);
246 pr " /* do_%s has already called reply_with_error */\n" name;
250 pr " /* size == 0 && r == NULL could be a non-error case (just\n";
251 pr " * an ordinary zero-length buffer), so be careful ...\n";
253 pr " if (size == 1 && r == NULL)\n";
254 pr " /* do_%s has already called reply_with_error */\n" name;
259 (* If there are any FileOut parameters, then the impl must
260 * send its own reply.
263 List.exists (function FileOut _ -> true | _ -> false) args in
265 pr " /* do_%s has already sent a reply */\n" name
268 | RErr -> pr " reply (NULL, NULL);\n"
269 | RInt n | RInt64 n | RBool n ->
270 pr " struct guestfs_%s_ret ret;\n" name;
271 pr " ret.%s = r;\n" n;
272 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
274 | RConstString _ | RConstOptString _ ->
275 failwithf "RConstString|RConstOptString cannot be used by daemon functions"
277 pr " struct guestfs_%s_ret ret;\n" name;
278 pr " ret.%s = r;\n" n;
279 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
282 | RStringList n | RHashtable n ->
283 pr " struct guestfs_%s_ret ret;\n" name;
284 pr " ret.%s.%s_len = count_strings (r);\n" n n;
285 pr " ret.%s.%s_val = r;\n" n n;
286 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
288 pr " free_strings (r);\n"
290 pr " struct guestfs_%s_ret ret;\n" name;
291 pr " ret.%s = *r;\n" n;
293 pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
295 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
297 | RStructList (n, _) ->
298 pr " struct guestfs_%s_ret ret;\n" name;
299 pr " ret.%s = *r;\n" n;
301 pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
303 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
306 pr " struct guestfs_%s_ret ret;\n" name;
307 pr " ret.%s.%s_val = r;\n" n n;
308 pr " ret.%s.%s_len = size;\n" n n;
309 pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
319 pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
326 (* Dispatch function. *)
327 pr "void dispatch_incoming_message (XDR *xdr_in)\n";
329 pr " switch (proc_nr) {\n";
332 fun (name, _, _, _, _, _, _) ->
333 pr " case GUESTFS_PROC_%s:\n" (String.uppercase name);
334 pr " %s_stub (xdr_in);\n" name;
339 pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory\", proc_nr);\n";
344 (* LVM columns and tokenization functions. *)
345 (* XXX This generates crap code. We should rethink how we
351 pr "static const char *lvm_%s_cols = \"%s\";\n"
352 typ (String.concat "," (List.map fst cols));
355 pr "static int lvm_tokenize_%s (char *str, guestfs_int_lvm_%s *r)\n" typ typ;
357 pr " char *tok, *p, *next;\n";
358 pr " size_t i, j;\n";
361 pr " fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
365 pr " fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
368 pr " if (!*str || c_isspace (*str)) {\n";
369 pr " fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
374 fun (name, coltype) ->
376 pr " fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
379 pr " p = strchrnul (tok, ',');\n";
380 pr " if (*p) next = p+1; else next = NULL;\n";
384 pr " r->%s = strdup (tok);\n" name;
385 pr " if (r->%s == NULL) {\n" name;
386 pr " perror (\"strdup\");\n";
390 pr " for (i = j = 0; i < 32; ++j) {\n";
391 pr " if (tok[j] == '\\0') {\n";
392 pr " fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
394 pr " } else if (tok[j] != '-')\n";
395 pr " r->%s[i++] = tok[j];\n" name;
398 pr " if (sscanf (tok, \"%%\"SCNu64, &r->%s) != 1) {\n" name;
399 pr " fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
403 pr " if (sscanf (tok, \"%%\"SCNi64, &r->%s) != 1) {\n" name;
404 pr " fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
408 pr " if (tok[0] == '\\0')\n";
409 pr " r->%s = -1;\n" name;
410 pr " else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
411 pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
414 | FBuffer | FInt32 | FUInt32 | FUInt64 | FChar ->
415 assert false (* can never be an LVM column *)
420 pr " if (tok != NULL) {\n";
421 pr " fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
428 pr "guestfs_int_lvm_%s_list *\n" typ;
429 pr "parse_command_line_%ss (void)\n" typ;
431 pr " char *out, *err;\n";
432 pr " char *p, *pend;\n";
434 pr " guestfs_int_lvm_%s_list *ret;\n" typ;
437 pr " ret = malloc (sizeof *ret);\n";
439 pr " reply_with_perror (\"malloc\");\n";
440 pr " return NULL;\n";
443 pr " ret->guestfs_int_lvm_%s_list_len = 0;\n" typ;
444 pr " ret->guestfs_int_lvm_%s_list_val = NULL;\n" typ;
446 pr " r = command (&out, &err,\n";
447 pr " \"lvm\", \"%ss\",\n" typ;
448 pr " \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
449 pr " \"--nosuffix\", \"--separator\", \",\", \"--units\", \"b\", NULL);\n";
450 pr " if (r == -1) {\n";
451 pr " reply_with_error (\"%%s\", err);\n";
455 pr " return NULL;\n";
460 pr " /* Tokenize each line of the output. */\n";
464 pr " pend = strchr (p, '\\n'); /* Get the next line of output. */\n";
466 pr " *pend = '\\0';\n";
470 pr " while (*p && c_isspace (*p)) /* Skip any leading whitespace. */\n";
473 pr " if (!*p) { /* Empty line? Skip it. */\n";
478 pr " /* Allocate some space to store this next entry. */\n";
479 pr " newp = realloc (ret->guestfs_int_lvm_%s_list_val,\n" typ;
480 pr " sizeof (guestfs_int_lvm_%s) * (i+1));\n" typ;
481 pr " if (newp == NULL) {\n";
482 pr " reply_with_perror (\"realloc\");\n";
483 pr " free (ret->guestfs_int_lvm_%s_list_val);\n" typ;
486 pr " return NULL;\n";
488 pr " ret->guestfs_int_lvm_%s_list_val = newp;\n" typ;
490 pr " /* Tokenize the next entry. */\n";
491 pr " r = lvm_tokenize_%s (p, &ret->guestfs_int_lvm_%s_list_val[i]);\n" typ typ;
492 pr " if (r == -1) {\n";
493 pr " reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
494 pr " free (ret->guestfs_int_lvm_%s_list_val);\n" typ;
497 pr " return NULL;\n";
504 pr " ret->guestfs_int_lvm_%s_list_len = i;\n" typ;
510 ) ["pv", lvm_pv_cols; "vg", lvm_vg_cols; "lv", lvm_lv_cols]
512 (* Generate a list of function names, for debugging in the daemon.. *)
513 and generate_daemon_names () =
514 generate_header CStyle GPLv2plus;
516 pr "#include <config.h>\n";
518 pr "#include \"daemon.h\"\n";
521 pr "/* This array is indexed by proc_nr. See guestfs_protocol.x. */\n";
522 pr "const char *function_names[] = {\n";
524 fun (name, _, proc_nr, _, _, _, _) -> pr " [%d] = \"%s\",\n" proc_nr name
528 (* Generate the optional groups for the daemon to implement
531 and generate_daemon_optgroups_c () =
532 generate_header CStyle GPLv2plus;
534 pr "#include <config.h>\n";
536 pr "#include \"daemon.h\"\n";
537 pr "#include \"optgroups.h\"\n";
540 pr "struct optgroup optgroups[] = {\n";
543 pr " { \"%s\", optgroup_%s_available },\n" group group
545 pr " { NULL, NULL }\n";
548 and generate_daemon_optgroups_h () =
549 generate_header CStyle GPLv2plus;
553 pr "extern int optgroup_%s_available (void);\n" group