summaryrefslogtreecommitdiffstats
path: root/src/lib-old-stats/stats-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-old-stats/stats-connection.c')
-rw-r--r--src/lib-old-stats/stats-connection.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/lib-old-stats/stats-connection.c b/src/lib-old-stats/stats-connection.c
new file mode 100644
index 0000000..24b5e73
--- /dev/null
+++ b/src/lib-old-stats/stats-connection.c
@@ -0,0 +1,119 @@
+/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "str.h"
+#include "master-service.h"
+#include "stats-connection.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#define STATS_EAGAIN_WARN_INTERVAL_SECS 30
+
+struct stats_connection {
+ int refcount;
+
+ int fd;
+ char *path;
+
+ bool open_failed;
+ time_t next_warning_timestamp;
+};
+
+static bool stats_connection_open(struct stats_connection *conn)
+{
+ if (conn->open_failed)
+ return FALSE;
+
+ conn->fd = open(conn->path, O_WRONLY | O_NONBLOCK);
+ if (conn->fd == -1) {
+ i_error("stats: open(%s) failed: %m", conn->path);
+ conn->open_failed = TRUE;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+struct stats_connection *
+stats_connection_create(const char *path)
+{
+ struct stats_connection *conn;
+
+ conn = i_new(struct stats_connection, 1);
+ conn->refcount = 1;
+ conn->path = i_strdup(path);
+ (void)stats_connection_open(conn);
+ return conn;
+}
+
+void stats_connection_ref(struct stats_connection *conn)
+{
+ conn->refcount++;
+}
+
+void stats_connection_unref(struct stats_connection **_conn)
+{
+ struct stats_connection *conn = *_conn;
+
+ i_assert(conn->refcount > 0);
+ if (--conn->refcount > 0)
+ return;
+
+ *_conn = NULL;
+ i_close_fd_path(&conn->fd, conn->path);
+ i_free(conn->path);
+ i_free(conn);
+}
+
+int stats_connection_send(struct stats_connection *conn, const string_t *str)
+{
+ static bool pipe_warned = FALSE;
+ ssize_t ret;
+
+ /* if master process has been stopped (and restarted), don't even try
+ to notify the stats process anymore. even if one exists, it doesn't
+ know about us. */
+ if (master_service_is_master_stopped(master_service))
+ return -1;
+
+ if (conn->fd == -1) {
+ if (!stats_connection_open(conn))
+ return -1;
+ i_assert(conn->fd != -1);
+ }
+
+ if (str_len(str) > PIPE_BUF && !pipe_warned) {
+ i_warning("stats update sent more bytes that PIPE_BUF "
+ "(%zu > %u), this may break statistics",
+ str_len(str), (unsigned int)PIPE_BUF);
+ pipe_warned = TRUE;
+ }
+
+ ret = write(conn->fd, str_data(str), str_len(str));
+ if (ret == (ssize_t)str_len(str)) {
+ /* success */
+ return 0;
+ } else if (ret < 0 && errno == EAGAIN) {
+ /* stats process is busy */
+ if (ioloop_time > conn->next_warning_timestamp) {
+ i_warning("write(%s) failed: %m (stats process is busy)", conn->path);
+ conn->next_warning_timestamp = ioloop_time +
+ STATS_EAGAIN_WARN_INTERVAL_SECS;
+ }
+ return -1;
+ } else {
+ /* error - reconnect */
+ if (ret < 0) {
+ /* don't log EPIPE errors. they can happen when
+ Dovecot is stopped. */
+ if (errno != EPIPE)
+ i_error("write(%s) failed: %m", conn->path);
+ } else if ((size_t)ret != str_len(str))
+ i_error("write(%s) wrote partial update", conn->path);
+ if (close(conn->fd) < 0)
+ i_error("close(%s) failed: %m", conn->path);
+ conn->fd = -1;
+ return -1;
+ }
+}