16.11. 使用编程语言的 API
红帽企业 Linux 6.2 中的以下语言可直接使用 libguestfs API: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,以确保两个线程无法同时在一个进程上执行命令。
- 您不应该在同一磁盘镜像中打开多个句柄。如果所有句柄都是只读的,但仍然不建议这样做。
- 如果其他部分可能正在使用该磁盘镜像(例如,实时虚拟机),您不应该添加磁盘镜像来写入。执行此操作将导致磁盘损坏。
- 在当前使用(例如,实时虚拟机)的磁盘镜像上打开只读句柄;但是,如果磁盘映像在读取时大量写入该磁盘镜像时,结果可能会无法预测或不一致。
16.11.1. 通过 C 程序与 API 交互
您的 C 程序应该首先包括 {s.> 标头文件并创建一个句柄:
#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)man page 详细阐述此问题。