LCOV - code coverage report
Current view: top level - src - japi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 166 281 59.1 %
Date: 2024-01-19 10:32:55 Functions: 10 14 71.4 %

          Line data    Source code
       1             : /*!
       2             :  * \file
       3             :  * \author Christopher Stender
       4             :  * \date 2018-02-15
       5             :  * \version 0.1
       6             :  *
       7             :  * \brief Universal JSON API library.
       8             :  *
       9             :  * \details
      10             :  * libjapi is a universal JSON API library.
      11             :  *
      12             :  * \copyright
      13             :  * Copyright (c) 2023 Fraunhofer IIS
      14             :  *
      15             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
      16             :  * of this software and associated documentation files (the “Software”), to deal
      17             :  * in the Software without restriction, including without limitation the rights
      18             :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      19             :  * copies of the Software, and to permit persons to whom the Software is
      20             :  * furnished to do so, subject to the following conditions:
      21             :  *
      22             :  * The above copyright notice and this permission notice shall be included in
      23             :  * all copies or substantial portions of the Software.
      24             :  *
      25             :  * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      26             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      27             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      28             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      29             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      30             :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      31             :  * THE SOFTWARE.
      32             :  */
      33             : 
      34             : #include <assert.h>
      35             : #include <signal.h>
      36             : #include <stdio.h>
      37             : #include <string.h> /* strcmp */
      38             : #include <strings.h> /* strcasecmp */
      39             : #include <sys/select.h>
      40             : #include <sys/socket.h>
      41             : #include <sys/time.h>
      42             : #include <unistd.h>
      43             : 
      44             : #include "japi.h"
      45             : 
      46             : #include "creadline.h"
      47             : #include "japi_intern.h"
      48             : #include "japi_pushsrv.h"
      49             : #include "japi_pushsrv_intern.h"
      50             : #include "japi_utils.h"
      51             : #include "networking.h"
      52             : #include "prntdbg.h"
      53             : #include "rw_n.h"
      54             : 
      55             : /* Look for a request handler matching the name 'name'.
      56             :  *
      57             :  * NULL is returned if no suitable handler was found.
      58             :  */
      59          75 : static japi_req_handler japi_get_request_handler(japi_context *ctx, const char *name)
      60             : {
      61             :         japi_request *req;
      62             : 
      63          75 :         req = ctx->requests;
      64         330 :         while (req != NULL) {
      65             : 
      66         185 :                 if (strcasecmp(name, req->name) == 0) {
      67           5 :                         return req->func;
      68             :                 }
      69         180 :                 req = req->next;
      70             :         }
      71             : 
      72          70 :         return NULL;
      73             : }
      74             : 
      75             : /* Steps performed while processing a JSON request:
      76             :  * - Convert the received message into a JSON object
      77             :  * - Extract the request name
      78             :  * - Search a suitable request handler
      79             :  * - Call the request handler
      80             :  * - Prepare the JSON response
      81             :  * - Free memory
      82             :  */
      83           4 : int japi_process_message(japi_context *ctx, const char *request, char **response,
      84             :                                                  int socket)
      85             : {
      86             :         const char *req_name;
      87             :         json_object *jreq;
      88             :         json_object *jreq_no;
      89             :         json_object *jresp;
      90             :         json_object *jresp_data;
      91             :         json_object *jargs;
      92             :         japi_req_handler req_handler;
      93             :         int ret;
      94             :         bool args;
      95             : 
      96           4 :         assert(ctx != NULL);
      97           4 :         assert(response != NULL);
      98           4 :         assert(socket >= 0);
      99             : 
     100           4 :         ret = -1;
     101           4 :         *response = NULL;
     102             : 
     103             :         /* Create JSON object from received message */
     104           4 :         jreq = json_tokener_parse(request);
     105           4 :         if (jreq == NULL) {
     106           0 :                 fprintf(stderr, "ERROR: json_tokener_parse() failed. Received message: %s\n",
     107             :                                 request);
     108           0 :                 return -1;
     109             :         }
     110             : 
     111             :         /* Only create new JSON objects after a valid JSON request was parsed. */
     112           4 :         jresp = json_object_new_object(); /* Response object */
     113           4 :         jresp_data = json_object_new_object();
     114             : 
     115           4 :         if ((japi_get_value_as_str(jreq, "japi_request", &req_name)) == 0) {
     116             : 
     117             :                 /* Prepare response */
     118           4 :                 json_object_object_add(jresp, "japi_response",
     119             :                                                            json_object_new_string(req_name));
     120             : 
     121             :                 /* Include japi_request_no in response, if included with request */
     122           4 :                 if (json_object_object_get_ex(jreq, "japi_request_no", &jreq_no)) {
     123           0 :                         json_object_get(jreq_no);
     124           0 :                         json_object_object_add(jresp, "japi_request_no", jreq_no);
     125             :                 }
     126             : 
     127             :                 /* Get arguments as an JSON object */
     128           4 :                 args = json_object_object_get_ex(jreq, "args", &jargs);
     129             : 
     130             :                 /* Add an empty args JSON object if no args were given
     131             :                    Otherwise, include args with response, if configured. */
     132           4 :                 if (!args) {
     133           2 :                         json_object_object_add(jreq, "args", NULL);
     134           2 :                         json_object_object_get_ex(jreq, "args", &jargs);
     135             :                 } else {
     136           2 :                         if (ctx->include_args_in_response) {
     137           2 :                                 json_object_get(jargs);
     138           2 :                                 json_object_object_add(jresp, "args", jargs);
     139             :                         }
     140             :                 }
     141             : 
     142             :                 /* Subscribe/unsubscribe service needs client socket */
     143           8 :                 if (strcasecmp(req_name, "japi_pushsrv_subscribe") == 0 ||
     144           4 :                         strcasecmp(req_name, "japi_pushsrv_unsubscribe") == 0) {
     145           0 :                         json_object_object_add(jargs, "socket", json_object_new_int(socket));
     146             :                 }
     147             : 
     148             :                 /* Try to find a suitable handler for the given request */
     149           4 :                 req_handler = japi_get_request_handler(ctx, req_name);
     150           4 :                 if (req_handler == NULL) {
     151             : 
     152             :                         /* No request handler found? Check if a fallback handler was registered. */
     153           1 :                         req_handler = japi_get_request_handler(ctx, "request_not_found_handler");
     154             : 
     155           1 :                         if (req_handler == NULL) {
     156           1 :                                 fprintf(stderr,
     157             :                                                 "ERROR: No suitable request handler found. Falling back to "
     158             :                                                 "default fallback handler. Request was: %s\n",
     159             :                                                 req_name);
     160           1 :                                 req_handler =
     161             :                                         japi_get_request_handler(ctx, "japi_request_not_found_handler");
     162             :                         } else {
     163           0 :                                 fprintf(stderr,
     164             :                                                 "WARNING: No suitable request handler found. Falling back to "
     165             :                                                 "user registered fallback handler. Request was: %s\n",
     166             :                                                 req_name);
     167             :                         }
     168             :                 }
     169             : 
     170             :                 /* Call request handler */
     171           4 :                 req_handler(ctx, jargs, jresp_data);
     172             : 
     173             :         } else {
     174             :                 /* Get request name */
     175           0 :                 if (req_name == NULL) {
     176           0 :                         fprintf(stderr, "ERROR: No keyword found!\n");
     177           0 :                         goto out_free;
     178             :                 }
     179             :         }
     180             : 
     181             :         /* Add response arguments */
     182           4 :         json_object_object_add(jresp, "data", jresp_data);
     183             : 
     184             :         /* Stringify response */
     185           4 :         *response = japi_get_jobj_as_ndstr(jresp);
     186           4 :         json_object_put(jresp);
     187             : 
     188           4 :         ret = 0;
     189             : 
     190             : out_free:
     191             :         /* Free JSON request object */
     192           4 :         json_object_put(jreq);
     193             : 
     194           4 :         return ret;
     195             : }
     196             : 
     197           0 : int japi_shutdown(japi_context *ctx)
     198             : {
     199           0 :         if (ctx == NULL) {
     200           0 :                 fprintf(stderr, "ERROR: JAPI context is NULL.\n");
     201           0 :                 return -1;
     202             :         }
     203             : 
     204           0 :         ctx->shutdown = true;
     205             : 
     206           0 :         return 0;
     207             : }
     208             : 
     209           5 : int japi_destroy(japi_context *ctx)
     210             : {
     211             :         japi_request *req, *req_next;
     212             :         japi_pushsrv_context *psc, *psc_next;
     213             : 
     214           5 :         if (ctx == NULL) {
     215           0 :                 fprintf(stderr, "ERROR: JAPI context is NULL.\n");
     216           0 :                 return -1;
     217             :         }
     218             : 
     219           5 :         req = ctx->requests;
     220          39 :         while (req != NULL) {
     221          29 :                 req_next = req->next;
     222          29 :                 free(req);
     223          29 :                 req = req_next;
     224             :         }
     225             : 
     226           5 :         psc = ctx->push_services;
     227          14 :         while (psc != NULL) {
     228           4 :                 psc_next = psc->next;
     229           4 :                 japi_pushsrv_destroy(ctx, psc);
     230           4 :                 psc = psc_next;
     231             :         }
     232             : 
     233           5 :         pthread_mutex_destroy(&(ctx->lock));
     234           5 :         free(ctx);
     235             : 
     236           5 :         return 0;
     237             : }
     238             : 
     239          73 : int japi_register_request(japi_context *ctx, const char *req_name,
     240             :                                                   japi_req_handler req_handler)
     241             : {
     242             :         japi_request *req;
     243          73 :         char *bad_req_name = "japi_";
     244             : 
     245             :         /* Error handling */
     246          73 :         if (ctx == NULL) {
     247           1 :                 fprintf(stderr, "ERROR: JAPI context is NULL.\n");
     248           1 :                 return -1;
     249             :         }
     250             : 
     251          72 :         if ((req_name == NULL) || (strcmp(req_name, "") == 0)) {
     252           2 :                 fprintf(stderr, "ERROR: Request name is NULL or empty.\n");
     253           2 :                 return -2;
     254             :         }
     255             : 
     256          70 :         if (req_handler == NULL) {
     257           1 :                 fprintf(stderr, "ERROR: Request handler is NULL.\n");
     258           1 :                 return -3;
     259             :         }
     260             : 
     261          69 :         if (japi_get_request_handler(ctx, req_name) != NULL) {
     262           1 :                 fprintf(stderr,
     263             :                                 "ERROR: A request handler called '%s' was already registered.\n",
     264             :                                 req_name);
     265           1 :                 return -4;
     266             :         }
     267             : 
     268          68 :         if (ctx->init && strncmp(req_name, bad_req_name, strlen(bad_req_name)) == 0) {
     269           1 :                 fprintf(stderr, "ERROR: Request name is not allowed.\n");
     270           1 :                 return -6;
     271             :         }
     272             : 
     273          67 :         req = (japi_request *)malloc(sizeof(japi_request));
     274          67 :         if (req == NULL) {
     275           0 :                 perror("ERROR: malloc() failed");
     276           0 :                 return -5;
     277             :         }
     278             : 
     279          67 :         req->name = req_name;
     280          67 :         req->func = req_handler;
     281          67 :         req->next = ctx->requests;
     282             : 
     283          67 :         ctx->requests = req;
     284             : 
     285          67 :         return 0;
     286             : }
     287             : 
     288          12 : japi_context *japi_init(void *userptr)
     289             : {
     290             :         japi_context *ctx;
     291             : 
     292          12 :         ctx = (japi_context *)malloc(sizeof(japi_context));
     293          12 :         if (ctx == NULL) {
     294           0 :                 perror("ERROR: malloc() failed");
     295           0 :                 return NULL;
     296             :         }
     297             : 
     298          12 :         ctx->init = false;
     299          12 :         ctx->userptr = userptr;
     300          12 :         ctx->requests = NULL;
     301          12 :         ctx->push_services = NULL;
     302          12 :         ctx->clients = NULL;
     303          12 :         ctx->num_clients = 0;
     304          12 :         ctx->max_clients = 0;
     305          12 :         ctx->include_args_in_response = false;
     306          12 :         ctx->shutdown = false;
     307             : 
     308             :         /* Initialize mutex */
     309          12 :         if (pthread_mutex_init(&(ctx->lock), NULL) != 0) {
     310           0 :                 fprintf(stderr, "ERROR: mutex initialization has failed\n");
     311           0 :                 return NULL;
     312             :         }
     313             : 
     314             :         /* Ignore SIGPIPE Signal */
     315          12 :         signal(SIGPIPE, SIG_IGN);
     316             : 
     317             :         /* Register the default fallback handler */
     318          12 :         japi_register_request(ctx, "japi_request_not_found_handler",
     319             :                                                   &japi_request_not_found_handler);
     320             :         /* Register subscribe/unsubscribe service function */
     321          12 :         japi_register_request(ctx, "japi_pushsrv_subscribe", &japi_pushsrv_subscribe);
     322          12 :         japi_register_request(ctx, "japi_pushsrv_unsubscribe", &japi_pushsrv_unsubscribe);
     323             :         /* Register list_push_service function  */
     324          12 :         japi_register_request(ctx, "japi_pushsrv_list", &japi_pushsrv_list);
     325          12 :         japi_register_request(ctx, "japi_cmd_list", &japi_cmd_list);
     326             : 
     327          12 :         ctx->init = true;
     328             : 
     329          12 :         return ctx;
     330             : }
     331             : 
     332             : /*
     333             :  * Set maximal allowed number of clients.
     334             :  * 0 is interpreted as unlimited number of allowed clients.
     335             :  */
     336           0 : int japi_set_max_allowed_clients(japi_context *ctx, uint16_t num)
     337             : {
     338             :         /* Error handling */
     339           0 :         if (ctx == NULL) {
     340           0 :                 fprintf(stderr, "ERROR: JAPI context is NULL.\n");
     341           0 :                 return -1;
     342             :         }
     343             : 
     344           0 :         ctx->max_clients = num;
     345             : 
     346           0 :         return 0;
     347             : }
     348             : 
     349             : /*
     350             :  * Include request arguments in response.
     351             :  */
     352           3 : int japi_include_args_in_response(japi_context *ctx, bool include_args)
     353             : {
     354             :         /* Error handling */
     355           3 :         if (ctx == NULL) {
     356           1 :                 fprintf(stderr, "ERROR: JAPI context is NULL.\n");
     357           1 :                 return -1;
     358             :         }
     359             : 
     360           2 :         ctx->include_args_in_response = include_args;
     361             : 
     362           2 :         return 0;
     363             : }
     364             : 
     365             : /*
     366             :  * Add new client element to list
     367             :  */
     368           6 : int japi_add_client(japi_context *ctx, int socket)
     369             : {
     370             :         japi_client *client;
     371             : 
     372             :         /* Error handling */
     373           6 :         assert(ctx != NULL);
     374           6 :         assert(socket >= 0);
     375             : 
     376             :         /* Create new client list element */
     377           6 :         client = (japi_client *)malloc(sizeof(japi_client));
     378           6 :         if (client == NULL) {
     379           0 :                 perror("ERROR: malloc() failed");
     380           0 :                 return -1;
     381             :         }
     382             : 
     383             :         /* Reset the clients buffer used by creadline_r */
     384           6 :         client->crl_buffer.nbytes = 0;
     385             : 
     386           6 :         pthread_mutex_lock(&(ctx->lock));
     387           6 :         prntdbg("adding client %d to japi context\n", socket);
     388             :         /* Add socket */
     389           6 :         client->socket = socket;
     390             : 
     391             :         /* Link list */
     392           6 :         client->next = ctx->clients;
     393           6 :         ctx->clients = client;
     394             :         /* Increment number of connected clients */
     395           6 :         ctx->num_clients++;
     396           6 :         pthread_mutex_unlock(&(ctx->lock));
     397             : 
     398           6 :         return 0;
     399             : }
     400             : 
     401             : /*
     402             :  * Remove client from client list
     403             :  */
     404           4 : int japi_remove_client(japi_context *ctx, int socket)
     405             : {
     406             :         japi_client *client, *prev;
     407             :         int ret;
     408             : 
     409             :         /* Error Handling */
     410           4 :         assert(ctx != NULL);
     411           4 :         assert(socket >= 0);
     412             : 
     413           4 :         client = ctx->clients;
     414           4 :         prev = NULL;
     415           4 :         ret = -1;
     416             : 
     417           4 :         japi_pushsrv_remove_client_from_all_pushsrv(ctx, socket);
     418             : 
     419           4 :         pthread_mutex_lock(&(ctx->lock));
     420             :         /* Remove client from list */
     421          21 :         while (client != NULL) {
     422             :                 /* If first element */
     423          15 :                 if ((client->socket == socket) && (prev == NULL)) {
     424           1 :                         ctx->clients = client->next;
     425           1 :                         prntdbg("removing client %d from japi context and close socket\n",
     426             :                                         client->socket);
     427           1 :                         close(client->socket);
     428           1 :                         free(client);
     429           1 :                         ctx->num_clients--;
     430           1 :                         ret = 0;
     431           1 :                         break;
     432             :                 }
     433             :                 /* If last element */
     434          14 :                 if ((client->socket == socket) && (client->next == NULL)) {
     435           1 :                         prev->next = NULL;
     436           1 :                         prntdbg("removing client %d from japi context and close socket\n",
     437             :                                         client->socket);
     438           1 :                         close(client->socket);
     439           1 :                         free(client);
     440           1 :                         ctx->num_clients--;
     441           1 :                         ret = 0;
     442           1 :                         break;
     443             :                 }
     444          13 :                 if (client->socket == socket) {
     445           0 :                         prev->next = client->next;
     446           0 :                         prntdbg("removing client %d from japi context and close socket\n",
     447             :                                         client->socket);
     448           0 :                         close(client->socket);
     449           0 :                         free(client);
     450           0 :                         ctx->num_clients--;
     451           0 :                         ret = 0;
     452           0 :                         break;
     453             :                 }
     454             : 
     455          13 :                 prev = client;
     456          13 :                 client = client->next;
     457             :         }
     458           4 :         pthread_mutex_unlock(&(ctx->lock));
     459             : 
     460           4 :         return ret;
     461             : }
     462             : 
     463           0 : int japi_remove_all_clients(japi_context *ctx)
     464             : {
     465             :         japi_client *client, *following_client;
     466             : 
     467             :         /* Error Handling */
     468           0 :         assert(ctx != NULL);
     469             : 
     470           0 :         client = ctx->clients;
     471           0 :         while (client != NULL) {
     472           0 :                 following_client = client->next;
     473           0 :                 if (japi_remove_client(ctx, client->socket) != 0) {
     474           0 :                         return -1;
     475             :                 }
     476           0 :                 client = following_client;
     477             :         }
     478             : 
     479           0 :         return 0;
     480             : }
     481             : 
     482           0 : int japi_start_server(japi_context *ctx, const char *port)
     483             : {
     484             :         int server_socket;
     485             : 
     486           0 :         server_socket = tcp_start_server(port);
     487           0 :         if (server_socket < 0) {
     488           0 :                 fprintf(stderr, "ERROR: Failed to start tcp server on port %s\n", port);
     489           0 :                 return -1;
     490             :         }
     491             : 
     492             :         int ret;
     493             :         int nfds;
     494             :         fd_set fdrd;
     495             :         struct timeval timeout;
     496             :         japi_client *client, *following_client;
     497             : 
     498           0 :         if (listen(server_socket, 1) != 0) {
     499           0 :                 perror("ERROR: listen() failed\n");
     500           0 :                 return -1;
     501             :         }
     502             : 
     503             :         while (1) {
     504             : 
     505           0 :                 FD_ZERO(&fdrd);
     506             : 
     507             :                 /* Add server socket to set */
     508           0 :                 FD_SET(server_socket, &fdrd);
     509             : 
     510           0 :                 nfds = server_socket + 1;
     511             : 
     512             :                 /* Add all file descriptors to set */
     513           0 :                 client = ctx->clients;
     514           0 :                 while (client != NULL) {
     515           0 :                         FD_SET(client->socket, &fdrd);
     516           0 :                         if (client->socket >= nfds) {
     517           0 :                                 nfds = client->socket + 1;
     518             :                         }
     519           0 :                         client = client->next;
     520             :                 }
     521             : 
     522           0 :                 timeout.tv_sec = 0;
     523           0 :                 timeout.tv_usec = 200000;
     524           0 :                 ret = select(nfds, &fdrd, NULL, NULL, &timeout);
     525           0 :                 if (ret == -1) {
     526           0 :                         perror("ERROR: select() failed\n");
     527           0 :                         return -1;
     528             :                 }
     529             : 
     530             :                 /* Check if there is a request to shutdown the server */
     531           0 :                 if (ctx->shutdown == true) {
     532           0 :                         break;
     533           0 :                 } else if (ret == 0) {
     534           0 :                         continue;
     535             :                 }
     536             : 
     537           0 :                 client = ctx->clients; // Reset pointer to list
     538             : 
     539           0 :                 while (client != NULL) {
     540             : 
     541           0 :                         following_client = client->next;
     542             : 
     543             :                         /* Check whether there is data to process */
     544           0 :                         if (FD_ISSET(client->socket, &fdrd)) {
     545             : 
     546             :                                 int ret;
     547             :                                 char *request;
     548             :                                 char *response;
     549             : 
     550             :                                 do {
     551             : 
     552           0 :                                         ret = creadline_r(client->socket, (void **)&request,
     553             :                                                                           &(client->crl_buffer));
     554           0 :                                         if (ret > 0) {
     555             : 
     556           0 :                                                 response = NULL;
     557             : 
     558             :                                                 /* Received a line, process it... */
     559           0 :                                                 japi_process_message(ctx, request, &response, client->socket);
     560             : 
     561             :                                                 /* After the request buffer is processed, the memory
     562             :                                                  *is not needed anymore and can be freed at this point. */
     563           0 :                                                 free(request);
     564             : 
     565             :                                                 /* Send response (if provided) */
     566           0 :                                                 if (response != NULL) {
     567           0 :                                                         ret = write_n(client->socket, response, strlen(response));
     568           0 :                                                         free(response);
     569             : 
     570           0 :                                                         if (ret <= 0) {
     571             :                                                                 /* Write failed */
     572           0 :                                                                 fprintf(stderr,
     573             :                                                                                 "ERROR: Failed to send response to client %i "
     574             :                                                                                 "(write returned %i)\n",
     575             :                                                                                 client->socket, ret);
     576           0 :                                                                 japi_remove_client(ctx, client->socket);
     577           0 :                                                                 break;
     578             :                                                         }
     579             :                                                 }
     580           0 :                                         } else if (ret == 0) {
     581           0 :                                                 if (request == NULL) {
     582             :                                                         /* Received EOF (client disconnected) */
     583           0 :                                                         prntdbg("client %d disconnected\n", client->socket);
     584           0 :                                                         japi_remove_client(ctx, client->socket);
     585           0 :                                                         break;
     586             :                                                 } else {
     587             :                                                         /* Received an empty line */
     588           0 :                                                         free(request);
     589             :                                                 }
     590             :                                         } else {
     591           0 :                                                 fprintf(stderr, "ERROR: creadline() failed (ret = %i)\n", ret);
     592           0 :                                                 japi_remove_client(ctx, client->socket);
     593           0 :                                                 break;
     594             :                                         }
     595             : 
     596           0 :                                 } while (client->crl_buffer.nbytes != 0);
     597             :                         }
     598           0 :                         client = following_client;
     599             :                 }
     600             : 
     601             :                 /* Check whether there are new clients */
     602           0 :                 if (FD_ISSET(server_socket, &fdrd)) {
     603           0 :                         int client_socket = 0;
     604             : 
     605           0 :                         client_socket = accept(server_socket, NULL, NULL);
     606           0 :                         if (client_socket < 0) {
     607           0 :                                 perror("ERROR: accept() failed\n");
     608           0 :                                 return -1;
     609             :                         }
     610           0 :                         if (ctx->max_clients == 0 || ctx->num_clients < ctx->max_clients) {
     611           0 :                                 japi_add_client(ctx, client_socket);
     612           0 :                                 prntdbg("client %d added\n", client_socket);
     613             :                         } else {
     614           0 :                                 close(client_socket);
     615             :                         }
     616             :                 }
     617           0 :         }
     618             : 
     619             :         /* Clean up */
     620           0 :         japi_remove_all_clients(ctx);
     621             : 
     622           0 :         close(server_socket);
     623             : 
     624           0 :         return 0;
     625             : }
     626             : 
     627             : /*
     628             :  * Provide the names of all registered commands as a JAPI response.
     629             :  */
     630           1 : void japi_cmd_list(japi_context *ctx, json_object *request, json_object *response)
     631             : {
     632             :         japi_request *req;
     633             :         json_object *jstring;
     634             :         json_object *jarray;
     635             : 
     636           1 :         assert(ctx != NULL);
     637           1 :         assert(response != NULL);
     638             : 
     639           1 :         jarray = json_object_new_array();
     640           1 :         req = ctx->requests;
     641             : 
     642             :         /* Iterate through push service list and return JSON object  */
     643          10 :         while (req != NULL) {
     644           8 :                 jstring = json_object_new_string(req->name); /* Create JSON-string */
     645           8 :                 json_object_array_add(jarray, jstring); /* Add string to JSON array */
     646           8 :                 req = req->next;
     647             :         }
     648             : 
     649             :         /* Add array to JSON-object */
     650           1 :         json_object_object_add(response, "commands", jarray);
     651           1 : }
     652             : 
     653             : /*
     654             :  * Default handler for reacting to unknown requests.
     655             :  */
     656           1 : void japi_request_not_found_handler(japi_context *ctx, json_object *request,
     657             :                                                                         json_object *response)
     658             : {
     659           1 :         json_object_object_add(response, "error",
     660             :                                                    json_object_new_string("no request handler found"));
     661           1 : }

Generated by: LCOV version 1.13