1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
|
/*
*crun - OCI runtime written in C
*
*Copyright (C) Rubicon Rowe <l1589002388@gmail.com>
*crun is free software; you can redistribute it and/or modify
*it under the terms of the GNU Lesser General Public License as published by
*the Free Software Foundation; either version 2.1 of the License, or
*(at your option) any later version.
*
*crun is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU Lesser General Public License for more details.
*
*You should have received a copy of the GNU Lesser General Public License
*along with crun. If not, see <http://www.gnu.org/licenses/>.
*/
/* This library is a bare libcrun interface and can be further wrapped by other libraries.
*
* There are some problems still and the API is a subject to change.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <lua.h>
#include <lauxlib.h>
#include <libcrun/container.h>
#include <libcrun/status.h>
#include <libcrun/utils.h>
#include <libcrun/error.h>
static const char *LUA_CRUN_TAG_CTX = "crun-ctx";
static const char *LUA_CRUN_TAG_CONT = "crun-container";
static const char *LUA_CRUN_TAG_CONTS_ITER = "crun-containers-iterator";
#define luacrunL_optboolean(L, n, d) luaL_opt (S, lua_toboolean, n, d)
// Soft error = return an error.
// When `expr` is false, run `onfailed` and push the string from `crun_err`.
// Return `addret + 1`.
#define luacrun_SoftErrIf(S, expr, crun_err, onfailed, addret) \
if (expr) \
{ \
onfailed; \
return luacrun_error (S, crun_err) + addret; \
}
extern int lua_error (lua_State *L);
extern int luaL_error (lua_State *L, const char *fmt, ...);
/* Build the error string, push onto stack. */
LUA_API int
luacrun_error (lua_State *S, libcrun_error_t *err)
{
luaL_checkstack (S, 1, NULL);
if (*err == NULL)
{
lua_pushstring (S, "the error is NULL, this may be a bug in luacrun");
return 1;
}
if ((*err)->status == 0)
{
lua_pushfstring (S, "crun: %s", (*err)->msg);
}
else
{
lua_pushfstring (S, "crun: %s(%s)", (*err)->msg, strerror ((*err)->status));
}
libcrun_error_release (err);
return 1;
}
LUA_API void
luacrun_set_error (lua_State *S, libcrun_error_t *err)
{
luacrun_error (S, err);
lua_error (S);
}
/* This is a custom version of `xstrdup`(in src/libcrun/utils.h), return a Lua userdata.
* Push the userdata onto stack, or nil if `s` is `NULL`.
* This function does not check stack.
* -0, +1, -
*/
static char *
luacrun_xstrdup (lua_State *S, const char *s)
{
if (s != NULL)
{
size_t size = strlen (s) + 1;
char *ret = lua_newuserdata (S, size);
/* `lua_newuserdatauv` always returns a valid address,
no need to check if the allocation is success */
memcpy (ret, s, size);
return ret;
}
else
{
lua_pushnil (S);
return NULL;
}
}
struct luacrun_args_holder
{
const char **argv;
int argc;
};
/* uservalues used by the ctx, the index + 1 is the uservalue idx.
the definition here is not stable. */
const char *luacrun_ctx_uservalues[] = {
"state_root",
"id",
"bundle",
"console_socket",
"pid_file",
"notify_socket",
"handler",
"args", // argv and argc
};
#define luacrun_CtxSetupStringField(S, ret, ctxidx, tabidx, field_name, name, uvalidx) \
ret = lua_getfield (S, tabidx, field_name); \
if (ret == LUA_TSTRING) \
{ \
ctx->name = luacrun_xstrdup (S, lua_tostring (S, -1)); \
} \
else if (ret == LUA_TNIL) \
{ \
lua_pushnil (S); \
ctx->name = NULL; \
} \
else \
{ \
lua_pop (S, 1); \
luaL_error (S, "unknown type %s for field \"%s\"", lua_typename (S, ret), field_name); \
} \
lua_setiuservalue (S, ctxidx, uvalidx); \
lua_pop (S, 1)
#define luacrun_CtxSetupBoolField(S, ret, ctxidx, tabidx, field_name, name) \
ret = lua_getfield (S, tab_idx, field_name); \
if (ret == LUA_TBOOLEAN) \
{ \
ctx->name = lua_toboolean (S, -1); \
} \
else if (ret != LUA_TNIL) \
{ \
lua_pop (S, 1); \
luaL_error (S, "unknown type %s for field \"%s\"", lua_typename (S, ret), field_name); \
} \
lua_pop (S, 1)
/* Setup context_t by a table. The table must be the stack top. [-0, +0] */
static void
luacrun_ctx_setup (lua_State *S, int ctxidx, int tab_idx)
{
libcrun_context_t *ctx = lua_touserdata (S, ctxidx);
int ret;
luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "state_root", state_root, 1);
luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "id", id, 2);
luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "bundle", bundle, 3);
luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "console_socket", console_socket, 4);
luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "pid_file", pid_file, 5);
luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "notify_socket", notify_socket, 6);
luacrun_CtxSetupStringField (S, ret, ctxidx, tab_idx, "handler", handler, 7);
luacrun_CtxSetupBoolField (S, ret, ctxidx, tabidx, "systemd_cgroup", systemd_cgroup);
luacrun_CtxSetupBoolField (S, ret, ctxidx, tabidx, "detach", detach);
ret = lua_getfield (S, tab_idx, "args");
if (ret == LUA_TTABLE)
{
lua_Integer length = luaL_len (S, -1);
if (length < 0 || length > (INT_MAX - 1))
{ /* A userdata can have INT_MAX uservalues */
luaL_error (S, "field \"args\": length should be <= %d and > 0", INT_MAX - 1);
}
int argc = (int) length;
const char **argv = lua_newuserdatauv (S, sizeof (char *) * argc, argc);
int argv_idx = lua_gettop (S);
for (int i = argc; i > 0; i--)
{
lua_geti (S, tab_idx, i);
const char *arg = luaL_tolstring (S, -1, NULL);
if (arg != NULL)
{
const char *copy = luacrun_xstrdup (S, arg);
argv[i] = copy;
lua_setiuservalue (S, argv_idx, i);
lua_pop (S, 2); /* pop arg and result from lua_geti */
}
else
{
luaL_error (S, "field \"args\": failed to convert value (index %d) to string", i);
}
}
/* Stack top: argv */
struct luacrun_args_holder *args = lua_newuserdatauv (S, sizeof (struct luacrun_args_holder), 1);
int args_idx = lua_gettop (S);
args->argc = argc;
args->argv = argv;
lua_pushvalue (S, argv_idx);
lua_setiuservalue (S, args_idx, 1);
/* Stack top: args */
lua_setiuservalue (S, ctxidx, 8); /* -1 */
lua_pop (S, 1);
}
else if (ret != LUA_TNIL)
{
lua_pop (S, 1);
luaL_error (S, "unknown type %s for field \"%s\"", lua_typename (S, ret), "args");
}
lua_pop (S, 1);
}
/* Create a crun context.
*/
LUA_API int
luacrun_new_ctx (lua_State *S)
{
if (! (lua_isnil (S, 1) || lua_istable (S, 1)))
{
luaL_typeerror (S, 1, "table or nil");
}
luaL_checkstack (S, 1, NULL);
/* Lua does not guarantee that string addresses will be valid for the lifetime of libcrun_context_t,
* but we must respect the memory management function set by the user in lua_State.
* (For the string memory guarantees: https://www.lua.org/manual/5.4/manual.html#4.1.3)
*
* - Use userdata as string to ensure it will not be moved.
* ("Lua ensures that this address is valid as long as the corresponding userdata is alive":
* https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv)
* - Use uservalue to prevent being collected by GC.
*
*/
libcrun_context_t *ctx = lua_newuserdatauv (S, sizeof (libcrun_context_t), 8);
int ctx_idx = lua_gettop (S);
memset (ctx, 0, sizeof (libcrun_context_t));
ctx->fifo_exec_wait_fd = -1;
if (lua_istable (S, 1))
{
luacrun_ctx_setup (S, ctx_idx, 1);
} else if (!lua_isnoneornil(S, 1)) {
luaL_argerror(S, 1, "expect table, nil or none");
}
luaL_setmetatable (S, LUA_CRUN_TAG_CTX);
return 1;
}
/*Grab a basic container spec.*/
LUA_API int
luacrun_container_spec (lua_State *S)
{
bool rootless = luacrunL_optboolean (S, 1, true);
libcrun_error_t crun_err = NULL;
luaL_checkstack (S, 1, NULL);
char buf[4096] = {};
FILE *memfile = fmemopen (buf, 4095, "w");
int ret = libcrun_container_spec (rootless, memfile, &crun_err);
fclose (memfile);
luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushnil (S), 1);
lua_pushlstring (S, buf, ret);
return 1;
}
LUA_API int
luacrun_new_container_from_string (lua_State *S)
{
libcrun_error_t crun_err = NULL;
const char *def = luaL_checkstring (S, 1);
libcrun_container_t **cont = lua_newuserdata (S, sizeof (libcrun_container_t *));
luaL_setmetatable (S, LUA_CRUN_TAG_CONT);
*cont = libcrun_container_load_from_memory (def, &crun_err);
if (*cont == NULL)
{
lua_pushnil (S);
return luacrun_error (S, &crun_err) + 1;
}
return 1;
}
LUA_API int
luacrun_new_container_from_file (lua_State *S)
{
libcrun_error_t crun_err = NULL;
const char *path = luaL_checkstring (S, 1);
libcrun_container_t **cont = lua_newuserdata (S, sizeof (libcrun_container_t *));
luaL_setmetatable (S, LUA_CRUN_TAG_CONT);
// create the userdata before calling crun, so we don't need to clean up when Lua failed
*cont = libcrun_container_load_from_file (path, &crun_err);
if (*cont == NULL)
{
lua_pushnil (S);
return luacrun_error (S, &crun_err) + 1;
}
return 1;
}
/*Release resource linked with container userdata. Double use is supported.*/
LUA_API int
luacrun_container_finalizer (lua_State *S)
{
libcrun_container_t **cont = luaL_checkudata (S, 1, LUA_CRUN_TAG_CONT);
if (*cont != NULL)
{
free_runtime_spec_schema_config_schema ((*cont)->container_def);
*cont = NULL;
}
return 0;
}
LUA_API int
luacrun_set_verbosity (lua_State *S)
{
lua_Integer verbosity = luaL_checkinteger (S, 1);
if (verbosity >= INT_MIN && verbosity <= INT_MAX)
{
libcrun_set_verbosity (verbosity);
}
else
{
luaL_error (S, "verbosity should be >= %d and <= %d", INT_MIN, INT_MAX);
}
return 0;
}
LUA_API int
luacrun_get_verbosity (lua_State *S)
{
int verbosity = libcrun_get_verbosity ();
lua_pushinteger (S, verbosity);
return 1;
}
static unsigned int
luacrun_build_run_flags (lua_State *S, int idx)
{
luaL_checktype (S, idx, LUA_TTABLE);
lua_getfield (S, idx, "prefork");
bool prefork = lua_toboolean (S, -1);
lua_pop (S, 1);
return (prefork ? LIBCRUN_RUN_OPTIONS_PREFORK : 0) | 0;
}
LUA_API int
luacrun_ctx_run (lua_State *S)
{
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
libcrun_container_t **cont = luaL_checkudata (S, 2, LUA_CRUN_TAG_CONT);
unsigned int flags = luaL_opt (S, luacrun_build_run_flags, 3, 0);
libcrun_error_t crun_err = NULL;
luaL_checkstack (S, 1, NULL);
int ret = libcrun_container_run (ctx, *cont, flags, &crun_err);
if (ret < 0)
{
if (crun_err != NULL) {
lua_pushnil (S);
return luacrun_error (S, &crun_err) + 1;
} else {
luaL_error(S, "failed to run container");
}
}
else
{
lua_pushinteger (S, ret);
return 1;
}
}
LUA_API int
luacrun_ctx_create_container (lua_State *S)
{
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
libcrun_container_t **cont = luaL_checkudata (S, 2, LUA_CRUN_TAG_CONT);
unsigned int flags = luaL_opt (S, luacrun_build_run_flags, 3, LIBCRUN_RUN_OPTIONS_PREFORK);
libcrun_error_t crun_err = NULL;
luaL_checkstack (S, 1, NULL);
int ret = libcrun_container_create (ctx, *cont, flags, &crun_err);
if (ret < 0)
{
lua_pushnil (S);
return luacrun_error (S, &crun_err) + 1;
}
else
{
lua_pushinteger (S, ret);
return 1;
}
}
LUA_API int
luacrun_ctx_delete_container (lua_State *S)
{
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
const char *id = luaL_checkstring (S, 2);
bool force = luaL_opt (S, lua_toboolean, 3, false);
libcrun_error_t crun_err = NULL;
luaL_checkstack (S, 1, NULL);
int ret = libcrun_container_delete (ctx, NULL, id, force, &crun_err);
bool has_error = ret < 0 && crun_err != NULL;
luacrun_SoftErrIf (S, has_error, &crun_err, lua_pushboolean (S, false), 1);
lua_pushboolean (S, ret >= 0); // false if failed to read the exec.fifo in the state dir
return 1;
}
LUA_API int
luacrun_ctx_kill_container (lua_State *S)
{
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
const char *id = luaL_checkstring (S, 2);
const char *signame = luaL_checkstring (S, 3);
libcrun_error_t crun_err = NULL;
luaL_checkstack (S, 1, NULL);
int ret = libcrun_container_kill (ctx, id, signame, &crun_err);
luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushboolean (S, false), 1);
lua_pushboolean (S, true);
return 1;
}
/* Get the container status. (ctx: userdata, id: string) [-0, +1, -]
This function is a rewrite of `libcrun_container_state` for Lua.
`libcrun_container_state` receives `FILE*` and writes JSON.
We could not use `fmemopen` like `luacrun_container_spec` since the final size of
the string is 100% unpredictable.
*/
LUA_API int
luacrun_ctx_status_container (lua_State *S)
{
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
const char *id = luaL_checkstring (S, 2);
luaL_checkstack (S, 3, NULL);
libcrun_error_t crun_err = NULL;
int ret;
lua_createtable (S, 0, 0);
int tabidx = lua_gettop (S);
// We know there are two frames available on the stack from this point.
lua_pushstring (S, "1.0.0");
lua_setfield (S, tabidx, "ociVersion");
lua_pushvalue (S, 2);
lua_setfield (S, tabidx, "id");
libcrun_container_status_t status = {};
const char *state_root = ctx->state_root;
ret = libcrun_read_container_status (&status, state_root, id, &crun_err);
luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushnil (S), 1);
const char *container_status = NULL;
int running;
ret = libcrun_get_container_state_string (id, &status, state_root, &container_status, &running, &crun_err);
luacrun_SoftErrIf (S, ret < 0 && crun_err != NULL, &crun_err, lua_pushnil (S), 1);
if (ret < 0) {
// Failed to read the exec.fifo in the state dir
lua_pushnil(S);
lua_pushstring(S, "failed to read state");
return 2;
}
lua_pushinteger (S, running ? status.pid : 0);
lua_setfield (S, tabidx, "pid");
struct luacrun_string_pair
{
const char *k;
const char *v;
};
const struct luacrun_string_pair values[] = {
{ "status", container_status },
{ "bundle", status.bundle },
{ "rootfs", status.rootfs },
{ "created", status.created },
{ "systemd-scope", status.scope }, /* maybe NULL*/
{ "owner", status.owner }, /* maybe NULL */
{ NULL, NULL },
};
for (int i = 0; values[i].k != NULL; i++)
{
const struct luacrun_string_pair p = values[i];
if (p.v != NULL)
{
lua_pushstring (S, p.v);
lua_setfield (S, tabidx, p.k);
}
}
{
cleanup_container libcrun_container_t *container = NULL;
cleanup_free char *dir = NULL;
ret = libcrun_get_state_directory (&dir, state_root, id, &crun_err);
if (UNLIKELY (ret < 0))
{
libcrun_error_release (&crun_err);
lua_pushnil (S);
lua_pushstring (S, "cannot get state directory");
return 2;
}
const char *config_file = lua_pushfstring (S, "%s/%s", dir, "config.json");
container = libcrun_container_load_from_file (config_file, &crun_err);
lua_pop (S, 1);
if (container == NULL)
{
libcrun_error_release (&crun_err);
lua_pushnil (S);
lua_pushstring (S, "error loading config.json");
return 2;
}
if (container->container_def->annotations && container->container_def->annotations->len)
{
/* Check stack again, we need three available frames here. */
luaL_checkstack (S, 3, NULL);
lua_createtable (S, 0, container->container_def->annotations->len);
for (size_t i = 0; i < container->container_def->annotations->len; i++)
{
const char *key = container->container_def->annotations->keys[i];
const char *val = container->container_def->annotations->values[i];
lua_pushstring (S, val);
lua_setfield (S, tabidx, key);
}
lua_setfield (S, tabidx, "annotations");
}
}
return 1;
}
LUA_API int
luacrun_ctx_start_container (lua_State *S)
{
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
const char *id = luaL_checkstring (S, 2);
libcrun_error_t crun_err = NULL;
luaL_checkstack (S, 2, NULL);
int ret = libcrun_container_start (ctx, id, &crun_err);
luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushboolean (S, false), 1);
lua_pushboolean (S, true);
return 1;
}
struct luacrun_ctx_containers_iterator
{
bool closed; // flag for if the libcrun_container_list_t free'd
libcrun_container_list_t *start;
libcrun_container_list_t *curr;
lua_Integer counter;
};
static int
luacrun_ctx_containers_iteratorf (lua_State *S)
{
// params: userdata integer
luaL_checktype (S, 1, LUA_TUSERDATA);
struct luacrun_ctx_containers_iterator *it = lua_touserdata (S, 1);
luaL_checkstack (S, 2, NULL);
if (it->curr != NULL)
{
lua_pushinteger (S, ++(it->counter));
lua_pushstring (S, it->curr->name);
it->curr = it->curr->next;
return 2;
}
else
{
it->closed = true;
libcrun_free_containers_list (it->start);
lua_pushnil (S);
return 1;
}
}
static int
luacrun_ctx_containers_finalizer (lua_State *S)
{
luaL_checktype (S, 1, LUA_TUSERDATA);
struct luacrun_ctx_containers_iterator *iter = lua_touserdata (S, 1);
if (! iter->closed)
{
libcrun_free_containers_list (iter->start);
}
return 0;
}
static const luaL_Reg luacrun_ctx_containers_iterator_metamethods[] = {
{ "__gc", &luacrun_ctx_containers_finalizer },
{ NULL, NULL },
};
static int
luacrun_setup_ctx_iter_metatable (lua_State *S)
{
luaL_newmetatable (S, LUA_CRUN_TAG_CONTS_ITER);
luaL_setfuncs (S, luacrun_ctx_containers_iterator_metamethods, 0);
lua_pop (S, 1);
return 0;
}
LUA_API int
luacrun_ctx_iter_containers (lua_State *S)
{
libcrun_error_t crun_err = NULL;
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
luaL_checkstack (S, 4, NULL);
lua_pushcfunction (S, &luacrun_ctx_containers_iteratorf);
libcrun_container_list_t *containers;
int ret = libcrun_get_containers_list (&containers, ctx->state_root, &crun_err);
if (ret < 0 && crun_err != NULL)
luacrun_set_error (S, &crun_err);
// ret < 0 && crun_err == NULL: the status file does not exists
// The `containers` is still NULL,
// the iterator function knows how to handle the situation
struct luacrun_ctx_containers_iterator *it = lua_newuserdata (S, sizeof (struct luacrun_ctx_containers_iterator));
*it = (struct luacrun_ctx_containers_iterator){
.closed = false,
.counter = 0,
.curr = containers,
.start = containers,
};
luaL_setmetatable (S, LUA_CRUN_TAG_CONTS_ITER);
lua_pushinteger (S, it->counter);
lua_pushnil (S);
return 4;
}
LUA_API int
luacrun_ctx_update_container (lua_State *S)
{
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX);
const char *id = luaL_checkstring (S, 2);
const char *content = luaL_checkstring (S, 3);
luaL_checkstack (S, 2, NULL);
char errbuf[1024] = {};
yajl_val parsed_json = yajl_tree_parse (content, errbuf, sizeof (errbuf));
if (parsed_json == NULL)
{
lua_pushboolean (S, false);
lua_pushfstring (S, "cannot parse the data: \"%s\"", errbuf);
return 2;
}
struct parser_context parser_ctx = { .options = 0, .errfile = stderr };
runtime_spec_schema_config_schema_process *rt_spec_process;
parser_error p_err = NULL;
rt_spec_process = make_runtime_spec_schema_config_schema_process (parsed_json, &parser_ctx, &p_err);
yajl_tree_free (parsed_json);
if (rt_spec_process == NULL)
{
lua_pushboolean (S, false);
lua_pushfstring (S, "cannot parse process: \"%s\"", p_err);
free (p_err);
return 2;
}
libcrun_error_t crun_err = NULL;
int ret = libcrun_container_exec (ctx, id, rt_spec_process, &crun_err);
free_runtime_spec_schema_config_schema_process (rt_spec_process);
luacrun_SoftErrIf (S, ret < 0, &crun_err, lua_pushboolean (S, false), 1);
lua_pushboolean (S, true);
return 1;
}
#define luacrun_CtxStringAccessor(name, uval_idx) \
LUA_API int luacrun_ctx_get_##name (lua_State *S) \
{ \
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \
if (ctx->name != NULL) \
{ \
luaL_checkstack (S, 1, NULL); \
lua_pushstring (S, ctx->name); \
return 1; \
} \
else \
{ \
return 0; \
} \
} \
LUA_API int luacrun_ctx_set_##name (lua_State *S) \
{ \
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \
const char *val = luaL_optstring (S, 2, NULL); \
luaL_checkstack (S, 2, NULL); \
if (ctx->name != NULL) \
{ \
lua_pushstring (S, ctx->name); \
} \
else \
{ \
lua_pushnil (S); \
} \
const char *copy = luacrun_xstrdup (S, val); \
ctx->name = copy; \
lua_setiuservalue (S, 1, uval_idx); \
return 1; \
}
#define luacrun_CtxBoolAccessor(name) \
LUA_API int luacrun_ctx_get_##name (lua_State *S) \
{ \
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \
luaL_checkstack (S, 1, NULL); \
lua_pushboolean (S, ctx->name); \
return 1; \
} \
LUA_API int luacrun_ctx_set_##name (lua_State *S) \
{ \
libcrun_context_t *ctx = luaL_checkudata (S, 1, LUA_CRUN_TAG_CTX); \
luaL_checktype (S, 2, LUA_TBOOLEAN); \
luaL_checkstack (S, 1, NULL); \
bool oldval = ctx->name; \
ctx->name = lua_toboolean (S, 2); \
lua_pushboolean (S, oldval); \
return 1; \
}
luacrun_CtxStringAccessor (state_root, 1);
luacrun_CtxStringAccessor (id, 2);
luacrun_CtxStringAccessor (bundle, 3);
luacrun_CtxStringAccessor (console_socket, 4);
luacrun_CtxStringAccessor (pid_file, 5);
luacrun_CtxStringAccessor (notify_socket, 6);
luacrun_CtxStringAccessor (handler, 7);
luacrun_CtxBoolAccessor (systemd_cgroup);
#define luacrun_RegAddCtxAccessor(method_name, name) \
{ method_name, &luacrun_ctx_get_##name }, \
{ \
"set_" method_name, &luacrun_ctx_set_##name \
}
static const luaL_Reg luacrun_ctx_index[]
= {
{ "run", &luacrun_ctx_run },
{ "create", &luacrun_ctx_create_container },
{ "delete", &luacrun_ctx_delete_container },
{ "kill", &luacrun_ctx_kill_container },
{ "start", &luacrun_ctx_start_container },
{ "status", &luacrun_ctx_status_container },
{ "iter_names", &luacrun_ctx_iter_containers },
{ "update", &luacrun_ctx_update_container },
luacrun_RegAddCtxAccessor ("state_root", state_root),
luacrun_RegAddCtxAccessor ("id", id),
luacrun_RegAddCtxAccessor ("bundle", bundle),
luacrun_RegAddCtxAccessor ("console_socket", console_socket),
luacrun_RegAddCtxAccessor ("pid_file", pid_file),
luacrun_RegAddCtxAccessor ("notify_socket", notify_socket),
luacrun_RegAddCtxAccessor ("handler", handler),
luacrun_RegAddCtxAccessor ("systemd_cgroup", systemd_cgroup),
{ NULL, NULL },
};
LUA_API int
luacrun_setup_ctx_metatable (lua_State *S)
{
luaL_checkstack (S, 3, NULL);
luaL_newmetatable (S, LUA_CRUN_TAG_CTX);
int mtab_idx = lua_gettop (S);
lua_newtable (S);
luaL_setfuncs (S, luacrun_ctx_index, 0);
lua_setfield (S, mtab_idx, "__index");
lua_pop (S, 1);
return 0;
}
LUA_API int
luacrun_setup_cont_metatable (lua_State *S)
{
luaL_checkstack (S, 2, NULL);
luaL_newmetatable (S, LUA_CRUN_TAG_CONT);
int mtab_idx = lua_gettop (S);
lua_pushcfunction (S, &luacrun_container_finalizer);
lua_setfield (S, mtab_idx, "__gc");
// Can we do better than a finalizer?
// Indirect pointer and wild memory make
// Lua GC could not recognize the memory usage.
lua_pop (S, 1);
return 0;
}
static const luaL_Reg luacrun_library_reg[] = {
{ .name = "new_ctx", .func = &luacrun_new_ctx },
{ .name = "container_spec", .func = &luacrun_container_spec },
{ .name = "new_container_from_string", .func = &luacrun_new_container_from_string },
{ .name = "new_container_from_file", .func = &luacrun_new_container_from_file },
{ .name = "get_verbosity", .func = &luacrun_get_verbosity },
{ .name = "set_verbosity", .func = &luacrun_set_verbosity },
{ .name = "run", .func = &luacrun_ctx_run },
{ .name = "create_container", .func = &luacrun_ctx_create_container },
{ .name = "delete_container", .func = &luacrun_ctx_delete_container },
{ .name = "kill_container", .func = &luacrun_ctx_kill_container },
{ .name = "start_container", .func = &luacrun_ctx_start_container },
{ .name = "status_container", .func = &luacrun_ctx_status_container },
{ .name = "iter_container_names", .func = &luacrun_ctx_iter_containers },
{ .name = "update_container", .func = &luacrun_ctx_update_container },
{ NULL, NULL },
};
LUA_API int
luaopen_luacrun (lua_State *S)
{
luaL_checkstack (S, 2, NULL);
luaL_newlib (S, luacrun_library_reg);
int libtab_idx = lua_gettop (S);
lua_pushinteger (S, LIBCRUN_VERBOSITY_ERROR);
lua_setfield (S, libtab_idx, "VERBOSITY_ERROR");
lua_pushinteger (S, LIBCRUN_VERBOSITY_WARNING);
lua_setfield (S, libtab_idx, "VERBOSITY_WARNING");
lua_pushinteger (S, LIBCRUN_VERBOSITY_DEBUG);
lua_setfield (S, libtab_idx, "VERBOSITY_DEBUG");
luacrun_setup_ctx_metatable (S);
luacrun_setup_cont_metatable (S);
luacrun_setup_ctx_iter_metatable (S);
return 1;
}
|