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