diff options
Diffstat (limited to 'tc/tc.c')
-rw-r--r-- | tc/tc.c | 199 |
1 files changed, 184 insertions, 15 deletions
@@ -193,16 +193,16 @@ static void usage(void) " -nm | -nam[es] | { -cf | -conf } path } | -j[son]\n"); } -static int do_cmd(int argc, char **argv) +static int do_cmd(int argc, char **argv, void *buf, size_t buflen) { if (matches(*argv, "qdisc") == 0) return do_qdisc(argc-1, argv+1); if (matches(*argv, "class") == 0) return do_class(argc-1, argv+1); if (matches(*argv, "filter") == 0) - return do_filter(argc-1, argv+1); + return do_filter(argc-1, argv+1, buf, buflen); if (matches(*argv, "actions") == 0) - return do_action(argc-1, argv+1); + return do_action(argc-1, argv+1, buf, buflen); if (matches(*argv, "monitor") == 0) return do_tcmonitor(argc-1, argv+1); if (matches(*argv, "exec") == 0) @@ -217,11 +217,110 @@ static int do_cmd(int argc, char **argv) return -1; } +#define TC_MAX_SUBC 10 +static bool batchsize_enabled(int argc, char *argv[]) +{ + struct { + char *c; + char *subc[TC_MAX_SUBC]; + } table[] = { + {"filter", {"add", "delete", "change", "replace", NULL}}, + {"actions", {"add", "change", "replace", NULL}}, + { NULL }, + }, *iter; + char *s; + int i; + + if (argc < 2) + return false; + + for (iter = table; iter->c; iter++) { + if (matches(argv[0], iter->c)) + continue; + for (i = 0; i < TC_MAX_SUBC; i++) { + s = iter->subc[i]; + if (s && matches(argv[1], s) == 0) + return true; + } + } + + return false; +} + +struct batch_buf { + struct batch_buf *next; + char buf[16420]; /* sizeof (struct nlmsghdr) + + max(sizeof (struct tcmsg) + + sizeof (struct tcamsg)) + + MAX_MSG */ +}; + +static struct batch_buf *get_batch_buf(struct batch_buf **pool, + struct batch_buf **head, + struct batch_buf **tail) +{ + struct batch_buf *buf; + + if (*pool == NULL) + buf = calloc(1, sizeof (struct batch_buf)); + else { + buf = *pool; + *pool = (*pool)->next; + memset(buf, 0, sizeof (struct batch_buf)); + } + + if (*head == NULL) + *head = *tail = buf; + else { + (*tail)->next = buf; + (*tail) = buf; + } + + return buf; +} + +static void put_batch_bufs(struct batch_buf **pool, + struct batch_buf **head, + struct batch_buf **tail) +{ + if (*head == NULL || *tail == NULL) + return; + + if (*pool == NULL) + *pool = *head; + else { + (*tail)->next = *pool; + *pool = *head; + } + *head = NULL; + *tail = NULL; +} + +static void free_batch_bufs(struct batch_buf **pool) +{ + struct batch_buf *buf; + + for (buf = *pool; buf != NULL; buf = *pool) { + *pool = buf->next; + free(buf); + } + *pool = NULL; +} + static int batch(const char *name) { - char *line = NULL; + struct batch_buf *head = NULL, *tail = NULL, *buf_pool = NULL; + char *largv[100], *largv_next[100]; + char *line, *line_next = NULL; + bool bs_enabled_next = false; + bool bs_enabled = false; + bool lastline = false; + int largc, largc_next; + bool bs_enabled_saved; + int batchsize = 0; size_t len = 0; int ret = 0; + bool send; batch_mode = 1; if (name && strcmp(name, "-") != 0) { @@ -240,25 +339,95 @@ static int batch(const char *name) } cmdlineno = 0; - while (getcmdline(&line, &len, stdin) != -1) { - char *largv[100]; - int largc; + if (getcmdline(&line, &len, stdin) == -1) + goto Exit; + largc = makeargs(line, largv, 100); + bs_enabled = batchsize_enabled(largc, largv); + bs_enabled_saved = bs_enabled; + do { + if (getcmdline(&line_next, &len, stdin) == -1) + lastline = true; + + largc_next = makeargs(line_next, largv_next, 100); + bs_enabled_next = batchsize_enabled(largc_next, largv_next); + if (bs_enabled) { + struct batch_buf *buf; + + buf = get_batch_buf(&buf_pool, &head, &tail); + if (!buf) { + fprintf(stderr, + "failed to allocate batch_buf\n"); + return -1; + } + ++batchsize; + } - largc = makeargs(line, largv, 100); - if (largc == 0) + /* + * In batch mode, if we haven't accumulated enough commands + * and this is not the last command and this command & next + * command both support the batchsize feature, don't send the + * message immediately. + */ + if (!lastline && bs_enabled && bs_enabled_next + && batchsize != MSG_IOV_MAX) + send = false; + else + send = true; + + line = line_next; + line_next = NULL; + len = 0; + bs_enabled_saved = bs_enabled; + bs_enabled = bs_enabled_next; + bs_enabled_next = false; + + if (largc == 0) { + largc = largc_next; + memcpy(largv, largv_next, largc * sizeof(char *)); continue; /* blank line */ + } - if (do_cmd(largc, largv)) { - fprintf(stderr, "Command failed %s:%d\n", name, cmdlineno); + ret = do_cmd(largc, largv, tail == NULL ? NULL : tail->buf, + tail == NULL ? 0 : sizeof (tail->buf)); + if (ret != 0) { + fprintf(stderr, "Command failed %s:%d\n", name, + cmdlineno - 1); ret = 1; if (!force) break; } - } - if (line) - free(line); + largc = largc_next; + memcpy(largv, largv_next, largc * sizeof(char *)); + + if (send && bs_enabled_saved) { + struct iovec *iov, *iovs; + struct batch_buf *buf; + struct nlmsghdr *n; + + iov = iovs = malloc(batchsize * sizeof (struct iovec)); + for (buf = head; buf != NULL; buf = buf->next, ++iov) { + n = (struct nlmsghdr *)&buf->buf; + iov->iov_base = n; + iov->iov_len = n->nlmsg_len; + } + + ret = rtnl_talk_iov(&rth, iovs, batchsize, NULL); + if (ret < 0) { + fprintf(stderr, "Command failed %s:%d\n", name, + cmdlineno - (batchsize + ret) - 1); + return 2; + } + put_batch_bufs(&buf_pool, &head, &tail); + batchsize = 0; + free(iovs); + } + } while (!lastline); + free_batch_bufs(&buf_pool); +Exit: + free(line); rtnl_close(&rth); + return ret; } @@ -341,7 +510,7 @@ int main(int argc, char **argv) goto Exit; } - ret = do_cmd(argc-1, argv+1); + ret = do_cmd(argc-1, argv+1, NULL, 0); Exit: rtnl_close(&rth); |