]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
doveadm: Added doveadm kick command.
authorPascal Volk <user@localhost.localdomain.org>
Thu, 18 Mar 2010 03:41:22 +0000 (03:41 +0000)
committerPascal Volk <user@localhost.localdomain.org>
Thu, 18 Mar 2010 03:41:22 +0000 (03:41 +0000)
Moved some parts from doveadm-who.c to doveadm-who.h, so they can be reused.

--HG--
branch : HEAD

src/doveadm/Makefile.am
src/doveadm/doveadm-kick.c [new file with mode: 0644]
src/doveadm/doveadm-who.c
src/doveadm/doveadm-who.h [new file with mode: 0644]
src/doveadm/doveadm.c
src/doveadm/doveadm.h

index 60c0578f0d5ab99d70399cdb8451b1bd124281ab..e1912dc4fb68a4a715a65ad5992eb3b92928ebf3 100644 (file)
@@ -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 (file)
index 0000000..26e7a08
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+
+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 <anvil socket path>] [-f] <user>|<ip/bits> | <user> <ip/bits>",
+       NULL
+};
index 155839b7f943f07c797dd4c6d5894ae02e39fcb0..3835c42c081d757284adae272d0c3e40fbbc2979 100644 (file)
@@ -7,19 +7,12 @@
 #include "wildcard-match.h"
 #include "hash.h"
 #include "doveadm.h"
+#include "doveadm-who.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-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 (file)
index 0000000..301916a
--- /dev/null
@@ -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 */
index 4fc71f5ac681d7df0b26d06429b06b799bff3222..b1fe765fafe8ef358d9fd50c99e3b7d5fcf24404 100644 (file)
@@ -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();
 
index 21c4e60c5f0cf5c8847c47f4b4ef634eb88ac3cf..44ee8fe55c9973a4099883a1b382379a1335a249 100644 (file)
@@ -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;