diff options
Diffstat (limited to 'tracecmd/trace-setup-guest.c')
-rw-r--r-- | tracecmd/trace-setup-guest.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/tracecmd/trace-setup-guest.c b/tracecmd/trace-setup-guest.c new file mode 100644 index 00000000..f20b48e2 --- /dev/null +++ b/tracecmd/trace-setup-guest.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 VMware Inc, Slavomir Kaslev <kaslevs@vmware.com> + * + */ + +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <grp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "trace-local.h" +#include "trace-msg.h" + +static int make_dir(const char *path, mode_t mode) +{ + char buf[PATH_MAX+2], *p; + + strncpy(buf, path, sizeof(buf)); + if (buf[PATH_MAX]) + return -E2BIG; + + for (p = buf; *p; p++) { + p += strspn(p, "/"); + p += strcspn(p, "/"); + *p = '\0'; + if (mkdir(buf, mode) < 0 && errno != EEXIST) + return -errno; + *p = '/'; + } + + return 0; +} + +static int make_fifo(const char *path, mode_t mode) +{ + struct stat st; + + if (!stat(path, &st)) { + if (S_ISFIFO(st.st_mode)) + return 0; + return -EEXIST; + } + + if (mkfifo(path, mode)) + return -errno; + return 0; +} + +static int make_guest_dir(const char *guest) +{ + char path[PATH_MAX]; + + snprintf(path, sizeof(path), GUEST_DIR_FMT, guest); + return make_dir(path, 0750); +} + +static int make_guest_fifo(const char *guest, int cpu, mode_t mode) +{ + static const char *exts[] = {".in", ".out"}; + char path[PATH_MAX]; + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(exts); i++) { + snprintf(path, sizeof(path), GUEST_FIFO_FMT "%s", + guest, cpu, exts[i]); + ret = make_fifo(path, mode); + if (ret < 0) + break; + } + + return ret; +} + +static int make_guest_fifos(const char *guest, int nr_cpus, mode_t mode) +{ + int i, ret = 0; + mode_t mask; + + mask = umask(0); + for (i = 0; i < nr_cpus; i++) { + ret = make_guest_fifo(guest, i, mode); + if (ret < 0) + break; + } + umask(mask); + + return ret; +} + +static int get_guest_cpu_count(const char *guest) +{ + const char *cmd_fmt = "virsh vcpucount --maximum '%s' 2>/dev/null"; + int nr_cpus = -1; + char cmd[1024]; + FILE *f; + + snprintf(cmd, sizeof(cmd), cmd_fmt, guest); + f = popen(cmd, "r"); + if (!f) + return -errno; + + fscanf(f, "%d", &nr_cpus); + pclose(f); + + return nr_cpus; +} + +static int attach_guest_fifos(const char *guest, int nr_cpus) +{ + const char *cmd_fmt = + "virsh attach-device --config '%s' '%s' >/dev/null 2>/dev/null"; + const char *xml_fmt = + "<channel type='pipe'>\n" + " <source path='%s'/>\n" + " <target type='virtio' name='%s%d'/>\n" + "</channel>"; + char tmp_path[PATH_MAX], path[PATH_MAX]; + char cmd[PATH_MAX], xml[PATH_MAX]; + int i, fd, ret = 0; + +#ifdef __ANDROID__ + strcpy(tmp_path, "/data/local/tmp/pipexmlXXXXXX"); +#else /* !__ANDROID__ */ + strcpy(tmp_path, "/tmp/pipexmlXXXXXX"); +#endif /* __ANDROID__ */ + + fd = mkstemp(tmp_path); + if (fd < 0) + return fd; + + for (i = 0; i < nr_cpus; i++) { + snprintf(path, sizeof(path), GUEST_FIFO_FMT, guest, i); + snprintf(xml, sizeof(xml), xml_fmt, path, GUEST_PIPE_NAME, i); + pwrite(fd, xml, strlen(xml), 0); + + snprintf(cmd, sizeof(cmd), cmd_fmt, guest, tmp_path); + errno = 0; + if (system(cmd) != 0) { + ret = -errno; + break; + } + } + + close(fd); + unlink(tmp_path); + + return ret; +} + +static void do_setup_guest(const char *guest, int nr_cpus, + mode_t mode, gid_t gid, bool attach) +{ + gid_t save_egid; + int ret; + + if (gid != -1) { + save_egid = getegid(); + ret = setegid(gid); + if (ret < 0) + die("failed to set effective group ID"); + } + + ret = make_guest_dir(guest); + if (ret < 0) + die("failed to create guest directory for %s", guest); + + ret = make_guest_fifos(guest, nr_cpus, mode); + if (ret < 0) + die("failed to create FIFOs for %s", guest); + + if (attach) { + ret = attach_guest_fifos(guest, nr_cpus); + if (ret < 0) + die("failed to attach FIFOs to %s", guest); + } + + if (gid != -1) { + ret = setegid(save_egid); + if (ret < 0) + die("failed to restore effective group ID"); + } +} + +void trace_setup_guest(int argc, char **argv) +{ + bool attach = false; + struct group *group; + mode_t mode = 0660; + int nr_cpus = -1; + gid_t gid = -1; + char *guest; + + if (argc < 2) + usage(argv); + + if (strcmp(argv[1], "setup-guest") != 0) + usage(argv); + + for (;;) { + int c, option_index = 0; + static struct option long_options[] = { + {"help", no_argument, NULL, '?'}, + {NULL, 0, NULL, 0} + }; + + c = getopt_long(argc-1, argv+1, "+hc:p:g:a", + long_options, &option_index); + if (c == -1) + break; + switch (c) { + case 'h': + usage(argv); + break; + case 'c': + nr_cpus = atoi(optarg); + break; + case 'p': + mode = strtol(optarg, NULL, 8); + break; + case 'g': + group = getgrnam(optarg); + if (!group) + die("group %s does not exist", optarg); + gid = group->gr_gid; + break; + case 'a': + attach = true; + break; + default: + usage(argv); + } + } + + if (optind != argc-2) + usage(argv); + + guest = argv[optind+1]; + + if (nr_cpus <= 0) + nr_cpus = get_guest_cpu_count(guest); + + if (nr_cpus <= 0) + die("invalid number of cpus for guest %s", guest); + + do_setup_guest(guest, nr_cpus, mode, gid, attach); +} |