diff mrjunejune/latex_renderer.c @ 247:70f2a3dafc1c

[build] Bundle offline Tectonic runtime Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Mon, 03 Aug 2026 20:20:06 -0700
parents b8aa08503378
children b8b6e726964a
line wrap: on
line diff
--- a/mrjunejune/latex_renderer.c	Mon Aug 03 18:30:45 2026 -0700
+++ b/mrjunejune/latex_renderer.c	Mon Aug 03 20:20:06 2026 -0700
@@ -44,6 +44,10 @@
 #define LATEX_DIAGNOSTICS_MAX_BYTES (16 * 1024)
 #define LATEX_CPU_TIMEOUT_SECONDS 5
 #define LATEX_WALL_TIMEOUT_SECONDS 8
+#define TECTONIC_BUNDLE_URL "https://data1.fullyjustified.net/tlextras-2022.0r0.tar"
+#define TECTONIC_CACHE_PATH "third_party/tectonic/runtime/cache"
+#define TECTONIC_COMPILER_PATH "third_party/tectonic/runtime/bin/tectonic"
+#define TECTONIC_FONTCONFIG_SYSROOT_PATH "third_party/tectonic/runtime/sysroot"
 
 static char *duplicate_message(const char *message)
 {
@@ -166,7 +170,56 @@
   return ok;
 }
 
-static bool apply_filesystem_sandbox(const char *job_directory)
+static bool add_landlock_tree_rules(
+    int ruleset_fd,
+    const char *path,
+    uint64_t access,
+    unsigned depth)
+{
+  if (depth > 32 ||
+      !add_landlock_path_rule(ruleset_fd, path, access, true))
+    return false;
+
+  struct stat status;
+  if (lstat(path, &status) != 0)
+    return false;
+  if (!S_ISDIR(status.st_mode))
+    return true;
+
+  DIR *directory = opendir(path);
+  if (!directory)
+    return false;
+  bool ok = true;
+  struct dirent *entry;
+  while (ok && (entry = readdir(directory)) != NULL)
+  {
+    if (strcmp(entry->d_name, ".") == 0 ||
+        strcmp(entry->d_name, "..") == 0)
+      continue;
+    char child[PATH_MAX];
+    int length = snprintf(
+        child,
+        sizeof(child),
+        "%s/%s",
+        path,
+        entry->d_name);
+    ok = length >= 0 &&
+        (size_t)length < sizeof(child) &&
+        add_landlock_tree_rules(
+            ruleset_fd,
+            child,
+            access,
+            depth + 1);
+  }
+  closedir(directory);
+  return ok;
+}
+
+static bool apply_filesystem_sandbox(
+    const char *job_directory,
+    const char *compiler,
+    const char *cache_directory,
+    const char *fontconfig_sysroot)
 {
   int abi = syscall(
       SYS_landlock_create_ruleset,
@@ -207,32 +260,25 @@
   if (ruleset_fd < 0)
     return false;
 
-  const char *read_only_directories[] = {
-    "/usr",
-    "/lib",
-    "/lib64",
-    "/bin",
-    "/var/lib/texmf",
-    "/etc/texmf",
-    "/etc/fonts",
-  };
   bool ok = true;
-  for (size_t i = 0;
-       ok && i < sizeof(read_only_directories) / sizeof(read_only_directories[0]);
-       i++)
-  {
-    ok = add_landlock_path_rule(
-        ruleset_fd,
-        read_only_directories[i],
-        read_access,
-        false);
-  }
   if (ok)
     ok = add_landlock_path_rule(
         ruleset_fd,
-        "/etc/ld.so.cache",
-        LANDLOCK_ACCESS_FS_READ_FILE,
-        false);
+        compiler,
+        LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_FILE,
+        true);
+  if (ok)
+    ok = add_landlock_tree_rules(
+        ruleset_fd,
+        cache_directory,
+        read_access | LANDLOCK_ACCESS_FS_MAKE_DIR,
+        0);
+  if (ok)
+    ok = add_landlock_tree_rules(
+        ruleset_fd,
+        fontconfig_sysroot,
+        read_access,
+        0);
   if (ok)
     ok = add_landlock_path_rule(
         ruleset_fd,
@@ -310,17 +356,28 @@
   return prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program) == 0;
 }
 
-static void configure_child_environment(const char *job_directory)
+static void configure_child_environment(
+    const char *job_directory,
+    const char *cache_directory,
+    const char *fontconfig_sysroot)
 {
   clearenv();
   setenv("HOME", job_directory, 1);
   setenv("TMPDIR", job_directory, 1);
-  setenv("TEXMFVAR", job_directory, 1);
-  setenv("TEXMFCONFIG", job_directory, 1);
-  setenv("openin_any", "p", 1);
-  setenv("openout_any", "p", 1);
-  setenv("shell_escape", "0", 1);
+  setenv("XDG_CACHE_HOME", job_directory, 1);
+  char tectonic_cache[PATH_MAX];
+  snprintf(
+      tectonic_cache,
+      sizeof(tectonic_cache),
+      "%s/tectonic",
+      cache_directory);
+  setenv("TECTONIC_CACHE_DIR", tectonic_cache, 1);
+  setenv("FONTCONFIG_FILE", "fonts.conf", 1);
+  setenv("FONTCONFIG_PATH", "/etc/fonts", 1);
+  setenv("FONTCONFIG_SYSROOT", fontconfig_sysroot, 1);
+  setenv("TECTONIC_UNTRUSTED_MODE", "1", 1);
   setenv("SOURCE_DATE_EPOCH", "0", 1);
+  setenv("TZ", "UTC", 1);
   setenv("LANG", "C.UTF-8", 1);
 }
 
