LCOV - code coverage report
Current view: top level - util - secmod_common.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 72.5 % 222 161
Test Date: 2026-01-04 22:17:00 Functions: 100.0 % 10 10

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2020 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file util/secmod_common.c
      18              :  * @brief Common functions for the exchange security modules
      19              :  * @author Florian Dold <dold@taler.net>
      20              :  */
      21              : #include "taler/platform.h"
      22              : #include "taler/taler_util.h"
      23              : #include "taler/taler_signatures.h"
      24              : #include "secmod_common.h"
      25              : #include <poll.h>
      26              : #ifdef __linux__
      27              : #include <sys/eventfd.h>
      28              : #endif
      29              : 
      30              : 
      31              : /**
      32              :  * Head of DLL of clients connected to us.
      33              :  */
      34              : struct TES_Client *TES_clients_head;
      35              : 
      36              : /**
      37              :  * Tail of DLL of clients connected to us.
      38              :  */
      39              : struct TES_Client *TES_clients_tail;
      40              : 
      41              : /**
      42              :  * Lock for the client queue.
      43              :  */
      44              : pthread_mutex_t TES_clients_lock;
      45              : 
      46              : /**
      47              :  * Private key of this security module. Used to sign denomination key
      48              :  * announcements.
      49              :  */
      50              : struct TALER_SecurityModulePrivateKeyP TES_smpriv;
      51              : 
      52              : /**
      53              :  * Public key of this security module.
      54              :  */
      55              : struct TALER_SecurityModulePublicKeyP TES_smpub;
      56              : 
      57              : /**
      58              :  * Our listen socket.
      59              :  */
      60              : static struct GNUNET_NETWORK_Handle *unix_sock;
      61              : 
      62              : /**
      63              :  * Path where we are listening.
      64              :  */
      65              : static char *unixpath;
      66              : 
      67              : /**
      68              :  * Task run to accept new inbound connections.
      69              :  */
      70              : static struct GNUNET_SCHEDULER_Task *listen_task;
      71              : 
      72              : /**
      73              :  * Set once we are in shutdown and workers should terminate.
      74              :  */
      75              : static volatile bool in_shutdown;
      76              : 
      77              : 
      78              : enum GNUNET_GenericReturnValue
      79         5782 : TES_transmit_raw (int sock,
      80              :                   size_t end,
      81              :                   const void *pos)
      82              : {
      83         5782 :   size_t off = 0;
      84              : 
      85         5782 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
      86              :               "Sending message of length %u\n",
      87              :               (unsigned int) end);
      88        11554 :   while (off < end)
      89              :   {
      90         5766 :     ssize_t ret = send (sock,
      91              :                         pos,
      92              :                         end - off,
      93              :                         0 /* no flags => blocking! */);
      94              : 
      95         5772 :     if ( (-1 == ret) &&
      96            0 :          ( (EAGAIN == errno) ||
      97            0 :            (EINTR == errno) ) )
      98              :     {
      99            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
     100              :                            "send");
     101            0 :       continue;
     102              :     }
     103         5772 :     if (-1 == ret)
     104              :     {
     105            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     106              :                            "send");
     107            0 :       return GNUNET_SYSERR;
     108              :     }
     109         5772 :     if (0 == ret)
     110              :     {
     111            0 :       GNUNET_break (0);
     112            0 :       return GNUNET_SYSERR;
     113              :     }
     114         5772 :     off += ret;
     115         5772 :     pos += ret;
     116              :   }
     117         5788 :   return GNUNET_OK;
     118              : }
     119              : 
     120              : 
     121              : enum GNUNET_GenericReturnValue
     122         5718 : TES_transmit (int sock,
     123              :               const struct GNUNET_MessageHeader *hdr)
     124              : {
     125         5718 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     126              :               "Sending message of type %u and length %u\n",
     127              :               (unsigned int) ntohs (hdr->type),
     128              :               (unsigned int) ntohs (hdr->size));
     129        11443 :   return TES_transmit_raw (sock,
     130         5718 :                            ntohs (hdr->size),
     131              :                            hdr);
     132              : }
     133              : 
     134              : 
     135              : struct GNUNET_NETWORK_Handle *
     136           54 : TES_open_socket (const char *my_unixpath)
     137              : {
     138              :   int sock;
     139              :   mode_t old_umask;
     140           54 :   struct GNUNET_NETWORK_Handle *ret = NULL;
     141              : 
     142              :   /* Change permissions so that group read/writes are allowed.
     143              :    * We need this for multi-user exchange deployment with privilege
     144              :    * separation, where taler-exchange-httpd is part of a group
     145              :    * that allows it to talk to secmod.
     146              :    */
     147           54 :   old_umask = umask (S_IROTH | S_IWOTH | S_IXOTH);
     148              : 
     149           54 :   sock = socket (PF_UNIX,
     150              :                  SOCK_STREAM,
     151              :                  0);
     152           54 :   if (-1 == sock)
     153              :   {
     154            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     155              :                          "socket");
     156            0 :     goto cleanup;
     157              :   }
     158              :   {
     159              :     struct sockaddr_un un;
     160              : 
     161           54 :     if (GNUNET_OK !=
     162           54 :         GNUNET_DISK_directory_create_for_file (my_unixpath))
     163              :     {
     164            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     165              :                                 "mkdir(dirname)",
     166              :                                 my_unixpath);
     167              :     }
     168           54 :     if (0 != unlink (my_unixpath))
     169              :     {
     170           54 :       if (ENOENT != errno)
     171            0 :         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     172              :                                   "unlink",
     173              :                                   my_unixpath);
     174              :     }
     175           54 :     memset (&un,
     176              :             0,
     177              :             sizeof (un));
     178           54 :     un.sun_family = AF_UNIX;
     179           54 :     strncpy (un.sun_path,
     180              :              my_unixpath,
     181              :              sizeof (un.sun_path) - 1);
     182           54 :     if (0 != bind (sock,
     183              :                    (const struct sockaddr *) &un,
     184              :                    sizeof (un)))
     185              :     {
     186            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     187              :                                 "bind",
     188              :                                 my_unixpath);
     189            0 :       GNUNET_break (0 == close (sock));
     190            0 :       goto cleanup;
     191              :     }
     192           54 :     ret = GNUNET_NETWORK_socket_box_native (sock);
     193           54 :     if (GNUNET_OK !=
     194           54 :         GNUNET_NETWORK_socket_listen (ret,
     195              :                                       512))
     196              :     {
     197            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     198              :                                 "listen",
     199              :                                 my_unixpath);
     200            0 :       GNUNET_break (GNUNET_OK ==
     201              :                     GNUNET_NETWORK_socket_close (ret));
     202            0 :       ret = NULL;
     203              :     }
     204              :   }
     205           54 : cleanup:
     206           54 :   (void) umask (old_umask);
     207           54 :   return ret;
     208              : }
     209              : 
     210              : 
     211              : void
     212          103 : TES_wake_clients (void)
     213              : {
     214          103 :   uint64_t num = 1;
     215              : 
     216          103 :   GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
     217          103 :   for (struct TES_Client *client = TES_clients_head;
     218          192 :        NULL != client;
     219           89 :        client = client->next)
     220              :   {
     221              : #ifdef __linux__
     222           89 :     if (-1 == client->esock)
     223           30 :       continue;
     224           59 :     GNUNET_assert (sizeof (num) ==
     225              :                    write (client->esock,
     226              :                           &num,
     227              :                           sizeof (num)));
     228              : #else
     229              :     if (-1 == client->esock_in)
     230              :       continue;
     231              :     GNUNET_assert (sizeof (num) ==
     232              :                    write (client->esock_in,
     233              :                           &num,
     234              :                           sizeof (num)));
     235              : #endif
     236              :   }
     237          103 :   GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
     238          103 : }
     239              : 
     240              : 
     241              : enum GNUNET_GenericReturnValue
     242         3875 : TES_read_work (void *cls,
     243              :                TES_MessageDispatch dispatch)
     244              : {
     245         3875 :   struct TES_Client *client = cls;
     246         3875 :   char *buf = client->iobuf;
     247         3875 :   size_t off = 0;
     248         3875 :   uint16_t msize = 0;
     249         3875 :   const struct GNUNET_MessageHeader *hdr = NULL;
     250              :   enum GNUNET_GenericReturnValue ret;
     251              : 
     252              :   do
     253              :   {
     254              :     ssize_t recv_size;
     255              : 
     256         3875 :     recv_size = recv (client->csock,
     257         3875 :                       &buf[off],
     258              :                       sizeof (client->iobuf) - off,
     259              :                       0);
     260         3877 :     if (-1 == recv_size)
     261              :     {
     262            3 :       if ( (0 == off) &&
     263            1 :            (EAGAIN == errno) )
     264            0 :         return GNUNET_NO;
     265            3 :       if ( (EINTR == errno) ||
     266            1 :            (EAGAIN == errno) )
     267              :       {
     268            2 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
     269              :                              "recv");
     270            0 :         continue;
     271              :       }
     272            1 :       if (ECONNRESET != errno)
     273            0 :         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     274              :                              "recv");
     275            1 :       return GNUNET_SYSERR;
     276              :     }
     277         3874 :     if (0 == recv_size)
     278              :     {
     279              :       /* regular disconnect? */
     280           32 :       GNUNET_break_op (0 == off);
     281           32 :       return GNUNET_SYSERR;
     282              :     }
     283         3842 :     off += recv_size;
     284         3842 : more:
     285         3842 :     if (off < sizeof (struct GNUNET_MessageHeader))
     286            0 :       continue;
     287         3842 :     hdr = (const struct GNUNET_MessageHeader *) buf;
     288         3842 :     msize = ntohs (hdr->size);
     289              : #if 0
     290              :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     291              :                 "Received message of type %u with %u bytes\n",
     292              :                 (unsigned int) ntohs (hdr->type),
     293              :                 (unsigned int) msize);
     294              : #endif
     295         3842 :     if (msize < sizeof (struct GNUNET_MessageHeader))
     296              :     {
     297            0 :       GNUNET_break_op (0);
     298            0 :       return GNUNET_SYSERR;
     299              :     }
     300         3842 :   } while (off < msize);
     301              : 
     302         3842 :   ret = dispatch (client,
     303              :                   hdr);
     304         3837 :   if ( (GNUNET_OK != ret) ||
     305         3837 :        (off == msize) )
     306         3837 :     return ret;
     307            0 :   memmove (buf,
     308            0 :            &buf[msize],
     309              :            off - msize);
     310            0 :   off -= msize;
     311            0 :   goto more;
     312              : }
     313              : 
     314              : 
     315              : bool
     316         3929 : TES_await_ready (struct TES_Client *client)
     317              : {
     318              :   /* wait for reply with 1s timeout */
     319         3929 :   struct pollfd pfds[] = {
     320              :     {
     321         3929 :       .fd = client->csock,
     322              :       .events = POLLIN
     323              :     },
     324              :     {
     325              : #ifdef __linux__
     326         3929 :       .fd = client->esock,
     327              : #else
     328              :       .fd = client->esock_out,
     329              : #endif
     330              :       .events = POLLIN
     331              :     },
     332              :   };
     333              :   int ret;
     334              : 
     335         3929 :   ret = poll (pfds,
     336              :               2,
     337              :               -1);
     338         3936 :   if ( (-1 == ret) &&
     339            0 :        (EINTR != errno) )
     340            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     341              :                          "poll");
     342        11749 :   for (int i = 0; i<2; i++)
     343              :   {
     344         7872 :     if (
     345              : #ifdef __linux__
     346         7872 :       (pfds[i].fd == client->esock) &&
     347              : #else
     348              :       (pfds[i].fd == client->esock_out) &&
     349              : #endif
     350         3936 :       (POLLIN == pfds[i].revents) )
     351              :     {
     352              :       uint64_t num;
     353              : 
     354              : #ifdef __linux__
     355           59 :       GNUNET_assert (sizeof (num) ==
     356              :                      read (client->esock,
     357              :                            &num,
     358              :                            sizeof (num)));
     359              : #else
     360              :       GNUNET_assert (sizeof (num) ==
     361              :                      read (client->esock_out,
     362              :                            &num,
     363              :                            sizeof (num)));
     364              : #endif
     365           59 :       return true;
     366              :     }
     367              :   }
     368         3877 :   return false;
     369              : }
     370              : 
     371              : 
     372              : /**
     373              :  * Main function of a worker thread that signs.
     374              :  *
     375              :  * @param cls the client we are working on
     376              :  * @return NULL
     377              :  */
     378              : static void *
     379           78 : sign_worker (void *cls)
     380              : {
     381           78 :   struct TES_Client *client = cls;
     382              : 
     383           78 :   if (GNUNET_OK !=
     384           78 :       client->cb.init (client))
     385              :   {
     386            0 :     GNUNET_break (0);
     387            0 :     return NULL;
     388              :   }
     389         3973 :   while (! in_shutdown)
     390              :   {
     391         3929 :     if (TES_await_ready (client))
     392              :     {
     393           59 :       if (GNUNET_OK !=
     394           59 :           client->cb.updater (client))
     395            0 :         break;
     396              :     }
     397              :     else
     398              :     {
     399         3869 :       if (GNUNET_SYSERR ==
     400         3877 :           TES_read_work (client,
     401              :                          client->cb.dispatch))
     402           33 :         break;
     403              :     }
     404              :   }
     405           77 :   GNUNET_break (0 == close (client->csock));
     406           78 :   client->csock = -1;
     407              : #ifdef __linux__
     408           78 :   GNUNET_break (0 == close (client->esock));
     409           78 :   client->esock = -1;
     410              : #else
     411              :   GNUNET_break (0 == close (client->esock_in));
     412              :   client->esock_in = -1;
     413              :   GNUNET_break (0 == close (client->esock_out));
     414              :   client->esock_out = -1;
     415              : #endif
     416           78 :   return NULL;
     417              : }
     418              : 
     419              : 
     420              : /**
     421              :  * Task that listens for incoming clients.
     422              :  *
     423              :  * @param cls a `struct TES_Callbacks`
     424              :  */
     425              : static void
     426           78 : listen_job (void *cls)
     427              : {
     428           78 :   const struct TES_Callbacks *cb = cls;
     429              :   int s;
     430              : #ifdef __linux__
     431              :   int e;
     432              : #else
     433              :   int e[2];
     434              : #endif
     435              :   struct sockaddr_storage sa;
     436           78 :   socklen_t sa_len = sizeof (sa);
     437              : 
     438           78 :   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
     439              :                                                unix_sock,
     440              :                                                &listen_job,
     441              :                                                cls);
     442           78 :   s = accept (GNUNET_NETWORK_get_fd (unix_sock),
     443              :               (struct sockaddr *) &sa,
     444              :               &sa_len);
     445           78 :   if (-1 == s)
     446              :   {
     447            0 :     bool st = ( (ENFILE == errno) ||
     448            0 :                 (EMFILE == errno) );
     449            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     450              :                          "accept");
     451            0 :     if (st)
     452              :     {
     453            0 :       GNUNET_SCHEDULER_cancel (listen_task);
     454            0 :       listen_task = NULL;
     455              :     }
     456            0 :     return;
     457              :   }
     458              : #ifdef __linux__
     459           78 :   e = eventfd (0,
     460              :                EFD_CLOEXEC);
     461           78 :   if (-1 == e)
     462              :   {
     463            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     464              :                          "eventfd");
     465            0 :     GNUNET_break (0 == close (s));
     466            0 :     return;
     467              :   }
     468              : #else
     469              :   if (0 != pipe (e))
     470              :   {
     471              :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     472              :                          "pipe");
     473              :     GNUNET_break (0 == close (s));
     474              :     return;
     475              :   }
     476              : #endif
     477              :   {
     478              :     struct TES_Client *client;
     479              :     struct TES_Client *nxt;
     480              : 
     481           78 :     client = GNUNET_new (struct TES_Client);
     482           78 :     client->cb = *cb;
     483           78 :     client->csock = s;
     484              : #ifdef __linux__
     485           78 :     client->esock = e;
     486              : #else
     487              :     client->esock_in = e[1];
     488              :     client->esock_out = e[0];
     489              : #endif
     490           78 :     GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
     491           78 :     for (struct TES_Client *pos = TES_clients_head;
     492          165 :          NULL != pos;
     493           87 :          pos = nxt)
     494              :     {
     495           87 :       nxt = pos->next;
     496           87 :       if (-1 == pos->csock)
     497              :       {
     498              :         void *rval;
     499              : 
     500            3 :         GNUNET_CONTAINER_DLL_remove (TES_clients_head,
     501              :                                      TES_clients_tail,
     502              :                                      pos);
     503            3 :         GNUNET_break (0 ==
     504              :                       pthread_join (pos->worker,
     505              :                                     &rval));
     506            3 :         GNUNET_free (pos);
     507              :       }
     508              :     }
     509           78 :     GNUNET_CONTAINER_DLL_insert (TES_clients_head,
     510              :                                  TES_clients_tail,
     511              :                                  client);
     512           78 :     GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
     513           78 :     if (0 !=
     514           78 :         pthread_create (&client->worker,
     515              :                         NULL,
     516              :                         &sign_worker,
     517              :                         client))
     518              :     {
     519            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     520              :                            "pthread_create");
     521            0 :       GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
     522            0 :       GNUNET_CONTAINER_DLL_remove (TES_clients_head,
     523              :                                    TES_clients_tail,
     524              :                                    client);
     525            0 :       GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
     526            0 :       GNUNET_break (0 == close (client->csock));
     527              : #ifdef __linux__
     528            0 :       GNUNET_break (0 == close (client->esock));
     529              : #else
     530              :       GNUNET_break (0 == close (client->esock_in));
     531              :       GNUNET_break (0 == close (client->esock_out));
     532              : #endif
     533            0 :       GNUNET_free (client);
     534              :     }
     535              :   }
     536              : }
     537              : 
     538              : 
     539              : int
     540           54 : TES_listen_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
     541              :                   const char *section,
     542              :                   const struct TES_Callbacks *cb)
     543              : {
     544              :   {
     545              :     char *pfn;
     546              : 
     547           54 :     if (GNUNET_OK !=
     548           54 :         GNUNET_CONFIGURATION_get_value_filename (cfg,
     549              :                                                  section,
     550              :                                                  "SM_PRIV_KEY",
     551              :                                                  &pfn))
     552              :     {
     553            0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     554              :                                  section,
     555              :                                  "SM_PRIV_KEY");
     556            0 :       return EXIT_NOTCONFIGURED;
     557              :     }
     558           54 :     if (GNUNET_SYSERR ==
     559           54 :         GNUNET_CRYPTO_eddsa_key_from_file (pfn,
     560              :                                            GNUNET_YES,
     561              :                                            &TES_smpriv.eddsa_priv))
     562              :     {
     563            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     564              :                                  section,
     565              :                                  "SM_PRIV_KEY",
     566              :                                  "Could not use file to persist private key");
     567            0 :       GNUNET_free (pfn);
     568            0 :       return EXIT_NOPERMISSION;
     569              :     }
     570           54 :     GNUNET_free (pfn);
     571           54 :     GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv,
     572              :                                         &TES_smpub.eddsa_pub);
     573              :   }
     574              : 
     575           54 :   if (GNUNET_OK !=
     576           54 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     577              :                                                section,
     578              :                                                "UNIXPATH",
     579              :                                                &unixpath))
     580              :   {
     581            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     582              :                                section,
     583              :                                "UNIXPATH");
     584            0 :     return EXIT_NOTCONFIGURED;
     585              :   }
     586           54 :   GNUNET_assert (NULL != unixpath);
     587           54 :   unix_sock = TES_open_socket (unixpath);
     588           54 :   if (NULL == unix_sock)
     589              :   {
     590            0 :     GNUNET_free (unixpath);
     591            0 :     GNUNET_break (0);
     592            0 :     return EXIT_NOPERMISSION;
     593              :   }
     594              :   /* start job to accept incoming requests on 'sock' */
     595           54 :   listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
     596              :                                                unix_sock,
     597              :                                                &listen_job,
     598              :                                                (void *) cb);
     599           54 :   return 0;
     600              : }
     601              : 
     602              : 
     603              : void
     604           54 : TES_listen_stop (void)
     605              : {
     606              :   struct TES_Client *client;
     607              : 
     608           54 :   if (NULL != listen_task)
     609              :   {
     610           54 :     GNUNET_SCHEDULER_cancel (listen_task);
     611           54 :     listen_task = NULL;
     612              :   }
     613           54 :   if (NULL != unix_sock)
     614              :   {
     615           54 :     GNUNET_break (GNUNET_OK ==
     616              :                   GNUNET_NETWORK_socket_close (unix_sock));
     617           54 :     unix_sock = NULL;
     618              :   }
     619           54 :   if (0 != unlink (unixpath))
     620              :   {
     621            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     622              :                               "unlink",
     623              :                               unixpath);
     624              :   }
     625           54 :   GNUNET_free (unixpath);
     626           54 :   in_shutdown = true;
     627           54 :   TES_wake_clients ();
     628           54 :   GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
     629          129 :   while (NULL != (client = TES_clients_head))
     630              :   {
     631              :     void *rval;
     632              : 
     633           75 :     GNUNET_CONTAINER_DLL_remove (TES_clients_head,
     634              :                                  TES_clients_tail,
     635              :                                  client);
     636           75 :     GNUNET_break (0 ==
     637              :                   pthread_join (client->worker,
     638              :                                 &rval));
     639           75 :     GNUNET_free (client);
     640              :   }
     641           54 :   GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
     642           54 : }
        

Generated by: LCOV version 2.0-1