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 : }
|