qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v4 2/3] qga: add implementation of guest-get-disks for Linux


From: Tomáš Golembiovský
Subject: Re: [PATCH v4 2/3] qga: add implementation of guest-get-disks for Linux
Date: Wed, 14 Oct 2020 11:34:40 +0200

On Tue, Oct 13, 2020 at 11:26:45AM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Mon, Oct 12, 2020 at 12:36 PM Tomáš Golembiovský <tgolembi@redhat.com>
> wrote:
> 
> > The command lists all disks (real and virtual) as well as disk
> > partitions. For each disk the list of dependent disks is also listed and
> > /dev path is used as a handle so it can be matched with "name" field of
> > other returned disk entries. For disk partitions the "dependents" list
> > is populated with the the parent device for easier tracking of
> > hierarchy.
> >
> > Example output:
> > {
> >   "return": [
> >     ...
> >     {
> >       "name": "/dev/dm-0",
> >       "partition": false,
> >       "dependents": [
> >         "/dev/sda2"
> >       ],
> >       "alias": "luks-7062202e-5b9b-433e-81e8-6628c40da9f7"
> >     },
> >     {
> >       "name": "/dev/sda2",
> >       "partition": true,
> >       "dependents": [
> >         "/dev/sda"
> >       ]
> >     },
> >     {
> >       "name": "/dev/sda",
> >       "partition": false,
> >       "address": {
> >         "serial": "SAMSUNG_MZ7LN512HCHP-000L1_S1ZKNXAG822493",
> >         "bus-type": "sata",
> >         ...
> >         "dev": "/dev/sda",
> >         "target": 0
> >       },
> >       "dependents": []
> >     },
> >     ...
> >   ]
> > }
> >
> > Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
> > ---
> >  qga/commands-posix.c | 296 +++++++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 285 insertions(+), 11 deletions(-)
> >
> > diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> > index 422144bcff..14683dfbd5 100644
> > --- a/qga/commands-posix.c
> > +++ b/qga/commands-posix.c
> > @@ -1150,13 +1150,27 @@ static void
> > build_guest_fsinfo_for_virtual_device(char const *syspath,
> >      closedir(dir);
> >  }
> >
> > +static bool is_disk_virtual(const char *devpath, Error **errp)
> > +{
> > +    g_autofree char *syspath = realpath(devpath, NULL);
> > +
> > +    if (!syspath) {
> > +        error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
> > +        return false;
> > +    }
> > +    return strstr(syspath, "/devices/virtual/block/") != NULL;
> > +}
> > +
> >  /* Dispatch to functions for virtual/real device */
> >  static void build_guest_fsinfo_for_device(char const *devpath,
> >                                            GuestFilesystemInfo *fs,
> >                                            Error **errp)
> >  {
> > -    char *syspath = realpath(devpath, NULL);
> > +    ERRP_GUARD();
> > +    g_autofree char *syspath = NULL;
> > +    bool is_virtual = false;
> >
> > +    syspath = realpath(devpath, NULL);
> >      if (!syspath) {
> >          error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
> >          return;
> > @@ -1167,16 +1181,281 @@ static void build_guest_fsinfo_for_device(char
> > const *devpath,
> >      }
> >
> >      g_debug("  parse sysfs path '%s'", syspath);
> > -
> > -    if (strstr(syspath, "/devices/virtual/block/")) {
> > +    is_virtual = is_disk_virtual(syspath, errp);
> > +    if (*errp != NULL) {
> > +        return;
> > +    }
> > +    if (is_virtual) {
> >          build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
> >      } else {
> >          build_guest_fsinfo_for_real_device(syspath, fs, errp);
> >      }
> > +}
> > +
> > +#ifdef CONFIG_LIBUDEV
> > +
> > +/*
> > + * Wrapper around build_guest_fsinfo_for_device() for getting just
> > + * the disk address.
> > + */
> > +static GuestDiskAddress *get_disk_address(const char *syspath, Error
> > **errp)
> > +{
> > +    g_autoptr(GuestFilesystemInfo) fs = NULL;
> > +
> > +    fs = g_new0(GuestFilesystemInfo, 1);
> > +    build_guest_fsinfo_for_device(syspath, fs, errp);
> > +    if (fs->disk != NULL) {
> > +        return g_steal_pointer(&fs->disk->value);
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static char *get_alias_for_syspath(const char *syspath)
> > +{
> > +    struct udev *udev = NULL;
> > +    struct udev_device *udevice = NULL;
> > +    char *ret = NULL;
> > +
> > +    udev = udev_new();
> > +    if (udev == NULL) {
> > +        g_debug("failed to query udev");
> > +        goto out;
> > +    }
> > +    udevice = udev_device_new_from_syspath(udev, syspath);
> > +    if (udevice == NULL) {
> > +        g_debug("failed to query udev for path: %s", syspath);
> > +        goto out;
> > +    } else {
> > +        const char *alias = udev_device_get_property_value(
> > +            udevice, "DM_NAME");
> > +        /*
> > +         * NULL means there was an error and empty string means there is
> > no
> > +         * alias. In case of no alias we return NULL instead of empty
> > string.
> > +         */
> > +        if (alias == NULL) {
> > +            g_debug("failed to query udev for device alias for: %s",
> > +                syspath);
> > +        } else if (*alias != 0) {
> > +            ret = g_strdup(alias);
> > +        }
> > +    }
> > +
> > +out:
> > +    udev_unref(udev);
> > +    udev_device_unref(udevice);
> > +    return ret;
> > +}
> > +
> > +static char *get_device_for_syspath(const char *syspath)
> > +{
> > +    struct udev *udev = NULL;
> > +    struct udev_device *udevice = NULL;
> > +    char *ret = NULL;
> > +
> > +    udev = udev_new();
> > +    if (udev == NULL) {
> > +        g_debug("failed to query udev");
> > +        goto out;
> > +    }
> > +    udevice = udev_device_new_from_syspath(udev, syspath);
> > +    if (udevice == NULL) {
> > +        g_debug("failed to query udev for path: %s", syspath);
> > +        goto out;
> > +    } else {
> > +        ret = g_strdup(udev_device_get_devnode(udevice));
> > +    }
> > +
> > +out:
> > +    udev_unref(udev);
> > +    udev_device_unref(udevice);
> > +    return ret;
> > +}
> > +
> > +static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
> > +{
> > +    g_autofree char *deps_dir = NULL;
> > +    const gchar *dep;
> > +    GDir *dp_deps = NULL;
> > +
> > +    /* List dependent disks */
> > +    deps_dir = g_strdup_printf("%s/slaves", disk_dir);
> > +    g_debug("  listing entries in: %s", deps_dir);
> > +    dp_deps = g_dir_open(deps_dir, 0, NULL);
> > +    if (dp_deps == NULL) {
> > +        g_debug("failed to list entries in %s", deps_dir);
> > +        return;
> > +    }
> > +    while ((dep = g_dir_read_name(dp_deps)) != NULL) {
> > +        g_autofree char *dep_dir = NULL;
> > +        strList *dep_item = NULL;
> > +        char *dev_name;
> >
> > -    free(syspath);
> > +        /* Add dependent disks */
> > +        dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
> > +        dev_name = get_device_for_syspath(dep_dir);
> > +        if (dev_name != NULL) {
> > +            g_debug("  adding dependent device: %s", dev_name);
> > +            dep_item = g_new0(strList, 1);
> > +            dep_item->value = dev_name;
> > +            dep_item->next = disk->dependents;
> > +            disk->dependents = dep_item;
> > +        }
> > +    }
> > +    g_dir_close(dp_deps);
> >  }
> >
> > +/*
> > + * Detect partitions subdirectory, name is "<disk_name><number>" or
> > + * "<disk_name>p<number>"
> > + *
> > + * @disk_name -- last component of /sys path (e.g. sda)
> > + * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
> > + * @disk_dev -- device node of the disk (e.g. /dev/sda)
> > + */
> > +static GuestDiskInfoList *get_disk_partitions(
> > +    GuestDiskInfoList *list,
> > +    const char *disk_name, const char *disk_dir,
> > +    const char *disk_dev)
> > +{
> > +    GuestDiskInfoList *item, *ret = list;
> > +    struct dirent *de_disk;
> > +    DIR *dp_disk = NULL;
> > +    size_t len = strlen(disk_name);
> > +
> > +    dp_disk = opendir(disk_dir);
> >
> 
> Any reason to use both readdir and g_dir APIs in the same patch? Maybe use
> g_dir alone?

g_dir_* provides only names of the entries in the other places where
opendir() is used we check also type of the entry (d_type).

Tomas

> 
> +    while ((de_disk = readdir(dp_disk)) != NULL) {
> > +        g_autofree char *partition_dir = NULL;
> > +        char *dev_name;
> > +        GuestDiskInfo *partition;
> > +
> > +        if (!(de_disk->d_type & DT_DIR)) {
> > +            continue;
> > +        }
> > +
> > +        if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
> > +            ((*(de_disk->d_name + len) == 'p' &&
> > +            isdigit(*(de_disk->d_name + len + 1))) ||
> > +                isdigit(*(de_disk->d_name + len))))) {
> > +            continue;
> > +        }
> > +
> > +        partition_dir = g_strdup_printf("%s/%s",
> > +            disk_dir, de_disk->d_name);
> > +        dev_name = get_device_for_syspath(partition_dir);
> > +        if (dev_name == NULL) {
> > +            g_debug("Failed to get device name for syspath: %s",
> > +                disk_dir);
> > +            continue;
> > +        }
> > +        partition = g_new0(GuestDiskInfo, 1);
> > +        partition->name = dev_name;
> > +        partition->partition = true;
> > +        /* Add parent disk as dependent for easier tracking of hierarchy
> > */
> > +        partition->dependents = g_new0(strList, 1);
> > +        partition->dependents->value = g_strdup(disk_dev);
> > +
> > +        item = g_new0(GuestDiskInfoList, 1);
> > +        item->value = partition;
> > +        item->next = ret;
> > +        ret = item;
> > +
> > +    }
> > +    closedir(dp_disk);
> > +
> > +    return ret;
> > +}
> > +
> > +GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> > +{
> > +    GuestDiskInfoList *item, *ret = NULL;
> > +    GuestDiskInfo *disk;
> > +    DIR *dp = NULL;
> > +    struct dirent *de = NULL;
> > +
> > +    g_debug("listing /sys/block directory");
> > +    dp = opendir("/sys/block");
> > +    if (dp == NULL) {
> > +        error_setg_errno(errp, errno, "Can't open directory
> > \"/sys/block\"");
> > +        return NULL;
> > +    }
> > +    while ((de = readdir(dp)) != NULL) {
> > +        g_autofree char *disk_dir = NULL, *line = NULL,
> > +            *size_path = NULL, *deps_dir = NULL;
> > +        char *dev_name;
> > +        Error *local_err = NULL;
> > +        if (de->d_type != DT_LNK) {
> > +            g_debug("  skipping entry: %s", de->d_name);
> > +            continue;
> > +        }
> > +
> > +        /* Check size and skip zero-sized disks */
> > +        g_debug("  checking disk size");
> > +        size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
> > +        if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
> > +            g_debug("  failed to read disk size");
> > +            continue;
> > +        }
> > +        if (g_strcmp0(line, "0\n") == 0) {
> > +            g_debug("  skipping zero-sized disk");
> > +            continue;
> > +        }
> > +
> > +        g_debug("  adding %s", de->d_name);
> > +        disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
> > +        dev_name = get_device_for_syspath(disk_dir);
> > +        if (dev_name == NULL) {
> > +            g_debug("Failed to get device name for syspath: %s",
> > +                disk_dir);
> > +            continue;
> > +        }
> > +        disk = g_new0(GuestDiskInfo, 1);
> > +        disk->name = dev_name;
> > +        disk->partition = false;
> > +        disk->alias = get_alias_for_syspath(disk_dir);
> > +        disk->has_alias = (disk->alias != NULL);
> > +        item = g_new0(GuestDiskInfoList, 1);
> > +        item->value = disk;
> > +        item->next = ret;
> > +        ret = item;
> > +
> > +        /* Get address for non-virtual devices */
> > +        bool is_virtual = is_disk_virtual(disk_dir, &local_err);
> > +        if (local_err != NULL) {
> > +            g_debug("  failed to check disk path, ignoring error: %s",
> > +                error_get_pretty(local_err));
> > +            error_free(local_err);
> > +            local_err = NULL;
> > +            /* Don't try to get the address */
> > +            is_virtual = true;
> > +        }
> > +        if (!is_virtual) {
> > +            disk->address = get_disk_address(disk_dir, &local_err);
> > +            if (local_err != NULL) {
> > +                g_debug("  failed to get device info, ignoring error: %s",
> > +                    error_get_pretty(local_err));
> > +                error_free(local_err);
> > +                local_err = NULL;
> > +            } else if (disk->address != NULL) {
> > +                disk->has_address = true;
> > +            }
> > +        }
> > +
> > +        get_disk_deps(disk_dir, disk);
> > +        ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
> > +    }
> > +    return ret;
> > +}
> > +
> > +#else
> > +
> > +GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> > +{
> > +    error_setg(errp, QERR_UNSUPPORTED);
> > +    return NULL;
> > +}
> > +
> > +#endif
> > +
> >  /* Return a list of the disk device(s)' info which @mount lies on */
> >  static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
> >                                                 Error **errp)
> > @@ -2809,7 +3088,8 @@ GList *ga_command_blacklist_init(GList *blacklist)
> >          const char *list[] = {
> >              "guest-get-fsinfo", "guest-fsfreeze-status",
> >              "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
> > -            "guest-fsfreeze-thaw", "guest-get-fsinfo", NULL};
> > +            "guest-fsfreeze-thaw", "guest-get-fsinfo",
> > +            "guest-get-disks", NULL};
> >          char **p = (char **)list;
> >
> >          while (*p) {
> > @@ -3051,9 +3331,3 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error
> > **errp)
> >
> >      return NULL;
> >  }
> > -
> > -GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> > -{
> > -    error_setg(errp, QERR_UNSUPPORTED);
> > -    return NULL;
> > -}
> > --
> > 2.28.0
> >
> >
> lgtm otherwise
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> -- 
> Marc-André Lureau

-- 
Tomáš Golembiovský <tgolembi@redhat.com>




reply via email to

[Prev in Thread] Current Thread [Next in Thread]