mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-24 09:34:24 +01:00
ccan: update, new modules.
ccan/autodata, ccan/breakpoint, ccan/crypto/hmac_sha256, ccan/crypto/hkdf_sha256, ccan/fdpass and ccan/io/fdpass. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
27
Makefile
27
Makefile
@@ -53,16 +53,22 @@ CORE_SRC := \
|
||||
CORE_OBJS := $(CORE_SRC:.c=.o)
|
||||
|
||||
CCAN_OBJS := \
|
||||
ccan-asort.o \
|
||||
ccan-autodata.o \
|
||||
ccan-breakpoint.o \
|
||||
ccan-crypto-hmac.o \
|
||||
ccan-crypto-hkdf.o \
|
||||
ccan-crypto-ripemd160.o \
|
||||
ccan-crypto-sha256.o \
|
||||
ccan-crypto-shachain.o \
|
||||
ccan-asort.o \
|
||||
ccan-crypto-siphash24.o \
|
||||
ccan-err.o \
|
||||
ccan-fdpass.o \
|
||||
ccan-htable.o \
|
||||
ccan-ilog.o \
|
||||
ccan-io-io.o \
|
||||
ccan-io-poll.o \
|
||||
ccan-io-fdpass.o \
|
||||
ccan-isaac.o \
|
||||
ccan-isaac64.o \
|
||||
ccan-list.o \
|
||||
@@ -89,6 +95,8 @@ CCAN_HEADERS := \
|
||||
$(CCANDIR)/ccan/alignof/alignof.h \
|
||||
$(CCANDIR)/ccan/array_size/array_size.h \
|
||||
$(CCANDIR)/ccan/asort/asort.h \
|
||||
$(CCANDIR)/ccan/autodata/autodata.h \
|
||||
$(CCANDIR)/ccan/breakpoint/breakpoint.h \
|
||||
$(CCANDIR)/ccan/build_assert/build_assert.h \
|
||||
$(CCANDIR)/ccan/cast/cast.h \
|
||||
$(CCANDIR)/ccan/cdump/cdump.h \
|
||||
@@ -96,16 +104,20 @@ CCAN_HEADERS := \
|
||||
$(CCANDIR)/ccan/compiler/compiler.h \
|
||||
$(CCANDIR)/ccan/container_of/container_of.h \
|
||||
$(CCANDIR)/ccan/cppmagic/cppmagic.h \
|
||||
$(CCANDIR)/ccan/crypto/hkdf_sha256/hkdf_sha256.h \
|
||||
$(CCANDIR)/ccan/crypto/hmac_sha256/hmac_sha256.h \
|
||||
$(CCANDIR)/ccan/crypto/ripemd160/ripemd160.h \
|
||||
$(CCANDIR)/ccan/crypto/sha256/sha256.h \
|
||||
$(CCANDIR)/ccan/crypto/shachain/shachain.h \
|
||||
$(CCANDIR)/ccan/crypto/siphash24/siphash24.h \
|
||||
$(CCANDIR)/ccan/endian/endian.h \
|
||||
$(CCANDIR)/ccan/err/err.h \
|
||||
$(CCANDIR)/ccan/fdpass/fdpass.h \
|
||||
$(CCANDIR)/ccan/htable/htable.h \
|
||||
$(CCANDIR)/ccan/htable/htable_type.h \
|
||||
$(CCANDIR)/ccan/ilog/ilog.h \
|
||||
$(CCANDIR)/ccan/io/backend.h \
|
||||
$(CCANDIR)/ccan/io/fdpass/fdpass.h \
|
||||
$(CCANDIR)/ccan/io/io.h \
|
||||
$(CCANDIR)/ccan/io/io_plan.h \
|
||||
$(CCANDIR)/ccan/isaac/isaac.h \
|
||||
@@ -118,6 +130,7 @@ CCAN_HEADERS := \
|
||||
$(CCANDIR)/ccan/opt/private.h \
|
||||
$(CCANDIR)/ccan/order/order.h \
|
||||
$(CCANDIR)/ccan/pipecmd/pipecmd.h \
|
||||
$(CCANDIR)/ccan/ptr_valid/ptr_valid.h \
|
||||
$(CCANDIR)/ccan/ptrint/ptrint.h \
|
||||
$(CCANDIR)/ccan/read_write_all/read_write_all.h \
|
||||
$(CCANDIR)/ccan/short_types/short_types.h \
|
||||
@@ -337,6 +350,8 @@ include daemon/Makefile
|
||||
unittest/%: %
|
||||
$(VALGRIND) $(VALGRIND_TEST_ARGS) $*
|
||||
|
||||
ccan-breakpoint.o: $(CCANDIR)/ccan/breakpoint/breakpoint.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-tal-str.o: $(CCANDIR)/ccan/tal/str/str.c
|
||||
@@ -351,6 +366,8 @@ ccan-list.o: $(CCANDIR)/ccan/list/list.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-asort.o: $(CCANDIR)/ccan/asort/asort.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-autodata.o: $(CCANDIR)/ccan/autodata/autodata.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-read_write_all.o: $(CCANDIR)/ccan/read_write_all/read_write_all.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-str.o: $(CCANDIR)/ccan/str/str.c
|
||||
@@ -369,6 +386,10 @@ ccan-noerr.o: $(CCANDIR)/ccan/noerr/noerr.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-str-hex.o: $(CCANDIR)/ccan/str/hex/hex.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-crypto-hmac.o: $(CCANDIR)/ccan/crypto/hmac_sha256/hmac_sha256.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-crypto-hkdf.o: $(CCANDIR)/ccan/crypto/hkdf_sha256/hkdf_sha256.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-crypto-shachain.o: $(CCANDIR)/ccan/crypto/shachain/shachain.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-crypto-sha256.o: $(CCANDIR)/ccan/crypto/sha256/sha256.c
|
||||
@@ -397,7 +418,11 @@ ccan-io-io.o: $(CCANDIR)/ccan/io/io.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-io-poll.o: $(CCANDIR)/ccan/io/poll.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-io-fdpass.o: $(CCANDIR)/ccan/io/fdpass/fdpass.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-pipecmd.o: $(CCANDIR)/ccan/pipecmd/pipecmd.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-mem.o: $(CCANDIR)/ccan/mem/mem.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-fdpass.o: $(CCANDIR)/ccan/fdpass/fdpass.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
CCAN imported from http://ccodearchive.net.
|
||||
|
||||
CCAN version: init-2302-g6b74669
|
||||
CCAN version: init-2338-g97ac583
|
||||
|
||||
1
ccan/ccan/autodata/LICENSE
Symbolic link
1
ccan/ccan/autodata/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/BSD-MIT
|
||||
111
ccan/ccan/autodata/_info
Normal file
111
ccan/ccan/autodata/_info
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* autodata - stash pointers in your binary for automatic registration
|
||||
*
|
||||
* This code allows declarations in your source which you can gather
|
||||
* together at runtime to form tables. This is often used in place of
|
||||
* having a central registration function or table.
|
||||
*
|
||||
* Note that this technique does not work in general for shared libaries,
|
||||
* only for code compiled into a binary.
|
||||
*
|
||||
* License: BSD-MIT
|
||||
*
|
||||
* Example:
|
||||
* // Distributed commandline option registration (note: ccan/opt is better!)
|
||||
* #include <ccan/autodata/autodata.h>
|
||||
* #include <stdio.h>
|
||||
* #include <unistd.h>
|
||||
* #include <stdbool.h>
|
||||
* #include <err.h>
|
||||
*
|
||||
* static bool verbose = false;
|
||||
*
|
||||
* // This would normally be in a header, so any C file can use it.
|
||||
* struct option {
|
||||
* char c;
|
||||
* bool takes_arg;
|
||||
* bool (*cb)(char *optarg);
|
||||
* };
|
||||
* AUTODATA_TYPE(options, struct option);
|
||||
* #define REGISTER_OPTION(optstruct) \
|
||||
* AUTODATA(options, (optstruct))
|
||||
*
|
||||
* // Now a few examples (could be anywhere in source)
|
||||
* static bool verbose_cb(char *unused)
|
||||
* {
|
||||
* verbose = true;
|
||||
* return true;
|
||||
* }
|
||||
* static struct option dash_v = { 'v', false, verbose_cb };
|
||||
* REGISTER_OPTION(&dash_v);
|
||||
*
|
||||
* static bool chdir_cb(char *dir)
|
||||
* {
|
||||
* if (verbose)
|
||||
* printf("chdir to %s. ", dir);
|
||||
* if (chdir(dir) != 0)
|
||||
* return false;
|
||||
* return true;
|
||||
* }
|
||||
* static struct option dash_C = { 'C', true, chdir_cb };
|
||||
* REGISTER_OPTION(&dash_C);
|
||||
*
|
||||
* int main(int argc, char *argv[])
|
||||
* {
|
||||
* struct option **opts;
|
||||
* size_t i, num;
|
||||
* int o;
|
||||
* char *optstring, *p;
|
||||
*
|
||||
* // Gather together all the registered options.
|
||||
* opts = autodata_get(options, &num);
|
||||
*
|
||||
* // Make pretty string for getopt().
|
||||
* p = optstring = malloc(num * 2 + 1);
|
||||
* for (i = 0; i < num; i++) {
|
||||
* *(p++) = opts[i]->c;
|
||||
* if (opts[i]->takes_arg)
|
||||
* *(p++) = ':';
|
||||
* }
|
||||
* *p = '\0';
|
||||
*
|
||||
* while ((o = getopt(argc, argv, optstring)) != -1) {
|
||||
* if (o == '?')
|
||||
* exit(1);
|
||||
* // Call callback in matching option.
|
||||
* for (i = 0; i < num; i++) {
|
||||
* if (opts[i]->c == o) {
|
||||
* if (!opts[i]->cb(optarg))
|
||||
* err(1, "parsing -%c", o);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* // free up gathered option table.
|
||||
* autodata_free(opts);
|
||||
*
|
||||
* if (verbose)
|
||||
* printf("verbose mode on\n");
|
||||
* return 0;
|
||||
* }
|
||||
* // Given "-v" outputs "verbose mode on\n"
|
||||
* // Given "-v -C /" outputs "chdir to /. verbose mode on\n"
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Expect exactly one argument */
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/compiler\n");
|
||||
printf("ccan/ptr_valid\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
80
ccan/ccan/autodata/autodata.c
Normal file
80
ccan/ccan/autodata/autodata.c
Normal file
@@ -0,0 +1,80 @@
|
||||
// Licensed under BSD-MIT: See LICENSE.
|
||||
#include "autodata.h"
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_SECTION_START_STOP
|
||||
void *autodata_get_section(void *start, void *stop, size_t *nump)
|
||||
{
|
||||
*nump = (void **)(stop) - (void **)(start);
|
||||
return start;
|
||||
}
|
||||
|
||||
void autodata_free(void *table UNNEEDED)
|
||||
{
|
||||
}
|
||||
#else
|
||||
#include <ccan/ptr_valid/ptr_valid.h>
|
||||
|
||||
void *autodata_make_table(const void *example, const char *name, size_t *nump)
|
||||
{
|
||||
const char *start, *end, *tag;
|
||||
struct ptr_valid_batch batch;
|
||||
const void *const magic = (void *)AUTODATA_MAGIC;
|
||||
void **table = NULL;
|
||||
char first_magic;
|
||||
|
||||
if (!ptr_valid_batch_start(&batch))
|
||||
return NULL;
|
||||
|
||||
/* Get range to search. */
|
||||
for (start = (char *)((intptr_t)example & ~(getpagesize() - 1));
|
||||
ptr_valid_batch(&batch, start-getpagesize(), 1, sizeof(void *),
|
||||
false);
|
||||
start -= getpagesize());
|
||||
|
||||
for (end = (char *)((intptr_t)example & ~(getpagesize() - 1));
|
||||
ptr_valid_batch(&batch, end, 1, sizeof(void *), false);
|
||||
end += getpagesize());
|
||||
|
||||
*nump = 0;
|
||||
first_magic = *(char *)&magic;
|
||||
for (tag = memchr(start, first_magic, end - start);
|
||||
tag;
|
||||
tag = memchr(tag+1, first_magic, end - (tag + 1))) {
|
||||
void *adata[4];
|
||||
|
||||
/* We can read 4 void *'s here? */
|
||||
if (tag + sizeof(adata) > end)
|
||||
continue;
|
||||
|
||||
memcpy(adata, tag, sizeof(adata));
|
||||
|
||||
/* False match? */
|
||||
if (adata[0] != (void *)AUTODATA_MAGIC || adata[1] != tag)
|
||||
continue;
|
||||
|
||||
/* OK, check name. */
|
||||
if (!ptr_valid_batch_string(&batch, adata[3])
|
||||
|| strcmp(name, adata[3]) != 0)
|
||||
continue;
|
||||
|
||||
if (!ptr_valid_batch_read(&batch, (char *)adata[2]))
|
||||
continue;
|
||||
|
||||
table = realloc(table, sizeof(void *) * (*nump + 1));
|
||||
if (!table)
|
||||
break;
|
||||
table[*nump] = adata[2];
|
||||
(*nump)++;
|
||||
}
|
||||
ptr_valid_batch_end(&batch);
|
||||
return table;
|
||||
}
|
||||
|
||||
void autodata_free(void *table)
|
||||
{
|
||||
free(table);
|
||||
}
|
||||
#endif
|
||||
109
ccan/ccan/autodata/autodata.h
Normal file
109
ccan/ccan/autodata/autodata.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Licensed under BSD-MIT: See LICENSE.
|
||||
#ifndef CCAN_AUTODATA_H
|
||||
#define CCAN_AUTODATA_H
|
||||
#include "config.h"
|
||||
#include <ccan/compiler/compiler.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if HAVE_SECTION_START_STOP
|
||||
|
||||
/**
|
||||
* AUTODATA_TYPE - declare the type for a given autodata name.
|
||||
* @name: the name for this set of autodata
|
||||
* @type: the type this autodata points to
|
||||
*
|
||||
* This macro is usually placed in a header: it must preceed any
|
||||
* autodata functions in the file.
|
||||
*
|
||||
* Example:
|
||||
* #include <ccan/autodata/autodata.h>
|
||||
*
|
||||
* // My set of char pointers.
|
||||
* AUTODATA_TYPE(names, char);
|
||||
*/
|
||||
#define AUTODATA_TYPE(name, type) \
|
||||
typedef type autodata_##name##_; \
|
||||
extern type *__start_xautodata_##name[], *__stop_xautodata_##name[]
|
||||
|
||||
/**
|
||||
* AUTODATA - add a pointer to this autodata set
|
||||
* @name: the name of the set of autodata
|
||||
* @ptr: the compile-time-known pointer
|
||||
*
|
||||
* This embeds @ptr into the binary, with the tag corresponding to
|
||||
* @name (which must look like a valid identifier, no punctuation!).
|
||||
* The type of @ptr must match that given by AUTODATA_TYPE. It is
|
||||
* usually a file-level declaration.
|
||||
*
|
||||
* Example:
|
||||
* // Put two char pointers into the names AUTODATA set.
|
||||
* AUTODATA(names, "Arabella");
|
||||
* AUTODATA(names, "Alex");
|
||||
*/
|
||||
#define AUTODATA(name, ptr) \
|
||||
static const autodata_##name##_ *NEEDED \
|
||||
__attribute__((section("xautodata_" #name))) \
|
||||
AUTODATA_VAR_(name, __LINE__) = (ptr);
|
||||
|
||||
/**
|
||||
* autodata_get - get an autodata set
|
||||
* @name: the name of the set of autodata
|
||||
* @nump: the number of items in the set.
|
||||
*
|
||||
* This extract the embedded pointers matching @name. It may fail
|
||||
* if malloc() fails, or if there is no AUTODATA at all.
|
||||
*
|
||||
* The return will be a pointer to an array of @type pointers (from
|
||||
* AUTODATA_TYPE).
|
||||
*
|
||||
* Example:
|
||||
* static void print_embedded_names(void)
|
||||
* {
|
||||
* unsigned int i;
|
||||
* size_t num;
|
||||
* char **n = autodata_get(names, &num);
|
||||
*
|
||||
* for (i = 0; i < num; i++)
|
||||
* printf("%s\n", n[i]);
|
||||
* }
|
||||
*/
|
||||
#define autodata_get(name, nump) \
|
||||
((autodata_##name##_ **) \
|
||||
autodata_get_section(__start_xautodata_##name, \
|
||||
__stop_xautodata_##name, (nump)))
|
||||
#endif /* HAVE_SECTION_START_STOP */
|
||||
|
||||
/**
|
||||
* autodata_free - free the table returned by autodata_get()
|
||||
* @p: the table.
|
||||
*/
|
||||
void autodata_free(void *p);
|
||||
|
||||
/* Internal functions. */
|
||||
#define AUTODATA_VAR__(name, line) autodata_##name##_##line
|
||||
#define AUTODATA_VAR_(name, line) AUTODATA_VAR__(name, line)
|
||||
|
||||
#if HAVE_SECTION_START_STOP
|
||||
void *autodata_get_section(void *start, void *stop, size_t *nump);
|
||||
#else
|
||||
#define AUTODATA_TYPE(name, type) \
|
||||
typedef type autodata_##name##_; \
|
||||
static const void *autodata_##name##_ex = &autodata_##name##_ex
|
||||
|
||||
#define AUTODATA_MAGIC ((long)0xFEEDA10DA7AF00D5ULL)
|
||||
#define AUTODATA(name, ptr) \
|
||||
static const autodata_##name##_ *NEEDED \
|
||||
AUTODATA_VAR_(name, __LINE__)[4] = \
|
||||
{ (void *)AUTODATA_MAGIC, \
|
||||
(void *)&AUTODATA_VAR_(name, __LINE__), \
|
||||
(ptr), \
|
||||
(void *)#name }
|
||||
|
||||
#define autodata_get(name, nump) \
|
||||
((autodata_##name##_ **) \
|
||||
autodata_make_table(&autodata_##name##_ex, #name, (nump)))
|
||||
|
||||
void *autodata_make_table(const void *example, const char *name, size_t *nump);
|
||||
#endif
|
||||
|
||||
#endif /* CCAN_AUTODATA_H */
|
||||
6
ccan/ccan/autodata/test/helper.c
Normal file
6
ccan/ccan/autodata/test/helper.c
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Check that linking together works. */
|
||||
#include <ccan/autodata/autodata.h>
|
||||
|
||||
AUTODATA_TYPE(autostrings, char);
|
||||
|
||||
AUTODATA(autostrings, "helper");
|
||||
71
ccan/ccan/autodata/test/run-fools.c
Normal file
71
ccan/ccan/autodata/test/run-fools.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <ccan/autodata/autodata.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/autodata/autodata.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
AUTODATA_TYPE(autostrings, char);
|
||||
|
||||
AUTODATA(autostrings, "genuine");
|
||||
|
||||
#if !HAVE_SECTION_START_STOP
|
||||
/* These are all fake, to test the various failure paths. */
|
||||
/* Hopefully fake_alpha or fake_omega will test run-past-end. */
|
||||
static const void *NEEDED fake_alpha[] = { (void *)AUTODATA_MAGIC };
|
||||
|
||||
/* Wrong magic in the middle. */
|
||||
static const void *NEEDED fake1[] = { (void *)(AUTODATA_MAGIC ^ 0x10000),
|
||||
(void *)&fake1,
|
||||
"fake1",
|
||||
(void *)"autostrings" };
|
||||
|
||||
/* Wrong self pointer. */
|
||||
static const void *NEEDED fake2[] = { (void *)AUTODATA_MAGIC,
|
||||
(void *)&fake1,
|
||||
"fake2",
|
||||
(void *)"autostrings" };
|
||||
|
||||
/* Wrong name. */
|
||||
static const void *NEEDED fake3[] = { (void *)AUTODATA_MAGIC,
|
||||
(void *)&fake3,
|
||||
"fake3",
|
||||
(void *)"autostrings2" };
|
||||
|
||||
/* Invalid self-pointer. */
|
||||
static const void *NEEDED fake4[] = { (void *)AUTODATA_MAGIC,
|
||||
(void *)1UL,
|
||||
"fake4",
|
||||
(void *)"autostrings" };
|
||||
|
||||
/* Invalid name pointer */
|
||||
static const void *NEEDED fake5[] = { (void *)AUTODATA_MAGIC,
|
||||
(void *)&fake5,
|
||||
"fake5",
|
||||
(void *)1UL };
|
||||
|
||||
/* Invalid contents pointer */
|
||||
static const void *NEEDED fake6[] = { (void *)AUTODATA_MAGIC,
|
||||
(void *)&fake6,
|
||||
(char *)1UL,
|
||||
(void *)"autostrings" };
|
||||
|
||||
static const void *NEEDED fake_omega[] = { (void *)AUTODATA_MAGIC };
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char **table;
|
||||
size_t num;
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(2);
|
||||
|
||||
table = autodata_get(autostrings, &num);
|
||||
ok1(num == 2);
|
||||
ok1((!strcmp(table[0], "genuine") && !strcmp(table[1], "helper"))
|
||||
|| (!strcmp(table[1], "genuine") && !strcmp(table[0], "helper")));
|
||||
|
||||
autodata_free(table);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
41
ccan/ccan/autodata/test/run.c
Normal file
41
ccan/ccan/autodata/test/run.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <ccan/autodata/autodata.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/autodata/autodata.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
AUTODATA_TYPE(autostrings, char);
|
||||
|
||||
AUTODATA(autostrings, "hello");
|
||||
AUTODATA(autostrings, "world");
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char **table;
|
||||
size_t num;
|
||||
int i, hello = -1, world = -1, helper = -1;
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(4);
|
||||
|
||||
table = autodata_get(autostrings, &num);
|
||||
ok1(num == 3);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (strcmp(table[i], "hello") == 0)
|
||||
hello = i;
|
||||
else if (strcmp(table[i], "world") == 0)
|
||||
world = i;
|
||||
else if (strcmp(table[i], "helper") == 0)
|
||||
helper = i;
|
||||
else
|
||||
fail("Unknown entry %s", table[i]);
|
||||
}
|
||||
ok1(hello != -1);
|
||||
ok1(world != -1);
|
||||
ok1(helper != -1);
|
||||
|
||||
autodata_free(table);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
1
ccan/ccan/breakpoint/LICENSE
Symbolic link
1
ccan/ccan/breakpoint/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/CC0
|
||||
34
ccan/ccan/breakpoint/_info
Normal file
34
ccan/ccan/breakpoint/_info
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* breakpoint - break if the program is run under gdb.
|
||||
*
|
||||
* This code allows you to insert breakpoints within a program. These will
|
||||
* do nothing unless your program is run under GDB.
|
||||
*
|
||||
* License: CC0 (Public domain)
|
||||
*
|
||||
* Example:
|
||||
* #include <ccan/breakpoint/breakpoint.h>
|
||||
*
|
||||
* int main(void)
|
||||
* {
|
||||
* breakpoint();
|
||||
* return 0;
|
||||
* }
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Expect exactly one argument */
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/compiler\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
32
ccan/ccan/breakpoint/breakpoint.c
Normal file
32
ccan/ccan/breakpoint/breakpoint.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/* CC0 (Public domain) - see LICENSE file for details
|
||||
*
|
||||
* Idea for implementation thanks to stackoverflow.com:
|
||||
* http://stackoverflow.com/questions/3596781/detect-if-gdb-is-running
|
||||
*/
|
||||
#include <ccan/breakpoint/breakpoint.h>
|
||||
|
||||
bool breakpoint_initialized;
|
||||
bool breakpoint_under_debug;
|
||||
|
||||
/* This doesn't get called if we're under GDB. */
|
||||
static void trap(int signum)
|
||||
{
|
||||
breakpoint_initialized = true;
|
||||
}
|
||||
|
||||
void breakpoint_init(void)
|
||||
{
|
||||
struct sigaction old, new;
|
||||
|
||||
new.sa_handler = trap;
|
||||
new.sa_flags = 0;
|
||||
sigemptyset(&new.sa_mask);
|
||||
sigaction(SIGTRAP, &new, &old);
|
||||
kill(getpid(), SIGTRAP);
|
||||
sigaction(SIGTRAP, &old, NULL);
|
||||
|
||||
if (!breakpoint_initialized) {
|
||||
breakpoint_initialized = true;
|
||||
breakpoint_under_debug = true;
|
||||
}
|
||||
}
|
||||
24
ccan/ccan/breakpoint/breakpoint.h
Normal file
24
ccan/ccan/breakpoint/breakpoint.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/* CC0 (Public domain) - see LICENSE file for details */
|
||||
#ifndef CCAN_BREAKPOINT_H
|
||||
#define CCAN_BREAKPOINT_H
|
||||
#include <ccan/compiler/compiler.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void breakpoint_init(void) COLD;
|
||||
extern bool breakpoint_initialized;
|
||||
extern bool breakpoint_under_debug;
|
||||
|
||||
/**
|
||||
* breakpoint - stop if running under the debugger.
|
||||
*/
|
||||
static inline void breakpoint(void)
|
||||
{
|
||||
if (!breakpoint_initialized)
|
||||
breakpoint_init();
|
||||
if (breakpoint_under_debug)
|
||||
kill(getpid(), SIGTRAP);
|
||||
}
|
||||
#endif /* CCAN_BREAKPOINT_H */
|
||||
17
ccan/ccan/breakpoint/test/run.c
Normal file
17
ccan/ccan/breakpoint/test/run.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <ccan/breakpoint/breakpoint.h>
|
||||
#include <ccan/breakpoint/breakpoint.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(2);
|
||||
|
||||
breakpoint();
|
||||
|
||||
ok1(breakpoint_initialized);
|
||||
ok1(!breakpoint_under_debug);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
@@ -15,7 +15,7 @@ static inline void check1(const char *orig, const char *expand,
|
||||
#define CHECK1(orig, match) \
|
||||
check1(#orig, CPPMAGIC_STRINGIFY(orig), match)
|
||||
|
||||
#define TESTRECURSE() R CPPMAGIC_DEFER1(_TESTRECURSE)()()
|
||||
#define TESTRECURSE() R CPPMAGIC_DEFER1(_TESTRECURSE) ()()
|
||||
#define _TESTRECURSE() TESTRECURSE
|
||||
|
||||
#define TESTMAP1(x) <<x>>
|
||||
|
||||
1
ccan/ccan/crypto/hkdf_sha256/LICENSE
Symbolic link
1
ccan/ccan/crypto/hkdf_sha256/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../licenses/BSD-MIT
|
||||
30
ccan/ccan/crypto/hkdf_sha256/_info
Normal file
30
ccan/ccan/crypto/hkdf_sha256/_info
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* crypto/hkdf_sha256 - RFC5869 Hardened Key Derivation Functions using SHA256
|
||||
*
|
||||
* This code implements the hkdf described in RFC5869.
|
||||
*
|
||||
* License: BSD-MIT
|
||||
* Maintainer: 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) {
|
||||
printf("ccan/crypto/hmac_sha256\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "testdepends") == 0) {
|
||||
printf("ccan/str/hex\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
97
ccan/ccan/crypto/hkdf_sha256/hkdf_sha256.c
Normal file
97
ccan/ccan/crypto/hkdf_sha256/hkdf_sha256.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/* MIT (BSD) license - see LICENSE file for details */
|
||||
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
|
||||
#include <ccan/crypto/hmac_sha256/hmac_sha256.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
void hkdf_sha256(void *okm, size_t okm_size,
|
||||
const void *s, size_t ssize,
|
||||
const void *k, size_t ksize,
|
||||
const void *info, size_t isize)
|
||||
{
|
||||
struct hmac_sha256 prk, t;
|
||||
struct hmac_sha256_ctx ctx;
|
||||
unsigned char c;
|
||||
|
||||
assert(okm_size < 255 * sizeof(t));
|
||||
|
||||
/* RFC 5869:
|
||||
*
|
||||
* 2.2. Step 1: Extract
|
||||
*
|
||||
* HKDF-Extract(salt, IKM) -> PRK
|
||||
*
|
||||
* Options:
|
||||
* Hash a hash function; HashLen denotes the length of the
|
||||
* hash function output in octets
|
||||
*
|
||||
* Inputs:
|
||||
* salt optional salt value (a non-secret random value);
|
||||
* if not provided, it is set to a string of HashLen zeros.
|
||||
* IKM input keying material
|
||||
*
|
||||
* Output:
|
||||
* PRK a pseudorandom key (of HashLen octets)
|
||||
*
|
||||
* The output PRK is calculated as follows:
|
||||
*
|
||||
* PRK = HMAC-Hash(salt, IKM)
|
||||
*/
|
||||
hmac_sha256(&prk, s, ssize, k, ksize);
|
||||
|
||||
/*
|
||||
* 2.3. Step 2: Expand
|
||||
*
|
||||
* HKDF-Expand(PRK, info, L) -> OKM
|
||||
*
|
||||
* Options:
|
||||
* Hash a hash function; HashLen denotes the length of the
|
||||
* hash function output in octets
|
||||
*
|
||||
* Inputs:
|
||||
* PRK a pseudorandom key of at least HashLen octets
|
||||
* (usually, the output from the extract step)
|
||||
* info optional context and application specific information
|
||||
* (can be a zero-length string)
|
||||
* L length of output keying material in octets
|
||||
* (<= 255*HashLen)
|
||||
*
|
||||
* Output:
|
||||
* OKM output keying material (of L octets)
|
||||
*
|
||||
* The output OKM is calculated as follows:
|
||||
*
|
||||
* N = ceil(L/HashLen)
|
||||
* T = T(1) | T(2) | T(3) | ... | T(N)
|
||||
* OKM = first L octets of T
|
||||
*
|
||||
* where:
|
||||
* T(0) = empty string (zero length)
|
||||
* T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
|
||||
* T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
|
||||
* T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
|
||||
* ...
|
||||
*
|
||||
* (where the constant concatenated to the end of each T(n) is a
|
||||
* single octet.)
|
||||
*/
|
||||
c = 1;
|
||||
hmac_sha256_init(&ctx, &prk, sizeof(prk));
|
||||
hmac_sha256_update(&ctx, info, isize);
|
||||
hmac_sha256_update(&ctx, &c, 1);
|
||||
hmac_sha256_done(&ctx, &t);
|
||||
|
||||
while (okm_size > sizeof(t)) {
|
||||
memcpy(okm, &t, sizeof(t));
|
||||
okm = (char *)okm + sizeof(t);
|
||||
okm_size -= sizeof(t);
|
||||
|
||||
c++;
|
||||
hmac_sha256_init(&ctx, &prk, sizeof(prk));
|
||||
hmac_sha256_update(&ctx, &t, sizeof(t));
|
||||
hmac_sha256_update(&ctx, info, isize);
|
||||
hmac_sha256_update(&ctx, &c, 1);
|
||||
hmac_sha256_done(&ctx, &t);
|
||||
}
|
||||
memcpy(okm, &t, okm_size);
|
||||
}
|
||||
22
ccan/ccan/crypto/hkdf_sha256/hkdf_sha256.h
Normal file
22
ccan/ccan/crypto/hkdf_sha256/hkdf_sha256.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef CCAN_CRYPTO_HKDF_SHA256_H
|
||||
#define CCAN_CRYPTO_HKDF_SHA256_H
|
||||
/* BSD-MIT - see LICENSE file for details */
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* hkdf_sha256 - generate a derived key
|
||||
* @okm: where to output the key
|
||||
* @okm_size: the number of bytes pointed to by @okm (must be less than 255*32)
|
||||
* @s: salt
|
||||
* @ssize: the number of bytes pointed to by @s
|
||||
* @k: pointer to input key
|
||||
* @ksize: the number of bytes pointed to by @k
|
||||
* @info: pointer to info
|
||||
* @isize: the number of bytes pointed to by @info
|
||||
*/
|
||||
void hkdf_sha256(void *okm, size_t okm_size,
|
||||
const void *s, size_t ssize,
|
||||
const void *k, size_t ksize,
|
||||
const void *info, size_t isize);
|
||||
#endif /* CCAN_CRYPTO_HKDF_SHA256_H */
|
||||
101
ccan/ccan/crypto/hkdf_sha256/test/api-rfc5869.c
Normal file
101
ccan/ccan/crypto/hkdf_sha256/test/api-rfc5869.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/* From RFC5869 Appendix A
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc5869
|
||||
*/
|
||||
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
|
||||
#include <ccan/tap/tap.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct test {
|
||||
const char *ikm, *salt, *info, *okm;
|
||||
};
|
||||
|
||||
static struct test tests[] = { {
|
||||
/* Test Case 1
|
||||
Basic test case with SHA-256
|
||||
*/
|
||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", /* (22 octets) */
|
||||
"000102030405060708090a0b0c", /* (13 octets) */
|
||||
"f0f1f2f3f4f5f6f7f8f9", /* (10 octets) */
|
||||
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", /* (42 octets) */
|
||||
},
|
||||
{
|
||||
/* Test Case 2
|
||||
*
|
||||
* Test with SHA-256 and longer inputs/outputs */
|
||||
"000102030405060708090a0b0c0d0e0f"
|
||||
"101112131415161718191a1b1c1d1e1f"
|
||||
"202122232425262728292a2b2c2d2e2f"
|
||||
"303132333435363738393a3b3c3d3e3f"
|
||||
"404142434445464748494a4b4c4d4e4f", /* (80 octets) */
|
||||
"606162636465666768696a6b6c6d6e6f"
|
||||
"707172737475767778797a7b7c7d7e7f"
|
||||
"808182838485868788898a8b8c8d8e8f"
|
||||
"909192939495969798999a9b9c9d9e9f"
|
||||
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", /* (80 octets )*/
|
||||
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
|
||||
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
|
||||
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
|
||||
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
|
||||
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", /* (80 octets) */
|
||||
"b11e398dc80327a1c8e7f78c596a4934"
|
||||
"4f012eda2d4efad8a050cc4c19afa97c"
|
||||
"59045a99cac7827271cb41c65e590e09"
|
||||
"da3275600c2f09b8367793a9aca3db71"
|
||||
"cc30c58179ec3e87c14c01d5c1f3434f"
|
||||
"1d87" /* (82 octets) */
|
||||
},
|
||||
{
|
||||
/* Test Case 3
|
||||
*
|
||||
* Test with SHA-256 and zero-length salt/info
|
||||
*/
|
||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", /* (22 octets) */
|
||||
"", /* (0 octets) */
|
||||
"", /* (0 octets) */
|
||||
"8da4e775a563c18f715f802a063c5a31"
|
||||
"b8a11f5c5ee1879ec3454e5f3c738d2d"
|
||||
"9d201395faa4b61a96c8" /* (42 octets) */
|
||||
}
|
||||
};
|
||||
|
||||
static void *fromhex(const char *str, size_t *len)
|
||||
{
|
||||
void *p;
|
||||
|
||||
*len = hex_data_size(strlen(str));
|
||||
p = malloc(*len);
|
||||
if (!hex_decode(str, strlen(str), p, *len))
|
||||
abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
plan_tests(sizeof(tests) / sizeof(tests[0]));
|
||||
|
||||
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
|
||||
size_t ksize, ssize, isize, okmsize;
|
||||
void *k, *s, *info, *expect, *okm;
|
||||
|
||||
k = fromhex(tests[i].ikm, &ksize);
|
||||
s = fromhex(tests[i].salt, &ssize);
|
||||
info = fromhex(tests[i].info, &isize);
|
||||
expect = fromhex(tests[i].okm, &okmsize);
|
||||
okm = malloc(okmsize);
|
||||
hkdf_sha256(okm, okmsize, s, ssize, k, ksize, info, isize);
|
||||
ok1(memcmp(okm, expect, okmsize) == 0);
|
||||
|
||||
free(k);
|
||||
free(s);
|
||||
free(info);
|
||||
free(expect);
|
||||
free(okm);
|
||||
}
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
1
ccan/ccan/crypto/hmac_sha256/LICENSE
Symbolic link
1
ccan/ccan/crypto/hmac_sha256/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../licenses/BSD-MIT
|
||||
52
ccan/ccan/crypto/hmac_sha256/_info
Normal file
52
ccan/ccan/crypto/hmac_sha256/_info
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* crypto/hmac_sha256 - RFC2104 HMAC using SHA256.
|
||||
*
|
||||
* This code implements RFC2104, which is a fairly standard HMAC.
|
||||
*
|
||||
* License: BSD-MIT
|
||||
* Maintainer: Rusty Russell <rusty@rustcorp.com.au>
|
||||
*
|
||||
* Example:
|
||||
* #include <ccan/crypto/hmac_sha256/hmac_sha256.h>
|
||||
* #include <err.h>
|
||||
* #include <stdio.h>
|
||||
* #include <string.h>
|
||||
*
|
||||
* // Simple demonstration: idential strings will have the same hash, but
|
||||
* // two different strings will not.
|
||||
* int main(int argc, char *argv[])
|
||||
* {
|
||||
* struct hmac_sha256 hash1, hash2;
|
||||
*
|
||||
* if (argc != 3)
|
||||
* errx(1, "Usage: %s <string1> <string2>", argv[0]);
|
||||
*
|
||||
* hmac_sha256(&hash1, "key", 3, argv[1], strlen(argv[1]));
|
||||
* hmac_sha256(&hash2, "key", 3, argv[2], strlen(argv[2]));
|
||||
* printf("Hash is %s\n", memcmp(&hash1, &hash2, sizeof(hash1))
|
||||
* ? "different" : "same");
|
||||
* return 0;
|
||||
* }
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Expect exactly one argument */
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/crypto/sha256\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "testdepends") == 0) {
|
||||
printf("ccan/str/hex\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
160
ccan/ccan/crypto/hmac_sha256/hmac_sha256.c
Normal file
160
ccan/ccan/crypto/hmac_sha256/hmac_sha256.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/* MIT (BSD) license - see LICENSE file for details */
|
||||
#include <ccan/crypto/hmac_sha256/hmac_sha256.h>
|
||||
#include <string.h>
|
||||
|
||||
#define IPAD 0x3636363636363636ULL
|
||||
#define OPAD 0x5C5C5C5C5C5C5C5CULL
|
||||
|
||||
#define BLOCK_U64S (HMAC_SHA256_BLOCKSIZE / sizeof(uint64_t))
|
||||
|
||||
static inline void xor_block(uint64_t block[BLOCK_U64S], uint64_t pad)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < BLOCK_U64S; i++)
|
||||
block[i] ^= pad;
|
||||
}
|
||||
|
||||
void hmac_sha256_init(struct hmac_sha256_ctx *ctx,
|
||||
const void *k, size_t ksize)
|
||||
{
|
||||
struct sha256 hashed_key;
|
||||
/* We use k_opad as k_ipad temporarily. */
|
||||
uint64_t *k_ipad = ctx->k_opad;
|
||||
|
||||
/* (keys longer than B bytes are first hashed using H) */
|
||||
if (ksize > HMAC_SHA256_BLOCKSIZE) {
|
||||
sha256(&hashed_key, k, ksize);
|
||||
k = &hashed_key;
|
||||
ksize = sizeof(hashed_key);
|
||||
}
|
||||
|
||||
/* From RFC2104:
|
||||
*
|
||||
* (1) append zeros to the end of K to create a B byte string
|
||||
* (e.g., if K is of length 20 bytes and B=64, then K will be
|
||||
* appended with 44 zero bytes 0x00)
|
||||
*/
|
||||
memcpy(k_ipad, k, ksize);
|
||||
memset((char *)k_ipad + ksize, 0, HMAC_SHA256_BLOCKSIZE - ksize);
|
||||
|
||||
/*
|
||||
* (2) XOR (bitwise exclusive-OR) the B byte string computed
|
||||
* in step (1) with ipad
|
||||
*/
|
||||
xor_block(k_ipad, IPAD);
|
||||
|
||||
/*
|
||||
* We start (4) here, appending text later:
|
||||
*
|
||||
* (3) append the stream of data 'text' to the B byte string resulting
|
||||
* from step (2)
|
||||
* (4) apply H to the stream generated in step (3)
|
||||
*/
|
||||
sha256_init(&ctx->sha);
|
||||
sha256_update(&ctx->sha, k_ipad, HMAC_SHA256_BLOCKSIZE);
|
||||
|
||||
/*
|
||||
* (5) XOR (bitwise exclusive-OR) the B byte string computed in
|
||||
* step (1) with opad
|
||||
*/
|
||||
xor_block(ctx->k_opad, IPAD^OPAD);
|
||||
}
|
||||
|
||||
void hmac_sha256_update(struct hmac_sha256_ctx *ctx, const void *p, size_t size)
|
||||
{
|
||||
/* This is the appending-text part of this:
|
||||
*
|
||||
* (3) append the stream of data 'text' to the B byte string resulting
|
||||
* from step (2)
|
||||
* (4) apply H to the stream generated in step (3)
|
||||
*/
|
||||
sha256_update(&ctx->sha, p, size);
|
||||
}
|
||||
|
||||
void hmac_sha256_done(struct hmac_sha256_ctx *ctx,
|
||||
struct hmac_sha256 *hmac)
|
||||
{
|
||||
/* (4) apply H to the stream generated in step (3) */
|
||||
sha256_done(&ctx->sha, &hmac->sha);
|
||||
|
||||
/*
|
||||
* (6) append the H result from step (4) to the B byte string
|
||||
* resulting from step (5)
|
||||
* (7) apply H to the stream generated in step (6) and output
|
||||
* the result
|
||||
*/
|
||||
sha256_init(&ctx->sha);
|
||||
sha256_update(&ctx->sha, ctx->k_opad, sizeof(ctx->k_opad));
|
||||
sha256_update(&ctx->sha, &hmac->sha, sizeof(hmac->sha));
|
||||
sha256_done(&ctx->sha, &hmac->sha);
|
||||
}
|
||||
|
||||
#if 1
|
||||
void hmac_sha256(struct hmac_sha256 *hmac,
|
||||
const void *k, size_t ksize,
|
||||
const void *d, size_t dsize)
|
||||
{
|
||||
struct hmac_sha256_ctx ctx;
|
||||
|
||||
hmac_sha256_init(&ctx, k, ksize);
|
||||
hmac_sha256_update(&ctx, d, dsize);
|
||||
hmac_sha256_done(&ctx, hmac);
|
||||
}
|
||||
#else
|
||||
/* Direct mapping from MD5 example in RFC2104 */
|
||||
void hmac_sha256(struct hmac_sha256 *hmac,
|
||||
const void *key, size_t key_len,
|
||||
const void *text, size_t text_len)
|
||||
{
|
||||
struct sha256_ctx context;
|
||||
unsigned char k_ipad[65]; /* inner padding -
|
||||
* key XORd with ipad
|
||||
*/
|
||||
unsigned char k_opad[65]; /* outer padding -
|
||||
* key XORd with opad
|
||||
*//* start out by storing key in pads */
|
||||
unsigned char tk[32];
|
||||
int i;
|
||||
|
||||
/* if key is longer than 64 bytes reset it to key=MD5(key) */
|
||||
if (key_len > 64) {
|
||||
|
||||
struct sha256_ctx tctx;
|
||||
|
||||
sha256_init(&tctx);
|
||||
sha256_update(&tctx, key, key_len);
|
||||
sha256_done(&tctx, tk);
|
||||
|
||||
key = tk;
|
||||
key_len = 32;
|
||||
}
|
||||
bzero( k_ipad, sizeof k_ipad);
|
||||
bzero( k_opad, sizeof k_opad);
|
||||
bcopy( key, k_ipad, key_len);
|
||||
bcopy( key, k_opad, key_len);
|
||||
|
||||
/* XOR key with ipad and opad values */
|
||||
for (i=0; i<64; i++) {
|
||||
k_ipad[i] ^= 0x36;
|
||||
k_opad[i] ^= 0x5c;
|
||||
}
|
||||
/*
|
||||
* perform inner MD5
|
||||
*/
|
||||
sha256_init(&context); /* init context for 1st
|
||||
* pass */
|
||||
sha256_update(&context, k_ipad, 64); /* start with inner pad */
|
||||
sha256_update(&context, text, text_len); /* then text of datagram */
|
||||
sha256_done(&context, &hmac->sha); /* finish up 1st pass */
|
||||
/*
|
||||
* perform outer MD5
|
||||
*/
|
||||
sha256_init(&context); /* init context for 2nd
|
||||
* pass */
|
||||
sha256_update(&context, k_opad, 64); /* start with outer pad */
|
||||
sha256_update(&context, &hmac->sha, 32); /* then results of 1st
|
||||
* hash */
|
||||
sha256_done(&context, &hmac->sha); /* finish up 2nd pass */
|
||||
}
|
||||
#endif
|
||||
86
ccan/ccan/crypto/hmac_sha256/hmac_sha256.h
Normal file
86
ccan/ccan/crypto/hmac_sha256/hmac_sha256.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef CCAN_CRYPTO_HMAC_SHA256_H
|
||||
#define CCAN_CRYPTO_HMAC_SHA256_H
|
||||
/* BSD-MIT - see LICENSE file for details */
|
||||
#include "config.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <ccan/crypto/sha256/sha256.h>
|
||||
|
||||
/* Number of bytes per block. */
|
||||
#define HMAC_SHA256_BLOCKSIZE 64
|
||||
|
||||
/**
|
||||
* struct hmac_sha256 - structure representing a completed HMAC.
|
||||
*/
|
||||
struct hmac_sha256 {
|
||||
struct sha256 sha;
|
||||
};
|
||||
|
||||
/**
|
||||
* hmac_sha256 - return hmac of an object with a key.
|
||||
* @hmac: the hmac to fill in
|
||||
* @k: pointer to the key,
|
||||
* @ksize: the number of bytes pointed to by @k
|
||||
* @d: pointer to memory,
|
||||
* @dsize: the number of bytes pointed to by @d
|
||||
*/
|
||||
void hmac_sha256(struct hmac_sha256 *hmac,
|
||||
const void *k, size_t ksize,
|
||||
const void *d, size_t dsize);
|
||||
|
||||
/**
|
||||
* struct hmac_sha256_ctx - structure to store running context for hmac_sha256
|
||||
*/
|
||||
struct hmac_sha256_ctx {
|
||||
struct sha256_ctx sha;
|
||||
uint64_t k_opad[HMAC_SHA256_BLOCKSIZE / sizeof(uint64_t)];
|
||||
};
|
||||
|
||||
/**
|
||||
* hmac_sha256_init - initialize an HMAC_SHA256 context.
|
||||
* @ctx: the hmac_sha256_ctx to initialize
|
||||
* @k: pointer to the key,
|
||||
* @ksize: the number of bytes pointed to by @k
|
||||
*
|
||||
* This must be called before hmac_sha256_update or hmac_sha256_done.
|
||||
*
|
||||
* If it was already initialized, this forgets anything which was
|
||||
* hashed before.
|
||||
*
|
||||
* Example:
|
||||
* static void hmac_all(const char *key,
|
||||
* const char **arr, struct hmac_sha256 *hash)
|
||||
* {
|
||||
* size_t i;
|
||||
* struct hmac_sha256_ctx ctx;
|
||||
*
|
||||
* hmac_sha256_init(&ctx, key, strlen(key));
|
||||
* for (i = 0; arr[i]; i++)
|
||||
* hmac_sha256_update(&ctx, arr[i], strlen(arr[i]));
|
||||
* hmac_sha256_done(&ctx, hash);
|
||||
* }
|
||||
*/
|
||||
void hmac_sha256_init(struct hmac_sha256_ctx *ctx,
|
||||
const void *k, size_t ksize);
|
||||
|
||||
/**
|
||||
* hmac_sha256_update - include some memory in the hash.
|
||||
* @ctx: the hmac_sha256_ctx to use
|
||||
* @p: pointer to memory,
|
||||
* @size: the number of bytes pointed to by @p
|
||||
*
|
||||
* You can call this multiple times to hash more data, before calling
|
||||
* hmac_sha256_done().
|
||||
*/
|
||||
void hmac_sha256_update(struct hmac_sha256_ctx *ctx, const void *p, size_t size);
|
||||
|
||||
/**
|
||||
* hmac_sha256_done - finish HMAC_SHA256 and return the hash
|
||||
* @ctx: the hmac_sha256_ctx to complete
|
||||
* @res: the hash to return.
|
||||
*
|
||||
* Note that @ctx is *destroyed* by this, and must be reinitialized.
|
||||
* To avoid that, pass a copy instead.
|
||||
*/
|
||||
void hmac_sha256_done(struct hmac_sha256_ctx *hmac_sha256, struct hmac_sha256 *res);
|
||||
#endif /* CCAN_CRYPTO_HMAC_SHA256_H */
|
||||
159
ccan/ccan/crypto/hmac_sha256/test/api-rfc4231.c
Normal file
159
ccan/ccan/crypto/hmac_sha256/test/api-rfc4231.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/* From RFC4231 "Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256,
|
||||
* HMAC-SHA-384, and HMAC-SHA-512"
|
||||
*
|
||||
* https://tools.ietf.org/html/rfc4231
|
||||
*/
|
||||
#include <ccan/crypto/hmac_sha256/hmac_sha256.h>
|
||||
#include <ccan/tap/tap.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct test {
|
||||
const char *key, *data, *hmac;
|
||||
};
|
||||
|
||||
static struct test tests[] = { {
|
||||
/* Test Case 1 */
|
||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", /* (20 bytes) */
|
||||
"4869205468657265", /* ("Hi There") */
|
||||
"b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
|
||||
},
|
||||
/* Test Case 2:
|
||||
Test with a key shorter than the length of the HMAC output. */
|
||||
{
|
||||
"4a656665", /* ("Jefe") */
|
||||
/* ("what do ya want for nothing?") */
|
||||
"7768617420646f2079612077616e7420666f72206e6f7468696e673f",
|
||||
"5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
|
||||
},
|
||||
{
|
||||
/* Test Case 3
|
||||
|
||||
Test with a combined length of key and data that is larger than 64
|
||||
bytes (= block-size of SHA-224 and SHA-256).
|
||||
*/
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", /* (20 bytes) */
|
||||
"dddddddddddddddddddddddddddddddd"
|
||||
"dddddddddddddddddddddddddddddddd"
|
||||
"dddddddddddddddddddddddddddddddd"
|
||||
"dddd", /* (50 bytes) */
|
||||
"773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"
|
||||
},
|
||||
{
|
||||
/* Test Case 4
|
||||
|
||||
Test with a combined length of key and data that is larger than 64
|
||||
bytes (= block-size of SHA-224 and SHA-256).
|
||||
*/
|
||||
"0102030405060708090a0b0c0d0e0f10111213141516171819", /* (25 bytes) */
|
||||
"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
|
||||
"cdcd", /* (50 bytes) */
|
||||
"82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"
|
||||
},
|
||||
#if 0
|
||||
{
|
||||
/* Test Case 5
|
||||
|
||||
Test with a truncation of output to 128 bits.
|
||||
*/
|
||||
"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", /* (20 bytes) */
|
||||
"546573742057697468205472756e636174696f6e", /* ("Test With Truncation") */
|
||||
"a3b6167473100ee06e0c796c2955552b"
|
||||
},
|
||||
#endif
|
||||
{
|
||||
/* Test Case 6
|
||||
|
||||
Test with a key larger than 128 bytes (= block-size of SHA-384 and
|
||||
SHA-512).
|
||||
*/
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaa", /* (131 bytes) */
|
||||
"54657374205573696e67204c61726765" /* ("Test Using Large") */
|
||||
"72205468616e20426c6f636b2d53697a" /* ("r Than Block-Siz") */
|
||||
"65204b6579202d2048617368204b6579" /* ("e Key - Hash Key") */
|
||||
"204669727374", /* (" First") */
|
||||
"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"
|
||||
},
|
||||
{
|
||||
/* Test Case 7
|
||||
|
||||
Test with a key and data that is larger than 128 bytes (= block-size
|
||||
of SHA-384 and SHA-512). */
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
"aaaaaa", /* (131 bytes) */
|
||||
"54686973206973206120746573742075" /* ("This is a test u") */
|
||||
"73696e672061206c6172676572207468" /* ("sing a larger th") */
|
||||
"616e20626c6f636b2d73697a65206b65" /* ("an block-size ke") */
|
||||
"7920616e642061206c61726765722074" /* ("y and a larger t") */
|
||||
"68616e20626c6f636b2d73697a652064" /* ("han block-size d") */
|
||||
"6174612e20546865206b6579206e6565" /* ("ata. The key nee") */
|
||||
"647320746f2062652068617368656420" /* ("ds to be hashed ") */
|
||||
"6265666f7265206265696e6720757365" /* ("before being use") */
|
||||
"642062792074686520484d414320616c" /* ("d by the HMAC al") */
|
||||
"676f726974686d2e", /* ("gorithm.") */
|
||||
"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"
|
||||
}
|
||||
};
|
||||
|
||||
static void *fromhex(const char *str, size_t *len)
|
||||
{
|
||||
void *p;
|
||||
|
||||
*len = hex_data_size(strlen(str));
|
||||
p = malloc(*len);
|
||||
if (!hex_decode(str, strlen(str), p, *len))
|
||||
abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
size_t i;
|
||||
struct hmac_sha256 hmac;
|
||||
|
||||
plan_tests(sizeof(tests) / sizeof(tests[0]) * 2);
|
||||
|
||||
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
|
||||
size_t ksize, dsize, hmacsize;
|
||||
void *k, *d, *expect;
|
||||
struct hmac_sha256_ctx ctx;
|
||||
|
||||
k = fromhex(tests[i].key, &ksize);
|
||||
d = fromhex(tests[i].data, &dsize);
|
||||
expect = fromhex(tests[i].hmac, &hmacsize);
|
||||
assert(hmacsize == sizeof(hmac));
|
||||
hmac_sha256(&hmac, k, ksize, d, dsize);
|
||||
ok1(memcmp(&hmac, expect, hmacsize) == 0);
|
||||
|
||||
/* Now test partial API. */
|
||||
hmac_sha256_init(&ctx, k, ksize);
|
||||
hmac_sha256_update(&ctx, d, dsize / 2);
|
||||
hmac_sha256_update(&ctx, (char *)d + dsize/2, dsize - dsize/2);
|
||||
hmac_sha256_done(&ctx, &hmac);
|
||||
ok1(memcmp(&hmac, expect, hmacsize) == 0);
|
||||
|
||||
free(k);
|
||||
free(d);
|
||||
free(expect);
|
||||
}
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define INDEX_BITS ((sizeof(shachain_index_t)) * CHAR_BIT)
|
||||
|
||||
static void change_bit(unsigned char *arr, size_t index)
|
||||
{
|
||||
arr[index / CHAR_BIT] ^= (1 << (index % CHAR_BIT));
|
||||
@@ -15,11 +13,11 @@ static void change_bit(unsigned char *arr, size_t index)
|
||||
static unsigned int count_trailing_zeroes(shachain_index_t index)
|
||||
{
|
||||
#if HAVE_BUILTIN_CTZLL
|
||||
return index ? (unsigned int)__builtin_ctzll(index) : INDEX_BITS;
|
||||
return index ? (unsigned int)__builtin_ctzll(index) : SHACHAIN_BITS;
|
||||
#else
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < INDEX_BITS; i++) {
|
||||
for (i = 0; i < SHACHAIN_BITS; i++) {
|
||||
if (index & (1ULL << i))
|
||||
break;
|
||||
}
|
||||
@@ -81,7 +79,8 @@ bool shachain_add_hash(struct shachain *chain,
|
||||
|
||||
/* You have to insert them in order! */
|
||||
assert(index == chain->min_index - 1 ||
|
||||
(index == (shachain_index_t)(-1ULL) && chain->num_valid == 0));
|
||||
(index == (shachain_index_t)(UINT64_MAX >> (64 - SHACHAIN_BITS))
|
||||
&& chain->num_valid == 0));
|
||||
|
||||
pos = count_trailing_zeroes(index);
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#define shachain_index_t uint64_t
|
||||
#endif
|
||||
|
||||
#ifndef SHACHAIN_BITS
|
||||
#define SHACHAIN_BITS (sizeof(shachain_index_t) * 8)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* shachain_from_seed - Generate an unpredictable SHA from a seed value.
|
||||
* @seed: (secret) seed value to use
|
||||
@@ -56,7 +60,7 @@ struct shachain {
|
||||
struct {
|
||||
shachain_index_t index;
|
||||
struct sha256 hash;
|
||||
} known[sizeof(shachain_index_t) * 8 + 1];
|
||||
} known[SHACHAIN_BITS + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
39
ccan/ccan/crypto/shachain/tools/Makefile
Normal file
39
ccan/ccan/crypto/shachain/tools/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
#! /usr/bin/make
|
||||
|
||||
CCANDIR=../../../..
|
||||
CFLAGS=-Wall -Werror -O3 -I$(CCANDIR) -DSHACHAIN_BITS=48
|
||||
#CFLAGS=-Wall -Werror -g3 -I$(CCANDIR) -DSHACHAIN_BITS=48
|
||||
|
||||
# 48 bit index for shachain. This is what lightning uses.
|
||||
CCAN_OBJS:=ccan-str.o ccan-err.o ccan-hex.o ccan-shachain.o ccan-sha256.o ccan-rbuf.o
|
||||
|
||||
all: shachain48
|
||||
|
||||
shachain48: shachain48.o $(CCAN_OBJS)
|
||||
|
||||
shachain48.o: $(CCANDIR)/ccan/crypto/shachain/shachain.h \
|
||||
$(CCANDIR)/ccan/str/hex/hex.h \
|
||||
$(CCANDIR)/ccan/str/str.h \
|
||||
$(CCANDIR)/ccan/err/err.h \
|
||||
$(CCANDIR)/ccan/rbuf/rbuf.h
|
||||
|
||||
shachain48.o $(CCAN_OBJS): $(CCANDIR)/config.h
|
||||
|
||||
$(CCANDIR)/config.h:
|
||||
$(MAKE) -C $(CCANDIR) config.h
|
||||
|
||||
clean:
|
||||
rm -f shachain *.o
|
||||
|
||||
ccan-err.o: $(CCANDIR)/ccan/err/err.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-hex.o: $(CCANDIR)/ccan/str/hex/hex.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-str.o: $(CCANDIR)/ccan/str/str.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-shachain.o: $(CCANDIR)/ccan/crypto/shachain/shachain.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-sha256.o: $(CCANDIR)/ccan/crypto/sha256/sha256.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
ccan-rbuf.o: $(CCANDIR)/ccan/rbuf/rbuf.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
58
ccan/ccan/crypto/shachain/tools/shachain48.c
Normal file
58
ccan/ccan/crypto/shachain/tools/shachain48.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <ccan/crypto/shachain/shachain.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <ccan/str/str.h>
|
||||
#include <ccan/err/err.h>
|
||||
#include <ccan/rbuf/rbuf.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc == 2 && streq(argv[1], "--store")) {
|
||||
struct shachain s;
|
||||
struct rbuf rbuf;
|
||||
size_t size = rbuf_good_size(STDIN_FILENO);
|
||||
char *p;
|
||||
|
||||
shachain_init(&s);
|
||||
rbuf_init(&rbuf, STDIN_FILENO, malloc(size), size);
|
||||
|
||||
while ((p = rbuf_read_str(&rbuf, '\n', realloc)) != NULL) {
|
||||
struct sha256 hash;
|
||||
unsigned long long idx;
|
||||
|
||||
if (strstarts(p, "0x"))
|
||||
p += 2;
|
||||
if (!hex_decode(p, 64, &hash, sizeof(hash)))
|
||||
errx(2, "%.*s is not 64 chars of hex", 64, p);
|
||||
p += 64;
|
||||
p += strspn(p, " \t");
|
||||
idx = strtoull(p, NULL, 0);
|
||||
if (shachain_add_hash(&s, idx, &hash))
|
||||
printf("OK\n");
|
||||
else
|
||||
printf("ERROR\n");
|
||||
}
|
||||
} else if (argc == 3) {
|
||||
struct sha256 seed, hash;
|
||||
const char *p;
|
||||
unsigned long long idx;
|
||||
char hex[65];
|
||||
|
||||
if (strstarts(argv[1], "0x"))
|
||||
p = argv[1] + 2;
|
||||
else
|
||||
p = argv[1];
|
||||
idx = strtoull(argv[2], NULL, 0);
|
||||
|
||||
if (!hex_decode(p, 64, &seed, sizeof(seed)))
|
||||
errx(2, "%s is not 64 chars of hex", p);
|
||||
|
||||
shachain_from_seed(&seed, idx, &hash);
|
||||
hex_encode(&hash, sizeof(hash), hex, sizeof(hex));
|
||||
printf("0x%s\n", hex);
|
||||
} else
|
||||
errx(1, "Usage: shachain --store OR shachain <seed> <index>");
|
||||
return 0;
|
||||
}
|
||||
1
ccan/ccan/fdpass/LICENSE
Symbolic link
1
ccan/ccan/fdpass/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/CC0
|
||||
65
ccan/ccan/fdpass/_info
Normal file
65
ccan/ccan/fdpass/_info
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* fdpass - routines to pass a file descriptor over a socket.
|
||||
*
|
||||
* This code handles all the hairy details of fd passing.
|
||||
*
|
||||
* License: CC0 (Public domain)
|
||||
* Maintainer: Rusty Russell <rusty@rustcorp.com.au>
|
||||
*
|
||||
* Example:
|
||||
* // Outputs hello!
|
||||
* #include <ccan/fdpass/fdpass.h>
|
||||
* #include <sys/socket.h>
|
||||
* #include <sys/un.h>
|
||||
* #include <stdio.h>
|
||||
* #include <stdlib.h>
|
||||
* #include <unistd.h>
|
||||
*
|
||||
* static void child(int sockfd)
|
||||
* {
|
||||
* char buffer[6];
|
||||
* int newfd = fdpass_recv(sockfd);
|
||||
* read(newfd, buffer, sizeof(buffer));
|
||||
* printf("%.*s\n", (int)sizeof(buffer), buffer);
|
||||
* exit(0);
|
||||
* }
|
||||
*
|
||||
* static void parent(int sockfd)
|
||||
* {
|
||||
* int pfds[2];
|
||||
*
|
||||
* pipe(pfds);
|
||||
* fdpass_send(sockfd, pfds[0]);
|
||||
* close(pfds[0]);
|
||||
* write(pfds[1], "hello!", 6);
|
||||
* exit(0);
|
||||
* }
|
||||
*
|
||||
* int main(void)
|
||||
* {
|
||||
* int sv[2];
|
||||
*
|
||||
* socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
* if (fork() == 0)
|
||||
* child(sv[0]);
|
||||
* else
|
||||
* parent(sv[1]);
|
||||
* }
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
83
ccan/ccan/fdpass/fdpass.c
Normal file
83
ccan/ccan/fdpass/fdpass.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/* CC0 license (public domain) - see LICENSE file for details */
|
||||
#include <ccan/fdpass/fdpass.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
bool fdpass_send(int sockout, int fd)
|
||||
{
|
||||
/* From the cmsg(3) manpage: */
|
||||
struct msghdr msg = { 0 };
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov;
|
||||
char c = 0;
|
||||
union { /* Ancillary data buffer, wrapped in a union
|
||||
in order to ensure it is suitably aligned */
|
||||
char buf[CMSG_SPACE(sizeof(fd))];
|
||||
struct cmsghdr align;
|
||||
} u;
|
||||
|
||||
msg.msg_control = u.buf;
|
||||
msg.msg_controllen = sizeof(u.buf);
|
||||
memset(&u, 0, sizeof(u));
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
|
||||
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
|
||||
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
/* Keith Packard reports that 0-length sends don't work, so we
|
||||
* always send 1 byte. */
|
||||
iov.iov_base = &c;
|
||||
iov.iov_len = 1;
|
||||
|
||||
return sendmsg(sockout, &msg, 0) == 1;
|
||||
}
|
||||
|
||||
int fdpass_recv(int sockin)
|
||||
{
|
||||
/* From the cmsg(3) manpage: */
|
||||
struct msghdr msg = { 0 };
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov;
|
||||
int fd;
|
||||
char c;
|
||||
union { /* Ancillary data buffer, wrapped in a union
|
||||
in order to ensure it is suitably aligned */
|
||||
char buf[CMSG_SPACE(sizeof(fd))];
|
||||
struct cmsghdr align;
|
||||
} u;
|
||||
|
||||
msg.msg_control = u.buf;
|
||||
msg.msg_controllen = sizeof(u.buf);
|
||||
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
iov.iov_base = &c;
|
||||
iov.iov_len = 1;
|
||||
|
||||
if (recvmsg(sockin, &msg, 0) < 0)
|
||||
return -1;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (!cmsg
|
||||
|| cmsg->cmsg_len != CMSG_LEN(sizeof(fd))
|
||||
|| cmsg->cmsg_level != SOL_SOCKET
|
||||
|| cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
errno = -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
|
||||
return fd;
|
||||
}
|
||||
23
ccan/ccan/fdpass/fdpass.h
Normal file
23
ccan/ccan/fdpass/fdpass.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* CC0 license (public domain) - see LICENSE file for details */
|
||||
#ifndef CCAN_FDPASS_H
|
||||
#define CCAN_FDPASS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* fdpass_send - send a file descriptor across a socket
|
||||
* @sockout: socket to write to
|
||||
* @fd: file descriptor to pass
|
||||
*
|
||||
* On failure, sets errno and returns false.
|
||||
*/
|
||||
bool fdpass_send(int sockout, int fd);
|
||||
|
||||
/**
|
||||
* fdpass_recv - receive a file descriptor from a socket
|
||||
* @sockin: socket to read from
|
||||
*
|
||||
* On failure, returns -1 and sets errno. Otherwise returns fd.
|
||||
*/
|
||||
int fdpass_recv(int sockin);
|
||||
#endif /* CCAN_FDPASS_H */
|
||||
88
ccan/ccan/fdpass/test/run.c
Normal file
88
ccan/ccan/fdpass/test/run.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <ccan/fdpass/fdpass.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/fdpass/fdpass.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
static void child(int sockfd)
|
||||
{
|
||||
char c;
|
||||
int newfd = fdpass_recv(sockfd);
|
||||
assert(newfd >= 0);
|
||||
assert(read(newfd, &c, 1) == 1);
|
||||
assert(c == 0x77);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void child_nofd(int sockfd)
|
||||
{
|
||||
assert(fdpass_recv(sockfd) == -1);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void parent(int sockfd)
|
||||
{
|
||||
int pfds[2];
|
||||
|
||||
ok1(pipe(pfds) == 0);
|
||||
ok1(fdpass_send(sockfd, pfds[0]));
|
||||
ok1(close(pfds[0]) == 0);
|
||||
ok1(write(pfds[1], "\x77", 1) == 1);
|
||||
ok1(close(pfds[1]) == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int sv[2];
|
||||
int pid, wstatus;
|
||||
|
||||
plan_tests(17);
|
||||
ok1(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
close(sv[1]);
|
||||
child(sv[0]);
|
||||
}
|
||||
|
||||
parent(sv[1]);
|
||||
ok1(waitpid(pid, &wstatus, 0) == pid);
|
||||
ok1(WIFEXITED(wstatus));
|
||||
ok1(WEXITSTATUS(wstatus) == 0);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
close(sv[1]);
|
||||
child_nofd(sv[0]);
|
||||
}
|
||||
/* Don't write an fd. */
|
||||
ok1(write(sv[1], "1", 1) == 1);
|
||||
ok1(waitpid(pid, &wstatus, 0) == pid);
|
||||
ok1(WIFEXITED(wstatus));
|
||||
ok1(WEXITSTATUS(wstatus) == 0);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
close(sv[1]);
|
||||
child_nofd(sv[0]);
|
||||
}
|
||||
/* Don't write anything. */
|
||||
close(sv[1]);
|
||||
ok1(waitpid(pid, &wstatus, 0) == pid);
|
||||
ok1(WIFEXITED(wstatus));
|
||||
ok1(WEXITSTATUS(wstatus) == 0);
|
||||
|
||||
close(sv[0]);
|
||||
/* Test fdpass_recv from invalid fd. */
|
||||
ok1(fdpass_recv(sv[0]) == -1 && errno == EBADF);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
@@ -31,9 +31,7 @@ enum io_plan_status {
|
||||
/* Waiting for io_wake */
|
||||
IO_WAITING,
|
||||
/* Always do this. */
|
||||
IO_ALWAYS,
|
||||
/* Closing (both plans will be the same). */
|
||||
IO_CLOSING
|
||||
IO_ALWAYS
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -59,12 +57,9 @@ struct io_plan {
|
||||
/* One connection per client. */
|
||||
struct io_conn {
|
||||
struct fd fd;
|
||||
bool debug;
|
||||
/* For duplex to save. */
|
||||
bool debug_saved;
|
||||
|
||||
/* always and closing lists. */
|
||||
struct list_node always, closing;
|
||||
/* always list. */
|
||||
struct list_node always;
|
||||
|
||||
void (*finish)(struct io_conn *, void *arg);
|
||||
void *finish_arg;
|
||||
@@ -78,7 +73,6 @@ bool add_listener(struct io_listener *l);
|
||||
bool add_conn(struct io_conn *c);
|
||||
bool add_duplex(struct io_conn *c);
|
||||
void del_listener(struct io_listener *l);
|
||||
void backend_new_closing(struct io_conn *conn);
|
||||
void backend_new_always(struct io_conn *conn);
|
||||
void backend_new_plan(struct io_conn *conn);
|
||||
void remove_from_always(struct io_conn *conn);
|
||||
|
||||
1
ccan/ccan/io/fdpass/LICENSE
Symbolic link
1
ccan/ccan/io/fdpass/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../licenses/LGPL-2.1
|
||||
95
ccan/ccan/io/fdpass/_info
Normal file
95
ccan/ccan/io/fdpass/_info
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* io/fdpass - IO helper for passing file descriptors across local sockets
|
||||
*
|
||||
* This code adds the ability to pass file descriptors to ccan/io.
|
||||
*
|
||||
* License: LGPL (v2.1 or any later version)
|
||||
* Author: Rusty Russell <rusty@rustcorp.com.au>
|
||||
*
|
||||
* Example:
|
||||
* // Given "hello" outputs hello
|
||||
* #include <ccan/io/fdpass/fdpass.h>
|
||||
* #include <sys/types.h>
|
||||
* #include <sys/socket.h>
|
||||
* #include <sys/un.h>
|
||||
* #include <stdio.h>
|
||||
* #include <stdlib.h>
|
||||
* #include <unistd.h>
|
||||
*
|
||||
* // Child reads stdin into the buffer, prints it out.
|
||||
* struct buf {
|
||||
* size_t used;
|
||||
* char c[100];
|
||||
* };
|
||||
* static struct io_plan *read_more(struct io_conn *conn, struct buf *buf)
|
||||
* {
|
||||
* printf("%.*s", (int)buf->used, buf->c);
|
||||
* return io_read_partial(conn, buf->c, sizeof(buf->c), &buf->used,
|
||||
* read_more, buf);
|
||||
* }
|
||||
*
|
||||
* // Child has received fd, start reading loop.
|
||||
* static struct io_plan *got_infd(struct io_conn *conn, int *infd)
|
||||
* {
|
||||
* struct buf *buf = calloc(1, sizeof(*buf));
|
||||
*
|
||||
* io_new_conn(NULL, *infd, read_more, buf);
|
||||
* return io_close(conn);
|
||||
* }
|
||||
* // Child is receiving the fd to read into.
|
||||
* static struct io_plan *recv_infd(struct io_conn *conn, int *infd)
|
||||
* {
|
||||
* return io_recv_fd(conn, infd, got_infd, infd);
|
||||
* }
|
||||
*
|
||||
* // Gets passed fd (stdin), which it reads from.
|
||||
* static void child(int sockfd)
|
||||
* {
|
||||
* int infd;
|
||||
*
|
||||
* io_new_conn(NULL, sockfd, recv_infd, &infd);
|
||||
* io_loop(NULL, NULL);
|
||||
* exit(0);
|
||||
* }
|
||||
*
|
||||
* static struct io_plan *send_stdin(struct io_conn *conn, void *unused)
|
||||
* {
|
||||
* return io_send_fd(conn, STDIN_FILENO, io_close_cb, NULL);
|
||||
* }
|
||||
*
|
||||
* static void parent(int sockfd)
|
||||
* {
|
||||
* io_new_conn(NULL, sockfd, send_stdin, NULL);
|
||||
* io_loop(NULL, NULL);
|
||||
* exit(0);
|
||||
* }
|
||||
*
|
||||
* int main(void)
|
||||
* {
|
||||
* int sv[2];
|
||||
*
|
||||
* socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
* if (fork() == 0)
|
||||
* child(sv[0]);
|
||||
* else
|
||||
* parent(sv[1]);
|
||||
* }
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Expect exactly one argument */
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/fdpass\n");
|
||||
printf("ccan/io\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
54
ccan/ccan/io/fdpass/fdpass.c
Normal file
54
ccan/ccan/io/fdpass/fdpass.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/* GNU LGPL version 2 (or later) - see LICENSE file for details */
|
||||
#include <ccan/io/fdpass/fdpass.h>
|
||||
#include <ccan/fdpass/fdpass.h>
|
||||
#include <ccan/io/io_plan.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int do_fd_send(int fd, struct io_plan_arg *arg)
|
||||
{
|
||||
if (!fdpass_send(fd, arg->u1.s)) {
|
||||
/* In case ccan/io ever gets smart with non-blocking. */
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct io_plan *io_send_fd_(struct io_conn *conn,
|
||||
int fd,
|
||||
struct io_plan *(*next)(struct io_conn *, void *),
|
||||
void *next_arg)
|
||||
{
|
||||
struct io_plan_arg *arg = io_plan_arg(conn, IO_OUT);
|
||||
|
||||
arg->u1.s = fd;
|
||||
|
||||
return io_set_plan(conn, IO_OUT, do_fd_send, next, next_arg);
|
||||
}
|
||||
|
||||
static int do_fd_recv(int fd, struct io_plan_arg *arg)
|
||||
{
|
||||
int fdin = fdpass_recv(fd);
|
||||
|
||||
if (fdin < 0) {
|
||||
/* In case ccan/io ever gets smart with non-blocking. */
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
*(int *)arg->u1.vp = fdin;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct io_plan *io_recv_fd_(struct io_conn *conn,
|
||||
int *fd,
|
||||
struct io_plan *(*next)(struct io_conn *, void *),
|
||||
void *next_arg)
|
||||
{
|
||||
struct io_plan_arg *arg = io_plan_arg(conn, IO_IN);
|
||||
|
||||
arg->u1.vp = fd;
|
||||
|
||||
return io_set_plan(conn, IO_IN, do_fd_recv, next, next_arg);
|
||||
}
|
||||
68
ccan/ccan/io/fdpass/fdpass.h
Normal file
68
ccan/ccan/io/fdpass/fdpass.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* GNU LGPL version 2 (or later) - see LICENSE file for details */
|
||||
#ifndef CCAN_IO_FDPASS_H
|
||||
#define CCAN_IO_FDPASS_H
|
||||
#include <ccan/io/io.h>
|
||||
|
||||
/**
|
||||
* io_send_fd - output plan to send a file descriptor
|
||||
* @conn: the connection that plan is for.
|
||||
* @fd: the file descriptor to pass.
|
||||
* @next: function to call output is done.
|
||||
* @arg: @next argument
|
||||
*
|
||||
* This updates the output plan, to write out a file descriptor. This
|
||||
* usually only works over an AF_LOCAL (ie. Unix domain) socket. Once
|
||||
* that's sent, the @next function will be called: on an error, the
|
||||
* finish function is called instead.
|
||||
*
|
||||
* Note that the I/O may actually be done immediately, and the other end
|
||||
* of the socket must use io_recv_fd: if it does a normal read, the file
|
||||
* descriptor will be lost.
|
||||
*
|
||||
* Example:
|
||||
* static struct io_plan *fd_to_conn(struct io_conn *conn, int fd)
|
||||
* {
|
||||
* // Write fd, then close.
|
||||
* return io_send_fd(conn, fd, io_close_cb, NULL);
|
||||
* }
|
||||
*/
|
||||
#define io_send_fd(conn, fd, next, arg) \
|
||||
io_send_fd_((conn), (fd), \
|
||||
typesafe_cb_preargs(struct io_plan *, void *, \
|
||||
(next), (arg), struct io_conn *), \
|
||||
(arg))
|
||||
struct io_plan *io_send_fd_(struct io_conn *conn,
|
||||
int fd,
|
||||
struct io_plan *(*next)(struct io_conn *, void *),
|
||||
void *arg);
|
||||
|
||||
/**
|
||||
* io_recv_fd - input plan to receive a file descriptor
|
||||
* @conn: the connection that plan is for.
|
||||
* @fd: a pointer to where to place to file descriptor
|
||||
* @next: function to call once input is done.
|
||||
* @arg: @next argument
|
||||
*
|
||||
* This creates a plan to receive a file descriptor, as sent by
|
||||
* io_send_fd. Once it's all read, the @next function will be called:
|
||||
* on an error, the finish function is called instead.
|
||||
*
|
||||
* Note that the I/O may actually be done immediately.
|
||||
*
|
||||
* Example:
|
||||
* static struct io_plan *read_from_conn(struct io_conn *conn, int *fdp)
|
||||
* {
|
||||
* // Read message, then close.
|
||||
* return io_recv_fd(conn, fdp, io_close_cb, NULL);
|
||||
* }
|
||||
*/
|
||||
#define io_recv_fd(conn, fd, next, arg) \
|
||||
io_recv_fd_((conn), (fd), \
|
||||
typesafe_cb_preargs(struct io_plan *, void *, \
|
||||
(next), (arg), struct io_conn *), \
|
||||
(arg))
|
||||
struct io_plan *io_recv_fd_(struct io_conn *conn,
|
||||
int *fd,
|
||||
struct io_plan *(*next)(struct io_conn *, void *),
|
||||
void *arg);
|
||||
#endif /* CCAN_IO_FDPASS_H */
|
||||
51
ccan/ccan/io/fdpass/test/run.c
Normal file
51
ccan/ccan/io/fdpass/test/run.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <ccan/io/fdpass/fdpass.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/io/fdpass/fdpass.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
static struct io_plan *try_reading(struct io_conn *conn, int *fd)
|
||||
{
|
||||
char buf[6];
|
||||
ok1(read(*fd, buf, sizeof(buf)) == sizeof(buf));
|
||||
ok1(memcmp(buf, "hello!", sizeof(buf)) == 0);
|
||||
return io_close(conn);
|
||||
}
|
||||
|
||||
static struct io_plan *get_fd(struct io_conn *conn, void *unused)
|
||||
{
|
||||
int *fd = tal(conn, int);
|
||||
return io_recv_fd(conn, fd, try_reading, fd);
|
||||
}
|
||||
|
||||
static struct io_plan *try_writing(struct io_conn *conn, int *pfd)
|
||||
{
|
||||
close(pfd[0]);
|
||||
ok1(write(pfd[1], "hello!", 6) == 6);
|
||||
return io_close(conn);
|
||||
}
|
||||
|
||||
static struct io_plan *send_fd(struct io_conn *conn, int *pfd)
|
||||
{
|
||||
return io_send_fd(conn, pfd[0], try_writing, pfd);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int sv[2];
|
||||
int pfd[2];
|
||||
|
||||
plan_tests(5);
|
||||
ok1(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
|
||||
ok1(pipe(pfd) == 0);
|
||||
|
||||
/* Pass read end of pipe to ourselves, test. */
|
||||
io_new_conn(NULL, sv[0], get_fd, NULL);
|
||||
io_new_conn(NULL, sv[1], send_fd, pfd);
|
||||
|
||||
io_loop(NULL, NULL);
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
void *io_loop_return;
|
||||
|
||||
struct io_plan io_conn_freed;
|
||||
|
||||
struct io_listener *io_new_listener_(const tal_t *ctx, int fd,
|
||||
struct io_plan *(*init)(struct io_conn *,
|
||||
void *),
|
||||
@@ -35,8 +37,6 @@ struct io_listener *io_new_listener_(const tal_t *ctx, int fd,
|
||||
|
||||
void io_close_listener(struct io_listener *l)
|
||||
{
|
||||
close(l->fd.fd);
|
||||
del_listener(l);
|
||||
tal_free(l);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@ static struct io_plan *io_never_called(struct io_conn *conn, void *arg)
|
||||
abort();
|
||||
}
|
||||
|
||||
static void next_plan(struct io_conn *conn, struct io_plan *plan)
|
||||
/* Returns false if conn was freed. */
|
||||
static bool next_plan(struct io_conn *conn, struct io_plan *plan)
|
||||
{
|
||||
struct io_plan *(*next)(struct io_conn *, void *arg);
|
||||
|
||||
@@ -57,6 +58,9 @@ static void next_plan(struct io_conn *conn, struct io_plan *plan)
|
||||
|
||||
plan = next(conn, plan->next_arg);
|
||||
|
||||
if (plan == &io_conn_freed)
|
||||
return false;
|
||||
|
||||
/* It should have set a plan inside this conn (or duplex) */
|
||||
assert(plan == &conn->plan[IO_IN]
|
||||
|| plan == &conn->plan[IO_OUT]
|
||||
@@ -65,6 +69,7 @@ static void next_plan(struct io_conn *conn, struct io_plan *plan)
|
||||
|| conn->plan[IO_OUT].status != IO_UNSET);
|
||||
|
||||
backend_new_plan(conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void set_blocking(int fd, bool block)
|
||||
@@ -93,8 +98,6 @@ struct io_conn *io_new_conn_(const tal_t *ctx, int fd,
|
||||
conn->finish = NULL;
|
||||
conn->finish_arg = NULL;
|
||||
list_node_init(&conn->always);
|
||||
list_node_init(&conn->closing);
|
||||
conn->debug = false;
|
||||
|
||||
if (!add_conn(conn))
|
||||
return tal_free(conn);
|
||||
@@ -107,7 +110,8 @@ struct io_conn *io_new_conn_(const tal_t *ctx, int fd,
|
||||
|
||||
conn->plan[IO_IN].next = init;
|
||||
conn->plan[IO_IN].next_arg = arg;
|
||||
next_plan(conn, &conn->plan[IO_IN]);
|
||||
if (!next_plan(conn, &conn->plan[IO_IN]))
|
||||
return NULL;
|
||||
|
||||
return conn;
|
||||
}
|
||||
@@ -356,24 +360,20 @@ void io_wake(const void *wait)
|
||||
backend_wake(wait);
|
||||
}
|
||||
|
||||
static int do_plan(struct io_conn *conn, struct io_plan *plan)
|
||||
/* Returns false if this has been freed. */
|
||||
static bool do_plan(struct io_conn *conn, struct io_plan *plan)
|
||||
{
|
||||
/* Someone else might have called io_close() on us. */
|
||||
if (plan->status == IO_CLOSING)
|
||||
return -1;
|
||||
|
||||
/* We shouldn't have polled for this event if this wasn't true! */
|
||||
assert(plan->status == IO_POLLING);
|
||||
|
||||
switch (plan->io(conn->fd.fd, &plan->arg)) {
|
||||
case -1:
|
||||
io_close(conn);
|
||||
return -1;
|
||||
return false;
|
||||
case 0:
|
||||
return 0;
|
||||
return true;
|
||||
case 1:
|
||||
next_plan(conn, plan);
|
||||
return 1;
|
||||
return next_plan(conn, plan);
|
||||
default:
|
||||
/* IO should only return -1, 0 or 1 */
|
||||
abort();
|
||||
@@ -383,7 +383,8 @@ static int do_plan(struct io_conn *conn, struct io_plan *plan)
|
||||
void io_ready(struct io_conn *conn, int pollflags)
|
||||
{
|
||||
if (pollflags & POLLIN)
|
||||
do_plan(conn, &conn->plan[IO_IN]);
|
||||
if (!do_plan(conn, &conn->plan[IO_IN]))
|
||||
return;
|
||||
|
||||
if (pollflags & POLLOUT)
|
||||
do_plan(conn, &conn->plan[IO_OUT]);
|
||||
@@ -392,7 +393,8 @@ void io_ready(struct io_conn *conn, int pollflags)
|
||||
void io_do_always(struct io_conn *conn)
|
||||
{
|
||||
if (conn->plan[IO_IN].status == IO_ALWAYS)
|
||||
next_plan(conn, &conn->plan[IO_IN]);
|
||||
if (!next_plan(conn, &conn->plan[IO_IN]))
|
||||
return;
|
||||
|
||||
if (conn->plan[IO_OUT].status == IO_ALWAYS)
|
||||
next_plan(conn, &conn->plan[IO_OUT]);
|
||||
@@ -410,15 +412,8 @@ void io_do_wakeup(struct io_conn *conn, enum io_direction dir)
|
||||
/* Close the connection, we're done. */
|
||||
struct io_plan *io_close(struct io_conn *conn)
|
||||
{
|
||||
/* Already closing? Don't close twice. */
|
||||
if (conn->plan[IO_IN].status == IO_CLOSING)
|
||||
return &conn->plan[IO_IN];
|
||||
|
||||
conn->plan[IO_IN].status = conn->plan[IO_OUT].status = IO_CLOSING;
|
||||
conn->plan[IO_IN].arg.u1.s = errno;
|
||||
backend_new_closing(conn);
|
||||
|
||||
return io_set_plan(conn, IO_IN, NULL, NULL, NULL);
|
||||
tal_free(conn);
|
||||
return &io_conn_freed;
|
||||
}
|
||||
|
||||
struct io_plan *io_close_cb(struct io_conn *conn, void *next_arg)
|
||||
@@ -443,43 +438,17 @@ int io_conn_fd(const struct io_conn *conn)
|
||||
return conn->fd.fd;
|
||||
}
|
||||
|
||||
void io_duplex_prepare(struct io_conn *conn)
|
||||
struct io_plan *io_duplex(struct io_conn *conn,
|
||||
struct io_plan *in_plan, struct io_plan *out_plan)
|
||||
{
|
||||
assert(conn->plan[IO_IN].status == IO_UNSET);
|
||||
assert(conn->plan[IO_OUT].status == IO_UNSET);
|
||||
|
||||
/* We can't sync debug until we've set both: io_wait() and io_always
|
||||
* can't handle it. */
|
||||
conn->debug_saved = conn->debug;
|
||||
io_set_debug(conn, false);
|
||||
}
|
||||
|
||||
struct io_plan *io_duplex_(struct io_plan *in_plan, struct io_plan *out_plan)
|
||||
{
|
||||
struct io_conn *conn;
|
||||
|
||||
assert(conn == container_of(in_plan, struct io_conn, plan[IO_IN]));
|
||||
/* in_plan must be conn->plan[IO_IN], out_plan must be [IO_OUT] */
|
||||
assert(out_plan == in_plan + 1);
|
||||
|
||||
/* Restore debug. */
|
||||
conn = container_of(in_plan, struct io_conn, plan[IO_IN]);
|
||||
io_set_debug(conn, conn->debug_saved);
|
||||
|
||||
/* Now set the plans again, to invoke sync debug. */
|
||||
io_set_plan(conn, IO_OUT,
|
||||
out_plan->io, out_plan->next, out_plan->next_arg);
|
||||
io_set_plan(conn, IO_IN,
|
||||
in_plan->io, in_plan->next, in_plan->next_arg);
|
||||
|
||||
return out_plan + 1;
|
||||
}
|
||||
|
||||
struct io_plan *io_halfclose(struct io_conn *conn)
|
||||
{
|
||||
/* Already closing? Don't close twice. */
|
||||
if (conn->plan[IO_IN].status == IO_CLOSING)
|
||||
return &conn->plan[IO_IN];
|
||||
|
||||
/* Both unset? OK. */
|
||||
if (conn->plan[IO_IN].status == IO_UNSET
|
||||
&& conn->plan[IO_OUT].status == IO_UNSET)
|
||||
@@ -502,45 +471,7 @@ struct io_plan *io_set_plan(struct io_conn *conn, enum io_direction dir,
|
||||
plan->io = io;
|
||||
plan->next = next;
|
||||
plan->next_arg = next_arg;
|
||||
assert(plan->status == IO_CLOSING || next != NULL);
|
||||
|
||||
if (!conn->debug)
|
||||
return plan;
|
||||
|
||||
if (io_loop_return) {
|
||||
io_debug_complete(conn);
|
||||
return plan;
|
||||
}
|
||||
|
||||
switch (plan->status) {
|
||||
case IO_POLLING:
|
||||
while (do_plan(conn, plan) == 0);
|
||||
break;
|
||||
/* Shouldn't happen, since you said you did plan! */
|
||||
case IO_UNSET:
|
||||
abort();
|
||||
case IO_ALWAYS:
|
||||
/* If other one is ALWAYS, leave in list! */
|
||||
if (conn->plan[!dir].status != IO_ALWAYS)
|
||||
remove_from_always(conn);
|
||||
next_plan(conn, plan);
|
||||
break;
|
||||
case IO_WAITING:
|
||||
case IO_CLOSING:
|
||||
io_debug_complete(conn);
|
||||
}
|
||||
assert(next != NULL);
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
||||
void io_set_debug(struct io_conn *conn, bool debug)
|
||||
{
|
||||
conn->debug = debug;
|
||||
|
||||
/* Debugging means fds must block. */
|
||||
set_blocking(io_conn_fd(conn), debug);
|
||||
}
|
||||
|
||||
void io_debug_complete(struct io_conn *conn)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -454,11 +454,8 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr,
|
||||
* io_write(conn, b->out, sizeof(b->out), io_close_cb,b));
|
||||
* }
|
||||
*/
|
||||
#define io_duplex(conn, in_plan, out_plan) \
|
||||
(io_duplex_prepare(conn), io_duplex_(in_plan, out_plan))
|
||||
|
||||
struct io_plan *io_duplex_(struct io_plan *in_plan, struct io_plan *out_plan);
|
||||
void io_duplex_prepare(struct io_conn *conn);
|
||||
struct io_plan *io_duplex(struct io_conn *conn,
|
||||
struct io_plan *in_plan, struct io_plan *out_plan);
|
||||
|
||||
/**
|
||||
* io_halfclose - close half of an io_duplex connection.
|
||||
@@ -660,37 +657,4 @@ int io_conn_fd(const struct io_conn *conn);
|
||||
*/
|
||||
struct timemono (*io_time_override(struct timemono (*now)(void)))(void);
|
||||
|
||||
/**
|
||||
* io_set_debug - set synchronous mode on a connection.
|
||||
* @conn: the connection.
|
||||
* @debug: whether to enable or disable debug.
|
||||
*
|
||||
* Once @debug is true on a connection, all I/O is done synchronously
|
||||
* as soon as it is set, until it is unset or @conn is closed. This
|
||||
* makes it easy to debug what's happening with a connection, but note
|
||||
* that other connections are starved while this is being done.
|
||||
*
|
||||
* See also: io_debug_complete()
|
||||
*
|
||||
* Example:
|
||||
* // Dumb init function to set debug and tell conn to close.
|
||||
* static struct io_plan *conn_init(struct io_conn *conn, const char *msg)
|
||||
* {
|
||||
* io_set_debug(conn, true);
|
||||
* return io_close(conn);
|
||||
* }
|
||||
*/
|
||||
void io_set_debug(struct io_conn *conn, bool debug);
|
||||
|
||||
/**
|
||||
* io_debug_complete - empty function called when conn is closing/waiting.
|
||||
* @conn: the connection.
|
||||
*
|
||||
* This is for putting a breakpoint onto, when debugging. It is called
|
||||
* when a conn with io_set_debug() true can no longer be synchronous:
|
||||
* 1) It is io_close()'d
|
||||
* 2) It enters io_wait() (sychronous debug will resume after io_wake())
|
||||
* 3) io_break() is called (sychronous debug will resume after io_loop())
|
||||
*/
|
||||
void io_debug_complete(struct io_conn *conn);
|
||||
#endif /* CCAN_IO_H */
|
||||
|
||||
@@ -95,10 +95,17 @@ static void del_fd(struct fd *fd)
|
||||
close(fd->fd);
|
||||
}
|
||||
|
||||
static void destroy_listener(struct io_listener *l)
|
||||
{
|
||||
close(l->fd.fd);
|
||||
del_fd(&l->fd);
|
||||
}
|
||||
|
||||
bool add_listener(struct io_listener *l)
|
||||
{
|
||||
if (!add_fd(&l->fd, POLLIN))
|
||||
return false;
|
||||
tal_add_destructor(l, destroy_listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -107,13 +114,6 @@ void remove_from_always(struct io_conn *conn)
|
||||
list_del_init(&conn->always);
|
||||
}
|
||||
|
||||
void backend_new_closing(struct io_conn *conn)
|
||||
{
|
||||
/* In case it's on always list, remove it. */
|
||||
list_del_init(&conn->always);
|
||||
list_add_tail(&closing, &conn->closing);
|
||||
}
|
||||
|
||||
void backend_new_always(struct io_conn *conn)
|
||||
{
|
||||
/* In case it's already in always list. */
|
||||
@@ -164,25 +164,28 @@ void backend_wake(const void *wait)
|
||||
}
|
||||
}
|
||||
|
||||
bool add_conn(struct io_conn *c)
|
||||
static void destroy_conn(struct io_conn *conn)
|
||||
{
|
||||
return add_fd(&c->fd, 0);
|
||||
}
|
||||
int saved_errno = errno;
|
||||
|
||||
static void del_conn(struct io_conn *conn)
|
||||
{
|
||||
close(conn->fd.fd);
|
||||
del_fd(&conn->fd);
|
||||
/* In case it's on always list, remove it. */
|
||||
list_del_init(&conn->always);
|
||||
|
||||
/* errno saved/restored by tal_free itself. */
|
||||
if (conn->finish) {
|
||||
/* Saved by io_close */
|
||||
errno = conn->plan[IO_IN].arg.u1.s;
|
||||
errno = saved_errno;
|
||||
conn->finish(conn, conn->finish_arg);
|
||||
}
|
||||
tal_free(conn);
|
||||
}
|
||||
|
||||
void del_listener(struct io_listener *l)
|
||||
bool add_conn(struct io_conn *c)
|
||||
{
|
||||
del_fd(&l->fd);
|
||||
if (!add_fd(&c->fd, 0))
|
||||
return false;
|
||||
tal_add_destructor(c, destroy_conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void accept_conn(struct io_listener *l)
|
||||
@@ -196,22 +199,6 @@ static void accept_conn(struct io_listener *l)
|
||||
io_new_conn(l->ctx, fd, l->init, l->arg);
|
||||
}
|
||||
|
||||
/* It's OK to miss some, as long as we make progress. */
|
||||
static bool close_conns(void)
|
||||
{
|
||||
bool ret = false;
|
||||
struct io_conn *conn;
|
||||
|
||||
while ((conn = list_pop(&closing, struct io_conn, closing)) != NULL) {
|
||||
assert(conn->plan[IO_IN].status == IO_CLOSING);
|
||||
assert(conn->plan[IO_OUT].status == IO_CLOSING);
|
||||
|
||||
del_conn(conn);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool handle_always(void)
|
||||
{
|
||||
bool ret = false;
|
||||
@@ -244,11 +231,6 @@ void *io_loop(struct timers *timers, struct timer **expired)
|
||||
while (!io_loop_return) {
|
||||
int i, r, ms_timeout = -1;
|
||||
|
||||
if (close_conns()) {
|
||||
/* Could have started/finished more. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (handle_always()) {
|
||||
/* Could have started/finished more. */
|
||||
continue;
|
||||
@@ -309,8 +291,6 @@ void *io_loop(struct timers *timers, struct timer **expired)
|
||||
}
|
||||
}
|
||||
|
||||
close_conns();
|
||||
|
||||
ret = io_loop_return;
|
||||
io_loop_return = NULL;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-01-start-finish.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64001"
|
||||
#else
|
||||
#define PORT "65001"
|
||||
#endif
|
||||
static int expected_fd;
|
||||
|
||||
static void finish_ok(struct io_conn *conn, int *state)
|
||||
@@ -23,9 +19,6 @@ static void finish_ok(struct io_conn *conn, int *state)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, int *state)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(*state == 0);
|
||||
(*state)++;
|
||||
expected_fd = io_conn_fd(conn);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-02-read.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64002"
|
||||
#else
|
||||
#define PORT "65002"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
int state;
|
||||
@@ -26,9 +22,6 @@ static void finish_ok(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-03-readpartial.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64003"
|
||||
#else
|
||||
#define PORT "65003"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
int state;
|
||||
@@ -27,9 +23,6 @@ static void finish_ok(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-04-writepartial.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64004"
|
||||
#else
|
||||
#define PORT "65004"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
int state;
|
||||
@@ -27,9 +23,6 @@ static void finish_ok(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
io_set_finish(conn, finish_ok, d);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-05-write.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64005"
|
||||
#else
|
||||
#define PORT "65005"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
int state;
|
||||
@@ -27,9 +23,6 @@ static void finish_ok(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
io_set_finish(conn, finish_ok, d);
|
||||
|
||||
@@ -9,11 +9,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64006"
|
||||
#else
|
||||
#define PORT "65006"
|
||||
#endif
|
||||
|
||||
static struct io_conn *idler;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-07-break.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64007"
|
||||
#else
|
||||
#define PORT "65007"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
int state;
|
||||
@@ -32,9 +28,6 @@ static void finish_ok(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-09-connect.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64009"
|
||||
#else
|
||||
#define PORT "65009"
|
||||
#endif
|
||||
|
||||
static struct io_listener *l;
|
||||
static struct data *d2;
|
||||
@@ -35,9 +31,6 @@ static struct io_plan *connected(struct io_conn *conn, struct data *d2)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
io_close_listener(l);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-12-bidir.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64012"
|
||||
#else
|
||||
#define PORT "65012"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
struct io_listener *l;
|
||||
@@ -42,9 +38,6 @@ static struct io_plan *w_done(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-14-duplex-both-read.c"
|
||||
@@ -8,11 +8,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64014"
|
||||
#else
|
||||
#define PORT "65014"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
struct io_listener *l;
|
||||
@@ -47,9 +43,6 @@ static struct io_plan *make_duplex(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
|
||||
|
||||
@@ -8,11 +8,7 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64015"
|
||||
#else
|
||||
#define PORT "65015"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
struct timers timers;
|
||||
@@ -38,9 +34,6 @@ static struct io_plan *no_timeout(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-16-duplex-test.c"
|
||||
@@ -8,11 +8,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64016"
|
||||
#else
|
||||
#define PORT "65016"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
struct io_listener *l;
|
||||
@@ -34,9 +30,6 @@ static struct io_plan *io_done(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-17-homemade-io.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64017"
|
||||
#else
|
||||
#define PORT "65017"
|
||||
#endif
|
||||
|
||||
struct packet {
|
||||
int state;
|
||||
@@ -85,9 +81,6 @@ static struct io_plan *io_read_packet(struct io_conn *conn,
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct packet *pkt)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(pkt->state == 0);
|
||||
pkt->state++;
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-18-errno.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64018"
|
||||
#else
|
||||
#define PORT "65018"
|
||||
#endif
|
||||
|
||||
static void finish_100(struct io_conn *conn, int *state)
|
||||
{
|
||||
@@ -29,13 +25,10 @@ static void finish_EBADF(struct io_conn *conn, int *state)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, int *state)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
if (*state == 0) {
|
||||
(*state)++;
|
||||
errno = 100;
|
||||
io_set_finish(conn, finish_100, state);
|
||||
errno = 100;
|
||||
return io_close(conn);
|
||||
} else {
|
||||
ok1(*state == 2);
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#define DEBUG_CONN
|
||||
#include "run-19-always.c"
|
||||
@@ -6,11 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DEBUG_CONN
|
||||
#define PORT "64019"
|
||||
#else
|
||||
#define PORT "65019"
|
||||
#endif
|
||||
|
||||
struct data {
|
||||
int state;
|
||||
@@ -32,9 +28,6 @@ static struct io_plan *write_buf(struct io_conn *conn, struct data *d)
|
||||
|
||||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d)
|
||||
{
|
||||
#ifdef DEBUG_CONN
|
||||
io_set_debug(conn, true);
|
||||
#endif
|
||||
ok1(d->state == 0);
|
||||
d->state++;
|
||||
io_set_finish(conn, finish_ok, d);
|
||||
|
||||
1
ccan/ccan/ptr_valid/LICENSE
Symbolic link
1
ccan/ccan/ptr_valid/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/BSD-MIT
|
||||
29
ccan/ccan/ptr_valid/_info
Normal file
29
ccan/ccan/ptr_valid/_info
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* ptr_valid - test whether a pointer is safe to dereference.
|
||||
*
|
||||
* This little helper tells you if an address is mapped; it doesn't tell you
|
||||
* if it's read-only (or execute only).
|
||||
*
|
||||
* License: BSD-MIT
|
||||
*
|
||||
* Ccanlint:
|
||||
* // Our child actually crashes, but that's OK!
|
||||
* tests_pass_valgrind test/run.c:--child-silent-after-fork=yes
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
/* Expect exactly one argument */
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
if (strcmp(argv[1], "depends") == 0) {
|
||||
printf("ccan/noerr\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
344
ccan/ccan/ptr_valid/ptr_valid.c
Normal file
344
ccan/ccan/ptr_valid/ptr_valid.c
Normal file
@@ -0,0 +1,344 @@
|
||||
// Licensed under BSD-MIT: See LICENSE.
|
||||
#include "ptr_valid.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <ccan/noerr/noerr.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_PROC_SELF_MAPS
|
||||
static char *grab(const char *filename)
|
||||
{
|
||||
int ret, fd;
|
||||
size_t max = 16384, s = 0;
|
||||
char *buffer;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
buffer = malloc(max+1);
|
||||
if (!buffer)
|
||||
goto close;
|
||||
|
||||
while ((ret = read(fd, buffer + s, max - s)) > 0) {
|
||||
s += ret;
|
||||
if (s == max) {
|
||||
buffer = realloc(buffer, max*2+1);
|
||||
if (!buffer)
|
||||
goto close;
|
||||
max *= 2;
|
||||
}
|
||||
}
|
||||
if (ret < 0)
|
||||
goto free;
|
||||
|
||||
close(fd);
|
||||
buffer[s] = '\0';
|
||||
return buffer;
|
||||
|
||||
free:
|
||||
free(buffer);
|
||||
close:
|
||||
close_noerr(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *skip_line(char *p)
|
||||
{
|
||||
char *nl = strchr(p, '\n');
|
||||
if (!nl)
|
||||
return NULL;
|
||||
return nl + 1;
|
||||
}
|
||||
|
||||
static struct ptr_valid_map *add_map(struct ptr_valid_map *map,
|
||||
unsigned int *num,
|
||||
unsigned int *max,
|
||||
unsigned long start, unsigned long end, bool is_write)
|
||||
{
|
||||
if (*num == *max) {
|
||||
*max *= 2;
|
||||
map = realloc(map, sizeof(*map) * *max);
|
||||
if (!map)
|
||||
return NULL;
|
||||
}
|
||||
map[*num].start = (void *)start;
|
||||
map[*num].end = (void *)end;
|
||||
map[*num].is_write = is_write;
|
||||
(*num)++;
|
||||
return map;
|
||||
}
|
||||
|
||||
static struct ptr_valid_map *get_proc_maps(unsigned int *num)
|
||||
{
|
||||
char *buf, *p;
|
||||
struct ptr_valid_map *map;
|
||||
unsigned int max = 16;
|
||||
|
||||
buf = grab("/proc/self/maps");
|
||||
if (!buf) {
|
||||
*num = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
map = malloc(sizeof(*map) * max);
|
||||
if (!map)
|
||||
goto free_buf;
|
||||
|
||||
*num = 0;
|
||||
for (p = buf; p && *p; p = skip_line(p)) {
|
||||
unsigned long start, end;
|
||||
char *endp;
|
||||
|
||||
/* Expect '<start-in-hex>-<end-in-hex> rw... */
|
||||
start = strtoul(p, &endp, 16);
|
||||
if (*endp != '-')
|
||||
goto malformed;
|
||||
end = strtoul(endp+1, &endp, 16);
|
||||
if (*endp != ' ')
|
||||
goto malformed;
|
||||
|
||||
endp++;
|
||||
if (endp[0] != 'r' && endp[0] != '-')
|
||||
goto malformed;
|
||||
if (endp[1] != 'w' && endp[1] != '-')
|
||||
goto malformed;
|
||||
|
||||
/* We only add readable mappings. */
|
||||
if (endp[0] == 'r') {
|
||||
map = add_map(map, num, &max, start, end,
|
||||
endp[1] == 'w');
|
||||
if (!map)
|
||||
goto free_buf;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
return map;
|
||||
|
||||
|
||||
malformed:
|
||||
free(map);
|
||||
free_buf:
|
||||
free(buf);
|
||||
*num = 0;
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static struct ptr_valid_map *get_proc_maps(unsigned int *num)
|
||||
{
|
||||
*num = 0;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool check_with_maps(struct ptr_valid_batch *batch,
|
||||
const char *p, size_t size, bool is_write)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < batch->num_maps; i++) {
|
||||
if (p >= batch->maps[i].start && p < batch->maps[i].end) {
|
||||
/* Overlap into other maps? Recurse with remainder. */
|
||||
if (p + size > batch->maps[i].end) {
|
||||
size_t len = p + size - batch->maps[i].end;
|
||||
if (!check_with_maps(batch, batch->maps[i].end,
|
||||
len, is_write))
|
||||
return false;
|
||||
}
|
||||
return !is_write || batch->maps[i].is_write;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void finish_child(struct ptr_valid_batch *batch)
|
||||
{
|
||||
close(batch->to_child);
|
||||
close(batch->from_child);
|
||||
waitpid(batch->child_pid, NULL, 0);
|
||||
batch->child_pid = 0;
|
||||
}
|
||||
|
||||
static bool child_alive(struct ptr_valid_batch *batch)
|
||||
{
|
||||
return batch->child_pid != 0;
|
||||
}
|
||||
|
||||
static void run_child(int infd, int outfd)
|
||||
{
|
||||
volatile char *p;
|
||||
|
||||
/* This is how we expect to exit. */
|
||||
while (read(infd, &p, sizeof(p)) == sizeof(p)) {
|
||||
size_t i, size;
|
||||
bool is_write;
|
||||
char ret = 0;
|
||||
|
||||
/* This is weird. */
|
||||
if (read(infd, &size, sizeof(size)) != sizeof(size))
|
||||
exit(1);
|
||||
if (read(infd, &is_write, sizeof(is_write)) != sizeof(is_write))
|
||||
exit(2);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
ret = p[i];
|
||||
if (is_write)
|
||||
p[i] = ret;
|
||||
}
|
||||
|
||||
/* If we're still here, the answer is "yes". */
|
||||
if (write(outfd, &ret, 1) != 1)
|
||||
exit(3);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static bool create_child(struct ptr_valid_batch *batch)
|
||||
{
|
||||
int outpipe[2], inpipe[2];
|
||||
|
||||
if (pipe(outpipe) != 0)
|
||||
return false;
|
||||
if (pipe(inpipe) != 0)
|
||||
goto close_outpipe;
|
||||
|
||||
fflush(stdout);
|
||||
batch->child_pid = fork();
|
||||
if (batch->child_pid == 0) {
|
||||
close(outpipe[1]);
|
||||
close(inpipe[0]);
|
||||
run_child(outpipe[0], inpipe[1]);
|
||||
}
|
||||
|
||||
if (batch->child_pid == -1)
|
||||
goto cleanup_pid;
|
||||
|
||||
close(outpipe[0]);
|
||||
close(inpipe[1]);
|
||||
|
||||
batch->to_child = outpipe[1];
|
||||
batch->from_child = inpipe[0];
|
||||
return true;
|
||||
|
||||
cleanup_pid:
|
||||
batch->child_pid = 0;
|
||||
close_noerr(inpipe[0]);
|
||||
close_noerr(inpipe[1]);
|
||||
close_outpipe:
|
||||
close_noerr(outpipe[0]);
|
||||
close_noerr(outpipe[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_with_child(struct ptr_valid_batch *batch,
|
||||
const void *p, size_t size, bool is_write)
|
||||
{
|
||||
char ret;
|
||||
|
||||
if (!child_alive(batch)) {
|
||||
if (!create_child(batch))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (write(batch->to_child, &p, sizeof(p))
|
||||
+ write(batch->to_child, &size, sizeof(size))
|
||||
+ write(batch->to_child, &is_write, sizeof(is_write))
|
||||
!= sizeof(p) + sizeof(size) + sizeof(is_write)) {
|
||||
finish_child(batch);
|
||||
errno = EFAULT;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (read(batch->from_child, &ret, sizeof(ret)) != sizeof(ret)) {
|
||||
finish_child(batch);
|
||||
errno = EFAULT;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* msync seems most well-defined test, but page could be mapped with
|
||||
* no permissions, and can't distiguish readonly from writable. */
|
||||
bool ptr_valid_batch(struct ptr_valid_batch *batch,
|
||||
const void *p, size_t alignment, size_t size, bool write)
|
||||
{
|
||||
char *start, *end;
|
||||
bool ret;
|
||||
|
||||
if ((intptr_t)p & (alignment - 1))
|
||||
return false;
|
||||
|
||||
start = (void *)((intptr_t)p & ~(getpagesize() - 1));
|
||||
end = (void *)(((intptr_t)p + size - 1) & ~(getpagesize() - 1));
|
||||
|
||||
/* We cache single page hits. */
|
||||
if (start == end) {
|
||||
if (batch->last && batch->last == start)
|
||||
return batch->last_ok;
|
||||
}
|
||||
|
||||
if (batch->num_maps)
|
||||
ret = check_with_maps(batch, p, size, write);
|
||||
else
|
||||
ret = check_with_child(batch, p, size, write);
|
||||
|
||||
if (start == end) {
|
||||
batch->last = start;
|
||||
batch->last_ok = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ptr_valid_batch_string(struct ptr_valid_batch *batch, const char *p)
|
||||
{
|
||||
while (ptr_valid_batch(batch, p, 1, 1, false)) {
|
||||
if (*p == '\0')
|
||||
return true;
|
||||
p++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ptr_valid(const void *p, size_t alignment, size_t size, bool write)
|
||||
{
|
||||
bool ret;
|
||||
struct ptr_valid_batch batch;
|
||||
if (!ptr_valid_batch_start(&batch))
|
||||
return false;
|
||||
ret = ptr_valid_batch(&batch, p, alignment, size, write);
|
||||
ptr_valid_batch_end(&batch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ptr_valid_string(const char *p)
|
||||
{
|
||||
bool ret;
|
||||
struct ptr_valid_batch batch;
|
||||
if (!ptr_valid_batch_start(&batch))
|
||||
return false;
|
||||
ret = ptr_valid_batch_string(&batch, p);
|
||||
ptr_valid_batch_end(&batch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ptr_valid_batch_start(struct ptr_valid_batch *batch)
|
||||
{
|
||||
batch->child_pid = 0;
|
||||
batch->maps = get_proc_maps(&batch->num_maps);
|
||||
batch->last = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ptr_valid_batch_end(struct ptr_valid_batch *batch)
|
||||
{
|
||||
if (child_alive(batch))
|
||||
finish_child(batch);
|
||||
free(batch->maps);
|
||||
}
|
||||
229
ccan/ccan/ptr_valid/ptr_valid.h
Normal file
229
ccan/ccan/ptr_valid/ptr_valid.h
Normal file
@@ -0,0 +1,229 @@
|
||||
// Licensed under BSD-MIT: See LICENSE.
|
||||
#ifndef CCAN_PTR_VALID_H
|
||||
#define CCAN_PTR_VALID_H
|
||||
#include "config.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* ptr_valid_read - can I safely read from a pointer?
|
||||
* @p: the proposed pointer.
|
||||
*
|
||||
* This function verifies that the pointer @p is safe to dereference for
|
||||
* reading. It is very slow, particularly if the answer is "no".
|
||||
*
|
||||
* Sets errno to EFAULT on failure.
|
||||
*
|
||||
* See Also:
|
||||
* ptr_valid_batch_read()
|
||||
*/
|
||||
#define ptr_valid_read(p) \
|
||||
ptr_valid_r((p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))
|
||||
|
||||
/**
|
||||
* ptr_valid_write - can I safely write to a pointer?
|
||||
* @p: the proposed pointer.
|
||||
*
|
||||
* This function verifies that the pointer @p is safe to dereference
|
||||
* for writing (and reading). It is very slow, particularly if the
|
||||
* answer is "no".
|
||||
*
|
||||
* Sets errno to EFAULT on failure.
|
||||
*
|
||||
* See Also:
|
||||
* ptr_valid_batch_write()
|
||||
*/
|
||||
#define ptr_valid_write(p) \
|
||||
ptr_valid_w((p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))
|
||||
|
||||
/**
|
||||
* ptr_valid_string - can I safely read a string?
|
||||
* @p: the proposed string.
|
||||
*
|
||||
* This function verifies that the pointer @p is safe to dereference
|
||||
* up to a nul character. It is very slow, particularly if the answer
|
||||
* is "no".
|
||||
*
|
||||
* Sets errno to EFAULT on failure.
|
||||
*
|
||||
* See Also:
|
||||
* ptr_valid_batch_string()
|
||||
*/
|
||||
bool ptr_valid_string(const char *p);
|
||||
|
||||
/**
|
||||
* ptr_valid - generic pointer check function
|
||||
* @p: the proposed pointer.
|
||||
* @align: the alignment requirements of the pointer.
|
||||
* @size: the size of the region @p should point to
|
||||
* @write: true if @p should be writable as well as readable.
|
||||
*
|
||||
* This function verifies that the pointer @p is safe to dereference.
|
||||
* It is very slow, particularly if the answer is "no".
|
||||
*
|
||||
* Sets errno to EFAULT on failure.
|
||||
*
|
||||
* See Also:
|
||||
* ptr_valid_batch()
|
||||
*/
|
||||
bool ptr_valid(const void *p, size_t align, size_t size, bool write);
|
||||
|
||||
/**
|
||||
* struct ptr_valid_batch - pointer to store state for batch ptr ops
|
||||
*
|
||||
* Treat as private.
|
||||
*/
|
||||
struct ptr_valid_batch {
|
||||
unsigned int num_maps;
|
||||
struct ptr_valid_map *maps;
|
||||
int child_pid;
|
||||
int to_child, from_child;
|
||||
void *last;
|
||||
bool last_ok;
|
||||
};
|
||||
|
||||
/**
|
||||
* ptr_valid_batch_start - prepare for a batch of ptr_valid checks.
|
||||
* @batch: an uninitialized ptr_valid_batch structure.
|
||||
*
|
||||
* This initializes @batch; this same @batch pointer can be reused
|
||||
* until the memory map changes (eg. via mmap(), munmap() or even
|
||||
* malloc() and free()).
|
||||
*
|
||||
* This is useful to check many pointers, because otherwise it can be
|
||||
* extremely slow.
|
||||
*
|
||||
* Example:
|
||||
* struct linked {
|
||||
* struct linked *next;
|
||||
* const char *str;
|
||||
* };
|
||||
*
|
||||
* static bool check_linked_carefully(struct linked *head)
|
||||
* {
|
||||
* struct ptr_valid_batch batch;
|
||||
* struct linked *old = head;
|
||||
* bool half = true;
|
||||
*
|
||||
* // If this fails, we can't check. Assume OK.
|
||||
* if (!ptr_valid_batch_start(&batch))
|
||||
* return true;
|
||||
*
|
||||
* while (head) {
|
||||
* if (!ptr_valid_batch_read(&batch, head))
|
||||
* goto fail;
|
||||
* if (!ptr_valid_batch_string(&batch, head->str))
|
||||
* goto fail;
|
||||
* // Loop detection; move old at half speed of head.
|
||||
* if (half)
|
||||
* old = old->next;
|
||||
* half = !half;
|
||||
* if (head == old) {
|
||||
* errno = ELOOP;
|
||||
* goto fail;
|
||||
* }
|
||||
* }
|
||||
* ptr_valid_batch_end(&batch);
|
||||
* return true;
|
||||
*
|
||||
* fail:
|
||||
* ptr_valid_batch_end(&batch);
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* See Also:
|
||||
* ptr_valid_batch_stop()
|
||||
*/
|
||||
bool ptr_valid_batch_start(struct ptr_valid_batch *batch);
|
||||
|
||||
/**
|
||||
* ptr_valid_batch_read - can I safely read from a pointer?
|
||||
* @batch: the batch initialized by ptr_valid_batch_start().
|
||||
* @p: the proposed pointer.
|
||||
*
|
||||
* Batched version of ptr_valid_read().
|
||||
*/
|
||||
#define ptr_valid_batch_read(batch, p) \
|
||||
ptr_valid_batch_r((batch), \
|
||||
(p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))
|
||||
|
||||
/**
|
||||
* ptr_valid_batch_write - can I safely write to a pointer?
|
||||
* @batch: the batch initialized by ptr_valid_batch_start().
|
||||
* @p: the proposed pointer.
|
||||
*
|
||||
* Batched version of ptr_valid_write().
|
||||
*/
|
||||
#define ptr_valid_batch_write(batch, p) \
|
||||
ptr_valid_batch_w((batch), \
|
||||
(p), PTR_VALID_ALIGNOF(*(p)), sizeof(*(p)))
|
||||
|
||||
/**
|
||||
* ptr_valid_batch_string - can I safely read a string?
|
||||
* @batch: the batch initialized by ptr_valid_batch_start().
|
||||
* @p: the proposed string.
|
||||
*
|
||||
* Batched version of ptr_valid_string().
|
||||
*/
|
||||
bool ptr_valid_batch_string(struct ptr_valid_batch *batch, const char *p);
|
||||
|
||||
/**
|
||||
* ptr_valid_batch - generic batched pointer check function
|
||||
* @batch: the batch initialized by ptr_valid_batch_start().
|
||||
* @p: the proposed pointer.
|
||||
* @align: the alignment requirements of the pointer.
|
||||
* @size: the size of the region @p should point to
|
||||
* @write: true if @p should be writable as well as readable.
|
||||
*
|
||||
* Batched version of ptr_valid().
|
||||
*/
|
||||
bool ptr_valid_batch(struct ptr_valid_batch *batch,
|
||||
const void *p, size_t alignment, size_t size, bool write);
|
||||
|
||||
/**
|
||||
* ptr_valid_batch_end - end a batch of ptr_valid checks.
|
||||
* @batch: a ptr_valid_batch structure.
|
||||
*
|
||||
* This is used after all checks are complete.
|
||||
*
|
||||
* See Also:
|
||||
* ptr_valid_batch_start()
|
||||
*/
|
||||
void ptr_valid_batch_end(struct ptr_valid_batch *batch);
|
||||
|
||||
|
||||
/* These wrappers get constness correct. */
|
||||
static inline bool ptr_valid_r(const void *p, size_t align, size_t size)
|
||||
{
|
||||
return ptr_valid(p, align, size, false);
|
||||
}
|
||||
|
||||
static inline bool ptr_valid_w(void *p, size_t align, size_t size)
|
||||
{
|
||||
return ptr_valid(p, align, size, true);
|
||||
}
|
||||
|
||||
static inline bool ptr_valid_batch_r(struct ptr_valid_batch *batch,
|
||||
const void *p, size_t align, size_t size)
|
||||
{
|
||||
return ptr_valid_batch(batch, p, align, size, false);
|
||||
}
|
||||
|
||||
static inline bool ptr_valid_batch_w(struct ptr_valid_batch *batch,
|
||||
void *p, size_t align, size_t size)
|
||||
{
|
||||
return ptr_valid_batch(batch, p, align, size, true);
|
||||
}
|
||||
|
||||
struct ptr_valid_map {
|
||||
const char *start, *end;
|
||||
bool is_write;
|
||||
};
|
||||
|
||||
#if HAVE_ALIGNOF
|
||||
#define PTR_VALID_ALIGNOF(var) __alignof__(var)
|
||||
#else
|
||||
/* Can't check this... */
|
||||
#define PTR_VALID_ALIGNOF(var) 1
|
||||
#endif
|
||||
#endif /* CCAN_PTR_VALID_H */
|
||||
56
ccan/ccan/ptr_valid/test/run-string.c
Normal file
56
ccan/ccan/ptr_valid/test/run-string.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <ccan/ptr_valid/ptr_valid.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/ptr_valid/ptr_valid.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char *page;
|
||||
struct ptr_valid_batch *batch = malloc(sizeof *batch);
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(14);
|
||||
|
||||
page = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
strcpy(page, "hello");
|
||||
ok1(ptr_valid_read(page));
|
||||
ok1(ptr_valid_write(page));
|
||||
ok1(ptr_valid_string(page));
|
||||
|
||||
ok1(ptr_valid_batch_start(batch));
|
||||
ok1(ptr_valid_batch_string(batch, page));
|
||||
ptr_valid_batch_end(batch);
|
||||
|
||||
/* Check invalid case. */
|
||||
munmap(page, getpagesize());
|
||||
ok1(!ptr_valid_string(page));
|
||||
|
||||
ok1(ptr_valid_batch_start(batch));
|
||||
ok1(!ptr_valid_batch_string(batch, page));
|
||||
ptr_valid_batch_end(batch);
|
||||
|
||||
/* Check for overrun. */
|
||||
page = mmap(NULL, getpagesize()*2, PROT_READ|PROT_WRITE,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
munmap(page + getpagesize(), getpagesize());
|
||||
|
||||
memset(page, 'a', getpagesize());
|
||||
ok1(!ptr_valid_string(page));
|
||||
ok1(ptr_valid_batch_start(batch));
|
||||
ok1(!ptr_valid_batch_string(batch, page));
|
||||
ptr_valid_batch_end(batch);
|
||||
|
||||
page[getpagesize()-1] = '\0';
|
||||
ok1(ptr_valid_string(page));
|
||||
|
||||
ok1(ptr_valid_batch_start(batch));
|
||||
ok1(ptr_valid_batch_string(batch, page));
|
||||
ptr_valid_batch_end(batch);
|
||||
munmap(page, getpagesize());
|
||||
|
||||
free(batch);
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
90
ccan/ccan/ptr_valid/test/run.c
Normal file
90
ccan/ccan/ptr_valid/test/run.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#include <ccan/ptr_valid/ptr_valid.h>
|
||||
/* Include the C files directly. */
|
||||
#include <ccan/ptr_valid/ptr_valid.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
static bool check_batch(char *p, unsigned int num, bool expect)
|
||||
{
|
||||
struct ptr_valid_batch batch;
|
||||
unsigned int i;
|
||||
|
||||
if (!ptr_valid_batch_start(&batch))
|
||||
return false;
|
||||
for (i = 0; i < num; i++) {
|
||||
if (ptr_valid_batch(&batch, p + i, 1, 1, false) != expect)
|
||||
return false;
|
||||
if (ptr_valid_batch(&batch, p + i, 1, 1, true) != expect)
|
||||
return false;
|
||||
}
|
||||
ptr_valid_batch_end(&batch);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char *page;
|
||||
|
||||
/* This is how many tests you plan to run */
|
||||
plan_tests(30);
|
||||
|
||||
page = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
ok1(ptr_valid_read(page));
|
||||
ok1(ptr_valid_write(page));
|
||||
ok1(ptr_valid(page, 1, getpagesize(), false));
|
||||
ok1(ptr_valid(page, 1, getpagesize(), true));
|
||||
|
||||
/* Test alignment constraints. */
|
||||
ok1(ptr_valid(page, getpagesize(), getpagesize(), false));
|
||||
ok1(ptr_valid(page, getpagesize(), getpagesize(), true));
|
||||
ok1(!ptr_valid(page+1, getpagesize(), 1, false));
|
||||
ok1(!ptr_valid(page+1, getpagesize(), 1, true));
|
||||
|
||||
/* Test batch. */
|
||||
ok1(check_batch(page, getpagesize(), true));
|
||||
|
||||
/* Unmap, all should fail. */
|
||||
munmap(page, getpagesize());
|
||||
ok1(!ptr_valid_read(page));
|
||||
ok1(!ptr_valid_write(page));
|
||||
ok1(!ptr_valid(page, 1, getpagesize(), false));
|
||||
ok1(!ptr_valid(page, 1, getpagesize(), true));
|
||||
|
||||
/* Test alignment constraints. */
|
||||
ok1(!ptr_valid(page, getpagesize(), getpagesize(), false));
|
||||
ok1(!ptr_valid(page, getpagesize(), getpagesize(), true));
|
||||
ok1(!ptr_valid(page+1, getpagesize(), 1, false));
|
||||
ok1(!ptr_valid(page, getpagesize(), 1, true));
|
||||
|
||||
/* Test batch (slow, since each fails, so reduce count). */
|
||||
ok1(check_batch(page, 4, false));
|
||||
|
||||
/* Check read-only */
|
||||
page = mmap(NULL, getpagesize(), PROT_READ,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
ok1(ptr_valid_read(page));
|
||||
ok1(!ptr_valid_write(page));
|
||||
ok1(ptr_valid(page, 1, getpagesize(), false));
|
||||
ok1(!ptr_valid(page, 1, getpagesize(), true));
|
||||
|
||||
/* Test alignment constraints. */
|
||||
ok1(ptr_valid(page, getpagesize(), getpagesize(), false));
|
||||
ok1(!ptr_valid(page, getpagesize(), getpagesize(), true));
|
||||
ok1(!ptr_valid(page+1, getpagesize(), 1, false));
|
||||
ok1(!ptr_valid(page+1, getpagesize(), 1, true));
|
||||
munmap(page, getpagesize());
|
||||
|
||||
/* Check for overrun. */
|
||||
page = mmap(NULL, getpagesize()*2, PROT_READ|PROT_WRITE,
|
||||
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
|
||||
munmap(page + getpagesize(), getpagesize());
|
||||
|
||||
ok1(ptr_valid(page, 1, getpagesize(), false));
|
||||
ok1(ptr_valid(page, 1, getpagesize(), true));
|
||||
ok1(!ptr_valid(page, 1, getpagesize()+1, false));
|
||||
ok1(!ptr_valid(page, 1, getpagesize()+1, true));
|
||||
|
||||
/* This exits depending on whether all tests passed */
|
||||
return exit_status();
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
//#define TAL_DEBUG 1
|
||||
|
||||
#define NOTIFY_IS_DESTRUCTOR 512
|
||||
#define NOTIFY_EXTRA_ARG 1024
|
||||
|
||||
/* 32-bit type field, first byte 0 in either endianness. */
|
||||
enum prop_type {
|
||||
@@ -56,9 +57,18 @@ struct notifier {
|
||||
union {
|
||||
void (*notifyfn)(tal_t *, enum tal_notify_type, void *);
|
||||
void (*destroy)(tal_t *); /* If NOTIFY_IS_DESTRUCTOR set */
|
||||
void (*destroy2)(tal_t *, void *); /* If NOTIFY_EXTRA_ARG */
|
||||
} u;
|
||||
};
|
||||
|
||||
/* Extra arg */
|
||||
struct notifier_extra_arg {
|
||||
struct notifier n;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
#define EXTRA_ARG(n) (((struct notifier_extra_arg *)(n))->arg)
|
||||
|
||||
static struct {
|
||||
struct tal_hdr hdr;
|
||||
struct children c;
|
||||
@@ -203,7 +213,8 @@ static struct tal_hdr *debug_tal(struct tal_hdr *tal)
|
||||
#endif
|
||||
|
||||
static void notify(const struct tal_hdr *ctx,
|
||||
enum tal_notify_type type, const void *info)
|
||||
enum tal_notify_type type, const void *info,
|
||||
int saved_errno)
|
||||
{
|
||||
const struct prop_hdr *p;
|
||||
|
||||
@@ -216,9 +227,14 @@ static void notify(const struct tal_hdr *ctx,
|
||||
continue;
|
||||
n = (struct notifier *)p;
|
||||
if (n->types & type) {
|
||||
if (n->types & NOTIFY_IS_DESTRUCTOR)
|
||||
n->u.destroy(from_tal_hdr(ctx));
|
||||
else
|
||||
errno = saved_errno;
|
||||
if (n->types & NOTIFY_IS_DESTRUCTOR) {
|
||||
if (n->types & NOTIFY_EXTRA_ARG)
|
||||
n->u.destroy2(from_tal_hdr(ctx),
|
||||
EXTRA_ARG(n));
|
||||
else
|
||||
n->u.destroy(from_tal_hdr(ctx));
|
||||
} else
|
||||
n->u.notifyfn(from_tal_hdr(ctx), type,
|
||||
(void *)info);
|
||||
}
|
||||
@@ -274,13 +290,22 @@ static struct notifier *add_notifier_property(struct tal_hdr *t,
|
||||
enum tal_notify_type types,
|
||||
void (*fn)(void *,
|
||||
enum tal_notify_type,
|
||||
void *))
|
||||
void *),
|
||||
void *extra_arg)
|
||||
{
|
||||
struct notifier *prop = allocate(sizeof(*prop));
|
||||
struct notifier *prop;
|
||||
|
||||
if (types & NOTIFY_EXTRA_ARG)
|
||||
prop = allocate(sizeof(struct notifier_extra_arg));
|
||||
else
|
||||
prop = allocate(sizeof(struct notifier));
|
||||
|
||||
if (prop) {
|
||||
init_property(&prop->hdr, t, NOTIFIER);
|
||||
prop->types = types;
|
||||
prop->u.notifyfn = fn;
|
||||
if (types & NOTIFY_EXTRA_ARG)
|
||||
EXTRA_ARG(prop) = extra_arg;
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
@@ -288,24 +313,33 @@ static struct notifier *add_notifier_property(struct tal_hdr *t,
|
||||
static enum tal_notify_type del_notifier_property(struct tal_hdr *t,
|
||||
void (*fn)(tal_t *,
|
||||
enum tal_notify_type,
|
||||
void *))
|
||||
void *),
|
||||
bool match_extra_arg,
|
||||
void *extra_arg)
|
||||
{
|
||||
struct prop_hdr **p;
|
||||
|
||||
for (p = (struct prop_hdr **)&t->prop; *p; p = &(*p)->next) {
|
||||
struct notifier *n;
|
||||
enum tal_notify_type types;
|
||||
|
||||
if (is_literal(*p))
|
||||
break;
|
||||
if ((*p)->type != NOTIFIER)
|
||||
continue;
|
||||
n = (struct notifier *)*p;
|
||||
if (n->u.notifyfn == fn) {
|
||||
enum tal_notify_type types = n->types;
|
||||
*p = (*p)->next;
|
||||
freefn(n);
|
||||
return types & ~NOTIFY_IS_DESTRUCTOR;
|
||||
}
|
||||
if (n->u.notifyfn != fn)
|
||||
continue;
|
||||
|
||||
types = n->types;
|
||||
if ((types & NOTIFY_EXTRA_ARG)
|
||||
&& match_extra_arg
|
||||
&& extra_arg != EXTRA_ARG(n))
|
||||
continue;
|
||||
|
||||
*p = (*p)->next;
|
||||
freefn(n);
|
||||
return types & ~(NOTIFY_IS_DESTRUCTOR|NOTIFY_EXTRA_ARG);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -348,7 +382,7 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void del_tree(struct tal_hdr *t, const tal_t *orig)
|
||||
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
|
||||
{
|
||||
struct prop_hdr **prop, *p, *next;
|
||||
|
||||
@@ -359,7 +393,7 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig)
|
||||
set_destroying_bit(&t->parent_child);
|
||||
|
||||
/* Call free notifiers. */
|
||||
notify(t, TAL_NOTIFY_FREE, (tal_t *)orig);
|
||||
notify(t, TAL_NOTIFY_FREE, (tal_t *)orig, saved_errno);
|
||||
|
||||
/* Now free children and groups. */
|
||||
prop = find_property_ptr(t, CHILDREN);
|
||||
@@ -369,7 +403,7 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig)
|
||||
|
||||
while ((i = list_top(&c->children, struct tal_hdr, list))) {
|
||||
list_del(&i->list);
|
||||
del_tree(i, orig);
|
||||
del_tree(i, orig, saved_errno);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +460,7 @@ void *tal_alloc_(const tal_t *ctx, size_t size,
|
||||
}
|
||||
debug_tal(parent);
|
||||
if (notifiers)
|
||||
notify(parent, TAL_NOTIFY_ADD_CHILD, from_tal_hdr(child));
|
||||
notify(parent, TAL_NOTIFY_ADD_CHILD, from_tal_hdr(child), 0);
|
||||
return from_tal_hdr(debug_tal(child));
|
||||
}
|
||||
|
||||
@@ -466,9 +500,9 @@ void *tal_free(const tal_t *ctx)
|
||||
t = debug_tal(to_tal_hdr(ctx));
|
||||
if (notifiers)
|
||||
notify(ignore_destroying_bit(t->parent_child)->parent,
|
||||
TAL_NOTIFY_DEL_CHILD, ctx);
|
||||
TAL_NOTIFY_DEL_CHILD, ctx, saved_errno);
|
||||
list_del(&t->list);
|
||||
del_tree(t, ctx);
|
||||
del_tree(t, ctx, saved_errno);
|
||||
errno = saved_errno;
|
||||
}
|
||||
return NULL;
|
||||
@@ -495,7 +529,7 @@ void *tal_steal_(const tal_t *new_parent, const tal_t *ctx)
|
||||
}
|
||||
debug_tal(newpar);
|
||||
if (notifiers)
|
||||
notify(t, TAL_NOTIFY_STEAL, new_parent);
|
||||
notify(t, TAL_NOTIFY_STEAL, new_parent, 0);
|
||||
}
|
||||
return (void *)ctx;
|
||||
}
|
||||
@@ -504,9 +538,19 @@ bool tal_add_destructor_(const tal_t *ctx, void (*destroy)(void *me))
|
||||
{
|
||||
tal_t *t = debug_tal(to_tal_hdr(ctx));
|
||||
return add_notifier_property(t, TAL_NOTIFY_FREE|NOTIFY_IS_DESTRUCTOR,
|
||||
(void *)destroy);
|
||||
(void *)destroy, NULL);
|
||||
}
|
||||
|
||||
bool tal_add_destructor2_(const tal_t *ctx, void (*destroy)(void *me, void *arg),
|
||||
void *arg)
|
||||
{
|
||||
tal_t *t = debug_tal(to_tal_hdr(ctx));
|
||||
return add_notifier_property(t, TAL_NOTIFY_FREE|NOTIFY_IS_DESTRUCTOR
|
||||
|NOTIFY_EXTRA_ARG,
|
||||
(void *)destroy, arg);
|
||||
}
|
||||
|
||||
/* We could support notifiers with an extra arg, but we didn't add to API */
|
||||
bool tal_add_notifier_(const tal_t *ctx, enum tal_notify_type types,
|
||||
void (*callback)(tal_t *, enum tal_notify_type, void *))
|
||||
{
|
||||
@@ -521,12 +565,12 @@ bool tal_add_notifier_(const tal_t *ctx, enum tal_notify_type types,
|
||||
| TAL_NOTIFY_DEL_NOTIFIER)) == 0);
|
||||
|
||||
/* Don't call notifier about itself: set types after! */
|
||||
n = add_notifier_property(t, 0, callback);
|
||||
n = add_notifier_property(t, 0, callback, NULL);
|
||||
if (unlikely(!n))
|
||||
return false;
|
||||
|
||||
if (notifiers)
|
||||
notify(t, TAL_NOTIFY_ADD_NOTIFIER, callback);
|
||||
notify(t, TAL_NOTIFY_ADD_NOTIFIER, callback, 0);
|
||||
|
||||
n->types = types;
|
||||
if (types != TAL_NOTIFY_FREE)
|
||||
@@ -535,14 +579,15 @@ bool tal_add_notifier_(const tal_t *ctx, enum tal_notify_type types,
|
||||
}
|
||||
|
||||
bool tal_del_notifier_(const tal_t *ctx,
|
||||
void (*callback)(tal_t *, enum tal_notify_type, void *))
|
||||
void (*callback)(tal_t *, enum tal_notify_type, void *),
|
||||
bool match_extra_arg, void *extra_arg)
|
||||
{
|
||||
struct tal_hdr *t = debug_tal(to_tal_hdr(ctx));
|
||||
enum tal_notify_type types;
|
||||
|
||||
types = del_notifier_property(t, callback);
|
||||
types = del_notifier_property(t, callback, match_extra_arg, extra_arg);
|
||||
if (types) {
|
||||
notify(t, TAL_NOTIFY_DEL_NOTIFIER, callback);
|
||||
notify(t, TAL_NOTIFY_DEL_NOTIFIER, callback, 0);
|
||||
if (types != TAL_NOTIFY_FREE)
|
||||
notifiers--;
|
||||
return true;
|
||||
@@ -552,7 +597,13 @@ bool tal_del_notifier_(const tal_t *ctx,
|
||||
|
||||
bool tal_del_destructor_(const tal_t *ctx, void (*destroy)(void *me))
|
||||
{
|
||||
return tal_del_notifier_(ctx, (void *)destroy);
|
||||
return tal_del_notifier_(ctx, (void *)destroy, false, NULL);
|
||||
}
|
||||
|
||||
bool tal_del_destructor2_(const tal_t *ctx, void (*destroy)(void *me, void *arg),
|
||||
void *arg)
|
||||
{
|
||||
return tal_del_notifier_(ctx, (void *)destroy, true, arg);
|
||||
}
|
||||
|
||||
bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
|
||||
@@ -582,7 +633,7 @@ bool tal_set_name_(tal_t *ctx, const char *name, bool literal)
|
||||
|
||||
debug_tal(t);
|
||||
if (notifiers)
|
||||
notify(t, TAL_NOTIFY_RENAME, name);
|
||||
notify(t, TAL_NOTIFY_RENAME, name, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -720,10 +771,10 @@ bool tal_resize_(tal_t **ctxp, size_t size, size_t count, bool clear)
|
||||
}
|
||||
*ctxp = from_tal_hdr(debug_tal(t));
|
||||
if (notifiers)
|
||||
notify(t, TAL_NOTIFY_MOVE, from_tal_hdr(old_t));
|
||||
notify(t, TAL_NOTIFY_MOVE, from_tal_hdr(old_t), 0);
|
||||
}
|
||||
if (notifiers)
|
||||
notify(t, TAL_NOTIFY_RESIZE, (void *)size);
|
||||
notify(t, TAL_NOTIFY_RESIZE, (void *)size, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ typedef void tal_t;
|
||||
* children (recursively) before finally freeing the memory. It returns
|
||||
* NULL, for convenience.
|
||||
*
|
||||
* Note: errno is preserved by this call.
|
||||
* Note: errno is preserved by this call, and also saved and restored
|
||||
* for any destructors or notifiers.
|
||||
*
|
||||
* Example:
|
||||
* p = tal_free(p);
|
||||
@@ -168,6 +169,53 @@ void *tal_free(const tal_t *p);
|
||||
#define tal_del_destructor(ptr, function) \
|
||||
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
|
||||
|
||||
/**
|
||||
* tal_add_destructor2 - add a 2-arg callback function when context is destroyed.
|
||||
* @ptr: The tal allocated object.
|
||||
* @function: the function to call before it's freed.
|
||||
* @arg: the extra argument to the function.
|
||||
*
|
||||
* Sometimes an extra argument is required for a destructor; this
|
||||
* saves the extra argument internally to avoid the caller having to
|
||||
* do an extra allocation.
|
||||
*
|
||||
* Note that this can only fail if your allocfn fails and your errorfn returns.
|
||||
*/
|
||||
#define tal_add_destructor2(ptr, function, arg) \
|
||||
tal_add_destructor2_((ptr), \
|
||||
typesafe_cb_cast(void (*)(tal_t *, void *), \
|
||||
void (*)(__typeof__(ptr), \
|
||||
__typeof__(arg)), \
|
||||
(function)), \
|
||||
(arg))
|
||||
|
||||
/**
|
||||
* tal_del_destructor - remove a destructor callback function.
|
||||
* @ptr: The tal allocated object.
|
||||
* @function: the function to call before it's freed.
|
||||
*
|
||||
* If @function has not been successfully added as a destructor, this returns
|
||||
* false.
|
||||
*/
|
||||
#define tal_del_destructor(ptr, function) \
|
||||
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
|
||||
|
||||
/**
|
||||
* tal_del_destructor2 - remove 2-arg callback function.
|
||||
* @ptr: The tal allocated object.
|
||||
* @function: the function to call before it's freed.
|
||||
* @arg: the extra argument to the function.
|
||||
*
|
||||
* If @function has not been successfully added as a destructor with
|
||||
* @arg, this returns false.
|
||||
*/
|
||||
#define tal_del_destructor2(ptr, function, arg) \
|
||||
tal_del_destructor2_((ptr), \
|
||||
typesafe_cb_cast(void (*)(tal_t *, void *), \
|
||||
void (*)(__typeof__(ptr), \
|
||||
__typeof__(arg)), \
|
||||
(function)), \
|
||||
(arg))
|
||||
enum tal_notify_type {
|
||||
TAL_NOTIFY_FREE = 1,
|
||||
TAL_NOTIFY_STEAL = 2,
|
||||
@@ -194,6 +242,7 @@ enum tal_notify_type {
|
||||
* TAL_NOTIFY_FREE is called when @ptr is freed, either directly or
|
||||
* because an ancestor is freed: @info is the argument to tal_free().
|
||||
* It is exactly equivalent to a destructor, with more information.
|
||||
* errno is set to the value it was at the call of tal_free().
|
||||
*
|
||||
* TAL_NOTIFY_STEAL is called when @ptr's parent changes: @info is the
|
||||
* new parent.
|
||||
@@ -232,7 +281,8 @@ enum tal_notify_type {
|
||||
tal_del_notifier_((ptr), \
|
||||
typesafe_cb_postargs(void, void *, (callback), \
|
||||
(ptr), \
|
||||
enum tal_notify_type, void *))
|
||||
enum tal_notify_type, void *), \
|
||||
false, NULL)
|
||||
|
||||
/**
|
||||
* tal_set_name - attach a name to a tal pointer.
|
||||
@@ -447,12 +497,17 @@ bool tal_resize_(tal_t **ctxp, size_t size, size_t count, bool clear);
|
||||
bool tal_expand_(tal_t **ctxp, const void *src, size_t size, size_t count);
|
||||
|
||||
bool tal_add_destructor_(const tal_t *ctx, void (*destroy)(void *me));
|
||||
bool tal_add_destructor2_(const tal_t *ctx, void (*destroy)(void *me, void *arg),
|
||||
void *arg);
|
||||
bool tal_del_destructor_(const tal_t *ctx, void (*destroy)(void *me));
|
||||
bool tal_del_destructor2_(const tal_t *ctx, void (*destroy)(void *me, void *arg),
|
||||
void *arg);
|
||||
|
||||
bool tal_add_notifier_(const tal_t *ctx, enum tal_notify_type types,
|
||||
void (*notify)(tal_t *ctx, enum tal_notify_type,
|
||||
void *info));
|
||||
bool tal_del_notifier_(const tal_t *ctx,
|
||||
void (*notify)(tal_t *ctx, enum tal_notify_type,
|
||||
void *info));
|
||||
void *info),
|
||||
bool match_extra_arg, void *arg);
|
||||
#endif /* CCAN_TAL_H */
|
||||
|
||||
38
ccan/ccan/tal/test/run-destructor2.c
Normal file
38
ccan/ccan/tal/test/run-destructor2.c
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <ccan/tal/tal.h>
|
||||
#include <ccan/tal/tal.c>
|
||||
#include <ccan/tap/tap.h>
|
||||
|
||||
static void destroy_inc(char *p UNNEEDED, int *destroy_count)
|
||||
{
|
||||
(*destroy_count)++;
|
||||
}
|
||||
|
||||
static void destroy_dec(char *p UNNEEDED, int *destroy_count)
|
||||
{
|
||||
(*destroy_count)--;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char *p;
|
||||
int destroy_count1 = 0, destroy_count2 = 0;
|
||||
|
||||
plan_tests(10);
|
||||
|
||||
p = tal(NULL, char);
|
||||
/* Del must match both fn and arg. */
|
||||
ok1(tal_add_destructor2(p, destroy_inc, &destroy_count1));
|
||||
ok1(!tal_del_destructor2(p, destroy_inc, &destroy_count2));
|
||||
ok1(!tal_del_destructor2(p, destroy_dec, &destroy_count1));
|
||||
ok1(tal_del_destructor2(p, destroy_inc, &destroy_count1));
|
||||
ok1(!tal_del_destructor2(p, destroy_inc, &destroy_count1));
|
||||
|
||||
ok1(tal_add_destructor2(p, destroy_inc, &destroy_count1));
|
||||
ok1(tal_add_destructor2(p, destroy_dec, &destroy_count2));
|
||||
ok1(tal_free(p) == NULL);
|
||||
ok1(destroy_count1 == 1);
|
||||
ok1(destroy_count2 == -1);
|
||||
|
||||
tal_cleanup();
|
||||
return exit_status();
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
static void destroy_errno(char *p UNNEEDED)
|
||||
{
|
||||
/* Errno restored for all the destructors. */
|
||||
ok1(errno == EINVAL);
|
||||
errno = ENOENT;
|
||||
}
|
||||
|
||||
@@ -11,10 +13,11 @@ int main(void)
|
||||
{
|
||||
char *p;
|
||||
|
||||
plan_tests(2);
|
||||
plan_tests(5);
|
||||
|
||||
p = tal(NULL, char);
|
||||
ok1(tal_add_destructor(p, destroy_errno));
|
||||
ok1(tal_add_destructor(p, destroy_errno));
|
||||
|
||||
/* Errno save/restored across free. */
|
||||
errno = EINVAL;
|
||||
|
||||
@@ -28,7 +28,7 @@ struct timeabs time_now(void)
|
||||
struct timemono time_mono(void)
|
||||
{
|
||||
struct timemono ret;
|
||||
#ifdef TIME_HAVE_MONOTONIC
|
||||
#if TIME_HAVE_MONOTONIC
|
||||
clock_gettime(CLOCK_MONOTONIC, &ret.ts);
|
||||
#else /* Best we can do */
|
||||
ret.ts = time_now().ts;
|
||||
|
||||
@@ -71,6 +71,8 @@ struct timemono {
|
||||
*/
|
||||
#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC)
|
||||
#define TIME_HAVE_MONOTONIC 1
|
||||
#else
|
||||
#define TIME_HAVE_MONOTONIC 0
|
||||
#endif
|
||||
|
||||
struct timespec time_check_(struct timespec in, const char *abortstr);
|
||||
|
||||
Reference in New Issue
Block a user