Prechádzať zdrojové kódy

Kernel: Implement shebang executables ("#!/bin/sh")

This patch makes it possible to *run* text files that start with the
characters "#!" followed by an interpreter.

I've tested this with both the Serenity built-in shell and the Bash
shell, and it works as expected. :^)
Andreas Kling 5 rokov pred
rodič
commit
85d629103d
2 zmenil súbory, kde vykonal 40 pridanie a 0 odobranie
  1. 38 0
      Kernel/Process.cpp
  2. 2 0
      Kernel/Process.h

+ 38 - 0
Kernel/Process.cpp

@@ -527,8 +527,46 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir
     return 0;
 }
 
+KResultOr<String> Process::find_shebang_interpreter_for_executable(const String& executable_path)
+{
+    // FIXME: It's a bit sad that we'll open the executable twice (in case there's no shebang)
+    //        Maybe we can find a way to plumb this opened FileDescription to the rest of the
+    //        exec implementation..
+    auto result = VFS::the().open(executable_path, 0, 0, current_directory());
+    if (result.is_error())
+        return result.error();
+    auto description = result.value();
+    auto metadata = description->metadata();
+
+    if (!metadata.may_execute(m_euid, m_gids))
+        return KResult(-EACCES);
+
+    if (metadata.size < 3)
+        return KResult(-ENOEXEC);
+
+    char first_page[PAGE_SIZE];
+    int nread = description->read((u8*)&first_page, sizeof(first_page));
+    int interpreter_length = 0;
+    if (nread > 2 && first_page[0] == '#' && first_page[1] == '!') {
+        for (int i = 2; i < nread; ++i) {
+            if (first_page[i] == '\n') {
+                interpreter_length = i - 2;
+                break;
+            }
+        }
+        if (interpreter_length > 0)
+            return String(&first_page[2], interpreter_length);
+    }
+
+    return KResult(-ENOEXEC);
+}
+
 int Process::exec(String path, Vector<String> arguments, Vector<String> environment)
 {
+    auto result = find_shebang_interpreter_for_executable(path);
+    if (!result.is_error())
+        return exec(result.value(), { result.value(), path }, move(environment));
+
     // The bulk of exec() is done by do_exec(), which ensures that all locals
     // are cleaned up by the time we yield-teleport below.
     int rc = do_exec(move(path), move(arguments), move(environment));

+ 2 - 0
Kernel/Process.h

@@ -310,6 +310,8 @@ private:
     int alloc_fd(int first_candidate_fd = 0);
     void disown_all_shared_buffers();
 
+    KResultOr<String> find_shebang_interpreter_for_executable(const String& executable_path);
+
     Thread* m_main_thread { nullptr };
 
     RefPtr<PageDirectory> m_page_directory;