LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-cancel.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 115 215 53.5 %
Date: 2024-04-27 14:11:08 Functions: 12 14 85.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * fe-cancel.c
       4             :  *    functions related to setting up a connection to the backend
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/interfaces/libpq/fe-cancel.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres_fe.h"
      17             : 
      18             : #include <unistd.h>
      19             : 
      20             : #include "libpq-fe.h"
      21             : #include "libpq-int.h"
      22             : #include "port/pg_bswap.h"
      23             : 
      24             : 
      25             : /*
      26             :  * pg_cancel_conn (backing struct for PGcancelConn) is a wrapper around a
      27             :  * PGconn to send cancellations using PQcancelBlocking and PQcancelStart.
      28             :  * This isn't just a typedef because we want the compiler to complain when a
      29             :  * PGconn is passed to a function that expects a PGcancelConn, and vice versa.
      30             :  */
      31             : struct pg_cancel_conn
      32             : {
      33             :     PGconn      conn;
      34             : };
      35             : 
      36             : /*
      37             :  * pg_cancel (backing struct for PGcancel) stores all data necessary to send a
      38             :  * cancel request.
      39             :  */
      40             : struct pg_cancel
      41             : {
      42             :     SockAddr    raddr;          /* Remote address */
      43             :     int         be_pid;         /* PID of to-be-canceled backend */
      44             :     int         be_key;         /* cancel key of to-be-canceled backend */
      45             :     int         pgtcp_user_timeout; /* tcp user timeout */
      46             :     int         keepalives;     /* use TCP keepalives? */
      47             :     int         keepalives_idle;    /* time between TCP keepalives */
      48             :     int         keepalives_interval;    /* time between TCP keepalive
      49             :                                          * retransmits */
      50             :     int         keepalives_count;   /* maximum number of TCP keepalive
      51             :                                      * retransmits */
      52             : };
      53             : 
      54             : 
      55             : /*
      56             :  *      PQcancelCreate
      57             :  *
      58             :  * Create and return a PGcancelConn, which can be used to securely cancel a
      59             :  * query on the given connection.
      60             :  *
      61             :  * This requires either following the non-blocking flow through
      62             :  * PQcancelStart() and PQcancelPoll(), or the blocking PQcancelBlocking().
      63             :  */
      64             : PGcancelConn *
      65           8 : PQcancelCreate(PGconn *conn)
      66             : {
      67           8 :     PGconn     *cancelConn = pqMakeEmptyPGconn();
      68             :     pg_conn_host originalHost;
      69             : 
      70           8 :     if (cancelConn == NULL)
      71           0 :         return NULL;
      72             : 
      73             :     /* Check we have an open connection */
      74           8 :     if (!conn)
      75             :     {
      76           0 :         libpq_append_conn_error(cancelConn, "passed connection was NULL");
      77           0 :         return (PGcancelConn *) cancelConn;
      78             :     }
      79             : 
      80           8 :     if (conn->sock == PGINVALID_SOCKET)
      81             :     {
      82           0 :         libpq_append_conn_error(cancelConn, "passed connection is not open");
      83           0 :         return (PGcancelConn *) cancelConn;
      84             :     }
      85             : 
      86             :     /*
      87             :      * Indicate that this connection is used to send a cancellation
      88             :      */
      89           8 :     cancelConn->cancelRequest = true;
      90             : 
      91           8 :     if (!pqCopyPGconn(conn, cancelConn))
      92           0 :         return (PGcancelConn *) cancelConn;
      93             : 
      94             :     /*
      95             :      * Compute derived options
      96             :      */
      97           8 :     if (!pqConnectOptions2(cancelConn))
      98           0 :         return (PGcancelConn *) cancelConn;
      99             : 
     100             :     /*
     101             :      * Copy cancellation token data from the original connection
     102             :      */
     103           8 :     cancelConn->be_pid = conn->be_pid;
     104           8 :     cancelConn->be_key = conn->be_key;
     105             : 
     106             :     /*
     107             :      * Cancel requests should not iterate over all possible hosts. The request
     108             :      * needs to be sent to the exact host and address that the original
     109             :      * connection used. So we manually create the host and address arrays with
     110             :      * a single element after freeing the host array that we generated from
     111             :      * the connection options.
     112             :      */
     113           8 :     pqReleaseConnHosts(cancelConn);
     114           8 :     cancelConn->nconnhost = 1;
     115           8 :     cancelConn->naddr = 1;
     116             : 
     117           8 :     cancelConn->connhost = calloc(cancelConn->nconnhost, sizeof(pg_conn_host));
     118           8 :     if (!cancelConn->connhost)
     119           0 :         goto oom_error;
     120             : 
     121           8 :     originalHost = conn->connhost[conn->whichhost];
     122           8 :     if (originalHost.host)
     123             :     {
     124           8 :         cancelConn->connhost[0].host = strdup(originalHost.host);
     125           8 :         if (!cancelConn->connhost[0].host)
     126           0 :             goto oom_error;
     127             :     }
     128           8 :     if (originalHost.hostaddr)
     129             :     {
     130           0 :         cancelConn->connhost[0].hostaddr = strdup(originalHost.hostaddr);
     131           0 :         if (!cancelConn->connhost[0].hostaddr)
     132           0 :             goto oom_error;
     133             :     }
     134           8 :     if (originalHost.port)
     135             :     {
     136           8 :         cancelConn->connhost[0].port = strdup(originalHost.port);
     137           8 :         if (!cancelConn->connhost[0].port)
     138           0 :             goto oom_error;
     139             :     }
     140           8 :     if (originalHost.password)
     141             :     {
     142           0 :         cancelConn->connhost[0].password = strdup(originalHost.password);
     143           0 :         if (!cancelConn->connhost[0].password)
     144           0 :             goto oom_error;
     145             :     }
     146             : 
     147           8 :     cancelConn->addr = calloc(cancelConn->naddr, sizeof(AddrInfo));
     148           8 :     if (!cancelConn->connhost)
     149           0 :         goto oom_error;
     150             : 
     151           8 :     cancelConn->addr[0].addr = conn->raddr;
     152           8 :     cancelConn->addr[0].family = conn->raddr.addr.ss_family;
     153             : 
     154           8 :     cancelConn->status = CONNECTION_ALLOCATED;
     155           8 :     return (PGcancelConn *) cancelConn;
     156             : 
     157           0 : oom_error:
     158           0 :     conn->status = CONNECTION_BAD;
     159           0 :     libpq_append_conn_error(cancelConn, "out of memory");
     160           0 :     return (PGcancelConn *) cancelConn;
     161             : }
     162             : 
     163             : 
     164             : /*
     165             :  *      PQcancelBlocking
     166             :  *
     167             :  * Send a cancellation request in a blocking fashion.
     168             :  * Returns 1 if successful 0 if not.
     169             :  */
     170             : int
     171           2 : PQcancelBlocking(PGcancelConn *cancelConn)
     172             : {
     173           2 :     if (!PQcancelStart(cancelConn))
     174           0 :         return 0;
     175           2 :     return pqConnectDBComplete(&cancelConn->conn);
     176             : }
     177             : 
     178             : /*
     179             :  *      PQcancelStart
     180             :  *
     181             :  * Starts sending a cancellation request in a non-blocking fashion. Returns
     182             :  * 1 if successful 0 if not.
     183             :  */
     184             : int
     185          10 : PQcancelStart(PGcancelConn *cancelConn)
     186             : {
     187          10 :     if (!cancelConn || cancelConn->conn.status == CONNECTION_BAD)
     188           0 :         return 0;
     189             : 
     190          10 :     if (cancelConn->conn.status != CONNECTION_ALLOCATED)
     191             :     {
     192           0 :         libpq_append_conn_error(&cancelConn->conn,
     193             :                                 "cancel request is already being sent on this connection");
     194           0 :         cancelConn->conn.status = CONNECTION_BAD;
     195           0 :         return 0;
     196             :     }
     197             : 
     198          10 :     return pqConnectDBStart(&cancelConn->conn);
     199             : }
     200             : 
     201             : /*
     202             :  *      PQcancelPoll
     203             :  *
     204             :  * Poll a cancel connection. For usage details see PQconnectPoll.
     205             :  */
     206             : PostgresPollingStatusType
     207          20 : PQcancelPoll(PGcancelConn *cancelConn)
     208             : {
     209          20 :     PGconn     *conn = &cancelConn->conn;
     210             :     int         n;
     211             : 
     212             :     /*
     213             :      * We leave most of the connection establishment to PQconnectPoll, since
     214             :      * it's very similar to normal connection establishment. But once we get
     215             :      * to the CONNECTION_AWAITING_RESPONSE we need to start doing our own
     216             :      * thing.
     217             :      */
     218          20 :     if (conn->status != CONNECTION_AWAITING_RESPONSE)
     219             :     {
     220          10 :         return PQconnectPoll(conn);
     221             :     }
     222             : 
     223             :     /*
     224             :      * At this point we are waiting on the server to close the connection,
     225             :      * which is its way of communicating that the cancel has been handled.
     226             :      */
     227             : 
     228          10 :     n = pqReadData(conn);
     229             : 
     230          10 :     if (n == 0)
     231           0 :         return PGRES_POLLING_READING;
     232             : 
     233             : #ifndef WIN32
     234             : 
     235             :     /*
     236             :      * If we receive an error report it, but only if errno is non-zero.
     237             :      * Otherwise we assume it's an EOF, which is what we expect from the
     238             :      * server.
     239             :      *
     240             :      * We skip this for Windows, because Windows is a bit special in its EOF
     241             :      * behaviour for TCP. Sometimes it will error with an ECONNRESET when
     242             :      * there is a clean connection closure. See these threads for details:
     243             :      * https://www.postgresql.org/message-id/flat/90b34057-4176-7bb0-0dbb-9822a5f6425b%40greiz-reinsdorf.de
     244             :      *
     245             :      * https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BOeoETZQ%3DQw5Ub5h3tmwQhBmDA%3DnuNO3KG%3DzWfUypFAw%40mail.gmail.com
     246             :      *
     247             :      * PQcancel ignores such errors and reports success for the cancellation
     248             :      * anyway, so even if this is not always correct we do the same here.
     249             :      */
     250          10 :     if (n < 0 && errno != 0)
     251             :     {
     252           0 :         conn->status = CONNECTION_BAD;
     253           0 :         return PGRES_POLLING_FAILED;
     254             :     }
     255             : #endif
     256             : 
     257             :     /*
     258             :      * We don't expect any data, only connection closure. So if we strangely
     259             :      * do receive some data we consider that an error.
     260             :      */
     261          10 :     if (n > 0)
     262             :     {
     263           0 :         libpq_append_conn_error(conn, "received unexpected response from server");
     264           0 :         conn->status = CONNECTION_BAD;
     265           0 :         return PGRES_POLLING_FAILED;
     266             :     }
     267             : 
     268             :     /*
     269             :      * Getting here means that we received an EOF, which is what we were
     270             :      * expecting -- the cancel request has completed.
     271             :      */
     272          10 :     cancelConn->conn.status = CONNECTION_OK;
     273          10 :     resetPQExpBuffer(&conn->errorMessage);
     274          10 :     return PGRES_POLLING_OK;
     275             : }
     276             : 
     277             : /*
     278             :  *      PQcancelStatus
     279             :  *
     280             :  * Get the status of a cancel connection.
     281             :  */
     282             : ConnStatusType
     283           4 : PQcancelStatus(const PGcancelConn *cancelConn)
     284             : {
     285           4 :     return PQstatus(&cancelConn->conn);
     286             : }
     287             : 
     288             : /*
     289             :  *      PQcancelSocket
     290             :  *
     291             :  * Get the socket of the cancel connection.
     292             :  */
     293             : int
     294          12 : PQcancelSocket(const PGcancelConn *cancelConn)
     295             : {
     296          12 :     return PQsocket(&cancelConn->conn);
     297             : }
     298             : 
     299             : /*
     300             :  *      PQcancelErrorMessage
     301             :  *
     302             :  * Get the socket of the cancel connection.
     303             :  */
     304             : char *
     305           0 : PQcancelErrorMessage(const PGcancelConn *cancelConn)
     306             : {
     307           0 :     return PQerrorMessage(&cancelConn->conn);
     308             : }
     309             : 
     310             : /*
     311             :  *      PQcancelReset
     312             :  *
     313             :  * Resets the cancel connection, so it can be reused to send a new cancel
     314             :  * request.
     315             :  */
     316             : void
     317           2 : PQcancelReset(PGcancelConn *cancelConn)
     318             : {
     319           2 :     pqClosePGconn(&cancelConn->conn);
     320           2 :     cancelConn->conn.status = CONNECTION_ALLOCATED;
     321           2 :     cancelConn->conn.whichhost = 0;
     322           2 :     cancelConn->conn.whichaddr = 0;
     323           2 :     cancelConn->conn.try_next_host = false;
     324           2 :     cancelConn->conn.try_next_addr = false;
     325           2 : }
     326             : 
     327             : /*
     328             :  *      PQcancelFinish
     329             :  *
     330             :  * Closes and frees the cancel connection.
     331             :  */
     332             : void
     333           8 : PQcancelFinish(PGcancelConn *cancelConn)
     334             : {
     335           8 :     PQfinish(&cancelConn->conn);
     336           8 : }
     337             : 
     338             : /*
     339             :  * PQgetCancel: get a PGcancel structure corresponding to a connection.
     340             :  *
     341             :  * A copy is needed to be able to cancel a running query from a different
     342             :  * thread. If the same structure is used all structure members would have
     343             :  * to be individually locked (if the entire structure was locked, it would
     344             :  * be impossible to cancel a synchronous query because the structure would
     345             :  * have to stay locked for the duration of the query).
     346             :  */
     347             : PGcancel *
     348      376960 : PQgetCancel(PGconn *conn)
     349             : {
     350             :     PGcancel   *cancel;
     351             : 
     352      376960 :     if (!conn)
     353          44 :         return NULL;
     354             : 
     355      376916 :     if (conn->sock == PGINVALID_SOCKET)
     356           0 :         return NULL;
     357             : 
     358      376916 :     cancel = malloc(sizeof(PGcancel));
     359      376916 :     if (cancel == NULL)
     360           0 :         return NULL;
     361             : 
     362      376916 :     memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
     363      376916 :     cancel->be_pid = conn->be_pid;
     364      376916 :     cancel->be_key = conn->be_key;
     365             :     /* We use -1 to indicate an unset connection option */
     366      376916 :     cancel->pgtcp_user_timeout = -1;
     367      376916 :     cancel->keepalives = -1;
     368      376916 :     cancel->keepalives_idle = -1;
     369      376916 :     cancel->keepalives_interval = -1;
     370      376916 :     cancel->keepalives_count = -1;
     371      376916 :     if (conn->pgtcp_user_timeout != NULL)
     372             :     {
     373           0 :         if (!pqParseIntParam(conn->pgtcp_user_timeout,
     374             :                              &cancel->pgtcp_user_timeout,
     375             :                              conn, "tcp_user_timeout"))
     376           0 :             goto fail;
     377             :     }
     378      376916 :     if (conn->keepalives != NULL)
     379             :     {
     380           0 :         if (!pqParseIntParam(conn->keepalives,
     381             :                              &cancel->keepalives,
     382             :                              conn, "keepalives"))
     383           0 :             goto fail;
     384             :     }
     385      376916 :     if (conn->keepalives_idle != NULL)
     386             :     {
     387           0 :         if (!pqParseIntParam(conn->keepalives_idle,
     388             :                              &cancel->keepalives_idle,
     389             :                              conn, "keepalives_idle"))
     390           0 :             goto fail;
     391             :     }
     392      376916 :     if (conn->keepalives_interval != NULL)
     393             :     {
     394           0 :         if (!pqParseIntParam(conn->keepalives_interval,
     395             :                              &cancel->keepalives_interval,
     396             :                              conn, "keepalives_interval"))
     397           0 :             goto fail;
     398             :     }
     399      376916 :     if (conn->keepalives_count != NULL)
     400             :     {
     401           0 :         if (!pqParseIntParam(conn->keepalives_count,
     402             :                              &cancel->keepalives_count,
     403             :                              conn, "keepalives_count"))
     404           0 :             goto fail;
     405             :     }
     406             : 
     407      376916 :     return cancel;
     408             : 
     409           0 : fail:
     410           0 :     free(cancel);
     411           0 :     return NULL;
     412             : }
     413             : 
     414             : /* PQfreeCancel: free a cancel structure */
     415             : void
     416      376894 : PQfreeCancel(PGcancel *cancel)
     417             : {
     418      376894 :     free(cancel);
     419      376894 : }
     420             : 
     421             : 
     422             : /*
     423             :  * Sets an integer socket option on a TCP socket, if the provided value is
     424             :  * not negative.  Returns false if setsockopt fails for some reason.
     425             :  *
     426             :  * CAUTION: This needs to be signal safe, since it's used by PQcancel.
     427             :  */
     428             : #if defined(TCP_USER_TIMEOUT) || !defined(WIN32)
     429             : static bool
     430           0 : optional_setsockopt(int fd, int protoid, int optid, int value)
     431             : {
     432           0 :     if (value < 0)
     433           0 :         return true;
     434           0 :     if (setsockopt(fd, protoid, optid, (char *) &value, sizeof(value)) < 0)
     435           0 :         return false;
     436           0 :     return true;
     437             : }
     438             : #endif
     439             : 
     440             : 
     441             : /*
     442             :  * PQcancel: old, non-encrypted, but signal-safe way of requesting query cancel
     443             :  *
     444             :  * The return value is true if the cancel request was successfully
     445             :  * dispatched, false if not (in which case an error message is available).
     446             :  * Note: successful dispatch is no guarantee that there will be any effect at
     447             :  * the backend.  The application must read the operation result as usual.
     448             :  *
     449             :  * On failure, an error message is stored in *errbuf, which must be of size
     450             :  * errbufsize (recommended size is 256 bytes).  *errbuf is not changed on
     451             :  * success return.
     452             :  *
     453             :  * CAUTION: we want this routine to be safely callable from a signal handler
     454             :  * (for example, an application might want to call it in a SIGINT handler).
     455             :  * This means we cannot use any C library routine that might be non-reentrant.
     456             :  * malloc/free are often non-reentrant, and anything that might call them is
     457             :  * just as dangerous.  We avoid sprintf here for that reason.  Building up
     458             :  * error messages with strcpy/strcat is tedious but should be quite safe.
     459             :  * We also save/restore errno in case the signal handler support doesn't.
     460             :  */
     461             : int
     462           8 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
     463             : {
     464           8 :     int         save_errno = SOCK_ERRNO;
     465           8 :     pgsocket    tmpsock = PGINVALID_SOCKET;
     466             :     int         maxlen;
     467             :     struct
     468             :     {
     469             :         uint32      packetlen;
     470             :         CancelRequestPacket cp;
     471             :     }           crp;
     472             : 
     473           8 :     if (!cancel)
     474             :     {
     475           0 :         strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
     476             :         /* strlcpy probably doesn't change errno, but be paranoid */
     477           0 :         SOCK_ERRNO_SET(save_errno);
     478           0 :         return false;
     479             :     }
     480             : 
     481             :     /*
     482             :      * We need to open a temporary connection to the postmaster. Do this with
     483             :      * only kernel calls.
     484             :      */
     485           8 :     if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
     486             :     {
     487           0 :         strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
     488           0 :         goto cancel_errReturn;
     489             :     }
     490             : 
     491             :     /*
     492             :      * Since this connection will only be used to send a single packet of
     493             :      * data, we don't need NODELAY.  We also don't set the socket to
     494             :      * nonblocking mode, because the API definition of PQcancel requires the
     495             :      * cancel to be sent in a blocking way.
     496             :      *
     497             :      * We do set socket options related to keepalives and other TCP timeouts.
     498             :      * This ensures that this function does not block indefinitely when
     499             :      * reasonable keepalive and timeout settings have been provided.
     500             :      */
     501           8 :     if (cancel->raddr.addr.ss_family != AF_UNIX &&
     502           0 :         cancel->keepalives != 0)
     503             :     {
     504             : #ifndef WIN32
     505           0 :         if (!optional_setsockopt(tmpsock, SOL_SOCKET, SO_KEEPALIVE, 1))
     506             :         {
     507           0 :             strlcpy(errbuf, "PQcancel() -- setsockopt(SO_KEEPALIVE) failed: ", errbufsize);
     508           0 :             goto cancel_errReturn;
     509             :         }
     510             : 
     511             : #ifdef PG_TCP_KEEPALIVE_IDLE
     512             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
     513             :                                  cancel->keepalives_idle))
     514             :         {
     515             :             strlcpy(errbuf, "PQcancel() -- setsockopt(" PG_TCP_KEEPALIVE_IDLE_STR ") failed: ", errbufsize);
     516             :             goto cancel_errReturn;
     517             :         }
     518             : #endif
     519             : 
     520             : #ifdef TCP_KEEPINTVL
     521             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPINTVL,
     522             :                                  cancel->keepalives_interval))
     523             :         {
     524             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPINTVL) failed: ", errbufsize);
     525             :             goto cancel_errReturn;
     526             :         }
     527             : #endif
     528             : 
     529             : #ifdef TCP_KEEPCNT
     530             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPCNT,
     531             :                                  cancel->keepalives_count))
     532             :         {
     533             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPCNT) failed: ", errbufsize);
     534             :             goto cancel_errReturn;
     535             :         }
     536             : #endif
     537             : 
     538             : #else                           /* WIN32 */
     539             : 
     540             : #ifdef SIO_KEEPALIVE_VALS
     541             :         if (!pqSetKeepalivesWin32(tmpsock,
     542             :                                   cancel->keepalives_idle,
     543             :                                   cancel->keepalives_interval))
     544             :         {
     545             :             strlcpy(errbuf, "PQcancel() -- WSAIoctl(SIO_KEEPALIVE_VALS) failed: ", errbufsize);
     546             :             goto cancel_errReturn;
     547             :         }
     548             : #endif                          /* SIO_KEEPALIVE_VALS */
     549             : #endif                          /* WIN32 */
     550             : 
     551             :         /* TCP_USER_TIMEOUT works the same way on Unix and Windows */
     552             : #ifdef TCP_USER_TIMEOUT
     553             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_USER_TIMEOUT,
     554             :                                  cancel->pgtcp_user_timeout))
     555             :         {
     556             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_USER_TIMEOUT) failed: ", errbufsize);
     557             :             goto cancel_errReturn;
     558             :         }
     559             : #endif
     560             :     }
     561             : 
     562           8 : retry3:
     563           8 :     if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
     564             :                 cancel->raddr.salen) < 0)
     565             :     {
     566           0 :         if (SOCK_ERRNO == EINTR)
     567             :             /* Interrupted system call - we'll just try again */
     568           0 :             goto retry3;
     569           0 :         strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
     570           0 :         goto cancel_errReturn;
     571             :     }
     572             : 
     573             :     /* Create and send the cancel request packet. */
     574             : 
     575           8 :     crp.packetlen = pg_hton32((uint32) sizeof(crp));
     576           8 :     crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
     577           8 :     crp.cp.backendPID = pg_hton32(cancel->be_pid);
     578           8 :     crp.cp.cancelAuthCode = pg_hton32(cancel->be_key);
     579             : 
     580           8 : retry4:
     581           8 :     if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
     582             :     {
     583           0 :         if (SOCK_ERRNO == EINTR)
     584             :             /* Interrupted system call - we'll just try again */
     585           0 :             goto retry4;
     586           0 :         strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
     587           0 :         goto cancel_errReturn;
     588             :     }
     589             : 
     590             :     /*
     591             :      * Wait for the postmaster to close the connection, which indicates that
     592             :      * it's processed the request.  Without this delay, we might issue another
     593             :      * command only to find that our cancel zaps that command instead of the
     594             :      * one we thought we were canceling.  Note we don't actually expect this
     595             :      * read to obtain any data, we are just waiting for EOF to be signaled.
     596             :      */
     597           8 : retry5:
     598           8 :     if (recv(tmpsock, (char *) &crp, 1, 0) < 0)
     599             :     {
     600           0 :         if (SOCK_ERRNO == EINTR)
     601             :             /* Interrupted system call - we'll just try again */
     602           0 :             goto retry5;
     603             :         /* we ignore other error conditions */
     604             :     }
     605             : 
     606             :     /* All done */
     607           8 :     closesocket(tmpsock);
     608           8 :     SOCK_ERRNO_SET(save_errno);
     609           8 :     return true;
     610             : 
     611           0 : cancel_errReturn:
     612             : 
     613             :     /*
     614             :      * Make sure we don't overflow the error buffer. Leave space for the \n at
     615             :      * the end, and for the terminating zero.
     616             :      */
     617           0 :     maxlen = errbufsize - strlen(errbuf) - 2;
     618           0 :     if (maxlen >= 0)
     619             :     {
     620             :         /*
     621             :          * We can't invoke strerror here, since it's not signal-safe.  Settle
     622             :          * for printing the decimal value of errno.  Even that has to be done
     623             :          * the hard way.
     624             :          */
     625           0 :         int         val = SOCK_ERRNO;
     626             :         char        buf[32];
     627             :         char       *bufp;
     628             : 
     629           0 :         bufp = buf + sizeof(buf) - 1;
     630           0 :         *bufp = '\0';
     631             :         do
     632             :         {
     633           0 :             *(--bufp) = (val % 10) + '0';
     634           0 :             val /= 10;
     635           0 :         } while (val > 0);
     636           0 :         bufp -= 6;
     637           0 :         memcpy(bufp, "error ", 6);
     638           0 :         strncat(errbuf, bufp, maxlen);
     639           0 :         strcat(errbuf, "\n");
     640             :     }
     641           0 :     if (tmpsock != PGINVALID_SOCKET)
     642           0 :         closesocket(tmpsock);
     643           0 :     SOCK_ERRNO_SET(save_errno);
     644           0 :     return false;
     645             : }
     646             : 
     647             : /*
     648             :  * PQrequestCancel: old, not thread-safe function for requesting query cancel
     649             :  *
     650             :  * Returns true if able to send the cancel request, false if not.
     651             :  *
     652             :  * On failure, the error message is saved in conn->errorMessage; this means
     653             :  * that this can't be used when there might be other active operations on
     654             :  * the connection object.
     655             :  *
     656             :  * NOTE: error messages will be cut off at the current size of the
     657             :  * error message buffer, since we dare not try to expand conn->errorMessage!
     658             :  */
     659             : int
     660           2 : PQrequestCancel(PGconn *conn)
     661             : {
     662             :     int         r;
     663             :     PGcancel   *cancel;
     664             : 
     665             :     /* Check we have an open connection */
     666           2 :     if (!conn)
     667           0 :         return false;
     668             : 
     669           2 :     if (conn->sock == PGINVALID_SOCKET)
     670             :     {
     671           0 :         strlcpy(conn->errorMessage.data,
     672             :                 "PQrequestCancel() -- connection is not open\n",
     673             :                 conn->errorMessage.maxlen);
     674           0 :         conn->errorMessage.len = strlen(conn->errorMessage.data);
     675           0 :         conn->errorReported = 0;
     676             : 
     677           0 :         return false;
     678             :     }
     679             : 
     680           2 :     cancel = PQgetCancel(conn);
     681           2 :     if (cancel)
     682             :     {
     683           2 :         r = PQcancel(cancel, conn->errorMessage.data,
     684           2 :                      conn->errorMessage.maxlen);
     685           2 :         PQfreeCancel(cancel);
     686             :     }
     687             :     else
     688             :     {
     689           0 :         strlcpy(conn->errorMessage.data, "out of memory",
     690             :                 conn->errorMessage.maxlen);
     691           0 :         r = false;
     692             :     }
     693             : 
     694           2 :     if (!r)
     695             :     {
     696           0 :         conn->errorMessage.len = strlen(conn->errorMessage.data);
     697           0 :         conn->errorReported = 0;
     698             :     }
     699             : 
     700           2 :     return r;
     701             : }

Generated by: LCOV version 1.14