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