pespin submitted this change.

View Change

Approvals: Jenkins Builder: Verified pespin: Looks good to me, approved
pcap-server: Introduce VTY cmd '[no] completed-path'

This VTY allows enabling feature to instruct osmo-pcap-server to move
the closed files (due to ration) into a separate directory.
This is useful for users who want to monitor and act on closed pcap
files to eg. generate statistics, etc.

Important: completed-path must be in the same filesystem mountpoint as
base-path, since osmo-pcap-server is actually not copying the files,
but atomically renaming the paths through rename() syscall.

Related: SYS#7248
Change-Id: I5166d1c5d9eb45358c87c2e1c5fbf7969d1d5294
---
M doc/manuals/chapters/server.adoc
M include/osmo-pcap/osmo_pcap_server.h
M src/osmo_server_network.c
M src/osmo_server_vty.c
4 files changed, 141 insertions(+), 0 deletions(-)

diff --git a/doc/manuals/chapters/server.adoc b/doc/manuals/chapters/server.adoc
index c6404ab..e0d9ff4 100644
--- a/doc/manuals/chapters/server.adoc
+++ b/doc/manuals/chapters/server.adoc
@@ -125,3 +125,31 @@
Saving procedure), osmo-pcap-server may end up recreating (and truncating) a
previous pcap file if it is generated with the same localtime timestamp, for
instance because connection from osmo-pcap-client was re-established.
+
+=== Completed pcap directory
+
+`osmo-pcap-server` creates and writes data to opened pcap files in the
+`base-path` directory configured through VTY. Furthermore, if user supplies a
+`completed-path` directory through the VTY, `osmo-pcap-server` will move the
+file from `base-path` to `completed-path` directory once it closes the current
+file due to rotation or because `osmo-pcap-client` became disconnected. The file
+name is kept when moving from one directory to the other.
+
+This feature is useful for users willing to have an external tool to monitor a
+directory for new closed pcap files and then acting on them, eg. to gather
+statistics of packets.
+
+.Example: Move files from /tmp/recording to /tmp/done
+----
+server
+ ...
+ base-path /tmp/recording <1>
+ completed-path /tmp/done <2>
+----
+<1> Files are opened and recorded under /tmp/recording
+<2> Once closed files are moved under /tmp/done
+
+NOTE:: `osmo-pcap-server` uses a rename() call to move the file atomically.
+However, this has the drawback that both `base-path` and `completed-path` must
+be placed in the same filesystem mounting point. Using directories in different
+filesystem mount points will fail.
diff --git a/include/osmo-pcap/osmo_pcap_server.h b/include/osmo-pcap/osmo_pcap_server.h
index 50bb121..614ceca 100644
--- a/include/osmo-pcap/osmo_pcap_server.h
+++ b/include/osmo-pcap/osmo_pcap_server.h
@@ -140,6 +140,7 @@
bool dh_params_allocated;

char *base_path;
+ char *completed_path;
mode_t permission_mask;
off_t max_size;
bool max_size_enabled;
diff --git a/src/osmo_server_network.c b/src/osmo_server_network.c
index 8747091..9750170 100644
--- a/src/osmo_server_network.c
+++ b/src/osmo_server_network.c
@@ -39,6 +39,9 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <libgen.h>

static void pcap_zmq_send(void *publ, const void *data, size_t len, int flags)
{
@@ -112,6 +115,84 @@
0);
}

