blkid: Use -c /dev/null because RHEL 5 blkid doesn't have -p option.
[libguestfs.git] / generator / generator_daemon.ml
1 (* libguestfs
2  * Copyright (C) 2009-2010 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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
17  *)
18
19 (* Please read generator/README first. *)
20
21 open Printf
22
23 open Generator_types
24 open Generator_utils
25 open Generator_pr
26 open Generator_docstrings
27 open Generator_optgroups
28 open Generator_actions
29 open Generator_structs
30 open Generator_c
31
32 (* Generate daemon/actions.h. *)
33 let generate_daemon_actions_h () =
34   generate_header CStyle GPLv2plus;
35
36   pr "#include \"guestfs_protocol.h\"\n";
37   pr "\n";
38
39   List.iter (
40     function
41     | shortname, (_, _, (_::_ as optargs)), _, _, _, _, _ ->
42         iteri (
43           fun i arg ->
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"
48               uc_shortname uc_n i
49         ) optargs
50     | _ -> ()
51   ) daemon_functions;
52
53   List.iter (
54     fun (name, (ret, args, optargs), _, _, _, _, _) ->
55       let style = ret, args @ optargs, [] in
56       generate_prototype
57         ~single_line:true ~newline:true ~in_daemon:true ~prefix:"do_"
58         name style;
59   ) daemon_functions
60
61 (* Generate the server-side stubs. *)
62 and generate_daemon_actions () =
63   generate_header CStyle GPLv2plus;
64
65   pr "#include <config.h>\n";
66   pr "\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";
73   pr "\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";
78   pr "\n";
79
80   List.iter (
81     fun (name, (ret, args, optargs), _, _, _, _, _) ->
82       (* Generate server-side stubs. *)
83       pr "static void %s_stub (XDR *xdr_in)\n" name;
84       pr "{\n";
85       (match ret with
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
95        | RBufferOut _ ->
96            pr "  size_t size = 1;\n";
97            pr "  char *r;\n"
98       );
99
100       if args <> [] || optargs <> [] then (
101         pr "  struct guestfs_%s_args args;\n" name;
102         List.iter (
103           function
104           | Device n | Dev_or_Path n
105           | Pathname n
106           | String n
107           | Key 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 _ -> ()
114           | BufferIn n ->
115               pr "  const char *%s;\n" n;
116               pr "  size_t %s_size;\n" n
117           | Pointer _ -> assert false
118         ) (args @ optargs)
119       );
120       pr "\n";
121
122       let is_filein =
123         List.exists (function FileIn _ -> true | _ -> false) args in
124
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
128        * in that case.
129        *)
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;
134         if is_filein then
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";
137         pr "    goto done;\n";
138         pr "  }\n";
139       ) else (
140         pr "  if (optargs_bitmask != 0) {\n";
141         if is_filein then
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";
144         pr "    goto done;\n";
145         pr "  }\n";
146       );
147       pr "\n";
148
149       (* Decode arguments. *)
150       if args <> [] || optargs <> [] then (
151         pr "  memset (&args, 0, sizeof args);\n";
152         pr "\n";
153         pr "  if (!xdr_guestfs_%s_args (xdr_in, &args)) {\n" name;
154         if is_filein then
155           pr "    cancel_receive ();\n";
156         pr "    reply_with_error (\"daemon failed to decode procedure arguments\");\n";
157         pr "    goto done;\n";
158         pr "  }\n";
159         let pr_args n =
160           pr "  char *%s = args.%s;\n" n n
161         in
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;
166           if is_filein then
167             pr "    cancel_receive ();\n";
168           pr "    reply_with_perror (\"realloc\");\n";
169           pr "    goto done;\n";
170           pr "  }\n";
171           pr "  %s[args.%s.%s_len] = NULL;\n" n n n;
172           pr "  args.%s.%s_val = %s;\n" n n n;
173         in
174         List.iter (
175           function
176           | Pathname n ->
177               pr_args n;
178               pr "  ABS_PATH (%s, %s, goto done);\n"
179                 n (if is_filein then "cancel_receive ()" else "");
180           | Device n ->
181               pr_args n;
182               pr "  RESOLVE_DEVICE (%s, %s, goto done);\n"
183                 n (if is_filein then "cancel_receive ()" else "");
184           | Dev_or_Path n ->
185               pr_args n;
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
190           | StringList n ->
191               pr_list_handling_code n;
192           | DeviceList n ->
193               pr_list_handling_code n;
194               pr "  /* Ensure that each is a device,\n";
195               pr "   * and perform device name translation.\n";
196               pr "   */\n";
197               pr "  {\n";
198               pr "    size_t i;\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 "");
202               pr "  }\n";
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 _ -> ()
207           | BufferIn n ->
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
211         ) (args @ optargs);
212         pr "\n"
213       );
214
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 "");
221       );
222
223       (* Don't want to call the impl with any FileIn or FileOut
224        * parameters, since these go "outside" the RPC protocol.
225        *)
226       let () =
227         let args' =
228           List.filter
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;
233         pr ";\n" in
234
235       (match ret with
236        | RConstOptString _ -> assert false
237        | RErr | RInt _ | RInt64 _ | RBool _
238        | RConstString _
239        | RString _ | RStringList _ | RHashtable _
240        | RStruct (_, _) | RStructList (_, _) ->
241            let errcode =
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;
247            pr "    goto done;\n";
248            pr "\n"
249        | RBufferOut _ ->
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";
252            pr "   */\n";
253            pr "  if (size == 1 && r == NULL)\n";
254            pr "    /* do_%s has already called reply_with_error */\n" name;
255            pr "    goto done;\n";
256            pr "\n"
257       );
258
259       (* If there are any FileOut parameters, then the impl must
260        * send its own reply.
261        *)
262       let no_reply =
263         List.exists (function FileOut _ -> true | _ -> false) args in
264       if no_reply then
265         pr "  /* do_%s has already sent a reply */\n" name
266       else (
267         match ret with
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"
273               name
274         | RConstString _ | RConstOptString _ ->
275             failwithf "RConstString|RConstOptString cannot be used by daemon functions"
276         | RString n ->
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"
280               name;
281             pr "  free (r);\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"
287               name;
288             pr "  free_strings (r);\n"
289         | RStruct (n, _) ->
290             pr "  struct guestfs_%s_ret ret;\n" name;
291             pr "  ret.%s = *r;\n" n;
292             pr "  free (r);\n";
293             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
294               name;
295             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
296               name
297         | RStructList (n, _) ->
298             pr "  struct guestfs_%s_ret ret;\n" name;
299             pr "  ret.%s = *r;\n" n;
300             pr "  free (r);\n";
301             pr "  reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
302               name;
303             pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
304               name
305         | RBufferOut 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"
310               name;
311             pr "  free (r);\n"
312       );
313
314       (* Free the args. *)
315       pr "done:\n";
316       (match args with
317        | [] -> ()
318        | _ ->
319            pr "  xdr_free ((xdrproc_t) xdr_guestfs_%s_args, (char *) &args);\n"
320              name
321       );
322       pr "  return;\n";
323       pr "}\n\n";
324   ) daemon_functions;
325
326   (* Dispatch function. *)
327   pr "void dispatch_incoming_message (XDR *xdr_in)\n";
328   pr "{\n";
329   pr "  switch (proc_nr) {\n";
330
331   List.iter (
332     fun (name, _, _, _, _, _, _) ->
333       pr "    case GUESTFS_PROC_%s:\n" (String.uppercase name);
334       pr "      %s_stub (xdr_in);\n" name;
335       pr "      break;\n"
336   ) daemon_functions;
337
338   pr "    default:\n";
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";
340   pr "  }\n";
341   pr "}\n";
342   pr "\n";
343
344   (* LVM columns and tokenization functions. *)
345   (* XXX This generates crap code.  We should rethink how we
346    * do this parsing.
347    *)
348   List.iter (
349     function
350     | typ, cols ->
351         pr "static const char *lvm_%s_cols = \"%s\";\n"
352           typ (String.concat "," (List.map fst cols));
353         pr "\n";
354
355         pr "static int lvm_tokenize_%s (char *str, guestfs_int_lvm_%s *r)\n" typ typ;
356         pr "{\n";
357         pr "  char *tok, *p, *next;\n";
358         pr "  size_t i, j;\n";
359         pr "\n";
360         (*
361           pr "  fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
362           pr "\n";
363         *)
364         pr "  if (!str) {\n";
365         pr "    fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
366         pr "    return -1;\n";
367         pr "  }\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";
370         pr "    return -1;\n";
371         pr "  }\n";
372         pr "  tok = str;\n";
373         List.iter (
374           fun (name, coltype) ->
375             pr "  if (!tok) {\n";
376             pr "    fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
377             pr "    return -1;\n";
378             pr "  }\n";
379             pr "  p = strchrnul (tok, ',');\n";
380             pr "  if (*p) next = p+1; else next = NULL;\n";
381             pr "  *p = '\\0';\n";
382             (match coltype with
383              | FString ->
384                  pr "  r->%s = strdup (tok);\n" name;
385                  pr "  if (r->%s == NULL) {\n" name;
386                  pr "    perror (\"strdup\");\n";
387                  pr "    return -1;\n";
388                  pr "  }\n"
389              | FUUID ->
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";
393                  pr "      return -1;\n";
394                  pr "    } else if (tok[j] != '-')\n";
395                  pr "      r->%s[i++] = tok[j];\n" name;
396                  pr "  }\n";
397              | FBytes ->
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;
400                  pr "    return -1;\n";
401                  pr "  }\n";
402              | FInt64 ->
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;
405                  pr "    return -1;\n";
406                  pr "  }\n";
407              | FOptPercent ->
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;
412                  pr "    return -1;\n";
413                  pr "  }\n";
414              | FBuffer | FInt32 | FUInt32 | FUInt64 | FChar ->
415                  assert false (* can never be an LVM column *)
416             );
417             pr "  tok = next;\n";
418         ) cols;
419
420         pr "  if (tok != NULL) {\n";
421         pr "    fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
422         pr "    return -1;\n";
423         pr "  }\n";
424         pr "  return 0;\n";
425         pr "}\n";
426         pr "\n";
427
428         pr "guestfs_int_lvm_%s_list *\n" typ;
429         pr "parse_command_line_%ss (void)\n" typ;
430         pr "{\n";
431         pr "  char *out, *err;\n";
432         pr "  char *p, *pend;\n";
433         pr "  int r, i;\n";
434         pr "  guestfs_int_lvm_%s_list *ret;\n" typ;
435         pr "  void *newp;\n";
436         pr "\n";
437         pr "  ret = malloc (sizeof *ret);\n";
438         pr "  if (!ret) {\n";
439         pr "    reply_with_perror (\"malloc\");\n";
440         pr "    return NULL;\n";
441         pr "  }\n";
442         pr "\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;
445         pr "\n";
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";
452         pr "    free (out);\n";
453         pr "    free (err);\n";
454         pr "    free (ret);\n";
455         pr "    return NULL;\n";
456         pr "  }\n";
457         pr "\n";
458         pr "  free (err);\n";
459         pr "\n";
460         pr "  /* Tokenize each line of the output. */\n";
461         pr "  p = out;\n";
462         pr "  i = 0;\n";
463         pr "  while (p) {\n";
464         pr "    pend = strchr (p, '\\n');       /* Get the next line of output. */\n";
465         pr "    if (pend) {\n";
466         pr "      *pend = '\\0';\n";
467         pr "      pend++;\n";
468         pr "    }\n";
469         pr "\n";
470         pr "    while (*p && c_isspace (*p))    /* Skip any leading whitespace. */\n";
471         pr "      p++;\n";
472         pr "\n";
473         pr "    if (!*p) {                      /* Empty line?  Skip it. */\n";
474         pr "      p = pend;\n";
475         pr "      continue;\n";
476         pr "    }\n";
477         pr "\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;
484         pr "      free (ret);\n";
485         pr "      free (out);\n";
486         pr "      return NULL;\n";
487         pr "    }\n";
488         pr "    ret->guestfs_int_lvm_%s_list_val = newp;\n" typ;
489         pr "\n";
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;
495         pr "      free (ret);\n";
496         pr "      free (out);\n";
497         pr "      return NULL;\n";
498         pr "    }\n";
499         pr "\n";
500         pr "    ++i;\n";
501         pr "    p = pend;\n";
502         pr "  }\n";
503         pr "\n";
504         pr "  ret->guestfs_int_lvm_%s_list_len = i;\n" typ;
505         pr "\n";
506         pr "  free (out);\n";
507         pr "  return ret;\n";
508         pr "}\n"
509
510   ) ["pv", lvm_pv_cols; "vg", lvm_vg_cols; "lv", lvm_lv_cols]
511
512 (* Generate a list of function names, for debugging in the daemon.. *)
513 and generate_daemon_names () =
514   generate_header CStyle GPLv2plus;
515
516   pr "#include <config.h>\n";
517   pr "\n";
518   pr "#include \"daemon.h\"\n";
519   pr "\n";
520
521   pr "/* This array is indexed by proc_nr.  See guestfs_protocol.x. */\n";
522   pr "const char *function_names[] = {\n";
523   List.iter (
524     fun (name, _, proc_nr, _, _, _, _) -> pr "  [%d] = \"%s\",\n" proc_nr name
525   ) daemon_functions;
526   pr "};\n";
527
528 (* Generate the optional groups for the daemon to implement
529  * guestfs_available.
530  *)
531 and generate_daemon_optgroups_c () =
532   generate_header CStyle GPLv2plus;
533
534   pr "#include <config.h>\n";
535   pr "\n";
536   pr "#include \"daemon.h\"\n";
537   pr "#include \"optgroups.h\"\n";
538   pr "\n";
539
540   pr "struct optgroup optgroups[] = {\n";
541   List.iter (
542     fun (group, _) ->
543       pr "  { \"%s\", optgroup_%s_available },\n" group group
544   ) optgroups;
545   pr "  { NULL, NULL }\n";
546   pr "};\n"
547
548 and generate_daemon_optgroups_h () =
549   generate_header CStyle GPLv2plus;
550
551   List.iter (
552     fun (group, _) ->
553       pr "extern int optgroup_%s_available (void);\n" group
554   ) optgroups