doveadm-kick.c revision 678d0463849ba777106eb7875f27db07a5d8e3df
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "lib.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "array.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "network.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "hash.h"
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen#include "doveadm.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include "doveadm-who.h"
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <stdio.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <stdlib.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <unistd.h>
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen#include <sys/types.h>
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen#include <signal.h>
c115c742f730e312d6b6ab5064595cd0d8b4e26eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstruct kick_user {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen const char *username;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen bool kick_me; /* true if username and/or ip[/mask] matches.
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ignored when the -f switch is given. */
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen};
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstruct kick_pid {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen pid_t pid;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen ARRAY_DEFINE(users, struct kick_user);
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen bool kick;
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen};
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainenstruct kick_context {
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen struct who_context who;
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen HASH_TABLE(pid_t, struct kick_pid *) pids;
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen bool force_kick;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ARRAY_DEFINE(kicked_users, const char *);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen};
499fec3443374cc89fb8c83b8027c1614097d7a3Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic void
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainenkick_aggregate_line(struct who_context *_ctx, const struct who_line *line)
f5a7396b31762a1f876517e13ce9065820139f7cTimo Sirainen{
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen struct kick_context *ctx = (struct kick_context *)_ctx;
a18335e18aac7fc219b6f18dde083359155cc524Timo Sirainen const bool user_match = who_line_filter_match(line, &ctx->who.filter);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct kick_pid *k_pid;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen struct kick_user new_user, *user;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen memset(&new_user, 0, sizeof(new_user));
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen k_pid = hash_table_lookup(ctx->pids, line->pid);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (k_pid == NULL) {
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen k_pid = p_new(ctx->who.pool, struct kick_pid, 1);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen k_pid->pid = line->pid;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen p_array_init(&k_pid->users, ctx->who.pool, 5);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen hash_table_insert(ctx->pids, line->pid, k_pid);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen array_foreach_modifiable(&k_pid->users, user) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (strcmp(line->username, user->username) == 0) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (user_match)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen user->kick_me = TRUE;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen return;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen }
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen }
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen new_user.username = p_strdup(ctx->who.pool, line->username);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen new_user.kick_me = user_match;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen array_append(&k_pid->users, &new_user, 1);
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen}
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
11352dc3e4b29f3d2763c82f8ea4f99e8daf4fa3Timo Sirainenstatic bool
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenkick_pid_want_kicked(struct kick_context *ctx, const struct kick_pid *k_pid,
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen bool *show_warning)
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen{
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen unsigned int kick_count = 0;
942302b0247403645394d848b3c620ead262a2a5Timo Sirainen const struct kick_user *user;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (array_count(&k_pid->users) == 1) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen user = array_idx(&k_pid->users, 0);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (!user->kick_me)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return FALSE;
cd8507179823de33d6e8242e10dbc15d136245b5Timo Sirainen } else {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen array_foreach(&k_pid->users, user) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (user->kick_me)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen kick_count++;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen }
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (kick_count == 0)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen return FALSE;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (kick_count < array_count(&k_pid->users) &&
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen !ctx->force_kick) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen array_foreach(&k_pid->users, user) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen if (!user->kick_me) {
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen array_append(&ctx->kicked_users,
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen &user->username, 1);
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen *show_warning = TRUE;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return FALSE;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen }
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen return TRUE;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen}
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenstatic void
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainenkick_print_kicked(struct kick_context *ctx, const bool show_warning)
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen{
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen unsigned int i, count;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const char *const *users;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (array_count(&ctx->kicked_users) == 0) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen printf("no users kicked\n");
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen doveadm_exit_code = DOVEADM_EX_NOTFOUND;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (show_warning) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen printf("warning: other connections would also be "
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen "kicked from following users:\n");
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen } else
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen printf("kicked connections from the following users:\n");
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen array_sort(&ctx->kicked_users, i_strcmp_p);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen users = array_get(&ctx->kicked_users, &count);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen printf("%s ", users[0]);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen for (i = 1; i < count; i++) {
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen if (strcmp(users[i-1], users[i]) != 0)
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen printf("%s ", users[i]);
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen }
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen printf("\n");
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen if (show_warning)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen printf("Use the '-f' option to enforce the disconnect.\n");
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen}
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainenstatic void kick_users(struct kick_context *ctx)
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen{
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen bool show_enforce_warning = FALSE;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct hash_iterate_context *iter;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen void *key, *value;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen const struct kick_user *user;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
4ece61edd7c266a4b8f3b290a7f0a3cb3d13ca0fTimo Sirainen p_array_init(&ctx->kicked_users, ctx->who.pool, 10);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen iter = hash_table_iterate_init(ctx->pids);
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen while (hash_table_iterate(iter, &key, &value)) {
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen struct kick_pid *k_pid = value;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen if (kick_pid_want_kicked(ctx, k_pid, &show_enforce_warning))
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen k_pid->kick = TRUE;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen }
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen hash_table_iterate_deinit(&iter);
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen if (show_enforce_warning) {
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen kick_print_kicked(ctx, show_enforce_warning);
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen return;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen }
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen iter = hash_table_iterate_init(ctx->pids);
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen while (hash_table_iterate(iter, &key, &value)) {
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen struct kick_pid *k_pid = value;
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen
c93ff0433cc3d348116f75a64f9988fedb86fd18Timo Sirainen if (!k_pid->kick)
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen continue;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (kill(k_pid->pid, SIGTERM) < 0 && errno != ESRCH) {
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen fprintf(stderr, "kill(%s, SIGTERM) failed: %m\n",
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen dec2str(k_pid->pid));
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen } else {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen array_foreach(&k_pid->users, user) {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen array_append(&ctx->kicked_users,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen &user->username, 1);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen }
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen hash_table_iterate_deinit(&iter);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen kick_print_kicked(ctx, show_enforce_warning);
755aea84bbe2b15ed7fe991f6462a93333ff571fTimo Sirainen}
8305127d1074cf9a1e25dec9be2735276462079dTimo Sirainen
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainenstatic void cmd_kick(int argc, char *argv[])
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen{
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen struct kick_context ctx;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen int c;
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen
212e9e43a7d49242446331fd43ba519eda936d60Timo Sirainen memset(&ctx, 0, sizeof(ctx));
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ctx.who.anvil_path = t_strconcat(doveadm_settings->base_dir, "/anvil", NULL);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx.force_kick = FALSE;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen ctx.who.pool = pool_alloconly_create("kick pids", 10240);
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen hash_table_create_direct(&ctx.pids, ctx.who.pool, 0);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen while ((c = getopt(argc, argv, "a:f")) > 0) {
596433ccbca59ce2328dc1d029586154cd937155Timo Sirainen switch (c) {
596433ccbca59ce2328dc1d029586154cd937155Timo Sirainen case 'a':
15b5076a239682277b44880e33ea23b55fff7e71Timo Sirainen ctx.who.anvil_path = optarg;
15b5076a239682277b44880e33ea23b55fff7e71Timo Sirainen break;
24e5e4526d8f5cbc056ab97fd0d154d0936d7a5eTimo Sirainen case 'f':
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen ctx.force_kick = TRUE;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen break;
e5fd6dfd0a492e4708d4dbb7971d7fc5d7b8fd85Timo Sirainen default:
dd37e2ff291fbebac1b94e8aad50f3bdf7531049Timo Sirainen help(&doveadm_cmd_kick);
61cf001f1944d92eb25f113ba4c08985d6e30d53Timo Sirainen }
61cf001f1944d92eb25f113ba4c08985d6e30d53Timo Sirainen }
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen argv += optind - 1;
132487b9a47c2eb6fc80cfa2b0aaf82c6dc3af56Timo Sirainen if (argv[1] == NULL) {
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen i_fatal_status(EX_USAGE,
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen "user and/or ip[/bits] must be specified.");
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen }
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen who_parse_args(&ctx.who, argv);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen who_lookup(&ctx.who, kick_aggregate_line);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen kick_users(&ctx);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen hash_table_destroy(&ctx.pids);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen pool_unref(&ctx.who.pool);
18a41cbd38f83429b790414c1159c097af4a59b8Timo Sirainen}
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainenstruct doveadm_cmd doveadm_cmd_kick = {
074055dadbca01626437cc4724853a374acab6a8Timo Sirainen cmd_kick, "kick",
074055dadbca01626437cc4724853a374acab6a8Timo Sirainen "[-a <anvil socket path>] [-f] <user mask>[|]<ip/bits>"
963842c00ef1714db2855c8952f1b46d78cba1caTimo Sirainen};
6b399f555c9c5c722d4cd5eab8faa02b2a4731d3Timo Sirainen