pespin has submitted this change. ( 
https://gerrit.osmocom.org/c/osmo-pcap/+/39201?usp=email )

Change subject: pcap-server: Introduce VTY cmd '[no] completed-path'
......................................................................

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(-)

Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, approved




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 https://gerrit.osmocom.org/c/osmo-pcap/+/39201?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: osmo-pcap
Gerrit-Branch: master
Gerrit-Change-Id: I5166d1c5d9eb45358c87c2e1c5fbf7969d1d5294
Gerrit-Change-Number: 39201
Gerrit-PatchSet: 3
Gerrit-Owner: pespin <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <[email protected]>
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>

Reply via email to