mirror_iproute2/tc/tc_class.c
Matteo Croce 8589eb4efd treewide: refactor help messages
Every tool in the iproute2 package have one or more function to show
an help message to the user. Some of these functions print the help
line by line with a series of printf call, e.g. ip/xfrm_state.c does
60 fprintf calls.
If we group all the calls to a single one and just concatenate strings,
we save a lot of libc calls and thus object size. The size difference
of the compiled binaries calculated with bloat-o-meter is:

        ip/ip:
        add/remove: 0/0 grow/shrink: 5/15 up/down: 103/-4796 (-4693)
        Total: Before=672591, After=667898, chg -0.70%
        ip/rtmon:
        add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-54 (-54)
        Total: Before=48879, After=48825, chg -0.11%
        tc/tc:
        add/remove: 0/2 grow/shrink: 31/10 up/down: 882/-6133 (-5251)
        Total: Before=351912, After=346661, chg -1.49%
        bridge/bridge:
        add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-459 (-459)
        Total: Before=70502, After=70043, chg -0.65%
        misc/lnstat:
        add/remove: 0/1 grow/shrink: 1/0 up/down: 48/-486 (-438)
        Total: Before=9960, After=9522, chg -4.40%
        tipc/tipc:
        add/remove: 0/0 grow/shrink: 1/1 up/down: 18/-62 (-44)
        Total: Before=79182, After=79138, chg -0.06%

While at it, indent some strings which were starting at column 0,
and use tabs where possible, to have a consistent style across helps.

Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
2019-05-20 14:35:07 -07:00

491 lines
12 KiB
C

