1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
|
(* libguestfs
* Copyright (C) 2009-2023 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*)
(* Please read generator/README first. *)
open Printf
open Std_utils
open Types
open Utils
open Pr
open Docstrings
open Optgroups
open Actions
open Structs
open C
open Events
let generate_header = generate_header ~inputs:["generator/perl.ml"]
(* Generate Perl xs code, a sort of crazy variation of C with macros. *)
let rec generate_perl_xs () =
generate_header CStyle LGPLv2plus;
pr "\
#include <config.h>
/* It is safe to call deprecated functions from this file. */
#define GUESTFS_NO_WARN_DEPRECATED
#undef GUESTFS_NO_DEPRECATED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include \"EXTERN.h\"
#include \"perl.h\"
#include \"XSUB.h\"
/* perl CORE/config.h defines '_' to something completely bonkers. */
#ifdef _
#undef _
#endif
#include <guestfs.h>
#include \"guestfs-internal-all.h\"
static SV *
my_newSVll(long long val) {
#ifdef USE_64_BIT_ALL
return newSViv(val);
#else
char buf[100];
int len;
len = snprintf(buf, 100, \"%%\" PRId64, val);
return newSVpv(buf, len);
#endif
}
static SV *
my_newSVull(unsigned long long val) {
#ifdef USE_64_BIT_ALL
return newSVuv(val);
#else
char buf[100];
int len;
len = snprintf(buf, 100, \"%%\" PRIu64, val);
return newSVpv(buf, len);
#endif
}
/* Convert a 64 bit int on input. To cope with the case of having
* a 32 bit Perl interpreter, we allow the user to pass a string
* here which is scanned as a 64 bit integer.
*/
static int64_t
my_SvIV64 (SV *sv)
{
#ifdef USE_64_BIT_ALL
return SvIV (sv);
#else
if (SvTYPE (sv) == SVt_PV) {
const char *str = SvPV_nolen (sv);
int64_t r;
sscanf (str, \"%%\" SCNi64, &r);
return r;
}
else
return SvIV (sv);
#endif
}
/* http://www.perlmonks.org/?node_id=680842 */
static char **
XS_unpack_charPtrPtr (SV *arg) {
char **ret;
AV *av;
I32 i;
if (!arg || !SvOK (arg) || !SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV)
croak (\"array reference expected\");
av = (AV *)SvRV (arg);
ret = malloc ((av_len (av) + 1 + 1) * sizeof (char *));
if (!ret)
croak (\"malloc failed\");
for (i = 0; i <= av_len (av); i++) {
SV **elem = av_fetch (av, i, 0);
if (!elem || !*elem) {
free (ret);
croak (\"missing element in list\");
}
ret[i] = SvPV_nolen (*elem);
}
ret[i] = NULL;
return ret;
}
/* http://www.perlmonks.org/?node=338857 */
static void
_event_callback_wrapper (guestfs_h *g,
void *cb,
uint64_t event,
int event_handle,
int flags,
const char *buf, size_t buf_len,
const uint64_t *array, size_t array_len)
{
dSP;
ENTER;
SAVETMPS;
PUSHMARK (SP);
XPUSHs (sv_2mortal (my_newSVull (event)));
XPUSHs (sv_2mortal (newSViv (event_handle)));
XPUSHs (sv_2mortal (newSVpvn (buf ? buf : \"\", buf_len)));
AV *av = newAV ();
size_t i;
for (i = 0; i < array_len; ++i)
av_push (av, my_newSVull (array[i]));
XPUSHs (sv_2mortal (newRV ((SV *) av)));
PUTBACK;
call_sv ((SV *) cb, G_VOID | G_DISCARD | G_EVAL);
FREETMPS;
LEAVE;
}
static SV **
get_all_event_callbacks (guestfs_h *g, size_t *len_rtn)
{
SV **r;
size_t i;
const char *key;
SV *cb;
/* Count the length of the array that will be needed. */
*len_rtn = 0;
cb = guestfs_first_private (g, &key);
while (cb != NULL) {
if (strncmp (key, \"_perl_event_\", strlen (\"_perl_event_\")) == 0)
(*len_rtn)++;
cb = guestfs_next_private (g, &key);
}
/* Copy them into the return array. */
r = malloc (sizeof (SV *) * (*len_rtn));
if (r == NULL) croak (\"malloc: %%m\");
i = 0;
cb = guestfs_first_private (g, &key);
while (cb != NULL) {
if (strncmp (key, \"_perl_event_\", strlen (\"_perl_event_\")) == 0) {
r[i] = cb;
i++;
}
cb = guestfs_next_private (g, &key);
}
return r;
}
static void
_close_handle (guestfs_h *g)
{
size_t i, len;
SV **cbs;
assert (g != NULL);
/* As in the OCaml bindings, there is a hard to solve case where the
* caller can delete a callback from within the callback, resulting
* in a double-free here. XXX
*/
cbs = get_all_event_callbacks (g, &len);
guestfs_close (g);
for (i = 0; i < len; ++i)
SvREFCNT_dec (cbs[i]);
free (cbs);
}
MODULE = Sys::Guestfs PACKAGE = Sys::Guestfs
PROTOTYPES: ENABLE
guestfs_h *
_create (flags)
unsigned flags;
CODE:
RETVAL = guestfs_create_flags (flags);
if (!RETVAL)
croak (\"could not create guestfs handle\");
guestfs_set_error_handler (RETVAL, NULL, NULL);
OUTPUT:
RETVAL
void
DESTROY (sv)
SV *sv;
PPCODE:
/* For the 'g' argument above we do the conversion explicitly and
* don't rely on the typemap, because if the handle has been
* explicitly closed we don't want the typemap conversion to
* display an error.
*/
HV *hv = (HV *) SvRV (sv);
SV **svp = hv_fetch (hv, \"_g\", 2, 0);
if (svp != NULL) {
guestfs_h *g = INT2PTR (guestfs_h *, SvIV (*svp));
_close_handle (g);
}
void
close (g)
guestfs_h *g;
PPCODE:
_close_handle (g);
/* Avoid double-free in DESTROY method. */
HV *hv = (HV *) SvRV (ST(0));
(void) hv_delete (hv, \"_g\", 2, G_DISCARD);
SV *
set_event_callback (g, cb, event_bitmask)
guestfs_h *g;
SV *cb;
int event_bitmask;
PREINIT:
int eh;
char key[64];
CODE:
eh = guestfs_set_event_callback (g, _event_callback_wrapper,
event_bitmask, 0, cb);
if (eh == -1)
croak (\"%%s\", guestfs_last_error (g));
/* Increase the refcount for this callback, since we are storing
* it in the opaque C libguestfs handle. We need to remember that
* we did this, so we can decrease the refcount for all undeleted
* callbacks left around at close time (see _close_handle).
*/
SvREFCNT_inc (cb);
snprintf (key, sizeof key, \"_perl_event_%%d\", eh);
guestfs_set_private (g, key, cb);
RETVAL = newSViv (eh);
OUTPUT:
RETVAL
void
delete_event_callback (g, event_handle)
guestfs_h *g;
int event_handle;
PREINIT:
char key[64];
SV *cb;
CODE:
snprintf (key, sizeof key, \"_perl_event_%%d\", event_handle);
cb = guestfs_get_private (g, key);
if (cb) {
SvREFCNT_dec (cb);
guestfs_set_private (g, key, NULL);
guestfs_delete_event_callback (g, event_handle);
}
SV *
event_to_string (event_bitmask)
int event_bitmask;
PREINIT:
char *str;
CODE:
str = guestfs_event_to_string (event_bitmask);
if (str == NULL)
croak (\"%%m\");
RETVAL = newSVpv (str, 0);
free (str);
OUTPUT:
RETVAL
SV *
last_errno (g)
guestfs_h *g;
PREINIT:
int errnum;
CODE:
errnum = guestfs_last_errno (g);
RETVAL = newSViv (errnum);
OUTPUT:
RETVAL
";
List.iter (
fun { name; style = (ret, args, optargs as style);
c_function; c_optarg_prefix; deprecated_by } ->
(match ret with
| RErr -> pr "void\n"
| RInt _ -> pr "SV *\n"
| RInt64 _ -> pr "SV *\n"
| RBool _ -> pr "SV *\n"
| RConstString _ -> pr "SV *\n"
| RConstOptString _ -> pr "SV *\n"
| RString _ -> pr "SV *\n"
| RBufferOut _ -> pr "SV *\n"
| RStringList _
| RStruct _ | RStructList _
| RHashtable _ ->
pr "void\n" (* all lists returned implicitly on the stack *)
);
(* Call and arguments. *)
pr "%s (g" name;
List.iter (
fun arg -> pr ", %s" (name_of_argt arg)
) args;
if optargs <> [] then
pr ", ...";
pr ")\n";
pr " guestfs_h *g;\n";
List.iteri (
fun i ->
function
| String (_, n) ->
pr " char *%s;\n" n
| BufferIn n ->
pr " char *%s;\n" n;
pr " size_t %s_size = SvCUR (ST(%d));\n" n (i+1)
| OptString n ->
(* http://www.perlmonks.org/?node_id=554277
* Note that the implicit handle argument means we have
* to add 1 to the ST(x) operator.
*)
pr " char *%s = SvOK(ST(%d)) ? SvPV_nolen(ST(%d)) : NULL;\n" n (i+1) (i+1)
| StringList (_, n) ->
pr " char **%s;\n" n
| Bool n -> pr " int %s;\n" n
| Int n -> pr " int %s;\n" n
| Int64 n -> pr " int64_t %s;\n" n
| Pointer (t, n) -> pr " void * /* %s */ %s;\n" t n
) args;
(* PREINIT section (local variable declarations). *)
pr "PREINIT:\n";
(match ret with
| RErr ->
pr " int r;\n";
| RInt _
| RBool _ ->
pr " int r;\n";
| RInt64 _ ->
pr " int64_t r;\n";
| RConstString _ ->
pr " const char *r;\n";
| RConstOptString _ ->
pr " const char *r;\n";
| RString _ ->
pr " char *r;\n";
| RStringList _ | RHashtable _ ->
pr " char **r;\n";
pr " size_t i, n;\n";
| RStruct (_, typ) ->
pr " struct guestfs_%s *r;\n" typ;
| RStructList (_, typ) ->
pr " struct guestfs_%s_list *r;\n" typ;
pr " size_t i;\n";
pr " HV *hv;\n";
| RBufferOut _ ->
pr " char *r;\n";
pr " size_t size;\n";
);
if optargs <> [] then (
pr " struct %s optargs_s = { .bitmask = 0 };\n" c_function;
pr " struct %s *optargs = &optargs_s;\n" c_function;
pr " size_t items_i;\n";
);
(* CODE or PPCODE section. PPCODE is used where we are
* returning void, or where we push the return value on the stack
* ourselves. Using CODE means we will manipulate RETVAL.
*)
(match ret with
| RErr ->
pr " PPCODE:\n";
| RInt _
| RBool _ ->
pr " CODE:\n";
| RInt64 _ ->
pr " CODE:\n";
| RConstString _ ->
pr " CODE:\n";
| RConstOptString _ ->
pr " CODE:\n";
| RString _ ->
pr " CODE:\n";
| RStringList _ | RHashtable _ ->
pr " PPCODE:\n";
| RBufferOut _ ->
pr " CODE:\n";
| RStruct _
| RStructList _ ->
pr " PPCODE:\n";
);
(match deprecated_by with
| Not_deprecated -> ()
| Replaced_by alt ->
pr " Perl_ck_warner (aTHX_ packWARN(WARN_DEPRECATED),\n";
pr " \"Sys::Guestfs::%s is deprecated; use Sys::Guestfs::%s instead\");\n" name alt;
| Deprecated_no_replacement ->
pr " Perl_ck_warner (aTHX_ packWARN(WARN_DEPRECATED),\n";
pr " \"Sys::Guestfs::%s is deprecated\");\n" name;
);
(* For optional arguments, convert these from the XSUB "items"
* variable by hand.
*)
if optargs <> [] then (
let skip = List.length args + 1 in
pr " if (((items - %d) & 1) != 0)\n" skip;
pr " croak (\"expecting an even number of extra parameters\");\n";
pr " for (items_i = %d; items_i < items; items_i += 2) {\n" skip;
pr " uint64_t this_mask;\n";
pr " const char *this_arg;\n";
pr "\n";
pr " this_arg = SvPV_nolen (ST (items_i));\n";
pr " ";
List.iter (
fun argt ->
let n = name_of_optargt argt in
let uc_n = String.uppercase_ascii n in
pr "if (STREQ (this_arg, \"%s\")) {\n" n;
(match argt with
| OBool _
| OInt _ ->
pr " optargs_s.%s = SvIV (ST (items_i+1));\n" n;
| OInt64 _ ->
pr " optargs_s.%s = my_SvIV64 (ST (items_i+1));\n" n;
| OString _ ->
pr " optargs_s.%s = SvPV_nolen (ST (items_i+1));\n" n;
| OStringList _ ->
pr " size_t i, len;\n";
pr " char **r;\n";
pr " SV *arg;\n";
pr " AV *av;\n";
pr " SV **svp;\n";
pr "\n";
pr " arg = ST (items_i+1);\n";
pr " if (!SvROK (arg) || SvTYPE (SvRV (arg)) != SVt_PVAV)\n";
pr " croak (\"array reference expected for '%%s' argument\", \"%s\");\n" n;
pr " av = (AV *) SvRV (ST (items_i+1));\n";
pr "\n";
pr " /* Note av_len returns index of final element. */\n";
pr " len = av_len (av) + 1;\n";
pr "\n";
pr " r = malloc ((len+1) * sizeof (char *));\n";
pr " if (r == NULL) croak (\"malloc: %%m\");\n";
pr " for (i = 0; i < len; ++i) {\n";
pr " svp = av_fetch (av, i, 0);\n";
pr " r[i] = SvPV_nolen (*svp);\n";
pr " }\n";
pr " r[i] = NULL;\n";
pr " optargs_s.%s = r;\n" n
);
pr " this_mask = %s_%s_BITMASK;\n" c_optarg_prefix uc_n;
pr " }\n";
pr " else ";
) optargs;
pr "croak (\"unknown optional argument '%%s'\", this_arg);\n";
pr " if (optargs_s.bitmask & this_mask)\n";
pr " croak (\"optional argument '%%s' given more than once\",\n";
pr " this_arg);\n";
pr " optargs_s.bitmask |= this_mask;\n";
pr " }\n";
pr "\n";
);
(* The call to the C function. *)
pr " r = %s " c_function;
generate_c_call_args ~handle:"g" style;
pr ";\n";
(* Cleanup any arguments. *)
List.iter (
function
| String _ | OptString _
| Bool _ | Int _ | Int64 _
| BufferIn _
| Pointer _ -> ()
| StringList (_, n) ->
pr " free (%s);\n" n
) args;
(* Check return value for errors and return it if necessary. *)
(match ret with
| RErr ->
pr " if (r == -1)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
| RInt _
| RBool _ ->
pr " if (r == -1)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " RETVAL = newSViv (r);\n";
pr " OUTPUT:\n";
pr " RETVAL\n"
| RInt64 _ ->
pr " if (r == -1)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " RETVAL = my_newSVll (r);\n";
pr " OUTPUT:\n";
pr " RETVAL\n"
| RConstString _ ->
pr " if (r == NULL)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " RETVAL = newSVpv (r, 0);\n";
pr " OUTPUT:\n";
pr " RETVAL\n"
| RConstOptString _ ->
pr " if (r == NULL)\n";
pr " RETVAL = &PL_sv_undef;\n";
pr " else\n";
pr " RETVAL = newSVpv (r, 0);\n";
pr " OUTPUT:\n";
pr " RETVAL\n"
| RString _ ->
pr " if (r == NULL)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " RETVAL = newSVpv (r, 0);\n";
pr " free (r);\n";
pr " OUTPUT:\n";
pr " RETVAL\n"
| RStringList _ | RHashtable _ ->
pr " if (r == NULL)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " for (n = 0; r[n] != NULL; ++n) /**/;\n";
pr " EXTEND (SP, n);\n";
pr " for (i = 0; i < n; ++i) {\n";
pr " PUSHs (sv_2mortal (newSVpv (r[i], 0)));\n";
pr " free (r[i]);\n";
pr " }\n";
pr " free (r);\n";
| RStruct (_, typ) ->
let cols = cols_of_struct typ in
generate_perl_struct_code typ cols name style
| RStructList (_, typ) ->
let cols = cols_of_struct typ in
generate_perl_struct_list_code typ cols name style
| RBufferOut _ ->
pr " if (r == NULL)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " RETVAL = newSVpvn (r, size);\n";
pr " free (r);\n";
pr " OUTPUT:\n";
pr " RETVAL\n"
);
pr "\n"
) (actions |> external_functions |> sort)
and generate_perl_struct_list_code typ cols name style =
pr " if (r == NULL)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " EXTEND (SP, r->len);\n";
pr " for (i = 0; i < r->len; ++i) {\n";
pr " hv = newHV ();\n";
List.iter (
function
| name, FString ->
pr " (void) hv_store (hv, \"%s\", %d, newSVpv (r->val[i].%s, 0), 0);\n"
name (String.length name) name
| name, FUUID ->
pr " (void) hv_store (hv, \"%s\", %d, newSVpv (r->val[i].%s, 32), 0);\n"
name (String.length name) name
| name, FBuffer ->
pr " (void) hv_store (hv, \"%s\", %d, newSVpvn (r->val[i].%s, r->val[i].%s_len), 0);\n"
name (String.length name) name name
| name, (FBytes|FUInt64) ->
pr " (void) hv_store (hv, \"%s\", %d, my_newSVull (r->val[i].%s), 0);\n"
name (String.length name) name
| name, FInt64 ->
pr " (void) hv_store (hv, \"%s\", %d, my_newSVll (r->val[i].%s), 0);\n"
name (String.length name) name
| name, (FInt32|FUInt32) ->
pr " (void) hv_store (hv, \"%s\", %d, newSVnv (r->val[i].%s), 0);\n"
name (String.length name) name
| name, FChar ->
pr " (void) hv_store (hv, \"%s\", %d, newSVpv (&r->val[i].%s, 1), 0);\n"
name (String.length name) name
| name, FOptPercent ->
pr " (void) hv_store (hv, \"%s\", %d, newSVnv (r->val[i].%s), 0);\n"
name (String.length name) name
) cols;
pr " PUSHs (sv_2mortal (newRV ((SV *) hv)));\n";
pr " }\n";
pr " guestfs_free_%s_list (r);\n" typ
and generate_perl_struct_code typ cols name style =
pr " if (r == NULL)\n";
pr " croak (\"%%s\", guestfs_last_error (g));\n";
pr " EXTEND (SP, 2 * %d);\n" (List.length cols);
List.iter (
fun ((name, _) as col) ->
pr " PUSHs (sv_2mortal (newSVpv (\"%s\", 0)));\n" name;
match col with
| name, FString ->
pr " PUSHs (sv_2mortal (newSVpv (r->%s, 0)));\n"
name
| name, FBuffer ->
pr " PUSHs (sv_2mortal (newSVpvn (r->%s, r->%s_len)));\n"
name name
| name, FUUID ->
pr " PUSHs (sv_2mortal (newSVpv (r->%s, 32)));\n"
name
| name, (FBytes|FUInt64) ->
pr " PUSHs (sv_2mortal (my_newSVull (r->%s)));\n"
name
| name, FInt64 ->
pr " PUSHs (sv_2mortal (my_newSVll (r->%s)));\n"
name
| name, (FInt32|FUInt32) ->
pr " PUSHs (sv_2mortal (newSVnv (r->%s)));\n"
name
| name, FChar ->
pr " PUSHs (sv_2mortal (newSVpv (&r->%s, 1)));\n"
name
| name, FOptPercent ->
pr " PUSHs (sv_2mortal (newSVnv (r->%s)));\n"
name
) cols;
pr " guestfs_free_%s (r);\n" typ
(* Generate Sys/Guestfs.pm. *)
and generate_perl_pm () =
generate_header HashStyle LGPLv2plus;
pr "\
=encoding utf8
=pod
=head1 NAME
Sys::Guestfs - Perl bindings for libguestfs
=head1 SYNOPSIS
use Sys::Guestfs;
my $g = Sys::Guestfs->new ();
$g->add_drive_opts ('guest.img', format => 'raw');
$g->launch ();
$g->mount ('/dev/sda1', '/');
$g->touch ('/hello');
$g->shutdown ();
$g->close ();
=head1 DESCRIPTION
The C<Sys::Guestfs> module provides a Perl XS binding to the
libguestfs API for examining and modifying virtual machine
disk images.
Amongst the things this is good for: making batch configuration
changes to guests, getting disk used/free statistics (see also:
virt-df), migrating between virtualization systems (see also:
virt-p2v), performing partial backups, performing partial guest
clones, cloning guests and changing registry/UUID/hostname info, and
much else besides.
Libguestfs uses Linux kernel and qemu code, and can access any type of
guest filesystem that Linux and qemu can, including but not limited
to: ext2/3/4, btrfs, FAT and NTFS, LVM, many different disk partition
schemes, qcow, qcow2, vmdk.
Libguestfs provides ways to enumerate guest storage (eg. partitions,
LVs, what filesystem is in each LV, etc.). It can also run commands
in the context of the guest. Also you can access filesystems over
FUSE.
=head1 ERRORS
All errors turn into calls to C<croak> (see L<Carp(3)>).
The error string from libguestfs is directly available from
C<$@>. Use the C<last_errno> method if you want to get the errno.
=head1 METHODS
=over 4
=cut
package Sys::Guestfs;
use strict;
use warnings;
# This is always 1.0, never changes, and is unrelated to the
# real libguestfs version. If you want to find the libguestfs
# library version, use $g->version.
use vars qw($VERSION);
$VERSION = '1.0';
require XSLoader;
XSLoader::load ('Sys::Guestfs');
";
(* Methods. *)
pr "\
=item $g = Sys::Guestfs->new ([environment => 0,] [close_on_exit => 0]);
Create a new guestfs handle.
If the optional argument C<environment> is false, then
the C<GUESTFS_CREATE_NO_ENVIRONMENT> flag is set.
If the optional argument C<close_on_exit> is false, then
the C<GUESTFS_CREATE_NO_CLOSE_ON_EXIT> flag is set.
=cut
sub new {
my $proto = shift;
my $class = ref ($proto) || $proto;
my %%flags = @_;
my $flags = 0;
$flags |= 1 if exists $flags{environment} && !$flags{environment};
$flags |= 2 if exists $flags{close_on_exit} && !$flags{close_on_exit};
my $g = Sys::Guestfs::_create ($flags);
my $self = { _g => $g, _flags => $flags };
bless $self, $class;
# If we don't do this, the program name is always set to 'perl'.
my $program = $0;
$program =~ s{.*/}{};
$self->set_program ($program);
return $self;
}
=item $g->close ();
Explicitly close the guestfs handle.
B<Note:> You should not usually call this function. The handle will
be closed implicitly when its reference count goes to zero (eg.
when it goes out of scope or the program ends). This call is
only required in some exceptional cases, such as where the program
may contain cached references to the handle 'somewhere' and you
really have to have the close happen right away. After calling
C<close> the program must not call any method (including C<close>)
on the handle (but the implicit call to C<DESTROY> that happens
when the final reference is cleaned up is OK).
";
List.iter (
fun (name, bitmask) ->
pr "=item $Sys::Guestfs::EVENT_%s\n" (String.uppercase_ascii name);
pr "\n";
pr "See L<guestfs(3)/GUESTFS_EVENT_%s>.\n"
(String.uppercase_ascii name);
pr "\n";
pr "=cut\n";
pr "\n";
pr "our $EVENT_%s = 0x%x;\n" (String.uppercase_ascii name) bitmask;
pr "\n"
) events;
pr "=item $Sys::Guestfs::EVENT_ALL\n";
pr "\n";
pr "See L<guestfs(3)/GUESTFS_EVENT_ALL>.\n";
pr "\n";
pr "=cut\n";
pr "\n";
pr "our $EVENT_ALL = 0x%x;\n" all_events_bitmask;
pr "\n";
pr "\
=item $event_handle = $g->set_event_callback (\\&cb, $event_bitmask);
Register C<cb> as a callback function for all of the events
in C<$event_bitmask> (one or more C<$Sys::Guestfs::EVENT_*> flags
logically or'd together).
This function returns an event handle which
can be used to delete the callback using C<delete_event_callback>.
The callback function receives 4 parameters:
&cb ($event, $event_handle, $buf, $array)
=over 4
=item $event
The event which happened (equal to one of C<$Sys::Guestfs::EVENT_*>).
=item $event_handle
The event handle.
=item $buf
For some event types, this is a message buffer (ie. a string).
=item $array
For some event types (notably progress events), this is
an array of integers.
=back
You should carefully read the documentation for
L<guestfs(3)/guestfs_set_event_callback> before using
this function.
=item $g->delete_event_callback ($event_handle);
This removes the callback which was previously registered using
C<set_event_callback>.
=item $str = Sys::Guestfs::event_to_string ($events);
C<$events> is either a single event or a bitmask of events.
This returns a printable string, useful for debugging.
Note that this is a class function, not a method.
=item $errnum = $g->last_errno ();
This returns the last error number (errno) that happened on the
handle C<$g>.
If successful, an errno integer not equal to zero is returned.
If no error number is available, this returns 0.
See L<guestfs(3)/guestfs_last_errno> for more details of why
this can happen.
You can use the standard Perl module L<Errno(3)> to compare
the numeric error returned from this call with symbolic
errnos:
$g->mkdir (\"/foo\");
if ($g->last_errno() == Errno::EEXIST()) {
# mkdir failed because the directory exists already.
}
=cut
";
(* Actions. We only need to print documentation for these as
* they are pulled in from the XS code automatically.
*)
List.iter (
fun ({ name; style; longdesc; non_c_aliases } as f) ->
let longdesc = String.replace longdesc "C<guestfs_" "C<$g-E<gt>" in
pr "=item ";
generate_perl_prototype name style;
pr "\n\n";
pr "%s\n\n" longdesc;
if f.protocol_limit_warning then
pr "%s\n\n" protocol_limit_warning;
(match f.optional with
| None -> ()
| Some opt ->
pr "This function depends on the feature C<%s>. See also
C<$g-E<gt>feature-available>.\n\n" opt
);
(match deprecation_notice f with
| None -> ()
| Some txt -> pr "%s\n\n" txt
);
(* Aliases. *)
List.iter (
fun alias ->
pr "=item ";
generate_perl_prototype alias style;
pr "\n";
pr "\n";
pr "This is an alias of L</%s>.\n" name;
pr "\n";
pr "=cut\n\n";
pr "sub %s {\n" alias;
pr " &%s (@_)\n" name;
pr "}\n";
pr "\n";
pr "=pod\n";
pr "\n";
) non_c_aliases
) (actions |> documented_functions |> sort);
pr "=cut\n\n";
(* End of file. *)
pr "\
1;
=back
=head1 AVAILABILITY
From time to time we add new libguestfs APIs. Also some libguestfs
APIs won't be available in all builds of libguestfs (the Fedora
build is full-featured, but other builds may disable features).
How do you test whether the APIs that your Perl program needs are
available in the version of C<Sys::Guestfs> that you are using?
To test if a particular function is available in the C<Sys::Guestfs>
class, use the ordinary Perl UNIVERSAL method C<can(METHOD)>
(see L<perlobj(1)>). For example:
use Sys::Guestfs;
if (defined (Sys::Guestfs->can (\"set_verbose\"))) {
print \"\\$g->set_verbose is available\\n\";
}
To test if particular features are supported by the current
build, use the L</feature_available> method like the example below. Note
that the appliance must be launched first.
$g->feature_available ( [\"augeas\"] );
For further discussion on this topic, refer to
L<guestfs(3)/AVAILABILITY>.
=head1 STORING DATA IN THE HANDLE
The handle returned from L</new> is a hash reference. The hash
normally contains some elements:
{
_g => [private data used by libguestfs],
_flags => [flags provided when creating the handle]
}
Callers can add other elements to this hash to store data for their own
purposes. The data lasts for the lifetime of the handle.
Any fields whose names begin with an underscore are reserved
for private use by libguestfs. We may add more in future.
It is recommended that callers prefix the name of their field(s)
with some unique string, to avoid conflicts with other users.
=head1 COPYRIGHT
Copyright (C) %s Red Hat Inc.
=head1 LICENSE
Please see the file COPYING.LIB for the full license.
=head1 SEE ALSO
L<guestfs(3)>,
L<guestfish(1)>,
L<http://libguestfs.org>.
=cut
" copyright_years
and generate_perl_prototype name (ret, args, optargs) =
(match ret with
| RErr -> ()
| RBool n
| RInt n
| RInt64 n
| RConstString n
| RConstOptString n
| RString (_, n)
| RBufferOut n -> pr "$%s = " n
| RStruct (n,_)
| RHashtable (_, _, n) -> pr "%%%s = " n
| RStringList (_, n)
| RStructList (n,_) -> pr "@%s = " n
);
pr "$g->%s (" name;
let comma = ref false in
List.iter (
fun arg ->
if !comma then pr ", ";
comma := true;
match arg with
| String (_, n)
| OptString n | Bool n | Int n | Int64 n
| BufferIn n | Pointer (_, n) ->
pr "$%s" n
| StringList (_, n) ->
pr "\\@%s" n
) args;
List.iter (
fun arg ->
if !comma then pr " [, " else pr "[";
comma := true;
let n = name_of_optargt arg in
pr "%s => $%s]" n n
) optargs;
pr ");"
|