Merge branch 'lsfd--misc-tun' of https://github.com/masatake/util-linux
* 'lsfd--misc-tun' of https://github.com/masatake/util-linux: tests: (lsfd) add a case testing TUN.IFACE column tests: (mkfds) add a factor for opening tun device lsfd: add TUN.IFFACE, a column for interfaces behind tun devices lsfd: (refactor) move miscdev specific code to cdev_misc_ops lsfd: (refactor) make the way to handle character devices extensible lsfd: (refactor) introduce a content data type for char devices
This commit is contained in:
commit
3e2e4c32e8
7 changed files with 403 additions and 40 deletions
|
@ -33,70 +33,74 @@ struct miscdev {
|
|||
char *name;
|
||||
};
|
||||
|
||||
struct cdev {
|
||||
struct file file;
|
||||
const char *devdrv;
|
||||
const struct cdev_ops *cdev_ops;
|
||||
void *cdev_data;
|
||||
};
|
||||
|
||||
struct cdev_ops {
|
||||
const struct cdev_ops *parent;
|
||||
bool (*probe)(const struct cdev *);
|
||||
char * (*get_name)(struct cdev *);
|
||||
bool (*fill_column)(struct proc *,
|
||||
struct cdev *,
|
||||
struct libscols_line *,
|
||||
int,
|
||||
size_t,
|
||||
char **);
|
||||
void (*init)(const struct cdev *);
|
||||
void (*free)(const struct cdev *);
|
||||
int (*handle_fdinfo)(struct cdev *, const char *, const char *);
|
||||
};
|
||||
|
||||
static bool cdev_fill_column(struct proc *proc __attribute__((__unused__)),
|
||||
struct file *file __attribute__((__unused__)),
|
||||
struct file *file,
|
||||
struct libscols_line *ln,
|
||||
int column_id,
|
||||
size_t column_index)
|
||||
{
|
||||
struct cdev *cdev = (struct cdev *)file;
|
||||
const struct cdev_ops *ops = cdev->cdev_ops;
|
||||
char *str = NULL;
|
||||
const char *devdrv;
|
||||
const char *miscdev;
|
||||
|
||||
switch(column_id) {
|
||||
case COL_NAME:
|
||||
if (cdev->cdev_ops->get_name) {
|
||||
str = cdev->cdev_ops->get_name(cdev);
|
||||
if (str)
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
case COL_TYPE:
|
||||
if (scols_line_set_data(ln, column_index, "CHR"))
|
||||
err(EXIT_FAILURE, _("failed to add output data"));
|
||||
return true;
|
||||
case COL_MISCDEV:
|
||||
devdrv = get_chrdrv(major(file->stat.st_rdev));
|
||||
if (devdrv && strcmp(devdrv, "misc") == 0) {
|
||||
miscdev = get_miscdev(minor(file->stat.st_rdev));
|
||||
if (miscdev)
|
||||
str = xstrdup(miscdev);
|
||||
else
|
||||
xasprintf(&str, "%u",
|
||||
minor(file->stat.st_rdev));
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
case COL_DEVTYPE:
|
||||
if (scols_line_set_data(ln, column_index,
|
||||
"char"))
|
||||
err(EXIT_FAILURE, _("failed to add output data"));
|
||||
return true;
|
||||
case COL_CHRDRV:
|
||||
devdrv = get_chrdrv(major(file->stat.st_rdev));
|
||||
if (devdrv)
|
||||
str = xstrdup(devdrv);
|
||||
if (cdev->devdrv)
|
||||
str = xstrdup(cdev->devdrv);
|
||||
else
|
||||
xasprintf(&str, "%u",
|
||||
major(file->stat.st_rdev));
|
||||
break;
|
||||
case COL_SOURCE:
|
||||
devdrv = get_chrdrv(major(file->stat.st_rdev));
|
||||
miscdev = NULL;
|
||||
if (devdrv && strcmp(devdrv, "misc") == 0)
|
||||
miscdev = get_miscdev(minor(file->stat.st_rdev));
|
||||
if (devdrv) {
|
||||
if (miscdev) {
|
||||
xasprintf(&str, "misc:%s", miscdev);
|
||||
} else {
|
||||
xasprintf(&str, "%s:%u", devdrv,
|
||||
minor(file->stat.st_rdev));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case COL_MAJMIN:
|
||||
xasprintf(&str, "%u:%u",
|
||||
major(file->stat.st_rdev),
|
||||
minor(file->stat.st_rdev));
|
||||
break;
|
||||
default:
|
||||
while (ops) {
|
||||
if (ops->fill_column
|
||||
&& ops->fill_column(proc, cdev, ln,
|
||||
column_id, column_index, &str))
|
||||
goto out;
|
||||
ops = ops->parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
out:
|
||||
if (!str)
|
||||
err(EXIT_FAILURE, _("failed to add output data"));
|
||||
if (scols_line_refer_data(ln, column_index, str))
|
||||
|
@ -168,11 +172,220 @@ const char *get_miscdev(unsigned long minor)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* generic (fallback implementation)
|
||||
*/
|
||||
static bool cdev_generic_probe(const struct cdev *cdev __attribute__((__unused__))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cdev_generic_fill_column(struct proc *proc __attribute__((__unused__)),
|
||||
struct cdev *cdev,
|
||||
struct libscols_line *ln __attribute__((__unused__)),
|
||||
int column_id,
|
||||
size_t column_index __attribute__((__unused__)),
|
||||
char **str)
|
||||
{
|
||||
struct file *file = &cdev->file;
|
||||
|
||||
switch(column_id) {
|
||||
case COL_SOURCE:
|
||||
if (cdev->devdrv) {
|
||||
xasprintf(str, "%s:%u", cdev->devdrv,
|
||||
minor(file->stat.st_rdev));
|
||||
return true;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case COL_MAJMIN:
|
||||
xasprintf(str, "%u:%u",
|
||||
major(file->stat.st_rdev),
|
||||
minor(file->stat.st_rdev));
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct cdev_ops cdev_generic_ops = {
|
||||
.probe = cdev_generic_probe,
|
||||
.fill_column = cdev_generic_fill_column,
|
||||
};
|
||||
|
||||
/*
|
||||
* misc device driver
|
||||
*/
|
||||
static bool cdev_misc_probe(const struct cdev *cdev) {
|
||||
return cdev->devdrv && strcmp(cdev->devdrv, "misc") == 0;
|
||||
}
|
||||
|
||||
static bool cdev_misc_fill_column(struct proc *proc __attribute__((__unused__)),
|
||||
struct cdev *cdev,
|
||||
struct libscols_line *ln __attribute__((__unused__)),
|
||||
int column_id,
|
||||
size_t column_index __attribute__((__unused__)),
|
||||
char **str)
|
||||
{
|
||||
struct file *file = &cdev->file;
|
||||
const char *miscdev;
|
||||
|
||||
switch(column_id) {
|
||||
case COL_MISCDEV:
|
||||
miscdev = get_miscdev(minor(file->stat.st_rdev));
|
||||
if (miscdev)
|
||||
*str = xstrdup(miscdev);
|
||||
else
|
||||
xasprintf(str, "%u",
|
||||
minor(file->stat.st_rdev));
|
||||
return true;
|
||||
case COL_SOURCE:
|
||||
miscdev = get_miscdev(minor(file->stat.st_rdev));
|
||||
if (miscdev)
|
||||
xasprintf(str, "misc:%s", miscdev);
|
||||
else
|
||||
xasprintf(str, "misc:%u",
|
||||
minor(file->stat.st_rdev));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct cdev_ops cdev_misc_ops = {
|
||||
.parent = &cdev_generic_ops,
|
||||
.probe = cdev_misc_probe,
|
||||
.fill_column = cdev_misc_fill_column,
|
||||
};
|
||||
|
||||
/*
|
||||
* tun devcie driver
|
||||
*/
|
||||
static bool cdev_tun_probe(const struct cdev *cdev)
|
||||
{
|
||||
const char *miscdev;
|
||||
|
||||
if ((!cdev->devdrv) || strcmp(cdev->devdrv, "misc"))
|
||||
return false;
|
||||
|
||||
miscdev = get_miscdev(minor(cdev->file.stat.st_rdev));
|
||||
if (miscdev && strcmp(miscdev, "tun") == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void cdev_tun_free(const struct cdev *cdev)
|
||||
{
|
||||
if (cdev->cdev_data)
|
||||
free(cdev->cdev_data);
|
||||
}
|
||||
|
||||
static char * cdev_tun_get_name(struct cdev *cdev)
|
||||
{
|
||||
char *str = NULL;
|
||||
|
||||
if (cdev->cdev_data == NULL)
|
||||
return NULL;
|
||||
|
||||
xasprintf(&str, "iface=%s", (const char *)cdev->cdev_data);
|
||||
return str;
|
||||
}
|
||||
|
||||
static bool cdev_tun_fill_column(struct proc *proc __attribute__((__unused__)),
|
||||
struct cdev *cdev,
|
||||
struct libscols_line *ln __attribute__((__unused__)),
|
||||
int column_id,
|
||||
size_t column_index __attribute__((__unused__)),
|
||||
char **str)
|
||||
{
|
||||
switch(column_id) {
|
||||
case COL_MISCDEV:
|
||||
*str = xstrdup("tun");
|
||||
return true;
|
||||
case COL_SOURCE:
|
||||
*str = xstrdup("misc:tun");
|
||||
return true;
|
||||
case COL_TUN_IFACE:
|
||||
if (cdev->cdev_data) {
|
||||
*str = xstrdup(cdev->cdev_data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cdev_tun_handle_fdinfo(struct cdev *cdev, const char *key, const char *val)
|
||||
{
|
||||
if (strcmp(key, "iff") == 0 && cdev->cdev_data == NULL) {
|
||||
cdev->cdev_data = xstrdup(val);
|
||||
return 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct cdev_ops cdev_tun_ops = {
|
||||
.parent = &cdev_misc_ops,
|
||||
.probe = cdev_tun_probe,
|
||||
.free = cdev_tun_free,
|
||||
.get_name = cdev_tun_get_name,
|
||||
.fill_column = cdev_tun_fill_column,
|
||||
.handle_fdinfo = cdev_tun_handle_fdinfo,
|
||||
};
|
||||
|
||||
static const struct cdev_ops *cdev_ops[] = {
|
||||
&cdev_tun_ops,
|
||||
&cdev_misc_ops,
|
||||
&cdev_generic_ops /* This must be at the end. */
|
||||
};
|
||||
|
||||
static const struct cdev_ops *cdev_probe(const struct cdev *cdev)
|
||||
{
|
||||
const struct cdev_ops *r = NULL;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(cdev_ops); i++) {
|
||||
if (cdev_ops[i]->probe(cdev)) {
|
||||
r = cdev_ops[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void init_cdev_content(struct file *file)
|
||||
{
|
||||
struct cdev *cdev = (struct cdev *)file;
|
||||
|
||||
cdev->devdrv = get_chrdrv(major(file->stat.st_rdev));
|
||||
|
||||
cdev->cdev_data = NULL;
|
||||
cdev->cdev_ops = cdev_probe(cdev);
|
||||
if (cdev->cdev_ops->init)
|
||||
cdev->cdev_ops->init(cdev);
|
||||
}
|
||||
|
||||
static void free_cdev_content(struct file *file)
|
||||
{
|
||||
struct cdev *cdev = (struct cdev *)file;
|
||||
|
||||
if (cdev->cdev_ops->free)
|
||||
cdev->cdev_ops->free(cdev);
|
||||
}
|
||||
|
||||
static int cdev_handle_fdinfo(struct file *file, const char *key, const char *value)
|
||||
{
|
||||
struct cdev *cdev = (struct cdev *)file;
|
||||
|
||||
if (cdev->cdev_ops->handle_fdinfo)
|
||||
return cdev->cdev_ops->handle_fdinfo(cdev, key, value);
|
||||
return 0; /* Should be handled in parents */
|
||||
}
|
||||
|
||||
const struct file_class cdev_class = {
|
||||
.super = &file_class,
|
||||
.size = sizeof(struct file),
|
||||
.size = sizeof(struct cdev),
|
||||
.initialize_class = cdev_class_initialize,
|
||||
.finalize_class = cdev_class_finalize,
|
||||
.fill_column = cdev_fill_column,
|
||||
.free_content = NULL,
|
||||
.initialize_content = init_cdev_content,
|
||||
.free_content = free_cdev_content,
|
||||
.handle_fdinfo = cdev_handle_fdinfo,
|
||||
};
|
||||
|
|
|
@ -247,6 +247,9 @@ id=_EVENTFD.ID_
|
|||
inotify:::
|
||||
inodes=_INOTIFY.INODES_
|
||||
+
|
||||
misc:tun:::
|
||||
iface=_TUN.IFACE_
|
||||
+
|
||||
NETLINK:::
|
||||
protocol=_NETLINK.PROTOCOL_[ lport=_NETLINK.LPORT_[ group=_NETLINK.GROUPS_]]
|
||||
+
|
||||
|
@ -426,6 +429,9 @@ Interval.
|
|||
TIMERFD.REMAINING <``number``>::
|
||||
Remaining time.
|
||||
|
||||
TUN.IFACE <``string``>::
|
||||
Network intrface behind the tun device.
|
||||
|
||||
TYPE <``string``>::
|
||||
Cooked version of STTYPE. It is same as STTYPE with exceptions.
|
||||
For SOCK, print the value for SOCK.PROTONAME.
|
||||
|
|
|
@ -312,6 +312,9 @@ static const struct colinfo infos[] = {
|
|||
[COL_TIMERFD_REMAINING]= { "TIMERFD.REMAINING",
|
||||
0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
|
||||
N_("remaining time") },
|
||||
[COL_TUN_IFACE] = { "TUN.IFACE",
|
||||
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
|
||||
N_("network interface behind the tun device") },
|
||||
[COL_TYPE] = { "TYPE",
|
||||
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
|
||||
N_("file type (cooked)") },
|
||||
|
|
|
@ -101,6 +101,7 @@ enum {
|
|||
COL_TIMERFD_CLOCKID,
|
||||
COL_TIMERFD_INTERVAL,
|
||||
COL_TIMERFD_REMAINING,
|
||||
COL_TUN_IFACE,
|
||||
COL_TYPE,
|
||||
COL_UDP_LADDR,
|
||||
COL_UDP_RADDR,
|
||||
|
|
4
tests/expected/lsfd/mkfds-cdev-tun
Normal file
4
tests/expected/lsfd/mkfds-cdev-tun
Normal file
|
@ -0,0 +1,4 @@
|
|||
3 rw- CHR misc:tun
|
||||
ASSOC,MODE,TYPE,SOURCE: 0
|
||||
NAME: 0
|
||||
TUN.IFACE: 0
|
|
@ -25,6 +25,7 @@
|
|||
#include <getopt.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/sockios.h> /* SIOCGSKNS */
|
||||
#include <mqueue.h>
|
||||
|
@ -2466,6 +2467,62 @@ static void *make_signalfd(const struct factory *factory _U_, struct fdesc fdesc
|
|||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* ref. linux/Documentation/networking/tuntap.rst */
|
||||
static void *make_cdev_tun(const struct factory *factory _U_, struct fdesc fdescs[],
|
||||
int argc _U_, char ** argv _U_)
|
||||
{
|
||||
int tfd = open("/dev/net/tun", O_RDWR);
|
||||
struct ifreq ifr;
|
||||
|
||||
if (tfd < 0)
|
||||
err(EXIT_FAILURE, "failed in opening /dev/net/tun");
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
ifr.ifr_flags = IFF_TUN;
|
||||
strcpy(ifr.ifr_name, "mkfds%d");
|
||||
|
||||
if (ioctl(tfd, TUNSETIFF, (void *) &ifr) < 0) {
|
||||
int e = errno;
|
||||
close(tfd);
|
||||
errno = e;
|
||||
err(EXIT_FAILURE, "failed in setting \"lo\" to the tun device");
|
||||
}
|
||||
|
||||
if (tfd != fdescs[0].fd) {
|
||||
if (dup2(tfd, fdescs[0].fd) < 0) {
|
||||
int e = errno;
|
||||
close(tfd);
|
||||
errno = e;
|
||||
err(EXIT_FAILURE, "failed to dup %d -> %d", tfd, fdescs[0].fd);
|
||||
}
|
||||
close(tfd);
|
||||
}
|
||||
|
||||
fdescs[0] = (struct fdesc){
|
||||
.fd = fdescs[0].fd,
|
||||
.close = close_fdesc,
|
||||
.data = NULL
|
||||
};
|
||||
|
||||
return xstrdup(ifr.ifr_name);
|
||||
}
|
||||
|
||||
static void report_cdev_tun(const struct factory *factory _U_,
|
||||
int nth, void *data, FILE *fp)
|
||||
{
|
||||
if (nth == 0) {
|
||||
char *devname = data;
|
||||
fprintf(fp, "%s", devname);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_cdev_tun(const struct factory * factory _U_, void *data)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
#define PARAM_END { .name = NULL, }
|
||||
static const struct factory factories[] = {
|
||||
{
|
||||
|
@ -3139,6 +3196,20 @@ static const struct factory factories[] = {
|
|||
PARAM_END
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cdev-tun",
|
||||
.desc = "open /dev/net/tun",
|
||||
.priv = true,
|
||||
.N = 1,
|
||||
.EX_N = 0,
|
||||
.EX_R = 1,
|
||||
.make = make_cdev_tun,
|
||||
.report = report_cdev_tun,
|
||||
.free = free_cdev_tun,
|
||||
.params = (struct parameter []) {
|
||||
PARAM_END
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static int count_parameters(const struct factory *factory)
|
||||
|
|
65
tests/ts/lsfd/mkfds-cdev-tun
Executable file
65
tests/ts/lsfd/mkfds-cdev-tun
Executable file
|
@ -0,0 +1,65 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2023 Masatake YAMATO <yamato@redhat.com>
|
||||
#
|
||||
# This file is part of util-linux.
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
TS_TOPDIR="${0%/*}/../.."
|
||||
TS_DESC="tun device and interface behind the device"
|
||||
|
||||
. "$TS_TOPDIR"/functions.sh
|
||||
ts_init "$*"
|
||||
ts_skip_nonroot
|
||||
|
||||
[[ -e /dev/net/tun ]] || ts_skip "/dev/net/tun doest not exist"
|
||||
|
||||
ts_check_test_command "$TS_CMD_LSFD"
|
||||
ts_check_test_command "$TS_HELPER_MKFDS"
|
||||
|
||||
ts_cd "$TS_OUTDIR"
|
||||
|
||||
PID=
|
||||
FD=3
|
||||
IFNAME=
|
||||
|
||||
{
|
||||
coproc MKFDS { "$TS_HELPER_MKFDS" cdev-tun $FD ; }
|
||||
if read -u ${MKFDS[0]} PID IFNAME; then
|
||||
EXPR='(FD == '"$FD"')'
|
||||
${TS_CMD_LSFD} -p "${PID}" -n -o ASSOC,MODE,TYPE,SOURCE -Q "${EXPR}"
|
||||
echo 'ASSOC,MODE,TYPE,SOURCE': $?
|
||||
|
||||
output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o NAME -Q "${EXPR}")
|
||||
if [[ "$output" == "iface=$IFNAME" ]]; then
|
||||
echo 'NAME': $?
|
||||
else
|
||||
echo 'NAME': $?
|
||||
echo "expected NAME: iface=$IFNAME"
|
||||
echo "output NAME: $output"
|
||||
fi
|
||||
|
||||
output=$(${TS_CMD_LSFD} -p "${PID}" -n --raw -o TUN.IFACE -Q "${EXPR}")
|
||||
if [[ "$output" == "$IFNAME" ]]; then
|
||||
echo 'TUN.IFACE': $?
|
||||
else
|
||||
echo 'TUN.IFAEC': $?
|
||||
echo "expected TUN.IFACE: $IFNAME"
|
||||
echo "output TUN.IFACE: $output"
|
||||
fi
|
||||
|
||||
kill -CONT ${PID}
|
||||
fi
|
||||
wait ${MKFDS_PID}
|
||||
} > $TS_OUTPUT 2>&1
|
||||
|
||||
ts_finalize
|
Loading…
Add table
Add a link
Reference in a new issue