LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-cancel.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 134 243 55.1 %
Date: 2025-07-08 10:18:38 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      414710 : PQgetCancel(PGconn *conn)
     369             : {
     370             :     PGcancel   *cancel;
     371             :     int         cancel_req_len;
     372             :     CancelRequestPacket *req;
     373             : 
     374      414710 :     if (!conn)
     375          44 :         return NULL;
     376             : 
     377      414666 :     if (conn->sock == PGINVALID_SOCKET)
     378           0 :         return NULL;
     379             : 
     380             :     /* Check that we have received a cancellation key */
     381      414666 :     if (conn->be_cancel_key_len == 0)
     382           0 :         return NULL;
     383             : 
     384      414666 :     cancel_req_len = offsetof(CancelRequestPacket, cancelAuthCode) + conn->be_cancel_key_len;
     385      414666 :     cancel = malloc(offsetof(PGcancel, cancel_req) + cancel_req_len);
     386      414666 :     if (cancel == NULL)
     387           0 :         return NULL;
     388             : 
     389      414666 :     memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
     390             : 
     391             :     /* We use -1 to indicate an unset connection option */
     392      414666 :     cancel->pgtcp_user_timeout = -1;
     393      414666 :     cancel->keepalives = -1;
     394      414666 :     cancel->keepalives_idle = -1;
     395      414666 :     cancel->keepalives_interval = -1;
     396      414666 :     cancel->keepalives_count = -1;
     397      414666 :     if (conn->pgtcp_user_timeout != NULL)
     398             :     {
     399           0 :         if (!pqParseIntParam(conn->pgtcp_user_timeout,
     400             :                              &cancel->pgtcp_user_timeout,
     401             :                              conn, "tcp_user_timeout"))
     402           0 :             goto fail;
     403             :     }
     404      414666 :     if (conn->keepalives != NULL)
     405             :     {
     406           0 :         if (!pqParseIntParam(conn->keepalives,
     407             :                              &cancel->keepalives,
     408             :                              conn, "keepalives"))
     409           0 :             goto fail;
     410             :     }
     411      414666 :     if (conn->keepalives_idle != NULL)
     412             :     {
     413           0 :         if (!pqParseIntParam(conn->keepalives_idle,
     414             :                              &cancel->keepalives_idle,
     415             :                              conn, "keepalives_idle"))
     416           0 :             goto fail;
     417             :     }
     418      414666 :     if (conn->keepalives_interval != NULL)
     419             :     {
     420           0 :         if (!pqParseIntParam(conn->keepalives_interval,
     421             :                              &cancel->keepalives_interval,
     422             :                              conn, "keepalives_interval"))
     423           0 :             goto fail;
     424             :     }
     425      414666 :     if (conn->keepalives_count != NULL)
     426             :     {
     427           0 :         if (!pqParseIntParam(conn->keepalives_count,
     428             :                              &cancel->keepalives_count,
     429             :                              conn, "keepalives_count"))
     430           0 :             goto fail;
     431             :     }
     432             : 
     433      414666 :     req = (CancelRequestPacket *) &cancel->cancel_req;
     434      414666 :     req->cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
     435      414666 :     req->backendPID = pg_hton32(conn->be_pid);
     436      414666 :     memcpy(req->cancelAuthCode, conn->be_cancel_key, conn->be_cancel_key_len);
     437             :     /* include the length field itself in the length */
     438      414666 :     cancel->cancel_pkt_len = pg_hton32(cancel_req_len + 4);
     439             : 
     440      414666 :     return cancel;
     441             : 
     442           0 : fail:
     443           0 :     free(cancel);
     444           0 :     return NULL;
     445             : }
     446             : 
     447             : /*
     448             :  * PQsendCancelRequest
     449             :  *   Submit a CancelRequest message, but don't wait for it to finish
     450             :  *
     451             :  * Returns: 1 if successfully submitted
     452             :  *          0 if error (conn->errorMessage is set)
     453             :  */
     454             : int
     455          18 : PQsendCancelRequest(PGconn *cancelConn)
     456             : {
     457             :     CancelRequestPacket req;
     458             : 
     459             :     /* Start the message. */
     460          18 :     if (pqPutMsgStart(0, cancelConn))
     461           0 :         return STATUS_ERROR;
     462             : 
     463             :     /* Send the message body. */
     464          18 :     memset(&req, 0, offsetof(CancelRequestPacket, cancelAuthCode));
     465          18 :     req.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
     466          18 :     req.backendPID = pg_hton32(cancelConn->be_pid);
     467          18 :     if (pqPutnchar(&req, offsetof(CancelRequestPacket, cancelAuthCode), cancelConn))
     468           0 :         return STATUS_ERROR;
     469          18 :     if (pqPutnchar(cancelConn->be_cancel_key, cancelConn->be_cancel_key_len, cancelConn))
     470           0 :         return STATUS_ERROR;
     471             : 
     472             :     /* Finish the message. */
     473          18 :     if (pqPutMsgEnd(cancelConn))
     474           0 :         return STATUS_ERROR;
     475             : 
     476             :     /* Flush to ensure backend gets it. */
     477          18 :     if (pqFlush(cancelConn))
     478           0 :         return STATUS_ERROR;
     479             : 
     480          18 :     return STATUS_OK;
     481             : }
     482             : 
     483             : /* PQfreeCancel: free a cancel structure */
     484             : void
     485      414634 : PQfreeCancel(PGcancel *cancel)
     486             : {
     487      414634 :     free(cancel);
     488      414634 : }
     489             : 
     490             : 
     491             : /*
     492             :  * Sets an integer socket option on a TCP socket, if the provided value is
     493             :  * not negative.  Returns false if setsockopt fails for some reason.
     494             :  *
     495             :  * CAUTION: This needs to be signal safe, since it's used by PQcancel.
     496             :  */
     497             : #if defined(TCP_USER_TIMEOUT) || !defined(WIN32)
     498             : static bool
     499           0 : optional_setsockopt(int fd, int protoid, int optid, int value)
     500             : {
     501           0 :     if (value < 0)
     502           0 :         return true;
     503           0 :     if (setsockopt(fd, protoid, optid, (char *) &value, sizeof(value)) < 0)
     504           0 :         return false;
     505           0 :     return true;
     506             : }
     507             : #endif
     508             : 
     509             : 
     510             : /*
     511             :  * PQcancel: old, non-encrypted, but signal-safe way of requesting query cancel
     512             :  *
     513             :  * The return value is true if the cancel request was successfully
     514             :  * dispatched, false if not (in which case an error message is available).
     515             :  * Note: successful dispatch is no guarantee that there will be any effect at
     516             :  * the backend.  The application must read the operation result as usual.
     517             :  *
     518             :  * On failure, an error message is stored in *errbuf, which must be of size
     519             :  * errbufsize (recommended size is 256 bytes).  *errbuf is not changed on
     520             :  * success return.
     521             :  *
     522             :  * CAUTION: we want this routine to be safely callable from a signal handler
     523             :  * (for example, an application might want to call it in a SIGINT handler).
     524             :  * This means we cannot use any C library routine that might be non-reentrant.
     525             :  * malloc/free are often non-reentrant, and anything that might call them is
     526             :  * just as dangerous.  We avoid sprintf here for that reason.  Building up
     527             :  * error messages with strcpy/strcat is tedious but should be quite safe.
     528             :  * We also save/restore errno in case the signal handler support doesn't.
     529             :  */
     530             : int
     531          14 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
     532             : {
     533          14 :     int         save_errno = SOCK_ERRNO;
     534          14 :     pgsocket    tmpsock = PGINVALID_SOCKET;
     535             :     int         maxlen;
     536             :     char        recvbuf;
     537             :     int         cancel_pkt_len;
     538             : 
     539          14 :     if (!cancel)
     540             :     {
     541           0 :         strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
     542             :         /* strlcpy probably doesn't change errno, but be paranoid */
     543           0 :         SOCK_ERRNO_SET(save_errno);
     544           0 :         return false;
     545             :     }
     546             : 
     547             :     /*
     548             :      * We need to open a temporary connection to the postmaster. Do this with
     549             :      * only kernel calls.
     550             :      */
     551          14 :     if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
     552             :     {
     553           0 :         strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
     554           0 :         goto cancel_errReturn;
     555             :     }
     556             : 
     557             :     /*
     558             :      * Since this connection will only be used to send a single packet of
     559             :      * data, we don't need NODELAY.  We also don't set the socket to
     560             :      * nonblocking mode, because the API definition of PQcancel requires the
     561             :      * cancel to be sent in a blocking way.
     562             :      *
     563             :      * We do set socket options related to keepalives and other TCP timeouts.
     564             :      * This ensures that this function does not block indefinitely when
     565             :      * reasonable keepalive and timeout settings have been provided.
     566             :      */
     567          14 :     if (cancel->raddr.addr.ss_family != AF_UNIX &&
     568           0 :         cancel->keepalives != 0)
     569             :     {
     570             : #ifndef WIN32
     571           0 :         if (!optional_setsockopt(tmpsock, SOL_SOCKET, SO_KEEPALIVE, 1))
     572             :         {
     573           0 :             strlcpy(errbuf, "PQcancel() -- setsockopt(SO_KEEPALIVE) failed: ", errbufsize);
     574           0 :             goto cancel_errReturn;
     575             :         }
     576             : 
     577             : #ifdef PG_TCP_KEEPALIVE_IDLE
     578             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
     579             :                                  cancel->keepalives_idle))
     580             :         {
     581             :             strlcpy(errbuf, "PQcancel() -- setsockopt(" PG_TCP_KEEPALIVE_IDLE_STR ") failed: ", errbufsize);
     582             :             goto cancel_errReturn;
     583             :         }
     584             : #endif
     585             : 
     586             : #ifdef TCP_KEEPINTVL
     587             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPINTVL,
     588             :                                  cancel->keepalives_interval))
     589             :         {
     590             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPINTVL) failed: ", errbufsize);
     591             :             goto cancel_errReturn;
     592             :         }
     593             : #endif
     594             : 
     595             : #ifdef TCP_KEEPCNT
     596             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPCNT,
     597             :                                  cancel->keepalives_count))
     598             :         {
     599             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPCNT) failed: ", errbufsize);
     600             :             goto cancel_errReturn;
     601             :         }
     602             : #endif
     603             : 
     604             : #else                           /* WIN32 */
     605             : 
     606             : #ifdef SIO_KEEPALIVE_VALS
     607             :         if (!pqSetKeepalivesWin32(tmpsock,
     608             :                                   cancel->keepalives_idle,
     609             :                                   cancel->keepalives_interval))
     610             :         {
     611             :             strlcpy(errbuf, "PQcancel() -- WSAIoctl(SIO_KEEPALIVE_VALS) failed: ", errbufsize);
     612             :             goto cancel_errReturn;
     613             :         }
     614             : #endif                          /* SIO_KEEPALIVE_VALS */
     615             : #endif                          /* WIN32 */
     616             : 
     617             :         /* TCP_USER_TIMEOUT works the same way on Unix and Windows */
     618             : #ifdef TCP_USER_TIMEOUT
     619             :         if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_USER_TIMEOUT,
     620             :                                  cancel->pgtcp_user_timeout))
     621             :         {
     622             :             strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_USER_TIMEOUT) failed: ", errbufsize);
     623             :             goto cancel_errReturn;
     624             :         }
     625             : #endif
     626             :     }
     627             : 
     628          14 : retry3:
     629          14 :     if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
     630             :                 cancel->raddr.salen) < 0)
     631             :     {
     632           0 :         if (SOCK_ERRNO == EINTR)
     633             :             /* Interrupted system call - we'll just try again */
     634           0 :             goto retry3;
     635           0 :         strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
     636           0 :         goto cancel_errReturn;
     637             :     }
     638             : 
     639          14 :     cancel_pkt_len = pg_ntoh32(cancel->cancel_pkt_len);
     640             : 
     641          14 : retry4:
     642             : 
     643             :     /*
     644             :      * Send the cancel request packet. It starts with the message length at
     645             :      * cancel_pkt_len, followed by the actual packet.
     646             :      */
     647          14 :     if (send(tmpsock, (char *) &cancel->cancel_pkt_len, cancel_pkt_len, 0) != cancel_pkt_len)
     648             :     {
     649           0 :         if (SOCK_ERRNO == EINTR)
     650             :             /* Interrupted system call - we'll just try again */
     651           0 :             goto retry4;
     652           0 :         strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
     653           0 :         goto cancel_errReturn;
     654             :     }
     655             : 
     656             :     /*
     657             :      * Wait for the postmaster to close the connection, which indicates that
     658             :      * it's processed the request.  Without this delay, we might issue another
     659             :      * command only to find that our cancel zaps that command instead of the
     660             :      * one we thought we were canceling.  Note we don't actually expect this
     661             :      * read to obtain any data, we are just waiting for EOF to be signaled.
     662             :      */
     663          14 : retry5:
     664          14 :     if (recv(tmpsock, &recvbuf, 1, 0) < 0)
     665             :     {
     666           0 :         if (SOCK_ERRNO == EINTR)
     667             :             /* Interrupted system call - we'll just try again */
     668           0 :             goto retry5;
     669             :         /* we ignore other error conditions */
     670             :     }
     671             : 
     672             :     /* All done */
     673          14 :     closesocket(tmpsock);
     674          14 :     SOCK_ERRNO_SET(save_errno);
     675          14 :     return true;
     676             : 
     677           0 : cancel_errReturn:
     678             : 
     679             :     /*
     680             :      * Make sure we don't overflow the error buffer. Leave space for the \n at
     681             :      * the end, and for the terminating zero.
     682             :      */
     683           0 :     maxlen = errbufsize - strlen(errbuf) - 2;
     684           0 :     if (maxlen >= 0)
     685             :     {
     686             :         /*
     687             :          * We can't invoke strerror here, since it's not signal-safe.  Settle
     688             :          * for printing the decimal value of errno.  Even that has to be done
     689             :          * the hard way.
     690             :          */
     691           0 :         int         val = SOCK_ERRNO;
     692             :         char        buf[32];
     693             :         char       *bufp;
     694             : 
     695           0 :         bufp = buf + sizeof(buf) - 1;
     696           0 :         *bufp = '\0';
     697             :         do
     698             :         {
     699           0 :             *(--bufp) = (val % 10) + '0';
     700           0 :             val /= 10;
     701           0 :         } while (val > 0);
     702           0 :         bufp -= 6;
     703           0 :         memcpy(bufp, "error ", 6);
     704           0 :         strncat(errbuf, bufp, maxlen);
     705           0 :         strcat(errbuf, "\n");
     706             :     }
     707           0 :     if (tmpsock != PGINVALID_SOCKET)
     708           0 :         closesocket(tmpsock);
     709           0 :     SOCK_ERRNO_SET(save_errno);
     710           0 :     return false;
     711             : }
     712             : 
     713             : /*
     714             :  * PQrequestCancel: old, not thread-safe function for requesting query cancel
     715             :  *
     716             :  * Returns true if able to send the cancel request, false if not.
     717             :  *
     718             :  * On failure, the error message is saved in conn->errorMessage; this means
     719             :  * that this can't be used when there might be other active operations on
     720             :  * the connection object.
     721             :  *
     722             :  * NOTE: error messages will be cut off at the current size of the
     723             :  * error message buffer, since we dare not try to expand conn->errorMessage!
     724             :  */
     725             : int
     726           4 : PQrequestCancel(PGconn *conn)
     727             : {
     728             :     int         r;
     729             :     PGcancel   *cancel;
     730             : 
     731             :     /* Check we have an open connection */
     732           4 :     if (!conn)
     733           0 :         return false;
     734             : 
     735           4 :     if (conn->sock == PGINVALID_SOCKET)
     736             :     {
     737           0 :         strlcpy(conn->errorMessage.data,
     738             :                 "PQrequestCancel() -- connection is not open\n",
     739             :                 conn->errorMessage.maxlen);
     740           0 :         conn->errorMessage.len = strlen(conn->errorMessage.data);
     741           0 :         conn->errorReported = 0;
     742             : 
     743           0 :         return false;
     744             :     }
     745             : 
     746           4 :     cancel = PQgetCancel(conn);
     747           4 :     if (cancel)
     748             :     {
     749           4 :         r = PQcancel(cancel, conn->errorMessage.data,
     750           4 :                      conn->errorMessage.maxlen);
     751           4 :         PQfreeCancel(cancel);
     752             :     }
     753             :     else
     754             :     {
     755           0 :         strlcpy(conn->errorMessage.data, "out of memory",
     756             :                 conn->errorMessage.maxlen);
     757           0 :         r = false;
     758             :     }
     759             : 
     760           4 :     if (!r)
     761             :     {
     762           0 :         conn->errorMessage.len = strlen(conn->errorMessage.data);
     763           0 :         conn->errorReported = 0;
     764             :     }
     765             : 
     766           4 :     return r;
     767             : }

Generated by: LCOV version 1.16