diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2012-02-22 14:59:32 +1030 |
---|---|---|
committer | Amitay Isaacs <amitay@gmail.com> | 2012-03-07 13:16:16 +1100 |
commit | 361f3ea9ee577c5a3e2fed687a0b417b257c31de (patch) | |
tree | 6d356c3aa64317c609ff4e208be76e18996a55f8 /lib | |
parent | 4f5412dda687c3ff76b426842bf284d01d56a997 (diff) | |
download | samba-361f3ea9ee577c5a3e2fed687a0b417b257c31de.tar.gz |
lib/ccan: import failtest and required ccan modules for TDB2 unit tests.
New modules: failtest, list, time, read_write_all and tlist.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'lib')
69 files changed, 7608 insertions, 0 deletions
diff --git a/lib/ccan/check_type/_info b/lib/ccan/check_type/_info new file mode 100644 index 00000000000..33448b4f1f6 --- /dev/null +++ b/lib/ccan/check_type/_info @@ -0,0 +1,33 @@ +#include <stdio.h> +#include <string.h> +#include "config.h" + +/** + * check_type - routines for compile time type checking + * + * C has fairly weak typing: ints get automatically converted to longs, signed + * to unsigned, etc. There are some cases where this is best avoided, and + * these macros provide methods for evoking warnings (or build errors) when + * a precise type isn't used. + * + * On compilers which don't support typeof() these routines are less effective, + * since they have to use sizeof() which can only distiguish between types of + * different size. + * + * License: Public domain + * Author: Rusty Russell <rusty@rustcorp.com.au> + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { +#if !HAVE_TYPEOF + printf("ccan/build_assert\n"); +#endif + return 0; + } + + return 1; +} diff --git a/lib/ccan/check_type/check_type.h b/lib/ccan/check_type/check_type.h new file mode 100644 index 00000000000..b199347116b --- /dev/null +++ b/lib/ccan/check_type/check_type.h @@ -0,0 +1,63 @@ +#ifndef CCAN_CHECK_TYPE_H +#define CCAN_CHECK_TYPE_H +#include "config.h" + +/** + * check_type - issue a warning or build failure if type is not correct. + * @expr: the expression whose type we should check (not evaluated). + * @type: the exact type we expect the expression to be. + * + * This macro is usually used within other macros to try to ensure that a macro + * argument is of the expected type. No type promotion of the expression is + * done: an unsigned int is not the same as an int! + * + * check_type() always evaluates to 0. + * + * If your compiler does not support typeof, then the best we can do is fail + * to compile if the sizes of the types are unequal (a less complete check). + * + * Example: + * // They should always pass a 64-bit value to _set_some_value! + * #define set_some_value(expr) \ + * _set_some_value((check_type((expr), uint64_t), (expr))) + */ + +/** + * check_types_match - issue a warning or build failure if types are not same. + * @expr1: the first expression (not evaluated). + * @expr2: the second expression (not evaluated). + * + * This macro is usually used within other macros to try to ensure that + * arguments are of identical types. No type promotion of the expressions is + * done: an unsigned int is not the same as an int! + * + * check_types_match() always evaluates to 0. + * + * If your compiler does not support typeof, then the best we can do is fail + * to compile if the sizes of the types are unequal (a less complete check). + * + * Example: + * // Do subtraction to get to enclosing type, but make sure that + * // pointer is of correct type for that member. + * #define container_of(mbr_ptr, encl_type, mbr) \ + * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ + * ((encl_type *) \ + * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) + */ +#if HAVE_TYPEOF +#define check_type(expr, type) \ + ((typeof(expr) *)0 != (type *)0) + +#define check_types_match(expr1, expr2) \ + ((typeof(expr1) *)0 != (typeof(expr2) *)0) +#else +#include <ccan/build_assert/build_assert.h> +/* Without typeof, we can only test the sizes. */ +#define check_type(expr, type) \ + BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type)) + +#define check_types_match(expr1, expr2) \ + BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2)) +#endif /* HAVE_TYPEOF */ + +#endif /* CCAN_CHECK_TYPE_H */ diff --git a/lib/ccan/check_type/test/compile_fail-check_type.c b/lib/ccan/check_type/test/compile_fail-check_type.c new file mode 100644 index 00000000000..fe7d6a235c3 --- /dev/null +++ b/lib/ccan/check_type/test/compile_fail-check_type.c @@ -0,0 +1,9 @@ +#include <ccan/check_type/check_type.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL + check_type(argc, char); +#endif + return 0; +} diff --git a/lib/ccan/check_type/test/compile_fail-check_type_unsigned.c b/lib/ccan/check_type/test/compile_fail-check_type_unsigned.c new file mode 100644 index 00000000000..574d4aeb240 --- /dev/null +++ b/lib/ccan/check_type/test/compile_fail-check_type_unsigned.c @@ -0,0 +1,14 @@ +#include <ccan/check_type/check_type.h> + +int main(int argc, char *argv[]) +{ +#ifdef FAIL +#if HAVE_TYPEOF + check_type(argc, unsigned int); +#else + /* This doesn't work without typeof, so just fail */ +#error "Fail without typeof" +#endif +#endif + return 0; +} diff --git a/lib/ccan/check_type/test/compile_fail-check_types_match.c b/lib/ccan/check_type/test/compile_fail-check_types_match.c new file mode 100644 index 00000000000..cbd6e9bc5b4 --- /dev/null +++ b/lib/ccan/check_type/test/compile_fail-check_types_match.c @@ -0,0 +1,10 @@ +#include <ccan/check_type/check_type.h> + +int main(int argc, char *argv[]) +{ + unsigned char x = argc; +#ifdef FAIL + check_types_match(argc, x); +#endif + return x; +} diff --git a/lib/ccan/check_type/test/run.c b/lib/ccan/check_type/test/run.c new file mode 100644 index 00000000000..83b903c0b1b --- /dev/null +++ b/lib/ccan/check_type/test/run.c @@ -0,0 +1,22 @@ +#include <ccan/check_type/check_type.h> +#include <ccan/tap/tap.h> + +int main(int argc, char *argv[]) +{ + int x = 0, y = 0; + + plan_tests(9); + + ok1(check_type(argc, int) == 0); + ok1(check_type(&argc, int *) == 0); + ok1(check_types_match(argc, argc) == 0); + ok1(check_types_match(argc, x) == 0); + ok1(check_types_match(&argc, &x) == 0); + + ok1(check_type(x++, int) == 0); + ok(x == 0, "check_type does not evaluate expression"); + ok1(check_types_match(x++, y++) == 0); + ok(x == 0 && y == 0, "check_types_match does not evaluate expressions"); + + return exit_status(); +} diff --git a/lib/ccan/container_of/_info b/lib/ccan/container_of/_info new file mode 100644 index 00000000000..2f45ca7ccd7 --- /dev/null +++ b/lib/ccan/container_of/_info @@ -0,0 +1,63 @@ +#include <stdio.h> +#include <string.h> +#include "config.h" + +/** + * container_of - routine for upcasting + * + * It is often convenient to create code where the caller registers a pointer + * to a generic structure and a callback. The callback might know that the + * pointer points to within a larger structure, and container_of gives a + * convenient and fairly type-safe way of returning to the enclosing structure. + * + * This idiom is an alternative to providing a void * pointer for every + * callback. + * + * Example: + * #include <stdio.h> + * #include <ccan/container_of/container_of.h> + * + * struct timer { + * void *members; + * }; + * + * struct info { + * int my_stuff; + * struct timer timer; + * }; + * + * static void register_timer(struct timer *timer) + * { + * //... + * } + * + * static void my_timer_callback(struct timer *timer) + * { + * struct info *info = container_of(timer, struct info, timer); + * printf("my_stuff is %u\n", info->my_stuff); + * } + * + * int main(void) + * { + * struct info info = { .my_stuff = 1 }; + * + * register_timer(&info.timer); + * // ... + * return 0; + * } + * + * License: Public domain + * Author: Rusty Russell <rusty@rustcorp.com.au> + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/check_type\n"); + return 0; + } + + return 1; +} diff --git a/lib/ccan/container_of/container_of.h b/lib/ccan/container_of/container_of.h new file mode 100644 index 00000000000..1c9d147ae06 --- /dev/null +++ b/lib/ccan/container_of/container_of.h @@ -0,0 +1,108 @@ +#ifndef CCAN_CONTAINER_OF_H +#define CCAN_CONTAINER_OF_H +#include <stddef.h> + +#include "config.h" +#include <ccan/check_type/check_type.h> + +/** + * container_of - get pointer to enclosing structure + * @member_ptr: pointer to the structure member + * @containing_type: the type this member is within + * @member: the name of this member within the structure. + * + * Given a pointer to a member of a structure, this macro does pointer + * subtraction to return the pointer to the enclosing type. + * + * Example: + * struct foo { + * int fielda, fieldb; + * // ... + * }; + * struct info { + * int some_other_field; + * struct foo my_foo; + * }; + * + * static struct info *foo_to_info(struct foo *foo) + * { + * return container_of(foo, struct info, my_foo); + * } + */ +#define container_of(member_ptr, containing_type, member) \ + ((containing_type *) \ + ((char *)(member_ptr) \ + - container_off(containing_type, member)) \ + + check_types_match(*(member_ptr), ((containing_type *)0)->member)) + +/** + * container_off - get offset to enclosing structure + * @containing_type: the type this member is within + * @member: the name of this member within the structure. + * + * Given a pointer to a member of a structure, this macro does + * typechecking and figures out the offset to the enclosing type. + * + * Example: + * struct foo { + * int fielda, fieldb; + * // ... + * }; + * struct info { + * int some_other_field; + * struct foo my_foo; + * }; + * + * static struct info *foo_to_info(struct foo *foo) + * { + * size_t off = container_off(struct info, my_foo); + * return (void *)((char *)foo - off); + * } + */ +#define container_off(containing_type, member) \ + offsetof(containing_type, member) + +/** + * container_of_var - get pointer to enclosing structure using a variable + * @member_ptr: pointer to the structure member + * @container_var: a pointer of same type as this member's container + * @member: the name of this member within the structure. + * + * Given a pointer to a member of a structure, this macro does pointer + * subtraction to return the pointer to the enclosing type. + * + * Example: + * static struct info *foo_to_i(struct foo *foo) + * { + * struct info *i = container_of_var(foo, i, my_foo); + * return i; + * } + */ +#if HAVE_TYPEOF +#define container_of_var(member_ptr, container_var, member) \ + container_of(member_ptr, typeof(*container_var), member) +#else +#define container_of_var(member_ptr, container_var, member) \ + ((void *)((char *)(member_ptr) - \ + container_off_var(container_var, member))) +#endif + +/** + * container_off_var - get offset of a field in enclosing structure + * @container_var: a pointer to a container structure + * @member: the name of a member within the structure. + * + * Given (any) pointer to a structure and a its member name, this + * macro does pointer subtraction to return offset of member in a + * structure memory layout. + * + */ +#if HAVE_TYPEOF +#define container_off_var(var, member) \ + container_off(typeof(*var), member) +#else +#define container_off_var(var, member) \ + ((char *)&(var)->member - (char *)(var)) +#endif + +#endif /* CCAN_CONTAINER_OF_H */ diff --git a/lib/ccan/container_of/test/compile_fail-bad-type.c b/lib/ccan/container_of/test/compile_fail-bad-type.c new file mode 100644 index 00000000000..b7a1459386b --- /dev/null +++ b/lib/ccan/container_of/test/compile_fail-bad-type.c @@ -0,0 +1,22 @@ +#include <ccan/container_of/container_of.h> +#include <stdlib.h> + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }; + int *intp = &foo.a; + char *p; + +#ifdef FAIL + /* p is a char *, but this gives a struct foo * */ + p = container_of(intp, struct foo, a); +#else + p = (char *)intp; +#endif + return p == NULL; +} diff --git a/lib/ccan/container_of/test/compile_fail-types.c b/lib/ccan/container_of/test/compile_fail-types.c new file mode 100644 index 00000000000..cae1c7abd20 --- /dev/null +++ b/lib/ccan/container_of/test/compile_fail-types.c @@ -0,0 +1,22 @@ +#include <ccan/container_of/container_of.h> +#include <stdlib.h> + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }, *foop; + int *intp = &foo.a; + +#ifdef FAIL + /* b is a char, but intp is an int * */ + foop = container_of(intp, struct foo, b); +#else + foop = NULL; +#endif + (void) foop; /* Suppress unused-but-set-variable warning. */ + return intp == NULL; +} diff --git a/lib/ccan/container_of/test/compile_fail-var-types.c b/lib/ccan/container_of/test/compile_fail-var-types.c new file mode 100644 index 00000000000..f254d921020 --- /dev/null +++ b/lib/ccan/container_of/test/compile_fail-var-types.c @@ -0,0 +1,25 @@ +#include <ccan/container_of/container_of.h> +#include <stdlib.h> + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }, *foop; + int *intp = &foo.a; + +#ifdef FAIL + /* b is a char, but intp is an int * */ + foop = container_of_var(intp, foop, b); +#if !HAVE_TYPEOF +#error "Unfortunately we don't fail if we don't have typeof." +#endif +#else + foop = NULL; +#endif + (void) foop; /* Suppress unused-but-set-variable warning. */ + return intp == NULL; +} diff --git a/lib/ccan/container_of/test/run.c b/lib/ccan/container_of/test/run.c new file mode 100644 index 00000000000..5da440a1e5d --- /dev/null +++ b/lib/ccan/container_of/test/run.c @@ -0,0 +1,26 @@ +#include <ccan/container_of/container_of.h> +#include <ccan/tap/tap.h> + +struct foo { + int a; + char b; +}; + +int main(int argc, char *argv[]) +{ + struct foo foo = { .a = 1, .b = 2 }; + int *intp = &foo.a; + char *charp = &foo.b; + + plan_tests(8); + ok1(container_of(intp, struct foo, a) == &foo); + ok1(container_of(charp, struct foo, b) == &foo); + ok1(container_of_var(intp, &foo, a) == &foo); + ok1(container_of_var(charp, &foo, b) == &foo); + + ok1(container_off(struct foo, a) == 0); + ok1(container_off(struct foo, b) == offsetof(struct foo, b)); + ok1(container_off_var(&foo, a) == 0); + ok1(container_off_var(&foo, b) == offsetof(struct foo, b)); + return exit_status(); +} diff --git a/lib/ccan/failtest/LICENSE b/lib/ccan/failtest/LICENSE new file mode 100644 index 00000000000..cca7fc278f5 --- /dev/null +++ b/lib/ccan/failtest/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/lib/ccan/failtest/_info b/lib/ccan/failtest/_info new file mode 100644 index 00000000000..14dcb783be0 --- /dev/null +++ b/lib/ccan/failtest/_info @@ -0,0 +1,76 @@ +#include <stdio.h> +#include <string.h> +#include "config.h" + +/** + * failtest - unit test helpers for testing malloc and other failures. + * + * The failtest module overrides various standard functions, and forks + * your unit test at those points to test failure paths. The failing + * child are expected to fail (eg. when malloc fails), but should not + * leak memory or crash. After including failtest_override.h, you can + * include failtest_restore.h to return to non-failing versions. + * + * The unit test is a normal CCAN tap-style test, except it should + * start by calling failtest_init() and end by calling + * failtest_exit(). + * + * You can control what functions fail: see failtest_hook. + * + * Example: + * #include <stdio.h> + * #include <stdlib.h> + * #include <string.h> + * #include <ccan/tap/tap.h> + * #include <ccan/failtest/failtest_override.h> + * #include <ccan/failtest/failtest.h> + * + * int main(int argc, char *argv[]) + * { + * char *a, *b; + * + * failtest_init(argc, argv); + * plan_tests(3); + * + * // Simple malloc test. + * a = malloc(100); + * if (ok1(a)) { + * // Fill the memory. + * memset(a, 'x', 100); + * b = realloc(a, 200); + * if (ok1(b)) { + * // Fill the rest of the memory. + * memset(b + 100, 'y', 100); + * // Check it got a copy of a as expected. + * ok1(strspn(b, "x") == 100); + * free(b); + * } else { + * // Easy to miss: free a on realloc failure! + * free(a); + * } + * } + * failtest_exit(exit_status()); + * } + * + * License: LGPL + * Author: Rusty Russell <rusty@rustcorp.com.au> + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/build_assert\n"); + printf("ccan/compiler\n"); + printf("ccan/hash\n"); + printf("ccan/htable\n"); + printf("ccan/read_write_all\n"); + printf("ccan/str\n"); + printf("ccan/time\n"); + printf("ccan/tlist\n"); + return 0; + } + + return 1; +} diff --git a/lib/ccan/failtest/failtest.c b/lib/ccan/failtest/failtest.c new file mode 100644 index 00000000000..701586e9b88 --- /dev/null +++ b/lib/ccan/failtest/failtest.c @@ -0,0 +1,1713 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#include <ccan/failtest/failtest.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> +#include <err.h> +#include <unistd.h> +#include <poll.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <signal.h> +#include <assert.h> +#include <ccan/time/time.h> +#include <ccan/read_write_all/read_write_all.h> +#include <ccan/failtest/failtest_proto.h> +#include <ccan/build_assert/build_assert.h> +#include <ccan/hash/hash.h> +#include <ccan/htable/htable_type.h> +#include <ccan/str/str.h> +#include <ccan/compiler/compiler.h> + +enum failtest_result (*failtest_hook)(struct tlist_calls *); + +static FILE *tracef = NULL, *warnf; +static int traceindent = 0; + +unsigned int failtest_timeout_ms = 20000; + +const char *failpath; +const char *debugpath; + +enum info_type { + WRITE, + RELEASE_LOCKS, + FAILURE, + SUCCESS, + UNEXPECTED +}; + +struct lock_info { + int fd; + /* end is inclusive: you can't have a 0-byte lock. */ + off_t start, end; + int type; +}; + +/* We hash the call location together with its backtrace. */ +static size_t hash_call(const struct failtest_call *call) +{ + return hash(call->file, strlen(call->file), + hash(&call->line, 1, + hash(call->backtrace, call->backtrace_num, + call->type))); +} + +static bool call_eq(const struct failtest_call *call1, + const struct failtest_call *call2) +{ + unsigned int i; + + if (strcmp(call1->file, call2->file) != 0 + || call1->line != call2->line + || call1->type != call2->type + || call1->backtrace_num != call2->backtrace_num) + return false; + + for (i = 0; i < call1->backtrace_num; i++) + if (call1->backtrace[i] != call2->backtrace[i]) + return false; + + return true; +} + +/* Defines struct failtable. */ +HTABLE_DEFINE_TYPE(struct failtest_call, (struct failtest_call *), hash_call, + call_eq, failtable); + +bool (*failtest_exit_check)(struct tlist_calls *history); + +/* The entire history of all calls. */ +static struct tlist_calls history = TLIST_INIT(history); +/* If we're a child, the fd two write control info to the parent. */ +static int control_fd = -1; +/* If we're a child, this is the first call we did ourselves. */ +static struct failtest_call *our_history_start = NULL; +/* For printing runtime with --trace. */ +static struct timeval start; +/* Set when failtest_hook returns FAIL_PROBE */ +static bool probing = false; +/* Table to track duplicates. */ +static struct failtable failtable; + +/* Array of writes which our child did. We report them on failure. */ +static struct write_call *child_writes = NULL; +static unsigned int child_writes_num = 0; + +/* fcntl locking info. */ +static pid_t lock_owner; +static struct lock_info *locks = NULL; +static unsigned int lock_num = 0; + +/* Our original pid, which we return to anyone who asks. */ +static pid_t orig_pid; + +/* Mapping from failtest_type to char. */ +static const char info_to_arg[] = "mceoxprwfal"; + +/* Dummy call used for failtest_undo wrappers. */ +static struct failtest_call unrecorded_call; + +struct contents_saved { + size_t count; + off_t off; + off_t old_len; + char contents[1]; +}; + +/* File contents, saved in this child only. */ +struct saved_mmapped_file { + struct saved_mmapped_file *next; + struct failtest_call *opener; + struct contents_saved *s; +}; + +static struct saved_mmapped_file *saved_mmapped_files; + +#if HAVE_BACKTRACE +#include <execinfo.h> + +static void **get_backtrace(unsigned int *num) +{ + static unsigned int max_back = 100; + void **ret; + +again: + ret = malloc(max_back * sizeof(void *)); + *num = backtrace(ret, max_back); + if (*num == max_back) { + free(ret); + max_back *= 2; + goto again; + } + return ret; +} +#else +/* This will test slightly less, since will consider all of the same + * calls as identical. But, it's slightly faster! */ +static void **get_backtrace(unsigned int *num) +{ + *num = 0; + return NULL; +} +#endif /* HAVE_BACKTRACE */ + +static struct failtest_call *add_history_(enum failtest_call_type type, + bool can_leak, + const char *file, + unsigned int line, + const void *elem, + size_t elem_size) +{ + struct failtest_call *call; + + /* NULL file is how we suppress failure. */ + if (!file) + return &unrecorded_call; + + call = malloc(sizeof *call); + call->type = type; + call->can_leak = can_leak; + call->file = file; + call->line = line; + call->cleanup = NULL; + call->backtrace = get_backtrace(&call->backtrace_num); + memcpy(&call->u, elem, elem_size); + tlist_add_tail(&history, call, list); + return call; +} + +#define add_history(type, can_leak, file, line, elem) \ + add_history_((type), (can_leak), (file), (line), (elem), sizeof(*(elem))) + +/* We do a fake call inside a sizeof(), to check types. */ +#define set_cleanup(call, clean, type) \ + (call)->cleanup = (void *)((void)sizeof(clean((type *)NULL, false),1), (clean)) + +/* Dup the fd to a high value (out of the way I hope!), and close the old fd. */ +static int move_fd_to_high(int fd) +{ + int i; + + for (i = FD_SETSIZE - 1; i >= 0; i--) { + if (fcntl(i, F_GETFL) == -1 && errno == EBADF) { + if (dup2(fd, i) == -1) + err(1, "Failed to dup fd %i to %i", fd, i); + close(fd); + return i; + } + } + /* Nothing? Really? Er... ok? */ + return fd; +} + +static bool read_write_info(int fd) +{ + struct write_call *w; + char *buf; + + /* We don't need all of this, but it's simple. */ + child_writes = realloc(child_writes, + (child_writes_num+1) * sizeof(child_writes[0])); + w = &child_writes[child_writes_num]; + if (!read_all(fd, w, sizeof(*w))) + return false; + + w->buf = buf = malloc(w->count); + if (!read_all(fd, buf, w->count)) + return false; + + child_writes_num++; + return true; +} + +static char *failpath_string(void) +{ + struct failtest_call *i; + char *ret = strdup(""); + unsigned len = 0; + + /* Inefficient, but who cares? */ + tlist_for_each(&history, i, list) { + ret = realloc(ret, len + 2); + ret[len] = info_to_arg[i->type]; + if (i->fail) + ret[len] = toupper(ret[len]); + ret[++len] = '\0'; + } + return ret; +} + +static void do_warn(int e, const char *fmt, va_list ap) +{ + char *p = failpath_string(); + + vfprintf(warnf, fmt, ap); + if (e != -1) + fprintf(warnf, ": %s", strerror(e)); + fprintf(warnf, " [%s]\n", p); + free(p); +} + +static void fwarn(const char *fmt, ...) +{ + va_list ap; + int e = errno; + + va_start(ap, fmt); + do_warn(e, fmt, ap); + va_end(ap); +} + + +static void fwarnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + do_warn(-1, fmt, ap); + va_end(ap); +} + +static void tell_parent(enum info_type type) +{ + if (control_fd != -1) + write_all(control_fd, &type, sizeof(type)); +} + +static void child_fail(const char *out, size_t outlen, const char *fmt, ...) +{ + va_list ap; + char *path = failpath_string(); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "%.*s", (int)outlen, out); + printf("To reproduce: --failpath=%s\n", path); + free(path); + tell_parent(FAILURE); + exit(1); +} + +static void PRINTF_FMT(1, 2) trace(const char *fmt, ...) +{ + va_list ap; + unsigned int i; + char *p; + static int idx; + + if (!tracef) + return; + + for (i = 0; i < traceindent; i++) + fprintf(tracef, " "); + + p = failpath_string(); + fprintf(tracef, "%i: %u: %s ", idx++, getpid(), p); + va_start(ap, fmt); + vfprintf(tracef, fmt, ap); + va_end(ap); + free(p); +} + +static pid_t child; + +static void hand_down(int signum) +{ + kill(child, signum); +} + +static void release_locks(void) +{ + /* Locks were never acquired/reacquired? */ + if (lock_owner == 0) + return; + + /* We own them? Release them all. */ + if (lock_owner == getpid()) { + unsigned int i; + struct flock fl; + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + trace("Releasing %u locks\n", lock_num); + for (i = 0; i < lock_num; i++) + fcntl(locks[i].fd, F_SETLK, &fl); + } else { + /* Our parent must have them; pass request up. */ + enum info_type type = RELEASE_LOCKS; + assert(control_fd != -1); + write_all(control_fd, &type, sizeof(type)); + } + lock_owner = 0; +} + +/* off_t is a signed type. Getting its max is non-trivial. */ +static off_t off_max(void) +{ + BUILD_ASSERT(sizeof(off_t) == 4 || sizeof(off_t) == 8); + if (sizeof(off_t) == 4) + return (off_t)0x7FFFFFF; + else + return (off_t)0x7FFFFFFFFFFFFFFULL; +} + +static void get_locks(void) +{ + unsigned int i; + struct flock fl; + + if (lock_owner == getpid()) + return; + + if (lock_owner != 0) { + enum info_type type = RELEASE_LOCKS; + assert(control_fd != -1); + trace("Asking parent to release locks\n"); + write_all(control_fd, &type, sizeof(type)); + } + + fl.l_whence = SEEK_SET; + + for (i = 0; i < lock_num; i++) { + fl.l_type = locks[i].type; + fl.l_start = locks[i].start; + if (locks[i].end == off_max()) + fl.l_len = 0; + else + fl.l_len = locks[i].end - locks[i].start + 1; + + if (fcntl(locks[i].fd, F_SETLKW, &fl) != 0) + abort(); + } + trace("Acquired %u locks\n", lock_num); + lock_owner = getpid(); +} + + +static struct contents_saved *save_contents(const char *filename, + int fd, size_t count, off_t off, + const char *why) +{ + struct contents_saved *s = malloc(sizeof(*s) + count); + ssize_t ret; + + s->off = off; + + ret = pread(fd, s->contents, count, off); + if (ret < 0) { + fwarn("failtest_write: failed to save old contents!"); + s->count = 0; + } else + s->count = ret; + + /* Use lseek to get the size of file, but we have to restore + * file offset */ + off = lseek(fd, 0, SEEK_CUR); + s->old_len = lseek(fd, 0, SEEK_END); + lseek(fd, off, SEEK_SET); + + trace("Saving %p %s %zu@%llu after %s (filelength %llu) via fd %i\n", + s, filename, s->count, (long long)s->off, why, + (long long)s->old_len, fd); + return s; +} + +static void restore_contents(struct failtest_call *opener, + struct contents_saved *s, + bool restore_offset, + const char *caller) +{ + int fd; + + /* The top parent doesn't need to restore. */ + if (control_fd == -1) + return; + + /* Has the fd been closed? */ + if (opener->u.open.closed) { + /* Reopen, replace fd, close silently as we clean up. */ + fd = open(opener->u.open.pathname, O_RDWR); + if (fd < 0) { + fwarn("failtest: could not reopen %s to clean up %s!", + opener->u.open.pathname, caller); + return; + } + /* Make it clearly distinguisable from a "normal" fd. */ + fd = move_fd_to_high(fd); + trace("Reopening %s to restore it (was fd %i, now %i)\n", + opener->u.open.pathname, opener->u.open.ret, fd); + opener->u.open.ret = fd; + opener->u.open.closed = false; + } + fd = opener->u.open.ret; + + trace("Restoring %p %s %zu@%llu after %s (filelength %llu) via fd %i\n", + s, opener->u.open.pathname, s->count, (long long)s->off, caller, + (long long)s->old_len, fd); + if (pwrite(fd, s->contents, s->count, s->off) != s->count) { + fwarn("failtest: write failed cleaning up %s for %s!", + opener->u.open.pathname, caller); + } + + if (ftruncate(fd, s->old_len) != 0) { + fwarn("failtest_write: truncate failed cleaning up %s for %s!", + opener->u.open.pathname, caller); + } + + if (restore_offset) { + trace("Restoring offset of fd %i to %llu\n", + fd, (long long)s->off); + lseek(fd, s->off, SEEK_SET); + } +} + +/* We save/restore most things on demand, but always do mmaped files. */ +static void save_mmapped_files(void) +{ + struct failtest_call *i; + trace("Saving mmapped files in child\n"); + + tlist_for_each_rev(&history, i, list) { + struct mmap_call *m = &i->u.mmap; + struct saved_mmapped_file *s; + + if (i->type != FAILTEST_MMAP) + continue; + + /* FIXME: We only handle mmapped files where fd is still open. */ + if (m->opener->u.open.closed) + continue; + + s = malloc(sizeof *s); + s->s = save_contents(m->opener->u.open.pathname, + m->fd, m->length, m->offset, + "mmapped file before fork"); + s->opener = m->opener; + s->next = saved_mmapped_files; + saved_mmapped_files = s; + } +} + +static void free_mmapped_files(bool restore) +{ + trace("%s mmapped files in child\n", + restore ? "Restoring" : "Discarding"); + while (saved_mmapped_files) { + struct saved_mmapped_file *next = saved_mmapped_files->next; + if (restore) + restore_contents(saved_mmapped_files->opener, + saved_mmapped_files->s, false, + "saved mmap"); + free(saved_mmapped_files->s); + free(saved_mmapped_files); + saved_mmapped_files = next; + } +} + +/* Returns a FAILTEST_OPEN, FAILTEST_PIPE or NULL. */ +static struct failtest_call *opener_of(int fd) +{ + struct failtest_call *i; + + /* Don't get confused and match genuinely failed opens. */ + if (fd < 0) + return NULL; + + /* Figure out the set of live fds. */ + tlist_for_each_rev(&history, i, list) { + if (i->fail) + continue; + switch (i->type) { + case FAILTEST_CLOSE: + if (i->u.close.fd == fd) { + return NULL; + } + break; + case FAILTEST_OPEN: + if (i->u.open.ret == fd) { + if (i->u.open.closed) + return NULL; + return i; + } + break; + case FAILTEST_PIPE: + if (i->u.pipe.fds[0] == fd || i->u.pipe.fds[1] == fd) { + return i; + } + break; + default: + break; + } + } + + /* FIXME: socket, dup, etc are untracked! */ + return NULL; +} + +static void free_call(struct failtest_call *call) +{ + /* We don't do this in cleanup: needed even for failed opens. */ + if (call->type == FAILTEST_OPEN) + free((char *)call->u.open.pathname); + free(call->backtrace); + tlist_del_from(&history, call, list); + free(call); +} + +/* Free up memory, so valgrind doesn't report leaks. */ +static void free_everything(void) +{ + struct failtest_call *i; + + while ((i = tlist_top(&history, list)) != NULL) + free_call(i); + + failtable_clear(&failtable); +} + +static NORETURN void failtest_cleanup(bool forced_cleanup, int status) +{ + struct failtest_call *i; + bool restore = true; + + /* For children, we don't care if they "failed" the testing. */ + if (control_fd != -1) + status = 0; + else + /* We don't restore contents for original parent. */ + restore = false; + + /* Cleanup everything, in reverse order. */ + tlist_for_each_rev(&history, i, list) { + /* Don't restore things our parent did. */ + if (i == our_history_start) + restore = false; + + if (i->fail) + continue; + + if (i->cleanup) + i->cleanup(&i->u, restore); + + /* But their program shouldn't leak, even on failure. */ + if (!forced_cleanup && i->can_leak) { + printf("Leak at %s:%u: --failpath=%s\n", + i->file, i->line, failpath_string()); + status = 1; + } + } + + /* Put back mmaped files the way our parent (if any) expects. */ + free_mmapped_files(true); + + free_everything(); + if (status == 0) + tell_parent(SUCCESS); + else + tell_parent(FAILURE); + exit(status); +} + +static bool following_path(void) +{ + if (!failpath) + return false; + /* + means continue after end, like normal. */ + if (*failpath == '+') { + failpath = NULL; + return false; + } + return true; +} + +static bool follow_path(struct failtest_call *call) +{ + if (*failpath == '\0') { + /* Continue, but don't inject errors. */ + return call->fail = false; + } + + if (tolower((unsigned char)*failpath) != info_to_arg[call->type]) + errx(1, "Failpath expected '%s' got '%c'\n", + failpath, info_to_arg[call->type]); + call->fail = cisupper(*(failpath++)); + if (call->fail) + call->can_leak = false; + return call->fail; +} + +static bool should_fail(struct failtest_call *call) +{ + int status; + int control[2], output[2]; + enum info_type type = UNEXPECTED; + char *out = NULL; + size_t outlen = 0; + struct failtest_call *dup; + + if (call == &unrecorded_call) + return false; + + if (following_path()) + return follow_path(call); + + /* Attach debugger if they asked for it. */ + if (debugpath) { + char *path; + + /* Pretend this last call matches whatever path wanted: + * keeps valgrind happy. */ + call->fail = cisupper(debugpath[strlen(debugpath)-1]); + path = failpath_string(); + + if (streq(path, debugpath)) { + char str[80]; + + /* Don't timeout. */ + signal(SIGUSR1, SIG_IGN); + sprintf(str, "xterm -e gdb /proc/%d/exe %d &", + getpid(), getpid()); + if (system(str) == 0) + sleep(5); + } else { + /* Ignore last character: could be upper or lower. */ + path[strlen(path)-1] = '\0'; + if (!strstarts(debugpath, path)) { + fprintf(stderr, + "--debugpath not followed: %s\n", path); + debugpath = NULL; + } + } + free(path); + } + + /* Are we probing? If so, we never fail twice. */ + if (probing) { + trace("Not failing %c due to FAIL_PROBE return\n", + info_to_arg[call->type]); + return call->fail = false; + } + + /* Don't fail more than once in the same place. */ + dup = failtable_get(&failtable, call); + if (dup) { + trace("Not failing %c due to duplicate\n", + info_to_arg[call->type]); + return call->fail = false; + } + + if (failtest_hook) { + switch (failtest_hook(&history)) { + case FAIL_OK: + break; + case FAIL_PROBE: + probing = true; + break; + case FAIL_DONT_FAIL: + trace("Not failing %c due to failhook return\n", + info_to_arg[call->type]); + call->fail = false; + return false; + default: + abort(); + } + } + + /* Add it to our table of calls. */ + failtable_add(&failtable, call); + + /* We're going to fail in the child. */ + call->fail = true; + if (pipe(control) != 0 || pipe(output) != 0) + err(1, "opening pipe"); + + /* Move out the way, to high fds. */ + control[0] = move_fd_to_high(control[0]); + control[1] = move_fd_to_high(control[1]); + output[0] = move_fd_to_high(output[0]); + output[1] = move_fd_to_high(output[1]); + + /* Prevent double-printing (in child and parent) */ + fflush(stdout); + fflush(warnf); + if (tracef) + fflush(tracef); + child = fork(); + if (child == -1) + err(1, "forking failed"); + + if (child == 0) { + traceindent++; + if (tracef) { + struct timeval diff; + const char *p; + char *failpath; + struct failtest_call *c; + + c = tlist_tail(&history, list); + diff = time_sub(time_now(), start); + failpath = failpath_string(); + p = strrchr(c->file, '/'); + if (p) + p++; + else + p = c->file; + trace("%u->%u (%u.%02u): %s (%s:%u)\n", + getppid(), getpid(), + (int)diff.tv_sec, (int)diff.tv_usec / 10000, + failpath, p, c->line); + free(failpath); + } + /* From here on, we have to clean up! */ + our_history_start = tlist_tail(&history, list); + close(control[0]); + close(output[0]); + /* Don't swallow stderr if we're tracing. */ + if (!tracef) { + dup2(output[1], STDOUT_FILENO); + dup2(output[1], STDERR_FILENO); + if (output[1] != STDOUT_FILENO + && output[1] != STDERR_FILENO) + close(output[1]); + } + control_fd = move_fd_to_high(control[1]); + + /* Forget any of our parent's saved files. */ + free_mmapped_files(false); + + /* Now, save any files we need to. */ + save_mmapped_files(); + + /* Failed calls can't leak. */ + call->can_leak = false; + + return true; + } + + signal(SIGUSR1, hand_down); + + close(control[1]); + close(output[1]); + + /* We grab output so we can display it; we grab writes so we + * can compare. */ + do { + struct pollfd pfd[2]; + int ret; + + pfd[0].fd = output[0]; + pfd[0].events = POLLIN|POLLHUP; + pfd[1].fd = control[0]; + pfd[1].events = POLLIN|POLLHUP; + + if (type == SUCCESS) + ret = poll(pfd, 1, failtest_timeout_ms); + else + ret = poll(pfd, 2, failtest_timeout_ms); + + if (ret == 0) + hand_down(SIGUSR1); + if (ret < 0) { + if (errno == EINTR) + continue; + err(1, "Poll returned %i", ret); + } + + if (pfd[0].revents & POLLIN) { + ssize_t len; + + out = realloc(out, outlen + 8192); + len = read(output[0], out + outlen, 8192); + outlen += len; + } else if (type != SUCCESS && (pfd[1].revents & POLLIN)) { + if (read_all(control[0], &type, sizeof(type))) { + if (type == WRITE) { + if (!read_write_info(control[0])) + break; + } else if (type == RELEASE_LOCKS) { + release_locks(); + /* FIXME: Tell them we're done... */ + } + } + } else if (pfd[0].revents & POLLHUP) { + break; + } + } while (type != FAILURE); + + close(output[0]); + close(control[0]); + waitpid(child, &status, 0); + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGUSR1) + child_fail(out, outlen, "Timed out"); + else + child_fail(out, outlen, "Killed by signal %u: ", + WTERMSIG(status)); + } + /* Child printed failure already, just pass up exit code. */ + if (type == FAILURE) { + fprintf(stderr, "%.*s", (int)outlen, out); + tell_parent(type); + exit(WEXITSTATUS(status) ? WEXITSTATUS(status) : 1); + } + if (WEXITSTATUS(status) != 0) + child_fail(out, outlen, "Exited with status %i: ", + WEXITSTATUS(status)); + + free(out); + signal(SIGUSR1, SIG_DFL); + + /* Only child does probe. */ + probing = false; + + /* We continue onwards without failing. */ + call->fail = false; + return false; +} + +static void cleanup_calloc(struct calloc_call *call, bool restore) +{ + trace("undoing calloc %p\n", call->ret); + free(call->ret); +} + +void *failtest_calloc(size_t nmemb, size_t size, + const char *file, unsigned line) +{ + struct failtest_call *p; + struct calloc_call call; + call.nmemb = nmemb; + call.size = size; + p = add_history(FAILTEST_CALLOC, true, file, line, &call); + + if (should_fail(p)) { + p->u.calloc.ret = NULL; + p->error = ENOMEM; + } else { + p->u.calloc.ret = calloc(nmemb, size); + set_cleanup(p, cleanup_calloc, struct calloc_call); + } + trace("calloc %zu x %zu %s:%u -> %p\n", + nmemb, size, file, line, p->u.calloc.ret); + errno = p->error; + return p->u.calloc.ret; +} + +static void cleanup_malloc(struct malloc_call *call, bool restore) +{ + trace("undoing malloc %p\n", call->ret); + free(call->ret); +} + +void *failtest_malloc(size_t size, const char *file, unsigned line) +{ + struct failtest_call *p; + struct malloc_call call; + call.size = size; + + p = add_history(FAILTEST_MALLOC, true, file, line, &call); + if (should_fail(p)) { + p->u.malloc.ret = NULL; + p->error = ENOMEM; + } else { + p->u.malloc.ret = malloc(size); + set_cleanup(p, cleanup_malloc, struct malloc_call); + } + trace("malloc %zu %s:%u -> %p\n", + size, file, line, p->u.malloc.ret); + errno = p->error; + return p->u.malloc.ret; +} + +static void cleanup_realloc(struct realloc_call *call, bool restore) +{ + trace("undoing realloc %p\n", call->ret); + free(call->ret); +} + +/* Walk back and find out if we got this ptr from a previous routine. */ +static void fixup_ptr_history(void *ptr, const char *why) +{ + struct failtest_call *i; + + /* Start at end of history, work back. */ + tlist_for_each_rev(&history, i, list) { + switch (i->type) { + case FAILTEST_REALLOC: + if (i->u.realloc.ret == ptr) { + trace("found realloc %p %s:%u matching %s\n", + ptr, i->file, i->line, why); + i->cleanup = NULL; + i->can_leak = false; + return; + } + break; + case FAILTEST_MALLOC: + if (i->u.malloc.ret == ptr) { + trace("found malloc %p %s:%u matching %s\n", + ptr, i->file, i->line, why); + i->cleanup = NULL; + i->can_leak = false; + return; + } + break; + case FAILTEST_CALLOC: + if (i->u.calloc.ret == ptr) { + trace("found calloc %p %s:%u matching %s\n", + ptr, i->file, i->line, why); + i->cleanup = NULL; + i->can_leak = false; + return; + } + break; + default: + break; + } + } + trace("Did not find %p matching %s\n", ptr, why); +} + +void *failtest_realloc(void *ptr, size_t size, const char *file, unsigned line) +{ + struct failtest_call *p; + struct realloc_call call; + call.size = size; + p = add_history(FAILTEST_REALLOC, true, file, line, &call); + + /* FIXME: Try one child moving allocation, one not. */ + if (should_fail(p)) { + p->u.realloc.ret = NULL; + p->error = ENOMEM; + } else { + /* Don't catch this one in the history fixup... */ + p->u.realloc.ret = NULL; + fixup_ptr_history(ptr, "realloc"); + p->u.realloc.ret = realloc(ptr, size); + set_cleanup(p, cleanup_realloc, struct realloc_call); + } + trace("realloc %p %s:%u -> %p\n", + ptr, file, line, p->u.realloc.ret); + errno = p->error; + return p->u.realloc.ret; +} + +/* FIXME: Record free, so we can terminate fixup_ptr_history correctly. + * If there's an alloc we don't see, it could get confusing if it matches + * a previous allocation we did see. */ +void failtest_free(void *ptr) +{ + fixup_ptr_history(ptr, "free"); + trace("free %p\n", ptr); + free(ptr); +} + + +static struct contents_saved *save_file(const char *pathname) +{ + int fd; + struct contents_saved *s; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return NULL; + + s = save_contents(pathname, fd, lseek(fd, 0, SEEK_END), 0, + "open with O_TRUNC"); + close(fd); + return s; +} + +/* Optimization: don't create a child for an open which *we know* + * would fail anyway. */ +static bool open_would_fail(const char *pathname, int flags) +{ + if ((flags & O_ACCMODE) == O_RDONLY) + return access(pathname, R_OK) != 0; + if (!(flags & O_CREAT)) { + if ((flags & O_ACCMODE) == O_WRONLY) + return access(pathname, W_OK) != 0; + if ((flags & O_ACCMODE) == O_RDWR) + return access(pathname, W_OK) != 0 + || access(pathname, R_OK) != 0; + } + /* FIXME: We could check if it exists, for O_CREAT|O_EXCL */ + return false; +} + +static void cleanup_open(struct open_call *call, bool restore) +{ + if (restore && call->saved) + restore_contents(container_of(call, struct failtest_call, + u.open), + call->saved, false, "open with O_TRUNC"); + if (!call->closed) { + trace("Cleaning up open %s by closing fd %i\n", + call->pathname, call->ret); + close(call->ret); + call->closed = true; + } + free(call->saved); +} + +int failtest_open(const char *pathname, + const char *file, unsigned line, ...) +{ + struct failtest_call *p; + struct open_call call; + va_list ap; + + call.pathname = strdup(pathname); + va_start(ap, line); + call.flags = va_arg(ap, int); + call.always_save = false; + call.closed = false; + if (call.flags & O_CREAT) { + call.mode = va_arg(ap, int); + va_end(ap); + } + p = add_history(FAILTEST_OPEN, true, file, line, &call); + /* Avoid memory leak! */ + if (p == &unrecorded_call) + free((char *)call.pathname); + + if (should_fail(p)) { + /* Don't bother inserting failures that would happen anyway. */ + if (open_would_fail(pathname, call.flags)) { + trace("Open would have failed anyway: stopping\n"); + failtest_cleanup(true, 0); + } + p->u.open.ret = -1; + /* FIXME: Play with error codes? */ + p->error = EACCES; + } else { + /* Save the old version if they're truncating it. */ + if (call.flags & O_TRUNC) + p->u.open.saved = save_file(pathname); + else + p->u.open.saved = NULL; + p->u.open.ret = open(pathname, call.flags, call.mode); + if (p->u.open.ret == -1) { + p->u.open.closed = true; + p->can_leak = false; + } else { + set_cleanup(p, cleanup_open, struct open_call); + } + } + trace("open %s %s:%u -> %i (opener %p)\n", + pathname, file, line, p->u.open.ret, &p->u.open); + errno = p->error; + return p->u.open.ret; +} + +static void cleanup_mmap(struct mmap_call *mmap, bool restore) +{ + trace("cleaning up mmap @%p (opener %p)\n", + mmap->ret, mmap->opener); + if (restore) + restore_contents(mmap->opener, mmap->saved, false, "mmap"); + free(mmap->saved); +} + +void *failtest_mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset, const char *file, unsigned line) +{ + struct failtest_call *p; + struct mmap_call call; + + call.addr = addr; + call.length = length; + call.prot = prot; + call.flags = flags; + call.offset = offset; + call.fd = fd; + call.opener = opener_of(fd); + + /* If we don't know what file it was, don't fail. */ + if (!call.opener) { + if (fd != -1) { + fwarnx("failtest_mmap: couldn't figure out source for" + " fd %i at %s:%u", fd, file, line); + } + addr = mmap(addr, length, prot, flags, fd, offset); + trace("mmap of fd %i -> %p (opener = NULL)\n", fd, addr); + return addr; + } + + p = add_history(FAILTEST_MMAP, false, file, line, &call); + if (should_fail(p)) { + p->u.mmap.ret = MAP_FAILED; + p->error = ENOMEM; + } else { + p->u.mmap.ret = mmap(addr, length, prot, flags, fd, offset); + /* Save contents if we're writing to a normal file */ + if (p->u.mmap.ret != MAP_FAILED + && (prot & PROT_WRITE) + && call.opener->type == FAILTEST_OPEN) { + const char *fname = call.opener->u.open.pathname; + p->u.mmap.saved = save_contents(fname, fd, length, + offset, "being mmapped"); + set_cleanup(p, cleanup_mmap, struct mmap_call); + } + } + trace("mmap of fd %i %s:%u -> %p (opener = %p)\n", + fd, file, line, addr, call.opener); + errno = p->error; + return p->u.mmap.ret; +} + +static void cleanup_pipe(struct pipe_call *call, bool restore) +{ + trace("cleaning up pipe fd=%i%s,%i%s\n", + call->fds[0], call->closed[0] ? "(already closed)" : "", + call->fds[1], call->closed[1] ? "(already closed)" : ""); + if (!call->closed[0]) + close(call->fds[0]); + if (!call->closed[1]) + close(call->fds[1]); +} + +int failtest_pipe(int pipefd[2], const char *file, unsigned line) +{ + struct failtest_call *p; + struct pipe_call call; + + p = add_history(FAILTEST_PIPE, true, file, line, &call); + if (should_fail(p)) { + p->u.open.ret = -1; + /* FIXME: Play with error codes? */ + p->error = EMFILE; + } else { + p->u.pipe.ret = pipe(p->u.pipe.fds); + p->u.pipe.closed[0] = p->u.pipe.closed[1] = false; + set_cleanup(p, cleanup_pipe, struct pipe_call); + } + + trace("pipe %s:%u -> %i,%i\n", file, line, + p->u.pipe.ret ? -1 : p->u.pipe.fds[0], + p->u.pipe.ret ? -1 : p->u.pipe.fds[1]); + + /* This causes valgrind to notice if they use pipefd[] after failure */ + memcpy(pipefd, p->u.pipe.fds, sizeof(p->u.pipe.fds)); + errno = p->error; + return p->u.pipe.ret; +} + +static void cleanup_read(struct read_call *call, bool restore) +{ + if (restore) { + trace("cleaning up read on fd %i: seeking to %llu\n", + call->fd, (long long)call->off); + + /* Read (not readv!) moves file offset! */ + if (lseek(call->fd, call->off, SEEK_SET) != call->off) { + fwarn("Restoring lseek pointer failed (read)"); + } + } +} + +static ssize_t failtest_add_read(int fd, void *buf, size_t count, off_t off, + bool is_pread, const char *file, unsigned line) +{ + struct failtest_call *p; + struct read_call call; + call.fd = fd; + call.buf = buf; + call.count = count; + call.off = off; + p = add_history(FAILTEST_READ, false, file, line, &call); + + /* FIXME: Try partial read returns. */ + if (should_fail(p)) { + p->u.read.ret = -1; + p->error = EIO; + } else { + if (is_pread) + p->u.read.ret = pread(fd, buf, count, off); + else { + p->u.read.ret = read(fd, buf, count); + if (p->u.read.ret != -1) + set_cleanup(p, cleanup_read, struct read_call); + } + } + trace("%sread %s:%u fd %i %zu@%llu -> %i\n", + is_pread ? "p" : "", file, line, fd, count, (long long)off, + p->u.read.ret); + errno = p->error; + return p->u.read.ret; +} + +static void cleanup_write(struct write_call *write, bool restore) +{ + trace("cleaning up write on %s\n", write->opener->u.open.pathname); + if (restore) + restore_contents(write->opener, write->saved, !write->is_pwrite, + "write"); + free(write->saved); +} + +static ssize_t failtest_add_write(int fd, const void *buf, + size_t count, off_t off, + bool is_pwrite, + const char *file, unsigned line) +{ + struct failtest_call *p; + struct write_call call; + + call.fd = fd; + call.buf = buf; + call.count = count; + call.off = off; + call.is_pwrite = is_pwrite; + call.opener = opener_of(fd); + p = add_history(FAILTEST_WRITE, false, file, line, &call); + + /* If we're a child, we need to make sure we write the same thing + * to non-files as the parent does, so tell it. */ + if (control_fd != -1 && off == (off_t)-1) { + enum info_type type = WRITE; + + write_all(control_fd, &type, sizeof(type)); + write_all(control_fd, &p->u.write, sizeof(p->u.write)); + write_all(control_fd, buf, count); + } + + /* FIXME: Try partial write returns. */ + if (should_fail(p)) { + p->u.write.ret = -1; + p->error = EIO; + } else { + bool is_file; + assert(call.opener == p->u.write.opener); + + if (p->u.write.opener) { + is_file = (p->u.write.opener->type == FAILTEST_OPEN); + } else { + /* We can't unwind it, so at least check same + * in parent and child. */ + is_file = false; + } + + /* FIXME: We assume same write order in parent and child */ + if (!is_file && child_writes_num != 0) { + if (child_writes[0].fd != fd) + errx(1, "Child wrote to fd %u, not %u?", + child_writes[0].fd, fd); + if (child_writes[0].off != p->u.write.off) + errx(1, "Child wrote to offset %zu, not %zu?", + (size_t)child_writes[0].off, + (size_t)p->u.write.off); + if (child_writes[0].count != count) + errx(1, "Child wrote length %zu, not %zu?", + child_writes[0].count, count); + if (memcmp(child_writes[0].buf, buf, count)) { + child_fail(NULL, 0, + "Child wrote differently to" + " fd %u than we did!\n", fd); + } + free((char *)child_writes[0].buf); + child_writes_num--; + memmove(&child_writes[0], &child_writes[1], + sizeof(child_writes[0]) * child_writes_num); + + /* Child wrote it already. */ + trace("write %s:%i on fd %i already done by child\n", + file, line, fd); + p->u.write.ret = count; + errno = p->error; + return p->u.write.ret; + } + + if (is_file) { + p->u.write.saved = save_contents(call.opener->u.open.pathname, + fd, count, off, + "being overwritten"); + set_cleanup(p, cleanup_write, struct write_call); + } + + /* Though off is current seek ptr for write case, we need to + * move it. write() does that for us. */ + if (p->u.write.is_pwrite) + p->u.write.ret = pwrite(fd, buf, count, off); + else + p->u.write.ret = write(fd, buf, count); + } + trace("%swrite %s:%i %zu@%llu on fd %i -> %i\n", + p->u.write.is_pwrite ? "p" : "", + file, line, count, (long long)off, fd, p->u.write.ret); + errno = p->error; + return p->u.write.ret; +} + +ssize_t failtest_pwrite(int fd, const void *buf, size_t count, off_t offset, + const char *file, unsigned line) +{ + return failtest_add_write(fd, buf, count, offset, true, file, line); +} + +ssize_t failtest_write(int fd, const void *buf, size_t count, + const char *file, unsigned line) +{ + return failtest_add_write(fd, buf, count, lseek(fd, 0, SEEK_CUR), false, + file, line); +} + +ssize_t failtest_pread(int fd, void *buf, size_t count, off_t off, + const char *file, unsigned line) +{ + return failtest_add_read(fd, buf, count, off, true, file, line); +} + +ssize_t failtest_read(int fd, void *buf, size_t count, + const char *file, unsigned line) +{ + return failtest_add_read(fd, buf, count, lseek(fd, 0, SEEK_CUR), false, + file, line); +} + +static struct lock_info *WARN_UNUSED_RESULT +add_lock(struct lock_info *locks, int fd, off_t start, off_t end, int type) +{ + unsigned int i; + struct lock_info *l; + + for (i = 0; i < lock_num; i++) { + l = &locks[i]; + + if (l->fd != fd) + continue; + /* Four cases we care about: + * Start overlap: + * l = | | + * new = | | + * Mid overlap: + * l = | | + * new = | | + * End overlap: + * l = | | + * new = | | + * Total overlap: + * l = | | + * new = | | + */ + if (start > l->start && end < l->end) { + /* Mid overlap: trim entry, add new one. */ + off_t new_start, new_end; + new_start = end + 1; + new_end = l->end; + trace("splitting lock on fd %i from %llu-%llu" + " to %llu-%llu\n", + fd, (long long)l->start, (long long)l->end, + (long long)l->start, (long long)start - 1); + l->end = start - 1; + locks = add_lock(locks, + fd, new_start, new_end, l->type); + l = &locks[i]; + } else if (start <= l->start && end >= l->end) { + /* Total overlap: eliminate entry. */ + trace("erasing lock on fd %i %llu-%llu\n", + fd, (long long)l->start, (long long)l->end); + l->end = 0; + l->start = 1; + } else if (end >= l->start && end < l->end) { + trace("trimming lock on fd %i from %llu-%llu" + " to %llu-%llu\n", + fd, (long long)l->start, (long long)l->end, + (long long)end + 1, (long long)l->end); + /* Start overlap: trim entry. */ + l->start = end + 1; + } else if (start > l->start && start <= l->end) { + trace("trimming lock on fd %i from %llu-%llu" + " to %llu-%llu\n", + fd, (long long)l->start, (long long)l->end, + (long long)l->start, (long long)start - 1); + /* End overlap: trim entry. */ + l->end = start-1; + } + /* Nothing left? Remove it. */ + if (l->end < l->start) { + trace("forgetting lock on fd %i\n", fd); + memmove(l, l + 1, (--lock_num - i) * sizeof(l[0])); + i--; + } + } + + if (type != F_UNLCK) { + locks = realloc(locks, (lock_num + 1) * sizeof(*locks)); + l = &locks[lock_num++]; + l->fd = fd; + l->start = start; + l->end = end; + l->type = type; + trace("new lock on fd %i %llu-%llu\n", + fd, (long long)l->start, (long long)l->end); + } + return locks; +} + +/* We trap this so we can record it: we don't fail it. */ +int failtest_close(int fd, const char *file, unsigned line) +{ + struct close_call call; + struct failtest_call *p, *opener; + + /* Do this before we add ourselves to history! */ + opener = opener_of(fd); + + call.fd = fd; + p = add_history(FAILTEST_CLOSE, false, file, line, &call); + p->fail = false; + + /* Consume close from failpath (shouldn't tell us to fail). */ + if (following_path()) { + if (follow_path(p)) + abort(); + } + + trace("close on fd %i\n", fd); + if (fd < 0) + return close(fd); + + /* Mark opener as not leaking, remove its cleanup function. */ + if (opener) { + trace("close on fd %i found opener %p\n", fd, opener); + if (opener->type == FAILTEST_PIPE) { + /* From a pipe? */ + if (opener->u.pipe.fds[0] == fd) { + assert(!opener->u.pipe.closed[0]); + opener->u.pipe.closed[0] = true; + } else if (opener->u.pipe.fds[1] == fd) { + assert(!opener->u.pipe.closed[1]); + opener->u.pipe.closed[1] = true; + } else + abort(); + opener->can_leak = (!opener->u.pipe.closed[0] + || !opener->u.pipe.closed[1]); + } else if (opener->type == FAILTEST_OPEN) { + opener->u.open.closed = true; + opener->can_leak = false; + } else + abort(); + } + + /* Restore offset now, in case parent shared (can't do after close!). */ + if (control_fd != -1) { + struct failtest_call *i; + + tlist_for_each_rev(&history, i, list) { + if (i == our_history_start) + break; + if (i == opener) + break; + if (i->type == FAILTEST_LSEEK && i->u.lseek.fd == fd) { + trace("close on fd %i undoes lseek\n", fd); + /* This seeks back. */ + i->cleanup(&i->u, true); + i->cleanup = NULL; + } else if (i->type == FAILTEST_WRITE + && i->u.write.fd == fd + && !i->u.write.is_pwrite) { + trace("close on fd %i undoes write" + " offset change\n", fd); + /* Write (not pwrite!) moves file offset! */ + if (lseek(fd, i->u.write.off, SEEK_SET) + != i->u.write.off) { + fwarn("Restoring lseek pointer failed (write)"); + } + } else if (i->type == FAILTEST_READ + && i->u.read.fd == fd) { + /* preads don't *have* cleanups */ + if (i->cleanup) { + trace("close on fd %i undoes read" + " offset change\n", fd); + /* This seeks back. */ + i->cleanup(&i->u, true); + i->cleanup = NULL; + } + } + } + } + + /* Close unlocks everything. */ + locks = add_lock(locks, fd, 0, off_max(), F_UNLCK); + return close(fd); +} + +/* Zero length means "to end of file" */ +static off_t end_of(off_t start, off_t len) +{ + if (len == 0) + return off_max(); + return start + len - 1; +} + +/* FIXME: This only handles locks, really. */ +int failtest_fcntl(int fd, const char *file, unsigned line, int cmd, ...) +{ + struct failtest_call *p; + struct fcntl_call call; + va_list ap; + + call.fd = fd; + call.cmd = cmd; + + /* Argument extraction. */ + switch (cmd) { + case F_SETFL: + case F_SETFD: + va_start(ap, cmd); + call.arg.l = va_arg(ap, long); + va_end(ap); + trace("fcntl on fd %i F_SETFL/F_SETFD\n", fd); + return fcntl(fd, cmd, call.arg.l); + case F_GETFD: + case F_GETFL: + trace("fcntl on fd %i F_GETFL/F_GETFD\n", fd); + return fcntl(fd, cmd); + case F_GETLK: + trace("fcntl on fd %i F_GETLK\n", fd); + get_locks(); + va_start(ap, cmd); + call.arg.fl = *va_arg(ap, struct flock *); + va_end(ap); + return fcntl(fd, cmd, &call.arg.fl); + case F_SETLK: + case F_SETLKW: + trace("fcntl on fd %i F_SETLK%s\n", + fd, cmd == F_SETLKW ? "W" : ""); + va_start(ap, cmd); + call.arg.fl = *va_arg(ap, struct flock *); + va_end(ap); + break; + default: + /* This means you need to implement it here. */ + err(1, "failtest: unknown fcntl %u", cmd); + } + + p = add_history(FAILTEST_FCNTL, false, file, line, &call); + + if (should_fail(p)) { + p->u.fcntl.ret = -1; + if (p->u.fcntl.cmd == F_SETLK) + p->error = EAGAIN; + else + p->error = EDEADLK; + } else { + get_locks(); + p->u.fcntl.ret = fcntl(p->u.fcntl.fd, p->u.fcntl.cmd, + &p->u.fcntl.arg.fl); + if (p->u.fcntl.ret == -1) + p->error = errno; + else { + /* We don't handle anything else yet. */ + assert(p->u.fcntl.arg.fl.l_whence == SEEK_SET); + locks = add_lock(locks, + p->u.fcntl.fd, + p->u.fcntl.arg.fl.l_start, + end_of(p->u.fcntl.arg.fl.l_start, + p->u.fcntl.arg.fl.l_len), + p->u.fcntl.arg.fl.l_type); + } + } + trace("fcntl on fd %i -> %i\n", fd, p->u.fcntl.ret); + errno = p->error; + return p->u.fcntl.ret; +} + +static void cleanup_lseek(struct lseek_call *call, bool restore) +{ + if (restore) { + trace("cleaning up lseek on fd %i -> %llu\n", + call->fd, (long long)call->old_off); + if (lseek(call->fd, call->old_off, SEEK_SET) != call->old_off) + fwarn("Restoring lseek pointer failed"); + } +} + +/* We trap this so we can undo it: we don't fail it. */ +off_t failtest_lseek(int fd, off_t offset, int whence, const char *file, + unsigned int line) +{ + struct failtest_call *p; + struct lseek_call call; + call.fd = fd; + call.offset = offset; + call.whence = whence; + call.old_off = lseek(fd, 0, SEEK_CUR); + + p = add_history(FAILTEST_LSEEK, false, file, line, &call); + p->fail = false; + + /* Consume lseek from failpath. */ + if (failpath) + if (should_fail(p)) + abort(); + + p->u.lseek.ret = lseek(fd, offset, whence); + + if (p->u.lseek.ret != (off_t)-1) + set_cleanup(p, cleanup_lseek, struct lseek_call); + + trace("lseek %s:%u on fd %i from %llu to %llu%s\n", + file, line, fd, (long long)call.old_off, (long long)offset, + whence == SEEK_CUR ? " (from current off)" : + whence == SEEK_END ? " (from end)" : + whence == SEEK_SET ? "" : " (invalid whence)"); + return p->u.lseek.ret; +} + + +pid_t failtest_getpid(const char *file, unsigned line) +{ + /* You must call failtest_init first! */ + assert(orig_pid); + return orig_pid; +} + +void failtest_init(int argc, char *argv[]) +{ + unsigned int i; + + orig_pid = getpid(); + + warnf = fdopen(move_fd_to_high(dup(STDERR_FILENO)), "w"); + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "--failpath=", strlen("--failpath="))) { + failpath = argv[i] + strlen("--failpath="); + } else if (strcmp(argv[i], "--trace") == 0) { + tracef = warnf; + failtest_timeout_ms = -1; + } else if (!strncmp(argv[i], "--debugpath=", + strlen("--debugpath="))) { + debugpath = argv[i] + strlen("--debugpath="); + } + } + failtable_init(&failtable); + start = time_now(); +} + +bool failtest_has_failed(void) +{ + return control_fd != -1; +} + +void failtest_exit(int status) +{ + trace("failtest_exit with status %i\n", status); + if (failtest_exit_check) { + if (!failtest_exit_check(&history)) + child_fail(NULL, 0, "failtest_exit_check failed\n"); + } + + failtest_cleanup(false, status); +} diff --git a/lib/ccan/failtest/failtest.d b/lib/ccan/failtest/failtest.d new file mode 100644 index 00000000000..6aa295d6f8a --- /dev/null +++ b/lib/ccan/failtest/failtest.d @@ -0,0 +1,70 @@ +ccan/failtest/failtest.o: ccan/failtest/failtest.c \ + ccan/failtest/failtest.h config.h \ + /usr/include/i386-linux-gnu/sys/types.h /usr/include/features.h \ + /usr/include/i386-linux-gnu/bits/predefs.h \ + /usr/include/i386-linux-gnu/sys/cdefs.h \ + /usr/include/i386-linux-gnu/bits/wordsize.h \ + /usr/include/i386-linux-gnu/gnu/stubs.h \ + /usr/include/i386-linux-gnu/gnu/stubs-32.h \ + /usr/include/i386-linux-gnu/bits/types.h \ + /usr/include/i386-linux-gnu/bits/typesizes.h /usr/include/time.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stddef.h /usr/include/endian.h \ + /usr/include/i386-linux-gnu/bits/endian.h \ + /usr/include/i386-linux-gnu/bits/byteswap.h \ + /usr/include/i386-linux-gnu/sys/select.h \ + /usr/include/i386-linux-gnu/bits/select.h \ + /usr/include/i386-linux-gnu/bits/sigset.h \ + /usr/include/i386-linux-gnu/bits/time.h \ + /usr/include/i386-linux-gnu/sys/sysmacros.h \ + /usr/include/i386-linux-gnu/bits/pthreadtypes.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdbool.h /usr/include/fcntl.h \ + /usr/include/i386-linux-gnu/bits/fcntl.h \ + /usr/include/i386-linux-gnu/bits/uio.h \ + /usr/include/i386-linux-gnu/bits/stat.h ccan/compiler/compiler.h \ + ccan/tlist/tlist.h ccan/list/list.h /usr/include/assert.h \ + ccan/container_of/container_of.h ccan/check_type/check_type.h \ + ccan/tcon/tcon.h /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdarg.h \ + /usr/include/string.h /usr/include/xlocale.h /usr/include/stdio.h \ + /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/include/i386-linux-gnu/bits/stdio_lim.h \ + /usr/include/i386-linux-gnu/bits/sys_errlist.h /usr/include/ctype.h \ + /usr/include/err.h /usr/include/unistd.h \ + /usr/include/i386-linux-gnu/bits/posix_opt.h \ + /usr/include/i386-linux-gnu/bits/environments.h \ + /usr/include/i386-linux-gnu/bits/confname.h /usr/include/getopt.h \ + /usr/include/poll.h /usr/include/i386-linux-gnu/sys/poll.h \ + /usr/include/i386-linux-gnu/bits/poll.h /usr/include/errno.h \ + /usr/include/i386-linux-gnu/bits/errno.h /usr/include/linux/errno.h \ + /usr/include/i386-linux-gnu/asm/errno.h /usr/include/asm-generic/errno.h \ + /usr/include/asm-generic/errno-base.h \ + /usr/include/i386-linux-gnu/sys/wait.h /usr/include/signal.h \ + /usr/include/i386-linux-gnu/bits/signum.h \ + /usr/include/i386-linux-gnu/bits/siginfo.h \ + /usr/include/i386-linux-gnu/bits/sigaction.h \ + /usr/include/i386-linux-gnu/bits/sigcontext.h \ + /usr/include/i386-linux-gnu/asm/sigcontext.h /usr/include/linux/types.h \ + /usr/include/i386-linux-gnu/asm/types.h /usr/include/asm-generic/types.h \ + /usr/include/asm-generic/int-ll64.h \ + /usr/include/i386-linux-gnu/asm/bitsperlong.h \ + /usr/include/asm-generic/bitsperlong.h /usr/include/linux/posix_types.h \ + /usr/include/linux/stddef.h \ + /usr/include/i386-linux-gnu/asm/posix_types.h \ + /usr/include/i386-linux-gnu/asm/posix_types_32.h \ + /usr/include/i386-linux-gnu/bits/sigstack.h \ + /usr/include/i386-linux-gnu/sys/ucontext.h \ + /usr/include/i386-linux-gnu/bits/sigthread.h \ + /usr/include/i386-linux-gnu/sys/resource.h \ + /usr/include/i386-linux-gnu/bits/resource.h \ + /usr/include/i386-linux-gnu/bits/waitflags.h \ + /usr/include/i386-linux-gnu/bits/waitstatus.h \ + /usr/include/i386-linux-gnu/sys/stat.h \ + /usr/include/i386-linux-gnu/sys/time.h \ + /usr/include/i386-linux-gnu/sys/mman.h \ + /usr/include/i386-linux-gnu/bits/mman.h ccan/time/time.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdint.h /usr/include/stdint.h \ + /usr/include/i386-linux-gnu/bits/wchar.h \ + ccan/read_write_all/read_write_all.h ccan/failtest/failtest_proto.h \ + /usr/include/stdlib.h /usr/include/alloca.h \ + ccan/build_assert/build_assert.h ccan/hash/hash.h \ + ccan/htable/htable_type.h ccan/htable/htable.h ccan/str/str.h \ + ccan/str/str_debug.h /usr/include/execinfo.h diff --git a/lib/ccan/failtest/failtest.h b/lib/ccan/failtest/failtest.h new file mode 100644 index 00000000000..9af5669678f --- /dev/null +++ b/lib/ccan/failtest/failtest.h @@ -0,0 +1,258 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#ifndef CCAN_FAILTEST_H +#define CCAN_FAILTEST_H +#include "config.h" +#if HAVE_FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include <sys/types.h> +#include <stdbool.h> +#include <fcntl.h> +#include <ccan/compiler/compiler.h> +#include <ccan/tlist/tlist.h> + +/** + * failtest_init - initialize the failtest module + * @argc: the number of commandline arguments + * @argv: the commandline argument array + * + * This initializes the module, and in particular if argv[1] is "--failpath=" + * then it ensures that failures follow that pattern. This allows easy + * debugging of complex failure paths. + */ +void failtest_init(int argc, char *argv[]); + +/** + * failtest_exit - clean up and exit the test + * @status: the status (usually exit_status() from ccan/tap). + * + * This cleans up and changes to files made in this child, and exits the test. + * It also calls your failtest_default_hook, if any. + * + * A child which does not exit via failtest_exit() will cause the overall test + * to fail. + */ +void NORETURN failtest_exit(int status); + +/** + * enum failtest_call_type - discriminator for failtest_call.u + */ +enum failtest_call_type { + FAILTEST_MALLOC, + FAILTEST_CALLOC, + FAILTEST_REALLOC, + FAILTEST_OPEN, + FAILTEST_CLOSE, + FAILTEST_PIPE, + FAILTEST_READ, + FAILTEST_WRITE, + FAILTEST_FCNTL, + FAILTEST_MMAP, + FAILTEST_LSEEK +}; + +struct calloc_call { + void *ret; + size_t nmemb; + size_t size; +}; + +struct malloc_call { + void *ret; + size_t size; +}; + +struct realloc_call { + void *ret; + void *ptr; + size_t size; +}; + +struct open_call { + int ret; + const char *pathname; + int flags; + mode_t mode; + bool always_save; + bool closed; + /* This is used for O_TRUNC opens on existing files. */ + struct contents_saved *saved; +}; + +struct close_call { + int fd; +}; + +struct pipe_call { + int ret; + int fds[2]; + bool closed[2]; +}; + +struct read_call { + ssize_t ret; + off_t off; + int fd; + void *buf; + size_t count; +}; + +struct write_call { + ssize_t ret; + int fd; + const void *buf; + size_t count; + off_t off; + bool is_pwrite; + struct failtest_call *opener; + struct contents_saved *saved; +}; + +struct fcntl_call { + int ret; + int fd; + int cmd; + union { + struct flock fl; + long l; + int i; + } arg; +}; + +struct mmap_call { + void *ret; + void *addr; + size_t length; + int prot; + int flags; + int fd; + off_t offset; + struct failtest_call *opener; + struct contents_saved *saved; +}; + +struct lseek_call { + ssize_t ret; + int fd; + off_t offset; + int whence; + off_t old_off; +}; + +/** + * struct failtest_call - description of a call redirected to failtest module + * @type: the call type + * @file: the filename of the caller + * @line: the line number of the caller + * @fail: did this call fail + * @error: the errno (if any) + * @u: the union of call data + * + * This structure is used to represent the ordered history of calls. + * + * See Also: + * failtest_hook, failtest_exit_check + */ +struct failtest_call { + /* We're in the history list. */ + struct list_node list; + enum failtest_call_type type; + /* Where we were called from. */ + const char *file; + unsigned int line; + /* Did we fail? */ + bool fail; + /* What we set errno to. */ + int error; + /* How do we clean this up? */ + void (*cleanup)(void *u, bool restore); + /* Should their program have cleaned up? */ + bool can_leak; + /* Backtrace of call chain. */ + void **backtrace; + unsigned int backtrace_num; + /* The actual call data. */ + union { + struct calloc_call calloc; + struct malloc_call malloc; + struct realloc_call realloc; + struct open_call open; + struct close_call close; + struct pipe_call pipe; + struct read_call read; + struct write_call write; + struct fcntl_call fcntl; + struct mmap_call mmap; + struct lseek_call lseek; + } u; +}; + +/* This defines struct tlist_calls. */ +TLIST_TYPE(calls, struct failtest_call); + +enum failtest_result { + /* Yes try failing this call. */ + FAIL_OK, + /* No, don't try failing this call. */ + FAIL_DONT_FAIL, + /* Try failing this call but don't go too far down that path. */ + FAIL_PROBE, +}; + +/** + * failtest_hook - whether a certain call should fail or not. + * @history: the ordered history of all failtest calls. + * + * The default value of this hook is failtest_default_hook(), which returns + * FAIL_OK (ie. yes, fail the call). + * + * You can override it, and avoid failing certain calls. The parameters + * of the call (but not the return value(s)) will be filled in for the last + * call. + * + * Example: + * static enum failtest_result dont_fail_alloc(struct tlist_calls *history) + * { + * struct failtest_call *call; + * call = tlist_tail(history, list); + * if (call->type == FAILTEST_MALLOC + * || call->type == FAILTEST_CALLOC + * || call->type == FAILTEST_REALLOC) + * return FAIL_DONT_FAIL; + * return FAIL_OK; + * } + * ... + * failtest_hook = dont_fail_alloc; + */ +extern enum failtest_result (*failtest_hook)(struct tlist_calls *history); + +/** + * failtest_exit_check - hook for additional checks on a failed child. + * @history: the ordered history of all failtest calls. + * + * Your program might have additional checks to do on failure, such as + * check that a file is not corrupted, or than an error message has been + * logged. + * + * If this returns false, the path to this failure will be printed and the + * overall test will fail. + */ +extern bool (*failtest_exit_check)(struct tlist_calls *history); + +/** + * failtest_has_failed - determine if a failure has occurred. + * + * Sometimes you want to exit immediately if you've experienced an + * injected failure. This is useful when you have four separate tests + * in your test suite, and you don't want to do the next one if you've + * had a failure in a previous one. + */ +extern bool failtest_has_failed(void); + +/** + * failtest_timeout_ms - how long to wait before killing child. + * + * Default is 20,000 (20 seconds). + */ +extern unsigned int failtest_timeout_ms; +#endif /* CCAN_FAILTEST_H */ diff --git a/lib/ccan/failtest/failtest_override.h b/lib/ccan/failtest/failtest_override.h new file mode 100644 index 00000000000..204494b086c --- /dev/null +++ b/lib/ccan/failtest/failtest_override.h @@ -0,0 +1,81 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#ifndef CCAN_FAILTEST_OVERRIDE_H +#define CCAN_FAILTEST_OVERRIDE_H +/* This file is included before the source file to test. */ +#include "config.h" +#if HAVE_FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +/* Replacement of allocators. */ +#include <stdlib.h> + +#undef calloc +#define calloc(nmemb, size) \ + failtest_calloc((nmemb), (size), __FILE__, __LINE__) + +#undef malloc +#define malloc(size) \ + failtest_malloc((size), __FILE__, __LINE__) + +#undef realloc +#define realloc(ptr, size) \ + failtest_realloc((ptr), (size), __FILE__, __LINE__) + +#undef free +#define free(ptr) \ + failtest_free(ptr) + +/* Replacement of I/O. */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> + +#undef open +#define open(pathname, ...) \ + failtest_open((pathname), __FILE__, __LINE__, __VA_ARGS__) + +#undef pipe +#define pipe(pipefd) \ + failtest_pipe((pipefd), __FILE__, __LINE__) + +#undef read +#define read(fd, buf, count) \ + failtest_read((fd), (buf), (count), __FILE__, __LINE__) + +#undef write +#define write(fd, buf, count) \ + failtest_write((fd), (buf), (count), __FILE__, __LINE__) + +#undef pread +#define pread(fd, buf, count, off) \ + failtest_pread((fd), (buf), (count), (off), __FILE__, __LINE__) + +#undef pwrite +#define pwrite(fd, buf, count, off) \ + failtest_pwrite((fd), (buf), (count), (off), __FILE__, __LINE__) + +#undef close +#define close(fd) failtest_close(fd, __FILE__, __LINE__) + +#undef fcntl +#define fcntl(fd, ...) failtest_fcntl((fd), __FILE__, __LINE__, __VA_ARGS__) + +#undef mmap +#define mmap(addr, length, prot, flags, fd, offset) \ + failtest_mmap((addr), (length), (prot), (flags), (fd), (offset), \ + __FILE__, __LINE__) + +#undef lseek +#define lseek(fd, offset, whence) \ + failtest_lseek((fd), (offset), (whence), __FILE__, __LINE__) + +/* Replacement of getpid (since failtest will fork). */ +#undef getpid +#define getpid() failtest_getpid(__FILE__, __LINE__) + +#include <ccan/failtest/failtest_proto.h> + +#endif /* CCAN_FAILTEST_OVERRIDE_H */ diff --git a/lib/ccan/failtest/failtest_proto.h b/lib/ccan/failtest/failtest_proto.h new file mode 100644 index 00000000000..c7e6b489844 --- /dev/null +++ b/lib/ccan/failtest/failtest_proto.h @@ -0,0 +1,31 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#ifndef CCAN_FAILTEST_PROTO_H +#define CCAN_FAILTEST_PROTO_H +#include <stdlib.h> + +/* Potentially-failing versions of routines; #defined in failtest.h */ +void *failtest_calloc(size_t nmemb, size_t size, + const char *file, unsigned line); +void *failtest_malloc(size_t size, const char *file, unsigned line); +void *failtest_realloc(void *ptr, size_t size, + const char *file, unsigned line); +void failtest_free(void *ptr); +int failtest_open(const char *pathname, + const char *file, unsigned line, ...); +int failtest_pipe(int pipefd[2], const char *file, unsigned line); +ssize_t failtest_read(int fd, void *buf, size_t count, + const char *file, unsigned line); +ssize_t failtest_write(int fd, const void *buf, size_t count, + const char *file, unsigned line); +ssize_t failtest_pread(int fd, void *buf, size_t count, off_t offset, + const char *file, unsigned line); +ssize_t failtest_pwrite(int fd, const void *buf, size_t count, off_t offset, + const char *file, unsigned line); +void *failtest_mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset, const char *file, unsigned line); +off_t failtest_lseek(int fd, off_t offset, int whence, + const char *file, unsigned line); +int failtest_close(int fd, const char *file, unsigned line); +int failtest_fcntl(int fd, const char *file, unsigned line, int cmd, ...); +pid_t failtest_getpid(const char *file, unsigned line); +#endif /* CCAN_FAILTEST_PROTO_H */ diff --git a/lib/ccan/failtest/failtest_undo.h b/lib/ccan/failtest/failtest_undo.h new file mode 100644 index 00000000000..3bb953dedd3 --- /dev/null +++ b/lib/ccan/failtest/failtest_undo.h @@ -0,0 +1,49 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#ifndef CCAN_FAILTEST_RESTORE_H +#define CCAN_FAILTEST_RESTORE_H +/* This file undoes the effect of failtest_override.h. */ + +#undef calloc +#define calloc(nmemb, size) \ + failtest_calloc((nmemb), (size), NULL, 0) + +#undef malloc +#define malloc(size) \ + failtest_malloc((size), NULL, 0) + +#undef realloc +#define realloc(ptr, size) \ + failtest_realloc((ptr), (size), NULL, 0) + +#undef open +#define open(pathname, ...) \ + failtest_open((pathname), NULL, 0, __VA_ARGS__) + +#undef pipe +#define pipe(pipefd) \ + failtest_pipe((pipefd), NULL, 0) + +#undef read +#define read(fd, buf, count) \ + failtest_read((fd), (buf), (count), NULL, 0) + +#undef write +#define write(fd, buf, count) \ + failtest_write((fd), (buf), (count), NULL, 0) + +#undef mmap +#define mmap(addr, length, prot, flags, fd, offset) \ + failtest_mmap((addr), (length), (prot), (flags), (fd), (offset), NULL, 0) + +#undef lseek +#define lseek(fd, off, whence) \ + failtest_lseek((fd), (off), (whence), NULL, 0) + +#undef close +#define close(fd) failtest_close(fd) + +#undef fcntl +#define fcntl(fd, ...) \ + failtest_fcntl((fd), NULL, 0, __VA_ARGS__) + +#endif /* CCAN_FAILTEST_RESTORE_H */ diff --git a/lib/ccan/failtest/test/run-failpath.c b/lib/ccan/failtest/test/run-failpath.c new file mode 100644 index 00000000000..9795ee9d054 --- /dev/null +++ b/lib/ccan/failtest/test/run-failpath.c @@ -0,0 +1,39 @@ +#include <ccan/failtest/failtest.c> +#include <stdlib.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdarg.h> +#include <ccan/tap/tap.h> + +int main(void) +{ + int fds[2], fd; + void *p; + + plan_tests(14); + failtest_init(0, NULL); + + failpath = "mceopwrMCEOPWR"; + + ok1((p = failtest_malloc(10, "run-failpath.c", 1)) != NULL); + ok1(failtest_calloc(10, 5, "run-failpath.c", 1) != NULL); + ok1((p = failtest_realloc(p, 100, "run-failpath.c", 1)) != NULL); + ok1((fd = failtest_open("failpath-scratch", "run-failpath.c", 1, + O_RDWR|O_CREAT, 0600)) >= 0); + ok1(failtest_pipe(fds, "run-failpath.c", 1) == 0); + ok1(failtest_write(fd, "xxxx", 4, "run-failpath.c", 1) == 4); + lseek(fd, 0, SEEK_SET); + ok1(failtest_read(fd, p, 5, "run-failpath.c", 1) == 4); + + /* Now we're into the failures. */ + ok1(failtest_malloc(10, "run-failpath.c", 1) == NULL); + ok1(failtest_calloc(10, 5, "run-failpath.c", 1) == NULL); + ok1(failtest_realloc(p, 100, "run-failpath.c", 1) == NULL); + ok1(failtest_open("failpath-scratch", "run-failpath.c", 1, + O_RDWR|O_CREAT, 0600) == -1); + ok1(failtest_pipe(fds, "run-failpath.c", 1) == -1); + ok1(failtest_write(fd, "xxxx", 4, "run-failpath.c", 1) == -1); + lseek(fd, 0, SEEK_SET); + ok1(failtest_read(fd, p, 5, "run-failpath.c", 1) == -1); + return exit_status(); +} diff --git a/lib/ccan/failtest/test/run-history.c b/lib/ccan/failtest/test/run-history.c new file mode 100644 index 00000000000..b78682f5d5b --- /dev/null +++ b/lib/ccan/failtest/test/run-history.c @@ -0,0 +1,183 @@ +/* Include the C files directly. */ +#include <ccan/failtest/failtest.c> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <ccan/tap/tap.h> + +int main(void) +{ + struct failtest_call *call; + struct calloc_call calloc_call; + struct malloc_call malloc_call; + struct realloc_call realloc_call; + struct open_call open_call; + struct pipe_call pipe_call; + struct read_call read_call; + struct write_call write_call; + struct mmap_call mmap_call; + char buf[20]; + unsigned int i; + char *path; + + /* This is how many tests you plan to run */ + plan_tests(69); + + calloc_call.ret = calloc(1, 2); + calloc_call.nmemb = 1; + calloc_call.size = 2; + call = add_history(FAILTEST_CALLOC, true, + "run-history.c", 1, &calloc_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_CALLOC); + ok1(call->can_leak == true); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->line == 1); + ok1(call->u.calloc.ret == calloc_call.ret); + ok1(call->u.calloc.nmemb == calloc_call.nmemb); + ok1(call->u.calloc.size == calloc_call.size); + + malloc_call.ret = malloc(2); + malloc_call.size = 2; + call = add_history(FAILTEST_MALLOC, true, + "run-history.c", 2, &malloc_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_MALLOC); + ok1(call->can_leak == true); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->line == 2); + ok1(call->u.malloc.ret == malloc_call.ret); + ok1(call->u.malloc.size == malloc_call.size); + + realloc_call.ret = realloc(malloc_call.ret, 3); + realloc_call.ptr = malloc_call.ret; + realloc_call.size = 3; + call = add_history(FAILTEST_REALLOC, true, "run-history.c", 3, + &realloc_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_REALLOC); + ok1(call->can_leak == true); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->line == 3); + ok1(call->u.realloc.ret == realloc_call.ret); + ok1(call->u.realloc.ptr == realloc_call.ptr); + ok1(call->u.realloc.size == realloc_call.size); + + open_call.ret = open("test/run-history.c", O_RDONLY); + open_call.pathname = "test/run-history.c"; + open_call.flags = O_RDONLY; + open_call.mode = 0; + open_call.closed = false; + call = add_history(FAILTEST_OPEN, true, "run-history.c", 4, &open_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_OPEN); + ok1(call->can_leak == true); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->line == 4); + ok1(call->u.open.ret == open_call.ret); + ok1(strcmp(call->u.open.pathname, open_call.pathname) == 0); + ok1(call->u.open.flags == open_call.flags); + ok1(call->u.open.mode == open_call.mode); + + pipe_call.ret = pipe(pipe_call.fds); + call = add_history(FAILTEST_PIPE, true, "run-history.c", 5, &pipe_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_PIPE); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->can_leak == true); + ok1(call->line == 5); + ok1(call->u.pipe.ret == pipe_call.ret); + ok1(call->u.pipe.fds[0] == pipe_call.fds[0]); + ok1(call->u.pipe.fds[1] == pipe_call.fds[1]); + + read_call.ret = read(open_call.ret, buf, 20); + read_call.buf = buf; + read_call.fd = open_call.ret; + read_call.count = 20; + call = add_history(FAILTEST_READ, false, "run-history.c", 6, &read_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_READ); + ok1(call->can_leak == false); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->line == 6); + ok1(call->u.read.ret == read_call.ret); + ok1(call->u.read.buf == read_call.buf); + ok1(call->u.read.fd == read_call.fd); + ok1(call->u.read.count == read_call.count); + + write_call.ret = 20; + write_call.buf = buf; + write_call.fd = open_call.ret; + write_call.count = 20; + write_call.opener = NULL; + call = add_history(FAILTEST_WRITE, false, "run-history.c", 7, + &write_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_WRITE); + ok1(call->can_leak == false); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->line == 7); + ok1(call->u.write.ret == write_call.ret); + ok1(call->u.write.buf == write_call.buf); + ok1(call->u.write.fd == write_call.fd); + ok1(call->u.write.count == write_call.count); + ok1(call->u.write.opener == write_call.opener); + + mmap_call.ret = &mmap_call; + mmap_call.addr = NULL; + mmap_call.length = 4096; + mmap_call.prot = PROT_READ; + mmap_call.flags = 0; + mmap_call.fd = open_call.ret; + mmap_call.offset = 0; + mmap_call.opener = opener_of(open_call.ret); + ok1(mmap_call.opener->type == FAILTEST_OPEN); + mmap_call.saved = NULL; + + call = add_history(FAILTEST_MMAP, false, "run-history.c", 8, + &mmap_call); + /* Normally should_fail would set this. */ + call->fail = false; + ok1(call->type == FAILTEST_MMAP); + ok1(call->can_leak == false); + ok1(strcmp(call->file, "run-history.c") == 0); + ok1(call->line == 8); + ok1(call->u.mmap.ret == mmap_call.ret); + ok1(call->u.mmap.addr == mmap_call.addr); + ok1(call->u.mmap.length == mmap_call.length); + ok1(call->u.mmap.prot == mmap_call.prot); + ok1(call->u.mmap.flags == mmap_call.flags); + ok1(call->u.mmap.fd == mmap_call.fd); + ok1(call->u.mmap.offset == mmap_call.offset); + ok1(call->u.mmap.opener == mmap_call.opener); + ok1(call->u.mmap.saved == mmap_call.saved); + + i = 0; + tlist_for_each(&history, call, list) + i++; + + ok1(i == 8); + + tlist_for_each(&history, call, list) + call->fail = false; + + path = failpath_string(); + ok1(streq(path, "cmeoprwa")); + free(path); + + tlist_for_each(&history, call, list) + call->fail = true; + + path = failpath_string(); + ok1(streq(path, "CMEOPRWA")); + free(path); + + return exit_status(); +} diff --git a/lib/ccan/failtest/test/run-locking.c b/lib/ccan/failtest/test/run-locking.c new file mode 100644 index 00000000000..da0ee70facf --- /dev/null +++ b/lib/ccan/failtest/test/run-locking.c @@ -0,0 +1,134 @@ +/* Include the C files directly. */ +#include <ccan/failtest/failtest.c> +#include <stdlib.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <ccan/tap/tap.h> + +#define SIZE 8 + +/* We don't want to fork and fail; we're just testing lock recording. */ +static enum failtest_result dont_fail(struct tlist_calls *history) +{ + return FAIL_DONT_FAIL; +} + +static bool place_lock(int fd, char lockarr[], unsigned pos, unsigned size, + int type) +{ + struct flock fl; + + /* Update record keeping. */ + if (type == F_RDLCK) + memset(lockarr+pos, 1, size); + else if (type == F_WRLCK) + memset(lockarr+pos, 2, size); + else + memset(lockarr+pos, 0, size); + + fl.l_whence = SEEK_SET; + fl.l_type = type; + fl.l_start = pos; + fl.l_len = size; + return failtest_fcntl(fd, "run-locking.c", 1, F_SETLK, &fl) == 0; +} + +static char lock_lookup(int fd, unsigned pos) +{ + char ret = 0; + unsigned int i; + struct lock_info *l; + + for (i = 0; i < lock_num; i++) { + l = &locks[i]; + + if (l->fd != fd) + continue; + + if (pos >= l->start && pos <= l->end) { + if (ret) + ret = 3; + else if (l->type == F_RDLCK) + ret = 1; + else + ret = 2; + } + } + return ret; +} + +static bool test(int fd, + unsigned p1, unsigned s1, + unsigned p2, unsigned s2, + unsigned p3, unsigned s3) +{ + unsigned int i; + char lockarr[SIZE]; + + memset(lockarr, 0, sizeof(lockarr)); + + if (!place_lock(fd, lockarr, p1, s1, F_WRLCK)) + return false; + + if (!place_lock(fd, lockarr, p2, s2, F_RDLCK)) + return false; + + if (!place_lock(fd, lockarr, p3, s3, F_UNLCK)) + return false; + + for (i = 0; i < SIZE; i++) { + if (lock_lookup(fd, i) != lockarr[i]) + return false; + } + + /* Reset lock info. */ + lock_num = 0; + return true; +} + +int main(void) +{ + int fd; + long flags; + unsigned int isize; + + plan_tests(5835); + failtest_init(0, NULL); + failtest_hook = dont_fail; + + fd = open("run-locking-scratch", O_RDWR|O_CREAT, 0600); + /* GETFL and SETFL wrappers should pass through. */ + flags = fcntl(fd, F_GETFL); + ok1(failtest_fcntl(fd, "run-locking.c", 1, F_GETFL) == flags); + flags |= O_NONBLOCK; + ok1(failtest_fcntl(fd, "run-locking.c", 1, F_SETFL, flags) == 0); + ok1(failtest_fcntl(fd, "run-locking.c", 1, F_GETFL) == flags); + + for (isize = 1; isize < 4; isize++) { + unsigned int ipos; + for (ipos = 0; ipos + isize < SIZE; ipos++) { + unsigned int jsize; + for (jsize = 1; jsize < 4; jsize++) { + unsigned int jpos; + for (jpos = 0; jpos + jsize < SIZE; jpos++) { + unsigned int ksize; + for (ksize = 1; ksize < 4; ksize++) { + unsigned int kpos; + for (kpos = 0; + kpos + ksize < SIZE; + kpos++) { + ok1(test(fd, + ipos, isize, + jpos, jsize, + kpos, ksize)); + } + } + } + } + } + } + + return exit_status(); +} diff --git a/lib/ccan/failtest/test/run-malloc.c b/lib/ccan/failtest/test/run-malloc.c new file mode 100644 index 00000000000..96641b85250 --- /dev/null +++ b/lib/ccan/failtest/test/run-malloc.c @@ -0,0 +1,116 @@ +#include "config.h" +#include <stdlib.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <ccan/tap/tap.h> + +/* We don't actually want it to exit... */ +static jmp_buf exited; +#define exit(status) longjmp(exited, (status) + 1) + +#define printf saved_printf +static int saved_printf(const char *fmt, ...); + +#define fprintf saved_fprintf +static int saved_fprintf(FILE *ignored, const char *fmt, ...); + +#define vfprintf saved_vfprintf +static int saved_vfprintf(FILE *ignored, const char *fmt, va_list ap); + +/* Hack to avoid a memory leak which valgrind complains about. */ +#define realloc set_realloc +static void *set_realloc(void *ptr, size_t size); + +#define free set_free +static void set_free(void *ptr); + +/* Include the C files directly. */ +#include <ccan/failtest/failtest.c> + +#undef realloc +#undef free + +static char *buffer; +static void *set_realloc(void *ptr, size_t size) +{ + return buffer = realloc(ptr, size); +} + +static void set_free(void *ptr) +{ + if (ptr == buffer) + buffer = NULL; + free(ptr); +} + +static char *output = NULL; + +static int saved_vprintf(const char *fmt, va_list ap) +{ + int ret; + int len = 0; + va_list ap2; + + va_copy(ap2, ap); + ret = vsnprintf(NULL, 0, fmt, ap2); + va_end(ap2); + + if (output) + len = strlen(output); + + output = realloc(output, len + ret + 1); + return vsprintf(output + len, fmt, ap); +} + +static int saved_vfprintf(FILE *ignored, const char *fmt, va_list ap) +{ + return saved_vprintf(fmt, ap); +} + +static int saved_printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = saved_vprintf(fmt, ap); + va_end(ap); + return ret; +} + +static int saved_fprintf(FILE *ignored, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = saved_vprintf(fmt, ap); + va_end(ap); + return ret; +} + +int main(void) +{ + int status; + + plan_tests(3); + failtest_init(0, NULL); + + status = setjmp(exited); + if (status == 0) { + char *p = failtest_malloc(1, "run-malloc.c", 1); + /* If we just segv, valgrind counts that as a failure. + * So kill ourselves creatively. */ + if (!p) + kill(getpid(), SIGSEGV); + fail("Expected child to crash!"); + } else { + ok1(status == 2); + ok1(strstr(output, "Killed by signal")); + ok1(strstr(output, "--failpath=M\n")); + } + free(buffer); + return exit_status(); +} diff --git a/lib/ccan/failtest/test/run-open.c b/lib/ccan/failtest/test/run-open.c new file mode 100644 index 00000000000..0166506480d --- /dev/null +++ b/lib/ccan/failtest/test/run-open.c @@ -0,0 +1,72 @@ +/* Include the C files directly. */ +#include <ccan/failtest/failtest.c> +#include <stdlib.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <ccan/tap/tap.h> + +int main(void) +{ + int fd, pfd[2], err; + char buf[] = "Hello world!"; + struct stat st; + + plan_tests(12); + failtest_init(0, NULL); + + if (pipe(pfd)) + abort(); + fd = failtest_open("run-open-scratchpad", "run-open.c", 1, + O_RDWR|O_CREAT, 0600); + if (fd == -1) { + /* We are the child: write error code for parent to check. */ + err = errno; + if (write(pfd[1], &err, sizeof(err)) != sizeof(err)) + abort(); + failtest_exit(0); + } + /* Check it is read-write. */ + ok1(write(fd, buf, strlen(buf)) == strlen(buf)); + lseek(fd, SEEK_SET, 0); + ok1(read(fd, buf, strlen("Hello world!")) == strlen("Hello world!")); + ok1(strcmp(buf, "Hello world!") == 0); + + /* Check name and perms. */ + ok1(stat("run-open-scratchpad", &st) == 0); + ok1(st.st_size == strlen(buf)); + ok1(S_ISREG(st.st_mode)); + ok1((st.st_mode & 0777) == 0600); + + /* Check child got correct errno. */ + ok1(read(pfd[0], &err, sizeof(err)) == sizeof(err)); + ok1(err == EACCES); + + /* Clean up. */ + failtest_close(fd, "run-open.c", 1); + close(pfd[0]); + close(pfd[1]); + + /* Two-arg open. */ + if (pipe(pfd) != 0) + abort(); + fd = failtest_open("run-open-scratchpad", "run-open.c", 1, O_RDONLY); + if (fd == -1) { + /* We are the child: write error code for parent to check. */ + err = errno; + if (write(pfd[1], &err, sizeof(err)) != sizeof(err)) + abort(); + failtest_exit(0); + } + /* Check it is read-only. */ + ok1(write(fd, buf, strlen(buf)) == -1); + ok1(read(fd, buf, strlen("Hello world!")) == strlen("Hello world!")); + ok1(strcmp(buf, "Hello world!") == 0); + /* Clean up. */ + failtest_close(fd, "run-open.c", 1); + close(pfd[0]); + close(pfd[1]); + + return exit_status(); +} diff --git a/lib/ccan/failtest/test/run-write.c b/lib/ccan/failtest/test/run-write.c new file mode 100644 index 00000000000..6a712fec53a --- /dev/null +++ b/lib/ccan/failtest/test/run-write.c @@ -0,0 +1,51 @@ +/* Include the C files directly. */ +#include <ccan/failtest/failtest.c> +#include <stdlib.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <ccan/tap/tap.h> + +int main(int argc, char *argv[]) +{ + int fd; + char *p; + char buf[] = "Hello world!"; + + plan_tests(5); + failtest_init(argc, argv); + + fd = failtest_open("run-write-scratchpad", __FILE__, __LINE__, + O_RDWR|O_CREAT, 0600); + /* Child will fail, ignore. */ + if (fd < 0) + failtest_exit(0); + if (write(fd, buf, strlen(buf)) != strlen(buf)) + abort(); + ok1(lseek(fd, 0, SEEK_CUR) == strlen(buf)); + + p = failtest_malloc(100, __FILE__, __LINE__); + if (!p) { + /* We are the child. Do a heap of writes. */ + unsigned int i; + + for (i = 0; i < strlen(buf)+1; i++) + if (failtest_write(fd, "x", 1, __FILE__, __LINE__) + == 1) + break; + failtest_close(fd, __FILE__, __LINE__); + failtest_exit(0); + } + + /* Seek pointer should be left alone! */ + ok1(lseek(fd, 0, SEEK_CUR) == strlen(buf)); + /* Length should be restored. */ + ok1(lseek(fd, 0, SEEK_END) == strlen(buf)); + lseek(fd, 0, SEEK_SET); + ok1(read(fd, buf, strlen(buf)) == strlen("Hello world!")); + ok1(strcmp(buf, "Hello world!") == 0); + failtest_close(fd, __FILE__, __LINE__); + + return exit_status(); +} diff --git a/lib/ccan/list/LICENSE b/lib/ccan/list/LICENSE new file mode 100644 index 00000000000..5522aa5f33e --- /dev/null +++ b/lib/ccan/list/LICENSE @@ -0,0 +1,508 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library 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. + + This library 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/lib/ccan/list/_info b/lib/ccan/list/_info new file mode 100644 index 00000000000..a30659c1cf6 --- /dev/null +++ b/lib/ccan/list/_info @@ -0,0 +1,70 @@ +#include <stdio.h> +#include <string.h> +#include "config.h" + +/** + * list - double linked list routines + * + * The list header contains routines for manipulating double linked lists. + * It defines two types: struct list_head used for anchoring lists, and + * struct list_node which is usually embedded in the structure which is placed + * in the list. + * + * Example: + * #include <err.h> + * #include <stdio.h> + * #include <stdlib.h> + * #include <ccan/list/list.h> + * + * struct parent { + * const char *name; + * struct list_head children; + * unsigned int num_children; + * }; + * + * struct child { + * const char *name; + * struct list_node list; + * }; + * + * int main(int argc, char *argv[]) + * { + * struct parent p; + * struct child *c; + * unsigned int i; + * + * if (argc < 2) + * errx(1, "Usage: %s parent children...", argv[0]); + * + * p.name = argv[1]; + * list_head_init(&p.children); + * p.num_children = 0; + * for (i = 2; i < argc; i++) { + * c = malloc(sizeof(*c)); + * c->name = argv[i]; + * list_add(&p.children, &c->list); + * p.num_children++; + * } + * + * printf("%s has %u children:", p.name, p.num_children); + * list_for_each(&p.children, c, list) + * printf("%s ", c->name); + * printf("\n"); + * return 0; + * } + * + * License: LGPL (v2.1 or any later version) + * Author: Rusty Russell <rusty@rustcorp.com.au> + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/container_of\n"); + return 0; + } + + return 1; +} diff --git a/lib/ccan/list/list.c b/lib/ccan/list/list.c new file mode 100644 index 00000000000..29dc30ba195 --- /dev/null +++ b/lib/ccan/list/list.c @@ -0,0 +1,43 @@ +/* Licensed under LGPLv2.1+ - see LICENSE file for details */ +#include <stdio.h> +#include <stdlib.h> +#include "list.h" + +static void *corrupt(const char *abortstr, + const struct list_node *head, + const struct list_node *node, + unsigned int count) +{ + if (abortstr) { + fprintf(stderr, + "%s: prev corrupt in node %p (%u) of %p\n", + abortstr, node, count, head); + abort(); + } + return NULL; +} + +struct list_node *list_check_node(const struct list_node *node, + const char *abortstr) +{ + const struct list_node *p, *n; + int count = 0; + + for (p = node, n = node->next; n != node; p = n, n = n->next) { + count++; + if (n->prev != p) + return corrupt(abortstr, node, n, count); + } + /* Check prev on head node. */ + if (node->prev != p) + return corrupt(abortstr, node, node, 0); + + return (struct list_node *)node; +} + +struct list_head *list_check(const struct list_head *h, const char *abortstr) +{ + if (!list_check_node(&h->n, abortstr)) + return NULL; + return (struct list_head *)h; +} diff --git a/lib/ccan/list/list.d b/lib/ccan/list/list.d new file mode 100644 index 00000000000..dd249588c44 --- /dev/null +++ b/lib/ccan/list/list.d @@ -0,0 +1,27 @@ +ccan/list/list.o: ccan/list/list.c /usr/include/stdio.h \ + /usr/include/features.h /usr/include/i386-linux-gnu/bits/predefs.h \ + /usr/include/i386-linux-gnu/sys/cdefs.h \ + /usr/include/i386-linux-gnu/bits/wordsize.h \ + /usr/include/i386-linux-gnu/gnu/stubs.h \ + /usr/include/i386-linux-gnu/gnu/stubs-32.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stddef.h \ + /usr/include/i386-linux-gnu/bits/types.h \ + /usr/include/i386-linux-gnu/bits/typesizes.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdarg.h \ + /usr/include/i386-linux-gnu/bits/stdio_lim.h \ + /usr/include/i386-linux-gnu/bits/sys_errlist.h /usr/include/stdlib.h \ + /usr/include/i386-linux-gnu/bits/waitflags.h \ + /usr/include/i386-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/i386-linux-gnu/bits/endian.h \ + /usr/include/i386-linux-gnu/bits/byteswap.h \ + /usr/include/i386-linux-gnu/sys/types.h /usr/include/time.h \ + /usr/include/i386-linux-gnu/sys/select.h \ + /usr/include/i386-linux-gnu/bits/select.h \ + /usr/include/i386-linux-gnu/bits/sigset.h \ + /usr/include/i386-linux-gnu/bits/time.h \ + /usr/include/i386-linux-gnu/sys/sysmacros.h \ + /usr/include/i386-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + ccan/list/list.h /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdbool.h \ + /usr/include/assert.h ccan/container_of/container_of.h config.h \ + ccan/check_type/check_type.h diff --git a/lib/ccan/list/list.h b/lib/ccan/list/list.h new file mode 100644 index 00000000000..0091ea4b655 --- /dev/null +++ b/lib/ccan/list/list.h @@ -0,0 +1,469 @@ +/* Licensed under LGPLv2.1+ - see LICENSE file for details */ +#ifndef CCAN_LIST_H +#define CCAN_LIST_H +#include <stdbool.h> +#include <assert.h> +#include <ccan/container_of/container_of.h> +#include <ccan/check_type/check_type.h> + +/** + * struct list_node - an entry in a doubly-linked list + * @next: next entry (self if empty) + * @prev: previous entry (self if empty) + * + * This is used as an entry in a linked list. + * Example: + * struct child { + * const char *name; + * // Linked list of all us children. + * struct list_node list; + * }; + */ +struct list_node +{ + struct list_node *next, *prev; +}; + +/** + * struct list_head - the head of a doubly-linked list + * @h: the list_head (containing next and prev pointers) + * + * This is used as the head of a linked list. + * Example: + * struct parent { + * const char *name; + * struct list_head children; + * unsigned int num_children; + * }; + */ +struct list_head +{ + struct list_node n; +}; + +/** + * list_check - check head of a list for consistency + * @h: the list_head + * @abortstr: the location to print on aborting, or NULL. + * + * Because list_nodes have redundant information, consistency checking between + * the back and forward links can be done. This is useful as a debugging check. + * If @abortstr is non-NULL, that will be printed in a diagnostic if the list + * is inconsistent, and the function will abort. + * + * Returns the list head if the list is consistent, NULL if not (it + * can never return NULL if @abortstr is set). + * + * See also: list_check_node() + * + * Example: + * static void dump_parent(struct parent *p) + * { + * struct child *c; + * + * printf("%s (%u children):\n", p->name, p->num_children); + * list_check(&p->children, "bad child list"); + * list_for_each(&p->children, c, list) + * printf(" -> %s\n", c->name); + * } + */ +struct list_head *list_check(const struct list_head *h, const char *abortstr); + +/** + * list_check_node - check node of a list for consistency + * @n: the list_node + * @abortstr: the location to print on aborting, or NULL. + * + * Check consistency of the list node is in (it must be in one). + * + * See also: list_check() + * + * Example: + * static void dump_child(const struct child *c) + * { + * list_check_node(&c->list, "bad child list"); + * printf("%s\n", c->name); + * } + */ +struct list_node *list_check_node(const struct list_node *n, + const char *abortstr); + +#ifdef CCAN_LIST_DEBUG +#define list_debug(h) list_check((h), __func__) +#define list_debug_node(n) list_check_node((n), __func__) +#else +#define list_debug(h) (h) +#define list_debug_node(n) (n) +#endif + +/** + * LIST_HEAD_INIT - initializer for an empty list_head + * @name: the name of the list. + * + * Explicit initializer for an empty list. + * + * See also: + * LIST_HEAD, list_head_init() + * + * Example: + * static struct list_head my_list = LIST_HEAD_INIT(my_list); + */ +#define LIST_HEAD_INIT(name) { { &name.n, &name.n } } + +/** + * LIST_HEAD - define and initialize an empty list_head + * @name: the name of the list. + * + * The LIST_HEAD macro defines a list_head and initializes it to an empty + * list. It can be prepended by "static" to define a static list_head. + * + * See also: + * LIST_HEAD_INIT, list_head_init() + * + * Example: + * static LIST_HEAD(my_global_list); + */ +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * list_head_init - initialize a list_head + * @h: the list_head to set to the empty list + * + * Example: + * ... + * struct parent *parent = malloc(sizeof(*parent)); + * + * list_head_init(&parent->children); + * parent->num_children = 0; + */ +static inline void list_head_init(struct list_head *h) +{ + h->n.next = h->n.prev = &h->n; +} + +/** + * list_add - add an entry at the start of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * struct child *child = malloc(sizeof(*child)); + * + * child->name = "marvin"; + * list_add(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add(struct list_head *h, struct list_node *n) +{ + n->next = h->n.next; + n->prev = &h->n; + h->n.next->prev = n; + h->n.next = n; + (void)list_debug(h); +} + +/** + * list_add_tail - add an entry at the end of a linked list. + * @h: the list_head to add the node to + * @n: the list_node to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * list_add_tail(&parent->children, &child->list); + * parent->num_children++; + */ +static inline void list_add_tail(struct list_head *h, struct list_node *n) +{ + n->next = &h->n; + n->prev = h->n.prev; + h->n.prev->next = n; + h->n.prev = n; + (void)list_debug(h); +} + +/** + * list_empty - is a list empty? + * @h: the list_head + * + * If the list is empty, returns true. + * + * Example: + * assert(list_empty(&parent->children) == (parent->num_children == 0)); + */ +static inline bool list_empty(const struct list_head *h) +{ + (void)list_debug(h); + return h->n.next == &h->n; +} + +/** + * list_del - delete an entry from an (unknown) linked list. + * @n: the list_node to delete from the list. + * + * Note that this leaves @n in an undefined state; it can be added to + * another list, but not deleted again. + * + * See also: + * list_del_from() + * + * Example: + * list_del(&child->list); + * parent->num_children--; + */ +static inline void list_del(struct list_node *n) +{ + (void)list_debug_node(n); + n->next->prev = n->prev; + n->prev->next = n->next; +#ifdef CCAN_LIST_DEBUG + /* Catch use-after-del. */ + n->next = n->prev = NULL; +#endif +} + +/** + * list_del_from - delete an entry from a known linked list. + * @h: the list_head the node is in. + * @n: the list_node to delete from the list. + * + * This explicitly indicates which list a node is expected to be in, + * which is better documentation and can catch more bugs. + * + * See also: list_del() + * + * Example: + * list_del_from(&parent->children, &child->list); + * parent->num_children--; + */ +static inline void list_del_from(struct list_head *h, struct list_node *n) +{ +#ifdef CCAN_LIST_DEBUG + { + /* Thorough check: make sure it was in list! */ + struct list_node *i; + for (i = h->n.next; i != n; i = i->next) + assert(i != &h->n); + } +#endif /* CCAN_LIST_DEBUG */ + + /* Quick test that catches a surprising number of bugs. */ + assert(!list_empty(h)); + list_del(n); +} + +/** + * list_entry - convert a list_node back into the structure containing it. + * @n: the list_node + * @type: the type of the entry + * @member: the list_node member of the type + * + * Example: + * // First list entry is children.next; convert back to child. + * child = list_entry(parent->children.n.next, struct child, list); + * + * See Also: + * list_top(), list_for_each() + */ +#define list_entry(n, type, member) container_of(n, type, member) + +/** + * list_top - get the first entry in a list + * @h: the list_head + * @type: the type of the entry + * @member: the list_node member of the type + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *first; + * first = list_top(&parent->children, struct child, list); + */ +#define list_top(h, type, member) \ + ((type *)list_top_((h), list_off_(type, member))) + +static inline const void *list_top_(const struct list_head *h, size_t off) +{ + if (list_empty(h)) + return NULL; + return (const char *)h->n.next - off; +} + +/** + * list_tail - get the last entry in a list + * @h: the list_head + * @type: the type of the entry + * @member: the list_node member of the type + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *last; + * last = list_tail(&parent->children, struct child, list); + */ +#define list_tail(h, type, member) \ + ((type *)list_tail_((h), list_off_(type, member))) + +static inline const void *list_tail_(const struct list_head *h, size_t off) +{ + if (list_empty(h)) + return NULL; + return (const char *)h->n.prev - off; +} + +/** + * list_for_each - iterate through a list. + * @h: the list_head (warning: evaluated multiple times!) + * @i: the structure containing the list_node + * @member: the list_node member of the structure + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. + * + * Example: + * list_for_each(&parent->children, child, list) + * printf("Name: %s\n", child->name); + */ +#define list_for_each(h, i, member) \ + list_for_each_off(h, i, list_off_var_(i, member)) + +/** + * list_for_each_rev - iterate through a list backwards. + * @h: the list_head + * @i: the structure containing the list_node + * @member: the list_node member of the structure + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. + * + * Example: + * list_for_each_rev(&parent->children, child, list) + * printf("Name: %s\n", child->name); + */ +#define list_for_each_rev(h, i, member) \ + for (i = container_of_var(list_debug(h)->n.prev, i, member); \ + &i->member != &(h)->n; \ + i = container_of_var(i->member.prev, i, member)) + +/** + * list_for_each_safe - iterate through a list, maybe during deletion + * @h: the list_head + * @i: the structure containing the list_node + * @nxt: the structure containing the list_node + * @member: the list_node member of the structure + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. The extra variable + * @nxt is used to hold the next element, so you can delete @i from the list. + * + * Example: + * struct child *next; + * list_for_each_safe(&parent->children, child, next, list) { + * list_del(&child->list); + * parent->num_children--; + * } + */ +#define list_for_each_safe(h, i, nxt, member) \ + list_for_each_safe_off(h, i, nxt, list_off_var_(i, member)) + +/** + * list_for_each_off - iterate through a list of memory regions. + * @h: the list_head + * @i: the pointer to a memory region wich contains list node data. + * @off: offset(relative to @i) at which list node data resides. + * + * This is a low-level wrapper to iterate @i over the entire list, used to + * implement all oher, more high-level, for-each constructs. It's a for loop, + * so you can break and continue as normal. + * + * WARNING! Being the low-level macro that it is, this wrapper doesn't know + * nor care about the type of @i. The only assumtion made is that @i points + * to a chunk of memory that at some @offset, relative to @i, contains a + * properly filled `struct node_list' which in turn contains pointers to + * memory chunks and it's turtles all the way down. Whith all that in mind + * remember that given the wrong pointer/offset couple this macro will + * happilly churn all you memory untill SEGFAULT stops it, in other words + * caveat emptor. + * + * It is worth mentioning that one of legitimate use-cases for that wrapper + * is operation on opaque types with known offset for `struct list_node' + * member(preferably 0), because it allows you not to disclose the type of + * @i. + * + * Example: + * list_for_each_off(&parent->children, child, + * offsetof(struct child, list)) + * printf("Name: %s\n", child->name); + */ +#define list_for_each_off(h, i, off) \ + for (i = list_node_to_off_(list_debug(h)->n.next, (off)); \ + list_node_from_off_((void *)i, (off)) != &(h)->n; \ + i = list_node_to_off_(list_node_from_off_((void *)i, (off))->next, \ + (off))) + +/** + * list_for_each_safe_off - iterate through a list of memory regions, maybe + * during deletion + * @h: the list_head + * @i: the pointer to a memory region wich contains list node data. + * @nxt: the structure containing the list_node + * @off: offset(relative to @i) at which list node data resides. + * + * For details see `list_for_each_off' and `list_for_each_safe' + * descriptions. + * + * Example: + * list_for_each_safe_off(&parent->children, child, + * next, offsetof(struct child, list)) + * printf("Name: %s\n", child->name); + */ +#define list_for_each_safe_off(h, i, nxt, off) \ + for (i = list_node_to_off_(list_debug(h)->n.next, (off)), \ + nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ + (off)); \ + list_node_from_off_(i, (off)) != &(h)->n; \ + i = nxt, \ + nxt = list_node_to_off_(list_node_from_off_(i, (off))->next, \ + (off))) + + +/* Other -off variants. */ +#define list_entry_off(n, type, off) \ + ((type *)list_node_from_off_((n), (off))) + +#define list_head_off(h, type, off) \ + ((type *)list_head_off((h), (off))) + +#define list_tail_off(h, type, off) \ + ((type *)list_tail_((h), (off))) + +#define list_add_off(h, n, off) \ + list_add((h), list_node_from_off_((n), (off))) + +#define list_del_off(n, off) \ + list_del(list_node_from_off_((n), (off))) + +#define list_del_from_off(h, n, off) \ + list_del_from(h, list_node_from_off_((n), (off))) + +/* Offset helper functions so we only single-evaluate. */ +static inline void *list_node_to_off_(struct list_node *node, size_t off) +{ + return (void *)((char *)node - off); +} +static inline struct list_node *list_node_from_off_(void *ptr, size_t off) +{ + return (struct list_node *)((char *)ptr + off); +} + +/* Get the offset of the member, but make sure it's a list_node. */ +#define list_off_(type, member) \ + (container_off(type, member) + \ + check_type(((type *)0)->member, struct list_node)) + +#define list_off_var_(var, member) \ + (container_off_var(var, member) + \ + check_type(var->member, struct list_node)) + +#endif /* CCAN_LIST_H */ diff --git a/lib/ccan/list/test/compile_ok-constant.c b/lib/ccan/list/test/compile_ok-constant.c new file mode 100644 index 00000000000..c57cdadc316 --- /dev/null +++ b/lib/ccan/list/test/compile_ok-constant.c @@ -0,0 +1,49 @@ +#include <ccan/list/list.h> +#include <ccan/tap/tap.h> +#include <ccan/list/list.c> +#include <stdbool.h> +#include <stdio.h> + +struct child { + const char *name; + struct list_node list; +}; + +static bool children(const struct list_head *list) +{ + return !list_empty(list); +} + +static const struct child *first_child(const struct list_head *list) +{ + return list_top(list, struct child, list); +} + +static const struct child *last_child(const struct list_head *list) +{ + return list_tail(list, struct child, list); +} + +static void check_children(const struct list_head *list) +{ + list_check(list, "bad child list"); +} + +static void print_children(const struct list_head *list) +{ + const struct child *c; + list_for_each(list, c, list) + printf("%s\n", c->name); +} + +int main(void) +{ + LIST_HEAD(h); + + children(&h); + first_child(&h); + last_child(&h); + check_children(&h); + print_children(&h); + return 0; +} diff --git a/lib/ccan/list/test/helper.c b/lib/ccan/list/test/helper.c new file mode 100644 index 00000000000..8903ac17388 --- /dev/null +++ b/lib/ccan/list/test/helper.c @@ -0,0 +1,54 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <time.h> + +#include <ccan/list/list.h> +#include "helper.h" + +#define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \ + (42) + +struct opaque { + struct list_node list; + size_t secret_offset; + char secret_drawer[42]; +}; + +static bool not_randomized = true; + +struct opaque *create_opaque_blob(void) +{ + struct opaque *blob = calloc(1, sizeof(struct opaque)); + + if (not_randomized) { + srandom((int)time(NULL)); + not_randomized = false; + } + + blob->secret_offset = random() % (sizeof(blob->secret_drawer)); + blob->secret_drawer[blob->secret_offset] = + ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING; + + return blob; +} + +bool if_blobs_know_the_secret(struct opaque *blob) +{ + bool answer = true; + int i; + for (i = 0; i < sizeof(blob->secret_drawer) / + sizeof(blob->secret_drawer[0]); i++) + if (i != blob->secret_offset) + answer = answer && (blob->secret_drawer[i] == 0); + else + answer = answer && + (blob->secret_drawer[blob->secret_offset] == + ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING); + + return answer; +} + +void destroy_opaque_blob(struct opaque *blob) +{ + free(blob); +} diff --git a/lib/ccan/list/test/helper.h b/lib/ccan/list/test/helper.h new file mode 100644 index 00000000000..a09a3a997b4 --- /dev/null +++ b/lib/ccan/list/test/helper.h @@ -0,0 +1,7 @@ +/* These are in a separate C file so we can test undefined structures. */ +struct opaque; +typedef struct opaque opaque_t; + +opaque_t *create_opaque_blob(void); +bool if_blobs_know_the_secret(opaque_t *blob); +void destroy_opaque_blob(opaque_t *blob); diff --git a/lib/ccan/list/test/run-check-corrupt.c b/lib/ccan/list/test/run-check-corrupt.c new file mode 100644 index 00000000000..5dd9f9cc835 --- /dev/null +++ b/lib/ccan/list/test/run-check-corrupt.c @@ -0,0 +1,89 @@ +#include <setjmp.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <err.h> + +/* We don't actually want it to exit... */ +static jmp_buf aborted; +#define abort() longjmp(aborted, 1) + +#define fprintf my_fprintf +static char printf_buffer[1000]; + +static int my_fprintf(FILE *stream, const char *format, ...) +{ + va_list ap; + int ret; + va_start(ap, format); + ret = vsprintf(printf_buffer, format, ap); + va_end(ap); + return ret; +} + +#include <ccan/list/list.h> +#include <ccan/tap/tap.h> +#include <ccan/list/list.c> + +int main(int argc, char *argv[]) +{ + struct list_head list; + struct list_node n1; + char expect[100]; + + plan_tests(9); + /* Empty list. */ + list.n.next = &list.n; + list.n.prev = &list.n; + ok1(list_check(&list, NULL) == &list); + + /* Bad back ptr */ + list.n.prev = &n1; + /* Non-aborting version. */ + ok1(list_check(&list, NULL) == NULL); + + /* Aborting version. */ + sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", + &list, &list); + if (setjmp(aborted) == 0) { + list_check(&list, "test message"); + fail("list_check on empty with bad back ptr didn't fail!"); + } else { + ok1(strcmp(printf_buffer, expect) == 0); + } + + /* n1 in list. */ + list.n.next = &n1; + list.n.prev = &n1; + n1.prev = &list.n; + n1.next = &list.n; + ok1(list_check(&list, NULL) == &list); + ok1(list_check_node(&n1, NULL) == &n1); + + /* Bad back ptr */ + n1.prev = &n1; + ok1(list_check(&list, NULL) == NULL); + ok1(list_check_node(&n1, NULL) == NULL); + + /* Aborting version. */ + sprintf(expect, "test message: prev corrupt in node %p (1) of %p\n", + &n1, &list); + if (setjmp(aborted) == 0) { + list_check(&list, "test message"); + fail("list_check on n1 bad back ptr didn't fail!"); + } else { + ok1(strcmp(printf_buffer, expect) == 0); + } + + sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n", + &n1, &n1); + if (setjmp(aborted) == 0) { + list_check_node(&n1, "test message"); + fail("list_check_node on n1 bad back ptr didn't fail!"); + } else { + ok1(strcmp(printf_buffer, expect) == 0); + } + + return exit_status(); +} diff --git a/lib/ccan/list/test/run-list_del_from-assert.c b/lib/ccan/list/test/run-list_del_from-assert.c new file mode 100644 index 00000000000..05d6cad62c4 --- /dev/null +++ b/lib/ccan/list/test/run-list_del_from-assert.c @@ -0,0 +1,36 @@ +#define CCAN_LIST_DEBUG 1 +#include <ccan/list/list.h> +#include <ccan/tap/tap.h> +#include <ccan/list/list.c> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <signal.h> + +int main(int argc, char *argv[]) +{ + struct list_head list1, list2; + struct list_node n1, n2, n3; + pid_t child; + int status; + + plan_tests(1); + list_head_init(&list1); + list_head_init(&list2); + list_add(&list1, &n1); + list_add(&list2, &n2); + list_add_tail(&list2, &n3); + + child = fork(); + if (child) { + wait(&status); + } else { + /* This should abort. */ + list_del_from(&list1, &n3); + exit(0); + } + + ok1(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT); + list_del_from(&list2, &n3); + return exit_status(); +} diff --git a/lib/ccan/list/test/run-single-eval.c b/lib/ccan/list/test/run-single-eval.c new file mode 100644 index 00000000000..f90eed357ac --- /dev/null +++ b/lib/ccan/list/test/run-single-eval.c @@ -0,0 +1,168 @@ +/* Make sure macros only evaluate their args once. */ +#include <ccan/list/list.h> +#include <ccan/tap/tap.h> +#include <ccan/list/list.c> + +struct parent { + const char *name; + struct list_head children; + unsigned int num_children; + int eval_count; +}; + +struct child { + const char *name; + struct list_node list; +}; + +static LIST_HEAD(static_list); + +#define ref(obj, counter) ((counter)++, (obj)) + +int main(int argc, char *argv[]) +{ + struct parent parent; + struct child c1, c2, c3, *c, *n; + unsigned int i; + unsigned int static_count = 0, parent_count = 0, list_count = 0, + node_count = 0; + struct list_head list = LIST_HEAD_INIT(list); + + plan_tests(74); + /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ + ok1(list_empty(ref(&static_list, static_count))); + ok1(static_count == 1); + ok1(list_check(ref(&static_list, static_count), NULL)); + ok1(static_count == 2); + ok1(list_empty(ref(&list, list_count))); + ok1(list_count == 1); + ok1(list_check(ref(&list, list_count), NULL)); + ok1(list_count == 2); + + parent.num_children = 0; + list_head_init(ref(&parent.children, parent_count)); + ok1(parent_count == 1); + /* Test list_head_init */ + ok1(list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 2); + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 3); + + c2.name = "c2"; + list_add(ref(&parent.children, parent_count), &c2.list); + ok1(parent_count == 4); + /* Test list_add and !list_empty. */ + ok1(!list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 5); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &parent.children.n); + ok1(parent.children.n.next == &c2.list); + ok1(parent.children.n.prev == &c2.list); + /* Test list_check */ + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 6); + + c1.name = "c1"; + list_add(ref(&parent.children, parent_count), &c1.list); + ok1(parent_count == 7); + /* Test list_add and !list_empty. */ + ok1(!list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 8); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &c1.list); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c2.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + /* Test list_check */ + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 9); + + c3.name = "c3"; + list_add_tail(ref(&parent.children, parent_count), &c3.list); + ok1(parent_count == 10); + /* Test list_add_tail and !list_empty. */ + ok1(!list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 11); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c3.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + ok1(c2.list.next == &c3.list); + ok1(c2.list.prev == &c1.list); + ok1(c3.list.next == &parent.children.n); + ok1(c3.list.prev == &c2.list); + /* Test list_check */ + ok1(list_check(ref(&parent.children, parent_count), NULL)); + ok1(parent_count == 12); + + /* Test list_check_node */ + ok1(list_check_node(&c1.list, NULL)); + ok1(list_check_node(&c2.list, NULL)); + ok1(list_check_node(&c3.list, NULL)); + + /* Test list_top */ + ok1(list_top(ref(&parent.children, parent_count), struct child, list) == &c1); + ok1(parent_count == 13); + + /* Test list_tail */ + ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == &c3); + ok1(parent_count == 14); + + /* Test list_for_each. */ + i = 0; + list_for_each(&parent.children, c, list) { + switch (i++) { + case 0: + ok1(c == &c1); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c3); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test list_for_each_safe, list_del and list_del_from. */ + i = 0; + list_for_each_safe(&parent.children, c, n, list) { + switch (i++) { + case 0: + ok1(c == &c1); + list_del(ref(&c->list, node_count)); + ok1(node_count == 1); + break; + case 1: + ok1(c == &c2); + list_del_from(ref(&parent.children, parent_count), + ref(&c->list, node_count)); + ok1(node_count == 2); + break; + case 2: + ok1(c == &c3); + list_del_from(ref(&parent.children, parent_count), + ref(&c->list, node_count)); + ok1(node_count == 3); + break; + } + ok1(list_check(ref(&parent.children, parent_count), NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(parent_count == 19); + ok1(list_empty(ref(&parent.children, parent_count))); + ok1(parent_count == 20); + + /* Test list_top/list_tail on empty list. */ + ok1(list_top(ref(&parent.children, parent_count), struct child, list) == NULL); + ok1(parent_count == 21); + ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == NULL); + ok1(parent_count == 22); + return exit_status(); +} diff --git a/lib/ccan/list/test/run-with-debug.c b/lib/ccan/list/test/run-with-debug.c new file mode 100644 index 00000000000..d0902421f19 --- /dev/null +++ b/lib/ccan/list/test/run-with-debug.c @@ -0,0 +1,3 @@ +/* Just like run.c, but with all debug checks enabled. */ +#define CCAN_LIST_DEBUG 1 +#include <ccan/list/test/run.c> diff --git a/lib/ccan/list/test/run.c b/lib/ccan/list/test/run.c new file mode 100644 index 00000000000..952a0e15e69 --- /dev/null +++ b/lib/ccan/list/test/run.c @@ -0,0 +1,200 @@ +#include <ccan/list/list.h> +#include <ccan/tap/tap.h> +#include <ccan/list/list.c> +#include "helper.h" + +struct parent { + const char *name; + struct list_head children; + unsigned int num_children; +}; + +struct child { + const char *name; + struct list_node list; +}; + +static LIST_HEAD(static_list); + +int main(int argc, char *argv[]) +{ + struct parent parent; + struct child c1, c2, c3, *c, *n; + unsigned int i; + struct list_head list = LIST_HEAD_INIT(list); + opaque_t *q, *nq; + struct list_head opaque_list = LIST_HEAD_INIT(opaque_list); + + plan_tests(65); + /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */ + ok1(list_empty(&static_list)); + ok1(list_check(&static_list, NULL)); + ok1(list_empty(&list)); + ok1(list_check(&list, NULL)); + + parent.num_children = 0; + list_head_init(&parent.children); + /* Test list_head_init */ + ok1(list_empty(&parent.children)); + ok1(list_check(&parent.children, NULL)); + + c2.name = "c2"; + list_add(&parent.children, &c2.list); + /* Test list_add and !list_empty. */ + ok1(!list_empty(&parent.children)); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &parent.children.n); + ok1(parent.children.n.next == &c2.list); + ok1(parent.children.n.prev == &c2.list); + /* Test list_check */ + ok1(list_check(&parent.children, NULL)); + + c1.name = "c1"; + list_add(&parent.children, &c1.list); + /* Test list_add and !list_empty. */ + ok1(!list_empty(&parent.children)); + ok1(c2.list.next == &parent.children.n); + ok1(c2.list.prev == &c1.list); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c2.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + /* Test list_check */ + ok1(list_check(&parent.children, NULL)); + + c3.name = "c3"; + list_add_tail(&parent.children, &c3.list); + /* Test list_add_tail and !list_empty. */ + ok1(!list_empty(&parent.children)); + ok1(parent.children.n.next == &c1.list); + ok1(parent.children.n.prev == &c3.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.n); + ok1(c2.list.next == &c3.list); + ok1(c2.list.prev == &c1.list); + ok1(c3.list.next == &parent.children.n); + ok1(c3.list.prev == &c2.list); + /* Test list_check */ + ok1(list_check(&parent.children, NULL)); + + /* Test list_check_node */ + ok1(list_check_node(&c1.list, NULL)); + ok1(list_check_node(&c2.list, NULL)); + ok1(list_check_node(&c3.list, NULL)); + + /* Test list_top */ + ok1(list_top(&parent.children, struct child, list) == &c1); + + /* Test list_tail */ + ok1(list_tail(&parent.children, struct child, list) == &c3); + + /* Test list_for_each. */ + i = 0; + list_for_each(&parent.children, c, list) { + switch (i++) { + case 0: + ok1(c == &c1); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c3); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test list_for_each_rev. */ + i = 0; + list_for_each_rev(&parent.children, c, list) { + switch (i++) { + case 0: + ok1(c == &c3); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c1); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test list_for_each_safe, list_del and list_del_from. */ + i = 0; + list_for_each_safe(&parent.children, c, n, list) { + switch (i++) { + case 0: + ok1(c == &c1); + list_del(&c->list); + break; + case 1: + ok1(c == &c2); + list_del_from(&parent.children, &c->list); + break; + case 2: + ok1(c == &c3); + list_del_from(&parent.children, &c->list); + break; + } + ok1(list_check(&parent.children, NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(list_empty(&parent.children)); + + /* Test list_for_each_off. */ + list_add_tail(&opaque_list, + (struct list_node *)create_opaque_blob()); + list_add_tail(&opaque_list, + (struct list_node *)create_opaque_blob()); + list_add_tail(&opaque_list, + (struct list_node *)create_opaque_blob()); + + i = 0; + + list_for_each_off(&opaque_list, q, 0) { + i++; + ok1(if_blobs_know_the_secret(q)); + } + ok1(i == 3); + + /* Test list_for_each_safe_off, list_del_off and list_del_from_off. */ + i = 0; + list_for_each_safe_off(&opaque_list, q, nq, 0) { + switch (i++) { + case 0: + ok1(if_blobs_know_the_secret(q)); + list_del_off(q, 0); + destroy_opaque_blob(q); + break; + case 1: + ok1(if_blobs_know_the_secret(q)); + list_del_from_off(&opaque_list, q, 0); + destroy_opaque_blob(q); + break; + case 2: + ok1(c == &c3); + list_del_from_off(&opaque_list, q, 0); + destroy_opaque_blob(q); + break; + } + ok1(list_check(&opaque_list, NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(list_empty(&opaque_list)); + + /* Test list_top/list_tail on empty list. */ + ok1(list_top(&parent.children, struct child, list) == NULL); + ok1(list_tail(&parent.children, struct child, list) == NULL); + return exit_status(); +} diff --git a/lib/ccan/read_write_all/LICENSE b/lib/ccan/read_write_all/LICENSE new file mode 100644 index 00000000000..5522aa5f33e --- /dev/null +++ b/lib/ccan/read_write_all/LICENSE @@ -0,0 +1,508 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library 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. + + This library 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/lib/ccan/read_write_all/_info b/lib/ccan/read_write_all/_info new file mode 100644 index 00000000000..d70cbd1d157 --- /dev/null +++ b/lib/ccan/read_write_all/_info @@ -0,0 +1,44 @@ +#include <stdio.h> +#include <string.h> +#include "config.h" + +/** + * read_write_all - read_all and write_all routines. + * + * Successful read and write calls may only partly complete if a + * signal is received or they are not operating on a normal file. + * + * read_all() and write_all() do the looping for you. + * + * Example: + * #include <err.h> + * #include <stdio.h> + * #include <unistd.h> + * #include <ccan/read_write_all/read_write_all.h> + * + * #define BUFFER_SIZE 10 + * int main(int argc, char *argv[]) + * { + * char buffer[BUFFER_SIZE+1]; + * + * if (!read_all(STDIN_FILENO, buffer, BUFFER_SIZE)) + * err(1, "Could not read %u characters", BUFFER_SIZE); + * buffer[BUFFER_SIZE] = '\0'; + * printf("I read '%.*s'\n", BUFFER_SIZE, buffer); + * return 0; + * } + * + * License: LGPL (v2.1 or any later version) + * Author: Rusty Russell <rusty@rustcorp.com.au> + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + return 0; + } + + return 1; +} diff --git a/lib/ccan/read_write_all/read_write_all.c b/lib/ccan/read_write_all/read_write_all.c new file mode 100644 index 00000000000..c66d5215980 --- /dev/null +++ b/lib/ccan/read_write_all/read_write_all.c @@ -0,0 +1,38 @@ +/* Licensed under LGPLv2+ - see LICENSE file for details */ +#include "read_write_all.h" +#include <unistd.h> +#include <errno.h> + +bool write_all(int fd, const void *data, size_t size) +{ + while (size) { + ssize_t done; + + done = write(fd, data, size); + if (done < 0 && errno == EINTR) + continue; + if (done <= 0) + return false; + data = (const char *)data + done; + size -= done; + } + + return true; +} + +bool read_all(int fd, void *data, size_t size) +{ + while (size) { + ssize_t done; + + done = read(fd, data, size); + if (done < 0 && errno == EINTR) + continue; + if (done <= 0) + return false; + data = (char *)data + done; + size -= done; + } + + return true; +} diff --git a/lib/ccan/read_write_all/read_write_all.d b/lib/ccan/read_write_all/read_write_all.d new file mode 100644 index 00000000000..f3f48f02907 --- /dev/null +++ b/lib/ccan/read_write_all/read_write_all.d @@ -0,0 +1,19 @@ +ccan/read_write_all/read_write_all.o: \ + ccan/read_write_all/read_write_all.c \ + ccan/read_write_all/read_write_all.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stddef.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdbool.h \ + /usr/include/unistd.h /usr/include/features.h \ + /usr/include/i386-linux-gnu/bits/predefs.h \ + /usr/include/i386-linux-gnu/sys/cdefs.h \ + /usr/include/i386-linux-gnu/bits/wordsize.h \ + /usr/include/i386-linux-gnu/gnu/stubs.h \ + /usr/include/i386-linux-gnu/gnu/stubs-32.h \ + /usr/include/i386-linux-gnu/bits/posix_opt.h \ + /usr/include/i386-linux-gnu/bits/environments.h \ + /usr/include/i386-linux-gnu/bits/types.h \ + /usr/include/i386-linux-gnu/bits/typesizes.h \ + /usr/include/i386-linux-gnu/bits/confname.h /usr/include/getopt.h \ + /usr/include/errno.h /usr/include/i386-linux-gnu/bits/errno.h \ + /usr/include/linux/errno.h /usr/include/i386-linux-gnu/asm/errno.h \ + /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h diff --git a/lib/ccan/read_write_all/read_write_all.h b/lib/ccan/read_write_all/read_write_all.h new file mode 100644 index 00000000000..aa2635aaf26 --- /dev/null +++ b/lib/ccan/read_write_all/read_write_all.h @@ -0,0 +1,10 @@ +/* Licensed under LGPLv2+ - see LICENSE file for details */ +#ifndef _CCAN_READ_WRITE_H +#define _CCAN_READ_WRITE_H +#include <stddef.h> +#include <stdbool.h> + +bool write_all(int fd, const void *data, size_t size); +bool read_all(int fd, void *data, size_t size); + +#endif /* _CCAN_READ_WRITE_H */ diff --git a/lib/ccan/read_write_all/test/run-read_all.c b/lib/ccan/read_write_all/test/run-read_all.c new file mode 100644 index 00000000000..29f81fc703e --- /dev/null +++ b/lib/ccan/read_write_all/test/run-read_all.c @@ -0,0 +1,76 @@ +/* FIXME: Do something tricky to ensure we really do loop in read_all. */ + +#include <ccan/read_write_all/read_write_all.h> +#include <ccan/read_write_all/read_write_all.c> +#include <ccan/tap/tap.h> +#include <unistd.h> +#include <sys/types.h> +#include <signal.h> +#include <sys/wait.h> +#include <limits.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> + +static volatile int sigcount; +static int p2c[2], c2p[2]; +static void got_signal(int sig) +{ + char c = 0; + if (write(p2c[1], &c, 1) == 1) + sigcount++; +} + +/* < PIPE_BUF *will* be atomic. But > PIPE_BUF only *might* be non-atomic. */ +#define BUFSZ (1024*1024) + +int main(int argc, char *argv[]) +{ + char *buffer; + char c = 0; + int status; + pid_t child; + + buffer = calloc(BUFSZ, 2); + plan_tests(6); + + /* We fork and torture parent. */ + if (pipe(p2c) != 0 || pipe(c2p) != 0) + err(1, "pipe"); + child = fork(); + + if (!child) { + close(p2c[1]); + close(c2p[0]); + /* Child. Make sure parent ready, then write in two parts. */ + if (read(p2c[0], &c, 1) != 1) + exit(1); + memset(buffer, 0xff, BUFSZ*2); + if (!write_all(c2p[1], buffer, BUFSZ)) + exit(2); + if (kill(getppid(), SIGUSR1) != 0) + exit(3); + /* Make sure they get signal. */ + if (read(p2c[0], &c, 1) != 1) + exit(4); + if (write(c2p[1], buffer, BUFSZ) != BUFSZ) + exit(5); + exit(0); + } + if (child == -1) + err(1, "forking"); + + close(p2c[0]); + close(c2p[1]); + signal(SIGUSR1, got_signal); + ok1(write(p2c[1], &c, 1) == 1); + ok1(read_all(c2p[0], buffer, BUFSZ*2)); + ok1(memchr(buffer, 0, BUFSZ*2) == NULL); + ok1(sigcount == 1); + ok1(wait(&status) == child); + ok(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "WIFEXITED(status) = %u, WEXITSTATUS(status) = %u", + WIFEXITED(status), WEXITSTATUS(status)); + free(buffer); + return exit_status(); +} diff --git a/lib/ccan/read_write_all/test/run-write_all.c b/lib/ccan/read_write_all/test/run-write_all.c new file mode 100644 index 00000000000..e2baf48dfe7 --- /dev/null +++ b/lib/ccan/read_write_all/test/run-write_all.c @@ -0,0 +1,68 @@ +#include <ccan/read_write_all/read_write_all.h> +#include <ccan/tap/tap.h> +#include <unistd.h> +#include <sys/types.h> +#include <signal.h> +#include <limits.h> +#include <sys/wait.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +static ssize_t test_write(int fd, const void *buf, size_t count); +#define write test_write +#include <ccan/read_write_all/read_write_all.c> +#undef write + +static ssize_t write_return; + +static ssize_t test_write(int fd, const void *buf, size_t count) +{ + if (write_return == 0) { + errno = ENOSPC; + return 0; + } + + if (write_return < 0) { + errno = -write_return; + /* Don't return EINTR more than once! */ + if (errno == EINTR) + write_return = count; + return -1; + } + + if (write_return < count) + return write_return; + return count; +} + +#define BUFSZ 1024 + +int main(int argc, char *argv[]) +{ + char *buffer; + + buffer = malloc(BUFSZ); + plan_tests(8); + + write_return = -ENOSPC; + ok1(!write_all(100, buffer, BUFSZ)); + ok1(errno == ENOSPC); + + write_return = -EINTR; + ok1(write_all(100, buffer, BUFSZ)); + ok1(errno == EINTR); + + write_return = 1; + errno = 0; + ok1(write_all(100, buffer, BUFSZ)); + ok1(errno == 0); + + write_return = BUFSZ; + ok1(write_all(100, buffer, BUFSZ)); + ok1(errno == 0); + free(buffer); + + return exit_status(); +} diff --git a/lib/ccan/tcon/_info b/lib/ccan/tcon/_info new file mode 100644 index 00000000000..02c0dd8addc --- /dev/null +++ b/lib/ccan/tcon/_info @@ -0,0 +1,74 @@ +#include "config.h" +#include <string.h> + +/** + * tcon - routines for creating typesafe generic containers + * + * This code lets users create a structure with a typecanary; your API + * is then a set of macros which check the type canary before calling + * the generic routines. + * + * Example: + * #include <ccan/tcon/tcon.h> + * #include <stdio.h> + * + * // A simple container class. Can only contain one thing though! + * struct container { + * void *contents; + * }; + * static inline void container_add_raw(struct container *c, void *p) + * { + * c->contents = p; + * } + * static inline void *container_get_raw(struct container *c) + * { + * return c->contents; + * } + * + * // This lets the user define their container type; includes a + * // "type canary" to check types against. + * #define DEFINE_TYPED_CONTAINER_STRUCT(name, type) \ + * struct name { struct container raw; TCON(type canary); } + * + * // These macros make sure the container type and pointer match. + * #define container_add(c, p) \ + * container_add_raw(&tcon_check((c), canary, (p))->raw, (p)) + * #define container_get(c) \ + * tcon_cast((c), canary, container_get_raw(&(c)->raw)) + * + * // Now, let's define two different containers. + * DEFINE_TYPED_CONTAINER_STRUCT(int_container, int *); + * DEFINE_TYPED_CONTAINER_STRUCT(string_container, char *); + * + * int main(int argc, char *argv[]) + * { + * struct int_container ic; + * struct string_container sc; + * + * // We would get a warning if we used the wrong types... + * container_add(&ic, &argc); + * container_add(&sc, argv[argc-1]); + * + * printf("Last arg is %s of %i arguments\n", + * container_get(&sc), *container_get(&ic) - 1); + * return 0; + * } + * // Given "foo" outputs "Last arg is foo of 1 arguments" + * // Given "foo bar" outputs "Last arg is bar of 2 arguments" + * + * License: Public domain + * + * Author: Rusty Russell <rusty@rustcorp.com.au> + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + return 0; + } + + return 1; +} diff --git a/lib/ccan/tcon/tcon.h b/lib/ccan/tcon/tcon.h new file mode 100644 index 00000000000..93c3ea6b9f9 --- /dev/null +++ b/lib/ccan/tcon/tcon.h @@ -0,0 +1,115 @@ +/* Placed into the public domain */ +#ifndef CCAN_TCON_H +#define CCAN_TCON_H +#include "config.h" + +/** + * TCON - declare a _tcon type containing canary variables. + * @decls: the semi-colon separated list of type canaries. + * + * This declares a _tcon member for a structure. It should be the + * last element in your structure; with sufficient compiler support it + * will not use any actual storage. tcon_check() will compare + * expressions with one of these "type canaries" to cause warnings if + * the container is misused. + * + * A type of "void *" will allow tcon_check() to pass on any (pointer) type. + * + * Example: + * // Simply typesafe linked list. + * struct list_head { + * struct list_head *prev, *next; + * }; + * + * struct string_list { + * struct list_head raw; + * TCON(char *canary); + * }; + * + * // More complex: mapping from one type to another. + * struct map { + * void *contents; + * }; + * + * struct int_to_string_map { + * struct map raw; + * TCON(char *charp_canary; int int_canary); + * }; + */ +#if HAVE_FLEXIBLE_ARRAY_MEMBER +#define TCON(decls) struct { decls; } _tcon[] +#else +#define TCON(decls) struct { decls; } _tcon[1] +#endif + +/** + * tcon_check - typecheck a typed container + * @x: the structure containing the TCON. + * @canary: which canary to check against. + * @expr: the expression whose type must match the TCON (not evaluated) + * + * This macro is used to check that the expression is the type + * expected for this structure (note the "useless" sizeof() argument + * which contains this comparison with the type canary). + * + * It evaluates to @x so you can chain it. + * + * Example: + * #define tlist_add(h, n, member) \ + * list_add(&tcon_check((h), canary, (n))->raw, &(n)->member) + */ +#define tcon_check(x, canary, expr) \ + (sizeof((x)->_tcon[0].canary == (expr)) ? (x) : (x)) + +/** + * tcon_check_ptr - typecheck a typed container + * @x: the structure containing the TCON. + * @canary: which canary to check against. + * @expr: the expression whose type must match &TCON (not evaluated) + * + * This macro is used to check that the expression is a pointer to the type + * expected for this structure (note the "useless" sizeof() argument + * which contains this comparison with the type canary), or NULL. + * + * It evaluates to @x so you can chain it. + */ +#define tcon_check_ptr(x, canary, expr) \ + (sizeof(&(x)->_tcon[0].canary == (expr)) ? (x) : (x)) + + +/** + * tcon_type - the type within a container (or void *) + * @x: the structure containing the TCON. + * @canary: which canary to check against. + */ +#if HAVE_TYPEOF +#define tcon_type(x, canary) __typeof__((x)->_tcon[0].canary) +#else +#define tcon_type(x, canary) void * +#endif + +/** + * tcon_ptr_type - pointer to the type within a container (or void *) + * @x: the structure containing the TCON. + * @canary: which canary to check against. + */ +#if HAVE_TYPEOF +#define tcon_ptr_type(x, canary) __typeof__(&(x)->_tcon[0].canary) +#else +#define tcon_ptr_type(x, canary) void * +#endif + +/** + * tcon_cast - cast to a canary type for this container (or void *) + * @x: a structure containing the TCON. + * @canary: which canary to cast to. + * @expr: the value to cast + * + * This is used to cast to the correct type for this container. If the + * platform doesn't HAVE_TYPEOF, then it casts to void * (which will + * cause a warning if the user doesn't expect a pointer type). + */ +#define tcon_cast(x, canary, expr) ((tcon_type((x), canary))(expr)) +#define tcon_cast_ptr(x, canary, expr) ((tcon_ptr_type((x), canary))(expr)) + +#endif /* CCAN_TCON_H */ diff --git a/lib/ccan/tcon/test/compile_fail-tcon_cast.c b/lib/ccan/tcon/test/compile_fail-tcon_cast.c new file mode 100644 index 00000000000..f80ef42c091 --- /dev/null +++ b/lib/ccan/tcon/test/compile_fail-tcon_cast.c @@ -0,0 +1,29 @@ +#include <ccan/tcon/tcon.h> +#include <stdlib.h> + +struct container { + void *p; +}; + +struct int_and_charp_container { + struct container raw; + TCON(int *tc1; char *tc2); +}; + +int main(int argc, char *argv[]) +{ + struct int_and_charp_container icon; +#ifdef FAIL +#if !HAVE_TYPEOF +#error We cannot detect type problems without HAVE_TYPEOF +#endif + char * +#else + int * +#endif + x; + + icon.raw.p = NULL; + x = tcon_cast(&icon, tc1, icon.raw.p); + return 0; +} diff --git a/lib/ccan/tcon/test/compile_fail.c b/lib/ccan/tcon/test/compile_fail.c new file mode 100644 index 00000000000..683bbd62ffd --- /dev/null +++ b/lib/ccan/tcon/test/compile_fail.c @@ -0,0 +1,25 @@ +#include <ccan/tcon/tcon.h> +#include <stdlib.h> + +struct container { + void *p; +}; + +struct int_container { + struct container raw; + TCON(int *canary); +}; + +int main(int argc, char *argv[]) +{ + struct int_container icon; +#ifdef FAIL + char * +#else + int * +#endif + x = NULL; + + tcon_check(&icon, canary, x)->raw.p = x; + return 0; +} diff --git a/lib/ccan/tcon/test/compile_ok-void.c b/lib/ccan/tcon/test/compile_ok-void.c new file mode 100644 index 00000000000..26b712f6b24 --- /dev/null +++ b/lib/ccan/tcon/test/compile_ok-void.c @@ -0,0 +1,21 @@ +#include <ccan/tcon/tcon.h> +#include <stdlib.h> + +struct container { + void *p; +}; + +struct void_container { + struct container raw; + TCON(void *canary); +}; + +int main(int argc, char *argv[]) +{ + struct void_container vcon; + + tcon_check(&vcon, canary, NULL)->raw.p = NULL; + tcon_check(&vcon, canary, argv[0])->raw.p = NULL; + tcon_check(&vcon, canary, main)->raw.p = NULL; + return 0; +} diff --git a/lib/ccan/tcon/test/compile_ok.c b/lib/ccan/tcon/test/compile_ok.c new file mode 100644 index 00000000000..447f0ee50a0 --- /dev/null +++ b/lib/ccan/tcon/test/compile_ok.c @@ -0,0 +1,27 @@ +#include <ccan/tcon/tcon.h> +#include <stdlib.h> + +struct container { + void *p; +}; + +struct int_container { + struct container raw; + TCON(int tc); +}; + +struct charp_and_int_container { + struct container raw; + TCON(int tc1; char *tc2); +}; + +int main(int argc, char *argv[]) +{ + struct int_container icon; + struct charp_and_int_container cicon; + + tcon_check(&icon, tc, 7)->raw.p = NULL; + tcon_check(&cicon, tc1, 7)->raw.p = argv[0]; + tcon_check(&cicon, tc2, argv[0])->raw.p = argv[0]; + return 0; +} diff --git a/lib/ccan/time/LICENSE b/lib/ccan/time/LICENSE new file mode 100644 index 00000000000..89de354795e --- /dev/null +++ b/lib/ccan/time/LICENSE @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/ccan/time/_info b/lib/ccan/time/_info new file mode 100644 index 00000000000..fdad27561a1 --- /dev/null +++ b/lib/ccan/time/_info @@ -0,0 +1,47 @@ +#include <string.h> +#include "config.h" + +/** + * time - routines for dealing with time + * + * This code provides convenient functions for working with time. + * + * Author: Rusty Russell <rusty@rustcorp.com.au> + * License: BSD-MIT + * + * Example: + * #include <ccan/time/time.h> + * #include <stdlib.h> + * #include <stdio.h> + * #include <err.h> + * + * int main(int argc, char *argv[]) + * { + * struct timeval t; + * + * if (argc != 2) + * errx(1, "Usage: %s <diff in millisec>", argv[0]); + * + * t = time_now(); + * if (argv[1][0] == '-') + * t = time_sub(t, time_from_msec(atol(argv[1]+1))); + * else + * t = time_add(t, time_from_msec(atol(argv[1]))); + * + * printf("%lu.%06u\n", + * (unsigned long)t.tv_sec, (unsigned)t.tv_usec); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + return 0; + } + + return 1; +} diff --git a/lib/ccan/time/test/run.c b/lib/ccan/time/test/run.c new file mode 100644 index 00000000000..80391694cd9 --- /dev/null +++ b/lib/ccan/time/test/run.c @@ -0,0 +1,111 @@ +#include <ccan/time/time.h> +#include <ccan/time/time.c> +#include <ccan/tap/tap.h> + +int main(void) +{ + struct timeval t1, t2, t3, zero = { 0, 0 }; + + plan_tests(46); + + /* Test time_now */ + t1 = time_now(); + t2 = time_now(); + + /* Test time_sub. */ + t3 = time_sub(t2, t1); + ok1(t3.tv_sec > 0 || t3.tv_usec >= 0); + t3 = time_sub(t2, t2); + ok1(t3.tv_sec == 0 && t3.tv_usec == 0); + t3 = time_sub(t1, t1); + ok1(t3.tv_sec == 0 && t3.tv_usec == 0); + + /* Test time_eq */ + ok1(time_eq(t1, t1)); + ok1(time_eq(t2, t2)); + ok1(!time_eq(t1, t3)); + ok1(!time_eq(t2, t3)); + + /* Make sure t2 > t1. */ + t3.tv_sec = 0; + t3.tv_usec = 1; + t2 = time_add(t2, t3); + + /* Test time_less and time_greater. */ + ok1(!time_eq(t1, t2)); + ok1(!time_greater(t1, t2)); + ok1(time_less(t1, t2)); + ok1(time_greater(t2, t1)); + ok1(!time_less(t2, t1)); + t3.tv_sec = 0; + t3.tv_usec = 999999; + t2 = time_add(t2, t3); + ok1(!time_eq(t1, t2)); + ok1(!time_greater(t1, t2)); + ok1(time_less(t1, t2)); + ok1(time_greater(t2, t1)); + ok1(!time_less(t2, t1)); + + t3 = time_sub(t2, zero); + ok1(time_eq(t3, t2)); + t3 = time_sub(t2, t2); + ok1(time_eq(t3, zero)); + + /* time_from_msec / time_to_msec */ + t3 = time_from_msec(500); + ok1(t3.tv_sec == 0); + ok1(t3.tv_usec == 500000); + ok1(time_to_msec(t3) == 500); + + t3 = time_from_msec(1000); + ok1(t3.tv_sec == 1); + ok1(t3.tv_usec == 0); + ok1(time_to_msec(t3) == 1000); + + t3 = time_from_msec(1500); + ok1(t3.tv_sec == 1); + ok1(t3.tv_usec == 500000); + ok1(time_to_msec(t3) == 1500); + + /* time_from_usec */ + t3 = time_from_usec(500000); + ok1(t3.tv_sec == 0); + ok1(t3.tv_usec == 500000); + ok1(time_to_usec(t3) == 500000); + + t3 = time_from_usec(1000000); + ok1(t3.tv_sec == 1); + ok1(t3.tv_usec == 0); + ok1(time_to_usec(t3) == 1000000); + + t3 = time_from_usec(1500000); + ok1(t3.tv_sec == 1); + ok1(t3.tv_usec == 500000); + ok1(time_to_usec(t3) == 1500000); + + /* Test wrapunder */ + t3 = time_sub(time_sub(t2, time_from_msec(500)), time_from_msec(500)); + ok1(t3.tv_sec == t2.tv_sec - 1); + ok1(t3.tv_usec == t2.tv_usec); + + /* time_divide and time_multiply */ + t1.tv_usec = 100; + t1.tv_sec = 100; + + t3 = time_divide(t1, 2); + ok1(t3.tv_sec == 50); + ok1(t3.tv_usec == 50); + + t3 = time_divide(t1, 100); + ok1(t3.tv_sec == 1); + ok1(t3.tv_usec == 1); + + t3 = time_multiply(t3, 100); + ok1(time_eq(t3, t1)); + + t3 = time_divide(t1, 200); + ok1(t3.tv_sec == 0); + ok1(t3.tv_usec == 500000); + + return exit_status(); +} diff --git a/lib/ccan/time/time.c b/lib/ccan/time/time.c new file mode 100644 index 00000000000..5e36bf7b48d --- /dev/null +++ b/lib/ccan/time/time.c @@ -0,0 +1,108 @@ +/* Licensed under BSD-MIT - see LICENSE file for details */ +#include <ccan/time/time.h> +#include <stdlib.h> +#include <assert.h> + +struct timeval time_now(void) +{ + struct timeval now; + gettimeofday(&now, NULL); + return now; +} + +bool time_greater(struct timeval a, struct timeval b) +{ + if (a.tv_sec > b.tv_sec) + return true; + else if (a.tv_sec < b.tv_sec) + return false; + + return a.tv_usec > b.tv_usec; +} + +bool time_less(struct timeval a, struct timeval b) +{ + if (a.tv_sec < b.tv_sec) + return true; + else if (a.tv_sec > b.tv_sec) + return false; + + return a.tv_usec < b.tv_usec; +} + +bool time_eq(struct timeval a, struct timeval b) +{ + return a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec; +} + +struct timeval time_sub(struct timeval recent, struct timeval old) +{ + struct timeval diff; + + diff.tv_sec = recent.tv_sec - old.tv_sec; + if (old.tv_usec > recent.tv_usec) { + diff.tv_sec--; + diff.tv_usec = 1000000 + recent.tv_usec - old.tv_usec; + } else + diff.tv_usec = recent.tv_usec - old.tv_usec; + + assert(diff.tv_sec >= 0); + return diff; +} + +struct timeval time_add(struct timeval a, struct timeval b) +{ + struct timeval sum; + + sum.tv_sec = a.tv_sec + b.tv_sec; + sum.tv_usec = a.tv_usec + b.tv_usec; + if (sum.tv_usec > 1000000) { + sum.tv_sec++; + sum.tv_usec -= 1000000; + } + return sum; +} + +struct timeval time_divide(struct timeval t, unsigned long div) +{ + return time_from_usec(time_to_usec(t) / div); +} + +struct timeval time_multiply(struct timeval t, unsigned long mult) +{ + return time_from_usec(time_to_usec(t) * mult); +} + +uint64_t time_to_msec(struct timeval t) +{ + uint64_t msec; + + msec = t.tv_usec / 1000 + (uint64_t)t.tv_sec * 1000; + return msec; +} + +uint64_t time_to_usec(struct timeval t) +{ + uint64_t usec; + + usec = t.tv_usec + (uint64_t)t.tv_sec * 1000000; + return usec; +} + +struct timeval time_from_msec(uint64_t msec) +{ + struct timeval t; + + t.tv_usec = (msec % 1000) * 1000; + t.tv_sec = msec / 1000; + return t; +} + +struct timeval time_from_usec(uint64_t usec) +{ + struct timeval t; + + t.tv_usec = usec % 1000000; + t.tv_sec = usec / 1000000; + return t; +} diff --git a/lib/ccan/time/time.d b/lib/ccan/time/time.d new file mode 100644 index 00000000000..d61a6265ea6 --- /dev/null +++ b/lib/ccan/time/time.d @@ -0,0 +1,25 @@ +ccan/time/time.o: ccan/time/time.c ccan/time/time.h config.h \ + /usr/include/i386-linux-gnu/sys/time.h /usr/include/features.h \ + /usr/include/i386-linux-gnu/bits/predefs.h \ + /usr/include/i386-linux-gnu/sys/cdefs.h \ + /usr/include/i386-linux-gnu/bits/wordsize.h \ + /usr/include/i386-linux-gnu/gnu/stubs.h \ + /usr/include/i386-linux-gnu/gnu/stubs-32.h \ + /usr/include/i386-linux-gnu/bits/types.h \ + /usr/include/i386-linux-gnu/bits/typesizes.h /usr/include/time.h \ + /usr/include/i386-linux-gnu/bits/time.h \ + /usr/include/i386-linux-gnu/sys/select.h \ + /usr/include/i386-linux-gnu/bits/select.h \ + /usr/include/i386-linux-gnu/bits/sigset.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdint.h /usr/include/stdint.h \ + /usr/include/i386-linux-gnu/bits/wchar.h \ + /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stdbool.h \ + /usr/include/stdlib.h /usr/lib/gcc/i686-linux-gnu/4.5.4/include/stddef.h \ + /usr/include/i386-linux-gnu/bits/waitflags.h \ + /usr/include/i386-linux-gnu/bits/waitstatus.h /usr/include/endian.h \ + /usr/include/i386-linux-gnu/bits/endian.h \ + /usr/include/i386-linux-gnu/bits/byteswap.h /usr/include/xlocale.h \ + /usr/include/i386-linux-gnu/sys/types.h \ + /usr/include/i386-linux-gnu/sys/sysmacros.h \ + /usr/include/i386-linux-gnu/bits/pthreadtypes.h /usr/include/alloca.h \ + /usr/include/assert.h diff --git a/lib/ccan/time/time.h b/lib/ccan/time/time.h new file mode 100644 index 00000000000..fb2ee459d69 --- /dev/null +++ b/lib/ccan/time/time.h @@ -0,0 +1,184 @@ +/* Licensed under BSD-MIT - see LICENSE file for details */ +#ifndef CCAN_TIME_H +#define CCAN_TIME_H +#include "config.h" +#include <sys/time.h> +#include <stdint.h> +#include <stdbool.h> + +/** + * time_now - return the current time + * + * Example: + * printf("Now is %lu seconds since epoch\n", (long)time_now().tv_sec); + */ +struct timeval time_now(void); + +/** + * time_greater - is a after b? + * @a: one time. + * @b: another time. + * + * Example: + * static bool timed_out(const struct timeval *start) + * { + * #define TIMEOUT time_from_msec(1000) + * return time_greater(time_now(), time_add(*start, TIMEOUT)); + * } + */ +bool time_greater(struct timeval a, struct timeval b); + +/** + * time_less - is a before b? + * @a: one time. + * @b: another time. + * + * Example: + * static bool still_valid(const struct timeval *start) + * { + * #define TIMEOUT time_from_msec(1000) + * return time_less(time_now(), time_add(*start, TIMEOUT)); + * } + */ +bool time_less(struct timeval a, struct timeval b); + +/** + * time_eq - is a equal to b? + * @a: one time. + * @b: another time. + * + * Example: + * #include <sys/types.h> + * #include <sys/wait.h> + * + * // Can we fork in under a microsecond? + * static bool fast_fork(void) + * { + * struct timeval start = time_now(); + * if (fork() != 0) { + * exit(0); + * } + * wait(NULL); + * return time_eq(start, time_now()); + * } + */ +bool time_eq(struct timeval a, struct timeval b); + +/** + * time_sub - subtract two times + * @recent: the larger (more recent) time. + * @old: the smaller (less recent) time. + * + * This returns a well formed struct timeval. + * + * Example: + * static bool was_recent(const struct timeval *start) + * { + * return time_sub(time_now(), *start).tv_sec < 1; + * } + */ +struct timeval time_sub(struct timeval recent, struct timeval old); + +/** + * time_add - add two times + * @a: one time. + * @b: another time. + * + * The times must not overflow, or the results are undefined. + * + * Example: + * // We do one every second. + * static struct timeval next_time(void) + * { + * return time_add(time_now(), time_from_msec(1000)); + * } + */ +struct timeval time_add(struct timeval a, struct timeval b); + +/** + * time_divide - divide a time by a value. + * @t: a time. + * @div: number to divide it by. + * + * Example: + * // How long does it take to do a fork? + * static struct timeval forking_time(void) + * { + * struct timeval start = time_now(); + * unsigned int i; + * + * for (i = 0; i < 1000; i++) { + * if (fork() != 0) { + * exit(0); + * } + * wait(NULL); + * } + * return time_divide(time_sub(time_now(), start), i); + * } + */ +struct timeval time_divide(struct timeval t, unsigned long div); + +/** + * time_multiply - multiply a time by a value. + * @t: a time. + * @mult: number to multiply it by. + * + * Example: + * ... + * printf("Time to do 100000 forks would be %u sec\n", + * (unsigned)time_multiply(forking_time(), 1000000).tv_sec); + */ +struct timeval time_multiply(struct timeval t, unsigned long mult); + +/** + * time_to_msec - return number of milliseconds + * @t: a time + * + * It's often more convenient to deal with time values as + * milliseconds. Note that this will fit into a 32-bit variable if + * it's a time difference of less than ~7 weeks. + * + * Example: + * ... + * printf("Forking time is %u msec\n", + * (unsigned)time_to_msec(forking_time())); + */ +uint64_t time_to_msec(struct timeval t); + +/** + * time_to_usec - return number of microseconds + * @t: a time + * + * It's often more convenient to deal with time values as + * microseconds. Note that this will fit into a 32-bit variable if + * it's a time difference of less than ~1 hour. + * + * Example: + * ... + * printf("Forking time is %u usec\n", + * (unsigned)time_to_usec(forking_time())); + * + */ +uint64_t time_to_usec(struct timeval t); + +/** + * time_from_msec - convert milliseconds to a timeval + * @msec: time in milliseconds + * + * Example: + * // 1/2 second timeout + * #define TIMEOUT time_from_msec(500) + */ +struct timeval time_from_msec(uint64_t msec); + +/** + * time_from_usec - convert microseconds to a timeval + * @usec: time in microseconds + * + * Example: + * // 1/2 second timeout + * #define TIMEOUT time_from_usec(500000) + */ +struct timeval time_from_usec(uint64_t usec); + +#endif /* CCAN_TIME_H */ diff --git a/lib/ccan/tlist/LICENSE b/lib/ccan/tlist/LICENSE new file mode 100644 index 00000000000..cca7fc278f5 --- /dev/null +++ b/lib/ccan/tlist/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/lib/ccan/tlist/_info b/lib/ccan/tlist/_info new file mode 100644 index 00000000000..e18e2efb23b --- /dev/null +++ b/lib/ccan/tlist/_info @@ -0,0 +1,74 @@ +#include <stdio.h> +#include <string.h> +#include "config.h" + +/** + * tlist - typesafe double linked list routines + * + * The list header contains routines for manipulating double linked lists; + * this extends it so you can create list head types which only accomodate + * a specific entry type. + * + * Example: + * #include <err.h> + * #include <stdio.h> + * #include <stdlib.h> + * #include <ccan/tlist/tlist.h> + * + * // We could use TLIST_TYPE(children, struct child) to define this. + * struct tlist_children { + * struct list_head raw; + * TCON(struct child *canary); + * }; + * struct parent { + * const char *name; + * struct tlist_children children; + * unsigned int num_children; + * }; + * + * struct child { + * const char *name; + * struct list_node list; + * }; + * + * int main(int argc, char *argv[]) + * { + * struct parent p; + * struct child *c; + * unsigned int i; + * + * if (argc < 2) + * errx(1, "Usage: %s parent children...", argv[0]); + * + * p.name = argv[1]; + * tlist_init(&p.children); + * for (i = 2; i < argc; i++) { + * c = malloc(sizeof(*c)); + * c->name = argv[i]; + * tlist_add(&p.children, c, list); + * p.num_children++; + * } + * + * printf("%s has %u children:", p.name, p.num_children); + * tlist_for_each(&p.children, c, list) + * printf("%s ", c->name); + * printf("\n"); + * return 0; + * } + * + * License: LGPL + * Author: Rusty Russell <rusty@rustcorp.com.au> + */ +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/list\n"); + printf("ccan/tcon\n"); + return 0; + } + + return 1; +} diff --git a/lib/ccan/tlist/test/compile_fail-tlist_add.c b/lib/ccan/tlist/test/compile_fail-tlist_add.c new file mode 100644 index 00000000000..1b87bfd1193 --- /dev/null +++ b/lib/ccan/tlist/test/compile_fail-tlist_add.c @@ -0,0 +1,35 @@ +#include <ccan/tlist/tlist.h> + +TLIST_TYPE(children, struct child); +TLIST_TYPE(cousins, struct cousin); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist_children children; + struct tlist_cousins cousins; + struct child child = { "child" }; + struct cousin cousin = { "cousin" }; + + tlist_init(&children); + tlist_init(&cousins); + tlist_add(&children, &child, list); + tlist_add(&cousins, &cousin, list); + tlist_del_from(&cousins, &cousin, list); +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + tlist_add(&children, &cousin, list); +#endif + return 0; +} diff --git a/lib/ccan/tlist/test/compile_fail-tlist_add_tail.c b/lib/ccan/tlist/test/compile_fail-tlist_add_tail.c new file mode 100644 index 00000000000..33dff3d8eb0 --- /dev/null +++ b/lib/ccan/tlist/test/compile_fail-tlist_add_tail.c @@ -0,0 +1,35 @@ +#include <ccan/tlist/tlist.h> + +TLIST_TYPE(children, struct child); +TLIST_TYPE(cousins, struct cousin); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist_children children; + struct tlist_cousins cousins; + struct child child = { "child" }; + struct cousin cousin = { "cousin" }; + + tlist_init(&children); + tlist_init(&cousins); + tlist_add(&children, &child, list); + tlist_add(&cousins, &cousin, list); + tlist_del_from(&cousins, &cousin, list); +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + tlist_add_tail(&children, &cousin, list); +#endif + return 0; +} diff --git a/lib/ccan/tlist/test/compile_fail-tlist_del_from.c b/lib/ccan/tlist/test/compile_fail-tlist_del_from.c new file mode 100644 index 00000000000..d06a72fbfa1 --- /dev/null +++ b/lib/ccan/tlist/test/compile_fail-tlist_del_from.c @@ -0,0 +1,34 @@ +#include <ccan/tlist/tlist.h> + +TLIST_TYPE(children, struct child); +TLIST_TYPE(cousins, struct cousin); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist_children children; + struct tlist_cousins cousins; + struct child child = { "child" }; + struct cousin cousin = { "cousin" }; + + tlist_init(&children); + tlist_init(&cousins); + tlist_add(&children, &child, list); + tlist_add(&cousins, &cousin, list); +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + tlist_del_from(&children, &cousin, list); +#endif + return 0; +} diff --git a/lib/ccan/tlist/test/compile_fail-tlist_for_each.c b/lib/ccan/tlist/test/compile_fail-tlist_for_each.c new file mode 100644 index 00000000000..1b2fb6882fe --- /dev/null +++ b/lib/ccan/tlist/test/compile_fail-tlist_for_each.c @@ -0,0 +1,34 @@ +#include <ccan/tlist/tlist.h> + +TLIST_TYPE(children, struct child); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist_children children; + struct child child = { "child" }; +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + struct cousin *c; +#else + struct child *c; +#endif + + tlist_init(&children); + tlist_add(&children, &child, list); + + tlist_for_each(&children, c, list) + (void) c; /* Suppress unused-but-set-variable warning. */ + return 0; +} diff --git a/lib/ccan/tlist/test/compile_fail-tlist_for_each_safe.c b/lib/ccan/tlist/test/compile_fail-tlist_for_each_safe.c new file mode 100644 index 00000000000..651c6cefd6d --- /dev/null +++ b/lib/ccan/tlist/test/compile_fail-tlist_for_each_safe.c @@ -0,0 +1,33 @@ +#include <ccan/tlist/tlist.h> + +TLIST_TYPE(children, struct child); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist_children children; + struct child child = { "child" }; +#ifdef FAIL +#if !HAVE_FLEXIBLE_ARRAY_MEMBER +#error Need flexible array members to check type +#endif + struct cousin *c, *n; +#else + struct child *c, *n; +#endif + + tlist_init(&children); + tlist_add(&children, &child, list); + + tlist_for_each_safe(&children, c, n, list); + return 0; +} diff --git a/lib/ccan/tlist/test/compile_fail-tlist_tail.c b/lib/ccan/tlist/test/compile_fail-tlist_tail.c new file mode 100644 index 00000000000..48f394446e0 --- /dev/null +++ b/lib/ccan/tlist/test/compile_fail-tlist_tail.c @@ -0,0 +1,31 @@ +#include <ccan/tlist/tlist.h> + +TLIST_TYPE(children, struct child); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist_children children; + struct child child = { "child" }; +#ifdef FAIL + struct cousin *c; +#else + struct child *c; +#endif + + tlist_init(&children); + tlist_add(&children, &child, list); + + c = tlist_tail(&children, list); + (void) c; /* Suppress unused-but-set-variable warning. */ + return 0; +} diff --git a/lib/ccan/tlist/test/compile_fail-tlist_top.c b/lib/ccan/tlist/test/compile_fail-tlist_top.c new file mode 100644 index 00000000000..21651400ef0 --- /dev/null +++ b/lib/ccan/tlist/test/compile_fail-tlist_top.c @@ -0,0 +1,31 @@ +#include <ccan/tlist/tlist.h> + +TLIST_TYPE(children, struct child); + +struct child { + const char *name; + struct list_node list; +}; + +struct cousin { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct tlist_children children; + struct child child = { "child" }; +#ifdef FAIL + struct cousin *c; +#else + struct child *c; +#endif + + tlist_init(&children); + tlist_add(&children, &child, list); + + c = tlist_top(&children, list); + (void) c; /* Suppress unused-but-set-variable warning. */ + return 0; +} diff --git a/lib/ccan/tlist/test/run.c b/lib/ccan/tlist/test/run.c new file mode 100644 index 00000000000..95b02ebe219 --- /dev/null +++ b/lib/ccan/tlist/test/run.c @@ -0,0 +1,147 @@ +#define CCAN_LIST_DEBUG 1 +#include <ccan/tlist/tlist.h> +#include <ccan/tap/tap.h> + +TLIST_TYPE(children, struct child); + +struct parent { + const char *name; + struct tlist_children children; + unsigned int num_children; +}; + +struct child { + const char *name; + struct list_node list; +}; + +int main(int argc, char *argv[]) +{ + struct parent parent; + struct child c1, c2, c3, *c, *n; + unsigned int i; + struct tlist_children tlist = TLIST_INIT(tlist); + + plan_tests(48); + /* Test TLIST_INIT, and tlist_empty */ + ok1(tlist_empty(&tlist)); + ok1(tlist_check(&tlist, NULL)); + + parent.num_children = 0; + tlist_init(&parent.children); + /* Test tlist_init */ + ok1(tlist_empty(&parent.children)); + ok1(tlist_check(&parent.children, NULL)); + + c2.name = "c2"; + tlist_add(&parent.children, &c2, list); + /* Test tlist_add and !tlist_empty. */ + ok1(!tlist_empty(&parent.children)); + ok1(c2.list.next == &parent.children.raw.n); + ok1(c2.list.prev == &parent.children.raw.n); + ok1(parent.children.raw.n.next == &c2.list); + ok1(parent.children.raw.n.prev == &c2.list); + /* Test tlist_check */ + ok1(tlist_check(&parent.children, NULL)); + + c1.name = "c1"; + tlist_add(&parent.children, &c1, list); + /* Test list_add and !list_empty. */ + ok1(!tlist_empty(&parent.children)); + ok1(c2.list.next == &parent.children.raw.n); + ok1(c2.list.prev == &c1.list); + ok1(parent.children.raw.n.next == &c1.list); + ok1(parent.children.raw.n.prev == &c2.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.raw.n); + /* Test tlist_check */ + ok1(tlist_check(&parent.children, NULL)); + + c3.name = "c3"; + tlist_add_tail(&parent.children, &c3, list); + /* Test list_add_tail and !list_empty. */ + ok1(!tlist_empty(&parent.children)); + ok1(parent.children.raw.n.next == &c1.list); + ok1(parent.children.raw.n.prev == &c3.list); + ok1(c1.list.next == &c2.list); + ok1(c1.list.prev == &parent.children.raw.n); + ok1(c2.list.next == &c3.list); + ok1(c2.list.prev == &c1.list); + ok1(c3.list.next == &parent.children.raw.n); + ok1(c3.list.prev == &c2.list); + /* Test tlist_check */ + ok1(tlist_check(&parent.children, NULL)); + + /* Test tlist_top */ + ok1(tlist_top(&parent.children, list) == &c1); + + /* Test list_tail */ + ok1(tlist_tail(&parent.children, list) == &c3); + + /* Test tlist_for_each. */ + i = 0; + tlist_for_each(&parent.children, c, list) { + switch (i++) { + case 0: + ok1(c == &c1); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c3); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test tlist_for_each_rev. */ + i = 0; + tlist_for_each_rev(&parent.children, c, list) { + switch (i++) { + case 0: + ok1(c == &c3); + break; + case 1: + ok1(c == &c2); + break; + case 2: + ok1(c == &c1); + break; + } + if (i > 2) + break; + } + ok1(i == 3); + + /* Test tlist_for_each_safe, tlist_del and tlist_del_from. */ + i = 0; + tlist_for_each_safe(&parent.children, c, n, list) { + switch (i++) { + case 0: + ok1(c == &c1); + tlist_del(c, list); + break; + case 1: + ok1(c == &c2); + tlist_del_from(&parent.children, c, list); + break; + case 2: + ok1(c == &c3); + tlist_del_from(&parent.children, c, list); + break; + } + ok1(tlist_check(&parent.children, NULL)); + if (i > 2) + break; + } + ok1(i == 3); + ok1(tlist_empty(&parent.children)); + + /* Test list_top/list_tail on empty list. */ + ok1(tlist_top(&parent.children, list) == (struct child *)NULL); + ok1(tlist_tail(&parent.children, list) == (struct child *)NULL); + return exit_status(); +} diff --git a/lib/ccan/tlist/tlist.h b/lib/ccan/tlist/tlist.h new file mode 100644 index 00000000000..1ce0b85ed9f --- /dev/null +++ b/lib/ccan/tlist/tlist.h @@ -0,0 +1,265 @@ +/* Licensed under LGPL - see LICENSE file for details */ +#ifndef CCAN_TLIST_H +#define CCAN_TLIST_H +#include <ccan/list/list.h> +#include <ccan/tcon/tcon.h> + +/** + * TLIST_TYPE - declare a typed list type (struct tlist) + * @suffix: the name to use (struct tlist_@suffix) + * @type: the type the list will contain (void for any type) + * + * This declares a structure "struct tlist_@suffix" to use for + * lists containing this type. The actual list can be accessed using + * ".raw" or tlist_raw(). + * + * Example: + * // Defines struct tlist_children + * TLIST_TYPE(children, struct child); + * struct parent { + * const char *name; + * struct tlist_children children; + * unsigned int num_children; + * }; + * + * struct child { + * const char *name; + * struct list_node list; + * }; + */ +#define TLIST_TYPE(suffix, type) \ + struct tlist_##suffix { \ + struct list_head raw; \ + TCON(type *canary); \ + } + +/** + * TLIST_INIT - initalizer for an empty tlist + * @name: the name of the list. + * + * Explicit initializer for an empty list. + * + * See also: + * tlist_init() + * + * Example: + * static struct tlist_children my_list = TLIST_INIT(my_list); + */ +#define TLIST_INIT(name) { LIST_HEAD_INIT(name.raw) } + +/** + * tlist_check - check head of a list for consistency + * @h: the tlist_head + * @abortstr: the location to print on aborting, or NULL. + * + * Because list_nodes have redundant information, consistency checking between + * the back and forward links can be done. This is useful as a debugging check. + * If @abortstr is non-NULL, that will be printed in a diagnostic if the list + * is inconsistent, and the function will abort. + * + * Returns non-NULL if the list is consistent, NULL otherwise (it + * can never return NULL if @abortstr is set). + * + * See also: list_check() + * + * Example: + * static void dump_parent(struct parent *p) + * { + * struct child *c; + * + * printf("%s (%u children):\n", p->name, p->num_children); + * tlist_check(&p->children, "bad child list"); + * tlist_for_each(&p->children, c, list) + * printf(" -> %s\n", c->name); + * } + */ +#define tlist_check(h, abortstr) \ + list_check(&(h)->raw, (abortstr)) + +/** + * tlist_init - initialize a tlist + * @h: the tlist to set to the empty list + * + * Example: + * ... + * struct parent *parent = malloc(sizeof(*parent)); + * + * tlist_init(&parent->children); + * parent->num_children = 0; + */ +#define tlist_init(h) list_head_init(&(h)->raw) + +/** + * tlist_raw - unwrap the typed list and check the type + * @h: the tlist + * @expr: the expression to check the type against (not evaluated) + * + * This macro usually causes the compiler to emit a warning if the + * variable is of an unexpected type. It is used internally where we + * need to access the raw underlying list. + */ +#define tlist_raw(h, expr) (&tcon_check((h), canary, (expr))->raw) + +/** + * tlist_add - add an entry at the start of a linked list. + * @h: the tlist to add the node to + * @n: the entry to add to the list. + * @member: the member of n to add to the list. + * + * The entry's list_node does not need to be initialized; it will be + * overwritten. + * Example: + * struct child *child = malloc(sizeof(*child)); + * + * child->name = "marvin"; + * tlist_add(&parent->children, child, list); + * parent->num_children++; + */ +#define tlist_add(h, n, member) list_add(tlist_raw((h), (n)), &(n)->member) + +/** + * tlist_add_tail - add an entry at the end of a linked list. + * @h: the tlist to add the node to + * @n: the entry to add to the list. + * @member: the member of n to add to the list. + * + * The list_node does not need to be initialized; it will be overwritten. + * Example: + * tlist_add_tail(&parent->children, child, list); + * parent->num_children++; + */ +#define tlist_add_tail(h, n, member) \ + list_add_tail(tlist_raw((h), (n)), &(n)->member) + +/** + * tlist_del_from - delete an entry from a linked list. + * @h: the tlist @n is in + * @n: the entry to delete + * @member: the member of n to remove from the list. + * + * This explicitly indicates which list a node is expected to be in, + * which is better documentation and can catch more bugs. + * + * Note that this leaves @n->@member in an undefined state; it + * can be added to another list, but not deleted again. + * + * See also: tlist_del() + * + * Example: + * tlist_del_from(&parent->children, child, list); + * parent->num_children--; + */ +#define tlist_del_from(h, n, member) \ + list_del_from(tlist_raw((h), (n)), &(n)->member) + +/** + * tlist_del - delete an entry from an unknown linked list. + * @n: the entry to delete from the list. + * @member: the member of @n which is in the list. + * + * Example: + * tlist_del(child, list); + * parent->num_children--; + */ +#define tlist_del(n, member) \ + list_del(&(n)->member) + +/** + * tlist_empty - is a list empty? + * @h: the tlist + * + * If the list is empty, returns true. + * + * Example: + * assert(tlist_empty(&parent->children) == (parent->num_children == 0)); + */ +#define tlist_empty(h) list_empty(&(h)->raw) + +/** + * tlist_top - get the first entry in a list + * @h: the tlist + * @member: the list_node member of the type + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *first; + * first = tlist_top(&parent->children, list); + */ +#define tlist_top(h, member) \ + ((tcon_type((h), canary)) \ + list_top_(&(h)->raw, \ + (char *)(&(h)->_tcon[0].canary->member) - \ + (char *)((h)->_tcon[0].canary))) + +/** + * tlist_tail - get the last entry in a list + * @h: the tlist + * @member: the list_node member of the type + * + * If the list is empty, returns NULL. + * + * Example: + * struct child *last; + * last = tlist_tail(&parent->children, list); + */ +#define tlist_tail(h, member) \ + ((tcon_type((h), canary)) \ + list_tail_(&(h)->raw, \ + (char *)(&(h)->_tcon[0].canary->member) - \ + (char *)((h)->_tcon[0].canary))) + +/** + * tlist_for_each - iterate through a list. + * @h: the tlist + * @i: an iterator of suitable type for this list. + * @member: the list_node member of @i + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. + * + * Example: + * tlist_for_each(&parent->children, child, list) + * printf("Name: %s\n", child->name); + */ +#define tlist_for_each(h, i, member) \ + list_for_each(tlist_raw((h), (i)), (i), member) + +/** + * tlist_for_each - iterate through a list backwards. + * @h: the tlist + * @i: an iterator of suitable type for this list. + * @member: the list_node member of @i + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. + * + * Example: + * tlist_for_each_rev(&parent->children, child, list) + * printf("Name: %s\n", child->name); + */ +#define tlist_for_each_rev(h, i, member) \ + list_for_each_rev(tlist_raw((h), (i)), (i), member) + +/** + * tlist_for_each_safe - iterate through a list, maybe during deletion + * @h: the tlist + * @i: an iterator of suitable type for this list. + * @nxt: another iterator to store the next entry. + * @member: the list_node member of the structure + * + * This is a convenient wrapper to iterate @i over the entire list. It's + * a for loop, so you can break and continue as normal. The extra variable + * @nxt is used to hold the next element, so you can delete @i from the list. + * + * Example: + * struct child *next; + * tlist_for_each_safe(&parent->children, child, next, list) { + * tlist_del(child, list); + * parent->num_children--; + * } + */ +#define tlist_for_each_safe(h, i, nxt, member) \ + list_for_each_safe(tlist_raw((h), (i)), (i), (nxt), member) + +#endif /* CCAN_TLIST_H */ |