doveadm-who.c revision f6b8ff28fe15eff03e0127f11cd122d3eb872e2e
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2009-2017 Dovecot authors, see the included COPYING file */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include "lib.h"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include "array.h"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen#include "net.h"
472369cba85d9f7c995dda60e7cd01d78b4a960aTimo Sirainen#include "istream.h"
e28fa207d1a097fa6e4a867f74ee0761472ef1ceTimo Sirainen#include "wildcard-match.h"
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen#include "hash.h"
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen#include "str.h"
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen#include "strescape.h"
b1f37113a5760bee842c5a7678bb5fa6f5bd8b60Timo Sirainen#include "doveadm.h"
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen#include "doveadm-print.h"
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen#include "doveadm-who.h"
e28fa207d1a097fa6e4a867f74ee0761472ef1ceTimo Sirainen
37847ec8eaec9ad55c9df10ae109efe7b37ac573Timo Sirainen#include <stdio.h>
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen#include <unistd.h>
13d98ffa534f2e7d04a832c9d0153fc9c568b878Timo Sirainen
13d98ffa534f2e7d04a832c9d0153fc9c568b878Timo Sirainenstruct who_user {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *username;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *service;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ARRAY(struct ip_addr) ips;
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen ARRAY(pid_t) pids;
5694eeb99b69dea8033ca77ad69743c6b4871370Timo Sirainen unsigned int connection_count;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen};
5694eeb99b69dea8033ca77ad69743c6b4871370Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic void who_user_ip(const struct who_user *user, struct ip_addr *ip_r)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (array_count(&user->ips) == 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen i_zero(ip_r);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen else {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const struct ip_addr *ip = array_idx(&user->ips, 0);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen *ip_r = *ip;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen }
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
220e21750948941dc6e33b8f11b552fa21d7f81eTimo Sirainenstatic unsigned int who_user_hash(const struct who_user *user)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct ip_addr ip;
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen unsigned int hash = str_hash(user->service);
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen if (user->username[0] != '\0')
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen hash += str_hash(user->username);
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen else {
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen who_user_ip(user, &ip);
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen hash += net_ip_hash(&ip);
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen }
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen return hash;
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen}
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic int who_user_cmp(const struct who_user *user1,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const struct who_user *user2)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (strcmp(user1->username, user2->username) != 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return 1;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (strcmp(user1->service, user2->service) != 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return 1;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (user1->username[0] == '\0') {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* tracking only IP addresses, not usernames */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen struct ip_addr ip1, ip2;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen who_user_ip(user1, &ip1);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen who_user_ip(user2, &ip2);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return net_ip_cmp(&ip1, &ip2);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen }
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen return 0;
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen}
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainen
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainenstatic bool
8a0ad174adb1eb5108511b90e97f4e5f9089b0eeTimo Sirainenwho_user_has_ip(const struct who_user *user, const struct ip_addr *ip)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const struct ip_addr *ex_ip;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen array_foreach(&user->ips, ex_ip) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (net_ip_compare(ex_ip, ip))
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return TRUE;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen }
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return FALSE;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen}
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic int who_parse_line(const char *line, struct who_line *line_r)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen{
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *const *args = t_strsplit_tabescaped(line);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *ident = args[0];
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *pid_str = args[1];
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *refcount_str = args[2];
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen const char *p, *ip_str;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen i_zero(line_r);
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen /* ident = service/ip/username (imap, pop3)
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen or service/username (lmtp) */
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen p = strchr(ident, '/');
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen if (p == NULL)
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen return -1;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen if (str_to_pid(pid_str, &line_r->pid) < 0)
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen return -1;
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen line_r->service = t_strdup_until(ident, p++);
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen line_r->username = strchr(p, '/');
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (line_r->username == NULL) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* no IP */
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen line_r->username = p;
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen } else {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen ip_str = t_strdup_until(p, line_r->username++);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen (void)net_addr2ip(ip_str, &line_r->ip);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen }
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (str_to_uint(refcount_str, &line_r->refcount) < 0)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return -1;
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen return 0;
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic bool who_user_has_pid(struct who_user *user, pid_t pid)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const pid_t *ex_pid;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen array_foreach(&user->pids, ex_pid) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (*ex_pid == pid)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return TRUE;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen }
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen return FALSE;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen}
63e207529879438e9f4412d97cdc34bdc82a3702Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainenstatic void who_aggregate_line(struct who_context *ctx,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen const struct who_line *line)
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen{
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen struct who_user *user, lookup_user;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen lookup_user.username = line->username;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen lookup_user.service = line->service;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen user = hash_table_lookup(ctx->users, &lookup_user);
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen if (user == NULL) {
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen user = p_new(ctx->pool, struct who_user, 1);
a24519c36d5f8fa22f58b2c693ba547e8d175a54Timo Sirainen user->username = p_strdup(ctx->pool, line->username);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen user->service = p_strdup(ctx->pool, line->service);
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen p_array_init(&user->ips, ctx->pool, 3);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen p_array_init(&user->pids, ctx->pool, 8);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen hash_table_insert(ctx->users, user, user);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen user->connection_count += line->refcount;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (line->ip.family != 0 && !who_user_has_ip(user, &line->ip))
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen array_append(&user->ips, &line->ip, 1);
3fe67ec75ccae1230bb9eb9f16affc48377f6441Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (!who_user_has_pid(user, line->pid))
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen array_append(&user->pids, &line->pid, 1);
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen}
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainenint who_parse_args(struct who_context *ctx, const char *const *masks)
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen{
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen struct ip_addr net_ip;
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen unsigned int i, net_bits;
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen for (i = 0; masks[i] != NULL; i++) {
2a6dcd984104fed84bed8795ccdfabb20e41ce52Timo Sirainen if (net_parse_range(masks[i], &net_ip, &net_bits) == 0) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (ctx->filter.net_bits != 0) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_error("Multiple network masks not supported");
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen doveadm_exit_code = EX_USAGE;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return -1;
5214b67a7dabab87da74e04bb8b227f94b95bce4Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->filter.net_ip = net_ip;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->filter.net_bits = net_bits;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen } else {
27586e4785d56aeb76e1fd96af8db799688dc64aTimo Sirainen if (ctx->filter.username != NULL) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_error("Multiple username masks not supported");
5214b67a7dabab87da74e04bb8b227f94b95bce4Timo Sirainen doveadm_exit_code = EX_USAGE;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return -1;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->filter.username = masks[i];
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return 0;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainenvoid who_lookup(struct who_context *ctx, who_callback_t *callback)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n"
7920a47321690c932ffd4d286cd16b4048d22d41Timo Sirainen#define ANVIL_CMD ANVIL_HANDSHAKE"CONNECT-DUMP\n"
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk struct istream *input;
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk const char *line;
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk int fd;
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk fd = doveadm_connect(ctx->anvil_path);
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk net_set_nonblock(fd, FALSE);
7920a47321690c932ffd4d286cd16b4048d22d41Timo Sirainen if (write(fd, ANVIL_CMD, strlen(ANVIL_CMD)) < 0)
7920a47321690c932ffd4d286cd16b4048d22d41Timo Sirainen i_fatal("write(%s) failed: %m", ctx->anvil_path);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen input = i_stream_create_fd_autoclose(&fd, (size_t)-1);
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen if (*line == '\0')
db8b0a3f74a20528d66a3c4be7df920e5c4554c2Timo Sirainen break;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen T_BEGIN {
5666a3d6a7ea89362b8d9e8b39b15424cd9d6388Timo Sirainen struct who_line who_line;
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen if (who_parse_line(line, &who_line) < 0)
1701e3f91107051b1704721bf1dc1e32491faaf9Timo Sirainen i_error("Invalid input: %s", line);
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen else
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen callback(ctx, &who_line);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen } T_END;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen }
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (input->stream_errno != 0) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen i_fatal("read(%s) failed: %s", ctx->anvil_path,
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen i_stream_get_error(input));
9abf5be0962538e1f6f5c73c838ff677341da0c9Timo Sirainen }
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen i_stream_destroy(&input);
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen}
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainenstatic bool who_user_filter_match(const struct who_user *user,
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen const struct who_filter *filter)
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen{
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen if (filter->username != NULL) {
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen if (!wildcard_match_icase(user->username, filter->username))
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen return FALSE;
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen }
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen if (filter->net_bits > 0) {
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen const struct ip_addr *ip;
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen bool ret = FALSE;
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen array_foreach(&user->ips, ip) {
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen if (net_is_in_network(ip, &filter->net_ip,
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen filter->net_bits)) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen ret = TRUE;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen break;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen }
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen }
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen if (!ret)
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen return FALSE;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen }
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen return TRUE;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen}
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen
5c597df6aa8d81de4053c6986fab7739f3b44b20Timo Sirainenstatic void who_print_user(const struct who_user *user)
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen{
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen const struct ip_addr *ip;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen const pid_t *pid;
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen string_t *str = t_str_new(256);
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen doveadm_print(user->username);
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen doveadm_print(dec2str(user->connection_count));
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen doveadm_print(user->service);
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen str_append_c(str, '(');
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen array_foreach(&user->pids, pid)
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen str_printfa(str, "%ld ", (long)*pid);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (str_len(str) > 1)
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen str_truncate(str, str_len(str)-1);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen str_append_c(str, ')');
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen doveadm_print(str_c(str));
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen str_truncate(str, 0);
5214b67a7dabab87da74e04bb8b227f94b95bce4Timo Sirainen str_append_c(str, '(');
5214b67a7dabab87da74e04bb8b227f94b95bce4Timo Sirainen array_foreach(&user->ips, ip)
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen str_printfa(str, "%s ", net_ip2addr(ip));
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (str_len(str) > 1)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen str_truncate(str, str_len(str)-1);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen str_append_c(str, ')');
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen doveadm_print(str_c(str));
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen}
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainenstatic void who_print(struct who_context *ctx)
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen{
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen struct hash_iterate_context *iter;
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen struct who_user *user;
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen doveadm_print_header("username", "username", 0);
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen doveadm_print_header("connections", "#",
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen DOVEADM_PRINT_HEADER_FLAG_RIGHT_JUSTIFY);
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen doveadm_print_header("service", "proto", 0);
c58c12049c883b281c088d47a2a7278c21c390e1Timo Sirainen doveadm_print_header("pids", "(pids)", 0);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen doveadm_print_header("ips", "(ips)", 0);
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen iter = hash_table_iterate_init(ctx->users);
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen while (hash_table_iterate(iter, ctx->users, &user, &user)) {
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen if (who_user_filter_match(user, &ctx->filter)) T_BEGIN {
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen who_print_user(user);
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen } T_END;
1c1cecd3dfaf71b0c9499b044023e631841e88aaTimo Sirainen }
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen hash_table_iterate_deinit(&iter);
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen}
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainenbool who_line_filter_match(const struct who_line *line,
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen const struct who_filter *filter)
c1d19144dd7b1de6822df6ed1d10af0c9cb38840Timo Sirainen{
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (filter->username != NULL) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (!wildcard_match_icase(line->username, filter->username))
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen return FALSE;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen }
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen if (filter->net_bits > 0) {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen if (!net_is_in_network(&line->ip, &filter->net_ip,
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen filter->net_bits))
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen return FALSE;
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return TRUE;
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen}
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainenstatic void who_print_line(struct who_context *ctx,
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen const struct who_line *line)
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen{
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen unsigned int i;
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen
5494a6bc149da8f02fd25c0434a9d612ac33f659Timo Sirainen if (!who_line_filter_match(line, &ctx->filter))
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen return;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen for (i = 0; i < line->refcount; i++) T_BEGIN {
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen doveadm_print(line->username);
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen doveadm_print(line->service);
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen doveadm_print(dec2str(line->pid));
94b0ff77495c3ed14bdd4b5d7ae1eb37e8c9efb5Timo Sirainen doveadm_print(net_ip2addr(&line->ip));
02e61e13a8360a9d3ec92c5fa5ae60c0f0181b71Timo Sirainen } T_END;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen}
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainenstatic void cmd_who(struct doveadm_cmd_context *cctx)
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen{
b365bd121cdc87f63e1dd47c5085a27091118e00Timo Sirainen const char *const *masks;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen struct who_context ctx;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen bool separate_connections = FALSE;
adb6413686e52e00dded4932babcc08ff041876bTimo Sirainen
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen i_zero(&ctx);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if (!doveadm_cmd_param_str(cctx, "socket-path", &(ctx.anvil_path)))
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen ctx.anvil_path = t_strconcat(doveadm_settings->base_dir, "/anvil", NULL);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen (void)doveadm_cmd_param_bool(cctx, "separate-connections", &separate_connections);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen ctx.pool = pool_alloconly_create("who users", 10240);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen hash_table_create(&ctx.users, ctx.pool, 0, who_user_hash, who_user_cmp);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if (doveadm_cmd_param_array(cctx, "mask", &masks)) {
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen if (who_parse_args(&ctx, masks) != 0) {
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen hash_table_destroy(&ctx.users);
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen pool_unref(&ctx.pool);
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen return;
1433bf361ddb0bba8878c8ada5726d0284edad57Timo Sirainen }
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen }
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen if (!separate_connections) {
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen who_lookup(&ctx, who_aggregate_line);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen who_print(&ctx);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen } else {
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen doveadm_print_header("username", "username",
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen DOVEADM_PRINT_HEADER_FLAG_EXPAND);
4a514fb20e04df397842cde11cc9ea92abfe9728Timo Sirainen doveadm_print_header("service", "proto", 0);
94d8e51119003d2bc5a100c663f90141f297385dTimo Sirainen doveadm_print_header_simple("pid");
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen doveadm_print_header_simple("ip");
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen who_lookup(&ctx, who_print_line);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen }
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen hash_table_destroy(&ctx.users);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen pool_unref(&ctx.pool);
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen}
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainenstruct doveadm_cmd_ver2 doveadm_cmd_who_ver2 = {
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen .name = "who",
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen .cmd = cmd_who,
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo Sirainen .usage = "[-a <anvil socket path>] [-1] [<user mask>] [<ip/bits>]",
0b4e1043e596bfb36d999dacbf1d4d63ee96d75fTimo SirainenDOVEADM_CMD_PARAMS_START
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo SirainenDOVEADM_CMD_PARAM('a',"socket-path", CMD_PARAM_STR, 0)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo SirainenDOVEADM_CMD_PARAM('1',"separate-connections", CMD_PARAM_BOOL, 0)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo SirainenDOVEADM_CMD_PARAM('\0',"mask", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL)
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo SirainenDOVEADM_CMD_PARAMS_END
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen};
dc5606fb66d30a659459446b6ca1a8b4f1146052Timo Sirainen