[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] r27745 - msh/src
From: |
gnunet |
Subject: |
[GNUnet-SVN] r27745 - msh/src |
Date: |
Thu, 4 Jul 2013 16:05:40 +0200 |
Author: harsha
Date: 2013-07-04 16:05:39 +0200 (Thu, 04 Jul 2013)
New Revision: 27745
Added:
msh/src/test_scheduler_socket.c
Modified:
msh/src/
msh/src/Makefile.am
msh/src/common.h
msh/src/mshd.c
msh/src/scheduler.c
msh/src/scheduler.h
msh/src/test_scheduler.c
msh/src/util.c
msh/src/util.h
Log:
extend scheduler with socket listen|connect
Index: msh/src
===================================================================
--- msh/src 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src 2013-07-04 14:05:39 UTC (rev 27745)
Property changes on: msh/src
___________________________________________________________________
Modified: svn:ignore
## -4,6 +4,6 ##
mshd
mping
test-suite.log
-test-scheduler
-test-scheduler.log
-test-scheduler.trs
+test-scheduler*
+test-scheduler*.log
+test-scheduler*.trs
Modified: msh/src/Makefile.am
===================================================================
--- msh/src/Makefile.am 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/Makefile.am 2013-07-04 14:05:39 UTC (rev 27745)
@@ -2,14 +2,20 @@
mping_SOURCES = mping.c
-mshd_SOURCES = mshd.c util.c util.h
+mshd_SOURCES = mshd.c util.c util.h scheduler.c scheduler.h common.h
mshd_LDADD = -levent
check_PROGRAMS = \
- test-scheduler
+ test-scheduler \
+ test-scheduler-socket
test_scheduler_SOURCES = test_scheduler.c scheduler.c scheduler.h common.h
test_scheduler_LDADD = -levent
+test_scheduler_socket_SOURCES = test_scheduler_socket.c scheduler.c
scheduler.h \
+ common.h util.c util.h
+test_scheduler_socket_LDADD = -levent
+
TESTS = \
- test-scheduler
+ test-scheduler \
+ test-scheduler-socket
Modified: msh/src/common.h
===================================================================
--- msh/src/common.h 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/common.h 2013-07-04 14:05:39 UTC (rev 27745)
@@ -71,6 +71,10 @@
*/
#define MSH_free_non_null(ptr) do { if (NULL != (ptr)) free (ptr); } while(0)
+/**
+ * Close a file descriptor while printing a warning upon any failure
+ */
+#define MSH_close(fd) do { if(0 != close(fd)) LOG_STRERROR ("close"); else fd
= -1; } while (0)
/* ******************** doubly-linked list *************** */
/* To avoid mistakes: head->prev == tail->next == NULL */
Modified: msh/src/mshd.c
===================================================================
--- msh/src/mshd.c 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/mshd.c 2013-07-04 14:05:39 UTC (rev 27745)
@@ -40,21 +40,75 @@
static unsigned int rwidth;
/**
- * event base for libevent
+ * Tasks for handling SIGINT and SIGTERM
*/
-static struct event_base *ebase;
+static struct Task *sigshut_tasks[2];
/**
- * Select loop for a socket
- *
- * @param sock the fd corresponding to the socket
- * @param timeout the timeout in milliseconds
- * @return MSH_OK upon succes; MSH_SYSERR upon failure
+ * Task for running a round
*/
+static struct Task *rtask;
+
+/**
+ * Task for asynchronous accept on the socket
+ */
+static struct Task *atask;
+
+/**
+ * Array for checking which MPI processes have verified our addresses in the
+ * current round
+ */
+static uint8_t *barray;
+
+static size_t barray_size;
+
+static void
+barray_init ()
+{
+ barray_size = (rwidth + sizeof (barray[0]) - 1) / sizeof (barray[0]);
+ barray = MSH_malloc (barray_size);
+}
+
+static void
+barray_destroy ()
+{
+ free (barray);
+ barray = NULL;
+}
+
+static void
+barray_clear ()
+{
+ (void) memset (barray, 0, barray_size);
+}
+
+static void
+barray_set (unsigned int id)
+{
+ unsigned int off;
+ unsigned int idx;
+ typeof (barray[0]) one;
+
+ off = id / sizeof (barray[0]);
+ idx = id % sizeof (barray[0]);
+ MSH_assert (off < barray_size);
+ one = (typeof (barray[0])) 1; /* cast */
+ MSH_assert (0 == (barray[off] & (one << idx)) );
+ barray[off] = barray[off] | (one << idx);
+}
+
static int
-sock_select (int sock, long timeout)
+barray_isset (unsigned int id)
{
- return MSH_SYSERR;
+ unsigned int off;
+ unsigned int idx;
+ typeof (barray[0]) one;
+
+ off = id / sizeof (barray[0]);
+ idx = id % sizeof (barray[0]);
+ MSH_assert (off < barray_size);
+ one = (typeof (barray[0])) 1; /* cast */
+ return (0 == (barray[off] & (one << idx)) ) ? 0 : 1;
}
@@ -91,6 +145,42 @@
/**
+ * Task to call accept and close on a listening socket
+ *
+ * @param sock the socket
+ * @param flags EV_* flags
+ * @param cls &atask
+ */
+static void
+accept_task (evutil_socket_t sock, short flags, void *cls)
+{
+ scheduler_remove (atask);
+ atask = NULL;
+ if (IS_SHUTDOWN_EVENT (flags))
+ {
+ (void) close (sock);
+ return;
+ }
+ LOG_DEBUG ("Got a connect\n");
+
+}
+
+
+/**
+ * Send our addresses to an MPI processes
+ *
+ * @param rank the rank of the process which has to receive our request
+ * @return MSH_OK on success; MSH_SYSERR upon error
+ */
+static int
+send_addresses (int rank)
+{
+ MSH_break (0);
+ return MSH_OK;
+}
+
+
+/**
* Verify IP addresses of all the hosts where mshd services are running
*
* @return MSH_OK if verification is successful; MSH_SYSERR upon error (an
error
@@ -99,50 +189,22 @@
static int
verify_addresses ()
{
- struct sockaddr_in saddr;
+ struct sockaddr_in addr;
socklen_t addrlen;
int sock;
unsigned int cnt;
- /* open a listen socket */
- sock = socket (AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ addrlen = sizeof (struct sockaddr_in);
+ (void) memset (&addr, 0, addrlen);
+ sock = open_listen_socket ((struct sockaddr *) &addr, addrlen, rwidth);
if (-1 == sock)
- {
- LOG_STRERROR ("socket");
return MSH_SYSERR;
- }
- addrlen = sizeof (struct sockaddr_in);
- (void) memset (&saddr, 0, addrlen);
- saddr.sin_family = AF_INET;
- if (-1 == bind (sock, &saddr, addrlen))
- {
- LOG_STRERROR ("bind");
- goto clo_ret;
- }
- if (-1 == getsockname (sock, &saddr, &addrlen))
- {
- LOG_STRERROR ("getsockname");
- goto clo_ret;
- }
- if (sizeof (struct sockaddr_in) != addrlen)
- {
- MSH_break (0);
- goto clo_ret;
- }
- lport = ntohs (saddr.sin_port);
+ lport = ntohs (addr.sin_port);
if (0 == lport)
{
MSH_break (0);
goto clo_ret;
}
- if (-1 == listen (sock, rwidth))
- {
- LOG_STRERROR ("listen");
- goto clo_ret;
- }
- goto contd;
-
- contd:
LOG_DEBUG ("Bound to local port %u\n", lport);
if (MPI_SUCCESS != MPI_Barrier (MPI_COMM_WORLD))
{
@@ -150,13 +212,14 @@
goto clo_ret;
}
for (cnt = 0; cnt < rwidth; cnt++)
- //send_addresses ((round * rwidth) + cnt);
- sock_select (sock, 0);
+ if (MSH_SYSERR == send_addresses ((round * rwidth) + cnt))
+ goto clo_ret;
if (MPI_SUCCESS != MPI_Barrier (MPI_COMM_WORLD))
{
MSH_break (0);
goto clo_ret;
}
+ atask = scheduler_add_socket (sock, EV_READ, &accept_task, &atask, NULL);
return MSH_OK;
clo_ret:
@@ -166,46 +229,94 @@
/**
- * Event callback for the first running task
+ * Event callback for handling shutdown signals
*
+ * @param signal the signal
+ * @param flags EV_* flags
+ * @param cls pointer to the corresponding Task
+ */
+static void
+sig_shutdown (evutil_socket_t signal, short flags, void *cls)
+{
+ struct Task **task = cls;
+ unsigned int cnt;
+
+ scheduler_remove (*task);
+ *task = NULL;
+ if (IS_SHUTDOWN_EVENT (flags))
+ return;
+ LOG_DEBUG ("Got signal %d. Exiting.\n", signal);
+ scheduler_shutdown ();
+}
+
+
+/**
+ * Task for running a round
+ *
* @param nosock we have no sockets associated with this callback
* @param flags EV_* flags
* @param cls NULL
*/
static void
-run (evutil_socket_t nosock, short flags, void *cls)
+run_round (evutil_socket_t nosock, short flags, void *cls);
+
+
+/**
+ * Schedules next round
+ */
+static void
+schedule_next_round ()
{
- LOG_DEBUG ("run task ran\n");
- for (; round < nproc; round++)
+ MSH_assert (NULL == rtask);
+ if (round < ( (nproc + (rwidth - 1)) / rwidth) )
{
+ round++;
+ rtask = scheduler_add (&run_round, NULL, TV_IMMEDIATE);
}
}
-#define N_SHUTDOWN_SIGNALS 2 /* SIGTERM, SIGINT */
-static struct event *ev_sigs[N_SHUTDOWN_SIGNALS];
+/**
+ * Task for running a round
+ *
+ * @param nosock we have no sockets associated with this callback
+ * @param flags EV_* flags
+ * @param cls NULL
+ */
+static void
+run_round (evutil_socket_t nosock, short flags, void *cls)
+{
+ scheduler_remove (rtask);
+ rtask = NULL;
+ if (IS_SHUTDOWN_EVENT (flags))
+ return;
+ if (MSH_OK == verify_addresses ())
+ schedule_next_round ();
+}
+
/**
* Event callback for the first running task
*
- * @param signal the signal
+ * @param nosock we have no sockets associated with this callback
* @param flags EV_* flags
* @param cls NULL
*/
static void
-sig_shutdown (evutil_socket_t signal, short flags, void *cls)
+run (evutil_socket_t nosock, short flags, void *cls)
{
- struct event **ev_sig = cls;
- unsigned int cnt;
-
- *ev_sig = NULL;
+ LOG_DEBUG ("Running main task\n");
+ sigshut_tasks[0] = scheduler_add_signal (SIGTERM, &sig_shutdown,
+ &sigshut_tasks[0], NULL);
+ sigshut_tasks[1] = scheduler_add_signal (SIGTERM, &sig_shutdown,
+ &sigshut_tasks[1], NULL);
+ rtask = scheduler_add (&run_round, NULL, TV_IMMEDIATE);
}
int
main (int argc, char **argv)
{
- struct event *ev_run;
int ret;
ret = 1;
@@ -226,24 +337,23 @@
}
GNUNET_OS_network_interfaces_list (&net_if_processor, NULL);
if (0 == nips)
+ {
LOG_ERROR ("No IP addresses found\n");
- ebase = event_base_new ();
- ev_run = evtimer_new (ebase, &run, NULL);
- evtimer_add (ev_run, TV_IMMEDIATE);
- if (0 != event_base_dispatch (ebase))
+ goto fail;
+ }
+ barray_init ();
+ if (MSH_OK != scheduler_run (&run, NULL))
{
- evtimer_del (ev_run);
- LOG_ERROR ("Event loop dispatch error\n");
+ MSH_break (0);
+ barray_destroy ();
goto fail;
}
- evtimer_del (ev_run);
- ev_run = NULL;
+ barray_destroy ();
ret = 0;
+
fail:
MSH_break (MPI_SUCCESS == MPI_Finalize());
MSH_free_non_null (ip_addr_str);
- if (NULL != ebase)
- event_base_free (ebase);
- //libevent_global_shutdown ();
+ //libevent_global_shutdown ();
return ret;
}
Modified: msh/src/scheduler.c
===================================================================
--- msh/src/scheduler.c 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/scheduler.c 2013-07-04 14:05:39 UTC (rev 27745)
@@ -112,3 +112,79 @@
event_base_free (ebase);
return (1 == ret) ? MSH_OK : MSH_SYSERR;
}
+
+
+struct SocketOpenHandle
+{
+ socket_open_fn cb;
+
+ void *cls;
+
+ struct Task *task;
+
+ int sock;
+};
+
+static void
+open_socket_cb (evutil_socket_t sock, short flags, void *cls)
+{
+ struct SocketOpenHandle *h = cls;
+ socket_open_fn cb;
+ void *cbcls;
+ int errval;
+ socklen_t optlen;
+
+ scheduler_remove (h->task);
+ h->task = NULL;
+ cb = h->cb;
+ cbcls = h->cls;
+ MSH_assert (h->sock == sock);
+ free (h);
+ if (IS_SHUTDOWN_EVENT (flags))
+ goto err_ret;
+ errval = 1;
+ optlen = sizeof (errval);
+ if (0 != getsockopt (sock, SOL_SOCKET, SO_ERROR, &errval, &optlen))
+ {
+ LOG_STRERROR ("getsockopt");
+ goto err_ret;
+ }
+ if (0 != errval)
+ {
+ LOG_ERROR ("connect() failed for a socket: %s\n", strerror (errval));
+ goto err_ret;
+ }
+ cb (sock, cbcls);
+ return;
+
+ err_ret:
+ MSH_close (sock);
+ cb (-1, cbcls);
+}
+
+
+struct SocketOpenHandle *
+scheduler_open_socket (const struct sockaddr *addr, const socklen_t addrlen,
+ socket_open_fn cb, void *cls)
+{
+ struct SocketOpenHandle *h;
+ int sock;
+
+ MSH_assert (NULL != cb);
+ if (-1 == (sock = open_socket (addr, addrlen)))
+ return NULL;
+ h = MSH_malloc (sizeof (struct SocketOpenHandle));
+ h->cb = cb;
+ h->cls = cls;
+ h->sock = sock;
+ h->task = scheduler_add_socket (sock, EV_WRITE, &open_socket_cb, h, NULL);
+ return h;
+}
+
+void
+scheduler_open_socket_cancel (struct SocketOpenHandle *h)
+{
+ scheduler_remove (h->task);
+ MSH_close (h->sock);
+ free (h);
+}
Modified: msh/src/scheduler.h
===================================================================
--- msh/src/scheduler.h 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/scheduler.h 2013-07-04 14:05:39 UTC (rev 27745)
@@ -5,7 +5,7 @@
#define TV_IMMEDIATE &tv_immediate
-#define is_shutdown_event(flags) ((flags & (EV_READ | EV_WRITE | EV_TIMEOUT))
== (EV_READ | EV_WRITE | EV_TIMEOUT))
+#define IS_SHUTDOWN_EVENT(flags) ((flags & (EV_READ | EV_WRITE | EV_TIMEOUT))
== (EV_READ | EV_WRITE | EV_TIMEOUT))
struct Task;
@@ -29,3 +29,9 @@
int
scheduler_run (event_callback_fn cb, void *cls);
+
+typedef void (* socket_open_fn) (int sockfd, void *cls);
+
+struct SocketOpenHandle *
+scheduler_open_socket (const struct sockaddr *addr, const socklen_t addrlen,
+ socket_open_fn cb, void *cls);
Modified: msh/src/test_scheduler.c
===================================================================
--- msh/src/test_scheduler.c 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/test_scheduler.c 2013-07-04 14:05:39 UTC (rev 27745)
@@ -18,7 +18,7 @@
struct Task **sig_task = cls;
scheduler_remove (*sig_task);
- if (is_shutdown_event (flags))
+ if (IS_SHUTDOWN_EVENT (flags))
return;
LOG_DEBUG ("Got signal %d. Shutting down.\n", sig);
scheduler_shutdown ();
@@ -52,8 +52,8 @@
return;
}
scheduler_remove (task);
- //if (!is_shutdown_event (flags))
- scheduler_shutdown ();
+ if (!IS_SHUTDOWN_EVENT (flags))
+ scheduler_shutdown ();
}
int main (int argc, char *argv[])
Added: msh/src/test_scheduler_socket.c
===================================================================
--- msh/src/test_scheduler_socket.c (rev 0)
+++ msh/src/test_scheduler_socket.c 2013-07-04 14:05:39 UTC (rev 27745)
@@ -0,0 +1,139 @@
+#include "common.h"
+#include "scheduler.h"
+
+struct Task *sig_tasks[2];
+
+struct Task *atask;
+
+struct SocketOpenHandle *oh;
+
+static int lsock;
+
+static int csock;
+
+static int result;
+
+
+/**
+ * Event callback for a signal
+ *
+ * @param sig the signal
+ * @param flags EV_* flags
+ * @param cls NULL
+ */
+static void
+shutdown_signal (evutil_socket_t sig, short flags, void *cls)
+{
+ struct Task **sig_task = cls;
+
+ scheduler_remove (*sig_task);
+ if (IS_SHUTDOWN_EVENT (flags))
+ return;
+ LOG_DEBUG ("Got signal %d. Shutting down.\n", sig);
+ scheduler_shutdown ();
+}
+
+
+/**
+ * Task to call accept and close on a listening socket
+ *
+ * @param sock the socket
+ * @param flags EV_* flags
+ * @param cls &atask
+ */
+static void
+accept_task (evutil_socket_t fd, short flags, void *cls)
+{
+ struct sockaddr_in caddr;
+ socklen_t caddrlen;
+
+ LOG_DEBUG ("accept task\n");
+ scheduler_remove (atask);
+ atask = NULL;
+ if (IS_SHUTDOWN_EVENT (flags))
+ {
+ MSH_close (lsock);
+ return;
+ }
+ MSH_assert (fd == lsock);
+ LOG_DEBUG ("Got a connect\n");
+ caddrlen = sizeof (caddr);
+ if (0 > (csock = accept4 (lsock, &caddr, &caddrlen,
+ SOCK_NONBLOCK | SOCK_CLOEXEC)))
+ {
+ LOG_STRERROR ("accept4");
+ MSH_close (lsock);
+ scheduler_shutdown ();
+ }
+ if (2 == ++result)
+ scheduler_shutdown ();
+}
+
+
+static void
+socket_connect_cb (int fd, void *cls)
+{
+ oh = NULL;
+ if (fd < 0)
+ {
+ MSH_break (0);
+ return;
+ }
+ LOG_DEBUG ("Connected to local listen socket\n");
+ MSH_close (fd);
+ MSH_close (csock);
+ MSH_close (lsock);
+ if (2 == ++result)
+ scheduler_shutdown ();
+}
+
+
+/**
+ * Event callback for the first running task
+ *
+ * @param nosock we have no sockets associated with this callback
+ * @param flags EV_* flags
+ * @param cls NULL
+ */
+static void
+run (evutil_socket_t nosock, short flags, void *cls)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen;
+
+ if (IS_SHUTDOWN_EVENT (flags))
+ return;
+ MSH_assert (-1 == nosock);
+ MSH_assert (NULL == cls);
+ MSH_assert (0 != (EV_TIMEOUT & flags));
+ LOG_DEBUG ("Run\n");
+ sig_tasks[0] = scheduler_add_signal (SIGTERM, &shutdown_signal,
&sig_tasks[0],
+ NULL);
+ sig_tasks[1] = scheduler_add_signal (SIGINT, &shutdown_signal, &sig_tasks[1],
+ NULL);
+ addrlen = sizeof (addr);
+ (void) memset (&addr, 0, addrlen);
+ addr.sin_family = AF_INET;
+ if (-1 == (lsock = open_listen_socket (&addr, addrlen, 1)))
+ return;
+ atask = scheduler_add_socket (lsock, EV_READ, &accept_task, &atask, NULL);
+ /* connect to the listening socket */
+ addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ oh = scheduler_open_socket ((struct sockaddr *) &addr, addrlen,
+ socket_connect_cb, NULL);
+ if (NULL == oh)
+ scheduler_shutdown ();
+ // ctask = scheduler_add (run2, NULL, TV_IMMEDIATE);
+}
+
+int main (int argc, char *argv[])
+{
+ int ret;
+
+ result = 0;
+ lsock = -1;
+ csock = -1;
+ if (MSH_OK != scheduler_run (run, NULL))
+ return 1;
+ return (2 == result) ? 0 : 1;
+}
Modified: msh/src/util.c
===================================================================
--- msh/src/util.c 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/util.c 2013-07-04 14:05:39 UTC (rev 27745)
@@ -84,3 +84,85 @@
*old = tmp;
*oldCount = newCount;
}
+
+
+/**
+ * Creates a new non-blocking socket and binds it to the given address and
makes
+ * it a listen socket
+ *
+ * @param addr the address to bind to
+ * @param addrlen the length of the addr
+ * @param backlog the max length of the pending connections. This will be
+ * passed to listen()
+ * @return the socket's fd; -1 on error
+ */
+int
+open_listen_socket (struct sockaddr *addr, const socklen_t addrlen, int
backlog)
+{
+ socklen_t newaddrlen;
+ int sock;
+
+ sock = socket (AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (-1 == sock)
+ {
+ LOG_STRERROR ("socket");
+ return -1;
+ }
+ if (-1 == bind (sock, addr, addrlen))
+ {
+ LOG_STRERROR ("bind");
+ goto clo_ret;
+ }
+ newaddrlen = addrlen;
+ if (-1 == getsockname (sock, addr, &newaddrlen))
+ {
+ LOG_STRERROR ("getsockname");
+ goto clo_ret;
+ }
+ if (newaddrlen != addrlen)
+ {
+ MSH_break (0);
+ goto clo_ret;
+ }
+ if (-1 == listen (sock, backlog))
+ {
+ LOG_STRERROR ("listen");
+ goto clo_ret;
+ }
+ return sock;
+
+ clo_ret:
+ (void) close (sock);
+ return -1;
+}
+
+
+/**
+ * Creates a new non-blocking socket and binds it to the given address and
makes
+ * it a listen socket
+ *
+ * @param addr the address to bind to
+ * @param addrlen the length of the addr
+ * @param backlog the max length of the pending connections. This will be
+ * passed to listen()
+ * @return the socket's fd; -1 on error
+ */
+int
+open_socket (const struct sockaddr *addr, const socklen_t addrlen)
+{
+ int sock;
+
+ sock = socket (AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (-1 == sock)
+ {
+ LOG_STRERROR ("socket");
+ return -1;
+ }
+ if ((-1 == connect (sock, addr, addrlen)) && (EINPROGRESS != errno) )
+ {
+ LOG_STRERROR ("connect");
+ (void) close (sock);
+ return -1;
+ }
+ return sock;
+}
Modified: msh/src/util.h
===================================================================
--- msh/src/util.h 2013-07-04 12:31:42 UTC (rev 27744)
+++ msh/src/util.h 2013-07-04 14:05:39 UTC (rev 27745)
@@ -99,5 +99,19 @@
#define MSH_array_append(arr,size,element) do {
MSH_array_grow(arr,size,size+1); arr[size-1] = element; } while(0)
+/**
+ * Creates a new non-blocking socket and binds it to the given address and
makes
+ * it a listen socket
+ *
+ * @param addr the address to bind to
+ * @param addrlen the length of the addr
+ * @param backlog the max length of the pending connections. This will be
+ * passed to listen()
+ * @return the socket's fd; -1 on error
+ */
+int
+open_listen_socket (struct sockaddr *addr, const socklen_t addrlen, int
backlog);
+
+
#endif /* #ifndef MSH_UTIL_H */
/* end of MSH_UTIL_H */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r27745 - msh/src,
gnunet <=