Red Hat Training

A Red Hat training course is available for Red Hat Enterprise Linux

21.10. 使用从编程语言中使用 API

libguestfs API 可直接使用 Red Hat Enterprise Linux 7 中的以下语言:c、C++、Perl、Python、Java、Ruby 和 OCaml。
  • 要安装 C 和 C++ 绑定,请输入以下命令:
    # yum install libguestfs-devel
  • 安装 Perl 绑定:
    # yum install 'perl(Sys::Guestfs)'
  • 安装 Python 绑定:
    # yum install python-libguestfs
  • 安装 Java 绑定:
    # yum install libguestfs-java libguestfs-java-devel libguestfs-javadoc
  • 安装 Ruby 绑定:
    # yum install ruby-libguestfs
  • 安装 OCaml 绑定:
    # yum install ocaml-libguestfs ocaml-libguestfs-devel
每个语言的绑定本质上是相同的,但具有小的语法更改。一个 C 语句:
guestfs_launch (g);
在 Perl 中会出现以下内容:
$g->launch ()
或者类似 OCaml 中的以下内容:
g#launch ()
本节仅详述了 C 中的 API。
在 C 和 C++ 绑定中,您必须手动检查错误。在其他绑定中,错误会被转换为异常;以下示例中显示的其他错误检查不是其他语言所必需的,但您可能希望添加代码来进行异常。有关 libguestfs API 架构的一些兴趣点,请参见以下列表:
  • libguestfs API 是同步的。每个调用块直到完成为止。如果要异步调用,您必须创建一个线程。
  • libguestfs API 不是线程安全:每个句柄只能从一个线程使用;或者,如果您希望在线程间共享句柄,您应该实施自己的 mutex,以确保两个线程无法同时在一个句柄上执行命令。
  • 您不应该在同一磁盘镜像上打开多个句柄。如果所有句柄都是只读的,但仍然不推荐这样做。
  • 如果其他任何可能使用该磁盘镜像(例如,实时虚拟机)时,您不应该添加用于写入的磁盘镜像。这样做会导致磁盘崩溃。
  • 在当前使用(例如,实时虚拟机)的磁盘镜像上打开只读处理。但是,结果可能会不预计或不一致,特别是当您读取它时,磁盘镜像被大量写入。

21.10.1. 使用 C 程序与 API 交互

您的 C 程序应该首先包括 <guestfs.h> 标头文件并创建句柄:
#include <stdio.h>
#include <stdlib.h>
#include <guestfs.h>

int
main (int argc, char *argv[])
{
  guestfs_h *g;

  g = guestfs_create ();
  if (g == NULL) {
    perror ("failed to create libguestfs handle");
    exit (EXIT_FAILURE);
   }

   /* ... */

   guestfs_close (g);

   exit (EXIT_SUCCESS);
 }
将该程序保存到文件中(test.c)。编译该程序并使用以下两个命令运行它:
gcc -Wall test.c -o test -lguestfs
./test
在这个阶段,它不应该打印任何输出。本节的其余部分展示了如何扩展此程序来创建新的磁盘映像、对其进行分区、将其格式化为 ext4 文件系统,并在文件系统中创建一些文件。磁盘镜像名为 disk.img,并在当前目录中创建。
该程序的概要是:
  • 创建句柄。
  • 将磁盘添加到句柄中。
  • 启动 libguestfs 后端。
  • 创建分区、文件系统和文件。
  • 关闭句柄并退出。
