mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
Extract the duplicated generic netlink boilerplate (netlink_open,
send_request, get_family_id, and NLA walker macros) from cgroupstats.c and
taskstats_fill_stats_tgid.c into a shared netlink_helper.{h,c}.
Link: https://lore.kernel.org/a2adf27308b5cd90d50b59e8519b87da49486bee.1783876192.git.cyyzero16@gmail.com
Signed-off-by: Yiyang Chen <cyyzero16@gmail.com>
Suggested-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: Balbir Singh <balbirs@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
117 lines
2.5 KiB
C
117 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <linux/genetlink.h>
|
|
|
|
#include "netlink_helper.h"
|
|
|
|
int netlink_open(void)
|
|
{
|
|
struct timeval tv = { .tv_sec = ACCT_RCV_TIMEOUT_SEC };
|
|
struct sockaddr_nl addr = {
|
|
.nl_family = AF_NETLINK,
|
|
.nl_pid = getpid(),
|
|
};
|
|
int fd;
|
|
|
|
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
|
|
if (fd < 0)
|
|
return -errno;
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
|
|
int err = -errno;
|
|
|
|
close(fd);
|
|
return err;
|
|
}
|
|
|
|
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
int err = -errno;
|
|
|
|
close(fd);
|
|
return err;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
int send_request(int fd, void *buf, size_t len)
|
|
{
|
|
struct sockaddr_nl addr = {
|
|
.nl_family = AF_NETLINK,
|
|
};
|
|
|
|
if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Resolve the generic netlink family ID for @name.
|
|
* Returns the family ID (>= 0) on success, negative errno on failure.
|
|
*/
|
|
int get_family_id(int fd, const char *name)
|
|
{
|
|
struct {
|
|
struct nlmsghdr nlh;
|
|
struct genlmsghdr genl;
|
|
char buf[256];
|
|
} req = { 0 };
|
|
char resp[8192];
|
|
struct nlmsghdr *nlh;
|
|
struct genlmsghdr *genl;
|
|
struct nlattr *na;
|
|
int len;
|
|
int rem;
|
|
int ret;
|
|
|
|
req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
|
|
req.nlh.nlmsg_type = GENL_ID_CTRL;
|
|
req.nlh.nlmsg_flags = NLM_F_REQUEST;
|
|
req.nlh.nlmsg_seq = 1;
|
|
req.nlh.nlmsg_pid = getpid();
|
|
|
|
req.genl.cmd = CTRL_CMD_GETFAMILY;
|
|
req.genl.version = 1;
|
|
|
|
na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
|
|
na->nla_type = CTRL_ATTR_FAMILY_NAME;
|
|
na->nla_len = NLA_HDRLEN + strlen(name) + 1;
|
|
memcpy(nla_data(na), name, strlen(name) + 1);
|
|
req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
|
|
|
|
ret = send_request(fd, &req, req.nlh.nlmsg_len);
|
|
if (ret)
|
|
return ret;
|
|
|
|
len = recv(fd, resp, sizeof(resp), 0);
|
|
if (len < 0)
|
|
return -errno;
|
|
|
|
for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
|
|
nlh = NLMSG_NEXT(nlh, len)) {
|
|
if (nlh->nlmsg_type == NLMSG_ERROR) {
|
|
struct nlmsgerr *err = NLMSG_DATA(nlh);
|
|
|
|
return err->error ? err->error : -ENOENT;
|
|
}
|
|
|
|
genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
|
|
rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
|
|
na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
|
|
while (nla_ok(na, rem)) {
|
|
if (na->nla_type == CTRL_ATTR_FAMILY_ID)
|
|
return *(uint16_t *)nla_data(na);
|
|
na = nla_next(na, &rem);
|
|
}
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|