/ libpkg / lua_scripts.c
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