LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_client.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 214 316 67.7 %
Date: 2021-09-14 22:17:06 Functions: 16 22 72.7 %

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

Generated by: LCOV version 1.14