From 381daab1e3b56a0bc94d2191cf62beba0df51af9 Mon Sep 17 00:00:00 2001 From: Pascal Volk Date: Thu, 18 Mar 2010 03:41:22 +0000 Subject: [PATCH] doveadm: Added doveadm kick command. Moved some parts from doveadm-who.c to doveadm-who.h, so they can be reused. --HG-- branch : HEAD --- src/doveadm/Makefile.am | 4 +- src/doveadm/doveadm-kick.c | 212 +++++++++++++++++++++++++++++++++++++ src/doveadm/doveadm-who.c | 68 +++++------- src/doveadm/doveadm-who.h | 37 +++++++ src/doveadm/doveadm.c | 1 + src/doveadm/doveadm.h | 1 + 6 files changed, 279 insertions(+), 44 deletions(-) create mode 100644 src/doveadm/doveadm-kick.c create mode 100644 src/doveadm/doveadm-who.h diff --git a/src/doveadm/Makefile.am b/src/doveadm/Makefile.am index 60c0578f0d..e1912dc4fb 100644 --- a/src/doveadm/Makefile.am +++ b/src/doveadm/Makefile.am @@ -49,6 +49,7 @@ doveadm_SOURCES = \ doveadm-dump-log.c \ doveadm-dump-mailboxlog.c \ doveadm-dump-thread.c \ + doveadm-kick.c \ doveadm-mail.c \ doveadm-penalty.c \ doveadm-pw.c \ @@ -59,4 +60,5 @@ noinst_HEADERS = \ doveadm.h \ doveadm-dump.h \ doveadm-mail.h \ - doveadm-settings.h + doveadm-settings.h \ + doveadm-who.h diff --git a/src/doveadm/doveadm-kick.c b/src/doveadm/doveadm-kick.c new file mode 100644 index 0000000000..26e7a08f5c --- /dev/null +++ b/src/doveadm/doveadm-kick.c @@ -0,0 +1,212 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "network.h" +#include "hash.h" +#include "doveadm.h" +#include "doveadm-who.h" + +#include +#include +#include +#include +#include + +struct kick_user { + const char *username; + bool kick_me; /* true if username and/or ip[/mask] matches. + ignored when the -f switch is given. */ +}; + +struct kick_pid { + pid_t pid; + ARRAY_DEFINE(users, struct kick_user); + bool kick; +}; + +struct kick_context { + struct who_context who; + struct hash_table *pids; + bool force_kick; + ARRAY_DEFINE(kicked_users, const char *); +}; + +static void +kick_aggregate_line(struct who_context *_ctx, const struct who_line *line) +{ + struct kick_context *ctx = (struct kick_context *)_ctx; + const bool user_match = who_line_filter_match(line, &ctx->who.filter); + struct kick_pid *k_pid; + struct kick_user new_user, *user; + + memset(&new_user, 0, sizeof(new_user)); + + k_pid = hash_table_lookup(ctx->pids, POINTER_CAST(line->pid)); + if (k_pid == NULL) { + k_pid = p_new(ctx->who.pool, struct kick_pid, 1); + k_pid->pid = line->pid; + p_array_init(&k_pid->users, ctx->who.pool, 5); + hash_table_insert(ctx->pids, POINTER_CAST(line->pid), k_pid); + } + + array_foreach_modifiable(&k_pid->users, user) { + if (strcmp(line->username, user->username) == 0) { + if (user_match) + user->kick_me = TRUE; + return; + } + } + new_user.username = p_strdup(ctx->who.pool, line->username); + new_user.kick_me = user_match; + array_append(&k_pid->users, &new_user, 1); +} + +static bool +kick_pid_want_kicked(struct kick_context *ctx, const struct kick_pid *k_pid, + bool *show_warning) +{ + unsigned int kick_count = 0; + const struct kick_user *user; + + if (array_count(&k_pid->users) == 1) { + user = array_idx(&k_pid->users, 0); + if (!user->kick_me) + return FALSE; + } else { + array_foreach(&k_pid->users, user) { + if (user->kick_me) + kick_count++; + } + if (kick_count == 0) + return FALSE; + if (kick_count < array_count(&k_pid->users) && + !ctx->force_kick) { + array_foreach(&k_pid->users, user) { + if (!user->kick_me) { + array_append(&ctx->kicked_users, + &user->username, 1); + } + } + *show_warning = TRUE; + return FALSE; + } + } + return TRUE; +} + +static void +kick_print_kicked(struct kick_context *ctx, const bool show_warning) +{ + unsigned int i, count; + const char *const *users; + + if (array_count(&ctx->kicked_users) == 0) { + printf("no users kicked\n"); + return; + } + + if (show_warning) { + printf("warning: other connections would also be " + "kicked from following users:\n"); + } else + printf("kicked connections from the following users:\n"); + + array_sort(&ctx->kicked_users, i_strcmp_p); + users = array_get(&ctx->kicked_users, &count); + printf("%s ", users[0]); + for (i = 1; i < count; i++) { + if (strcmp(users[i-1], users[i]) != 0) + printf("%s ", users[i]); + } + printf("\n"); + + if (show_warning) + printf("Use the '-f' option to enforce the disconnect.\n"); +} + +static void kick_users(struct kick_context *ctx) +{ + bool show_enforce_warning = FALSE; + void *key, *value; + struct kick_pid *k_pid; + struct hash_iterate_context *iter; + const struct kick_user *user; + + p_array_init(&ctx->kicked_users, ctx->who.pool, 10); + + iter = hash_table_iterate_init(ctx->pids); + while (hash_table_iterate(iter, &key, &value)) { + k_pid = value; + if (kick_pid_want_kicked(ctx, k_pid, &show_enforce_warning)) + k_pid->kick = TRUE; + } + hash_table_iterate_deinit(&iter); + + if (show_enforce_warning) { + kick_print_kicked(ctx, show_enforce_warning); + return; + } + + iter = hash_table_iterate_init(ctx->pids); + while (hash_table_iterate(iter, &key, &value)) { + k_pid = value; + if (!k_pid->kick) + continue; + + if (kill(k_pid->pid, SIGTERM) < 0 && errno != ESRCH) { + fprintf(stderr, "kill(%s, SIGTERM) failed: %m\n", + dec2str(k_pid->pid)); + } else { + array_foreach(&k_pid->users, user) { + array_append(&ctx->kicked_users, + &user->username, 1); + } + } + } + hash_table_iterate_deinit(&iter); + + kick_print_kicked(ctx, show_enforce_warning); +} + +static void cmd_kick(int argc, char *argv[]) +{ + struct kick_context ctx; + int c; + + memset(&ctx, 0, sizeof(ctx)); + ctx.who.anvil_path = PKG_RUNDIR"/anvil"; + ctx.force_kick = FALSE; + ctx.who.pool = pool_alloconly_create("kick pids", 10240); + ctx.pids = hash_table_create(default_pool, ctx.who.pool, 0, NULL, NULL); + + while ((c = getopt(argc, argv, "a:f")) > 0) { + switch (c) { + case 'a': + ctx.who.anvil_path = optarg; + break; + case 'f': + ctx.force_kick = TRUE; + break; + default: + help(&doveadm_cmd_kick); + } + } + + argv += optind - 1; + if (argv[1] == NULL) + i_fatal("user and/or ip[/bits] must be specified."); + who_parse_args(&ctx.who, argv); + + who_lookup(&ctx.who, kick_aggregate_line); + kick_users(&ctx); + + hash_table_destroy(&ctx.pids); + pool_unref(&ctx.who.pool); +} + +struct doveadm_cmd doveadm_cmd_kick = { + cmd_kick, "kick", + "[-a ] [-f] | | ", + NULL +}; diff --git a/src/doveadm/doveadm-who.c b/src/doveadm/doveadm-who.c index 155839b7f9..3835c42c08 100644 --- a/src/doveadm/doveadm-who.c +++ b/src/doveadm/doveadm-who.c @@ -7,19 +7,12 @@ #include "wildcard-match.h" #include "hash.h" #include "doveadm.h" +#include "doveadm-who.h" #include #include #include -struct who_line { - const char *username; - const char *service; - struct ip_addr ip; - pid_t pid; - unsigned int refcount; -}; - struct who_user { const char *username; const char *service; @@ -28,23 +21,6 @@ struct who_user { unsigned int connection_count; }; -struct who_filter { - const char *username; - struct ip_addr net_ip; - unsigned int net_bits; -}; - -struct who_context { - const char *anvil_path; - struct who_filter filter; - - pool_t pool; - struct hash_table *users; /* username -> who_user */ -}; - -typedef void who_callback_t(struct who_context *ctx, - const struct who_line *line); - static unsigned int who_user_hash(const void *p) { const struct who_user *user = p; @@ -131,7 +107,27 @@ static void who_aggregate_line(struct who_context *ctx, array_append(&user->pids, &line->pid, 1); } -static void who_lookup(struct who_context *ctx, who_callback_t *callback) +void who_parse_args(struct who_context *ctx, char **args) +{ + struct ip_addr net_ip; + unsigned int net_bits; + + while (args[1] != NULL) { + if (net_parse_range(args[1], &net_ip, &net_bits) == 0) { + if (ctx->filter.net_bits != 0) + usage(); + ctx->filter.net_ip = net_ip; + ctx->filter.net_bits = net_bits; + } else { + if (ctx->filter.username != NULL) + usage(); + ctx->filter.username = args[1]; + } + args++; + } +} + +void who_lookup(struct who_context *ctx, who_callback_t *callback) { #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" #define ANVIL_CMD ANVIL_HANDSHAKE"CONNECT-DUMP\n" @@ -229,8 +225,8 @@ static void who_print(struct who_context *ctx) hash_table_iterate_deinit(&iter); } -static bool who_line_filter_match(const struct who_line *line, - const struct who_filter *filter) +bool who_line_filter_match(const struct who_line *line, + const struct who_filter *filter) { if (filter->username != NULL) { if (!wildcard_match_icase(line->username, filter->username)) @@ -261,8 +257,6 @@ static void who_print_line(struct who_context *ctx, static void cmd_who(int argc, char *argv[]) { struct who_context ctx; - struct ip_addr net_ip; - unsigned int net_bits; bool separate_connections = FALSE; int c; @@ -286,19 +280,7 @@ static void cmd_who(int argc, char *argv[]) } argv += optind - 1; - while (argv[1] != NULL) { - if (net_parse_range(argv[1], &net_ip, &net_bits) == 0) { - if (ctx.filter.net_bits != 0) - usage(); - ctx.filter.net_ip = net_ip; - ctx.filter.net_bits = net_bits; - } else { - if (ctx.filter.username != NULL) - usage(); - ctx.filter.username = argv[1]; - } - argv++; - } + who_parse_args(&ctx, argv); if (!separate_connections) { who_lookup(&ctx, who_aggregate_line); diff --git a/src/doveadm/doveadm-who.h b/src/doveadm/doveadm-who.h new file mode 100644 index 0000000000..301916a800 --- /dev/null +++ b/src/doveadm/doveadm-who.h @@ -0,0 +1,37 @@ +#ifndef DOVEADM_WHO_H +#define DOVEADM_WHO_H + +struct who_line { + const char *username; + const char *service; + struct ip_addr ip; + pid_t pid; + unsigned int refcount; +}; + + +struct who_filter { + const char *username; + struct ip_addr net_ip; + unsigned int net_bits; +}; + +struct who_context { + const char *anvil_path; + struct who_filter filter; + + pool_t pool; + struct hash_table *users; /* username -> who_user */ +}; + +typedef void who_callback_t(struct who_context *ctx, + const struct who_line *line); + +void who_parse_args(struct who_context *ctx, char **args); + +void who_lookup(struct who_context *ctx, who_callback_t *callback); + +bool who_line_filter_match(const struct who_line *line, + const struct who_filter *filter); + +#endif /* DOVEADM_WHO_H */ diff --git a/src/doveadm/doveadm.c b/src/doveadm/doveadm.c index 4fc71f5ac6..b1fe765faf 100644 --- a/src/doveadm/doveadm.c +++ b/src/doveadm/doveadm.c @@ -148,6 +148,7 @@ int main(int argc, char *argv[]) doveadm_register_cmd(&doveadm_cmd_pw); doveadm_register_cmd(&doveadm_cmd_who); doveadm_register_cmd(&doveadm_cmd_penalty); + doveadm_register_cmd(&doveadm_cmd_kick); doveadm_mail_init(); doveadm_load_modules(); diff --git a/src/doveadm/doveadm.h b/src/doveadm/doveadm.h index 21c4e60c5f..44ee8fe55c 100644 --- a/src/doveadm/doveadm.h +++ b/src/doveadm/doveadm.h @@ -18,6 +18,7 @@ extern struct doveadm_cmd doveadm_cmd_dump; extern struct doveadm_cmd doveadm_cmd_pw; extern struct doveadm_cmd doveadm_cmd_who; extern struct doveadm_cmd doveadm_cmd_penalty; +extern struct doveadm_cmd doveadm_cmd_kick; extern bool doveadm_verbose, doveadm_debug; -- 2.47.3