1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020, Ryan Moeller <freqlabs@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/jail.h>
35#include <errno.h>
36#include <jail.h>
37#include <stdlib.h>
38#include <string.h>
39
40#include <lua.h>
41#include <lauxlib.h>
42#include <lualib.h>
43
44int luaopen_jail(lua_State *);
45
46static int
47l_getid(lua_State *L)
48{
49	const char *name;
50	int jid;
51
52	name = luaL_checkstring(L, 1);
53	jid = jail_getid(name);
54	if (jid == -1) {
55		lua_pushnil(L);
56		lua_pushstring(L, jail_errmsg);
57		return (2);
58	}
59	lua_pushinteger(L, jid);
60	return (1);
61}
62
63static int
64l_getname(lua_State *L)
65{
66	char *name;
67	int jid;
68
69	jid = luaL_checkinteger(L, 1);
70	name = jail_getname(jid);
71	if (name == NULL) {
72		lua_pushnil(L);
73		lua_pushstring(L, jail_errmsg);
74		return (2);
75	}
76	lua_pushstring(L, name);
77	free(name);
78	return (1);
79}
80
81static int
82l_allparams(lua_State *L)
83{
84	struct jailparam *params;
85	int params_count;
86
87	params_count = jailparam_all(&params);
88	if (params_count == -1) {
89		lua_pushnil(L);
90		lua_pushstring(L, jail_errmsg);
91		return (2);
92	}
93	lua_newtable(L);
94	for (int i = 0; i < params_count; ++i) {
95		lua_pushstring(L, params[i].jp_name);
96		lua_rawseti(L, -2, i + 1);
97	}
98	jailparam_free(params, params_count);
99	free(params);
100	return (1);
101}
102
103static int
104l_getparams(lua_State *L)
105{
106	const char *name;
107	struct jailparam *params;
108	size_t params_count, skipped;
109	int flags, jid, type;
110
111	type = lua_type(L, 1);
112	luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
113	    "expected a jail name (string) or id (integer)");
114	luaL_checktype(L, 2, LUA_TTABLE);
115	params_count = 1 + lua_rawlen(L, 2);
116	flags = luaL_optinteger(L, 3, 0);
117
118	params = malloc(params_count * sizeof(struct jailparam));
119	if (params == NULL)
120		return (luaL_error(L, "malloc: %s", strerror(errno)));
121
122	/*
123	 * Set the jail name or id param as determined by the first arg.
124	 */
125
126	if (type == LUA_TSTRING) {
127		if (jailparam_init(&params[0], "name") == -1) {
128			free(params);
129			return (luaL_error(L, "jailparam_init: %s",
130			    jail_errmsg));
131		}
132		name = lua_tostring(L, 1);
133		if (jailparam_import(&params[0], name) == -1) {
134			jailparam_free(params, 1);
135			free(params);
136			return (luaL_error(L, "jailparam_import: %s",
137			    jail_errmsg));
138		}
139	} else /* type == LUA_TNUMBER */ {
140		if (jailparam_init(&params[0], "jid") == -1) {
141			free(params);
142			return (luaL_error(L, "jailparam_init: %s",
143			    jail_errmsg));
144		}
145		jid = lua_tointeger(L, 1);
146		if (jailparam_import_raw(&params[0], &jid, sizeof(jid)) == -1) {
147			jailparam_free(params, 1);
148			free(params);
149			return (luaL_error(L, "jailparam_import_raw: %s",
150			    jail_errmsg));
151		}
152	}
153
154	/*
155	 * Set the remaining param names being requested.
156	 */
157
158	skipped = 0;
159	for (size_t i = 1; i < params_count; ++i) {
160		const char *param_name;
161
162		lua_rawgeti(L, -1, i);
163		param_name = lua_tostring(L, -1);
164		if (param_name == NULL) {
165			jailparam_free(params, i - skipped);
166			free(params);
167			return (luaL_argerror(L, 2,
168			    "param names must be strings"));
169		}
170		lua_pop(L, 1);
171		/* Skip name or jid, whichever was given. */
172		if (type == LUA_TSTRING) {
173			if (strcmp(param_name, "name") == 0) {
174				++skipped;
175				continue;
176			}
177		} else /* type == LUA_TNUMBER */ {
178			if (strcmp(param_name, "jid") == 0) {
179				++skipped;
180				continue;
181			}
182		}
183		if (jailparam_init(&params[i - skipped], param_name) == -1) {
184			jailparam_free(params, i - skipped);
185			free(params);
186			return (luaL_error(L, "jailparam_init: %s",
187			    jail_errmsg));
188		}
189	}
190	params_count -= skipped;
191
192	/*
193	 * Get the values and convert to a table.
194	 */
195
196	jid = jailparam_get(params, params_count, flags);
197	if (jid == -1) {
198		jailparam_free(params, params_count);
199		free(params);
200		lua_pushnil(L);
201		lua_pushstring(L, jail_errmsg);
202		return (2);
203	}
204	lua_pushinteger(L, jid);
205
206	lua_newtable(L);
207	for (size_t i = 0; i < params_count; ++i) {
208		char *value;
209
210		value = jailparam_export(&params[i]);
211		lua_pushstring(L, value);
212		free(value);
213		lua_setfield(L, -2, params[i].jp_name);
214	}
215
216	jailparam_free(params, params_count);
217	free(params);
218
219	return (2);
220}
221
222static int
223l_setparams(lua_State *L)
224{
225	const char *name;
226	struct jailparam *params;
227	size_t params_count;
228	int flags, jid, type;
229
230	type = lua_type(L, 1);
231	luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
232	    "expected a jail name (string) or id (integer)");
233	luaL_checktype(L, 2, LUA_TTABLE);
234
235	lua_pushnil(L);
236	for (params_count = 1; lua_next(L, 2) != 0; ++params_count)
237		lua_pop(L, 1);
238
239	flags = luaL_optinteger(L, 3, 0);
240
241	params = malloc(params_count * sizeof(struct jailparam));
242	if (params == NULL)
243		return (luaL_error(L, "malloc: %s", strerror(errno)));
244
245	/*
246	 * Set the jail name or id param as determined by the first arg.
247	 */
248
249	if (type == LUA_TSTRING) {
250		if (jailparam_init(&params[0], "name") == -1) {
251			free(params);
252			return (luaL_error(L, "jailparam_init: %s",
253			    jail_errmsg));
254		}
255		name = lua_tostring(L, 1);
256		if (jailparam_import(&params[0], name) == -1) {
257			jailparam_free(params, 1);
258			free(params);
259			return (luaL_error(L, "jailparam_import: %s",
260			    jail_errmsg));
261		}
262	} else /* type == LUA_TNUMBER */ {
263		if (jailparam_init(&params[0], "jid") == -1) {
264			free(params);
265			return (luaL_error(L, "jailparam_init: %s",
266			    jail_errmsg));
267		}
268		jid = lua_tointeger(L, 1);
269		if (jailparam_import_raw(&params[0], &jid, sizeof(jid)) == -1) {
270			jailparam_free(params, 1);
271			free(params);
272			return (luaL_error(L, "jailparam_import_raw: %s",
273			    jail_errmsg));
274		}
275	}
276
277	/*
278	 * Set the rest of the provided params.
279	 */
280
281	lua_pushnil(L);
282	for (size_t i = 1; i < params_count && lua_next(L, 2) != 0; ++i) {
283		const char *value;
284
285		name = lua_tostring(L, -2);
286		if (name == NULL) {
287			jailparam_free(params, i);
288			free(params);
289			return (luaL_argerror(L, 2,
290			    "param names must be strings"));
291		}
292		if (jailparam_init(&params[i], name) == -1) {
293			jailparam_free(params, i);
294			free(params);
295			return (luaL_error(L, "jailparam_init: %s",
296			    jail_errmsg));
297		}
298
299		value = lua_tostring(L, -1);
300		if (value == NULL) {
301			jailparam_free(params, i + 1);
302			free(params);
303			return (luaL_argerror(L, 2,
304			    "param values must be strings"));
305		}
306		if (jailparam_import(&params[i], value) == -1) {
307			jailparam_free(params, i + 1);
308			free(params);
309			return (luaL_error(L, "jailparam_import: %s",
310			    jail_errmsg));
311		}
312
313		lua_pop(L, 1);
314	}
315
316	/*
317	 * Attempt to set the params.
318	 */
319
320	jid = jailparam_set(params, params_count, flags);
321	if (jid == -1) {
322		jailparam_free(params, params_count);
323		free(params);
324		lua_pushnil(L);
325		lua_pushstring(L, jail_errmsg);
326		return (2);
327	}
328	lua_pushinteger(L, jid);
329
330	jailparam_free(params, params_count);
331	free(params);
332	return (1);
333}
334
335static const struct luaL_Reg l_jail[] = {
336	/** Get id of a jail by name.
337	 * @param name	jail name (string)
338	 * @return	jail id (integer)
339	 *		or nil, error (string) on error
340	 */
341	{"getid", l_getid},
342	/** Get name of a jail by id.
343	 * @param jid	jail id (integer)
344	 * @return	jail name (string)
345	 *		or nil, error (string) on error
346	 */
347	{"getname", l_getname},
348	/** Get a list of all known jail parameters.
349	 * @return	list of jail parameter names (table of strings)
350	 *		or nil, error (string) on error
351	 */
352	{"allparams", l_allparams},
353	/** Get the listed params for a given jail.
354	 * @param jail	jail name (string) or id (integer)
355	 * @param params	list of parameter names (table of strings)
356	 * @param flags	optional flags (integer)
357	 * @return	jid (integer), params (table of [string] = string)
358	 *		or nil, error (string) on error
359	 */
360	{"getparams", l_getparams},
361	/** Set params for a given jail.
362	 * @param jail	jail name (string) or id (integer)
363	 * @param params	params and values (table of [string] = string)
364	 * @param flags	optional flags (integer)
365	 * @return	jid (integer)
366	 *		or nil, error (string) on error
367	 */
368	{"setparams", l_setparams},
369	{NULL, NULL}
370};
371
372int
373luaopen_jail(lua_State *L)
374{
375	lua_newtable(L);
376
377	luaL_setfuncs(L, l_jail, 0);
378
379	lua_pushinteger(L, JAIL_CREATE);
380	lua_setfield(L, -2, "CREATE");
381	lua_pushinteger(L, JAIL_UPDATE);
382	lua_setfield(L, -2, "UPDATE");
383	lua_pushinteger(L, JAIL_ATTACH);
384	lua_setfield(L, -2, "ATTACH");
385	lua_pushinteger(L, JAIL_DYING);
386	lua_setfield(L, -2, "DYING");
387
388	return (1);
389}
390