1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2eda14cbcSMatt Macy /*
3eda14cbcSMatt Macy * CDDL HEADER START
4eda14cbcSMatt Macy *
5eda14cbcSMatt Macy * This file and its contents are supplied under the terms of the
6eda14cbcSMatt Macy * Common Development and Distribution License ("CDDL"), version 1.0.
7eda14cbcSMatt Macy * You may only use this file in accordance with the terms of version
8eda14cbcSMatt Macy * 1.0 of the CDDL.
9eda14cbcSMatt Macy *
10eda14cbcSMatt Macy * A full copy of the text of the CDDL should have accompanied this
11eda14cbcSMatt Macy * source. A copy of the CDDL is also available via the Internet at
12eda14cbcSMatt Macy * http://www.illumos.org/license/CDDL.
13eda14cbcSMatt Macy *
14eda14cbcSMatt Macy * CDDL HEADER END
15eda14cbcSMatt Macy */
16eda14cbcSMatt Macy
17eda14cbcSMatt Macy /*
18eda14cbcSMatt Macy * Copyright (c) 2016, 2018 by Delphix. All rights reserved.
19eda14cbcSMatt Macy */
20eda14cbcSMatt Macy
21eda14cbcSMatt Macy #include <sys/lua/lua.h>
22eda14cbcSMatt Macy #include <sys/lua/lauxlib.h>
23eda14cbcSMatt Macy
24eda14cbcSMatt Macy #include <sys/dmu.h>
25eda14cbcSMatt Macy #include <sys/dsl_prop.h>
26eda14cbcSMatt Macy #include <sys/dsl_synctask.h>
27eda14cbcSMatt Macy #include <sys/dsl_bookmark.h>
28eda14cbcSMatt Macy #include <sys/dsl_dataset.h>
29eda14cbcSMatt Macy #include <sys/dsl_pool.h>
30eda14cbcSMatt Macy #include <sys/dmu_tx.h>
31eda14cbcSMatt Macy #include <sys/dmu_objset.h>
32eda14cbcSMatt Macy #include <sys/zap.h>
33eda14cbcSMatt Macy #include <sys/dsl_dir.h>
34eda14cbcSMatt Macy #include <sys/zcp_prop.h>
35eda14cbcSMatt Macy
36eda14cbcSMatt Macy #include <sys/zcp.h>
37eda14cbcSMatt Macy
38eda14cbcSMatt Macy #include "zfs_comutil.h"
39eda14cbcSMatt Macy
40eda14cbcSMatt Macy typedef int (zcp_list_func_t)(lua_State *);
41eda14cbcSMatt Macy typedef struct zcp_list_info {
42eda14cbcSMatt Macy const char *name;
43eda14cbcSMatt Macy zcp_list_func_t *func;
44eda14cbcSMatt Macy zcp_list_func_t *gc;
45eda14cbcSMatt Macy const zcp_arg_t pargs[4];
46eda14cbcSMatt Macy const zcp_arg_t kwargs[2];
47eda14cbcSMatt Macy } zcp_list_info_t;
48eda14cbcSMatt Macy
49eda14cbcSMatt Macy static int
zcp_clones_iter(lua_State * state)50eda14cbcSMatt Macy zcp_clones_iter(lua_State *state)
51eda14cbcSMatt Macy {
52eda14cbcSMatt Macy int err;
53eda14cbcSMatt Macy char clonename[ZFS_MAX_DATASET_NAME_LEN];
54eda14cbcSMatt Macy uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
55eda14cbcSMatt Macy uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
56eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
57eda14cbcSMatt Macy dsl_dataset_t *ds, *clone;
587a7741afSMartin Matuska zap_attribute_t *za;
59eda14cbcSMatt Macy zap_cursor_t zc;
60eda14cbcSMatt Macy
61eda14cbcSMatt Macy err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
62eda14cbcSMatt Macy if (err == ENOENT) {
63eda14cbcSMatt Macy return (0);
64eda14cbcSMatt Macy } else if (err != 0) {
65eda14cbcSMatt Macy return (luaL_error(state,
66eda14cbcSMatt Macy "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
67eda14cbcSMatt Macy err));
68eda14cbcSMatt Macy }
69eda14cbcSMatt Macy
70eda14cbcSMatt Macy if (dsl_dataset_phys(ds)->ds_next_clones_obj == 0) {
71eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
72eda14cbcSMatt Macy return (0);
73eda14cbcSMatt Macy }
74eda14cbcSMatt Macy
75eda14cbcSMatt Macy zap_cursor_init_serialized(&zc, dp->dp_meta_objset,
76eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_next_clones_obj, cursor);
77eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
78eda14cbcSMatt Macy
797a7741afSMartin Matuska za = zap_attribute_alloc();
807a7741afSMartin Matuska err = zap_cursor_retrieve(&zc, za);
81eda14cbcSMatt Macy if (err != 0) {
82eda14cbcSMatt Macy zap_cursor_fini(&zc);
837a7741afSMartin Matuska zap_attribute_free(za);
84eda14cbcSMatt Macy if (err != ENOENT) {
85eda14cbcSMatt Macy return (luaL_error(state,
86eda14cbcSMatt Macy "unexpected error %d from zap_cursor_retrieve()",
87eda14cbcSMatt Macy err));
88eda14cbcSMatt Macy }
89eda14cbcSMatt Macy return (0);
90eda14cbcSMatt Macy }
91eda14cbcSMatt Macy zap_cursor_advance(&zc);
92eda14cbcSMatt Macy cursor = zap_cursor_serialize(&zc);
93eda14cbcSMatt Macy zap_cursor_fini(&zc);
94eda14cbcSMatt Macy
957a7741afSMartin Matuska err = dsl_dataset_hold_obj(dp, za->za_first_integer, FTAG, &clone);
967a7741afSMartin Matuska zap_attribute_free(za);
97eda14cbcSMatt Macy if (err != 0) {
98eda14cbcSMatt Macy return (luaL_error(state,
99eda14cbcSMatt Macy "unexpected error %d from "
100eda14cbcSMatt Macy "dsl_dataset_hold_obj(za_first_integer)", err));
101eda14cbcSMatt Macy }
102eda14cbcSMatt Macy
103eda14cbcSMatt Macy dsl_dir_name(clone->ds_dir, clonename);
104eda14cbcSMatt Macy dsl_dataset_rele(clone, FTAG);
105eda14cbcSMatt Macy
106eda14cbcSMatt Macy lua_pushnumber(state, cursor);
107eda14cbcSMatt Macy lua_replace(state, lua_upvalueindex(2));
108eda14cbcSMatt Macy
109eda14cbcSMatt Macy (void) lua_pushstring(state, clonename);
110eda14cbcSMatt Macy return (1);
111eda14cbcSMatt Macy }
112eda14cbcSMatt Macy
113eda14cbcSMatt Macy static int zcp_clones_list(lua_State *);
114e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_clones_list_info = {
115eda14cbcSMatt Macy .name = "clones",
116eda14cbcSMatt Macy .func = zcp_clones_list,
117eda14cbcSMatt Macy .gc = NULL,
118eda14cbcSMatt Macy .pargs = {
119eda14cbcSMatt Macy { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
120eda14cbcSMatt Macy {NULL, 0}
121eda14cbcSMatt Macy },
122eda14cbcSMatt Macy .kwargs = {
123eda14cbcSMatt Macy {NULL, 0}
124eda14cbcSMatt Macy }
125eda14cbcSMatt Macy };
126eda14cbcSMatt Macy
127eda14cbcSMatt Macy static int
zcp_clones_list(lua_State * state)128eda14cbcSMatt Macy zcp_clones_list(lua_State *state)
129eda14cbcSMatt Macy {
130eda14cbcSMatt Macy const char *snapname = lua_tostring(state, 1);
131eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
132eda14cbcSMatt Macy
133eda14cbcSMatt Macy /*
134eda14cbcSMatt Macy * zcp_dataset_hold will either successfully return the requested
135eda14cbcSMatt Macy * dataset or throw a lua error and longjmp out of the zfs.list.clones
136eda14cbcSMatt Macy * call without returning.
137eda14cbcSMatt Macy */
138eda14cbcSMatt Macy dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
139eda14cbcSMatt Macy if (ds == NULL)
140eda14cbcSMatt Macy return (1); /* not reached; zcp_dataset_hold() longjmp'd */
141eda14cbcSMatt Macy boolean_t issnap = ds->ds_is_snapshot;
142eda14cbcSMatt Macy uint64_t cursor = 0;
143eda14cbcSMatt Macy uint64_t dsobj = ds->ds_object;
144eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
145eda14cbcSMatt Macy
146eda14cbcSMatt Macy if (!issnap) {
147eda14cbcSMatt Macy return (zcp_argerror(state, 1, "%s is not a snapshot",
148eda14cbcSMatt Macy snapname));
149eda14cbcSMatt Macy }
150eda14cbcSMatt Macy
151eda14cbcSMatt Macy lua_pushnumber(state, dsobj);
152eda14cbcSMatt Macy lua_pushnumber(state, cursor);
153eda14cbcSMatt Macy lua_pushcclosure(state, &zcp_clones_iter, 2);
154eda14cbcSMatt Macy return (1);
155eda14cbcSMatt Macy }
156eda14cbcSMatt Macy
157eda14cbcSMatt Macy static int
zcp_snapshots_iter(lua_State * state)158eda14cbcSMatt Macy zcp_snapshots_iter(lua_State *state)
159eda14cbcSMatt Macy {
160eda14cbcSMatt Macy int err;
161eda14cbcSMatt Macy char snapname[ZFS_MAX_DATASET_NAME_LEN];
162eda14cbcSMatt Macy uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
163eda14cbcSMatt Macy uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
164eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
165eda14cbcSMatt Macy dsl_dataset_t *ds;
166eda14cbcSMatt Macy objset_t *os;
167eda14cbcSMatt Macy char *p;
168eda14cbcSMatt Macy
169eda14cbcSMatt Macy err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
170eda14cbcSMatt Macy if (err != 0) {
171eda14cbcSMatt Macy return (luaL_error(state,
172eda14cbcSMatt Macy "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
173eda14cbcSMatt Macy err));
174eda14cbcSMatt Macy }
175eda14cbcSMatt Macy
176eda14cbcSMatt Macy dsl_dataset_name(ds, snapname);
177eda14cbcSMatt Macy VERIFY3U(sizeof (snapname), >,
178eda14cbcSMatt Macy strlcat(snapname, "@", sizeof (snapname)));
179eda14cbcSMatt Macy
180eda14cbcSMatt Macy p = strchr(snapname, '\0');
181eda14cbcSMatt Macy VERIFY0(dmu_objset_from_ds(ds, &os));
182eda14cbcSMatt Macy err = dmu_snapshot_list_next(os,
183eda14cbcSMatt Macy sizeof (snapname) - (p - snapname), p, NULL, &cursor, NULL);
184eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
185eda14cbcSMatt Macy
186eda14cbcSMatt Macy if (err == ENOENT) {
187eda14cbcSMatt Macy return (0);
188eda14cbcSMatt Macy } else if (err != 0) {
189eda14cbcSMatt Macy return (luaL_error(state,
190eda14cbcSMatt Macy "unexpected error %d from dmu_snapshot_list_next()", err));
191eda14cbcSMatt Macy }
192eda14cbcSMatt Macy
193eda14cbcSMatt Macy lua_pushnumber(state, cursor);
194eda14cbcSMatt Macy lua_replace(state, lua_upvalueindex(2));
195eda14cbcSMatt Macy
196eda14cbcSMatt Macy (void) lua_pushstring(state, snapname);
197eda14cbcSMatt Macy return (1);
198eda14cbcSMatt Macy }
199eda14cbcSMatt Macy
200eda14cbcSMatt Macy static int zcp_snapshots_list(lua_State *);
201e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_snapshots_list_info = {
202eda14cbcSMatt Macy .name = "snapshots",
203eda14cbcSMatt Macy .func = zcp_snapshots_list,
204eda14cbcSMatt Macy .gc = NULL,
205eda14cbcSMatt Macy .pargs = {
206eda14cbcSMatt Macy { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
207eda14cbcSMatt Macy {NULL, 0}
208eda14cbcSMatt Macy },
209eda14cbcSMatt Macy .kwargs = {
210eda14cbcSMatt Macy {NULL, 0}
211eda14cbcSMatt Macy }
212eda14cbcSMatt Macy };
213eda14cbcSMatt Macy
214eda14cbcSMatt Macy static int
zcp_snapshots_list(lua_State * state)215eda14cbcSMatt Macy zcp_snapshots_list(lua_State *state)
216eda14cbcSMatt Macy {
217eda14cbcSMatt Macy const char *fsname = lua_tostring(state, 1);
218eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
219eda14cbcSMatt Macy boolean_t issnap;
220eda14cbcSMatt Macy uint64_t dsobj;
221eda14cbcSMatt Macy
222eda14cbcSMatt Macy dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
223eda14cbcSMatt Macy if (ds == NULL)
224eda14cbcSMatt Macy return (1); /* not reached; zcp_dataset_hold() longjmp'd */
225eda14cbcSMatt Macy issnap = ds->ds_is_snapshot;
226eda14cbcSMatt Macy dsobj = ds->ds_object;
227eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
228eda14cbcSMatt Macy
229eda14cbcSMatt Macy if (issnap) {
230eda14cbcSMatt Macy return (zcp_argerror(state, 1,
231eda14cbcSMatt Macy "argument %s cannot be a snapshot", fsname));
232eda14cbcSMatt Macy }
233eda14cbcSMatt Macy
234eda14cbcSMatt Macy lua_pushnumber(state, dsobj);
235eda14cbcSMatt Macy lua_pushnumber(state, 0);
236eda14cbcSMatt Macy lua_pushcclosure(state, &zcp_snapshots_iter, 2);
237eda14cbcSMatt Macy return (1);
238eda14cbcSMatt Macy }
239eda14cbcSMatt Macy
240eda14cbcSMatt Macy static int
zcp_children_iter(lua_State * state)241eda14cbcSMatt Macy zcp_children_iter(lua_State *state)
242eda14cbcSMatt Macy {
243eda14cbcSMatt Macy int err;
244eda14cbcSMatt Macy char childname[ZFS_MAX_DATASET_NAME_LEN];
245eda14cbcSMatt Macy uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
246eda14cbcSMatt Macy uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
247eda14cbcSMatt Macy zcp_run_info_t *ri = zcp_run_info(state);
248eda14cbcSMatt Macy dsl_pool_t *dp = ri->zri_pool;
249eda14cbcSMatt Macy dsl_dataset_t *ds;
250eda14cbcSMatt Macy objset_t *os;
251eda14cbcSMatt Macy char *p;
252eda14cbcSMatt Macy
253eda14cbcSMatt Macy err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
254eda14cbcSMatt Macy if (err != 0) {
255eda14cbcSMatt Macy return (luaL_error(state,
256eda14cbcSMatt Macy "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
257eda14cbcSMatt Macy err));
258eda14cbcSMatt Macy }
259eda14cbcSMatt Macy
260eda14cbcSMatt Macy dsl_dataset_name(ds, childname);
261eda14cbcSMatt Macy VERIFY3U(sizeof (childname), >,
262eda14cbcSMatt Macy strlcat(childname, "/", sizeof (childname)));
263eda14cbcSMatt Macy p = strchr(childname, '\0');
264eda14cbcSMatt Macy
265eda14cbcSMatt Macy VERIFY0(dmu_objset_from_ds(ds, &os));
266eda14cbcSMatt Macy do {
267eda14cbcSMatt Macy err = dmu_dir_list_next(os,
268eda14cbcSMatt Macy sizeof (childname) - (p - childname), p, NULL, &cursor);
269eda14cbcSMatt Macy } while (err == 0 && zfs_dataset_name_hidden(childname));
270eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
271eda14cbcSMatt Macy
272eda14cbcSMatt Macy if (err == ENOENT) {
273eda14cbcSMatt Macy return (0);
274eda14cbcSMatt Macy } else if (err != 0) {
275eda14cbcSMatt Macy return (luaL_error(state,
276eda14cbcSMatt Macy "unexpected error %d from dmu_dir_list_next()",
277eda14cbcSMatt Macy err));
278eda14cbcSMatt Macy }
279eda14cbcSMatt Macy
280eda14cbcSMatt Macy lua_pushnumber(state, cursor);
281eda14cbcSMatt Macy lua_replace(state, lua_upvalueindex(2));
282eda14cbcSMatt Macy
283eda14cbcSMatt Macy (void) lua_pushstring(state, childname);
284eda14cbcSMatt Macy return (1);
285eda14cbcSMatt Macy }
286eda14cbcSMatt Macy
287eda14cbcSMatt Macy static int zcp_children_list(lua_State *);
288e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_children_list_info = {
289eda14cbcSMatt Macy .name = "children",
290eda14cbcSMatt Macy .func = zcp_children_list,
291eda14cbcSMatt Macy .gc = NULL,
292eda14cbcSMatt Macy .pargs = {
293eda14cbcSMatt Macy { .za_name = "filesystem | volume", .za_lua_type = LUA_TSTRING },
294eda14cbcSMatt Macy {NULL, 0}
295eda14cbcSMatt Macy },
296eda14cbcSMatt Macy .kwargs = {
297eda14cbcSMatt Macy {NULL, 0}
298eda14cbcSMatt Macy }
299eda14cbcSMatt Macy };
300eda14cbcSMatt Macy
301eda14cbcSMatt Macy static int
zcp_children_list(lua_State * state)302eda14cbcSMatt Macy zcp_children_list(lua_State *state)
303eda14cbcSMatt Macy {
304eda14cbcSMatt Macy const char *fsname = lua_tostring(state, 1);
305eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
306eda14cbcSMatt Macy boolean_t issnap;
307eda14cbcSMatt Macy uint64_t dsobj;
308eda14cbcSMatt Macy
309eda14cbcSMatt Macy dsl_dataset_t *ds = zcp_dataset_hold(state, dp, fsname, FTAG);
310eda14cbcSMatt Macy if (ds == NULL)
311eda14cbcSMatt Macy return (1); /* not reached; zcp_dataset_hold() longjmp'd */
312eda14cbcSMatt Macy
313eda14cbcSMatt Macy issnap = ds->ds_is_snapshot;
314eda14cbcSMatt Macy dsobj = ds->ds_object;
315eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
316eda14cbcSMatt Macy
317eda14cbcSMatt Macy if (issnap) {
318eda14cbcSMatt Macy return (zcp_argerror(state, 1,
319eda14cbcSMatt Macy "argument %s cannot be a snapshot", fsname));
320eda14cbcSMatt Macy }
321eda14cbcSMatt Macy
322eda14cbcSMatt Macy lua_pushnumber(state, dsobj);
323eda14cbcSMatt Macy lua_pushnumber(state, 0);
324eda14cbcSMatt Macy lua_pushcclosure(state, &zcp_children_iter, 2);
325eda14cbcSMatt Macy return (1);
326eda14cbcSMatt Macy }
327eda14cbcSMatt Macy
328eda14cbcSMatt Macy static int
zcp_user_props_list_gc(lua_State * state)329eda14cbcSMatt Macy zcp_user_props_list_gc(lua_State *state)
330eda14cbcSMatt Macy {
331eda14cbcSMatt Macy nvlist_t **props = lua_touserdata(state, 1);
332eda14cbcSMatt Macy if (*props != NULL)
333eda14cbcSMatt Macy fnvlist_free(*props);
334eda14cbcSMatt Macy return (0);
335eda14cbcSMatt Macy }
336eda14cbcSMatt Macy
337eda14cbcSMatt Macy static int
zcp_user_props_iter(lua_State * state)338eda14cbcSMatt Macy zcp_user_props_iter(lua_State *state)
339eda14cbcSMatt Macy {
3402a58b312SMartin Matuska const char *source, *val;
341eda14cbcSMatt Macy nvlist_t *nvprop;
342eda14cbcSMatt Macy nvlist_t **props = lua_touserdata(state, lua_upvalueindex(1));
343eda14cbcSMatt Macy nvpair_t *pair = lua_touserdata(state, lua_upvalueindex(2));
344eda14cbcSMatt Macy
345eda14cbcSMatt Macy do {
346eda14cbcSMatt Macy pair = nvlist_next_nvpair(*props, pair);
347eda14cbcSMatt Macy if (pair == NULL) {
348eda14cbcSMatt Macy fnvlist_free(*props);
349eda14cbcSMatt Macy *props = NULL;
350eda14cbcSMatt Macy return (0);
351eda14cbcSMatt Macy }
352eda14cbcSMatt Macy } while (!zfs_prop_user(nvpair_name(pair)));
353eda14cbcSMatt Macy
354eda14cbcSMatt Macy lua_pushlightuserdata(state, pair);
355eda14cbcSMatt Macy lua_replace(state, lua_upvalueindex(2));
356eda14cbcSMatt Macy
357eda14cbcSMatt Macy nvprop = fnvpair_value_nvlist(pair);
358eda14cbcSMatt Macy val = fnvlist_lookup_string(nvprop, ZPROP_VALUE);
359eda14cbcSMatt Macy source = fnvlist_lookup_string(nvprop, ZPROP_SOURCE);
360eda14cbcSMatt Macy
361eda14cbcSMatt Macy (void) lua_pushstring(state, nvpair_name(pair));
362eda14cbcSMatt Macy (void) lua_pushstring(state, val);
363eda14cbcSMatt Macy (void) lua_pushstring(state, source);
364eda14cbcSMatt Macy return (3);
365eda14cbcSMatt Macy }
366eda14cbcSMatt Macy
367eda14cbcSMatt Macy static int zcp_user_props_list(lua_State *);
368e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_user_props_list_info = {
369eda14cbcSMatt Macy .name = "user_properties",
370eda14cbcSMatt Macy .func = zcp_user_props_list,
371eda14cbcSMatt Macy .gc = zcp_user_props_list_gc,
372eda14cbcSMatt Macy .pargs = {
373eda14cbcSMatt Macy { .za_name = "filesystem | snapshot | volume",
374eda14cbcSMatt Macy .za_lua_type = LUA_TSTRING },
375eda14cbcSMatt Macy {NULL, 0}
376eda14cbcSMatt Macy },
377eda14cbcSMatt Macy .kwargs = {
378eda14cbcSMatt Macy {NULL, 0}
379eda14cbcSMatt Macy }
380eda14cbcSMatt Macy };
381eda14cbcSMatt Macy
382eda14cbcSMatt Macy /*
383eda14cbcSMatt Macy * 'properties' was the initial name for 'user_properties' seen
384eda14cbcSMatt Macy * above. 'user_properties' is a better name as it distinguishes
385eda14cbcSMatt Macy * these properties from 'system_properties' which are different.
386eda14cbcSMatt Macy * In order to avoid breaking compatibility between different
387eda14cbcSMatt Macy * versions of ZFS, we declare 'properties' as an alias for
388eda14cbcSMatt Macy * 'user_properties'.
389eda14cbcSMatt Macy */
390e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_props_list_info = {
391eda14cbcSMatt Macy .name = "properties",
392eda14cbcSMatt Macy .func = zcp_user_props_list,
393eda14cbcSMatt Macy .gc = zcp_user_props_list_gc,
394eda14cbcSMatt Macy .pargs = {
395eda14cbcSMatt Macy { .za_name = "filesystem | snapshot | volume",
396eda14cbcSMatt Macy .za_lua_type = LUA_TSTRING },
397eda14cbcSMatt Macy {NULL, 0}
398eda14cbcSMatt Macy },
399eda14cbcSMatt Macy .kwargs = {
400eda14cbcSMatt Macy {NULL, 0}
401eda14cbcSMatt Macy }
402eda14cbcSMatt Macy };
403eda14cbcSMatt Macy
404eda14cbcSMatt Macy static int
zcp_user_props_list(lua_State * state)405eda14cbcSMatt Macy zcp_user_props_list(lua_State *state)
406eda14cbcSMatt Macy {
407eda14cbcSMatt Macy const char *dsname = lua_tostring(state, 1);
408eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
409eda14cbcSMatt Macy objset_t *os;
410eda14cbcSMatt Macy nvlist_t **props = lua_newuserdata(state, sizeof (nvlist_t *));
411eda14cbcSMatt Macy
412eda14cbcSMatt Macy dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
413eda14cbcSMatt Macy if (ds == NULL)
414eda14cbcSMatt Macy return (1); /* not reached; zcp_dataset_hold() longjmp'd */
415eda14cbcSMatt Macy VERIFY0(dmu_objset_from_ds(ds, &os));
416eda14cbcSMatt Macy VERIFY0(dsl_prop_get_all(os, props));
417eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
418eda14cbcSMatt Macy
419eda14cbcSMatt Macy /*
420eda14cbcSMatt Macy * Set the metatable for the properties list to free it on
421eda14cbcSMatt Macy * completion.
422eda14cbcSMatt Macy */
423eda14cbcSMatt Macy luaL_getmetatable(state, zcp_user_props_list_info.name);
424eda14cbcSMatt Macy (void) lua_setmetatable(state, -2);
425eda14cbcSMatt Macy
426eda14cbcSMatt Macy lua_pushlightuserdata(state, NULL);
427eda14cbcSMatt Macy lua_pushcclosure(state, &zcp_user_props_iter, 2);
428eda14cbcSMatt Macy return (1);
429eda14cbcSMatt Macy }
430eda14cbcSMatt Macy
431eda14cbcSMatt Macy
432eda14cbcSMatt Macy /*
433eda14cbcSMatt Macy * Populate nv with all valid system properties and their values for the given
434eda14cbcSMatt Macy * dataset.
435eda14cbcSMatt Macy */
436eda14cbcSMatt Macy static void
zcp_dataset_system_props(dsl_dataset_t * ds,nvlist_t * nv)437eda14cbcSMatt Macy zcp_dataset_system_props(dsl_dataset_t *ds, nvlist_t *nv)
438eda14cbcSMatt Macy {
439eda14cbcSMatt Macy for (int prop = ZFS_PROP_TYPE; prop < ZFS_NUM_PROPS; prop++) {
440eda14cbcSMatt Macy /* Do not display hidden props */
441eda14cbcSMatt Macy if (!zfs_prop_visible(prop))
442eda14cbcSMatt Macy continue;
443eda14cbcSMatt Macy /* Do not display props not valid for this dataset */
444eda14cbcSMatt Macy if (!prop_valid_for_ds(ds, prop))
445eda14cbcSMatt Macy continue;
446eda14cbcSMatt Macy fnvlist_add_boolean(nv, zfs_prop_to_name(prop));
447eda14cbcSMatt Macy }
448eda14cbcSMatt Macy }
449eda14cbcSMatt Macy
450eda14cbcSMatt Macy static int zcp_system_props_list(lua_State *);
451e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_system_props_list_info = {
452eda14cbcSMatt Macy .name = "system_properties",
453eda14cbcSMatt Macy .func = zcp_system_props_list,
454eda14cbcSMatt Macy .pargs = {
455eda14cbcSMatt Macy { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
456eda14cbcSMatt Macy {NULL, 0}
457eda14cbcSMatt Macy },
458eda14cbcSMatt Macy .kwargs = {
459eda14cbcSMatt Macy {NULL, 0}
460eda14cbcSMatt Macy }
461eda14cbcSMatt Macy };
462eda14cbcSMatt Macy
463eda14cbcSMatt Macy /*
464eda14cbcSMatt Macy * Get a list of all visible system properties and their values for a given
465eda14cbcSMatt Macy * dataset. Returned on the stack as a Lua table.
466eda14cbcSMatt Macy */
467eda14cbcSMatt Macy static int
zcp_system_props_list(lua_State * state)468eda14cbcSMatt Macy zcp_system_props_list(lua_State *state)
469eda14cbcSMatt Macy {
470eda14cbcSMatt Macy int error;
471eda14cbcSMatt Macy char errbuf[128];
472eda14cbcSMatt Macy const char *dataset_name;
473eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
474e92ffd9bSMartin Matuska const zcp_list_info_t *libinfo = &zcp_system_props_list_info;
475eda14cbcSMatt Macy zcp_parse_args(state, libinfo->name, libinfo->pargs, libinfo->kwargs);
476eda14cbcSMatt Macy dataset_name = lua_tostring(state, 1);
477eda14cbcSMatt Macy nvlist_t *nv = fnvlist_alloc();
478eda14cbcSMatt Macy
479eda14cbcSMatt Macy dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dataset_name, FTAG);
480eda14cbcSMatt Macy if (ds == NULL)
481eda14cbcSMatt Macy return (1); /* not reached; zcp_dataset_hold() longjmp'd */
482eda14cbcSMatt Macy
483eda14cbcSMatt Macy /* Get the names of all valid system properties for this dataset */
484eda14cbcSMatt Macy zcp_dataset_system_props(ds, nv);
485eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
486eda14cbcSMatt Macy
487eda14cbcSMatt Macy /* push list as lua table */
488eda14cbcSMatt Macy error = zcp_nvlist_to_lua(state, nv, errbuf, sizeof (errbuf));
489eda14cbcSMatt Macy nvlist_free(nv);
490eda14cbcSMatt Macy if (error != 0) {
491eda14cbcSMatt Macy return (luaL_error(state,
492eda14cbcSMatt Macy "Error returning nvlist: %s", errbuf));
493eda14cbcSMatt Macy }
494eda14cbcSMatt Macy return (1);
495eda14cbcSMatt Macy }
496eda14cbcSMatt Macy
497eda14cbcSMatt Macy static int
zcp_bookmarks_iter(lua_State * state)498eda14cbcSMatt Macy zcp_bookmarks_iter(lua_State *state)
499eda14cbcSMatt Macy {
500eda14cbcSMatt Macy char ds_name[ZFS_MAX_DATASET_NAME_LEN];
501eda14cbcSMatt Macy char bookmark_name[ZFS_MAX_DATASET_NAME_LEN];
502eda14cbcSMatt Macy uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
503eda14cbcSMatt Macy uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
504eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
505eda14cbcSMatt Macy dsl_dataset_t *ds;
5067a7741afSMartin Matuska zap_attribute_t *za;
507eda14cbcSMatt Macy zap_cursor_t zc;
508eda14cbcSMatt Macy
509eda14cbcSMatt Macy int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
510eda14cbcSMatt Macy if (err == ENOENT) {
511eda14cbcSMatt Macy return (0);
512eda14cbcSMatt Macy } else if (err != 0) {
513eda14cbcSMatt Macy return (luaL_error(state,
514eda14cbcSMatt Macy "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
515eda14cbcSMatt Macy err));
516eda14cbcSMatt Macy }
517eda14cbcSMatt Macy
518eda14cbcSMatt Macy if (!dsl_dataset_is_zapified(ds)) {
519eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
520eda14cbcSMatt Macy return (0);
521eda14cbcSMatt Macy }
522eda14cbcSMatt Macy
523eda14cbcSMatt Macy err = zap_lookup(dp->dp_meta_objset, ds->ds_object,
524eda14cbcSMatt Macy DS_FIELD_BOOKMARK_NAMES, sizeof (ds->ds_bookmarks_obj), 1,
525eda14cbcSMatt Macy &ds->ds_bookmarks_obj);
526eda14cbcSMatt Macy if (err != 0 && err != ENOENT) {
527eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
528eda14cbcSMatt Macy return (luaL_error(state,
529eda14cbcSMatt Macy "unexpected error %d from zap_lookup()", err));
530eda14cbcSMatt Macy }
531eda14cbcSMatt Macy if (ds->ds_bookmarks_obj == 0) {
532eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
533eda14cbcSMatt Macy return (0);
534eda14cbcSMatt Macy }
535eda14cbcSMatt Macy
536eda14cbcSMatt Macy /* Store the dataset's name so we can append the bookmark's name */
537eda14cbcSMatt Macy dsl_dataset_name(ds, ds_name);
538eda14cbcSMatt Macy
539eda14cbcSMatt Macy zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
540eda14cbcSMatt Macy ds->ds_bookmarks_obj, cursor);
541eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
542eda14cbcSMatt Macy
5437a7741afSMartin Matuska za = zap_attribute_alloc();
5447a7741afSMartin Matuska err = zap_cursor_retrieve(&zc, za);
545eda14cbcSMatt Macy if (err != 0) {
546eda14cbcSMatt Macy zap_cursor_fini(&zc);
5477a7741afSMartin Matuska zap_attribute_free(za);
548eda14cbcSMatt Macy if (err != ENOENT) {
549eda14cbcSMatt Macy return (luaL_error(state,
550eda14cbcSMatt Macy "unexpected error %d from zap_cursor_retrieve()",
551eda14cbcSMatt Macy err));
552eda14cbcSMatt Macy }
553eda14cbcSMatt Macy return (0);
554eda14cbcSMatt Macy }
555eda14cbcSMatt Macy zap_cursor_advance(&zc);
556eda14cbcSMatt Macy cursor = zap_cursor_serialize(&zc);
557eda14cbcSMatt Macy zap_cursor_fini(&zc);
558eda14cbcSMatt Macy
559eda14cbcSMatt Macy /* Create the full "pool/fs#bookmark" string to return */
560eda14cbcSMatt Macy int n = snprintf(bookmark_name, ZFS_MAX_DATASET_NAME_LEN, "%s#%s",
5617a7741afSMartin Matuska ds_name, za->za_name);
5627a7741afSMartin Matuska zap_attribute_free(za);
563eda14cbcSMatt Macy if (n >= ZFS_MAX_DATASET_NAME_LEN) {
564eda14cbcSMatt Macy return (luaL_error(state,
565eda14cbcSMatt Macy "unexpected error %d from snprintf()", ENAMETOOLONG));
566eda14cbcSMatt Macy }
567eda14cbcSMatt Macy
568eda14cbcSMatt Macy lua_pushnumber(state, cursor);
569eda14cbcSMatt Macy lua_replace(state, lua_upvalueindex(2));
570eda14cbcSMatt Macy
571eda14cbcSMatt Macy (void) lua_pushstring(state, bookmark_name);
572eda14cbcSMatt Macy return (1);
573eda14cbcSMatt Macy }
574eda14cbcSMatt Macy
575eda14cbcSMatt Macy static int zcp_bookmarks_list(lua_State *);
576e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_bookmarks_list_info = {
577eda14cbcSMatt Macy .name = "bookmarks",
578eda14cbcSMatt Macy .func = zcp_bookmarks_list,
579eda14cbcSMatt Macy .pargs = {
580eda14cbcSMatt Macy { .za_name = "dataset", .za_lua_type = LUA_TSTRING },
581eda14cbcSMatt Macy {NULL, 0}
582eda14cbcSMatt Macy },
583eda14cbcSMatt Macy .kwargs = {
584eda14cbcSMatt Macy {NULL, 0}
585eda14cbcSMatt Macy }
586eda14cbcSMatt Macy };
587eda14cbcSMatt Macy
588eda14cbcSMatt Macy static int
zcp_bookmarks_list(lua_State * state)589eda14cbcSMatt Macy zcp_bookmarks_list(lua_State *state)
590eda14cbcSMatt Macy {
591eda14cbcSMatt Macy const char *dsname = lua_tostring(state, 1);
592eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
593eda14cbcSMatt Macy
594eda14cbcSMatt Macy dsl_dataset_t *ds = zcp_dataset_hold(state, dp, dsname, FTAG);
595eda14cbcSMatt Macy if (ds == NULL)
596eda14cbcSMatt Macy return (1); /* not reached; zcp_dataset_hold() longjmp'd */
597eda14cbcSMatt Macy
598eda14cbcSMatt Macy boolean_t issnap = ds->ds_is_snapshot;
599eda14cbcSMatt Macy uint64_t dsobj = ds->ds_object;
600eda14cbcSMatt Macy uint64_t cursor = 0;
601eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
602eda14cbcSMatt Macy
603eda14cbcSMatt Macy if (issnap) {
604eda14cbcSMatt Macy return (zcp_argerror(state, 1, "%s is a snapshot", dsname));
605eda14cbcSMatt Macy }
606eda14cbcSMatt Macy
607eda14cbcSMatt Macy lua_pushnumber(state, dsobj);
608eda14cbcSMatt Macy lua_pushnumber(state, cursor);
609eda14cbcSMatt Macy lua_pushcclosure(state, &zcp_bookmarks_iter, 2);
610eda14cbcSMatt Macy return (1);
611eda14cbcSMatt Macy }
612eda14cbcSMatt Macy
613eda14cbcSMatt Macy static int
zcp_holds_iter(lua_State * state)614eda14cbcSMatt Macy zcp_holds_iter(lua_State *state)
615eda14cbcSMatt Macy {
616eda14cbcSMatt Macy uint64_t dsobj = lua_tonumber(state, lua_upvalueindex(1));
617eda14cbcSMatt Macy uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2));
618eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
619eda14cbcSMatt Macy dsl_dataset_t *ds;
6207a7741afSMartin Matuska zap_attribute_t *za;
621eda14cbcSMatt Macy zap_cursor_t zc;
622eda14cbcSMatt Macy
623eda14cbcSMatt Macy int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds);
624eda14cbcSMatt Macy if (err == ENOENT) {
625eda14cbcSMatt Macy return (0);
626eda14cbcSMatt Macy } else if (err != 0) {
627eda14cbcSMatt Macy return (luaL_error(state,
628eda14cbcSMatt Macy "unexpected error %d from dsl_dataset_hold_obj(dsobj)",
629eda14cbcSMatt Macy err));
630eda14cbcSMatt Macy }
631eda14cbcSMatt Macy
632eda14cbcSMatt Macy if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
633eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
634eda14cbcSMatt Macy return (0);
635eda14cbcSMatt Macy }
636eda14cbcSMatt Macy
637eda14cbcSMatt Macy zap_cursor_init_serialized(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
638eda14cbcSMatt Macy dsl_dataset_phys(ds)->ds_userrefs_obj, cursor);
639eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
640eda14cbcSMatt Macy
6417a7741afSMartin Matuska za = zap_attribute_alloc();
6427a7741afSMartin Matuska err = zap_cursor_retrieve(&zc, za);
643eda14cbcSMatt Macy if (err != 0) {
644eda14cbcSMatt Macy zap_cursor_fini(&zc);
6457a7741afSMartin Matuska zap_attribute_free(za);
646eda14cbcSMatt Macy if (err != ENOENT) {
647eda14cbcSMatt Macy return (luaL_error(state,
648eda14cbcSMatt Macy "unexpected error %d from zap_cursor_retrieve()",
649eda14cbcSMatt Macy err));
650eda14cbcSMatt Macy }
651eda14cbcSMatt Macy return (0);
652eda14cbcSMatt Macy }
653eda14cbcSMatt Macy zap_cursor_advance(&zc);
654eda14cbcSMatt Macy cursor = zap_cursor_serialize(&zc);
655eda14cbcSMatt Macy zap_cursor_fini(&zc);
656eda14cbcSMatt Macy
657eda14cbcSMatt Macy lua_pushnumber(state, cursor);
658eda14cbcSMatt Macy lua_replace(state, lua_upvalueindex(2));
659eda14cbcSMatt Macy
6607a7741afSMartin Matuska (void) lua_pushstring(state, za->za_name);
6617a7741afSMartin Matuska (void) lua_pushnumber(state, za->za_first_integer);
6627a7741afSMartin Matuska zap_attribute_free(za);
663eda14cbcSMatt Macy return (2);
664eda14cbcSMatt Macy }
665eda14cbcSMatt Macy
666eda14cbcSMatt Macy static int zcp_holds_list(lua_State *);
667e92ffd9bSMartin Matuska static const zcp_list_info_t zcp_holds_list_info = {
668eda14cbcSMatt Macy .name = "holds",
669eda14cbcSMatt Macy .func = zcp_holds_list,
670eda14cbcSMatt Macy .gc = NULL,
671eda14cbcSMatt Macy .pargs = {
672eda14cbcSMatt Macy { .za_name = "snapshot", .za_lua_type = LUA_TSTRING },
673eda14cbcSMatt Macy {NULL, 0}
674eda14cbcSMatt Macy },
675eda14cbcSMatt Macy .kwargs = {
676eda14cbcSMatt Macy {NULL, 0}
677eda14cbcSMatt Macy }
678eda14cbcSMatt Macy };
679eda14cbcSMatt Macy
680eda14cbcSMatt Macy /*
681eda14cbcSMatt Macy * Iterate over all the holds for a given dataset. Each iteration returns
682eda14cbcSMatt Macy * a hold's tag and its timestamp as an integer.
683eda14cbcSMatt Macy */
684eda14cbcSMatt Macy static int
zcp_holds_list(lua_State * state)685eda14cbcSMatt Macy zcp_holds_list(lua_State *state)
686eda14cbcSMatt Macy {
687eda14cbcSMatt Macy const char *snapname = lua_tostring(state, 1);
688eda14cbcSMatt Macy dsl_pool_t *dp = zcp_run_info(state)->zri_pool;
689eda14cbcSMatt Macy
690eda14cbcSMatt Macy dsl_dataset_t *ds = zcp_dataset_hold(state, dp, snapname, FTAG);
691eda14cbcSMatt Macy if (ds == NULL)
692eda14cbcSMatt Macy return (1); /* not reached; zcp_dataset_hold() longjmp'd */
693eda14cbcSMatt Macy
694eda14cbcSMatt Macy boolean_t issnap = ds->ds_is_snapshot;
695eda14cbcSMatt Macy uint64_t dsobj = ds->ds_object;
696eda14cbcSMatt Macy uint64_t cursor = 0;
697eda14cbcSMatt Macy dsl_dataset_rele(ds, FTAG);
698eda14cbcSMatt Macy
699eda14cbcSMatt Macy if (!issnap) {
700eda14cbcSMatt Macy return (zcp_argerror(state, 1, "%s is not a snapshot",
701eda14cbcSMatt Macy snapname));
702eda14cbcSMatt Macy }
703eda14cbcSMatt Macy
704eda14cbcSMatt Macy lua_pushnumber(state, dsobj);
705eda14cbcSMatt Macy lua_pushnumber(state, cursor);
706eda14cbcSMatt Macy lua_pushcclosure(state, &zcp_holds_iter, 2);
707eda14cbcSMatt Macy return (1);
708eda14cbcSMatt Macy }
709eda14cbcSMatt Macy
710eda14cbcSMatt Macy static int
zcp_list_func(lua_State * state)711eda14cbcSMatt Macy zcp_list_func(lua_State *state)
712eda14cbcSMatt Macy {
713eda14cbcSMatt Macy zcp_list_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
714eda14cbcSMatt Macy
715eda14cbcSMatt Macy zcp_parse_args(state, info->name, info->pargs, info->kwargs);
716eda14cbcSMatt Macy
717eda14cbcSMatt Macy return (info->func(state));
718eda14cbcSMatt Macy }
719eda14cbcSMatt Macy
720eda14cbcSMatt Macy int
zcp_load_list_lib(lua_State * state)721eda14cbcSMatt Macy zcp_load_list_lib(lua_State *state)
722eda14cbcSMatt Macy {
723e92ffd9bSMartin Matuska const zcp_list_info_t *zcp_list_funcs[] = {
724eda14cbcSMatt Macy &zcp_children_list_info,
725eda14cbcSMatt Macy &zcp_snapshots_list_info,
726eda14cbcSMatt Macy &zcp_user_props_list_info,
727eda14cbcSMatt Macy &zcp_props_list_info,
728eda14cbcSMatt Macy &zcp_clones_list_info,
729eda14cbcSMatt Macy &zcp_system_props_list_info,
730eda14cbcSMatt Macy &zcp_bookmarks_list_info,
731eda14cbcSMatt Macy &zcp_holds_list_info,
732eda14cbcSMatt Macy NULL
733eda14cbcSMatt Macy };
734eda14cbcSMatt Macy
735eda14cbcSMatt Macy lua_newtable(state);
736eda14cbcSMatt Macy
737e92ffd9bSMartin Matuska for (int i = 0; zcp_list_funcs[i] != NULL; i++) {
738e92ffd9bSMartin Matuska const zcp_list_info_t *info = zcp_list_funcs[i];
739eda14cbcSMatt Macy
740eda14cbcSMatt Macy if (info->gc != NULL) {
741eda14cbcSMatt Macy /*
742eda14cbcSMatt Macy * If the function requires garbage collection, create
743eda14cbcSMatt Macy * a metatable with its name and register the __gc
744eda14cbcSMatt Macy * function.
745eda14cbcSMatt Macy */
746eda14cbcSMatt Macy (void) luaL_newmetatable(state, info->name);
747eda14cbcSMatt Macy (void) lua_pushstring(state, "__gc");
748eda14cbcSMatt Macy lua_pushcfunction(state, info->gc);
749eda14cbcSMatt Macy lua_settable(state, -3);
750eda14cbcSMatt Macy lua_pop(state, 1);
751eda14cbcSMatt Macy }
752eda14cbcSMatt Macy
753e92ffd9bSMartin Matuska lua_pushlightuserdata(state, (void *)(uintptr_t)info);
754eda14cbcSMatt Macy lua_pushcclosure(state, &zcp_list_func, 1);
755eda14cbcSMatt Macy lua_setfield(state, -2, info->name);
756eda14cbcSMatt Macy }
757eda14cbcSMatt Macy
758eda14cbcSMatt Macy return (1);
759eda14cbcSMatt Macy }
760