LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_client.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 267 342 78.1 %
Date: 2024-11-14 20:04:02 Functions: 15 20 75.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /*
      14             :  * Clients gain access to the Monet server through a internet connection.
      15             :  * Access through the internet requires a client program at the source,
      16             :  * which addresses the default port of a running server. It is a textual
      17             :  * interface for expert use.
      18             :  *
      19             :  * At the server side, each client is represented by a session record
      20             :  * with the current status, such as name, file descriptors, namespace,
      21             :  * and local stack.  Each client session has a dedicated thread of
      22             :  * control.
      23             :  *
      24             :  * The number of clients permitted concurrent access is a run time
      25             :  * option.
      26             :  *
      27             :  * Client sessions remain in existence until the corresponding
      28             :  * communication channels break.
      29             :  *
      30             :  * A client record is initialized upon acceptance of a connection.  The
      31             :  * client runs in his own thread of control until it finds a
      32             :  * soft-termination request mode (FINISHCLIENT) or its IO file descriptors
      33             :  * are closed. The latter generates an IO error, which leads to a safe
      34             :  * termination.
      35             :  *
      36             :  * The system administrator client runs in the primary thread of control
      37             :  * to simplify debugging with external debuggers.
      38             :  *
      39             :  * Searching a free client record is encapsulated in a critical section
      40             :  * to hand them out one-at-a-time.  Marking them as being claimed avoids
      41             :  * any interference from parallel actions to obtain client records.
      42             :  */
      43             : 
      44             : /* (author) M.L. Kersten */
      45             : #include "monetdb_config.h"
      46             : #include "mal_client.h"
      47             : #include "mal_import.h"
      48             : #include "mal_parser.h"
      49             : #include "mal_namespace.h"
      50             : #include "mal_private.h"
      51             : #include "mal_internal.h"
      52             : #include "mal_interpreter.h"
      53             : #include "mal_runtime.h"
      54             : #include "mal_authorize.h"
      55             : #include "mapi_prompt.h"
      56             : 
      57             : int MAL_MAXCLIENTS = 0;
      58             : ClientRec *mal_clients = NULL;
      59             : 
      60             : void
      61         326 : mal_client_reset(void)
      62             : {
      63         326 :         if (mal_clients) {
      64         326 :                 GDKfree(mal_clients);
      65         326 :                 mal_clients = NULL;
      66             :         }
      67         326 :         MAL_MAXCLIENTS = 0;
      68         326 : }
      69             : 
      70             : bool
      71         328 : MCinit(void)
      72             : {
      73         328 :         const char *max_clients = GDKgetenv("max_clients");
      74         328 :         int maxclients = 0;
      75             : 
      76         328 :         if (max_clients != NULL)
      77           2 :                 maxclients = atoi(max_clients);
      78           2 :         if (maxclients <= 0) {
      79         326 :                 maxclients = 64;
      80         326 :                 if (GDKsetenv("max_clients", "64") != GDK_SUCCEED) {
      81           0 :                         TRC_CRITICAL(MAL_SERVER,
      82             :                                                  "Initialization failed: " MAL_MALLOC_FAIL "\n");
      83           0 :                         return false;
      84             :                 }
      85             :         }
      86             : 
      87         328 :         MAL_MAXCLIENTS = /* client connections */ maxclients;
      88         328 :         mal_clients = GDKzalloc(sizeof(ClientRec) * MAL_MAXCLIENTS);
      89         328 :         if (mal_clients == NULL) {
      90           0 :                 TRC_CRITICAL(MAL_SERVER,
      91             :                                          "Initialization failed: " MAL_MALLOC_FAIL "\n");
      92           0 :                 return false;
      93             :         }
      94       23244 :         for (int i = 0; i < MAL_MAXCLIENTS; i++) {
      95       22916 :                 ATOMIC_INIT(&mal_clients[i].lastprint, 0);
      96       22916 :                 ATOMIC_INIT(&mal_clients[i].workers, 1);
      97       22916 :                 ATOMIC_INIT(&mal_clients[i].qryctx.datasize, 0);
      98       22916 :                 mal_clients[i].idx = -1;        /* indicate it's available */
      99             :         }
     100             :         return true;
     101             : }
     102             : 
     103             : /* stack the files from which you read */
     104             : int
     105           0 : MCpushClientInput(Client c, bstream *new_input, int listing, const char *prompt)
     106             : {
     107           0 :         ClientInput *x = (ClientInput *) GDKmalloc(sizeof(ClientInput));
     108           0 :         if (x == 0)
     109             :                 return -1;
     110           0 :         *x = (ClientInput) {
     111           0 :                 .fdin = c->fdin,
     112           0 :                 .yycur = c->yycur,
     113           0 :                 .listing = c->listing,
     114           0 :                 .prompt = c->prompt,
     115           0 :                 .next = c->bak,
     116             :         };
     117           0 :         c->bak = x;
     118           0 :         c->fdin = new_input;
     119           0 :         c->qryctx.bs = new_input;
     120           0 :         c->listing = listing;
     121           0 :         c->prompt = prompt ? prompt : "";
     122           0 :         c->promptlength = strlen(c->prompt);
     123           0 :         c->yycur = 0;
     124           0 :         return 0;
     125             : }
     126             : 
     127             : void
     128           0 : MCpopClientInput(Client c)
     129             : {
     130           0 :         ClientInput *x = c->bak;
     131           0 :         if (c->fdin) {
     132             :                 /* missing protection against closing stdin stream */
     133           0 :                 bstream_destroy(c->fdin);
     134             :         }
     135           0 :         c->fdin = x->fdin;
     136           0 :         c->qryctx.bs = c->fdin;
     137           0 :         c->yycur = x->yycur;
     138           0 :         c->listing = x->listing;
     139           0 :         c->prompt = x->prompt;
     140           0 :         c->promptlength = strlen(c->prompt);
     141           0 :         c->bak = x->next;
     142           0 :         GDKfree(x);
     143           0 : }
     144             : 
     145             : static Client
     146       37717 : MCnewClient(void)
     147             : {
     148      460705 :         for (Client c = mal_clients; c < mal_clients + MAL_MAXCLIENTS; c++) {
     149      460705 :                 if (c->idx == -1) {
     150       37717 :                         assert(c->mode == FREECLIENT);
     151       37717 :                         c->mode = RUNCLIENT;
     152       37717 :                         c->idx = (int) (c - mal_clients);
     153       37717 :                         return c;
     154             :                 }
     155             :         }
     156             : 
     157             :         return NULL;
     158             : }
     159             : 
     160             : /*
     161             :  * You can always retrieve a client record using the thread identifier,
     162             :  * because we maintain a 1-1 mapping between client and thread of
     163             :  * control.  Therefore, we don't need locks either.
     164             :  * If the number of clients becomes too large, we have to change the
     165             :  * allocation and lookup scheme.
     166             :  *
     167             :  * Finding a client record is tricky when we are spawning threads as
     168             :  * co-workers. It is currently passed as an argument.
     169             :  */
     170             : 
     171             : Client
     172      368573 : MCgetClient(int id)
     173             : {
     174      368573 :         if (id <0 || id >=MAL_MAXCLIENTS)
     175             :                 return NULL;
     176      368573 :         return mal_clients + id;
     177             : }
     178             : 
     179             : /*
     180             :  * The resetProfiler is called when the owner of the event stream
     181             :  * leaves the scene. (Unclear if parallelism may cause errors)
     182             :  */
     183             : 
     184             : static void
     185       37716 : MCresetProfiler(stream *fdout)
     186             : {
     187       37716 :         MT_lock_set(&mal_profileLock);
     188       37716 :         if (fdout == maleventstream) {
     189           0 :                 maleventstream = NULL;
     190           0 :                 profilerStatus = 0;
     191           0 :                 profilerMode = 0;
     192             :         }
     193       37716 :         MT_lock_unset(&mal_profileLock);
     194       37716 : }
     195             : 
     196             : static void
     197       37716 : MCexitClient(Client c)
     198             : {
     199       37716 :         MCresetProfiler(c->fdout);
     200             :         // Remove any left over constant symbols
     201       37716 :         if (c->curprg)
     202         327 :                 resetMalBlk(c->curprg->def);
     203       37717 :         if (c->father == NULL) {     /* normal client */
     204       37716 :                 if (c->fdout && c->fdout != GDKstdout)
     205       37366 :                         close_stream(c->fdout);
     206       37716 :                 assert(c->bak == NULL);
     207       37716 :                 if (c->fdin) {
     208             :                         /* protection against closing stdin stream */
     209       37716 :                         if (c->fdin->s == GDKstdin)
     210         350 :                                 c->fdin->s = NULL;
     211       37716 :                         bstream_destroy(c->fdin);
     212             :                 }
     213       37715 :                 c->fdout = NULL;
     214       37715 :                 c->fdin = NULL;
     215       37715 :                 c->qryctx.bs = NULL;
     216             :         }
     217       37716 :         assert(c->query == NULL);
     218       37716 :         if (profilerStatus > 0) {
     219           0 :                 lng Tend = GDKusec();
     220           0 :                 profilerEvent(NULL,
     221             :                                           &(struct NonMalEvent)
     222           0 :                                           { CLIENT_END, c, Tend, NULL, NULL, 0,
     223           0 :                                           Tend - (c->session) });
     224             :         }
     225       37716 : }
     226             : 
     227             : static Client
     228       37717 : MCinitClientRecord(Client c, oid user, bstream *fin, stream *fout)
     229             : {
     230             :         /* mal_contextLock is held when this is called */
     231       37717 :         c->user = user;
     232       37717 :         c->username = 0;
     233       37717 :         c->scenario = NULL;
     234       37717 :         c->srcFile = NULL;
     235       37717 :         c->blkmode = 0;
     236             : 
     237       37717 :         c->fdin = fin ? fin : bstream_create(GDKstdin, 0);
     238       37717 :         if (c->fdin == NULL) {
     239           0 :                 c->mode = FREECLIENT;
     240           0 :                 c->idx = -1;
     241           0 :                 TRC_ERROR(MAL_SERVER, "No stdin channel available\n");
     242           0 :                 return NULL;
     243             :         }
     244       37717 :         c->qryctx.bs = c->fdin;
     245       37717 :         c->yycur = 0;
     246       37717 :         c->bak = NULL;
     247             : 
     248       37717 :         c->listing = 0;
     249       37717 :         c->fdout = fout ? fout : GDKstdout;
     250       37717 :         c->curprg = c->backup = 0;
     251       37717 :         c->glb = 0;
     252             : 
     253             :         /* remove garbage from previous connection
     254             :          * be aware, a user can introduce several modules
     255             :          * that should be freed to avoid memory leaks */
     256       37717 :         c->usermodule = c->curmodule = 0;
     257             : 
     258       37717 :         c->father = NULL;
     259       37717 :         c->idle = c->login = c->lastcmd = time(0);
     260       37717 :         c->session = GDKusec();
     261       37717 :         strcpy_len(c->optimizer, "default_pipe", sizeof(c->optimizer));
     262       37717 :         c->workerlimit = 0;
     263       37717 :         c->memorylimit = 0;
     264       37717 :         c->querytimeout = 0;
     265       37717 :         c->sessiontimeout = 0;
     266       37717 :         c->logical_sessiontimeout = 0;
     267       37717 :         c->qryctx.starttime = 0;
     268       37717 :         c->qryctx.endtime = 0;
     269       37717 :         ATOMIC_SET(&c->qryctx.datasize, 0);
     270       37717 :         c->qryctx.maxmem = 0;
     271       37717 :         c->maxmem = 0;
     272       37717 :         c->errbuf = 0;
     273             : 
     274       37717 :         c->prompt = PROMPT1;
     275       37717 :         c->promptlength = strlen(c->prompt);
     276             : 
     277       37717 :         c->profticks = c->profstmt = c->profevents = NULL;
     278       37717 :         c->error_row = c->error_fld = c->error_msg = c->error_input = NULL;
     279       37717 :         c->sqlprofiler = 0;
     280       37717 :         c->blocksize = BLOCK;
     281       37717 :         c->protocol = PROTOCOL_9;
     282             : 
     283       37717 :         c->filetrans = false;
     284       37717 :         c->handshake_options = NULL;
     285       37717 :         c->query = NULL;
     286             : 
     287       37717 :         char name[MT_NAME_LEN];
     288       37717 :         snprintf(name, sizeof(name), "Client%d->s", (int) (c - mal_clients));
     289       37717 :         MT_sema_init(&c->s, 0, name);
     290       37717 :         return c;
     291             : }
     292             : 
     293             : Client
     294       37717 : MCinitClient(oid user, bstream *fin, stream *fout)
     295             : {
     296       37717 :         Client c = NULL;
     297             : 
     298       37717 :         MT_lock_set(&mal_contextLock);
     299       37717 :         c = MCnewClient();
     300       37717 :         if (c) {
     301       37717 :                 c = MCinitClientRecord(c, user, fin, fout);
     302       37717 :                 MT_thread_set_qry_ctx(&c->qryctx);
     303             :         }
     304       37717 :         MT_lock_unset(&mal_contextLock);
     305             : 
     306       37717 :         if (c && profilerStatus > 0)
     307           0 :                 profilerEvent(NULL,
     308             :                                           &(struct NonMalEvent)
     309           0 :                                           { CLIENT_START, c, c->session, NULL, NULL, 0, 0 }
     310             :         );
     311       37717 :         return c;
     312             : }
     313             : 
     314             : 
     315             : /*
     316             :  * The administrator should be initialized to enable interpretation of
     317             :  * the command line arguments, before it starts servicing statements
     318             :  */
     319             : int
     320       37681 : MCinitClientThread(Client c)
     321             : {
     322             :         /*
     323             :          * The GDK thread administration should be set to reflect use of
     324             :          * the proper IO descriptors.
     325             :          */
     326       37681 :         c->mythread = MT_thread_getname();
     327       37681 :         c->errbuf = GDKerrbuf;
     328       37681 :         if (c->errbuf == NULL) {
     329       37681 :                 char *n = GDKzalloc(GDKMAXERRLEN);
     330       37679 :                 if (n == NULL) {
     331           0 :                         MCresetProfiler(c->fdout);
     332           0 :                         return -1;
     333             :                 }
     334       37679 :                 GDKsetbuf(n);
     335       37681 :                 c->errbuf = GDKerrbuf;
     336             :         } else
     337           0 :                 c->errbuf[0] = 0;
     338             :         return 0;
     339             : }
     340             : 
     341             : static bool shutdowninprogress = false;
     342             : 
     343             : bool
     344           0 : MCshutdowninprogress(void)
     345             : {
     346           0 :         MT_lock_set(&mal_contextLock);
     347           0 :         bool ret = shutdowninprogress;
     348           0 :         MT_lock_unset(&mal_contextLock);
     349           0 :         return ret;
     350             : }
     351             : 
     352             : /*
     353             :  * When a client needs to be terminated then the file descriptors for
     354             :  * its input/output are simply closed.  This leads to a graceful
     355             :  * degradation, but may take some time when the client is busy.  A more
     356             :  * forceful method is to kill the client thread, but this may leave
     357             :  * locks and semaphores in an undesirable state.
     358             :  *
     359             :  * The routine freeClient ends a single client session, but through side
     360             :  * effects of sharing IO descriptors, also its children. Conversely, a
     361             :  * child can not close a parent.
     362             :  */
     363             : void
     364       37716 : MCcloseClient(Client c)
     365             : {
     366       37716 :         MT_lock_set(&mal_contextLock);
     367       37716 :         if (c->mode == FREECLIENT) {
     368           0 :                 assert(c->idx == -1);
     369           0 :                 MT_lock_unset(&mal_contextLock);
     370           0 :                 return;
     371             :         }
     372       37716 :         c->mode = FINISHCLIENT;
     373       37716 :         MT_lock_unset(&mal_contextLock);
     374             : 
     375       37716 :         MCexitClient(c);
     376             : 
     377             :         /* scope list and curprg can not be removed, because the client may
     378             :          * reside in a quit() command. Therefore the scopelist is re-used.
     379             :          */
     380       37716 :         c->scenario = NULL;
     381       37716 :         c->prompt = NULL;
     382       37716 :         c->promptlength = -1;
     383       37716 :         if (c->errbuf) {
     384             :                 /* no client threads in embedded mode */
     385       37680 :                 GDKsetbuf(NULL);
     386       37680 :                 if (c->father == NULL)
     387       37680 :                         GDKfree(c->errbuf);
     388       37680 :                 c->errbuf = NULL;
     389             :         }
     390       37716 :         if (c->usermodule)
     391         330 :                 freeModule(c->usermodule);
     392       37716 :         c->usermodule = c->curmodule = 0;
     393       37716 :         c->father = 0;
     394       37716 :         strcpy_len(c->optimizer, "default_pipe", sizeof(c->optimizer));
     395       37716 :         c->workerlimit = 0;
     396       37716 :         c->memorylimit = 0;
     397       37716 :         c->querytimeout = 0;
     398       37716 :         c->qryctx.endtime = 0;
     399       37716 :         c->sessiontimeout = 0;
     400       37716 :         c->logical_sessiontimeout = 0;
     401       37716 :         c->user = oid_nil;
     402       37716 :         if (c->username) {
     403       37363 :                 GDKfree(c->username);
     404       37363 :                 c->username = 0;
     405             :         }
     406       37716 :         if (c->peer) {
     407       37363 :                 GDKfree(c->peer);
     408       37363 :                 c->peer = 0;
     409             :         }
     410       37716 :         if (c->client_hostname) {
     411       36850 :                 GDKfree(c->client_hostname);
     412       36850 :                 c->client_hostname = 0;
     413             :         }
     414       37716 :         if (c->client_application) {
     415       36850 :                 GDKfree(c->client_application);
     416       36850 :                 c->client_application = 0;
     417             :         }
     418       37716 :         if (c->client_library) {
     419       36850 :                 GDKfree(c->client_library);
     420       36850 :                 c->client_library = 0;
     421             :         }
     422       37716 :         if (c->client_remark) {
     423          11 :                 GDKfree(c->client_remark);
     424          11 :                 c->client_remark = 0;
     425             :         }
     426       37716 :         c->client_pid = 0;
     427       37716 :         c->mythread = NULL;
     428       37716 :         if (c->glb) {
     429       37712 :                 freeStack(c->glb);
     430       37712 :                 c->glb = NULL;
     431             :         }
     432       37716 :         if (c->profticks) {
     433          26 :                 BBPunfix(c->profticks->batCacheid);
     434          26 :                 BBPunfix(c->profstmt->batCacheid);
     435          26 :                 BBPunfix(c->profevents->batCacheid);
     436          26 :                 c->profticks = c->profstmt = c->profevents = NULL;
     437             :         }
     438       37716 :         if (c->error_row) {
     439         484 :                 BBPunfix(c->error_row->batCacheid);
     440         484 :                 BBPunfix(c->error_fld->batCacheid);
     441         484 :                 BBPunfix(c->error_msg->batCacheid);
     442         484 :                 BBPunfix(c->error_input->batCacheid);
     443         484 :                 c->error_row = c->error_fld = c->error_msg = c->error_input = NULL;
     444             :         }
     445       37716 :         c->sqlprofiler = 0;
     446       37716 :         free(c->handshake_options);
     447       37716 :         c->handshake_options = NULL;
     448       37716 :         MT_thread_set_qry_ctx(NULL);
     449       37716 :         assert(c->qryctx.datasize == 0);
     450       37716 :         MT_sema_destroy(&c->s);
     451       37716 :         MT_lock_set(&mal_contextLock);
     452       37716 :         c->idle = c->login = c->lastcmd = 0;
     453       37716 :         if (shutdowninprogress) {
     454          32 :                 c->mode = BLOCKCLIENT;
     455             :         } else {
     456       37684 :                 c->mode = FREECLIENT;
     457       37684 :                 c->idx = -1;
     458             :         }
     459       37716 :         MT_lock_unset(&mal_contextLock);
     460             : }
     461             : 
     462             : /*
     463             :  * If a client disappears from the scene (eof on stream), we should
     464             :  * terminate all its children. This is in principle a forceful action,
     465             :  * because the children may be ignoring the primary IO streams.
     466             :  * (Instead they may be blocked in an infinite loop)
     467             :  *
     468             :  * Special care should be taken by closing the 'adm' thread.  It is
     469             :  * permitted to leave only when it is the sole user of the system.
     470             :  *
     471             :  * Furthermore, once we enter closeClient, the process in which it is
     472             :  * raised has already lost its file descriptors.
     473             :  *
     474             :  * When the server is about to shutdown, we should softly terminate
     475             :  * all outstanding session.
     476             :  */
     477             : void
     478         328 : MCstopClients(Client cntxt)
     479             : {
     480         328 :         MT_lock_set(&mal_contextLock);
     481       23244 :         for (int i = 0; i < MAL_MAXCLIENTS; i++) {
     482       22916 :                 Client c = mal_clients + i;
     483       22916 :                 if (cntxt != c) {
     484       22914 :                         if (c->mode == RUNCLIENT)
     485           0 :                                 c->mode = FINISHCLIENT;
     486       22914 :                         else if (c->mode == FREECLIENT) {
     487       22759 :                                 assert(c->idx == -1);
     488       22759 :                                 c->idx = i;
     489       22759 :                                 c->mode = BLOCKCLIENT;
     490             :                         }
     491             :                 }
     492             :         }
     493         328 :         shutdowninprogress = true;
     494         328 :         MT_lock_unset(&mal_contextLock);
     495         328 : }
     496             : 
     497             : int
     498       47834 : MCactiveClients(void)
     499             : {
     500       47834 :         int active = 0;
     501             : 
     502       47834 :         MT_lock_set(&mal_contextLock);
     503     4206846 :         for (Client cntxt = mal_clients; cntxt < mal_clients + MAL_MAXCLIENTS;
     504     4159012 :                  cntxt++) {
     505     8115791 :                 active += (cntxt->idle == 0 && cntxt->mode == RUNCLIENT);
     506             :         }
     507       47834 :         MT_lock_unset(&mal_contextLock);
     508       47834 :         return active;
     509             : }
     510             : 
     511             : str
     512           0 : MCsuspendClient(int id)
     513             : {
     514           0 :         if (id <0 || id >=MAL_MAXCLIENTS)
     515           0 :                 throw(INVCRED, "mal.clients", INVCRED_WRONG_ID);
     516             :         return MAL_SUCCEED;
     517             : }
     518             : 
     519             : str
     520           0 : MCawakeClient(int id)
     521             : {
     522           0 :         if (id <0 || id >=MAL_MAXCLIENTS)
     523           0 :                 throw(INVCRED, "mal.clients", INVCRED_WRONG_ID);
     524             :         return MAL_SUCCEED;
     525             : }
     526             : 
     527             : /*
     528             :  * Input to be processed is collected in a Client specific buffer.  It
     529             :  * is filled by reading information from a stream, a terminal, or by
     530             :  * scheduling strings constructed internally.  The latter involves
     531             :  * removing any escape character needed to manipulate the string within
     532             :  * the kernel.  The buffer space is automatically expanded to
     533             :  * accommodate new information and the read pointers are adjusted.
     534             :  *
     535             :  * The input is read from a (blocked) stream and stored in the client
     536             :  * record input buffer. The storage area grows automatically upon need.
     537             :  * The origin of the input stream depends on the connectivity mode.
     538             :  *
     539             :  * Each operation received from a front-end consists of at least one
     540             :  * line.  To simplify misaligned communication with front-ends, we use
     541             :  * different prompts structures.
     542             :  *
     543             :  * The default action is to read information from an ascii-stream one
     544             :  * line at a time. This is the preferred mode for reading from terminal.
     545             :  *
     546             :  * The next statement block is to be read. Send a prompt to warn the
     547             :  * front-end to issue the request.
     548             :  */
     549             : int
     550       10995 : MCreadClient(Client c)
     551             : {
     552       10995 :         bstream *in = c->fdin;
     553             : 
     554       12397 :         while (in->pos < in->len &&
     555        1402 :                    (isspace((unsigned char) (in->buf[in->pos])) ||
     556           0 :                         in->buf[in->pos] == ';' || !in->buf[in->pos]))
     557        1402 :                 in->pos++;
     558             : 
     559       10995 :         if (in->pos >= in->len || in->mode) {
     560       10995 :                 ssize_t rd;
     561             : 
     562       10995 :                 if (in->eof || !isa_block_stream(c->fdout)) {
     563       10995 :                         if (!isa_block_stream(c->fdout) && c->promptlength > 0)
     564           0 :                                 mnstr_write(c->fdout, c->prompt, c->promptlength, 1);
     565       10995 :                         mnstr_flush(c->fdout, MNSTR_FLUSH_DATA);
     566       10995 :                         in->eof = false;
     567             :                 }
     568       21491 :                 while ((rd = bstream_next(in)) > 0 && !in->eof) {
     569       10496 :                         if (!in->mode)               /* read one line at a time in line mode */
     570             :                                 break;
     571             :                 }
     572       10995 :                 if (rd < 0) {
     573             :                         /* force end of stream handling below */
     574           0 :                         in->pos = in->len;
     575       10995 :                 } else if (in->mode) {                       /* find last new line */
     576       10995 :                         char *p = in->buf + in->len - 1;
     577             : 
     578       10995 :                         while (p > in->buf && *p != '\n') {
     579           0 :                                 *(p + 1) = *p;
     580           0 :                                 p--;
     581             :                         }
     582       10995 :                         if (p > in->buf)
     583       10496 :                                 *(p + 1) = 0;
     584       10995 :                         if (p != in->buf + in->len - 1)
     585           0 :                                 in->len++;
     586             :                 }
     587             :         }
     588       10995 :         if (in->pos >= in->len) {
     589             :                 /* end of stream reached */
     590         499 :                 if (c->bak) {
     591           0 :                         MCpopClientInput(c);
     592           0 :                         if (c->fdin == NULL)
     593             :                                 return 0;
     594             :                         return MCreadClient(c);
     595             :                 }
     596             :                 return 0;
     597             :         }
     598             :         return 1;
     599             : }
     600             : 
     601             : int
     602          51 : MCvalid(Client tc)
     603             : {
     604          51 :         if (tc == NULL) {
     605             :                 return 0;
     606             :         }
     607          51 :         MT_lock_set(&mal_contextLock);
     608         111 :         for (Client c = mal_clients; c < mal_clients + MAL_MAXCLIENTS; c++) {
     609         111 :                 if (c == tc && c->mode == RUNCLIENT) {
     610          51 :                         MT_lock_unset(&mal_contextLock);
     611          51 :                         return 1;
     612             :                 }
     613             :         }
     614           0 :         MT_lock_unset(&mal_contextLock);
     615           0 :         return 0;
     616             : }
     617             : 
     618             : void
     619      183044 : MCsetClientInfo(Client c, const char *property, const char *value)
     620             : {
     621      183044 :         if (strlen(property) < 7)
     622             :                 return;
     623             : 
     624             :         // 012345 6 78...
     625             :         // Client H ostname
     626             :         // Applic a tionName
     627             :         // Client L ibrary
     628             :         // Client R emark
     629             :         // Client P id
     630      183044 :         int discriminant = toupper(property[6]);
     631             : 
     632      183044 :         switch (discriminant) {
     633       36851 :                 case 'H':
     634       36851 :                         if (strcasecmp(property, "ClientHostname") == 0) {
     635       36851 :                                 GDKfree(c->client_hostname);
     636       73702 :                                 c->client_hostname = value ? GDKstrdup(value) : NULL;
     637             :                         }
     638             :                         break;
     639       36851 :                 case 'A':
     640       36851 :                         if (strcasecmp(property, "ApplicationName") == 0) {
     641       36851 :                                 GDKfree(c->client_application);
     642       73701 :                                 c->client_application = value ? GDKstrdup(value) : NULL;
     643             :                         }
     644             :                         break;
     645       36851 :                 case 'L':
     646       36851 :                         if (strcasecmp(property, "ClientLibrary") == 0) {
     647       36851 :                                 GDKfree(c->client_library);
     648       73702 :                                 c->client_library = value ? GDKstrdup(value) : NULL;
     649             :                         }
     650             :                         break;
     651       35640 :                 case 'R':
     652       35640 :                         if (strcasecmp(property, "ClientRemark") == 0) {
     653       35641 :                                 GDKfree(c->client_remark);
     654       35651 :                                 c->client_remark = value ? GDKstrdup(value) : NULL;
     655             :                         }
     656             :                         break;
     657       36851 :                 case 'P':
     658       36851 :                         if (strcasecmp(property, "ClientPid") == 0 && value != NULL) {
     659       36851 :                                 char *end;
     660       36851 :                                 long n = strtol(value, &end, 10);
     661       36851 :                                 if (*value && !*end)
     662       36851 :                                         c->client_pid = n;
     663             :                         }
     664             :                         break;
     665             :                 default:
     666             :                         break;
     667             :         }
     668             : }

Generated by: LCOV version 1.14