doveadm-kick.c revision a7fc192e2177fd869bca779e9f6014f7149cda2d
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2010-2016 Dovecot authors, see the included COPYING file */
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "lib.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "array.h"
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen#include "net.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "hash.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "doveadm.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "doveadm-who.h"
a2fdfd2efdbb2d912aad23900a466cf74114920bTimo Sirainen
d29c67900d3d104e24ed213ed4a10d204decdf18Aki Tuomi#include <stdio.h>
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include <unistd.h>
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi#include <sys/types.h>
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include <signal.h>
c6be98b5270900746f35ebe28bd636019976e29eTimo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenstruct kick_user {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const char *username;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen bool kick_me; /* true if username and/or ip[/mask] matches.
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi ignored when the -f switch is given. */
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen};
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenstruct kick_pid {
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi pid_t pid;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen ARRAY(struct kick_user) users;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen bool kick;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen};
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainenstruct kick_context {
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen struct who_context who;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen HASH_TABLE(void *, struct kick_pid *) pids;
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen bool force_kick;
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen ARRAY(const char *) kicked_users;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen};
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenstatic void
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenkick_aggregate_line(struct who_context *_ctx, const struct who_line *line)
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen{
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct kick_context *ctx = (struct kick_context *)_ctx;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const bool user_match = who_line_filter_match(line, &ctx->who.filter);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct kick_pid *k_pid;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct kick_user new_user, *user;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen memset(&new_user, 0, sizeof(new_user));
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen k_pid = hash_table_lookup(ctx->pids, POINTER_CAST(line->pid));
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi if (k_pid == NULL) {
f300f927771a39549ce6cb7607129508e9041b4aStephan Bosch k_pid = p_new(ctx->who.pool, struct kick_pid, 1);
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi k_pid->pid = line->pid;
e60c3e17c656c53da60f0ac51aa15e9ef2742d77Stephan Bosch p_array_init(&k_pid->users, ctx->who.pool, 5);
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi hash_table_insert(ctx->pids, POINTER_CAST(line->pid), k_pid);
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen array_foreach_modifiable(&k_pid->users, user) {
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen if (strcmp(line->username, user->username) == 0) {
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen if (user_match)
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen user->kick_me = TRUE;
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen return;
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen }
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen }
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi new_user.username = p_strdup(ctx->who.pool, line->username);
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi new_user.kick_me = user_match;
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi array_append(&k_pid->users, &new_user, 1);
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi}
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomistatic bool
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenkick_pid_want_kicked(struct kick_context *ctx, const struct kick_pid *k_pid,
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen bool *show_warning)
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen{
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen unsigned int kick_count = 0;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const struct kick_user *user;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (array_count(&k_pid->users) == 1) {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen user = array_idx(&k_pid->users, 0);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (!user->kick_me)
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen return FALSE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen } else {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen array_foreach(&k_pid->users, user) {
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen if (user->kick_me)
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen kick_count++;
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen }
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen if (kick_count == 0)
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen return FALSE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (kick_count < array_count(&k_pid->users) &&
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen !ctx->force_kick) {
a13b1245bee0b6524b4aeb3c8fd9e34af648b746Aki Tuomi array_foreach(&k_pid->users, user) {
c6be98b5270900746f35ebe28bd636019976e29eTimo Sirainen if (!user->kick_me) {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen array_append(&ctx->kicked_users,
80a225c0b1f4bf322a562cc7c21d5891fb6895eeStephan Bosch &user->username, 1);
a2fdfd2efdbb2d912aad23900a466cf74114920bTimo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen *show_warning = TRUE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen return FALSE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
80a225c0b1f4bf322a562cc7c21d5891fb6895eeStephan Bosch return TRUE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen}
c6be98b5270900746f35ebe28bd636019976e29eTimo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenstatic void
a2fdfd2efdbb2d912aad23900a466cf74114920bTimo Sirainenkick_print_kicked(struct kick_context *ctx, const bool show_warning)
237a6211c7fc4d6dbb58dd0467da6dba1b8f21f6Timo Sirainen{
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen unsigned int i, count;
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen const char *const *users;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (array_count(&ctx->kicked_users) == 0) {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen printf("no users kicked\n");
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen doveadm_exit_code = DOVEADM_EX_NOTFOUND;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen return;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (show_warning) {
2d1892aaeb63b9774237b6e60d6bb04bf6f8259cTimo Sirainen printf("warning: other connections would also be "
838d6a4751c3fbe17c3ec45c0e109629c4156815Timo Sirainen "kicked from following users:\n");
2d1892aaeb63b9774237b6e60d6bb04bf6f8259cTimo Sirainen } else
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen printf("kicked connections from the following users:\n");
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen array_sort(&ctx->kicked_users, i_strcmp_p);
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen users = array_get(&ctx->kicked_users, &count);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen printf("%s ", users[0]);
2d1892aaeb63b9774237b6e60d6bb04bf6f8259cTimo Sirainen for (i = 1; i < count; i++) {
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen if (strcmp(users[i-1], users[i]) != 0)
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen printf("%s ", users[i]);
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen }
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen printf("\n");
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen if (show_warning)
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen printf("Use the '-f' option to enforce the disconnect.\n");
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen}
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainenstatic void kick_users(struct kick_context *ctx)
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen{
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen bool show_enforce_warning = FALSE;
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch struct hash_iterate_context *iter;
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen void *key;
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen struct kick_pid *k_pid;
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen const struct kick_user *user;
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen p_array_init(&ctx->kicked_users, ctx->who.pool, 10);
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen iter = hash_table_iterate_init(ctx->pids);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen while (hash_table_iterate(iter, ctx->pids, &key, &k_pid)) {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (kick_pid_want_kicked(ctx, k_pid, &show_enforce_warning))
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen k_pid->kick = TRUE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen hash_table_iterate_deinit(&iter);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainen if (show_enforce_warning) {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen kick_print_kicked(ctx, show_enforce_warning);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen return;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen iter = hash_table_iterate_init(ctx->pids);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen while (hash_table_iterate(iter, ctx->pids, &key, &k_pid)) {
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 = t_strconcat(doveadm_settings->base_dir, "/anvil", NULL);
ctx.force_kick = FALSE;
ctx.who.pool = pool_alloconly_create("kick pids", 10240);
hash_table_create_direct(&ctx.pids, ctx.who.pool, 0);
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_status(EX_USAGE,
"user and/or ip[/bits] must be specified.");
}
if (who_parse_args(&ctx.who, (const char *const *)argv + 1) < 0)
help(&doveadm_cmd_kick);
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 mask>[|]<ip/bits>"
};