LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-cancel.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 54.7 % 247 135
Test Date: 2026-03-12 01:15:13 Functions: 86.7 % 15 13
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-2026, 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            7 : PQcancelCreate(PGconn *conn)
      69              : {
      70            7 :     PGconn     *cancelConn = pqMakeEmptyPGconn();
      71              :     pg_conn_host originalHost;
      72              : 
      73            7 :     if (cancelConn == NULL)
      74            0 :         return NULL;
      75              : 
      76              :     /* Check we have an open connection */
      77            7 :     if (!conn)
      78              :     {
      79            0 :         libpq_append_conn_error(cancelConn, "connection pointer is NULL");
      80            0 :         return (PGcancelConn *) cancelConn;
      81              :     }
      82              : 
      83            7 :     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            7 :     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            7 :     cancelConn->cancelRequest = true;
     100              : 
     101            7 :     if (!pqCopyPGconn(conn, cancelConn))
     102            0 :         return (PGcancelConn *) cancelConn;
     103              : 
     104              :     /*
     105              :      * Compute derived options
     106              :      */
     107            7 :     if (!pqConnectOptions2(cancelConn))
     108            0 :         return (PGcancelConn *) cancelConn;
     109              : 
     110              :     /*
     111              :      * Copy cancellation token data from the original connection
     112              :      */
     113            7 :     cancelConn->be_pid = conn->be_pid;
     114            7 :     if (conn->be_cancel_key != NULL)
     115              :     {
     116            7 :         cancelConn->be_cancel_key = malloc(conn->be_cancel_key_len);
     117            7 :         if (cancelConn->be_cancel_key == NULL)
     118            0 :             goto oom_error;
     119            7 :         memcpy(cancelConn->be_cancel_key, conn->be_cancel_key, conn->be_cancel_key_len);
     120              :     }
     121            7 :     cancelConn->be_cancel_key_len = conn->be_cancel_key_len;
     122            7 :     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            7 :     pqReleaseConnHosts(cancelConn);
     132            7 :     cancelConn->nconnhost = 1;
     133            7 :     cancelConn->naddr = 1;
     134              : 
     135            7 :     cancelConn->connhost = calloc(cancelConn->nconnhost, sizeof(pg_conn_host));
     136            7 :     if (!cancelConn->connhost)
     137            0 :         goto oom_error;
     138              : 
     139            7 :     originalHost = conn->connhost[conn->whichhost];
     140            7 :     cancelConn->connhost[0].type = originalHost.type;
     141            7 :     if (originalHost.host)
     142              :     {
     143            7 :         cancelConn->connhost[0].host = strdup(originalHost.host);
     144            7 :         if (!cancelConn->connhost[0].host)
     145            0 :             goto oom_error;
     146              :     }
     147            7 :     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            7 :     if (originalHost.port)
     154              :     {
     155            7 :         cancelConn->connhost[0].port = strdup(originalHost.port);
     156            7 :         if (!cancelConn->connhost[0].port)
     157            0 :             goto oom_error;
     158              :     }
     159            7 :     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            7 :     cancelConn->addr = calloc(cancelConn->naddr, sizeof(AddrInfo));
     167            7 :     if (!cancelConn->addr)
     168            0 :         goto oom_error;
     169              : 
     170            7 :     cancelConn->addr[0].addr = conn->raddr;
     171            7 :     cancelConn->addr[0].family = conn->raddr.addr.ss_family;
     172              : 
     173            7 :     cancelConn->status = CONNECTION_ALLOCATED;
     174            7 :     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            2 : PQcancelBlocking(PGcancelConn *cancelConn)
     191              : {
     192            2 :     if (!PQcancelStart(cancelConn))
     193            0 :         return 0;
     194            2 :     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            9 : PQcancelStart(PGcancelConn *cancelConn)
     205              : {
     206            9 :     if (!cancelConn || cancelConn->conn.status == CONNECTION_BAD)
     207            0 :         return 0;
     208              : 
     209            9 :     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            9 :     return pqConnectDBStart(&cancelConn->conn);
     218              : }
     219              : 
     220              : /*
     221              :  *      PQcancelPoll
     222              :  *
     223              :  * Poll a cancel connection. For usage details see PQconnectPoll.
     224              :  */
     225              : PostgresPollingStatusType
     226           18 : PQcancelPoll(PGcancelConn *cancelConn)
     227              : {
     228           18 :     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           18 :     if (conn->status != CONNECTION_AWAITING_RESPONSE)
     238              :     {
     239            9 :         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            9 :     n = pqReadData(conn);
     248              : 
     249            9 :     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            9 :     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            9 :     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            9 :     cancelConn->conn.status = CONNECTION_OK;
     292            9 :     resetPQExpBuffer(&conn->errorMessage);
     293            9 :     return PGRES_POLLING_OK;
     294              : }
     295              : 
     296              : /*
     297              :  *      PQcancelStatus
     298              :  *
     299              :  * Get the status of a cancel connection.
     300              :  */
     301              : ConnStatusType
     302            4 : PQcancelStatus(const PGcancelConn *cancelConn)
     303              : {
     304            4 :     return PQstatus(&cancelConn->conn);
     305              : }
     306              : 
     307              : /*
     308              :  *      PQcancelSocket
     309              :  *
     310              :  * Get the socket of the cancel connection.
     311              :  */
     312              : int
     313           11 : PQcancelSocket(const PGcancelConn *cancelConn)
     314              : {
     315           11 :     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            2 : PQcancelReset(PGcancelConn *cancelConn)
     338              : {
     339            2 :     pqClosePGconn(&cancelConn->conn);
     340            2 :     cancelConn->conn.status = CONNECTION_ALLOCATED;
     341            2 :     cancelConn->conn.whichhost = 0;
     342            2 :     cancelConn->conn.whichaddr = 0;
     343            2 :     cancelConn->conn.try_next_host = false;
     344            2 :     cancelConn->conn.try_next_addr = false;
     345            2 : }
     346              : 
     347              : /*
     348              :  *      PQcancelFinish
     349              :  *
     350              :  * Closes and frees the cancel connection.
     351              :  */
     352              : void
     353            7 : PQcancelFinish(PGcancelConn *cancelConn)
     354              : {
     355            7 :     PQfinish(&cancelConn->conn);
     356            7 : }
     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       229644 : PQgetCancel(PGconn *conn)
     369              : {
     370              :     PGcancel   *cancel;
     371              :     int         cancel_req_len;
     372              :     CancelRequestPacket *req;
     373              : 
     374       229644 :     if (!conn)
     375           22 :         return NULL;
     376              : 
     377       229622 :     if (conn->sock == PGINVALID_SOCKET)
     378            0 :         return NULL;
     379              : 
     380              :     /* Check that we have received a cancellation key */
     381       229622 :     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       229622 :     cancel_req_len = offsetof(CancelRequestPacket, cancelAuthCode) + conn->be_cancel_key_len;
     402       229622 :     cancel = malloc(offsetof(PGcancel, cancel_req) + cancel_req_len);
     403       229622 :     if (cancel == NULL)
     404            0 :         return NULL;
     405              : 
     406       229622 :     memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
     407              : 
     408              :     /* We use -1 to indicate an unset connection option */
     409       229622 :     cancel->pgtcp_user_timeout = -1;
     410       229622 :     cancel->keepalives = -1;
     411       229622 :     cancel->keepalives_idle = -1;
     412       229622 :     cancel->keepalives_interval = -1;
     413       229622 :     cancel->keepalives_count = -1;
     414       229622 :     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       229622 :     if (conn->keepalives != NULL)
     422              :     {
     423            0 :         if (!pqParseIntParam(conn->keepalives,
     424              :                              &cancel->keepalives,
     425              :                              conn, "keepalives"))
     426            0 :             goto fail;
     427              :     }
     428       229622 :     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       229622 :     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       229622 :     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       229622 :     req = (CancelRequestPacket *) &cancel->cancel_req;
     451       229622 :     req->cancelRequestCode = pg_hton32(CANCEL_REQUEST_CODE);
     452       229622 :     req->backendPID = pg_hton32(conn->be_pid);
     453       229622 :     memcpy(req->cancelAuthCode, conn->be_cancel_key, conn->be_cancel_key_len);
     454              :     /* include the length field itself in the length */
     455       229622 :     cancel->cancel_pkt_len = pg_hton32(cancel_req_len + 4);
     456              : 
     457       229622 :     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            9 : PQsendCancelRequest(PGconn *cancelConn)
     473              : {
     474              :     CancelRequestPacket req;
     475              : 
     476              :     /* Start the message. */
     477            9 :     if (pqPutMsgStart(0, cancelConn))
     478            0 :         return STATUS_ERROR;
     479              : 
     480              :     /* Send the message body. */
     481            9 :     memset(&req, 0, offsetof(CancelRequestPacket, cancelAuthCode));
     482            9 :     req.cancelRequestCode = pg_hton32(CANCEL_REQUEST_CODE);
     483            9 :     req.backendPID = pg_hton32(cancelConn->be_pid);
     484            9 :     if (pqPutnchar(&req, offsetof(CancelRequestPacket, cancelAuthCode), cancelConn))
     485            0 :         return STATUS_ERROR;
     486            9 :     if (pqPutnchar(cancelConn->be_cancel_key, cancelConn->be_cancel_key_len, cancelConn))
     487            0 :         return STATUS_ERROR;
     488              : 
     489              :     /* Finish the message. */
     490            9 :     if (pqPutMsgEnd(cancelConn))
     491            0 :         return STATUS_ERROR;
     492              : 
     493              :     /* Flush to ensure backend gets it. */
     494            9 :     if (pqFlush(cancelConn))
     495            0 :         return STATUS_ERROR;
     496              : 
     497            9 :     return STATUS_OK;
     498              : }
     499              : 
     500              : /* PQfreeCancel: free a cancel structure */
     501              : void
     502       229604 : PQfreeCancel(PGcancel *cancel)
     503              : {
     504       229604 :     free(cancel);
     505       229604 : }
     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            7 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
     549              : {
     550            7 :     int         save_errno = SOCK_ERRNO;
     551            7 :     pgsocket    tmpsock = PGINVALID_SOCKET;
     552              :     int         maxlen;
     553              :     char        recvbuf;
     554              :     int         cancel_pkt_len;
     555              : 
     556            7 :     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            7 :     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            7 :     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            7 :     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            7 : retry3:
     655            7 :     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            7 :     cancel_pkt_len = pg_ntoh32(cancel->cancel_pkt_len);
     666              : 
     667            7 : 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            7 :     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            7 : retry5:
     690            7 :     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            7 :     closesocket(tmpsock);
     700            7 :     SOCK_ERRNO_SET(save_errno);
     701            7 :     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            2 : PQrequestCancel(PGconn *conn)
     753              : {
     754              :     int         r;
     755              :     PGcancel   *cancel;
     756              : 
     757              :     /* Check we have an open connection */
     758            2 :     if (!conn)
     759            0 :         return false;
     760              : 
     761            2 :     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            2 :     cancel = PQgetCancel(conn);
     773            2 :     if (cancel)
     774              :     {
     775            2 :         r = PQcancel(cancel, conn->errorMessage.data,
     776            2 :                      conn->errorMessage.maxlen);
     777            2 :         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            2 :     if (!r)
     787              :     {
     788            0 :         conn->errorMessage.len = strlen(conn->errorMessage.data);
     789            0 :         conn->errorReported = 0;
     790              :     }
     791              : 
     792            2 :     return r;
     793              : }
        

Generated by: LCOV version 2.0-1