summaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange')
-rw-r--r--src/exchange/Makefile.am4
-rw-r--r--src/exchange/taler-exchange-aggregator.c36
-rw-r--r--src/exchange/taler-exchange-httpd.c2
-rw-r--r--src/exchange/taler-exchange-httpd_mhd.c9
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_melt.c2
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_reveal.c2
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c57
-rw-r--r--src/exchange/taler-exchange-httpd_reserve_status.c2
-rw-r--r--src/exchange/taler-exchange-httpd_reserve_withdraw.c2
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c2
-rw-r--r--src/exchange/taler-exchange-wirewatch.c6
-rwxr-xr-xsrc/exchange/test_taler_exchange_httpd_restart.sh114
-rw-r--r--src/exchange/test_taler_exchange_unix.conf137
13 files changed, 321 insertions, 54 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 50eb545b0..cf9f984de 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -83,7 +83,8 @@ taler_exchange_httpd_LDADD = \
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
check_SCRIPTS = \
- test_taler_exchange_httpd.sh
+ test_taler_exchange_httpd.sh \
+ test_taler_exchange_httpd_restart.sh
if HAVE_EXPENSIVE_TESTS
check_SCRIPTS += \
test_taler_exchange_httpd_afl.sh
@@ -97,6 +98,7 @@ TESTS = \
EXTRA_DIST = \
test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv \
test_taler_exchange_httpd.conf \
+ test_taler_exchange_unix.conf \
test_taler_exchange_httpd.get \
test_taler_exchange_httpd.post \
exchange.conf \
diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c
index a43277b23..d380c3915 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -582,6 +582,7 @@ shutdown_task (void *cls)
ctc = NULL;
}
TALER_EXCHANGEDB_plugin_unload (db_plugin);
+ db_plugin = NULL;
{
struct WireAccount *wa;
@@ -670,6 +671,7 @@ parse_wirewatch_config ()
fprintf (stderr,
"Failed to initialize DB tables\n");
TALER_EXCHANGEDB_plugin_unload (db_plugin);
+ db_plugin = NULL;
return GNUNET_SYSERR;
}
TALER_EXCHANGEDB_find_accounts (cfg,
@@ -680,6 +682,7 @@ parse_wirewatch_config ()
fprintf (stderr,
"No wire accounts configured for debit!\n");
TALER_EXCHANGEDB_plugin_unload (db_plugin);
+ db_plugin = NULL;
return GNUNET_SYSERR;
}
return GNUNET_OK;
@@ -1179,10 +1182,9 @@ expired_reserve_cb (void *cls,
/* Reserve balance was almost zero OR soft error */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Reserve was virtually empty, moving on\n");
- (void) commit_or_warn (ctc->session);
- GNUNET_free (ctc->method);
- GNUNET_free (ctc);
- ctc = NULL;
+ (void) commit_or_warn (session);
+ erc->async_cont = GNUNET_YES;
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return qs;
@@ -1223,6 +1225,7 @@ expired_reserve_cb (void *cls,
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
erc->async_cont = GNUNET_YES;
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
GNUNET_free (ctc->method);
@@ -1299,6 +1302,7 @@ run_reserve_closures (void *cls)
case GNUNET_DB_STATUS_SOFT_ERROR:
db_plugin->rollback (db_plugin->cls,
session);
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
return;
@@ -1308,6 +1312,7 @@ run_reserve_closures (void *cls)
reserves_idle = GNUNET_YES;
db_plugin->rollback (db_plugin->cls,
session);
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1315,6 +1320,7 @@ run_reserve_closures (void *cls)
(void) commit_or_warn (session);
if (GNUNET_YES == erc.async_cont)
break;
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
return;
@@ -1345,6 +1351,7 @@ run_aggregation (void *cls)
return;
if (0 == (++swap % 2))
{
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
return;
@@ -1392,6 +1399,7 @@ run_aggregation (void *cls)
{
/* should re-try immediately */
swap--; /* do not count failed attempts */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1408,15 +1416,21 @@ run_aggregation (void *cls)
{
if ( (GNUNET_NO == reserves_idle) ||
(GNUNET_YES == test_mode) )
+ {
/* Possibly more to on reserves, go for it immediately */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
NULL);
+ }
else
+ {
/* FIXME(dold): We might want to read the duration to sleep from the config */
/* nothing to do, sleep for a minute and try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
&run_aggregation,
NULL);
+ }
}
return;
}
@@ -1451,6 +1465,7 @@ run_aggregation (void *cls)
"Serialization issue, trying again later!\n");
db_plugin->rollback (db_plugin->cls,
session);
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1515,6 +1530,7 @@ run_aggregation (void *cls)
session);
cleanup_au ();
/* start again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1531,6 +1547,7 @@ run_aggregation (void *cls)
(void) commit_or_warn (session);
cleanup_au ();
/* start again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1587,6 +1604,7 @@ run_aggregation (void *cls)
db_plugin->rollback (db_plugin->cls,
session);
/* start again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1613,6 +1631,7 @@ run_aggregation (void *cls)
/* try again */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Commit issue for prepared wire data; trying again later!\n");
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1625,6 +1644,7 @@ run_aggregation (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Preparation complete, switching to transfer mode\n");
/* run alternative task: actually do wire transfer! */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return;
@@ -1685,6 +1705,7 @@ wire_confirm_cb (void *cls,
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
/* try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
}
@@ -1703,6 +1724,7 @@ wire_confirm_cb (void *cls,
{
case GNUNET_DB_STATUS_SOFT_ERROR:
/* try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1716,6 +1738,7 @@ wire_confirm_cb (void *cls,
"Wire transfer complete\n");
/* continue with #run_transfers(), just to guard
against the unlikely case that there are more. */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return;
@@ -1846,6 +1869,7 @@ run_transfers (void *cls)
return;
case GNUNET_DB_STATUS_SOFT_ERROR:
/* try again */
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return;
@@ -1853,6 +1877,7 @@ run_transfers (void *cls)
/* no more prepared wire transfers, go back to aggregation! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No more pending wire transfers, starting aggregation\n");
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
@@ -1898,6 +1923,7 @@ run (void *cls,
return;
}
+ GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
@@ -1917,6 +1943,8 @@ main (int argc,
char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_option_flag ('t',
"test",
"run in test mode and exit when idle",
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 666cec0c1..426c48655 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -999,6 +999,8 @@ main (int argc,
"SECONDS",
"after how long do connections timeout by default (in seconds)",
&connection_timeout),
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
#if HAVE_DEVELOPER
GNUNET_GETOPT_option_filename ('f',
"file-input",
diff --git a/src/exchange/taler-exchange-httpd_mhd.c b/src/exchange/taler-exchange-httpd_mhd.c
index 53944b6a7..0f2ce033a 100644
--- a/src/exchange/taler-exchange-httpd_mhd.c
+++ b/src/exchange/taler-exchange-httpd_mhd.c
@@ -128,11 +128,10 @@ TEH_MHD_handler_send_json_pack_error (struct TEH_RequestHandler *rh,
(void) connection_cls;
(void) upload_data;
(void) upload_data_size;
- return TALER_MHD_reply_json_pack (connection,
- rh->response_code,
- "{s:s}",
- "error",
- rh->data);
+ return TALER_MHD_reply_with_error (connection,
+ rh->response_code,
+ TALER_EC_METHOD_INVALID,
+ rh->data);
}
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c b/src/exchange/taler-exchange-httpd_refresh_melt.c
index 71200037d..c7dc700f7 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.c
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.c
@@ -69,7 +69,7 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
- "error",
+ "hint",
"insufficient funds",
"code",
(json_int_t)
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c
index bbafdd1a1..6ac91caf8 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -108,7 +108,7 @@ reply_refresh_reveal_mismatch (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o}",
- "error", "commitment violation",
+ "hint", "commitment violation",
"code",
(json_int_t)
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
index 8c6e90302..8e24b9b47 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -83,28 +83,6 @@ reply_refund_success (struct MHD_Connection *connection,
/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code. The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec taler error code to include
- * @return MHD result code
- */
-static int
-reply_refund_failure (struct MHD_Connection *connection,
- unsigned int response_code,
- enum TALER_ErrorCode ec)
-{
- return TALER_MHD_reply_json_pack (connection,
- response_code,
- "{s:s, s:I}",
- "hint", "refund failure",
- "code", (json_int_t) ec);
-}
-
-
-/**
* Generate refund conflict failure message. Returns the
* transaction list @a tl with the details about the conflict.
*
@@ -194,9 +172,10 @@ refund_transaction (void *cls,
if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_COIN_NOT_FOUND);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_REFUND_COIN_NOT_FOUND,
+ "database transaction failure");
return qs;
}
deposit_found = GNUNET_NO;
@@ -319,9 +298,10 @@ refund_transaction (void *cls,
GNUNET_break_op (0); /* currency mismatch */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_PRECONDITION_FAILED,
- TALER_EC_REFUND_CURRENCY_MISSMATCH);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_REFUND_CURRENCY_MISSMATCH,
+ "currencies involved do not match");
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -353,9 +333,10 @@ refund_transaction (void *cls,
/* money was already transferred to merchant, can no longer refund */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_GONE,
- TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_GONE,
+ TALER_EC_REFUND_MERCHANT_ALREADY_PAID,
+ "money already sent to merchant");
return GNUNET_DB_STATUS_HARD_ERROR;
}
@@ -366,9 +347,10 @@ refund_transaction (void *cls,
GNUNET_break_op (0); /* cannot refund more than original value */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
- *mhd_ret = reply_refund_failure (connection,
- MHD_HTTP_PRECONDITION_FAILED,
- TALER_EC_REFUND_INSUFFICIENT_FUNDS);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_PRECONDITION_FAILED,
+ TALER_EC_REFUND_INSUFFICIENT_FUNDS,
+ "refund requested exceeds original value");
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* Check refund fee matches fee of denomination key! */
@@ -481,9 +463,10 @@ verify_and_execute_refund (struct MHD_Connection *connection,
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- return reply_refund_failure (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_REFUND_COIN_NOT_FOUND);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_REFUND_COIN_NOT_FOUND,
+ "denomination of coin to be refunded not found in DB");
}
}
diff --git a/src/exchange/taler-exchange-httpd_reserve_status.c b/src/exchange/taler-exchange-httpd_reserve_status.c
index 89bf8b38a..e2d35aaec 100644
--- a/src/exchange/taler-exchange-httpd_reserve_status.c
+++ b/src/exchange/taler-exchange-httpd_reserve_status.c
@@ -164,7 +164,7 @@ TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
"{s:s, s:s, s:I}",
- "error", "Reserve not found",
+ "hint", "Reserve not found",
"parameter", "reserve_pub",
"code",
(json_int_t)
diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.c b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
index 86633cd98..9daad0a02 100644
--- a/src/exchange/taler-exchange-httpd_reserve_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
@@ -74,7 +74,7 @@ reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o, s:o}",
- "error", "Insufficient funds",
+ "hint", "insufficient funds",
"code",
(json_int_t)
TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index c88a8a254..90ca14c88 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -446,7 +446,7 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct MHD_Connection *connection,
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_CONFLICT,
"{s:s, s:I, s:o}",
- "error", "insufficient funds",
+ "hint", "insufficient funds",
"code", (json_int_t) ec,
"history", history);
}
diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c
index 21d96668b..69929e127 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -340,7 +340,7 @@ history_cb (void *cls,
hh = NULL;
if (TALER_EC_NONE != ec)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Error fetching history: ec=%u, http_status=%u\n",
(unsigned int) ec,
http_status);
@@ -597,7 +597,9 @@ main (int argc,
char *const *argv)
{
struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_option_flag ('T',
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
+ GNUNET_GETOPT_option_flag ('t',
"test",
"run in test mode and exit when idle",
&test_mode),
diff --git a/src/exchange/test_taler_exchange_httpd_restart.sh b/src/exchange/test_taler_exchange_httpd_restart.sh
new file mode 100755
index 000000000..f7d7a3050
--- /dev/null
+++ b/src/exchange/test_taler_exchange_httpd_restart.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+#
+# This file is part of TALER
+# Copyright (C) 2020 Taler Systems SA
+#
+# TALER is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Affero General Public License as published by the Free Software
+# Foundation; either version 3, or (at your option) any later version.
+#
+# TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License along with
+# TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+#
+#
+# This script launches an exchange (binding to a UNIX domain socket) and then
+# restarts it in various ways (SIGHUP to re-read configuration, and SIGUSR1 to
+# re-spawn a new binary). Basically, the goal is to make sure that the HTTP
+# server survives these less common operations.
+#
+#
+set -eu
+
+# Exit, with status code "skip" (no 'real' failure)
+function exit_skip() {
+ echo $1
+ exit 77
+}
+
+# Exit, with error message (hard failure)
+function exit_fail() {
+ echo $1
+ kill `jobs -p` >/dev/null 2>/dev/null || true
+ wait
+ exit 1
+}
+
+echo -n "Testing for curl"
+curl --version >/dev/null </dev/null || exit_skip " MISSING"
+echo " FOUND"
+
+
+# Clear environment from variables that override config.
+unset XDG_DATA_HOME
+unset XDG_CONFIG_HOME
+#
+echo -n "Launching exchange ..."
+PREFIX=
+# Uncomment this line to run with valgrind...
+# PREFIX="valgrind --trace-children=yes --leak-check=yes --track-fds=yes --error-exitcode=1 --log-file=valgrind.%p"
+
+# Setup keys.
+taler-exchange-keyup -c test_taler_exchange_unix.conf || exit 1
+# Setup wire accounts.
+taler-exchange-wire -c test_taler_exchange_unix.conf > /dev/null || exit 1
+# Run Exchange HTTPD (in background)
+$PREFIX taler-exchange-httpd -c test_taler_exchange_unix.conf -i 2> test-exchange.log &
+
+# Where should we be bound to?
+UNIXPATH=`taler-config -s exchange -f -o UNIXPATH`
+
+# Give HTTP time to start
+
+for n in `seq 1 100`
+do
+ echo -n "."
+ sleep 0.1
+ OK=1
+ curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null && break
+ OK=0
+done
+if [ 1 != $OK ]
+then
+ exit_fail "Failed to launch exchange"
+fi
+echo " DONE"
+
+# Finally run test...
+echo -n "Reloading keys ..."
+kill -SIGUSR1 $!
+sleep 1
+curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null || exit_fail "SIGUSR1 killed HTTP service"
+echo " DONE"
+
+# Finally run test...
+echo -n "Restarting program ..."
+kill -SIGHUP $!
+sleep 1
+curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null || exit_fail "SIGHUP killed HTTP service"
+echo " DONE"
+
+echo -n "Waiting for parent to die ..."
+wait $!
+echo " DONE"
+
+echo -n "Testing child still alive ..."
+curl --unix-socket "${UNIXPATH}" "http://ignored/" >/dev/null 2> /dev/null || exit_fail "SIGHUP killed HTTP service"
+echo " DONE"
+
+
+echo -n "Killing grandchild ..."
+CPID=`ps x | grep taler-exchange-httpd | grep -v grep | awk '{print $1}'`
+kill -TERM $CPID
+while true
+do
+ ps x | grep -v grep | grep taler-exchange-httpd > /dev/null || break
+ sleep 0.1
+done
+echo " DONE"
+
+# Return status code from exchange for this script
+exit 0
diff --git a/src/exchange/test_taler_exchange_unix.conf b/src/exchange/test_taler_exchange_unix.conf
new file mode 100644
index 000000000..d41df9abe
--- /dev/null
+++ b/src/exchange/test_taler_exchange_unix.conf
@@ -0,0 +1,137 @@
+[PATHS]
+# Persistant data storage for the testcase
+TALER_TEST_HOME = test_taler_exchange_httpd_home/
+
+[taler]
+# Currency supported by the exchange (can only be one)
+CURRENCY = EUR
+CURRENCY_ROUND_UNIT = EUR:0.01
+
+[exchange]
+
+# Directory with our terms of service.
+TERMS_DIR = ../../contrib/tos
+
+# Etag / filename for the terms of service.
+TERMS_ETAG = 0
+
+
+# Directory with our privacy policy.
+PRIVACY_DIR = ../../contrib/pp
+
+# Etag / filename for the privacy policy.
+PRIVACY_ETAG = 0
+
+# MAX_REQUESTS = 2
+# how long is one signkey valid?
+SIGNKEY_DURATION = 4 weeks
+
+# how long are the signatures with the signkey valid?
+LEGAL_DURATION = 2 years
+
+# how long do we generate denomination and signing keys
+# ahead of time?
+LOOKAHEAD_SIGN = 32 weeks 1 day
+
+# how long do we provide to clients denomination and signing keys
+# ahead of time?
+LOOKAHEAD_PROVIDE = 4 weeks 1 day
+
+# HTTP port the exchange listens to (we want to use UNIX domain sockets,
+# so we use a port that just won't work on GNU/Linux without root rights)
+PORT = 999
+
+# Here we say we want to use a UNIX domain socket (to test that logic).
+SERVE = unix
+
+# Master public key used to sign the exchange's various keys
+MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
+
+# How to access our database
+DB = postgres
+
+
+[exchangedb]
+# After how long do we close idle reserves? The exchange
+# and the auditor must agree on this value. We currently
+# expect it to be globally defined for the whole system,
+# as there is no way for wallets to query this value. Thus,
+# it is only configurable for testing, and should be treated
+# as constant in production.
+IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+
+
+[exchangedb-postgres]
+CONFIG = "postgres:///talercheck"
+
+[exchange-account-1]
+PAYTO_URI = "payto://x-taler-bank/localhost:8082/3"
+WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
+ENABLE_DEBIT = YES
+ENABLE_CREDIT = YES
+TALER_BANK_AUTH_METHOD = NONE
+
+
+# Wire fees are specified by wire method
+[fees-x-taler-bank]
+# Fees for the forseeable future...
+# If you see this after 2018, update to match the next 10 years...
+WIRE-FEE-2018 = EUR:0.01
+WIRE-FEE-2019 = EUR:0.01
+WIRE-FEE-2020 = EUR:0.01
+WIRE-FEE-2021 = EUR:0.01
+WIRE-FEE-2022 = EUR:0.01
+WIRE-FEE-2023 = EUR:0.01
+WIRE-FEE-2024 = EUR:0.01
+WIRE-FEE-2025 = EUR:0.01
+WIRE-FEE-2026 = EUR:0.01
+WIRE-FEE-2027 = EUR:0.01
+
+CLOSING-FEE-2018 = EUR:0.01
+CLOSING-FEE-2019 = EUR:0.01
+CLOSING-FEE-2020 = EUR:0.01
+CLOSING-FEE-2021 = EUR:0.01
+CLOSING-FEE-2022 = EUR:0.01
+CLOSING-FEE-2023 = EUR:0.01
+CLOSING-FEE-2024 = EUR:0.01
+CLOSING-FEE-2025 = EUR:0.01
+CLOSING-FEE-2026 = EUR:0.01
+CLOSING-FEE-2027 = EUR:0.01
+
+
+# Coins for the tests.
+[coin_eur_ct_1]
+value = EUR:0.01
+duration_overlap = 5 minutes
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
+[coin_eur_ct_10]
+value = EUR:0.10
+duration_overlap = 5 minutes
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024
+
+[coin_eur_1]
+value = EUR:1
+duration_overlap = 5 minutes
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+rsa_keysize = 1024