lsblk: move udev and blkid stuff to lsblk-properties.c
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
baad6dcc6e
commit
ccafadb7c5
4 changed files with 210 additions and 193 deletions
|
@ -80,6 +80,7 @@ dist_man_MANS += misc-utils/lsblk.8
|
|||
lsblk_SOURCES = \
|
||||
misc-utils/lsblk.c \
|
||||
misc-utils/lsblk-mnt.c \
|
||||
misc-utils/lsblk-properties.c \
|
||||
misc-utils/lsblk.h
|
||||
lsblk_LDADD = $(LDADD) libblkid.la libmount.la libcommon.la libsmartcols.la
|
||||
lsblk_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir) -I$(ul_libmount_incdir) -I$(ul_libsmartcols_incdir)
|
||||
|
|
188
misc-utils/lsblk-properties.c
Normal file
188
misc-utils/lsblk-properties.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
|
||||
#include <blkid.h>
|
||||
|
||||
#ifdef HAVE_LIBUDEV
|
||||
# include <libudev.h>
|
||||
#endif
|
||||
|
||||
#include "c.h"
|
||||
#include "xalloc.h"
|
||||
#include "mangle.h"
|
||||
|
||||
#include "lsblk.h"
|
||||
|
||||
#ifdef HAVE_LIBUDEV
|
||||
static struct udev *udev;
|
||||
#endif
|
||||
|
||||
void lsblk_device_free_properties(struct lsblk_devprop *p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
free(p->fstype);
|
||||
free(p->uuid);
|
||||
free(p->ptuuid);
|
||||
free(p->pttype);
|
||||
free(p->label);
|
||||
free(p->parttype);
|
||||
free(p->partuuid);
|
||||
free(p->partlabel);
|
||||
free(p->wwn);
|
||||
free(p->serial);
|
||||
free(p->model);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
#ifndef HAVE_LIBUDEV
|
||||
static struct lsblk_devprop *get_properties_by_udev(struct blkdev_cxt *cxt
|
||||
__attribute__((__unused__)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static struct lsblk_devprop *get_properties_by_udev(struct blkdev_cxt *cxt)
|
||||
{
|
||||
struct udev_device *dev;
|
||||
|
||||
if (cxt->udev_requested)
|
||||
return cxt->properties;
|
||||
|
||||
if (lsblk->sysroot)
|
||||
goto done;
|
||||
if (!udev)
|
||||
udev = udev_new(); /* global handler */
|
||||
if (!udev)
|
||||
goto done;
|
||||
|
||||
dev = udev_device_new_from_subsystem_sysname(udev, "block", cxt->name);
|
||||
if (dev) {
|
||||
const char *data;
|
||||
struct lsblk_devprop *prop;
|
||||
|
||||
if (cxt->properties)
|
||||
lsblk_device_free_properties(cxt->properties);
|
||||
prop = cxt->properties = xcalloc(1, sizeof(*cxt->properties));
|
||||
|
||||
if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) {
|
||||
prop->label = xstrdup(data);
|
||||
unhexmangle_string(prop->label);
|
||||
}
|
||||
if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) {
|
||||
prop->uuid = xstrdup(data);
|
||||
unhexmangle_string(prop->uuid);
|
||||
}
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_UUID")))
|
||||
prop->ptuuid = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_TYPE")))
|
||||
prop->pttype = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) {
|
||||
prop->partlabel = xstrdup(data);
|
||||
unhexmangle_string(prop->partlabel);
|
||||
}
|
||||
if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
|
||||
prop->fstype = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_TYPE")))
|
||||
prop->parttype = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID")))
|
||||
prop->partuuid = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_FLAGS")))
|
||||
prop->partflags = xstrdup(data);
|
||||
|
||||
data = udev_device_get_property_value(dev, "ID_WWN_WITH_EXTENSION");
|
||||
if (!data)
|
||||
data = udev_device_get_property_value(dev, "ID_WWN");
|
||||
if (data)
|
||||
prop->wwn = xstrdup(data);
|
||||
|
||||
if ((data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT")))
|
||||
prop->serial = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_MODEL")))
|
||||
prop->model = xstrdup(data);
|
||||
|
||||
udev_device_unref(dev);
|
||||
DBG(DEV, ul_debugobj(cxt, "%s: found udev properties", cxt->name));
|
||||
}
|
||||
|
||||
done:
|
||||
cxt->udev_requested = 1;
|
||||
return cxt->properties;
|
||||
}
|
||||
#endif /* HAVE_LIBUDEV */
|
||||
|
||||
static struct lsblk_devprop *get_properties_by_blkid(struct blkdev_cxt *cxt)
|
||||
{
|
||||
blkid_probe pr = NULL;
|
||||
|
||||
if (cxt->blkid_requested)
|
||||
return cxt->properties;
|
||||
|
||||
if (!cxt->size)
|
||||
goto done;
|
||||
if (getuid() != 0)
|
||||
goto done;; /* no permissions to read from the device */
|
||||
|
||||
pr = blkid_new_probe_from_filename(cxt->filename);
|
||||
if (!pr)
|
||||
goto done;
|
||||
|
||||
blkid_probe_enable_superblocks(pr, 1);
|
||||
blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
|
||||
BLKID_SUBLKS_UUID |
|
||||
BLKID_SUBLKS_TYPE);
|
||||
blkid_probe_enable_partitions(pr, 1);
|
||||
blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
|
||||
|
||||
if (!blkid_do_safeprobe(pr)) {
|
||||
const char *data = NULL;
|
||||
struct lsblk_devprop *prop;
|
||||
|
||||
if (cxt->properties)
|
||||
lsblk_device_free_properties(cxt->properties);
|
||||
prop = cxt->properties = xcalloc(1, sizeof(*cxt->properties));
|
||||
|
||||
if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
|
||||
prop->fstype = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
|
||||
prop->uuid = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PTUUID", &data, NULL))
|
||||
prop->ptuuid = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PTTYPE", &data, NULL))
|
||||
prop->pttype = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
|
||||
prop->label = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &data, NULL))
|
||||
prop->parttype = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL))
|
||||
prop->partuuid = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL))
|
||||
prop->partlabel = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_FLAGS", &data, NULL))
|
||||
prop->partflags = xstrdup(data);
|
||||
|
||||
DBG(DEV, ul_debugobj(cxt, "%s: found blkid properties", cxt->name));
|
||||
}
|
||||
|
||||
done:
|
||||
blkid_free_probe(pr);
|
||||
|
||||
cxt->blkid_requested = 1;
|
||||
return cxt->properties;
|
||||
}
|
||||
|
||||
struct lsblk_devprop *lsblk_device_get_properties(struct blkdev_cxt *cxt)
|
||||
{
|
||||
struct lsblk_devprop *p = get_properties_by_udev(cxt);
|
||||
|
||||
if (!p)
|
||||
p = get_properties_by_blkid(cxt);
|
||||
return p;
|
||||
}
|
||||
|
||||
void lsblk_properties_deinit(void)
|
||||
{
|
||||
#ifdef HAVE_LIBUDEV
|
||||
udev_unref(udev);
|
||||
#endif
|
||||
}
|
|
@ -36,15 +36,9 @@
|
|||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <blkid.h>
|
||||
#include <libmount.h>
|
||||
|
||||
#ifdef HAVE_LIBUDEV
|
||||
#include <libudev.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "c.h"
|
||||
#include "pathnames.h"
|
||||
|
@ -55,7 +49,6 @@
|
|||
#include "strutils.h"
|
||||
#include "sysfs.h"
|
||||
#include "closestream.h"
|
||||
#include "mangle.h"
|
||||
#include "optutils.h"
|
||||
|
||||
#include "lsblk.h"
|
||||
|
@ -245,10 +238,6 @@ static size_t nexcludes;
|
|||
static int includes[256];
|
||||
static size_t nincludes;
|
||||
|
||||
#ifdef HAVE_LIBUDEV
|
||||
static struct udev *udev;
|
||||
#endif
|
||||
|
||||
static void lsblk_init_debug(void)
|
||||
{
|
||||
__UL_INIT_DEBUG_FROM_ENV(lsblk, LSBLK_DEBUG_, 0, LSBLK_DEBUG);
|
||||
|
@ -328,26 +317,6 @@ static int column_id_to_number(int id)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static void free_device_properties(struct lsblk_devprop *p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
free(p->fstype);
|
||||
free(p->uuid);
|
||||
free(p->ptuuid);
|
||||
free(p->pttype);
|
||||
free(p->label);
|
||||
free(p->parttype);
|
||||
free(p->partuuid);
|
||||
free(p->partlabel);
|
||||
free(p->wwn);
|
||||
free(p->serial);
|
||||
free(p->model);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
static void reset_blkdev_cxt(struct blkdev_cxt *cxt)
|
||||
{
|
||||
if (!cxt)
|
||||
|
@ -360,7 +329,7 @@ static void reset_blkdev_cxt(struct blkdev_cxt *cxt)
|
|||
free(cxt->filename);
|
||||
free(cxt->mountpoint);
|
||||
|
||||
free_device_properties(cxt->properties);
|
||||
lsblk_device_free_properties(cxt->properties);
|
||||
ul_unref_path(cxt->sysfs);
|
||||
|
||||
memset(cxt, 0, sizeof(*cxt));
|
||||
|
@ -403,151 +372,6 @@ static char *get_device_path(struct blkdev_cxt *cxt)
|
|||
return xstrdup(path);
|
||||
}
|
||||
|
||||
#ifndef HAVE_LIBUDEV
|
||||
static struct lsblk_devprop *get_properties_by_udev(struct blkdev_cxt *cxt
|
||||
__attribute__((__unused__)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static struct lsblk_devprop *get_properties_by_udev(struct blkdev_cxt *cxt)
|
||||
{
|
||||
struct udev_device *dev;
|
||||
|
||||
if (cxt->udev_requested)
|
||||
return cxt->properties;
|
||||
|
||||
if (lsblk->sysroot)
|
||||
goto done;
|
||||
if (!udev)
|
||||
udev = udev_new(); /* global handler */
|
||||
if (!udev)
|
||||
goto done;
|
||||
|
||||
dev = udev_device_new_from_subsystem_sysname(udev, "block", cxt->name);
|
||||
if (dev) {
|
||||
const char *data;
|
||||
struct lsblk_devprop *prop;
|
||||
|
||||
if (cxt->properties)
|
||||
free_device_properties(cxt->properties);
|
||||
prop = cxt->properties = xcalloc(1, sizeof(*cxt->properties));
|
||||
|
||||
if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) {
|
||||
prop->label = xstrdup(data);
|
||||
unhexmangle_string(prop->label);
|
||||
}
|
||||
if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) {
|
||||
prop->uuid = xstrdup(data);
|
||||
unhexmangle_string(prop->uuid);
|
||||
}
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_UUID")))
|
||||
prop->ptuuid = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_TYPE")))
|
||||
prop->pttype = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) {
|
||||
prop->partlabel = xstrdup(data);
|
||||
unhexmangle_string(prop->partlabel);
|
||||
}
|
||||
if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
|
||||
prop->fstype = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_TYPE")))
|
||||
prop->parttype = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID")))
|
||||
prop->partuuid = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_FLAGS")))
|
||||
prop->partflags = xstrdup(data);
|
||||
|
||||
data = udev_device_get_property_value(dev, "ID_WWN_WITH_EXTENSION");
|
||||
if (!data)
|
||||
data = udev_device_get_property_value(dev, "ID_WWN");
|
||||
if (data)
|
||||
prop->wwn = xstrdup(data);
|
||||
|
||||
if ((data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT")))
|
||||
prop->serial = xstrdup(data);
|
||||
if ((data = udev_device_get_property_value(dev, "ID_MODEL")))
|
||||
prop->model = xstrdup(data);
|
||||
|
||||
udev_device_unref(dev);
|
||||
DBG(DEV, ul_debugobj(cxt, "%s: found udev properties", cxt->name));
|
||||
}
|
||||
|
||||
done:
|
||||
cxt->udev_requested = 1;
|
||||
return cxt->properties;
|
||||
}
|
||||
#endif /* HAVE_LIBUDEV */
|
||||
|
||||
static struct lsblk_devprop *get_properties_by_blkid(struct blkdev_cxt *cxt)
|
||||
{
|
||||
blkid_probe pr = NULL;
|
||||
|
||||
if (cxt->blkid_requested)
|
||||
return cxt->properties;
|
||||
|
||||
if (!cxt->size)
|
||||
goto done;
|
||||
if (getuid() != 0)
|
||||
goto done;; /* no permissions to read from the device */
|
||||
|
||||
pr = blkid_new_probe_from_filename(cxt->filename);
|
||||
if (!pr)
|
||||
goto done;
|
||||
|
||||
blkid_probe_enable_superblocks(pr, 1);
|
||||
blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
|
||||
BLKID_SUBLKS_UUID |
|
||||
BLKID_SUBLKS_TYPE);
|
||||
blkid_probe_enable_partitions(pr, 1);
|
||||
blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
|
||||
|
||||
if (!blkid_do_safeprobe(pr)) {
|
||||
const char *data = NULL;
|
||||
struct lsblk_devprop *prop;
|
||||
|
||||
if (cxt->properties)
|
||||
free_device_properties(cxt->properties);
|
||||
prop = cxt->properties = xcalloc(1, sizeof(*cxt->properties));
|
||||
|
||||
if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
|
||||
prop->fstype = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
|
||||
prop->uuid = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PTUUID", &data, NULL))
|
||||
prop->ptuuid = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PTTYPE", &data, NULL))
|
||||
prop->pttype = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
|
||||
prop->label = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &data, NULL))
|
||||
prop->parttype = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL))
|
||||
prop->partuuid = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL))
|
||||
prop->partlabel = xstrdup(data);
|
||||
if (!blkid_probe_lookup_value(pr, "PART_ENTRY_FLAGS", &data, NULL))
|
||||
prop->partflags = xstrdup(data);
|
||||
|
||||
DBG(DEV, ul_debugobj(cxt, "%s: found blkid properties", cxt->name));
|
||||
}
|
||||
|
||||
done:
|
||||
blkid_free_probe(pr);
|
||||
|
||||
cxt->blkid_requested = 1;
|
||||
return cxt->properties;
|
||||
}
|
||||
|
||||
static struct lsblk_devprop *get_device_properties(struct blkdev_cxt *cxt)
|
||||
{
|
||||
struct lsblk_devprop *p = get_properties_by_udev(cxt);
|
||||
|
||||
if (!p)
|
||||
p = get_properties_by_blkid(cxt);
|
||||
return p;
|
||||
}
|
||||
|
||||
static int is_readonly_device(struct blkdev_cxt *cxt)
|
||||
{
|
||||
int fd, ro = 0;
|
||||
|
@ -913,7 +737,7 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
|
|||
set_sortdata_u64(ln, col, makedev(cxt->maj, cxt->min));
|
||||
break;
|
||||
case COL_FSTYPE:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->fstype)
|
||||
str = xstrdup(prop->fstype);
|
||||
break;
|
||||
|
@ -927,47 +751,47 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
|
|||
str = xstrdup(lsblk_device_get_mountpoint(cxt));
|
||||
break;
|
||||
case COL_LABEL:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->label)
|
||||
str = xstrdup(prop->label);
|
||||
break;
|
||||
case COL_UUID:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->uuid)
|
||||
str = xstrdup(prop->uuid);
|
||||
break;
|
||||
case COL_PTUUID:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->ptuuid)
|
||||
str = xstrdup(prop->ptuuid);
|
||||
break;
|
||||
case COL_PTTYPE:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->pttype)
|
||||
str = xstrdup(prop->pttype);
|
||||
break;
|
||||
case COL_PARTTYPE:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->parttype)
|
||||
str = xstrdup(prop->parttype);
|
||||
break;
|
||||
case COL_PARTLABEL:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->partlabel)
|
||||
str = xstrdup(prop->partlabel);
|
||||
break;
|
||||
case COL_PARTUUID:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->partuuid)
|
||||
str = xstrdup(prop->partuuid);
|
||||
break;
|
||||
case COL_PARTFLAGS:
|
||||
prop = get_device_properties(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->partflags)
|
||||
str = xstrdup(prop->partflags);
|
||||
break;
|
||||
case COL_WWN:
|
||||
prop = get_properties_by_udev(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->wwn)
|
||||
str = xstrdup(prop->wwn);
|
||||
break;
|
||||
|
@ -997,7 +821,7 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
|
|||
break;
|
||||
case COL_MODEL:
|
||||
if (!cxt->partition && cxt->nslaves == 0) {
|
||||
prop = get_properties_by_udev(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->model)
|
||||
str = xstrdup(prop->model);
|
||||
else
|
||||
|
@ -1006,7 +830,7 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
|
|||
break;
|
||||
case COL_SERIAL:
|
||||
if (!cxt->partition && cxt->nslaves == 0) {
|
||||
prop = get_properties_by_udev(cxt);
|
||||
prop = lsblk_device_get_properties(cxt);
|
||||
if (prop && prop->serial)
|
||||
str = xstrdup(prop->serial);
|
||||
else
|
||||
|
@ -1986,9 +1810,8 @@ leave:
|
|||
|
||||
scols_unref_table(lsblk->table);
|
||||
|
||||
#ifdef HAVE_LIBUDEV
|
||||
udev_unref(udev);
|
||||
#endif
|
||||
lsblk_mnt_deinit();
|
||||
lsblk_properties_deinit();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -103,4 +103,9 @@ extern void lsblk_mnt_deinit(void);
|
|||
|
||||
extern char *lsblk_device_get_mountpoint(struct blkdev_cxt *cxt);
|
||||
|
||||
/* lsblk-properties.c */
|
||||
extern void lsblk_device_free_properties(struct lsblk_devprop *p);
|
||||
extern struct lsblk_devprop *lsblk_device_get_properties(struct blkdev_cxt *cxt);
|
||||
extern void lsblk_properties_deinit(void);
|
||||
|
||||
#endif /* UTIL_LINUX_LSBLK_H */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue