]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add %file.tail(), along with tests and bug fixes
authorAlan T. DeKok <aland@freeradius.org>
Mon, 16 Oct 2023 15:12:18 +0000 (11:12 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 16 Oct 2023 15:13:20 +0000 (11:13 -0400)
doc/antora/modules/reference/pages/xlat/file.adoc
src/lib/unlang/xlat_builtin.c
src/tests/xlat/file.txt [new file with mode: 0644]
src/tests/xlat/file/one [new file with mode: 0644]
src/tests/xlat/file/one-no-lf [new file with mode: 0644]
src/tests/xlat/file/three [new file with mode: 0644]
src/tests/xlat/file/two [new file with mode: 0644]
src/tests/xlat/file/two-no-lf [new file with mode: 0644]

index 8da93f8ce5f91f3a4404bb5587152240e1501410..000a7d8f34dfc20a136db00fd5851b42c3f2a2d4 100644 (file)
@@ -38,6 +38,22 @@ string line
 ----
 ====
 
+== %file.rm(_string_)
+
+.Return: _bool_
+
+This function removes a file.  If the file exists and could be removed, it returns `true`.  Otherwise if the file does not exist, or if the file could not be removed, it returns `false.
+
+.Removing a file
+====
+[source,unlang]
+----
+if (%file.size("/var/log/radius.log") > (((uint64)1) << 20)) {
+       %file.rm("/var/log/radius.log")
+}
+----
+====
+
 == %file.size(_string_)
 
 .Return: _uint64_
@@ -53,19 +69,19 @@ if (%file.size("/var/log/radius.log") > (((uint64)1) << 20)) {
 }
 ----
 
-== %file.rm(_string_)
+== %file.tail(_string_)
 
-.Return: _bool_
+.Return: _string_
 
-This function removes a file.  If the file exists and could be removed, it returns `true`.  Otherwise if the file does not exist, or if the file could not be removed, it returns `false.
+This function returns the last line of the file.  If the file does not exist, or if the line is more than 256 characters in length, it fails and nothing is returned.
 
-.Removing a file
+.Returning the first line of a file
 ====
 [source,unlang]
 ----
-if (%file.size("/var/log/radius.log") > (((uint64)1) << 20)) {
-       %file.rm("/var/log/radius.log")
-}
+string line
+
+&line := %file.tail("/var/log/radius.log")
 ----
 ====
 
index 4e7fee5fd3413070b746c3ececce585098971a31..fc058d8b681638a7ca79ca4022b98082bb92a55a 100644 (file)
@@ -501,7 +501,7 @@ static xlat_action_t xlat_func_file_head(TALLOC_CTX *ctx, fr_dcursor_t *out,
                }
        }
 
-       if ((p - buffer) >= len) goto invalid;
+       if ((p - buffer) > len) goto invalid;
        close(fd);
 
        MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
@@ -542,6 +542,117 @@ static xlat_action_t xlat_func_file_size(TALLOC_CTX *ctx, fr_dcursor_t *out,
 }
 
 
+static xlat_action_t xlat_func_file_tail(TALLOC_CTX *ctx, fr_dcursor_t *out,
+                                        UNUSED xlat_ctx_t const *xctx,
+                                        request_t *request, fr_value_box_list_t *in)
+{
+       fr_value_box_t *dst, *vb;
+       char const      *filename;
+       ssize_t         len;
+       size_t          count = 0;
+       off_t           offset;
+       int             fd;
+       int             n, r;
+       char            *p, *end, *found, buffer[256];
+
+       XLAT_ARGS(in, &vb);
+       filename = xlat_file_name(vb);
+       if (!filename) return XLAT_ACTION_FAIL;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               REDEBUG3("Failed opening file %s - %s", filename, fr_syserror(errno));
+               return XLAT_ACTION_FAIL;
+       }
+
+       offset = lseek(fd, 0, SEEK_END);
+       if (offset < 0) {
+               REDEBUG3("Failed seeking to end of file %s - %s", filename, fr_syserror(errno));
+               goto fail;
+       }
+
+       if (offset > (off_t) sizeof(buffer)) {
+               offset -= sizeof(buffer);
+       } else {
+               offset = 0;
+       }
+
+       if (lseek(fd, offset, SEEK_SET) < 0) {
+               REDEBUG3("Failed seeking backwards from end of file %s - %s", filename, fr_syserror(errno));
+               goto fail;
+       }
+
+       len = read(fd, buffer, sizeof(buffer));
+       if (len < 0) {
+       fail:
+               REDEBUG3("Failed reading file %s - %s", filename, fr_syserror(errno));
+               close(fd);
+               return XLAT_ACTION_FAIL;
+       }
+
+       if (len == 0) {
+               found = buffer; /* count is zero, so who cares */
+               goto done;
+       }
+
+       n = r = 0;              /* be agnostic over CR / LF */
+
+       end = buffer + len;
+       found = NULL;
+
+       /*
+        *      Nuke any trailing CR/LF
+        */
+       p = end - 1;
+       while (p >= buffer) {
+               if (*p == '\r') {
+                       r++;
+
+                       if (r == 2) break;
+
+                       end = p;
+
+               } else if (*p == '\n') {
+                       n++;
+
+                       if (n == 2) break;
+                       end = p;
+
+               } else {
+                       if (!r) r++; /* if we didn't get a CR/LF at EOF, pretend we did */
+                       if (!n) n++;
+
+                       found = p;
+               }
+
+               p--;
+       }
+
+       /*
+        *      The buffer was only one line of CR/LF.
+        */
+       if (!found) {
+               found = buffer;
+               goto done;
+       }
+
+       count = (end - found);
+
+done:
+       close(fd);
+
+       MEM(dst = fr_value_box_alloc(ctx, FR_TYPE_STRING, NULL));
+       if (fr_value_box_bstrndup(dst, dst, NULL, found, count, false) < 0) {
+               talloc_free(dst);
+               return XLAT_ACTION_FAIL;
+       }
+
+       fr_dcursor_append(out, dst);
+
+       return XLAT_ACTION_DONE;
+}
+
+
 static xlat_action_t xlat_func_file_rm(TALLOC_CTX *ctx, fr_dcursor_t *out,
                                           UNUSED xlat_ctx_t const *xctx,
                                           request_t *request, fr_value_box_list_t *in)
