basic/fileio: clean up executable_is_script() a bit

- Rename to script_get_shebang_interpreter and return
  -EMEDIUMTYPE if the executable is not a script.
  We nowadays utilize the scheme of making ret param
  of getters optional, and use them directly as checkers.
- Don't unnecessarily read the whole line, but check
  only the shebang first.
This commit is contained in:
Mike Yuan
2024-12-11 19:11:22 +01:00
parent c0cf1c5826
commit ad9a66fee8
4 changed files with 48 additions and 36 deletions
+35 -20
View File
@@ -14,6 +14,7 @@
#include "alloc-util.h"
#include "chase.h"
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -856,35 +857,49 @@ int read_full_file_full(
return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size);
}
int executable_is_script(const char *path, char **interpreter) {
_cleanup_free_ char *line = NULL;
size_t len;
char *ans;
int script_get_shebang_interpreter(const char *path, char **ret) {
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(path);
r = read_one_line_file(path, &line);
if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
return 0;
f = fopen(path, "re");
if (!f)
return -errno;
char c;
r = safe_fgetc(f, &c);
if (r < 0)
return r;
if (r == 0)
return -EBADMSG;
if (c != '#')
return -EMEDIUMTYPE;
r = safe_fgetc(f, &c);
if (r < 0)
return r;
if (r == 0)
return -EBADMSG;
if (c != '!')
return -EMEDIUMTYPE;
_cleanup_free_ char *line = NULL;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (!startswith(line, "#!"))
return 0;
_cleanup_free_ char *p = NULL;
const char *s = line;
ans = strstrip(line + 2);
len = strcspn(ans, " \t");
r = extract_first_word(&s, &p, /* separators = */ NULL, /* flags = */ 0);
if (r < 0)
return r;
if (r == 0)
return -ENOEXEC;
if (len == 0)
return 0;
ans = strndup(ans, len);
if (!ans)
return -ENOMEM;
*interpreter = ans;
return 1;
if (ret)
*ret = TAKE_PTR(p);
return 0;
}
/**
+1 -1
View File
@@ -94,7 +94,7 @@ static inline int verify_file(const char *fn, const char *blob, bool accept_extr
return verify_file_at(AT_FDCWD, fn, blob, accept_extra_nl);
}
int executable_is_script(const char *path, char **interpreter);
int script_get_shebang_interpreter(const char *path, char **ret);
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
+8 -7
View File
@@ -317,7 +317,7 @@ static int add_matches_for_executable(sd_journal *j, const char *path) {
assert(j);
assert(path);
if (executable_is_script(path, &interpreter) > 0) {
if (script_get_shebang_interpreter(path, &interpreter) >= 0) {
_cleanup_free_ char *comm = NULL;
r = path_extract_filename(path, &comm);
@@ -329,14 +329,15 @@ static int add_matches_for_executable(sd_journal *j, const char *path) {
return log_error_errno(r, "Failed to add match: %m");
/* Append _EXE only if the interpreter is not a link. Otherwise, it might be outdated often. */
path = is_symlink(interpreter) > 0 ? interpreter : NULL;
if (is_symlink(interpreter) > 0)
return 0;
path = interpreter;
}
if (path) {
r = journal_add_match_pair(j, "_EXE", path);
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
}
r = journal_add_match_pair(j, "_EXE", path);
if (r < 0)
return log_error_errno(r, "Failed to add match: %m");
return 0;
}
+4 -8
View File
@@ -311,26 +311,22 @@ TEST(merge_env_file_invalid) {
assert_se(strv_isempty(a));
}
TEST(executable_is_script) {
TEST(script_get_shebang_interpreter) {
_cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
_cleanup_fclose_ FILE *f = NULL;
char *command;
int r;
assert_se(fmkostemp_safe(t, "w", &f) == 0);
fputs("#! /bin/script -a -b \ngoo goo", f);
fflush(f);
r = executable_is_script(t, &command);
assert_se(r > 0);
ASSERT_OK(script_get_shebang_interpreter(t, &command));
ASSERT_STREQ(command, "/bin/script");
free(command);
r = executable_is_script("/bin/sh", &command);
assert_se(r == 0);
ASSERT_ERROR(script_get_shebang_interpreter("/bin/sh", NULL), EMEDIUMTYPE);
r = executable_is_script("/usr/bin/yum", &command);
if (r > 0) {
if (script_get_shebang_interpreter("/usr/bin/yum", &command) >= 0) {
assert_se(startswith(command, "/"));
free(command);
}