+/* Move pcap file from base_path to completed_path, and updates
+ * conn->curr_filename to point to new location. */
+void move_completed_trace_if_needed(struct osmo_pcap_conn *conn)
+{
+ struct osmo_pcap_server *server = conn->server;
+ char *curr_filename_cpy_bname = NULL;
+ char *curr_filename_cpy_dname = NULL;
+ char *bname = NULL;
+ char *curr_dirname = NULL;
+ char *new_dirname = NULL;
+ char *new_filename = NULL;
+ size_t new_filename_len;
+ int rc;
+
+ if (!conn->curr_filename)
+ return;
+
+ if (!server->completed_path)
+ return;
+
+ /* Assumption: curr_filename is anonicalized absolute pathname. */
+
+ /* basename and dirname may modify input param, and return a string
+ * which shall not be freed, potentially pointing to the input param. */
+ curr_filename_cpy_dname = talloc_strdup(conn, conn->curr_filename);
+ curr_filename_cpy_bname = talloc_strdup(conn, conn->curr_filename);
+ if (!curr_filename_cpy_dname || !curr_filename_cpy_bname)
+ goto ret_free1;
+
+ curr_dirname = dirname(curr_filename_cpy_dname);
+ bname = basename(curr_filename_cpy_bname);
+ if (!curr_dirname || !bname) {
+ LOGP(DSERVER, LOGL_ERROR, "Failed to resolve dirname and basename for '%s'\n",
+ conn->curr_filename);
+ goto ret_free1;
+ }
+
+ new_dirname = realpath(server->completed_path, NULL);
+ if (!new_dirname) {
+ LOGP(DSERVER, LOGL_ERROR, "Failed to resolve path '%s': %s\n",
+ server->completed_path, strerror(errno));
+ goto ret_free1;
+ }
+
+ new_filename_len = strlen(new_dirname) + 1 /* '/' */ + strlen(bname) + 1 /* '\0' */;
+ new_filename = talloc_size(conn, new_filename_len);
+ if (!new_filename)
+ goto ret_free1;
+ rc = snprintf(new_filename, new_filename_len, "%s/%s", new_dirname, bname);
+ if (rc != new_filename_len - 1)
+ goto ret_free2;
+
+ LOGP(DSERVER, LOGL_INFO, "Moving completed pcap file '%s' -> '%s'\n", conn->curr_filename, new_filename);
+ rc = rename(conn->curr_filename, new_filename);
+ if (rc == -1) {
+ int err = errno;
+ LOGP(DSERVER, LOGL_ERROR, "Failed moving completed pcap file '%s' -> '%s': %s\n",
+ conn->curr_filename, new_filename, strerror(err));
+ if (err == EXDEV)
+ LOGP(DSERVER, LOGL_ERROR, "Fix your config! %s and %s shall not be in different filesystems!\n",
+ curr_dirname, new_dirname);
+ goto ret_free2;
+ }
+
+ /* Now replace conn->curr_filename with new path: */
+ talloc_free(conn->curr_filename);
+ conn->curr_filename = new_filename;
+ /* new_filename has been assigned, so we don't want to free it, hence move to ret_free1: */
+ goto ret_free1;
+
+ret_free2:
+ talloc_free(new_filename);
+ret_free1:
+ free(new_dirname);
+ talloc_free(curr_filename_cpy_bname);
+ talloc_free(curr_filename_cpy_dname);
+}
+
void osmo_pcap_server_close_trace(struct osmo_pcap_conn *conn)
{
if (conn->local_fd >= 0) {
@@ -119,6 +200,8 @@
conn->local_fd = -1;
}

+ move_completed_trace_if_needed(conn);
+
if (conn->curr_filename) {
client_event(conn, "closingtracefile", conn->curr_filename);
rate_ctr_inc2(conn->ctrg, PEER_CTR_PROTATE);
diff --git a/src/osmo_server_vty.c b/src/osmo_server_vty.c
index ee3f995..1e8e05c 100644
--- a/src/osmo_server_vty.c
+++ b/src/osmo_server_vty.c
@@ -97,6 +97,8 @@
vty_out(vty, "server%s", VTY_NEWLINE);

vty_out(vty, " base-path %s%s", pcap_server->base_path, VTY_NEWLINE);
+ if (pcap_server->completed_path)
+ vty_out(vty, " completed-path %s%s", pcap_server->completed_path, VTY_NEWLINE);
vty_out(vty, " file-permission-mask 0%o%s", pcap_server->permission_mask, VTY_NEWLINE);
if (pcap_server->addr)
vty_out(vty, " server ip %s%s", pcap_server->addr, VTY_NEWLINE);
@@ -159,6 +161,31 @@
return CMD_SUCCESS;
}

+DEFUN(cfg_server_no_completed_path,
+ cfg_server_no_completed_path_cmd,
+ "no completed-path",
+ NO_STR "Base path for completed (already closed, rotated) log files. Completed files won't be moved.\n")
+{
+ TALLOC_FREE(pcap_server->completed_path);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_server_completed_path,
+ cfg_server_completed_path_cmd,
+ "completed-path PATH",
+ "Base path for completed (already closed, rotated) log files\n" "Path\n")
+{
+ /* Validate we can resolve path: */
+ char *tmp = realpath(argv[0], NULL);
+ if (!tmp) {
+ vty_out(vty, "%% Failed to resolve path '%s': %s%s", argv[0], strerror(errno), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ free(tmp);
+ osmo_talloc_replace_string(pcap_server, &pcap_server->completed_path, argv[0]);
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_server_file_permission_mask,
cfg_server_file_permission_mask_cmd,
"file-permission-mask MODE",
@@ -671,6 +698,8 @@
install_node(&server_node, config_write_server);

install_element(SERVER_NODE, &cfg_server_base_cmd);
+ install_element(SERVER_NODE, &cfg_server_no_completed_path_cmd);
+ install_element(SERVER_NODE, &cfg_server_completed_path_cmd);
install_element(SERVER_NODE, &cfg_server_file_permission_mask_cmd);
install_element(SERVER_NODE, &cfg_server_ip_cmd);
install_element(SERVER_NODE, &cfg_server_port_cmd);

To view, visit change 39201. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: merged
Gerrit-Project: osmo-pcap
Gerrit-Branch: master
Gerrit-Change-Id: I5166d1c5d9eb45358c87c2e1c5fbf7969d1d5294
Gerrit-Change-Number: 39201
Gerrit-PatchSet: 3
Gerrit-Owner: pespin <pespin@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>