@@ -3549,8 +3660,9 @@ do { \
        XLAT_REGISTER_ARGS("explode", xlat_func_explode, FR_TYPE_STRING, xlat_func_explode_args);
        XLAT_REGISTER_ARGS("file.exists", xlat_func_file_exists, FR_TYPE_BOOL, xlat_func_file_exists_args);
        XLAT_REGISTER_ARGS("file.head", xlat_func_file_head, FR_TYPE_STRING, xlat_func_file_exists_args);
-       XLAT_REGISTER_ARGS("file.size", xlat_func_file_size, FR_TYPE_BOOL, xlat_func_file_exists_args);
        XLAT_REGISTER_ARGS("file.rm", xlat_func_file_rm, FR_TYPE_BOOL, xlat_func_file_exists_args);
+       XLAT_REGISTER_ARGS("file.size", xlat_func_file_size, FR_TYPE_UINT64, xlat_func_file_exists_args);
+       XLAT_REGISTER_ARGS("file.tail", xlat_func_file_tail, FR_TYPE_STRING, xlat_func_file_exists_args);
        XLAT_REGISTER_ARGS("hmacmd5", xlat_func_hmac_md5, FR_TYPE_OCTETS, xlat_hmac_args);
        XLAT_REGISTER_ARGS("hmacsha1", xlat_func_hmac_sha1, FR_TYPE_OCTETS, xlat_hmac_args);
        XLAT_REGISTER_ARGS("integer", xlat_func_integer, FR_TYPE_VOID, xlat_func_integer_args);
diff --git a/src/tests/xlat/file.txt b/src/tests/xlat/file.txt
new file mode 100644 (file)
index 0000000..0bbad01
--- /dev/null
@@ -0,0 +1,23 @@
+xlat_expr %file.head('src/tests/xlat/file/one')
+match {foo}
+
+#
+#  No LF at the end of the line
+#
+xlat_expr %file.head('src/tests/xlat/file/one-no-lf')
+match {foo}
+
+xlat_expr %file.head('src/tests/xlat/file/two')
+match {foo bar}
+
+xlat_expr %file.tail('src/tests/xlat/file/two')
+match {baz is good}
+
+xlat_expr %file.tail('src/tests/xlat/file/two-no-lf')
+match {baz is good}
+
+xlat_expr %file.tail('src/tests/xlat/file/three')
+match {but it was good}
+
+xlat_expr %file.size('src/tests/xlat/file/three')
+match {64}
diff --git a/src/tests/xlat/file/one b/src/tests/xlat/file/one
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/src/tests/xlat/file/one-no-lf b/src/tests/xlat/file/one-no-lf
new file mode 100644 (file)
index 0000000..1910281
--- /dev/null
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/src/tests/xlat/file/three b/src/tests/xlat/file/three
new file mode 100644 (file)
index 0000000..73195c2
--- /dev/null
@@ -0,0 +1,3 @@
+there once was a little lamb
+who was very baaad
+but it was good
diff --git a/src/tests/xlat/file/two b/src/tests/xlat/file/two
new file mode 100644 (file)
index 0000000..df61aa6
--- /dev/null
@@ -0,0 +1,2 @@
+foo bar
+baz is good
diff --git a/src/tests/xlat/file/two-no-lf b/src/tests/xlat/file/two-no-lf
new file mode 100644 (file)
index 0000000..0a3411e
--- /dev/null
@@ -0,0 +1,2 @@
+foo bar
+baz is good
\ No newline at end of file