@@ -342,6 +399,8 @@
 
 static void run_compiler_child(
     const char *compiler,
+    const char *cache_directory,
+    const char *fontconfig_sysroot,
     const char *job_directory,
     int output_fd)
 {
@@ -358,24 +417,38 @@
   if (chdir(job_directory) != 0)
     _exit(121);
 
-  configure_child_environment(job_directory);
+  configure_child_environment(
+      job_directory,
+      cache_directory,
+      fontconfig_sysroot);
   if (!set_limit(RLIMIT_CPU, LATEX_CPU_TIMEOUT_SECONDS) ||
       !set_limit(RLIMIT_AS, 768 * 1024 * 1024) ||
       !set_limit(RLIMIT_FSIZE, LATEX_PDF_MAX_BYTES) ||
       !set_limit(RLIMIT_NOFILE, 64) ||
       !set_limit(RLIMIT_NPROC, 1))
     _exit(122);
-  if (!apply_filesystem_sandbox(job_directory) ||
+  if (!apply_filesystem_sandbox(
+          job_directory,
+          compiler,
+          cache_directory,
+          fontconfig_sysroot) ||
       !apply_network_sandbox())
     _exit(123);
 
   char *const arguments[] = {
     (char *)compiler,
-    "-interaction=nonstopmode",
-    "-halt-on-error",
-    "-no-shell-escape",
-    "-file-line-error",
-    "-output-directory=.",
+    "--untrusted",
+    "--only-cached",
+    "--chatter",
+    "minimal",
+    "--color",
+    "never",
+    "--reruns",
+    "1",
+    "--outdir",
+    ".",
+    "--bundle",
+    TECTONIC_BUNDLE_URL,
     "document.tex",
     NULL,
   };
@@ -518,6 +591,71 @@
   return true;
 }
 
+static bool resolve_runtime_path(
+    const char *relative_path,
+    char resolved[PATH_MAX])
+{
+  if (realpath(relative_path, resolved))
+    return true;
+
+  const char *runfiles = getenv("RUNFILES_DIR");
+  const char *workspace = getenv("TEST_WORKSPACE");
+  char candidate[PATH_MAX];
+  if (runfiles && workspace)
+  {
+    int length = snprintf(
+        candidate,
+        sizeof(candidate),
+        "%s/%s/%s",
+        runfiles,
+        workspace,
+        relative_path);
+    if (length >= 0 &&
+        (size_t)length < sizeof(candidate) &&
+        realpath(candidate, resolved))
+      return true;
+  }
+
+  char executable[PATH_MAX];
+  ssize_t executable_length = readlink(
+      "/proc/self/exe",
+      executable,
+      sizeof(executable) - 1);
+  if (executable_length <= 0)
+    return false;
+  executable[executable_length] = '\0';
+
+  char *last_slash = strrchr(executable, '/');
+  if (last_slash)
+  {
+    *last_slash = '\0';
+    int length = snprintf(
+        candidate,
+        sizeof(candidate),
+        "%s/%s",
+        executable,
+        relative_path);
+    if (length >= 0 &&
+        (size_t)length < sizeof(candidate) &&
+        realpath(candidate, resolved))
+      return true;
+  }
+
+  char *package = strstr(executable, "/mrjunejune/");
+  if (!package)
+    return false;
+  *package = '\0';
+  int length = snprintf(
+      candidate,
+      sizeof(candidate),
+      "%s/%s",
+      executable,
+      relative_path);
+  return length >= 0 &&
+      (size_t)length < sizeof(candidate) &&
+      realpath(candidate, resolved) != NULL;
+}
+
 Latex_Render_Result Latex_Render(const uint8_t *source, size_t source_size)
 {
   if (!source || source_size == 0)
@@ -533,11 +671,18 @@
         LATEX_RENDER_INVALID_INPUT,
         "LaTeX source cannot contain NUL bytes.");
 
-  const char *compiler = "/usr/bin/pdflatex";
-  if (access(compiler, X_OK) != 0)
+  char compiler[PATH_MAX];
+  char cache_directory[PATH_MAX];
+  char fontconfig_sysroot[PATH_MAX];
+  if (!resolve_runtime_path(TECTONIC_COMPILER_PATH, compiler) ||
+      !resolve_runtime_path(TECTONIC_CACHE_PATH, cache_directory) ||
+      !resolve_runtime_path(
+          TECTONIC_FONTCONFIG_SYSROOT_PATH,
+          fontconfig_sysroot) ||
+      access(compiler, X_OK) != 0)
     return result_with_message(
         LATEX_RENDER_COMPILER_UNAVAILABLE,
-        "The server LaTeX compiler is unavailable.");
+        "The Bazel Tectonic runtime is unavailable.");
 
   char job_directory[] = "/tmp/mrjunejune-latex-XXXXXX";
   if (!mkdtemp(job_directory))
@@ -588,7 +733,12 @@
   if (child == 0)
   {
     close(output_pipe[0]);
-    run_compiler_child(compiler, job_directory, output_pipe[1]);
+    run_compiler_child(
+        compiler,
+        cache_directory,
+        fontconfig_sysroot,
+        job_directory,
+        output_pipe[1]);
   }
   setpgid(child, child);