From: Alan T. DeKok Date: Mon, 16 Oct 2023 15:12:18 +0000 (-0400) Subject: add %file.tail(), along with tests and bug fixes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d2ba5f3659a8ccefa0f3ca7a634fecd4a0971789;p=thirdparty%2Ffreeradius-server.git add %file.tail(), along with tests and bug fixes --- diff --git a/doc/antora/modules/reference/pages/xlat/file.adoc b/doc/antora/modules/reference/pages/xlat/file.adoc index 8da93f8ce5f..000a7d8f34d 100644 --- a/doc/antora/modules/reference/pages/xlat/file.adoc +++ b/doc/antora/modules/reference/pages/xlat/file.adoc @@ -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") ---- ==== diff --git a/src/lib/unlang/xlat_builtin.c b/src/lib/unlang/xlat_builtin.c index 4e7fee5fd34..fc058d8b681 100644 --- a/src/lib/unlang/xlat_builtin.c +++ b/src/lib/unlang/xlat_builtin.c @@ -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 index 00000000000..0bbad01122f --- /dev/null +++ b/src/tests/xlat/file.txt @@ -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 index 00000000000..257cc5642cb --- /dev/null +++ b/src/tests/xlat/file/one @@ -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 index 00000000000..19102815663 --- /dev/null +++ b/src/tests/xlat/file/one-no-lf @@ -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 index 00000000000..73195c21065 --- /dev/null +++ b/src/tests/xlat/file/three @@ -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 index 00000000000..df61aa626c1 --- /dev/null +++ b/src/tests/xlat/file/two @@ -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 index 00000000000..0a3411eda6d --- /dev/null +++ b/src/tests/xlat/file/two-no-lf @@ -0,0 +1,2 @@ +foo bar +baz is good \ No newline at end of file