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

Generated by: LCOV version 1.16