Reduce dirty records memory usage
[zfs.git] / module / zfs / zcp_iter.c
blobb4aa7a1317a6720cd4afe74f44bf740c29829402
1 /*
2 * CDDL HEADER START
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
13 * CDDL HEADER END
17 * Copyright (c) 2016, 2018 by Delphix. All rights reserved.
20 #include <sys/lua/lua.h>
21 #include <sys/lua/lauxlib.h>
23 #include <sys/dmu.h>
24 #include <sys/dsl_prop.h>
25 #include <sys/dsl_synctask.h>
26 #include <sys/dsl_bookmark.h>
27 #include <sys/dsl_dataset.h>
28 #include <sys/dsl_pool.h>
29 #include <sys/dmu_tx.h>
30 #include <sys/dmu_objset.h>
31 #include <sys/zap.h>
32 #include <sys/dsl_dir.h>
33 #include <sys/zcp_prop.h>
35 #include <sys/zcp.h>
37 #include "zfs_comutil.h"
39 typedef int (zcp_list_func_t)(lua_State *);
40 typedef struct zcp_list_info {
41 const char *name;
42 zcp_list_func_t *func;
43 zcp_list_func_t *gc;
44 const zcp_arg_t pargs[4];
45 const zcp_arg_t kwargs[2];
46 } zcp_list_info_t;
48 static int
49 zcp_clones_iter(lua_State *state)
51 int err;
52 char clonename[ZFS_MAX_DATASET_NAME_LEN];
53 uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
54 uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
55 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
56 dsl_dataset_t *ds, *clone;
57 zap_attribute_t *za;
58 zap_cursor_t zc;
60 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
61 if (err == ENOENT) {
62 return (0);
63 } else if (err != 0) {
64 return (luaL_error(state,
65 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
66 err));
69 if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) {
70 dsl_dataset_rele(ds, FTAG);
71 return (0);
74 zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
75 dsl_dataset_phys(ds)->ds_next_clones_obj, cursor);
76 dsl_dataset_rele(ds, FTAG);
78 za = zap_attribute_alloc();
79 err = zap_cursor_retrieve(&zc, za);
80 if (err != 0) {
81 zap_cursor_fini(&zc);
82 zap_attribute_free(za);
83 if (err != ENOENT) {
84 return (luaL_error(state,
85 "unexpected error %d from zap_cursor_retrieve()",
86 err));
88 return (0);
90 zap_cursor_advance(&zc);
91 cursor = zap_cursor_serialize(&zc);
92 zap_cursor_fini(&zc);
94 err = dsl_dataset_hold_obj(dp, za->za_first_integer, FTAG, &clone);
95 zap_attribute_free(za);
96 if (err != 0) {
97 return (luaL_error(state,
98 "unexpected error %d from "
99 "dsl_dataset_hold_obj(za_first_integer)", err));
102 dsl_dir_name(clone->ds_dir, clonename);
103 dsl_dataset_rele(clone, FTAG);
105 lua_pushnumber(state, cursor);
106 lua_replace(state, lua_upvalueindex(2));
108 (void) lua_pushstring(state, clonename);
109 return (1);
112 static int zcp_clones_list(lua_State *);
113 static const zcp_list_info_t zcp_clones_list_info = {
114 .name = "clones",
115 .func = zcp_clones_list,
116 .gc = NULL,
117 .pargs = {
118 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
119 {NULL, 0}
121 .kwargs = {
122 {NULL, 0}
126 static int
127 zcp_clones_list(lua_State *state)
129 const char *snapname = lua_tostring(state, 1);
130 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
133 * zcp_dataset_hold will either successfully return the requested
134 * dataset or throw a lua error and longjmp out of the zfs.list.clones
135 * call without returning.
137 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
138 if (ds == NULL)
139 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
140 boolean_t issnap = ds->ds_is_snapshot;
141 uint64_t cursor = 0;
142 uint64_t dsobj = ds->ds_object;
143 dsl_dataset_rele(ds, FTAG);
145 if (!issnap) {
146 return (zcp_argerror(state, 1, "%s is not a snapshot",
147 snapname));
150 lua_pushnumber(state, dsobj);
151 lua_pushnumber(state, cursor);
152 lua_pushcclosure(state, &zcp_clones_iter, 2);
153 return (1);
156 static int
157 zcp_snapshots_iter(lua_State *state)
159 int err;
160 char snapname[ZFS_MAX_DATASET_NAME_LEN];
161 uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
162 uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
163 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
164 dsl_dataset_t *ds;
165 objset_t *os;
166 char *p;
168 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
169 if (err != 0) {
170 return (luaL_error(state,
171 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
172 err));
175 dsl_dataset_name(ds, snapname);
176 VERIFY3U(sizeof (snapname), >,
177 strlcat(snapname, "@", sizeof (snapname)));
179 p = strchr(snapname, '\0');
180 VERIFY0(dmu_objset_from_ds(ds, &os));
181 err = dmu_snapshot_list_next(os,
182 sizeof (snapname) - (p - snapname), p, NULL, &cursor, NULL);
183 dsl_dataset_rele(ds, FTAG);
185 if (err == ENOENT) {
186 return (0);
187 } else if (err != 0) {
188 return (luaL_error(state,
189 "unexpected error %d from dmu_snapshot_list_next()", err));
192 lua_pushnumber(state, cursor);
193 lua_replace(state, lua_upvalueindex(2));
195 (void) lua_pushstring(state, snapname);
196 return (1);
199 static int zcp_snapshots_list(lua_State *);
200 static const zcp_list_info_t zcp_snapshots_list_info = {
201 .name = "snapshots",
202 .func = zcp_snapshots_list,
203 .gc = NULL,
204 .pargs = {
205 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
206 {NULL, 0}
208 .kwargs = {
209 {NULL, 0}
213 static int
214 zcp_snapshots_list(lua_State *state)
216 const char *fsname = lua_tostring(state, 1);
217 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
218 boolean_t issnap;
219 uint64_t dsobj;
221 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
222 if (ds == NULL)
223 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
224 issnap = ds->ds_is_snapshot;
225 dsobj = ds->ds_object;
226 dsl_dataset_rele(ds, FTAG);
228 if (issnap) {
229 return (zcp_argerror(state, 1,
230 "argument %s cannot be a snapshot", fsname));
233 lua_pushnumber(state, dsobj);
234 lua_pushnumber(state, 0);
235 lua_pushcclosure(state, &zcp_snapshots_iter, 2);
236 return (1);
239 static int
240 zcp_children_iter(lua_State *state)
242 int err;
243 char childname[ZFS_MAX_DATASET_NAME_LEN];
244 uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
245 uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
246 zcp_run_info_t *ri = zcp_run_info(state);
247 dsl_pool_t *dp = ri->zri_pool;
248 dsl_dataset_t *ds;
249 objset_t *os;
250 char *p;
252 err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
253 if (err != 0) {
254 return (luaL_error(state,
255 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
256 err));
259 dsl_dataset_name(ds, childname);
260 VERIFY3U(sizeof (childname), >,
261 strlcat(childname, "/", sizeof (childname)));
262 p = strchr(childname, '\0');
264 VERIFY0(dmu_objset_from_ds(ds, &os));
265 do {
266 err = dmu_dir_list_next(os,
267 sizeof (childname) - (p - childname), p, NULL, &cursor);
268 } while (err == 0 && zfs_dataset_name_hidden(childname));
269 dsl_dataset_rele(ds, FTAG);
271 if (err == ENOENT) {
272 return (0);
273 } else if (err != 0) {
274 return (luaL_error(state,
275 "unexpected error %d from dmu_dir_list_next()",
276 err));
279 lua_pushnumber(state, cursor);
280 lua_replace(state, lua_upvalueindex(2));
282 (void) lua_pushstring(state, childname);
283 return (1);
286 static int zcp_children_list(lua_State *);
287 static const zcp_list_info_t zcp_children_list_info = {
288 .name = "children",
289 .func = zcp_children_list,
290 .gc = NULL,
291 .pargs = {
292 { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
293 {NULL, 0}
295 .kwargs = {
296 {NULL, 0}
300 static int
301 zcp_children_list(lua_State *state)
303 const char *fsname = lua_tostring(state, 1);
304 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
305 boolean_t issnap;
306 uint64_t dsobj;
308 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
309 if (ds == NULL)
310 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
312 issnap = ds->ds_is_snapshot;
313 dsobj = ds->ds_object;
314 dsl_dataset_rele(ds, FTAG);
316 if (issnap) {
317 return (zcp_argerror(state, 1,
318 "argument %s cannot be a snapshot", fsname));
321 lua_pushnumber(state, dsobj);
322 lua_pushnumber(state, 0);
323 lua_pushcclosure(state, &zcp_children_iter, 2);
324 return (1);
327 static int
328 zcp_user_props_list_gc(lua_State *state)
330 nvlist_t **props = lua_touserdata(state, 1);
331 if (*props != NULL)
332 fnvlist_free(*props);
333 return (0);
336 static int
337 zcp_user_props_iter(lua_State *state)
339 const char *source, *val;
340 nvlist_t *nvprop;
341 nvlist_t **props = lua_touserdata(state, lua_upvalueindex(1));
342 nvpair_t *pair = lua_touserdata(state, lua_upvalueindex(2));
344 do {
345 pair = nvlist_next_nvpair(*props, pair);
346 if (pair == NULL) {
347 fnvlist_free(*props);
348 *props = NULL;
349 return (0);
351 } while (!zfs_prop_user(nvpair_name(pair)));
353 lua_pushlightuserdata(state, pair);
354 lua_replace(state, lua_upvalueindex(2));
356 nvprop = fnvpair_value_nvlist(pair);
357 val = fnvlist_lookup_string(nvprop, ZPROP_VALUE);
358 source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE);
360 (void) lua_pushstring(state, nvpair_name(pair));
361 (void) lua_pushstring(state, val);
362 (void) lua_pushstring(state, source);
363 return (3);
366 static int zcp_user_props_list(lua_State *);
367 static const zcp_list_info_t zcp_user_props_list_info = {
368 .name = "user_properties",
369 .func = zcp_user_props_list,
370 .gc = zcp_user_props_list_gc,
371 .pargs = {
372 { .za_name = "filesystem | snapshot | volume",
373 .za_lua_type = LUA_TSTRING },
374 {NULL, 0}
376 .kwargs = {
377 {NULL, 0}
382 * 'properties' was the initial name for 'user_properties' seen
383 * above. 'user_properties' is a better name as it distinguishes
384 * these properties from 'system_properties' which are different.
385 * In order to avoid breaking compatibility between different
386 * versions of ZFS, we declare 'properties' as an alias for
387 * 'user_properties'.
389 static const zcp_list_info_t zcp_props_list_info = {
390 .name = "properties",
391 .func = zcp_user_props_list,
392 .gc = zcp_user_props_list_gc,
393 .pargs = {
394 { .za_name = "filesystem | snapshot | volume",
395 .za_lua_type = LUA_TSTRING },
396 {NULL, 0}
398 .kwargs = {
399 {NULL, 0}
403 static int
404 zcp_user_props_list(lua_State *state)
406 const char *dsname = lua_tostring(state, 1);
407 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
408 objset_t *os;
409 nvlist_t **props = lua_newuserdata(state, sizeof (nvlist_t *));
411 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
412 if (ds == NULL)
413 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
414 VERIFY0(dmu_objset_from_ds(ds, &os));
415 VERIFY0(dsl_prop_get_all(os, props));
416 dsl_dataset_rele(ds, FTAG);
419 * Set the metatable for the properties list to free it on
420 * completion.
422 luaL_getmetatable(state, zcp_user_props_list_info.name);
423 (void) lua_setmetatable(state, -2);
425 lua_pushlightuserdata(state, NULL);
426 lua_pushcclosure(state, &zcp_user_props_iter, 2);
427 return (1);
432 * Populate nv with all valid system properties and their values for the given
433 * dataset.
435 static void
436 zcp_dataset_system_props(dsl_dataset_t *ds, nvlist_t *nv)
438 for (int prop = ZFS_PROP_TYPE; prop < ZFS_NUM_PROPS; prop++) {
439 /* Do not display hidden props */
440 if (!zfs_prop_visible(prop))
441 continue;
442 /* Do not display props not valid for this dataset */
443 if (!prop_valid_for_ds(ds, prop))
444 continue;
445 fnvlist_add_boolean(nv, zfs_prop_to_name(prop));
449 static int zcp_system_props_list(lua_State *);
450 static const zcp_list_info_t zcp_system_props_list_info = {
451 .name = "system_properties",
452 .func = zcp_system_props_list,
453 .pargs = {
454 { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
455 {NULL, 0}
457 .kwargs = {
458 {NULL, 0}
463 * Get a list of all visible system properties and their values for a given
464 * dataset. Returned on the stack as a Lua table.
466 static int
467 zcp_system_props_list(lua_State *state)
469 int error;
470 char errbuf[128];
471 const char *dataset_name;
472 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
473 const zcp_list_info_t *libinfo = &zcp_system_props_list_info;
474 zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs);
475 dataset_name = lua_tostring(state, 1);
476 nvlist_t *nv = fnvlist_alloc();
478 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
479 if (ds == NULL)
480 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
482 /* Get the names of all valid system properties for this dataset */
483 zcp_dataset_system_props(ds, nv);
484 dsl_dataset_rele(ds, FTAG);
486 /* push list as lua table */
487 error = zcp_nvlist_to_lua(state, nv, errbuf, sizeof (errbuf));
488 nvlist_free(nv);
489 if (error != 0) {
490 return (luaL_error(state,
491 "Error returning nvlist: %s", errbuf));
493 return (1);
496 static int
497 zcp_bookmarks_iter(lua_State *state)
499 char ds_name[ZFS_MAX_DATASET_NAME_LEN];
500 char bookmark_name[ZFS_MAX_DATASET_NAME_LEN];
501 uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
502 uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
503 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
504 dsl_dataset_t *ds;
505 zap_attribute_t *za;
506 zap_cursor_t zc;
508 int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
509 if (err == ENOENT) {
510 return (0);
511 } else if (err != 0) {
512 return (luaL_error(state,
513 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
514 err));
517 if (!dsl_dataset_is_zapified(ds)) {
518 dsl_dataset_rele(ds, FTAG);
519 return (0);
522 err = zap_lookup(dp->dp_meta_objset, ds->ds_object,
523 DS_FIELD_BOOKMARK_NAMES, sizeof (ds->ds_bookmarks_obj), 1,
524 &ds->ds_bookmarks_obj);
525 if (err != 0 && err != ENOENT) {
526 dsl_dataset_rele(ds, FTAG);
527 return (luaL_error(state,
528 "unexpected error %d from zap_lookup()", err));
530 if (ds->ds_bookmarks_obj == 0) {
531 dsl_dataset_rele(ds, FTAG);
532 return (0);
535 /* Store the dataset's name so we can append the bookmark's name */
536 dsl_dataset_name(ds, ds_name);
538 zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
539 ds->ds_bookmarks_obj, cursor);
540 dsl_dataset_rele(ds, FTAG);
542 za = zap_attribute_alloc();
543 err = zap_cursor_retrieve(&zc, za);
544 if (err != 0) {
545 zap_cursor_fini(&zc);
546 zap_attribute_free(za);
547 if (err != ENOENT) {
548 return (luaL_error(state,
549 "unexpected error %d from zap_cursor_retrieve()",
550 err));
552 return (0);
554 zap_cursor_advance(&zc);
555 cursor = zap_cursor_serialize(&zc);
556 zap_cursor_fini(&zc);
558 /* Create the full "pool/fs#bookmark" string to return */
559 int n = snprintf(bookmark_name, ZFS_MAX_DATASET_NAME_LEN, "%s#%s",
560 ds_name, za->za_name);
561 zap_attribute_free(za);
562 if (n >= ZFS_MAX_DATASET_NAME_LEN) {
563 return (luaL_error(state,
564 "unexpected error %d from snprintf()", ENAMETOOLONG));
567 lua_pushnumber(state, cursor);
568 lua_replace(state, lua_upvalueindex(2));
570 (void) lua_pushstring(state, bookmark_name);
571 return (1);
574 static int zcp_bookmarks_list(lua_State *);
575 static const zcp_list_info_t zcp_bookmarks_list_info = {
576 .name = "bookmarks",
577 .func = zcp_bookmarks_list,
578 .pargs = {
579 { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
580 {NULL, 0}
582 .kwargs = {
583 {NULL, 0}
587 static int
588 zcp_bookmarks_list(lua_State *state)
590 const char *dsname = lua_tostring(state, 1);
591 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
593 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
594 if (ds == NULL)
595 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
597 boolean_t issnap = ds->ds_is_snapshot;
598 uint64_t dsobj = ds->ds_object;
599 uint64_t cursor = 0;
600 dsl_dataset_rele(ds, FTAG);
602 if (issnap) {
603 return (zcp_argerror(state, 1, "%s is a snapshot", dsname));
606 lua_pushnumber(state, dsobj);
607 lua_pushnumber(state, cursor);
608 lua_pushcclosure(state, &zcp_bookmarks_iter, 2);
609 return (1);
612 static int
613 zcp_holds_iter(lua_State *state)
615 uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
616 uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
617 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
618 dsl_dataset_t *ds;
619 zap_attribute_t *za;
620 zap_cursor_t zc;
622 int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
623 if (err == ENOENT) {
624 return (0);
625 } else if (err != 0) {
626 return (luaL_error(state,
627 "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
628 err));
631 if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
632 dsl_dataset_rele(ds, FTAG);
633 return (0);
636 zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
637 dsl_dataset_phys(ds)->ds_userrefs_obj, cursor);
638 dsl_dataset_rele(ds, FTAG);
640 za = zap_attribute_alloc();
641 err = zap_cursor_retrieve(&zc, za);
642 if (err != 0) {
643 zap_cursor_fini(&zc);
644 zap_attribute_free(za);
645 if (err != ENOENT) {
646 return (luaL_error(state,
647 "unexpected error %d from zap_cursor_retrieve()",
648 err));
650 return (0);
652 zap_cursor_advance(&zc);
653 cursor = zap_cursor_serialize(&zc);
654 zap_cursor_fini(&zc);
656 lua_pushnumber(state, cursor);
657 lua_replace(state, lua_upvalueindex(2));
659 (void) lua_pushstring(state, za->za_name);
660 (void) lua_pushnumber(state, za->za_first_integer);
661 zap_attribute_free(za);
662 return (2);
665 static int zcp_holds_list(lua_State *);
666 static const zcp_list_info_t zcp_holds_list_info = {
667 .name = "holds",
668 .func = zcp_holds_list,
669 .gc = NULL,
670 .pargs = {
671 { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
672 {NULL, 0}
674 .kwargs = {
675 {NULL, 0}
680 * Iterate over all the holds for a given dataset. Each iteration returns
681 * a hold's tag and its timestamp as an integer.
683 static int
684 zcp_holds_list(lua_State *state)
686 const char *snapname = lua_tostring(state, 1);
687 dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
689 dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
690 if (ds == NULL)
691 return (1); /* not reached; zcp_dataset_hold() longjmp'd */
693 boolean_t issnap = ds->ds_is_snapshot;
694 uint64_t dsobj = ds->ds_object;
695 uint64_t cursor = 0;
696 dsl_dataset_rele(ds, FTAG);
698 if (!issnap) {
699 return (zcp_argerror(state, 1, "%s is not a snapshot",
700 snapname));
703 lua_pushnumber(state, dsobj);
704 lua_pushnumber(state, cursor);
705 lua_pushcclosure(state, &zcp_holds_iter, 2);
706 return (1);
709 static int
710 zcp_list_func(lua_State *state)
712 zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
714 zcp_parse_args(state, info->name, info->pargs, info->kwargs);
716 return (info->func(state));
720 zcp_load_list_lib(lua_State *state)
722 const zcp_list_info_t *zcp_list_funcs[] = {
723 &zcp_children_list_info,
724 &zcp_snapshots_list_info,
725 &zcp_user_props_list_info,
726 &zcp_props_list_info,
727 &zcp_clones_list_info,
728 &zcp_system_props_list_info,
729 &zcp_bookmarks_list_info,
730 &zcp_holds_list_info,
731 NULL
734 lua_newtable(state);
736 for (int i = 0; zcp_list_funcs[i] != NULL; i++) {
737 const zcp_list_info_t *info = zcp_list_funcs[i];
739 if (info->gc != NULL) {
741 * If the function requires garbage collection, create
742 * a metatable with its name and register the __gc
743 * function.
745 (void) luaL_newmetatable(state, info->name);
746 (void) lua_pushstring(state, "__gc");
747 lua_pushcfunction(state, info->gc);
748 lua_settable(state, -3);
749 lua_pop(state, 1);
752 lua_pushlightuserdata(state, (void *)(uintptr_t)info);
753 lua_pushcclosure(state, &zcp_list_func, 1);
754 lua_setfield(state, -2, info->name);
757 return (1);