lua_scripts.c
1 /*- 2 * Copyright (c) 2019-2025 Baptiste Daroussin <bapt@FreeBSD.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #if __has_include(<sys/procctl.h>) 8 #include <sys/procctl.h> 9 #endif 10 11 #include <sys/types.h> 12 #include <sys/wait.h> 13 14 #include <errno.h> 15 #include <poll.h> 16 #include <xstring.h> 17 #include <err.h> 18 #include <stdio.h> 19 20 #include "pkg.h" 21 #include "private/pkg.h" 22 #include "private/event.h" 23 #include "private/lua.h" 24 25 extern char **environ; 26 27 int 28 pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type, bool upgrade) 29 { 30 int ret = EPKG_OK; 31 int pstat; 32 #ifdef PROC_REAP_KILL 33 bool do_reap; 34 pid_t mypid; 35 struct procctl_reaper_status info; 36 struct procctl_reaper_kill killemall; 37 #endif 38 int cur_pipe[2]; 39 char *line = NULL; 40 41 if (vec_len(&pkg->lua_scripts[type]) == 0) 42 return (EPKG_OK); 43 44 if (!pkg_object_bool(pkg_config_get("RUN_SCRIPTS"))) { 45 return (EPKG_OK); 46 } 47 48 #ifdef PROC_REAP_KILL 49 mypid = getpid(); 50 do_reap = procctl(P_PID, mypid, PROC_REAP_ACQUIRE, NULL) == 0; 51 #endif 52 53 vec_foreach(pkg->lua_scripts[type], i) { 54 char *script = pkg->lua_scripts[type].d[i]; 55 if (get_socketpair(cur_pipe) == -1) { 56 pkg_emit_errno("pkg_lua_script_script", "socketpair"); 57 goto cleanup; 58 } 59 pid_t pid = fork(); 60 if (pid == 0) { 61 static const luaL_Reg pkg_lib[] = { 62 { "print_msg", lua_print_msg }, 63 { "prefixed_path", lua_prefix_path }, 64 { "filecmp", lua_pkg_filecmp }, 65 { "copy", lua_pkg_copy }, 66 { "stat", lua_stat }, 67 { "readdir", lua_readdir }, 68 { "exec", lua_exec }, 69 { "symlink", lua_pkg_symlink }, 70 { "metalog_copy", lua_metalog_copy }, 71 { NULL, NULL }, 72 }; 73 close(cur_pipe[0]); 74 lua_State *L = luaL_newstate(); 75 luaL_openlibs( L ); 76 lua_atpanic(L, (lua_CFunction)stack_dump ); 77 lua_pushinteger(L, cur_pipe[1]); 78 lua_setglobal(L, "msgfd"); 79 lua_pushlightuserdata(L, pkg); 80 lua_setglobal(L, "package"); 81 lua_pushinteger(L, pkg->rootfd); 82 lua_setglobal(L, "rootfd"); 83 lua_pushstring(L, pkg->prefix); 84 lua_setglobal(L, "pkg_prefix"); 85 lua_pushstring(L, pkg->name); 86 lua_setglobal(L, "pkg_name"); 87 if (ctx.pkg_rootdir == NULL) 88 ctx.pkg_rootdir = "/"; 89 lua_pushstring(L, ctx.pkg_rootdir); 90 lua_setglobal(L, "pkg_rootdir"); 91 if (ctx.metalog != NULL) { 92 lua_pushstring(L, ctx.metalog); 93 lua_setglobal(L, "pkg_metalog"); 94 } 95 lua_pushboolean(L, (upgrade)); 96 lua_setglobal(L, "pkg_upgrade"); 97 luaL_newlib(L, pkg_lib); 98 lua_setglobal(L, "pkg"); 99 lua_override_ios(L, true); 100 101 /* parse and set arguments of the line is in the comments */ 102 if (STARTS_WITH(script, "-- args: ")) { 103 char *walk, *begin, *cline = NULL; 104 int spaces, argc = 0; 105 char **args = NULL; 106 107 walk = strchr(script, '\n'); 108 begin = script + strlen("-- args: "); 109 cline = xstrndup(begin, walk - begin); 110 spaces = pkg_utils_count_spaces(cline); 111 args = xmalloc((spaces + 1)* sizeof(char *)); 112 walk = xstrdup(cline); 113 while (walk != NULL) { 114 args[argc++] = pkg_utils_tokenize(&walk); 115 } 116 lua_args_table(L, args, argc); 117 } 118 119 pkg_debug(3, "Scripts: executing lua\n--- BEGIN ---\n%s\nScripts: --- END ---", script); 120 if (luaL_dostring(L, script)) { 121 pkg_emit_error("Failed to execute lua script: %s", lua_tostring(L, -1)); 122 lua_close(L); 123 _exit(1); 124 } 125 126 if (lua_tonumber(L, -1) != 0) { 127 lua_close(L); 128 _exit(1); 129 } 130 131 lua_close(L); 132 _exit(0); 133 } else if (pid < 0) { 134 pkg_emit_errno("Cannot fork", "lua_script"); 135 ret = EPKG_FATAL; 136 goto cleanup; 137 } 138 139 close(cur_pipe[1]); 140 141 ret = pkg_script_run_child(pid, &pstat, cur_pipe[0], "lua"); 142 } 143 144 145 cleanup: 146 #ifdef PROC_REAP_KILL 147 /* 148 * If the prior PROCCTL_REAP_ACQUIRE call failed, the kernel 149 * probably doesn't support this, so don't try. 150 */ 151 if (!do_reap) 152 return (ret); 153 154 procctl(P_PID, mypid, PROC_REAP_STATUS, &info); 155 if (info.rs_children != 0) { 156 killemall.rk_sig = SIGKILL; 157 killemall.rk_flags = 0; 158 if (procctl(P_PID, mypid, PROC_REAP_KILL, &killemall) != 0) { 159 pkg_errno("%s", "Failed to kill all processes"); 160 } 161 } 162 procctl(P_PID, mypid, PROC_REAP_RELEASE, NULL); 163 #endif 164 free(line); 165 166 return (ret); 167 } 168 169 ucl_object_t * 170 pkg_lua_script_to_ucl(charv_t *scripts) 171 { 172 ucl_object_t *array; 173 174 array = ucl_object_typed_new(UCL_ARRAY); 175 vec_foreach(*scripts, i) 176 ucl_array_append(array, ucl_object_fromstring_common(scripts->d[i], 177 strlen(scripts->d[i]), UCL_STRING_RAW|UCL_STRING_TRIM)); 178 179 return (array); 180 } 181 182 int 183 pkg_lua_script_from_ucl(struct pkg *pkg, const ucl_object_t *obj, pkg_lua_script type) 184 { 185 const ucl_object_t *cur; 186 ucl_object_iter_t it = NULL; 187 188 while ((cur = ucl_iterate_object(obj, &it, true))) { 189 if (ucl_object_type(cur) != UCL_STRING) { 190 pkg_emit_error("lua scripts must be strings"); 191 return (EPKG_FATAL); 192 } 193 vec_push(&pkg->lua_scripts[type], xstrdup(ucl_object_tostring(cur))); 194 } 195 return (EPKG_OK); 196 } 197