以下是修改的程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <guestfs.h>

 int
 main (int argc, char *argv[])
 {
   guestfs_h *g;
   size_t i;

   g = guestfs_create ();
   if (g == NULL) {
     perror ("failed to create libguestfs handle");
     exit (EXIT_FAILURE);
  }

   /* Create a raw-format sparse disk image, 512 MB in size. */
   int fd = open ("disk.img", O_CREAT|O_WRONLY|O_TRUNC|O_NOCTTY, 0666);
   if (fd == -1) {
     perror ("disk.img");
     exit (EXIT_FAILURE);
   }
   if (ftruncate (fd, 512 * 1024 * 1024) == -1) {
     perror ("disk.img: truncate");
     exit (EXIT_FAILURE);
   }
   if (close (fd) == -1) {
     perror ("disk.img: close");
     exit (EXIT_FAILURE);
   }

   /* Set the trace flag so that we can see each libguestfs call. */
   guestfs_set_trace (g, 1);

   /* Set the autosync flag so that the disk will be synchronized
    * automatically when the libguestfs handle is closed.
    */
   guestfs_set_autosync (g, 1);

   /* Add the disk image to libguestfs. */
   if (guestfs_add_drive_opts (g, "disk.img",
         GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", /* raw format */
         GUESTFS_ADD_DRIVE_OPTS_READONLY, 0,   /* for write */
         -1 /* this marks end of optional arguments */ )
       == -1)
     exit (EXIT_FAILURE);

   /* Run the libguestfs back-end. */
   if (guestfs_launch (g) == -1)
     exit (EXIT_FAILURE);

   /* Get the list of devices. Because we only added one drive
    * above, we expect that this list should contain a single
    * element.
    */
   char **devices = guestfs_list_devices (g);
   if (devices == NULL)
     exit (EXIT_FAILURE);
   if (devices[0] == NULL || devices[1] != NULL) {
     fprintf (stderr,
              "error: expected a single device from list-devices\n");
     exit (EXIT_FAILURE);
   }

   /* Partition the disk as one single MBR partition. */
   if (guestfs_part_disk (g, devices[0], "mbr") == -1)
     exit (EXIT_FAILURE);

   /* Get the list of partitions. We expect a single element, which
    * is the partition we have just created.
    */
   char **partitions = guestfs_list_partitions (g);
   if (partitions == NULL)
     exit (EXIT_FAILURE);
   if (partitions[0] == NULL || partitions[1] != NULL) {
     fprintf (stderr,
              "error: expected a single partition from list-partitions\n");
     exit (EXIT_FAILURE);
   }

   /* Create an ext4 filesystem on the partition. */
   if (guestfs_mkfs (g, "ext4", partitions[0]) == -1)
     exit (EXIT_FAILURE);

   /* Now mount the filesystem so that we can add files. */
   if (guestfs_mount_options (g, "", partitions[0], "/") == -1)
     exit (EXIT_FAILURE);

   /* Create some files and directories. */
   if (guestfs_touch (g, "/empty") == -1)
     exit (EXIT_FAILURE);

   const char *message = "Hello, world\n";
   if (guestfs_write (g, "/hello", message, strlen (message)) == -1)
     exit (EXIT_FAILURE);

   if (guestfs_mkdir (g, "/foo") == -1)
     exit (EXIT_FAILURE);

   /* This uploads the local file /etc/resolv.conf into the disk image. */
   if (guestfs_upload (g, "/etc/resolv.conf", "/foo/resolv.conf") == -1)
     exit (EXIT_FAILURE);

   /* Because 'autosync' was set (above) we can just close the handle
    * and the disk contents will be synchronized. You can also do
    * this manually by calling guestfs_umount_all and guestfs_sync.
    */
   guestfs_close (g);

   /* Free up the lists. */
   for (i = 0; devices[i] != NULL; ++i)
     free (devices[i]);
   free (devices);
   for (i = 0; partitions[i] != NULL; ++i)
     free (partitions[i]);
   free (partitions);

   exit (EXIT_SUCCESS);
 }
使用以下两个命令编译并运行该程序:
gcc -Wall test.c -o test -lguestfs
./test
如果程序成功完成,您应该保留一个名为 disk.img 的磁盘镜像,您可以使用 guestfish 检查它:
guestfish --ro -a disk.img -m /dev/sda1
><fs> ll /
><fs> cat /foo/resolv.conf
默认情况下,对于 C 和 C++ 绑定,libguestfs 将错误打印到 stderr。您可以通过设置错误处理程序来更改此行为。guestfs(3)手册页详细讨论。