LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-cancel.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 115 215 53.5 %
Date: 2024-11-21 08:14:44 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, "connection pointer is NULL");
      77           0 :         return (PGcancelConn *) cancelConn;
      78             :     }
      79             : 
      80           8 :     if (conn->sock == PGINVALID_SOCKET)
      81             :     {
      82           0 :         libpq_append_conn_error(cancelConn, "connection 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->addr)
     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 :     cancelConn->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, "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             :  * Returns the error message most recently generated by an operation on the
     303             :  * cancel connection.
     304             :  */
     305             : char *
     306           0 : PQcancelErrorMessage(const PGcancelConn *cancelConn)
     307             : {
     308           0 :     return PQerrorMessage(&cancelConn->conn);
     309             : }
     310             : 
     311             : /*
     312             :  *      PQcancelReset
     313             :  *
     314             :  * Resets the cancel connection, so it can be reused to send a new cancel
     315             :  * request.
     316             :  */
     317             : void
     318           2 : PQcancelReset(PGcancelConn *cancelConn)
     319             : {
     320           2 :     pqClosePGconn(&cancelConn->conn);
     321           2 :     cancelConn->conn.status = CONNECTION_ALLOCATED;
     322           2 :     cancelConn->conn.whichhost = 0;
     323           2 :     cancelConn->conn.whichaddr = 0;
     324           2 :     cancelConn->conn.try_next_host = false;
     325           2 :     cancelConn->conn.try_next_addr = false;
     326           2 : }
     327             : 
     328             : /*
     329             :  *      PQcancelFinish
     330             :  *
     331             :  * Closes and frees the cancel connection.
     332             :  */
     333             : void
     334           8 : PQcancelFinish(PGcancelConn *cancelConn)
     335             : {
     336           8 :     PQfinish(&cancelConn->conn);
     337           8 : }
     338             : 
     339             : /*
     340             :  * PQgetCancel: get a PGcancel structure corresponding to a connection.
     341             :  *
     342             :  * A copy is needed to be able to cancel a running query from a different
     343             :  * thread. If the same structure is used all structure members would have
     344             :  * to be individually locked (if the entire structure was locked, it would
     345             :  * be impossible to cancel a synchronous query because the structure would
     346             :  * have to stay locked for the duration of the query).
     347             :  */
     348             : PGcancel *
     349      387042 : PQgetCancel(PGconn *conn)
     350             : {
     351             :     PGcancel   *cancel;
     352             : 
     353      387042 :     if (!conn)
     354          44 :         return NULL;
     355             : 
     356      386998 :     if (conn->sock == PGINVALID_SOCKET)
     357           0 :         return NULL;
     358             : 
     359      386998 :     cancel = malloc(sizeof(PGcancel));
     360      386998 :     if (cancel == NULL)
     361           0 :         return NULL;
     362             : 
     363      386998 :     memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
     364      386998 :     cancel->be_pid = conn->be_pid;
     365      386998 :     cancel->be_key = conn->be_key;
     366             :     /* We use -1 to indicate an unset connection option */
     367      386998 :     cancel->pgtcp_user_timeout = -1;
     368      386998 :     cancel->keepalives = -1;
     369      386998 :     cancel->keepalives_idle = -1;
     370      386998 :     cancel->keepalives_interval = -1;
     371      386998 :     cancel->keepalives_count = -1;
     372      386998 :     if (conn->pgtcp_user_timeout != NULL)
     373             :     {
     374           0 :         if (!pqParseIntParam(conn->pgtcp_user_timeout,
     375             :                              &cancel->pgtcp_user_timeout,
     376             :                              conn, "tcp_user_timeout"))
     377           0 :             goto fail;
     378             :     }
     379      386998 :     if (conn->keepalives != NULL)
     380             :     {
     381           0 :         if (!pqParseIntParam(conn->keepalives,
     382             :                              &cancel->keepalives,
     383             :                              conn, "keepalives"))
     384           0 :             goto fail;
     385             :     }
     386      386998 :     if (conn->keepalives_idle != NULL)
     387             :     {
     388           0 :         if (!pqParseIntParam(conn->keepalives_idle,
     389             :                              &cancel->keepalives_idle,
     390             :                              conn, "keepalives_idle"))
     391           0 :             goto fail;
     392             :     }
     393      386998 :     if (conn->keepalives_interval != NULL)
     394             :     {
     395           0 :         if (!pqParseIntParam(conn->keepalives_interval,
     396             :                              &cancel->keepalives_interval,
     397             :                              conn, "keepalives_interval"))
     398           0 :             goto fail;
     399             :     }
     400      386998 :     if (conn->keepalives_count != NULL)
     401             :     {
     402           0 :         if (!pqParseIntParam(conn->keepalives_count,
     403             :                              &cancel->keepalives_count,
     404             :                              conn, "keepalives_count"))
     405           0 :             goto fail;
     406             :     }
     407             : 
     408      386998 :     return cancel;
     409             : 
     410           0 : fail:
     411           0 :     free(cancel);
     412           0 :     return NULL;
     413             : }
     414             : 
     415             : /* PQfreeCancel: free a cancel structure */
     416             : void
     417      386972 : PQfreeCancel(PGcancel *cancel)
     418             : {
     419      386972 :     free(cancel);
     420      386972 : }
     421             : 
     422             : 
     423             : /*
     424             :  * Sets an integer socket option on a TCP socket, if the provided value is
     425             :  * not negative.  Returns false if setsockopt fails for some reason.
     426             :  *
     427             :  * CAUTION: This needs to be signal safe, since it's used by PQcancel.
     428             :  */
     429             : #if defined(TCP_USER_TIMEOUT) || !defined(WIN32)
     430             : static bool
     431           0 : optional_setsockopt(int fd, int protoid, int optid, int value)
     432             : {
     433           0 :     if (value < 0)
     434           0 :         return true;
     435           0 :     if (setsockopt(fd, protoid, optid, (char *) &value, sizeof(value)) < 0)
     436           0 :         return false;
     437           0 :     return true;
     438             : }
     439             : #endif
     440             : 
     441             : 
     442             : /*
     443             :  * PQcancel: old, non-encrypted, but signal-safe way of requesting query cancel
     444             :  *
     445             :  * The return value is true if the cancel request was successfully
     446             :  * dispatched, false if not (in which case an error message is available).
     447             :  * Note: successful dispatch is no guarantee that there will be any effect at
     448             :  * the backend.  The application must read the operation result as usual.
     449             :  *
     450             :  * On failure, an error message is stored in *errbuf, which must be of size
     451             :  * errbufsize (recommended size is 256 bytes).  *errbuf is not changed on
     452             :  * success return.
     453             :  *
     454             :  * CAUTION: we want this routine to be safely callable from a signal handler
     455             :  * (for example, an application might want to call it in a SIGINT handler).
     456             :  * This means we cannot use any C library routine that might be non-reentrant.
     457             :  * malloc/free are often non-reentrant, and anything that might call them is
     458             :  * just as dangerous.  We avoid sprintf here for that reason.  Building up
     459             :  * error messages with strcpy/strcat is tedious but should be quite safe.
     460             :  * We also save/restore errno in case the signal handler support doesn't.
     461             :  */
     462             : int
     463           8 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
     464             : {
     465           8 :     int         save_errno = SOCK_ERRNO;
     466           8 :     pgsocket    tmpsock = PGINVALID_SOCKET;
     467             :     int         maxlen;
     468             :     struct
     469             :     {
     470             :         uint32      packetlen;
     471             :         CancelRequestPacket cp;
     472             :     }           crp;
     473             : 
     474           8 :     if (!cancel)
     475             :     {
     476           0 :         strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
     477             :         /* strlcpy probably doesn't change errno, but be paranoid */
     478           0 :         SOCK_ERRNO_SET(save_errno);
     479           0 :         return false;
     480             :     }
     481             : 
     482             :     /*
     483             :      * We need to open a temporary connection to the postmaster. Do this with
     484             :      * only kernel calls.
     485             :      */
     486           8 :     if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
     487             :     {
     488           0 :         strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
     489           0 :         goto cancel_errReturn;
     490             :     }
     491             : 
     492             :     /*
     493             :      * Since this connection will only be used to send a single packet of
     494             :      * data, we don't need NODELAY.  We also don't set the socket to
     495             :      * nonblocking mode, because the API definition of PQcancel requires the
     496             :      * cancel to be sent in a blocking way.
     497             :      *
     498             :      * We do set socket options related to keepalives and other TCP timeouts.
     499             :      * This ensures that this function does not block indefinitely when
     500             :      * reasonable keepalive and timeout settings have been provided.
     501             :      */
     502           8 :     if (cancel->raddr.addr.ss_family != AF_UNIX &&
     503           0 :         cancel->keepalives != 0)
     504             :     {
     505             : #ifndef WIN32
     506           0 :         if (!optional_setsockopt(tmpsock, SOL_SOCKET, SO_KEEPALIVE, 1))
     507             :         {
     508           0 :             strlcpy(errbuf, "PQcancel() -- setsockopt(SO_KEEPALIVE) failed: ", errbufsize);
     509           0 :             goto cancel_errReturn;
     510             :         }
     511             : 
     512             : #ifdef PG_TCP_KEEPALIVE_IDLE
     513             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
     514             :                                  cancel->keepalives_idle))
     515             :         {
     516             :             strlcpy(errbuf, "PQcancel() -- setsockopt(" PG_TCP_KEEPALIVE_IDLE_STR ") failed: ", errbufsize);
     517             :             goto cancel_errReturn;
     518             :         }
     519             : #endif
     520             : 
     521             : #ifdef TCP_KEEPINTVL
     522             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPINTVL,
     523             :                                  cancel->keepalives_interval))
     524             :         {
     525             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPINTVL) failed: ", errbufsize);
     526             :             goto cancel_errReturn;
     527             :         }
     528             : #endif
     529             : 
     530             : #ifdef TCP_KEEPCNT
     531             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPCNT,
     532             :                                  cancel->keepalives_count))
     533             :         {
     534             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPCNT) failed: ", errbufsize);
     535             :             goto cancel_errReturn;
     536             :         }
     537             : #endif
     538             : 
     539             : #else                           /* WIN32 */
     540             : 
     541             : #ifdef SIO_KEEPALIVE_VALS
     542             :         if (!pqSetKeepalivesWin32(tmpsock,
     543             :                                   cancel->keepalives_idle,
     544             :                                   cancel->keepalives_interval))
     545             :         {
     546             :             strlcpy(errbuf, "PQcancel() -- WSAIoctl(SIO_KEEPALIVE_VALS) failed: ", errbufsize);
     547             :             goto cancel_errReturn;
     548             :         }
     549             : #endif                          /* SIO_KEEPALIVE_VALS */
     550             : #endif                          /* WIN32 */
     551             : 
     552             :         /* TCP_USER_TIMEOUT works the same way on Unix and Windows */
     553             : #ifdef TCP_USER_TIMEOUT
     554             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_USER_TIMEOUT,
     555             :                                  cancel->pgtcp_user_timeout))
     556             :         {
     557             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_USER_TIMEOUT) failed: ", errbufsize);
     558             :             goto cancel_errReturn;
     559             :         }
     560             : #endif
     561             :     }
     562             : 
     563           8 : retry3:
     564           8 :     if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
     565             :                 cancel->raddr.salen) < 0)
     566             :     {
     567           0 :         if (SOCK_ERRNO == EINTR)
     568             :             /* Interrupted system call - we'll just try again */
     569           0 :             goto retry3;
     570           0 :         strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
     571           0 :         goto cancel_errReturn;
     572             :     }
     573             : 
     574             :     /* Create and send the cancel request packet. */
     575             : 
     576           8 :     crp.packetlen = pg_hton32((uint32) sizeof(crp));
     577           8 :     crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
     578           8 :     crp.cp.backendPID = pg_hton32(cancel->be_pid);
     579           8 :     crp.cp.cancelAuthCode = pg_hton32(cancel->be_key);
     580             : 
     581           8 : retry4:
     582           8 :     if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
     583             :     {
     584           0 :         if (SOCK_ERRNO == EINTR)
     585             :             /* Interrupted system call - we'll just try again */
     586           0 :             goto retry4;
     587           0 :         strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
     588           0 :         goto cancel_errReturn;
     589             :     }
     590             : 
     591             :     /*
     592             :      * Wait for the postmaster to close the connection, which indicates that
     593             :      * it's processed the request.  Without this delay, we might issue another
     594             :      * command only to find that our cancel zaps that command instead of the
     595             :      * one we thought we were canceling.  Note we don't actually expect this
     596             :      * read to obtain any data, we are just waiting for EOF to be signaled.
     597             :      */
     598           8 : retry5:
     599           8 :     if (recv(tmpsock, (char *) &crp, 1, 0) < 0)
     600             :     {
     601           0 :         if (SOCK_ERRNO == EINTR)
     602             :             /* Interrupted system call - we'll just try again */
     603           0 :             goto retry5;
     604             :         /* we ignore other error conditions */
     605             :     }
     606             : 
     607             :     /* All done */
     608           8 :     closesocket(tmpsock);
     609           8 :     SOCK_ERRNO_SET(save_errno);
     610           8 :     return true;
     611             : 
     612           0 : cancel_errReturn:
     613             : 
     614             :     /*
     615             :      * Make sure we don't overflow the error buffer. Leave space for the \n at
     616             :      * the end, and for the terminating zero.
     617             :      */
     618           0 :     maxlen = errbufsize - strlen(errbuf) - 2;
     619           0 :     if (maxlen >= 0)
     620             :     {
     621             :         /*
     622             :          * We can't invoke strerror here, since it's not signal-safe.  Settle
     623             :          * for printing the decimal value of errno.  Even that has to be done
     624             :          * the hard way.
     625             :          */
     626           0 :         int         val = SOCK_ERRNO;
     627             :         char        buf[32];
     628             :         char       *bufp;
     629             : 
     630           0 :         bufp = buf + sizeof(buf) - 1;
     631           0 :         *bufp = '\0';
     632             :         do
     633             :         {
     634           0 :             *(--bufp) = (val % 10) + '0';
     635           0 :             val /= 10;
     636           0 :         } while (val > 0);
     637           0 :         bufp -= 6;
     638           0 :         memcpy(bufp, "error ", 6);
     639           0 :         strncat(errbuf, bufp, maxlen);
     640           0 :         strcat(errbuf, "\n");
     641             :     }
     642           0 :     if (tmpsock != PGINVALID_SOCKET)
     643           0 :         closesocket(tmpsock);
     644           0 :     SOCK_ERRNO_SET(save_errno);
     645           0 :     return false;
     646             : }
     647             : 
     648             : /*
     649             :  * PQrequestCancel: old, not thread-safe function for requesting query cancel
     650             :  *
     651             :  * Returns true if able to send the cancel request, false if not.
     652             :  *
     653             :  * On failure, the error message is saved in conn->errorMessage; this means
     654             :  * that this can't be used when there might be other active operations on
     655             :  * the connection object.
     656             :  *
     657             :  * NOTE: error messages will be cut off at the current size of the
     658             :  * error message buffer, since we dare not try to expand conn->errorMessage!
     659             :  */
     660             : int
     661           2 : PQrequestCancel(PGconn *conn)
     662             : {
     663             :     int         r;
     664             :     PGcancel   *cancel;
     665             : 
     666             :     /* Check we have an open connection */
     667           2 :     if (!conn)
     668           0 :         return false;
     669             : 
     670           2 :     if (conn->sock == PGINVALID_SOCKET)
     671             :     {
     672           0 :         strlcpy(conn->errorMessage.data,
     673             :                 "PQrequestCancel() -- connection is not open\n",
     674             :                 conn->errorMessage.maxlen);
     675           0 :         conn->errorMessage.len = strlen(conn->errorMessage.data);
     676           0 :         conn->errorReported = 0;
     677             : 
     678           0 :         return false;
     679             :     }
     680             : 
     681           2 :     cancel = PQgetCancel(conn);
     682           2 :     if (cancel)
     683             :     {
     684           2 :         r = PQcancel(cancel, conn->errorMessage.data,
     685           2 :                      conn->errorMessage.maxlen);
     686           2 :         PQfreeCancel(cancel);
     687             :     }
     688             :     else
     689             :     {
     690           0 :         strlcpy(conn->errorMessage.data, "out of memory",
     691             :                 conn->errorMessage.maxlen);
     692           0 :         r = false;
     693             :     }
     694             : 
     695           2 :     if (!r)
     696             :     {
     697           0 :         conn->errorMessage.len = strlen(conn->errorMessage.data);
     698           0 :         conn->errorReported = 0;
     699             :     }
     700             : 
     701           2 :     return r;
     702             : }

Generated by: LCOV version 1.14