/*
* tc_class.c "tc class".
*
* This program 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.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.h>
#include "utils.h"
#include "tc_util.h"
#include "tc_common.h"
#include "list.h"
struct graph_node {
struct hlist_node hlist;
__u32 id;
__u32 parent_id;
struct graph_node *parent_node;
struct graph_node *right_node;
void *data;
int data_len;
int nodes_count;
};
static struct hlist_head cls_list = {};
static struct hlist_head root_cls_list = {};
static void usage(void);
static void usage(void)
{
fprintf(stderr,
"Usage: tc class [ add | del | change | replace | show ] dev STRING\n"
" [ classid CLASSID ] [ root | parent CLASSID ]\n"
" [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
"\n"
" tc class show [ dev STRING ] [ root | parent CLASSID ]\n"
"Where:\n"
"QDISC_KIND := { prio | cbq | etc. }\n"
"OPTIONS := ... try tc class add <desired QDISC_KIND> help\n");
}
static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv)
{
struct {
struct nlmsghdr n;
struct tcmsg t;
char buf[4096];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
.n.nlmsg_flags = NLM_F_REQUEST | flags,
.n.nlmsg_type = cmd,
.t.tcm_family = AF_UNSPEC,
};
struct qdisc_util *q = NULL;
struct tc_estimator est = {};
char d[IFNAMSIZ] = {};
char k[FILTER_NAMESZ] = {};
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
if (d[0])
duparg("dev", *argv);
strncpy(d, *argv, sizeof(d)-1);
} else if (strcmp(*argv, "classid") == 0) {
__u32 handle;
NEXT_ARG();
if (req.t.tcm_handle)
duparg("classid", *argv);
if (get_tc_classid(&handle, *argv))
invarg("invalid class ID", *argv);
req.t.tcm_handle = handle;
} else if (strcmp(*argv, "handle") == 0) {
fprintf(stderr, "Error: try \"classid\" instead of \"handle\"\n");
return -1;
} else if (strcmp(*argv, "root") == 0) {
if (req.t.tcm_parent) {
fprintf(stderr, "Error: \"root\" is duplicate parent ID.\n");
return -1;
}
req.t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "parent") == 0) {
__u32 handle;
NEXT_ARG();
if (req.t.tcm_parent)
duparg("parent", *argv);
if (get_tc_classid(&handle, *argv))
invarg("invalid parent ID", *argv);
req.t.tcm_parent = handle;
} else if (matches(*argv, "estimator") == 0) {
if (parse_estimator(&argc, &argv, &est))
return -1;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
strncpy(k, *argv, sizeof(k)-1);
q = get_qdisc_kind(k);
argc--; argv++;
break;
}
argc--; argv++;
}
if (k[0])
addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
if (est.ewma_log)
addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
if (q) {
if (q->parse_copt == NULL) {
fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k);
return 1;
}
if (q->parse_copt(q, argc, argv, &req.n, d))
return 1;
} else {
if (argc) {
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc class help\".", *argv);
return -1;
}
}
if (d[0]) {
ll_init_map(&rth);
req.t.tcm_ifindex = ll_name_to_index(d);
if (!req.t.tcm_ifindex)
return -nodev(d);
}
if (rtnl_talk(&rth, &req.n, NULL) < 0)
return 2;
return 0;
}
static int filter_ifindex;
static __u32 filter_qdisc;
static __u32 filter_classid;
static void graph_node_add(__u32 parent_id, __u32 id, void *data,
int len)
{
struct graph_node *node = calloc(1, sizeof(struct graph_node));
node->id = id;
node->parent_id = parent_id;
if (data && len) {
node->data = malloc(len);
node->data_len = len;
memcpy(node->data, data, len);
}
if (parent_id == TC_H_ROOT)
hlist_add_head(&node->hlist, &root_cls_list);
else
hlist_add_head(&node->hlist, &cls_list);
}
static void graph_indent(char *buf, struct graph_node *node, int is_newline,
int add_spaces)
{
char spaces[100] = {0};
while (node && node->parent_node) {
node->parent_node->right_node = node;
node = node->parent_node;
}
while (node && node->right_node) {
if (node->hlist.next)
strcat(buf, "| ");
else
strcat(buf, " ");
node = node->right_node;
}
if (is_newline) {
if (node->hlist.next && node->nodes_count)
strcat(buf, "| |");
else if (node->hlist.next)
strcat(buf, "| ");
else if (node->nodes_count)
strcat(buf, " |");
else if (!node->hlist.next)
strcat(buf, " ");
}
if (add_spaces > 0) {
sprintf(spaces, "%-*s", add_spaces, "");
strcat(buf, spaces);
}
}
static void graph_cls_show(FILE *fp, char *buf, struct hlist_head *root_list,
int level)
{
struct hlist_node *n, *tmp_cls;
char cls_id_str[256] = {};
struct rtattr *tb[TCA_MAX + 1];
struct qdisc_util *q;
char str[300] = {};
hlist_for_each_safe(n, tmp_cls, root_list) {
struct hlist_node *c, *tmp_chld;
struct hlist_head children = {};
struct graph_node *cls = container_of(n, struct graph_node,
hlist);
hlist_for_each_safe(c, tmp_chld, &cls_list) {
struct graph_node *child = container_of(c,
struct graph_node, hlist);
if (cls->id == child->parent_id) {
hlist_del(c);
hlist_add_head(c, &children);
cls->nodes_count++;
child->parent_node = cls;
}
}
graph_indent(buf, cls, 0, 0);
print_tc_classid(cls_id_str, sizeof(cls_id_str), cls->id);
snprintf(str, sizeof(str),
"+---(%s)", cls_id_str);
strcat(buf, str);
parse_rtattr(tb, TCA_MAX, (struct rtattr *)cls->data,
cls->data_len);
if (tb[TCA_KIND] == NULL) {
strcat(buf, " [unknown qdisc kind] ");
} else {
const char *kind = rta_getattr_str(tb[TCA_KIND]);
sprintf(str, " %s ", kind);
strcat(buf, str);
fprintf(fp, "%s", buf);
buf[0] = '\0';
q = get_qdisc_kind(kind);
if (q && q->print_copt) {
q->print_copt(q, fp, tb[TCA_OPTIONS]);
}
if (q && show_stats) {
int cls_indent = strlen(q->id) - 2 +
strlen(cls_id_str);
struct rtattr *stats = NULL;
graph_indent(buf, cls, 1, cls_indent);
if (tb[TCA_STATS] || tb[TCA_STATS2]) {
fprintf(fp, "\n");
print_tcstats_attr(fp, tb, buf, &stats);
buf[0] = '\0';
}
if (cls->hlist.next || cls->nodes_count) {
strcat(buf, "\n");
graph_indent(buf, cls, 1, 0);
}
}
}
free(cls->data);
fprintf(fp, "%s\n", buf);
buf[0] = '\0';
graph_cls_show(fp, buf, &children, level + 1);
if (!cls->hlist.next) {
graph_indent(buf, cls, 0, 0);
strcat(buf, "\n");
}
fprintf(fp, "%s", buf);
buf[0] = '\0';
free(cls);
}
}
int print_class(struct nlmsghdr *n, void *arg)
{
FILE *fp = (FILE *)arg;
struct tcmsg *t = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr *tb[TCA_MAX + 1];
struct qdisc_util *q;
char abuf[256];
if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) {
fprintf(stderr, "Not a class\n");
return 0;
}
len -= NLMSG_LENGTH(sizeof(*t));
if (len < 0) {
fprintf(stderr, "Wrong len %d\n", len);
return -1;
}
if (show_graph) {
graph_node_add(t->tcm_parent, t->tcm_handle, TCA_RTA(t), len);
return 0;
}
if (filter_qdisc && TC_H_MAJ(t->tcm_handle^filter_qdisc))
return 0;
if (filter_classid && t->tcm_handle != filter_classid)
return 0;
parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
if (tb[TCA_KIND] == NULL) {
fprintf(stderr, "print_class: NULL kind\n");
return -1;
}
if (n->nlmsg_type == RTM_DELTCLASS)
fprintf(fp, "deleted ");
abuf[0] = 0;
if (t->tcm_handle) {
if (filter_qdisc)
print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_handle));
else
print_tc_classid(abuf, sizeof(abuf), t->tcm_handle);
}
fprintf(fp, "class %s %s ", rta_getattr_str(tb[TCA_KIND]), abuf);
if (filter_ifindex == 0)
fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex));
if (t->tcm_parent == TC_H_ROOT)
fprintf(fp, "root ");
else {
if (filter_qdisc)
print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_parent));
else
print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
fprintf(fp, "parent %s ", abuf);
}
if (t->tcm_info)
fprintf(fp, "leaf %x: ", t->tcm_info>>16);
q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND]));
if (tb[TCA_OPTIONS]) {
if (q && q->print_copt)
q->print_copt(q, fp, tb[TCA_OPTIONS]);
else
fprintf(fp, "[cannot parse class parameters]");
}
fprintf(fp, "\n");
if (show_stats) {
struct rtattr *xstats = NULL;
if (tb[TCA_STATS] || tb[TCA_STATS2]) {
print_tcstats_attr(fp, tb, " ", &xstats);
fprintf(fp, "\n");
}
if (q && (xstats || tb[TCA_XSTATS]) && q->print_xstats) {
q->print_xstats(q, fp, xstats ? : tb[TCA_XSTATS]);
fprintf(fp, "\n");
}
}
fflush(fp);
return 0;
}
static int tc_class_list(int argc, char **argv)
{
struct tcmsg t = { .tcm_family = AF_UNSPEC };
char d[IFNAMSIZ] = {};
char buf[1024] = {0};
filter_qdisc = 0;
filter_classid = 0;
while (argc > 0) {
if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
if (d[0])
duparg("dev", *argv);
strncpy(d, *argv, sizeof(d)-1);
} else if (strcmp(*argv, "qdisc") == 0) {
NEXT_ARG();
if (filter_qdisc)
duparg("qdisc", *argv);
if (get_qdisc_handle(&filter_qdisc, *argv))
invarg("invalid qdisc ID", *argv);
} else if (strcmp(*argv, "classid") == 0) {
NEXT_ARG();
if (filter_classid)
duparg("classid", *argv);
if (get_tc_classid(&filter_classid, *argv))
invarg("invalid class ID", *argv);
} else if (strcmp(*argv, "root") == 0) {
if (t.tcm_parent) {
fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
return -1;
}
t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "parent") == 0) {
__u32 handle;
if (t.tcm_parent)
duparg("parent", *argv);
NEXT_ARG();
if (get_tc_classid(&handle, *argv))
invarg("invalid parent ID", *argv);
t.tcm_parent = handle;
} else if (matches(*argv, "help") == 0) {
usage();
} else {
fprintf(stderr, "What is \"%s\"? Try \"tc class help\".\n", *argv);
return -1;
}
argc--; argv++;
}
ll_init_map(&rth);
if (d[0]) {
t.tcm_ifindex = ll_name_to_index(d);
if (!t.tcm_ifindex)
return -nodev(d);
filter_ifindex = t.tcm_ifindex;
}
if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) {
perror("Cannot send dump request");
return 1;
}
if (rtnl_dump_filter(&rth, print_class, stdout) < 0) {
fprintf(stderr, "Dump terminated\n");
return 1;
}
if (show_graph)
graph_cls_show(stdout, &buf[0], &root_cls_list, 0);
return 0;
}
int do_class(int argc, char **argv)
{
if (argc < 1)
return tc_class_list(0, NULL);
if (matches(*argv, "add") == 0)
return tc_class_modify(RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1);
if (matches(*argv, "change") == 0)
return tc_class_modify(RTM_NEWTCLASS, 0, argc-1, argv+1);
if (matches(*argv, "replace") == 0)
return tc_class_modify(RTM_NEWTCLASS, NLM_F_CREATE, argc-1, argv+1);
if (matches(*argv, "delete") == 0)
return tc_class_modify(RTM_DELTCLASS, 0, argc-1, argv+1);
#if 0
if (matches(*argv, "get") == 0)
return tc_class_get(RTM_GETTCLASS, 0, argc-1, argv+1);
#endif
if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
|| matches(*argv, "lst") == 0)
return tc_class_list(argc-1, argv+1);
if (matches(*argv, "help") == 0) {
usage();
return 0;
}
fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv);
return -1;
}