1716fd348SMartin Matuska /*
2716fd348SMartin Matuska  * CDDL HEADER START
3716fd348SMartin Matuska  *
4716fd348SMartin Matuska  * This file and its contents are supplied under the terms of the
5716fd348SMartin Matuska  * Common Development and Distribution License ("CDDL"), version 1.0.
6716fd348SMartin Matuska  * You may only use this file in accordance with the terms of version
7716fd348SMartin Matuska  * 1.0 of the CDDL.
8716fd348SMartin Matuska  *
9716fd348SMartin Matuska  * A full copy of the text of the CDDL should have accompanied this
10716fd348SMartin Matuska  * source.  A copy of the CDDL is also available via the Internet at
11716fd348SMartin Matuska  * http://www.illumos.org/license/CDDL.
12716fd348SMartin Matuska  *
13716fd348SMartin Matuska  * CDDL HEADER END
14716fd348SMartin Matuska  */
15716fd348SMartin Matuska 
16716fd348SMartin Matuska /*
17716fd348SMartin Matuska  * Copyright (c) 2016 by Delphix. All rights reserved.
18716fd348SMartin Matuska  */
19716fd348SMartin Matuska 
20716fd348SMartin Matuska #include <stdio.h>
21716fd348SMartin Matuska #include <stdlib.h>
22716fd348SMartin Matuska #include <string.h>
23716fd348SMartin Matuska #include <libzfs_core.h>
24716fd348SMartin Matuska #include <sys/nvpair.h>
25716fd348SMartin Matuska 
26716fd348SMartin Matuska static nvlist_t *nvl;
27716fd348SMartin Matuska static const char *pool;
28716fd348SMartin Matuska static boolean_t unexpected_failures;
29716fd348SMartin Matuska 
30716fd348SMartin Matuska static boolean_t
nvlist_equal(nvlist_t * nvla,nvlist_t * nvlb)31716fd348SMartin Matuska nvlist_equal(nvlist_t *nvla, nvlist_t *nvlb)
32716fd348SMartin Matuska {
33716fd348SMartin Matuska 	if (fnvlist_num_pairs(nvla) != fnvlist_num_pairs(nvlb))
34716fd348SMartin Matuska 		return (B_FALSE);
35716fd348SMartin Matuska 	/*
36716fd348SMartin Matuska 	 * The nvlists have the same number of pairs and keys are unique, so
37716fd348SMartin Matuska 	 * if every key in A is also in B and assigned to the same value, the
38716fd348SMartin Matuska 	 * lists are identical.
39716fd348SMartin Matuska 	 */
40716fd348SMartin Matuska 	for (nvpair_t *pair = nvlist_next_nvpair(nvla, NULL);
41716fd348SMartin Matuska 	    pair != NULL; pair = nvlist_next_nvpair(nvla, pair)) {
42*2a58b312SMartin Matuska 		const char *key = nvpair_name(pair);
43716fd348SMartin Matuska 
44716fd348SMartin Matuska 		if (!nvlist_exists(nvlb, key))
45716fd348SMartin Matuska 			return (B_FALSE);
46716fd348SMartin Matuska 
47716fd348SMartin Matuska 		if (nvpair_type(pair) !=
48716fd348SMartin Matuska 		    nvpair_type(fnvlist_lookup_nvpair(nvlb, key)))
49716fd348SMartin Matuska 			return (B_FALSE);
50716fd348SMartin Matuska 
51716fd348SMartin Matuska 		switch (nvpair_type(pair)) {
52716fd348SMartin Matuska 		case DATA_TYPE_BOOLEAN_VALUE:
53716fd348SMartin Matuska 			if (fnvpair_value_boolean_value(pair) !=
54716fd348SMartin Matuska 			    fnvlist_lookup_boolean_value(nvlb, key)) {
55716fd348SMartin Matuska 				return (B_FALSE);
56716fd348SMartin Matuska 			}
57716fd348SMartin Matuska 			break;
58716fd348SMartin Matuska 		case DATA_TYPE_STRING:
59716fd348SMartin Matuska 			if (strcmp(fnvpair_value_string(pair),
60716fd348SMartin Matuska 			    fnvlist_lookup_string(nvlb, key))) {
61716fd348SMartin Matuska 				return (B_FALSE);
62716fd348SMartin Matuska 			}
63716fd348SMartin Matuska 			break;
64716fd348SMartin Matuska 		case DATA_TYPE_INT64:
65716fd348SMartin Matuska 			if (fnvpair_value_int64(pair) !=
66716fd348SMartin Matuska 			    fnvlist_lookup_int64(nvlb, key)) {
67716fd348SMartin Matuska 				return (B_FALSE);
68716fd348SMartin Matuska 			}
69716fd348SMartin Matuska 			break;
70716fd348SMartin Matuska 		case DATA_TYPE_NVLIST:
71716fd348SMartin Matuska 			if (!nvlist_equal(fnvpair_value_nvlist(pair),
72716fd348SMartin Matuska 			    fnvlist_lookup_nvlist(nvlb, key))) {
73716fd348SMartin Matuska 				return (B_FALSE);
74716fd348SMartin Matuska 			}
75716fd348SMartin Matuska 			break;
76716fd348SMartin Matuska 		default:
77716fd348SMartin Matuska 			(void) printf("Unexpected type for nvlist_equal\n");
78716fd348SMartin Matuska 			return (B_FALSE);
79716fd348SMartin Matuska 		}
80716fd348SMartin Matuska 	}
81716fd348SMartin Matuska 	return (B_TRUE);
82716fd348SMartin Matuska }
83716fd348SMartin Matuska 
84716fd348SMartin Matuska static void
test(const char * testname,boolean_t expect_success,boolean_t expect_match)85716fd348SMartin Matuska test(const char *testname, boolean_t expect_success, boolean_t expect_match)
86716fd348SMartin Matuska {
87a0b956f5SMartin Matuska 	const char *progstr = "input = ...; return {output=input}";
88716fd348SMartin Matuska 
89716fd348SMartin Matuska 	nvlist_t *outnvl;
90716fd348SMartin Matuska 
91716fd348SMartin Matuska 	(void) printf("\nrunning test '%s'; input:\n", testname);
92716fd348SMartin Matuska 	dump_nvlist(nvl, 4);
93716fd348SMartin Matuska 
94716fd348SMartin Matuska 	int err = lzc_channel_program(pool, progstr,
95716fd348SMartin Matuska 	    10 * 1000 * 1000, 10 * 1024 * 1024, nvl, &outnvl);
96716fd348SMartin Matuska 
97716fd348SMartin Matuska 	(void) printf("lzc_channel_program returned %u\n", err);
98716fd348SMartin Matuska 	dump_nvlist(outnvl, 5);
99716fd348SMartin Matuska 
100716fd348SMartin Matuska 	if (err == 0 && expect_match) {
101716fd348SMartin Matuska 		/*
102716fd348SMartin Matuska 		 * Verify that outnvl is the same as input nvl, if we expect
103716fd348SMartin Matuska 		 * them to be. The input and output will never match if the
104716fd348SMartin Matuska 		 * input contains an array (since arrays are converted to lua
105716fd348SMartin Matuska 		 * tables), so this is only asserted for some test cases.
106716fd348SMartin Matuska 		 */
107716fd348SMartin Matuska 		nvlist_t *real_outnvl = fnvlist_lookup_nvlist(outnvl, "return");
108716fd348SMartin Matuska 		real_outnvl = fnvlist_lookup_nvlist(real_outnvl, "output");
109716fd348SMartin Matuska 		if (!nvlist_equal(nvl, real_outnvl)) {
110716fd348SMartin Matuska 			unexpected_failures = B_TRUE;
111716fd348SMartin Matuska 			(void) printf("unexpected input/output mismatch for "
112716fd348SMartin Matuska 			    "case: %s\n", testname);
113716fd348SMartin Matuska 		}
114716fd348SMartin Matuska 	}
115716fd348SMartin Matuska 	if (err != 0 && expect_success) {
116716fd348SMartin Matuska 		unexpected_failures = B_TRUE;
117716fd348SMartin Matuska 		(void) printf("unexpected FAIL of case: %s\n", testname);
118716fd348SMartin Matuska 	}
119716fd348SMartin Matuska 
120716fd348SMartin Matuska 	fnvlist_free(nvl);
121716fd348SMartin Matuska 	nvl = fnvlist_alloc();
122716fd348SMartin Matuska }
123716fd348SMartin Matuska 
124716fd348SMartin Matuska static void
run_tests(void)125716fd348SMartin Matuska run_tests(void)
126716fd348SMartin Matuska {
127716fd348SMartin Matuska 	const char *key = "key";
128716fd348SMartin Matuska 
129716fd348SMartin Matuska 	/* Note: maximum nvlist key length is 32KB */
130716fd348SMartin Matuska 	int len = 1024 * 31;
131716fd348SMartin Matuska 	char *bigstring = malloc(len);
132dbd5678dSMartin Matuska 	if (bigstring == NULL) {
133dbd5678dSMartin Matuska 		perror("malloc");
134dbd5678dSMartin Matuska 		exit(EXIT_FAILURE);
135dbd5678dSMartin Matuska 	}
136dbd5678dSMartin Matuska 
137716fd348SMartin Matuska 	for (int i = 0; i < len; i++)
138716fd348SMartin Matuska 		bigstring[i] = 'a' + i % 26;
139716fd348SMartin Matuska 	bigstring[len - 1] = '\0';
140716fd348SMartin Matuska 
141716fd348SMartin Matuska 	nvl = fnvlist_alloc();
142716fd348SMartin Matuska 
143716fd348SMartin Matuska 	fnvlist_add_boolean(nvl, key);
144716fd348SMartin Matuska 	test("boolean", B_TRUE, B_FALSE);
145716fd348SMartin Matuska 
146716fd348SMartin Matuska 	fnvlist_add_boolean_value(nvl, key, B_TRUE);
147716fd348SMartin Matuska 	test("boolean_value", B_FALSE, B_FALSE);
148716fd348SMartin Matuska 
149716fd348SMartin Matuska 	fnvlist_add_byte(nvl, key, 1);
150716fd348SMartin Matuska 	test("byte", B_FALSE, B_FALSE);
151716fd348SMartin Matuska 
152716fd348SMartin Matuska 	fnvlist_add_int8(nvl, key, 1);
153716fd348SMartin Matuska 	test("int8", B_FALSE, B_FALSE);
154716fd348SMartin Matuska 
155716fd348SMartin Matuska 	fnvlist_add_uint8(nvl, key, 1);
156716fd348SMartin Matuska 	test("uint8", B_FALSE, B_FALSE);
157716fd348SMartin Matuska 
158716fd348SMartin Matuska 	fnvlist_add_int16(nvl, key, 1);
159716fd348SMartin Matuska 	test("int16", B_FALSE, B_FALSE);
160716fd348SMartin Matuska 
161716fd348SMartin Matuska 	fnvlist_add_uint16(nvl, key, 1);
162716fd348SMartin Matuska 	test("uint16", B_FALSE, B_FALSE);
163716fd348SMartin Matuska 
164716fd348SMartin Matuska 	fnvlist_add_int32(nvl, key, 1);
165716fd348SMartin Matuska 	test("int32", B_FALSE, B_FALSE);
166716fd348SMartin Matuska 
167716fd348SMartin Matuska 	fnvlist_add_uint32(nvl, key, 1);
168716fd348SMartin Matuska 	test("uint32", B_FALSE, B_FALSE);
169716fd348SMartin Matuska 
170716fd348SMartin Matuska 	fnvlist_add_int64(nvl, key, 1);
171716fd348SMartin Matuska 	test("int64", B_TRUE, B_TRUE);
172716fd348SMartin Matuska 
173716fd348SMartin Matuska 	fnvlist_add_uint64(nvl, key, 1);
174716fd348SMartin Matuska 	test("uint64", B_FALSE, B_FALSE);
175716fd348SMartin Matuska 
176716fd348SMartin Matuska 	fnvlist_add_string(nvl, key, "1");
177716fd348SMartin Matuska 	test("string", B_TRUE, B_TRUE);
178716fd348SMartin Matuska 
179716fd348SMartin Matuska 
180716fd348SMartin Matuska 	{
181716fd348SMartin Matuska 		nvlist_t *val = fnvlist_alloc();
182716fd348SMartin Matuska 		fnvlist_add_string(val, "subkey", "subvalue");
183716fd348SMartin Matuska 		fnvlist_add_nvlist(nvl, key, val);
184716fd348SMartin Matuska 		fnvlist_free(val);
185716fd348SMartin Matuska 		test("nvlist", B_TRUE, B_TRUE);
186716fd348SMartin Matuska 	}
187716fd348SMartin Matuska 	{
188716fd348SMartin Matuska 		boolean_t val[2] = { B_FALSE, B_TRUE };
189716fd348SMartin Matuska 		fnvlist_add_boolean_array(nvl, key, val, 2);
190716fd348SMartin Matuska 		test("boolean_array", B_FALSE, B_FALSE);
191716fd348SMartin Matuska 	}
192716fd348SMartin Matuska 	{
193716fd348SMartin Matuska 		uchar_t val[2] = { 0, 1 };
194716fd348SMartin Matuska 		fnvlist_add_byte_array(nvl, key, val, 2);
195716fd348SMartin Matuska 		test("byte_array", B_FALSE, B_FALSE);
196716fd348SMartin Matuska 	}
197716fd348SMartin Matuska 	{
198716fd348SMartin Matuska 		int8_t val[2] = { 0, 1 };
199716fd348SMartin Matuska 		fnvlist_add_int8_array(nvl, key, val, 2);
200716fd348SMartin Matuska 		test("int8_array", B_FALSE, B_FALSE);
201716fd348SMartin Matuska 	}
202716fd348SMartin Matuska 	{
203716fd348SMartin Matuska 		uint8_t val[2] = { 0, 1 };
204716fd348SMartin Matuska 		fnvlist_add_uint8_array(nvl, key, val, 2);
205716fd348SMartin Matuska 		test("uint8_array", B_FALSE, B_FALSE);
206716fd348SMartin Matuska 	}
207716fd348SMartin Matuska 	{
208716fd348SMartin Matuska 		int16_t val[2] = { 0, 1 };
209716fd348SMartin Matuska 		fnvlist_add_int16_array(nvl, key, val, 2);
210716fd348SMartin Matuska 		test("int16_array", B_FALSE, B_FALSE);
211716fd348SMartin Matuska 	}
212716fd348SMartin Matuska 	{
213716fd348SMartin Matuska 		uint16_t val[2] = { 0, 1 };
214716fd348SMartin Matuska 		fnvlist_add_uint16_array(nvl, key, val, 2);
215716fd348SMartin Matuska 		test("uint16_array", B_FALSE, B_FALSE);
216716fd348SMartin Matuska 	}
217716fd348SMartin Matuska 	{
218716fd348SMartin Matuska 		int32_t val[2] = { 0, 1 };
219716fd348SMartin Matuska 		fnvlist_add_int32_array(nvl, key, val, 2);
220716fd348SMartin Matuska 		test("int32_array", B_FALSE, B_FALSE);
221716fd348SMartin Matuska 	}
222716fd348SMartin Matuska 	{
223716fd348SMartin Matuska 		uint32_t val[2] = { 0, 1 };
224716fd348SMartin Matuska 		fnvlist_add_uint32_array(nvl, key, val, 2);
225716fd348SMartin Matuska 		test("uint32_array", B_FALSE, B_FALSE);
226716fd348SMartin Matuska 	}
227716fd348SMartin Matuska 	{
228716fd348SMartin Matuska 		int64_t val[2] = { 0, 1 };
229716fd348SMartin Matuska 		fnvlist_add_int64_array(nvl, key, val, 2);
230716fd348SMartin Matuska 		test("int64_array", B_TRUE, B_FALSE);
231716fd348SMartin Matuska 	}
232716fd348SMartin Matuska 	{
233716fd348SMartin Matuska 		uint64_t val[2] = { 0, 1 };
234716fd348SMartin Matuska 		fnvlist_add_uint64_array(nvl, key, val, 2);
235716fd348SMartin Matuska 		test("uint64_array", B_FALSE, B_FALSE);
236716fd348SMartin Matuska 	}
237716fd348SMartin Matuska 	{
238a0b956f5SMartin Matuska 		const char *val[2] = { "0", "1" };
239a0b956f5SMartin Matuska 		fnvlist_add_string_array(nvl, key, val, 2);
240716fd348SMartin Matuska 		test("string_array", B_TRUE, B_FALSE);
241716fd348SMartin Matuska 	}
242716fd348SMartin Matuska 	{
243716fd348SMartin Matuska 		nvlist_t *val[2];
244716fd348SMartin Matuska 		val[0] = fnvlist_alloc();
245716fd348SMartin Matuska 		fnvlist_add_string(val[0], "subkey", "subvalue");
246716fd348SMartin Matuska 		val[1] = fnvlist_alloc();
247716fd348SMartin Matuska 		fnvlist_add_string(val[1], "subkey2", "subvalue2");
248716fd348SMartin Matuska 		fnvlist_add_nvlist_array(nvl, key, (const nvlist_t **)val, 2);
249716fd348SMartin Matuska 		fnvlist_free(val[0]);
250716fd348SMartin Matuska 		fnvlist_free(val[1]);
251716fd348SMartin Matuska 		test("nvlist_array", B_FALSE, B_FALSE);
252716fd348SMartin Matuska 	}
253716fd348SMartin Matuska 	{
254716fd348SMartin Matuska 		fnvlist_add_string(nvl, bigstring, "1");
255716fd348SMartin Matuska 		test("large_key", B_TRUE, B_TRUE);
256716fd348SMartin Matuska 	}
257716fd348SMartin Matuska 	{
258716fd348SMartin Matuska 		fnvlist_add_string(nvl, key, bigstring);
259716fd348SMartin Matuska 		test("large_value", B_TRUE, B_TRUE);
260716fd348SMartin Matuska 	}
261716fd348SMartin Matuska 	{
262716fd348SMartin Matuska 		for (int i = 0; i < 1024; i++) {
263716fd348SMartin Matuska 			char buf[32];
264716fd348SMartin Matuska 			(void) snprintf(buf, sizeof (buf), "key-%u", i);
265716fd348SMartin Matuska 			fnvlist_add_int64(nvl, buf, i);
266716fd348SMartin Matuska 		}
267716fd348SMartin Matuska 		test("many_keys", B_TRUE, B_TRUE);
268716fd348SMartin Matuska 	}
269716fd348SMartin Matuska #ifndef __sparc__
270716fd348SMartin Matuska 	{
271716fd348SMartin Matuska 		for (int i = 0; i < 10; i++) {
272716fd348SMartin Matuska 			nvlist_t *newval = fnvlist_alloc();
273716fd348SMartin Matuska 			fnvlist_add_nvlist(newval, "key", nvl);
274716fd348SMartin Matuska 			fnvlist_free(nvl);
275716fd348SMartin Matuska 			nvl = newval;
276716fd348SMartin Matuska 		}
277716fd348SMartin Matuska 		test("deeply_nested_pos", B_TRUE, B_TRUE);
278716fd348SMartin Matuska 	}
279716fd348SMartin Matuska 	{
280716fd348SMartin Matuska 		for (int i = 0; i < 90; i++) {
281716fd348SMartin Matuska 			nvlist_t *newval = fnvlist_alloc();
282716fd348SMartin Matuska 			fnvlist_add_nvlist(newval, "key", nvl);
283716fd348SMartin Matuska 			fnvlist_free(nvl);
284716fd348SMartin Matuska 			nvl = newval;
285716fd348SMartin Matuska 		}
286716fd348SMartin Matuska 		test("deeply_nested_neg", B_FALSE, B_FALSE);
287716fd348SMartin Matuska 	}
288716fd348SMartin Matuska #endif
289716fd348SMartin Matuska 	free(bigstring);
290716fd348SMartin Matuska 	fnvlist_free(nvl);
291716fd348SMartin Matuska }
292716fd348SMartin Matuska 
293716fd348SMartin Matuska int
main(int argc,const char * argv[])294716fd348SMartin Matuska main(int argc, const char *argv[])
295716fd348SMartin Matuska {
296716fd348SMartin Matuska 	(void) libzfs_core_init();
297716fd348SMartin Matuska 
298716fd348SMartin Matuska 	if (argc != 2) {
299716fd348SMartin Matuska 		(void) printf("usage: %s <pool>\n",
300716fd348SMartin Matuska 		    argv[0]);
301716fd348SMartin Matuska 		exit(2);
302716fd348SMartin Matuska 	}
303716fd348SMartin Matuska 	pool = argv[1];
304716fd348SMartin Matuska 
305716fd348SMartin Matuska 	run_tests();
306716fd348SMartin Matuska 
307716fd348SMartin Matuska 	libzfs_core_fini();
308716fd348SMartin Matuska 	return (unexpected_failures);
309716fd348SMartin Matuska }
310