doveadm-stats.c (dovecot-2.3.16) | : | doveadm-stats.c (dovecot-2.3.17) | ||
---|---|---|---|---|
skipping to change at line 12 | skipping to change at line 12 | |||
#include "lib.h" | #include "lib.h" | |||
#include "net.h" | #include "net.h" | |||
#include "istream.h" | #include "istream.h" | |||
#include "str.h" | #include "str.h" | |||
#include "strescape.h" | #include "strescape.h" | |||
#include "write-full.h" | #include "write-full.h" | |||
#include "master-service.h" | #include "master-service.h" | |||
#include "doveadm.h" | #include "doveadm.h" | |||
#include "doveadm-print.h" | #include "doveadm-print.h" | |||
#include "stats-settings.h" | ||||
#include <math.h> | #include <math.h> | |||
#define DOVEADM_DUMP_DEFAULT_FIELDS \ | #define DOVEADM_DUMP_DEFAULT_FIELDS \ | |||
"count sum min max avg median stddev %95" | "count sum min max avg median stddev %95" | |||
enum doveadm_dump_field_type { | enum doveadm_dump_field_type { | |||
DOVEADM_DUMP_FIELD_TYPE_PASSTHROUGH = 0, | DOVEADM_DUMP_FIELD_TYPE_PASSTHROUGH = 0, | |||
DOVEADM_DUMP_FIELD_TYPE_STDDEV, | DOVEADM_DUMP_FIELD_TYPE_STDDEV, | |||
}; | }; | |||
struct stats_cmd_context { | ||||
string_t *cmd; | ||||
struct doveadm_cmd_context *cctx; | ||||
struct istream *input; | ||||
const char *path; | ||||
void *data; | ||||
}; | ||||
struct dump_data { | ||||
const char **fields; | ||||
unsigned int field_count; | ||||
enum doveadm_dump_field_type *field_types; | ||||
}; | ||||
struct stats_cmd_vfuncs { | ||||
int (*build_cmd)(struct stats_cmd_context *ctx, const char **error_r); | ||||
void (*process_response)(struct stats_cmd_context *ctx); | ||||
}; | ||||
static int build_stats_dump_cmd(struct stats_cmd_context *ctx, const char **erro | ||||
r_r); | ||||
static int build_stats_add_cmd(struct stats_cmd_context *ctx, const char **error | ||||
_r); | ||||
static int build_stats_remove_cmd(struct stats_cmd_context *ctx, const char **er | ||||
ror_r); | ||||
static void stats_dump_process_response(struct stats_cmd_context *ctx); | ||||
static void stats_modify_process_response(struct stats_cmd_context *ctx); | ||||
static void stats_send_cmd(struct stats_cmd_context *ctx); | ||||
static struct stats_cmd_vfuncs dump_vfuncs = { | ||||
.build_cmd = build_stats_dump_cmd, | ||||
.process_response = stats_dump_process_response | ||||
}; | ||||
static struct stats_cmd_vfuncs add_vfuncs = { | ||||
.build_cmd = build_stats_add_cmd, | ||||
.process_response = stats_modify_process_response | ||||
}; | ||||
static struct stats_cmd_vfuncs remove_vfuncs = { | ||||
.build_cmd = build_stats_remove_cmd, | ||||
.process_response = stats_modify_process_response | ||||
}; | ||||
static string_t *init_stats_cmd(void) | ||||
{ | ||||
string_t *cmd = t_str_new(128); | ||||
str_append(cmd, "VERSION\tstats-reader-client\t2\t0\n"); | ||||
return cmd; | ||||
} | ||||
static void stats_exec_cmd(struct doveadm_cmd_context *cctx, | ||||
struct stats_cmd_vfuncs *vfuncs) | ||||
{ | ||||
struct stats_cmd_context ctx; | ||||
const char *build_cmd_error; | ||||
ctx.cctx = cctx; | ||||
if (vfuncs->build_cmd(&ctx, &build_cmd_error) < 0) { | ||||
i_error("%s", build_cmd_error); | ||||
return; | ||||
} | ||||
stats_send_cmd(&ctx); | ||||
vfuncs->process_response(&ctx); | ||||
i_stream_destroy(&ctx.input); | ||||
} | ||||
static void handle_disconnection(struct stats_cmd_context *ctx) | ||||
{ | ||||
i_error("read(%s) failed: %s", ctx->path, | ||||
i_stream_get_disconnect_reason(ctx->input)); | ||||
} | ||||
static void stats_send_cmd(struct stats_cmd_context *ctx) | ||||
{ | ||||
int fd; | ||||
const char *line; | ||||
if (!doveadm_cmd_param_str(ctx->cctx, "socket-path", &ctx->path)) | ||||
ctx->path = t_strconcat(doveadm_settings->base_dir, | ||||
"/stats-reader", NULL); | ||||
fd = doveadm_connect(ctx->path); | ||||
net_set_nonblock(fd, FALSE); | ||||
if (write_full(fd, str_data(ctx->cmd), str_len(ctx->cmd)) < 0) | ||||
i_fatal("write(%s) failed %m", ctx->path); | ||||
ctx->input = i_stream_create_fd_autoclose(&fd, SIZE_MAX); | ||||
if ((line = i_stream_read_next_line(ctx->input)) == NULL) | ||||
i_fatal("%s: Failed to read VERSION line", ctx->path); | ||||
else if (!version_string_verify(line, "stats-reader-server", 2)) { | ||||
i_fatal_status(EX_PROTOCOL, | ||||
"%s is not a compatible stats-reader socket", ctx->path); | ||||
} | ||||
} | ||||
static void dump_timing(const char *const **args, | static void dump_timing(const char *const **args, | |||
const enum doveadm_dump_field_type field_types[], | const enum doveadm_dump_field_type field_types[], | |||
unsigned int fields_count) | unsigned int fields_count) | |||
{ | { | |||
unsigned int i, args_count = str_array_length(*args); | unsigned int i, args_count = str_array_length(*args); | |||
if (args_count > fields_count) | if (args_count > fields_count) | |||
args_count = fields_count; | args_count = fields_count; | |||
for (i = 0; i < args_count; i++) { | for (i = 0; i < args_count; i++) { | |||
const char *value = (*args)[i]; | const char *value = (*args)[i]; | |||
skipping to change at line 48 | skipping to change at line 142 | |||
double variance = strtod(value, NULL); | double variance = strtod(value, NULL); | |||
value = t_strdup_printf("%.02f", sqrt(variance)); | value = t_strdup_printf("%.02f", sqrt(variance)); | |||
break; | break; | |||
} | } | |||
} | } | |||
doveadm_print(value); | doveadm_print(value); | |||
} | } | |||
*args += args_count; | *args += args_count; | |||
} | } | |||
static void stats_dump(const char *path, const char *const *fields, bool reset) | static int build_stats_dump_cmd(struct stats_cmd_context *ctx, | |||
const char **error_r ATTR_UNUSED) | ||||
{ | { | |||
struct istream *input; | bool reset; | |||
string_t *cmd = t_str_new(128); | struct dump_data *data = t_new(struct dump_data, 1); | |||
unsigned int i, fields_count = str_array_length(fields); | const char *fields_raw; | |||
enum doveadm_dump_field_type field_types[fields_count]; | const char **fields; | |||
char *line; | if (!doveadm_cmd_param_bool(ctx->cctx, "reset", &reset)) | |||
int fd; | reset = FALSE; | |||
if (!doveadm_cmd_param_str(ctx->cctx, "fields", &fields_raw)) | ||||
fields_raw = DOVEADM_DUMP_DEFAULT_FIELDS; | ||||
fd = doveadm_connect(path); | fields = t_strsplit_spaces(fields_raw, ", "); | |||
net_set_nonblock(fd, FALSE); | data->fields = fields; | |||
str_append(cmd, "VERSION\tstats-reader-client\t2\t0\n"); | data->field_count = str_array_length(fields); | |||
str_append(cmd, reset ? "DUMP-RESET" : "DUMP"); | data->field_types = | |||
i_zero(&field_types); | t_malloc0(sizeof(enum doveadm_dump_field_type) * data->field_coun | |||
for (i = 0; i < fields_count; i++) { | t); | |||
str_append_c(cmd, '\t'); | ctx->data = data; | |||
ctx->cmd = init_stats_cmd(); | ||||
str_append(ctx->cmd, reset ? "DUMP-RESET" : "DUMP"); | ||||
unsigned int i; | ||||
for (i = 0; i < data->field_count; i++) { | ||||
str_append_c(ctx->cmd, '\t'); | ||||
if (strcmp(fields[i], "stddev") == 0) { | if (strcmp(fields[i], "stddev") == 0) { | |||
field_types[i] = DOVEADM_DUMP_FIELD_TYPE_STDDEV; | data->field_types[i] = DOVEADM_DUMP_FIELD_TYPE_STDDEV; | |||
str_append(cmd, "variance"); | str_append(ctx->cmd, "variance"); | |||
} else { | } else { | |||
str_append_tabescaped(cmd, fields[i]); | str_append_tabescaped(ctx->cmd, fields[i]); | |||
} | } | |||
} | } | |||
str_append_c(cmd, '\n'); | str_append_c(ctx->cmd, '\n'); | |||
if (write_full(fd, str_data(cmd), str_len(cmd)) < 0) | return 0; | |||
i_fatal("write(%s) failed: %m", path); | } | |||
input = i_stream_create_fd_autoclose(&fd, SIZE_MAX); | static void stats_dump_process_response(struct stats_cmd_context *ctx) | |||
if ((line = i_stream_read_next_line(input)) == NULL) | { | |||
i_fatal("%s: Failed to read VERSION line", path); | unsigned int i; | |||
else if (!version_string_verify(line, "stats-reader-server", 2)) { | char *line; | |||
i_fatal_status(EX_PROTOCOL, | struct dump_data *data = ctx->data; | |||
"%s is not a compatible stats-reader socket", path); | ||||
} | ||||
doveadm_print_init(DOVEADM_PRINT_TYPE_TAB); | ||||
doveadm_print_header_simple("metric_name"); | doveadm_print_header_simple("metric_name"); | |||
doveadm_print_header_simple("field"); | doveadm_print_header_simple("field"); | |||
for (i = 0; i < fields_count; i++) | for (i = 0; i < data->field_count; i++) | |||
doveadm_print_header(fields[i], fields[i], DOVEADM_PRINT_HEADER_F | doveadm_print_header(data->fields[i], data->fields[i], | |||
LAG_NUMBER); | DOVEADM_PRINT_HEADER_FLAG_NUMBER); | |||
while ((line = i_stream_read_next_line(input)) != NULL) { | while ((line = i_stream_read_next_line(ctx->input)) != NULL) { | |||
if (line[0] == '\0') | if (line[0] == '\0') | |||
break; | break; | |||
T_BEGIN { | T_BEGIN { | |||
const char *const *args = | const char *const *args = | |||
t_strsplit_tabescaped_inplace(line); | t_strsplit_tabescaped_inplace(line); | |||
const char *metric_name = args[0]; | const char *metric_name = args[0]; | |||
doveadm_print(metric_name); args++; | doveadm_print(metric_name); args++; | |||
doveadm_print("duration"); | doveadm_print("duration"); | |||
dump_timing(&args, field_types, fields_count); | dump_timing(&args, data->field_types, data->field_count); | |||
while (*args != NULL) { | while (*args != NULL) { | |||
doveadm_print(metric_name); | doveadm_print(metric_name); | |||
doveadm_print(*args); args++; | doveadm_print(*args); args++; | |||
dump_timing(&args, field_types, fields_count); | dump_timing(&args, data->field_types, data->field _count); | |||
} | } | |||
} T_END; | } T_END; | |||
} | } | |||
if (line == NULL) | ||||
handle_disconnection(ctx); | ||||
} | ||||
static int build_stats_add_cmd(struct stats_cmd_context *ctx, | ||||
const char **error_r) | ||||
{ | ||||
unsigned int i; | ||||
const char *parameter; | ||||
struct { | ||||
const char *name; | ||||
const char *default_val; | ||||
} params[] = { | ||||
{ "name", "" }, | ||||
{ "description", "" }, | ||||
{ "fields", "" }, | ||||
{ "group_by", "" }, | ||||
{ "filter", "" }, | ||||
{ "exporter", "" }, | ||||
/* Default exporter-include is to be modified | ||||
together with stats-settings */ | ||||
{ "exporter-include", | ||||
STATS_METRIC_SETTINGS_DEFAULT_EXPORTER_INCLUDE }, | ||||
}; | ||||
ctx->cmd = init_stats_cmd(); | ||||
str_append(ctx->cmd, "METRICS-ADD"); | ||||
for (i = 0; i < N_ELEMENTS(params); i++) { | ||||
if (!doveadm_cmd_param_str(ctx->cctx, params[i].name, ¶meter) | ||||
) | ||||
parameter = params[i].default_val; | ||||
if (parameter[0] == '\0' && | ||||
(strcmp(params[i].name, "name") == 0 || | ||||
strcmp(params[i].name, "filter") == 0)) { | ||||
*error_r = | ||||
t_strdup_printf("stats add: missing %s parameter" | ||||
, | ||||
params[i].name); | ||||
return -1; | ||||
} | ||||
str_append_c(ctx->cmd, '\t'); | ||||
str_append_tabescaped(ctx->cmd, parameter); | ||||
} | ||||
if (input->stream_errno != 0) | str_append_c(ctx->cmd, '\n'); | |||
i_fatal("read(%s) failed: %s", path, i_stream_get_error(input)); | return 0; | |||
i_stream_destroy(&input); | ||||
} | } | |||
static void | static void stats_modify_process_response(struct stats_cmd_context *ctx) | |||
doveadm_cmd_stats_dump(struct doveadm_cmd_context *cctx) | ||||
{ | { | |||
const char *path, *fields; | const char *line = i_stream_read_next_line(ctx->input); | |||
bool reset; | if (line == NULL) { | |||
handle_disconnection(ctx); | ||||
return; | ||||
} | ||||
if (line[0] == '-') | ||||
i_error("%s", ++line); | ||||
else if (line[0] != '+') | ||||
i_error("Invalid response: %s", line); | ||||
} | ||||
if (!doveadm_cmd_param_str(cctx, "socket-path", &path)) | static int build_stats_remove_cmd(struct stats_cmd_context *ctx, | |||
path = t_strconcat(doveadm_settings->base_dir, "/stats-reader", N | const char **error_r) | |||
ULL); | { | |||
if (!doveadm_cmd_param_bool(cctx, "reset", &reset)) | const char *name; | |||
reset = FALSE; | ||||
if (!doveadm_cmd_param_str(cctx, "fields", &fields)) | ctx->cmd = init_stats_cmd(); | |||
fields = DOVEADM_DUMP_DEFAULT_FIELDS; | str_append(ctx->cmd, "METRICS-REMOVE\t"); | |||
doveadm_print_init(DOVEADM_PRINT_TYPE_TAB); | if (!doveadm_cmd_param_str(ctx->cctx, "name", &name)) { | |||
stats_dump(path, t_strsplit_spaces(fields, ", "), reset); | *error_r = "stats remove: missing name parameter"; | |||
return; | return -1; | |||
} | ||||
str_append_tabescaped(ctx->cmd, name); | ||||
str_append_c(ctx->cmd, '\n'); | ||||
return 0; | ||||
} | ||||
static void doveadm_cmd_stats_dump(struct doveadm_cmd_context *cctx) | ||||
{ | ||||
stats_exec_cmd(cctx, &dump_vfuncs); | ||||
} | ||||
static void doveadm_cmd_stats_add(struct doveadm_cmd_context *cctx) | ||||
{ | ||||
stats_exec_cmd(cctx, &add_vfuncs); | ||||
} | ||||
static void doveadm_cmd_stats_remove(struct doveadm_cmd_context *cctx) | ||||
{ | ||||
stats_exec_cmd(cctx, &remove_vfuncs); | ||||
} | } | |||
struct doveadm_cmd_ver2 doveadm_cmd_stats_dump_ver2 = { | struct doveadm_cmd_ver2 doveadm_cmd_stats_dump_ver2 = { | |||
.cmd = doveadm_cmd_stats_dump, | .cmd = doveadm_cmd_stats_dump, | |||
.name = "stats dump", | .name = "stats dump", | |||
.usage = "[-s <stats socket path>] [-r] [-f <fields>]", | .usage = "[-s <stats socket path>] [-r] [-f <fields>]", | |||
DOVEADM_CMD_PARAMS_START | DOVEADM_CMD_PARAMS_START | |||
DOVEADM_CMD_PARAM('s', "socket-path", CMD_PARAM_STR, 0) | DOVEADM_CMD_PARAM('s', "socket-path", CMD_PARAM_STR, 0) | |||
DOVEADM_CMD_PARAM('r', "reset", CMD_PARAM_BOOL, 0) | DOVEADM_CMD_PARAM('r', "reset", CMD_PARAM_BOOL, 0) | |||
DOVEADM_CMD_PARAM('f', "fields", CMD_PARAM_STR, 0) | DOVEADM_CMD_PARAM('f', "fields", CMD_PARAM_STR, 0) | |||
DOVEADM_CMD_PARAMS_END | DOVEADM_CMD_PARAMS_END | |||
}; | }; | |||
struct doveadm_cmd_ver2 doveadm_cmd_stats_add_ver2 = { | ||||
.cmd = doveadm_cmd_stats_add, | ||||
.name = "stats add", | ||||
.usage = "[--description <string>] [--exporter <name> [--exporter-include | ||||
<fields>]] [--fields <fields>] [--group_by <fields>] <name> <filter>", | ||||
DOVEADM_CMD_PARAMS_START | ||||
DOVEADM_CMD_PARAM('\0', "name", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) | ||||
DOVEADM_CMD_PARAM('\0', "filter", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) | ||||
DOVEADM_CMD_PARAM('\0', "exporter", CMD_PARAM_STR, 0) | ||||
DOVEADM_CMD_PARAM('\0', "exporter-include", CMD_PARAM_STR, 0) | ||||
DOVEADM_CMD_PARAM('\0', "description", CMD_PARAM_STR, 0) | ||||
DOVEADM_CMD_PARAM('\0', "fields", CMD_PARAM_STR, 0) | ||||
DOVEADM_CMD_PARAM('\0', "group-by", CMD_PARAM_STR, 0) | ||||
DOVEADM_CMD_PARAMS_END | ||||
}; | ||||
struct doveadm_cmd_ver2 doveadm_cmd_stats_remove_ver2 = { | ||||
.cmd = doveadm_cmd_stats_remove, | ||||
.name = "stats remove", | ||||
.usage = "<name>", | ||||
DOVEADM_CMD_PARAMS_START | ||||
DOVEADM_CMD_PARAM('\0', "name", CMD_PARAM_STR, CMD_PARAM_FLAG_POSITIONAL) | ||||
DOVEADM_CMD_PARAMS_END | ||||
}; | ||||
End of changes. 21 change blocks. | ||||
51 lines changed or deleted | 223 lines changed or added |