/*
* Unix SMB/CIFS implementation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "replace.h"
#include "system/filesys.h"
#include
#include "source3/include/smbprofile.h"
#include "event2/event.h"
#include "event2/http.h"
#include "event2/http_struct.h"
#include "event2/buffer.h"
struct export_state {
struct evbuffer *buf;
bool sent_help_authentication : 1;
bool sent_help_authentication_failed : 1;
bool sent_help_cpu_seconds : 1;
bool sent_help_smb1_request_total : 1;
bool sent_help_smb2_request_inbytes : 1;
bool sent_help_smb2_request_outbytes : 1;
bool sent_help_smb2_request_hist : 1;
bool sent_help_smb2_request_failed : 1;
};
static void export_count(const char *name,
const struct smbprofile_stats_count *val,
struct export_state *state)
{
int cmp;
cmp = strcmp(name, "authentication");
if (cmp == 0) {
if (!state->sent_help_authentication) {
evbuffer_add_printf(
state->buf,
"# HELP smb_authentications Total number "
"of authentication requests\n"
"# TYPE smb_authentications counter\n");
state->sent_help_authentication = true;
}
evbuffer_add_printf(
state->buf,
"smb_authentications %" PRIu64"\n",
val->count);
}
cmp = strcmp(name, "authentication_failed");
if (cmp == 0) {
if (!state->sent_help_authentication_failed) {
evbuffer_add_printf(
state->buf,
"# HELP smb_authentications_failed Total "
"number of failed authentication requests\n"
"# TYPE smb_authentications_failed counter\n");
state->sent_help_authentication_failed = true;
}
evbuffer_add_printf(
state->buf,
"smb_authentications_failed %" PRIu64"\n",
val->count);
}
}
static void export_time(const char *name,
const struct smbprofile_stats_time *val,
struct export_state *state)
{
bool is_cpu;
is_cpu = (strncmp(name, "cpu_", 4) == 0);
if (is_cpu) {
const char *mode = name + 4;
if (!state->sent_help_cpu_seconds) {
evbuffer_add_printf(
state->buf,
"# HELP smb_cpu_seconds_total Seconds spent "
"in worker smbds\n"
"# TYPE smb_cpu_seconds_total counter\n");
state->sent_help_cpu_seconds = true;
}
evbuffer_add_printf(
state->buf,
"smb_cpu_seconds_total { mode=\"%s\" } %f\n",
mode,
((double)val->time) / 1000000);
}
}
static void export_basic(const char *svc,
const char *name,
const struct smbprofile_stats_basic *val,
struct export_state *state)
{
bool is_smb;
is_smb = (strncmp(name, "SMB", 3) == 0);
if (is_smb) {
if (!state->sent_help_smb1_request_total) {
evbuffer_add_printf(
state->buf,
"# HELP smb_smb1_request_total Number of "
"SMB1 requests\n"
"# TYPE smb_smb1_request_total counter\n");
state->sent_help_smb1_request_total = true;
}
evbuffer_add_printf(state->buf,
"smb_smb1_request_total { "
"share=\"%s\",operation=\"%s\" } "
"%" PRIu64 "\n",
svc,
name + 3,
val->count);
}
return;
}
static void export_iobytes_inbytes(const char *svc,
const char *name,
const struct smbprofile_stats_iobytes *val,
struct export_state *state)
{
bool is_smb2;
is_smb2 = (strncmp(name, "smb2_", 5) == 0);
if (is_smb2) {
if (!state->sent_help_smb2_request_inbytes) {
evbuffer_add_printf(
state->buf,
"# HELP smb_smb2_request_inbytes Bytes "
"received for SMB2 requests\n"
"# TYPE smb_smb2_request_inbytes counter\n");
state->sent_help_smb2_request_inbytes = true;
}
evbuffer_add_printf(state->buf,
"smb_smb2_request_inbytes { "
"share=\"%s\",operation=\"%s\" } "
"%" PRIu64 "\n",
svc,
name + 5,
val->inbytes);
}
}
static void export_iobytes_outbytes(const char *svc,
const char *name,
const struct smbprofile_stats_iobytes *val,
struct export_state *state)
{
bool is_smb2;
is_smb2 = (strncmp(name, "smb2_", 5) == 0);
if (is_smb2) {
if (!state->sent_help_smb2_request_outbytes) {
evbuffer_add_printf(
state->buf,
"# HELP smb_smb2_request_outbytes Bytes "
"received for SMB2 requests\n"
"# TYPE smb_smb2_request_outbytes counter\n");
state->sent_help_smb2_request_outbytes = true;
}
evbuffer_add_printf(state->buf,
"smb_smb2_request_outbytes { "
"share=\"%s\",operation=\"%s\" } "
"%" PRIu64 "\n",
svc,
name + 5,
val->outbytes);
}
}
static void export_iobytes_buckets(const char *svc,
const char *name,
const struct smbprofile_stats_iobytes *val,
struct export_state *state)
{
bool is_smb2;
is_smb2 = (strncmp(name, "smb2_", 5) == 0);
if (is_smb2) {
int i;
if (!state->sent_help_smb2_request_hist) {
evbuffer_add_printf(
state->buf,
"# HELP smb_smb2_request_duration_microseconds "
"Histogram of latencies for SMB2 requests\n"
"# TYPE smb_smb2_request_duration_microseconds "
"histogram\n");
state->sent_help_smb2_request_hist = true;
}
for (i=0; i<9; i++) {
evbuffer_add_printf(
state->buf,
"smb_smb2_request_duration_microseconds_bucket "
"{share=\"%s\",operation=\"%s\",le=\"%d000\"} "
"%" PRIu64 "\n",
svc,
name + 5,
1 << i,
val->buckets[i]);
}
evbuffer_add_printf(
state->buf,
"smb_smb2_request_duration_microseconds_bucket "
"{share=\"%s\",operation=\"%s\",le=\"+Inf\"} "
"%" PRIu64 "\n",
svc,
name + 5,
val->buckets[9]);
evbuffer_add_printf(
state->buf,
"smb_smb2_request_duration_microseconds_sum "
"{share=\"%s\",operation=\"%s\"} "
"%" PRIu64 "\n",
svc,
name + 5,
val->time);
evbuffer_add_printf(
state->buf,
"smb_smb2_request_duration_microseconds_count "
"{share=\"%s\",operation=\"%s\"} "
"%" PRIu64 "\n",
svc,
name + 5,
val->count);
}
}
static void export_iobytes_failed(const char *svc,
const char *name,
const struct smbprofile_stats_iobytes *val,
struct export_state *state)
{
bool is_smb2;
is_smb2 = (strncmp(name, "smb2_", 5) == 0);
if (is_smb2) {
if (!state->sent_help_smb2_request_failed) {
evbuffer_add_printf(
state->buf,
"# HELP smb_smb2_request_failed Number "
"of failed SMB2 requests\n"
"# TYPE smb_smb2_request_failed counter\n");
state->sent_help_smb2_request_failed = true;
}
evbuffer_add_printf(state->buf,
"smb_smb2_request_failed { "
"share=\"%s\",operation=\"%s\" } "
"%" PRIu64 "\n",
svc,
name + 5,
val->failed_count);
}
}
static void export_profile_stats(const struct profile_stats *stats,
struct export_state *state)
{
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name) \
do { \
export_count(#name, &stats->values.name##_stats, state); \
} while (0);
#define SMBPROFILE_STATS_TIME(name) \
do { \
export_time(#name, &stats->values.name##_stats, state); \
} while (0);
#define SMBPROFILE_STATS_BASIC(name) \
do { \
export_basic("", #name, &stats->values.name##_stats, state); \
} while (0);
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name)
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_ALL_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_inbytes("", \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_ALL_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_outbytes("", \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_ALL_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_buckets("", \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_ALL_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_failed("", \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_ALL_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
}
static void export_profile_persvc_stats(const struct profile_stats *stats,
const char *svc,
struct export_state *state)
{
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_BASIC(name) \
do { \
export_basic(svc, #name, &stats->values.name##_stats, state); \
} while (0);
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name)
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_PERSVC_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_inbytes(svc, \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_ALL_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_outbytes(svc, \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_PERSVC_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_buckets(svc, \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_PERSVC_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
#define SMBPROFILE_STATS_START
#define SMBPROFILE_STATS_SECTION_START(name, display)
#define SMBPROFILE_STATS_COUNT(name)
#define SMBPROFILE_STATS_TIME(name)
#define SMBPROFILE_STATS_BASIC(name)
#define SMBPROFILE_STATS_BYTES(name)
#define SMBPROFILE_STATS_IOBYTES(name) \
do { \
export_iobytes_failed(svc, \
#name, \
&stats->values.name##_stats, \
state); \
} while (0);
#define SMBPROFILE_STATS_SECTION_END
#define SMBPROFILE_STATS_END
SMBPROFILE_STATS_PERSVC_SECTIONS
#undef SMBPROFILE_STATS_START
#undef SMBPROFILE_STATS_SECTION_START
#undef SMBPROFILE_STATS_COUNT
#undef SMBPROFILE_STATS_TIME
#undef SMBPROFILE_STATS_BASIC
#undef SMBPROFILE_STATS_BYTES
#undef SMBPROFILE_STATS_IOBYTES
#undef SMBPROFILE_STATS_SECTION_END
#undef SMBPROFILE_STATS_END
}
static int export_profile_persvc(const char *key,
const struct profile_stats *stats,
void *userp)
{
struct export_state *state = userp;
char *svc = NULL;
char *sep = NULL;
svc = strdup(key);
if (svc == NULL) {
return -1;
}
/* convert dbkey into service name only */
sep = strchr(svc, ':');
if (sep != NULL) {
*sep = '\0';
}
export_profile_persvc_stats(stats, svc, state);
free(svc);
return 0;
}
static void metrics_handler(struct evhttp_request *req, void *arg)
{
struct export_state state = {.buf = NULL};
const char *tdbfilename = arg;
struct tdb_context *tdb = NULL;
struct profile_stats stats = {.magic = 0};
uint64_t magic;
size_t num_workers;
int ret;
evhttp_add_header(req->output_headers,
"Content-Type",
"text/plain; charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "close");
state.buf = evbuffer_new();
if (state.buf == NULL) {
evhttp_send_reply(req, HTTP_INTERNAL, "NOMEM", state.buf);
return;
}
/*
* Open with O_RDWR although we won't write, but we want
* locking.
*/
tdb = tdb_open(tdbfilename,
0,
TDB_CLEAR_IF_FIRST | TDB_MUTEX_LOCKING,
O_RDWR,
0);
if (tdb == NULL) {
evbuffer_add_printf(state.buf,
"Could not open %s: %s\n",
tdbfilename,
strerror(errno));
evhttp_send_reply(req, HTTP_INTERNAL, "TDB failure", state.buf);
evbuffer_free(state.buf);
return;
}
ret = smbprofile_magic(&stats, &magic);
if (ret != 0) {
evbuffer_add_printf(state.buf, "Could calculate magic");
evhttp_send_reply(req,
HTTP_INTERNAL,
"magic failure",
state.buf);
evbuffer_free(state.buf);
return;
}
num_workers = smbprofile_collect_tdb(tdb, magic, &stats);
tdb_close(tdb);
evbuffer_add_printf(state.buf,
"# HELP smb_worker_smbd_num Number of worker smbds "
"serving clients\n"
"# TYPE smb_worker_smbd_num gauge\n"
"smb_worker_smbd_num %zu\n",
num_workers);
evbuffer_add_printf(
state.buf,
"# HELP smb_num_authenticated_sessions Number of users "
"logged in\n"
"# TYPE smb_num_authenticated_sessions gauge\n"
"smb_num_authenticated_sessions %" PRIu64 "\n",
stats.values.num_sessions_stats.count);
evbuffer_add_printf(
state.buf,
"# HELP smb_num_tree_connects Number of share connections\n"
"# TYPE smb_num_tree_connects gauge\n"
"smb_num_tree_connects %" PRIu64 "\n",
stats.values.num_tcons_stats.count);
evbuffer_add_printf(state.buf,
"# HELP smb_num_open_files Number of open files\n"
"# TYPE smb_num_open_files gauge\n"
"smb_num_open_files %" PRIu64 "\n",
stats.values.num_files_stats.count);
export_profile_stats(&stats, &state);
/*
* Re-open TDB file with read-only mode and iterate-export per-share
* metrics. Ignore failure case this time.
*/
tdb = tdb_open(tdbfilename, 0, 0, O_RDONLY, 0);
if (tdb == NULL) {
goto out;
}
smbprofile_persvc_collect_tdb(tdb, export_profile_persvc, &state);
tdb_close(tdb);
out:
evhttp_send_reply(req, HTTP_OK, "OK", state.buf);
evbuffer_free(state.buf);
}
static void default_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *buf = NULL;
evhttp_add_header(req->output_headers,
"Content-Type",
"text/plain; charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "close");
buf = evbuffer_new();
if (buf != NULL) {
evbuffer_add_printf(buf, "404 Not Found\n");
}
evhttp_send_reply(req, HTTP_NOTFOUND, "OK", buf);
if (buf != NULL) {
evbuffer_free(buf);
}
}
int main(int argc, char *argv[])
{
struct event_base *ev = NULL;
struct evhttp *http_server = NULL;
char *tdbfilename = NULL;
const char *addr = "127.0.0.1";
uint16_t port = 9922;
int ret, c;
while ((c = getopt(argc, argv, "a:p:")) != -1) {
switch (c) {
case 'a':
addr = optarg;
break;
case 'p':
port = atoi(optarg);
break;
}
}
if (optind != argc - 1) {
fprintf(stderr, "Missing tdb filename\n");
return 1;
}
tdbfilename = argv[optind];
ev = event_base_new();
if (ev == NULL) {
fprintf(stderr, "event_base_new() failed\n");
return 1;
}
http_server = evhttp_new(ev);
if (http_server == NULL) {
fprintf(stderr, "evhttp_new() failed\n");
return 1;
}
ret = evhttp_bind_socket(http_server, addr, port);
if (ret != 0) {
fprintf(stderr, "evhttp_bind_socket failed\n");
return 1;
}
evhttp_set_gencb(http_server, default_handler, ev);
evhttp_set_cb(http_server, "/metrics", metrics_handler, tdbfilename);
event_base_dispatch(ev);
evhttp_free(http_server);
event_base_free(ev);
return 0;
}