]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - tc/tc_class.c
q_cake: properly print memlimit
[mirror_iproute2.git] / tc / tc_class.c
index 0352fd63db9328c494bc5805d0b1886108b57220..39bea9712dda7bcb5c8724081a31b72a261e8e48 100644 (file)
@@ -13,7 +13,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <syslog.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include "utils.h"
 #include "tc_util.h"
 #include "tc_common.h"
+#include "list.h"
 
-static void usage(void) __attribute__((noreturn));
+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 | get ] dev STRING\n");
-       fprintf(stderr, "       [ classid CLASSID ] [ root | parent CLASSID ]\n");
-       fprintf(stderr, "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n");
-       fprintf(stderr, "\n");
-       fprintf(stderr, "       tc class show [ dev STRING ] [ root | parent CLASSID ]\n");
-       fprintf(stderr, "Where:\n");
-       fprintf(stderr, "QDISC_KIND := { prio | cbq | etc. }\n");
-       fprintf(stderr, "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n");
-       exit(-1);
+       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");
 }
 
-int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
+static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv)
 {
-       struct rtnl_handle rth;
        struct {
-               struct nlmsghdr         n;
-               struct tcmsg            t;
-               char                    buf[4096];
-       } req;
+               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[16];
-       char  k[16];
-
-       memset(&req, 0, sizeof(req));
-       memset(&est, 0, sizeof(est));
-       memset(d, 0, sizeof(d));
-       memset(k, 0, sizeof(k));
-
-       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
-       req.n.nlmsg_flags = NLM_F_REQUEST|flags;
-       req.n.nlmsg_type = cmd;
-       req.t.tcm_family = AF_UNSPEC;
+       struct tc_estimator est = {};
+       char  d[IFNAMSIZ] = {};
+       char  k[FILTER_NAMESZ] = {};
 
        while (argc > 0) {
                if (strcmp(*argv, "dev") == 0) {
@@ -71,25 +79,30 @@ int tc_class_modify(int cmd, unsigned flags, int argc, char **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(*argv, "invalid class ID");
+                               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");
-                               exit(-1);
+                               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(*argv, "invalid parent ID");
+                               invarg("invalid parent ID", *argv);
                        req.t.tcm_parent = handle;
                } else if (matches(*argv, "estimator") == 0) {
                        if (parse_estimator(&argc, &argv, &est))
@@ -114,50 +127,182 @@ int tc_class_modify(int cmd, unsigned flags, int argc, char **argv)
        if (q) {
                if (q->parse_copt == NULL) {
                        fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k);
-                       exit(1);
+                       return 1;
                }
-               if (q->parse_copt(q, argc, argv, &req.n))
-                       exit(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);
-                       exit(-1);
+                       return -1;
                }
        }
 
-       if (rtnl_open(&rth, 0) < 0) {
-               fprintf(stderr, "Cannot open rtnetlink\n");
-               exit(1);
-       }
-
        if (d[0])  {
                ll_init_map(&rth);
 
-               if ((req.t.tcm_ifindex = ll_name_to_index(d)) == 0) {
-                       fprintf(stderr, "Cannot find device \"%s\"\n", d);
-                       exit(1);
-               }
+               req.t.tcm_ifindex = ll_name_to_index(d);
+               if (!req.t.tcm_ifindex)
+                       return -nodev(d);
        }
 
-       if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
-               exit(2);
+       if (rtnl_talk(&rth, &req.n, NULL) < 0)
+               return 2;
 
-       rtnl_close(&rth);
        return 0;
 }
 
-int filter_ifindex;
-__u32 filter_qdisc;
+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 int print_class(const struct sockaddr_nl *who, 
-                      const struct nlmsghdr *n, void *arg)
+static void graph_indent(char *buf, struct graph_node *node, int is_newline,
+               int add_spaces)
 {
-       FILE *fp = (FILE*)arg;
+       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_flags(tb, TCA_MAX, (struct rtattr *)cls->data,
+                                  cls->data_len, NLA_F_NESTED);
+
+               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 rtattr *tb[TCA_MAX + 1];
        struct qdisc_util *q;
        char abuf[256];
 
@@ -170,11 +315,19 @@ static int print_class(const struct sockaddr_nl *who,
                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;
 
-       memset(tb, 0, sizeof(tb));
-       parse_rtattr(tb, TCA_MAX, TCA_RTA(t), len);
+       if (filter_classid && t->tcm_handle != filter_classid)
+               return 0;
+
+       parse_rtattr_flags(tb, TCA_MAX, TCA_RTA(t), len, NLA_F_NESTED);
 
        if (tb[TCA_KIND] == NULL) {
                fprintf(stderr, "print_class: NULL kind\n");
@@ -191,7 +344,7 @@ static int print_class(const struct sockaddr_nl *who,
                else
                        print_tc_classid(abuf, sizeof(abuf), t->tcm_handle);
        }
-       fprintf(fp, "class %s %s ", (char*)RTA_DATA(tb[TCA_KIND]), abuf);
+       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));
@@ -216,12 +369,14 @@ static int print_class(const struct sockaddr_nl *who,
        }
        fprintf(fp, "\n");
        if (show_stats) {
-               if (tb[TCA_STATS]) {
-                       print_tcstats_attr(fp, tb[TCA_STATS]);
+               struct rtattr *xstats = NULL;
+
+               if (tb[TCA_STATS] || tb[TCA_STATS2]) {
+                       print_tcstats_attr(fp, tb, " ", &xstats);
                        fprintf(fp, "\n");
                }
-               if (q && tb[TCA_XSTATS] && q->print_xstats) {
-                       q->print_xstats(q, fp, tb[TCA_XSTATS]);
+               if (q && (xstats || tb[TCA_XSTATS]) && q->print_xstats) {
+                       q->print_xstats(q, fp, xstats ? : tb[TCA_XSTATS]);
                        fprintf(fp, "\n");
                }
        }
@@ -230,15 +385,14 @@ static int print_class(const struct sockaddr_nl *who,
 }
 
 
-int tc_class_list(int argc, char **argv)
+static int tc_class_list(int argc, char **argv)
 {
-       struct tcmsg t;
-       struct rtnl_handle rth;
-       char d[16];
+       struct tcmsg t = { .tcm_family = AF_UNSPEC };
+       char d[IFNAMSIZ] = {};
+       char buf[1024] = {0};
 
-       memset(&t, 0, sizeof(t));
-       t.tcm_family = AF_UNSPEC;
-       memset(d, 0, sizeof(d));
+       filter_qdisc = 0;
+       filter_classid = 0;
 
        while (argc > 0) {
                if (strcmp(*argv, "dev") == 0) {
@@ -251,59 +405,60 @@ int tc_class_list(int argc, char **argv)
                        if (filter_qdisc)
                                duparg("qdisc", *argv);
                        if (get_qdisc_handle(&filter_qdisc, *argv))
-                               invarg(*argv, "invalid qdisc ID");
+                               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");
-                               exit(-1);
+                               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(*argv, "invalid parent ID");
+                               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);
-                       exit(-1);
+                       return -1;
                }
 
                argc--; argv++;
        }
 
-       if (rtnl_open(&rth, 0) < 0) {
-               fprintf(stderr, "Cannot open rtnetlink\n");
-               exit(1);
-       }
-
        ll_init_map(&rth);
 
        if (d[0]) {
-               if ((t.tcm_ifindex = ll_name_to_index(d)) == 0) {
-                       fprintf(stderr, "Cannot find device \"%s\"\n", d);
-                       exit(1);
-               }
+               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");
-               rtnl_close(&rth);
-               exit(1);
+               return 1;
        }
 
-       if (rtnl_dump_filter(&rth, print_class, stdout, NULL, NULL) < 0) {
+       if (rtnl_dump_filter(&rth, print_class, stdout) < 0) {
                fprintf(stderr, "Dump terminated\n");
-               rtnl_close(&rth);
-               exit(1);
+               return 1;
        }
 
-       rtnl_close(&rth);
+       if (show_graph)
+               graph_cls_show(stdout, &buf[0], &root_cls_list, 0);
+
        return 0;
 }
 
@@ -326,8 +481,10 @@ int do_class(int argc, char **argv)
        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)
+       if (matches(*argv, "help") == 0) {
                usage();
+               return 0;
+       }
        fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv);
        return -1;
 }