LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-cancel.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 133 242 55.0 %
Date: 2025-04-24 12:15:10 Functions: 13 15 86.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14