// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
#include <errno.h>
#include <poll.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/types.h>
#include <linux/genetlink.h>
#include <sys/socket.h>
#include "ynl.h"
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*arr))
#define __yerr_msg(yse, _msg...) \
({ \
struct ynl_error *_yse = (yse); \
\
if (_yse) { \
snprintf(_yse->msg, sizeof(_yse->msg) - 1, _msg); \
_yse->msg[sizeof(_yse->msg) - 1] = 0; \
} \
})
#define __yerr_code(yse, _code...) \
({ \
struct ynl_error *_yse = (yse); \
\
if (_yse) { \
_yse->code = _code; \
} \
})
#define __yerr(yse, _code, _msg...) \
({ \
__yerr_msg(yse, _msg); \
__yerr_code(yse, _code); \
})
#define __perr(yse, _msg) __yerr(yse, errno, _msg)
#define yerr_msg(_ys, _msg...) __yerr_msg(&(_ys)->err, _msg)
#define yerr(_ys, _code, _msg...) __yerr(&(_ys)->err, _code, _msg)
#define perr(_ys, _msg) __yerr(&(_ys)->err, errno, _msg)
/* -- Netlink boiler plate */
static bool
ynl_err_walk_is_sel(const struct ynl_policy_nest *policy,
const struct nlattr *attr)
{
unsigned int type = ynl_attr_type(attr);
return policy && type <= policy->max_attr &&
policy->table[type].is_selector;
}
static const struct ynl_policy_nest *
ynl_err_walk_sel_policy(const struct ynl_policy_attr *policy_attr,
const struct nlattr *selector)
{
const struct ynl_policy_nest *policy = policy_attr->nest;
const char *sel;
unsigned int i;
if (!policy_attr->is_submsg)
return policy;
sel = ynl_attr_get_str(selector);
for (i = 0; i <= policy->max_attr; i++) {
if (!strcmp(sel, policy->table[i].name))
return policy->table[i].nest;
}
return NULL;
}
static int
ynl_err_walk_report_one(const struct ynl_policy_nest *policy,
const struct nlattr *selector, unsigned int type,
char *str, int str_sz, int *n)
{
if (!policy) {
if (*n < str_sz)
*n += snprintf(str, str_sz, "!policy");
return 1;
}
if (type > policy->max_attr) {
if (*n < str_sz)
*n += snprintf(str, str_sz, "!oob");
return 1;
}
if (!policy->table[type].name) {
if (*n < str_sz)
*n += snprintf(str, str_sz, "!name");
return 1;
}
if (*n < str_sz) {
int sz;
sz = snprintf(str, str_sz - *n,
".%s", policy->table[type].name);
*n += sz;
str += sz;
}
if (policy->table[type].is_submsg) {
if (!selector) {
if (*n < str_sz)
*n += snprintf(str, str_sz, "(!selector)");
return 1;
}
if (ynl_attr_type(selector) !=
policy->table[type].selector_type) {
if (*n < str_sz)
*n += snprintf(str, str_sz, "(!=selector)");
return 1;
}
if (*n < str_sz)
*n += snprintf(str, str_sz - *n, "(%s)",
ynl_attr_get_str(selector));
}
return 0;
}
static int
ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
const struct ynl_policy_nest *