Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * fe-cancel.c
4 : : * functions related to setting up a connection to the backend
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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 be_key; /* cancel key of to-be-canceled backend */
45 : : int pgtcp_user_timeout; /* tcp user timeout */
46 : : int keepalives; /* use TCP keepalives? */
47 : : int keepalives_idle; /* time between TCP keepalives */
48 : : int keepalives_interval; /* time between TCP keepalive
49 : : * retransmits */
50 : : int keepalives_count; /* maximum number of TCP keepalive
51 : : * retransmits */
52 : : };
53 : :
54 : :
55 : : /*
56 : : * PQcancelCreate
57 : : *
58 : : * Create and return a PGcancelConn, which can be used to securely cancel a
59 : : * query on the given connection.
60 : : *
61 : : * This requires either following the non-blocking flow through
62 : : * PQcancelStart() and PQcancelPoll(), or the blocking PQcancelBlocking().
63 : : */
64 : : PGcancelConn *
33 alvherre@alvh.no-ip. 65 :GNC 5 : PQcancelCreate(PGconn *conn)
66 : : {
67 : 5 : PGconn *cancelConn = pqMakeEmptyPGconn();
68 : : pg_conn_host originalHost;
69 : :
70 [ - + ]: 5 : if (cancelConn == NULL)
33 alvherre@alvh.no-ip. 71 :UNC 0 : return NULL;
72 : :
73 : : /* Check we have an open connection */
33 alvherre@alvh.no-ip. 74 [ - + ]:GNC 5 : if (!conn)
75 : : {
33 alvherre@alvh.no-ip. 76 :UNC 0 : libpq_append_conn_error(cancelConn, "passed connection was NULL");
77 : 0 : return (PGcancelConn *) cancelConn;
78 : : }
79 : :
33 alvherre@alvh.no-ip. 80 [ - + ]:GNC 5 : if (conn->sock == PGINVALID_SOCKET)
81 : : {
33 alvherre@alvh.no-ip. 82 :UNC 0 : libpq_append_conn_error(cancelConn, "passed connection is not open");
83 : 0 : return (PGcancelConn *) cancelConn;
84 : : }
85 : :
86 : : /*
87 : : * Indicate that this connection is used to send a cancellation
88 : : */
33 alvherre@alvh.no-ip. 89 :GNC 5 : cancelConn->cancelRequest = true;
90 : :
91 [ - + ]: 5 : if (!pqCopyPGconn(conn, cancelConn))
33 alvherre@alvh.no-ip. 92 :UNC 0 : return (PGcancelConn *) cancelConn;
93 : :
94 : : /*
95 : : * Compute derived options
96 : : */
33 alvherre@alvh.no-ip. 97 [ - + ]:GNC 5 : if (!pqConnectOptions2(cancelConn))
33 alvherre@alvh.no-ip. 98 :UNC 0 : return (PGcancelConn *) cancelConn;
99 : :
100 : : /*
101 : : * Copy cancellation token data from the original connection
102 : : */
33 alvherre@alvh.no-ip. 103 :GNC 5 : cancelConn->be_pid = conn->be_pid;
104 : 5 : cancelConn->be_key = conn->be_key;
105 : :
106 : : /*
107 : : * Cancel requests should not iterate over all possible hosts. The request
108 : : * needs to be sent to the exact host and address that the original
109 : : * connection used. So we manually create the host and address arrays with
110 : : * a single element after freeing the host array that we generated from
111 : : * the connection options.
112 : : */
113 : 5 : pqReleaseConnHosts(cancelConn);
114 : 5 : cancelConn->nconnhost = 1;
115 : 5 : cancelConn->naddr = 1;
116 : :
117 : 5 : cancelConn->connhost = calloc(cancelConn->nconnhost, sizeof(pg_conn_host));
118 [ - + ]: 5 : if (!cancelConn->connhost)
33 alvherre@alvh.no-ip. 119 :UNC 0 : goto oom_error;
120 : :
33 alvherre@alvh.no-ip. 121 :GNC 5 : originalHost = conn->connhost[conn->whichhost];
122 [ + - ]: 5 : if (originalHost.host)
123 : : {
124 : 5 : cancelConn->connhost[0].host = strdup(originalHost.host);
125 [ - + ]: 5 : if (!cancelConn->connhost[0].host)
33 alvherre@alvh.no-ip. 126 :UNC 0 : goto oom_error;
127 : : }
33 alvherre@alvh.no-ip. 128 [ - + ]:GNC 5 : if (originalHost.hostaddr)
129 : : {
33 alvherre@alvh.no-ip. 130 :UNC 0 : cancelConn->connhost[0].hostaddr = strdup(originalHost.hostaddr);
131 [ # # ]: 0 : if (!cancelConn->connhost[0].hostaddr)
132 : 0 : goto oom_error;
133 : : }
33 alvherre@alvh.no-ip. 134 [ + - ]:GNC 5 : if (originalHost.port)
135 : : {
136 : 5 : cancelConn->connhost[0].port = strdup(originalHost.port);
137 [ - + ]: 5 : if (!cancelConn->connhost[0].port)
33 alvherre@alvh.no-ip. 138 :UNC 0 : goto oom_error;
139 : : }
33 alvherre@alvh.no-ip. 140 [ - + ]:GNC 5 : if (originalHost.password)
141 : : {
33 alvherre@alvh.no-ip. 142 :UNC 0 : cancelConn->connhost[0].password = strdup(originalHost.password);
143 [ # # ]: 0 : if (!cancelConn->connhost[0].password)
144 : 0 : goto oom_error;
145 : : }
146 : :
33 alvherre@alvh.no-ip. 147 :GNC 5 : cancelConn->addr = calloc(cancelConn->naddr, sizeof(AddrInfo));
148 [ - + ]: 5 : if (!cancelConn->connhost)
33 alvherre@alvh.no-ip. 149 :UNC 0 : goto oom_error;
150 : :
33 alvherre@alvh.no-ip. 151 :GNC 5 : cancelConn->addr[0].addr = conn->raddr;
152 : 5 : cancelConn->addr[0].family = conn->raddr.addr.ss_family;
153 : :
154 : 5 : cancelConn->status = CONNECTION_ALLOCATED;
155 : 5 : return (PGcancelConn *) cancelConn;
156 : :
33 alvherre@alvh.no-ip. 157 :UNC 0 : oom_error:
158 : 0 : conn->status = CONNECTION_BAD;
159 : 0 : libpq_append_conn_error(cancelConn, "out of memory");
160 : 0 : return (PGcancelConn *) cancelConn;
161 : : }
162 : :
163 : :
164 : : /*
165 : : * PQcancelBlocking
166 : : *
167 : : * Send a cancellation request in a blocking fashion.
168 : : * Returns 1 if successful 0 if not.
169 : : */
170 : : int
33 alvherre@alvh.no-ip. 171 :GNC 1 : PQcancelBlocking(PGcancelConn *cancelConn)
172 : : {
173 [ - + ]: 1 : if (!PQcancelStart(cancelConn))
33 alvherre@alvh.no-ip. 174 :UNC 0 : return 0;
33 alvherre@alvh.no-ip. 175 :GNC 1 : return pqConnectDBComplete(&cancelConn->conn);
176 : : }
177 : :
178 : : /*
179 : : * PQcancelStart
180 : : *
181 : : * Starts sending a cancellation request in a non-blocking fashion. Returns
182 : : * 1 if successful 0 if not.
183 : : */
184 : : int
185 : 6 : PQcancelStart(PGcancelConn *cancelConn)
186 : : {
187 [ + - - + ]: 6 : if (!cancelConn || cancelConn->conn.status == CONNECTION_BAD)
33 alvherre@alvh.no-ip. 188 :UNC 0 : return 0;
189 : :
33 alvherre@alvh.no-ip. 190 [ - + ]:GNC 6 : if (cancelConn->conn.status != CONNECTION_ALLOCATED)
191 : : {
33 alvherre@alvh.no-ip. 192 :UNC 0 : libpq_append_conn_error(&cancelConn->conn,
193 : : "cancel request is already being sent on this connection");
194 : 0 : cancelConn->conn.status = CONNECTION_BAD;
195 : 0 : return 0;
196 : : }
197 : :
33 alvherre@alvh.no-ip. 198 :GNC 6 : return pqConnectDBStart(&cancelConn->conn);
199 : : }
200 : :
201 : : /*
202 : : * PQcancelPoll
203 : : *
204 : : * Poll a cancel connection. For usage details see PQconnectPoll.
205 : : */
206 : : PostgresPollingStatusType
207 : 12 : PQcancelPoll(PGcancelConn *cancelConn)
208 : : {
209 : 12 : PGconn *conn = &cancelConn->conn;
210 : : int n;
211 : :
212 : : /*
213 : : * We leave most of the connection establishement to PQconnectPoll, since
214 : : * it's very similar to normal connection establishment. But once we get
215 : : * to the CONNECTION_AWAITING_RESPONSE we need to start doing our own
216 : : * thing.
217 : : */
218 [ + + ]: 12 : if (conn->status != CONNECTION_AWAITING_RESPONSE)
219 : : {
220 : 6 : return PQconnectPoll(conn);
221 : : }
222 : :
223 : : /*
224 : : * At this point we are waiting on the server to close the connection,
225 : : * which is its way of communicating that the cancel has been handled.
226 : : */
227 : :
228 : 6 : n = pqReadData(conn);
229 : :
230 [ - + ]: 6 : if (n == 0)
33 alvherre@alvh.no-ip. 231 :UNC 0 : return PGRES_POLLING_READING;
232 : :
233 : : #ifndef WIN32
234 : :
235 : : /*
236 : : * If we receive an error report it, but only if errno is non-zero.
237 : : * Otherwise we assume it's an EOF, which is what we expect from the
238 : : * server.
239 : : *
240 : : * We skip this for Windows, because Windows is a bit special in its EOF
241 : : * behaviour for TCP. Sometimes it will error with an ECONNRESET when
242 : : * there is a clean connection closure. See these threads for details:
243 : : * https://www.postgresql.org/message-id/flat/90b34057-4176-7bb0-0dbb-9822a5f6425b%40greiz-reinsdorf.de
244 : : *
245 : : * https://www.postgresql.org/message-id/flat/CA%2BhUKG%2BOeoETZQ%3DQw5Ub5h3tmwQhBmDA%3DnuNO3KG%3DzWfUypFAw%40mail.gmail.com
246 : : *
247 : : * PQcancel ignores such errors and reports success for the cancellation
248 : : * anyway, so even if this is not always correct we do the same here.
249 : : */
33 alvherre@alvh.no-ip. 250 [ + - - + ]:GNC 6 : if (n < 0 && errno != 0)
251 : : {
33 alvherre@alvh.no-ip. 252 :UNC 0 : conn->status = CONNECTION_BAD;
253 : 0 : return PGRES_POLLING_FAILED;
254 : : }
255 : : #endif
256 : :
257 : : /*
258 : : * We don't expect any data, only connection closure. So if we strangely
259 : : * do receive some data we consider that an error.
260 : : */
33 alvherre@alvh.no-ip. 261 [ - + ]:GNC 6 : if (n > 0)
262 : : {
33 alvherre@alvh.no-ip. 263 :UNC 0 : libpq_append_conn_error(conn, "received unexpected response from server");
264 : 0 : conn->status = CONNECTION_BAD;
265 : 0 : return PGRES_POLLING_FAILED;
266 : : }
267 : :
268 : : /*
269 : : * Getting here means that we received an EOF, which is what we were
270 : : * expecting -- the cancel request has completed.
271 : : */
33 alvherre@alvh.no-ip. 272 :GNC 6 : cancelConn->conn.status = CONNECTION_OK;
273 : 6 : resetPQExpBuffer(&conn->errorMessage);
274 : 6 : return PGRES_POLLING_OK;
275 : : }
276 : :
277 : : /*
278 : : * PQcancelStatus
279 : : *
280 : : * Get the status of a cancel connection.
281 : : */
282 : : ConnStatusType
283 : 2 : PQcancelStatus(const PGcancelConn *cancelConn)
284 : : {
285 : 2 : return PQstatus(&cancelConn->conn);
286 : : }
287 : :
288 : : /*
289 : : * PQcancelSocket
290 : : *
291 : : * Get the socket of the cancel connection.
292 : : */
293 : : int
294 : 7 : PQcancelSocket(const PGcancelConn *cancelConn)
295 : : {
296 : 7 : return PQsocket(&cancelConn->conn);
297 : : }
298 : :
299 : : /*
300 : : * PQcancelErrorMessage
301 : : *
302 : : * Get the socket of the cancel connection.
303 : : */
304 : : char *
33 alvherre@alvh.no-ip. 305 :UNC 0 : PQcancelErrorMessage(const PGcancelConn *cancelConn)
306 : : {
307 : 0 : return PQerrorMessage(&cancelConn->conn);
308 : : }
309 : :
310 : : /*
311 : : * PQcancelReset
312 : : *
313 : : * Resets the cancel connection, so it can be reused to send a new cancel
314 : : * request.
315 : : */
316 : : void
33 alvherre@alvh.no-ip. 317 :GNC 1 : PQcancelReset(PGcancelConn *cancelConn)
318 : : {
319 : 1 : pqClosePGconn(&cancelConn->conn);
320 : 1 : cancelConn->conn.status = CONNECTION_ALLOCATED;
321 : 1 : cancelConn->conn.whichhost = 0;
322 : 1 : cancelConn->conn.whichaddr = 0;
323 : 1 : cancelConn->conn.try_next_host = false;
324 : 1 : cancelConn->conn.try_next_addr = false;
325 : 1 : }
326 : :
327 : : /*
328 : : * PQcancelFinish
329 : : *
330 : : * Closes and frees the cancel connection.
331 : : */
332 : : void
333 : 5 : PQcancelFinish(PGcancelConn *cancelConn)
334 : : {
335 : 5 : PQfinish(&cancelConn->conn);
336 : 5 : }
337 : :
338 : : /*
339 : : * PQgetCancel: get a PGcancel structure corresponding to a connection.
340 : : *
341 : : * A copy is needed to be able to cancel a running query from a different
342 : : * thread. If the same structure is used all structure members would have
343 : : * to be individually locked (if the entire structure was locked, it would
344 : : * be impossible to cancel a synchronous query because the structure would
345 : : * have to stay locked for the duration of the query).
346 : : */
347 : : PGcancel *
76 348 : 188796 : PQgetCancel(PGconn *conn)
349 : : {
350 : : PGcancel *cancel;
351 : :
352 [ + + ]: 188796 : if (!conn)
353 : 22 : return NULL;
354 : :
355 [ - + ]: 188774 : if (conn->sock == PGINVALID_SOCKET)
76 alvherre@alvh.no-ip. 356 :UNC 0 : return NULL;
357 : :
76 alvherre@alvh.no-ip. 358 :GNC 188774 : cancel = malloc(sizeof(PGcancel));
359 [ - + ]: 188774 : if (cancel == NULL)
76 alvherre@alvh.no-ip. 360 :UNC 0 : return NULL;
361 : :
76 alvherre@alvh.no-ip. 362 :GNC 188774 : memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
363 : 188774 : cancel->be_pid = conn->be_pid;
364 : 188774 : cancel->be_key = conn->be_key;
365 : : /* We use -1 to indicate an unset connection option */
366 : 188774 : cancel->pgtcp_user_timeout = -1;
367 : 188774 : cancel->keepalives = -1;
368 : 188774 : cancel->keepalives_idle = -1;
369 : 188774 : cancel->keepalives_interval = -1;
370 : 188774 : cancel->keepalives_count = -1;
371 [ - + ]: 188774 : if (conn->pgtcp_user_timeout != NULL)
372 : : {
76 alvherre@alvh.no-ip. 373 [ # # ]:UNC 0 : if (!pqParseIntParam(conn->pgtcp_user_timeout,
374 : : &cancel->pgtcp_user_timeout,
375 : : conn, "tcp_user_timeout"))
376 : 0 : goto fail;
377 : : }
76 alvherre@alvh.no-ip. 378 [ - + ]:GNC 188774 : if (conn->keepalives != NULL)
379 : : {
76 alvherre@alvh.no-ip. 380 [ # # ]:UNC 0 : if (!pqParseIntParam(conn->keepalives,
381 : : &cancel->keepalives,
382 : : conn, "keepalives"))
383 : 0 : goto fail;
384 : : }
76 alvherre@alvh.no-ip. 385 [ - + ]:GNC 188774 : if (conn->keepalives_idle != NULL)
386 : : {
76 alvherre@alvh.no-ip. 387 [ # # ]:UNC 0 : if (!pqParseIntParam(conn->keepalives_idle,
388 : : &cancel->keepalives_idle,
389 : : conn, "keepalives_idle"))
390 : 0 : goto fail;
391 : : }
76 alvherre@alvh.no-ip. 392 [ - + ]:GNC 188774 : if (conn->keepalives_interval != NULL)
393 : : {
76 alvherre@alvh.no-ip. 394 [ # # ]:UNC 0 : if (!pqParseIntParam(conn->keepalives_interval,
395 : : &cancel->keepalives_interval,
396 : : conn, "keepalives_interval"))
397 : 0 : goto fail;
398 : : }
76 alvherre@alvh.no-ip. 399 [ - + ]:GNC 188774 : if (conn->keepalives_count != NULL)
400 : : {
76 alvherre@alvh.no-ip. 401 [ # # ]:UNC 0 : if (!pqParseIntParam(conn->keepalives_count,
402 : : &cancel->keepalives_count,
403 : : conn, "keepalives_count"))
404 : 0 : goto fail;
405 : : }
406 : :
76 alvherre@alvh.no-ip. 407 :GNC 188774 : return cancel;
408 : :
76 alvherre@alvh.no-ip. 409 :UNC 0 : fail:
410 : 0 : free(cancel);
411 : 0 : return NULL;
412 : : }
413 : :
414 : : /* PQfreeCancel: free a cancel structure */
415 : : void
76 alvherre@alvh.no-ip. 416 :GNC 188759 : PQfreeCancel(PGcancel *cancel)
417 : : {
418 : 188759 : free(cancel);
419 : 188759 : }
420 : :
421 : :
422 : : /*
423 : : * Sets an integer socket option on a TCP socket, if the provided value is
424 : : * not negative. Returns false if setsockopt fails for some reason.
425 : : *
426 : : * CAUTION: This needs to be signal safe, since it's used by PQcancel.
427 : : */
428 : : #if defined(TCP_USER_TIMEOUT) || !defined(WIN32)
429 : : static bool
76 alvherre@alvh.no-ip. 430 :UNC 0 : optional_setsockopt(int fd, int protoid, int optid, int value)
431 : : {
432 [ # # ]: 0 : if (value < 0)
433 : 0 : return true;
434 [ # # ]: 0 : if (setsockopt(fd, protoid, optid, (char *) &value, sizeof(value)) < 0)
435 : 0 : return false;
436 : 0 : return true;
437 : : }
438 : : #endif
439 : :
440 : :
441 : : /*
442 : : * PQcancel: old, non-encrypted, but signal-safe way of requesting query cancel
443 : : *
444 : : * The return value is true if the cancel request was successfully
445 : : * dispatched, false if not (in which case an error message is available).
446 : : * Note: successful dispatch is no guarantee that there will be any effect at
447 : : * the backend. The application must read the operation result as usual.
448 : : *
449 : : * On failure, an error message is stored in *errbuf, which must be of size
450 : : * errbufsize (recommended size is 256 bytes). *errbuf is not changed on
451 : : * success return.
452 : : *
453 : : * CAUTION: we want this routine to be safely callable from a signal handler
454 : : * (for example, an application might want to call it in a SIGINT handler).
455 : : * This means we cannot use any C library routine that might be non-reentrant.
456 : : * malloc/free are often non-reentrant, and anything that might call them is
457 : : * just as dangerous. We avoid sprintf here for that reason. Building up
458 : : * error messages with strcpy/strcat is tedious but should be quite safe.
459 : : * We also save/restore errno in case the signal handler support doesn't.
460 : : */
461 : : int
76 alvherre@alvh.no-ip. 462 :GNC 4 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
463 : : {
464 : 4 : int save_errno = SOCK_ERRNO;
465 : 4 : pgsocket tmpsock = PGINVALID_SOCKET;
466 : : int maxlen;
467 : : struct
468 : : {
469 : : uint32 packetlen;
470 : : CancelRequestPacket cp;
471 : : } crp;
472 : :
473 [ - + ]: 4 : if (!cancel)
474 : : {
76 alvherre@alvh.no-ip. 475 :UNC 0 : strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
476 : : /* strlcpy probably doesn't change errno, but be paranoid */
477 : 0 : SOCK_ERRNO_SET(save_errno);
478 : 0 : return false;
479 : : }
480 : :
481 : : /*
482 : : * We need to open a temporary connection to the postmaster. Do this with
483 : : * only kernel calls.
484 : : */
76 alvherre@alvh.no-ip. 485 [ - + ]:GNC 4 : if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
486 : : {
76 alvherre@alvh.no-ip. 487 :UNC 0 : strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
488 : 0 : goto cancel_errReturn;
489 : : }
490 : :
491 : : /*
492 : : * Since this connection will only be used to send a single packet of
493 : : * data, we don't need NODELAY. We also don't set the socket to
494 : : * nonblocking mode, because the API definition of PQcancel requires the
495 : : * cancel to be sent in a blocking way.
496 : : *
497 : : * We do set socket options related to keepalives and other TCP timeouts.
498 : : * This ensures that this function does not block indefinitely when
499 : : * reasonable keepalive and timeout settings have been provided.
500 : : */
76 alvherre@alvh.no-ip. 501 [ + - ]:GNC 4 : if (cancel->raddr.addr.ss_family != AF_UNIX &&
76 alvherre@alvh.no-ip. 502 [ # # ]:UNC 0 : cancel->keepalives != 0)
503 : : {
504 : : #ifndef WIN32
505 [ # # ]: 0 : if (!optional_setsockopt(tmpsock, SOL_SOCKET, SO_KEEPALIVE, 1))
506 : : {
507 : 0 : strlcpy(errbuf, "PQcancel() -- setsockopt(SO_KEEPALIVE) failed: ", errbufsize);
508 : 0 : goto cancel_errReturn;
509 : : }
510 : :
511 : : #ifdef PG_TCP_KEEPALIVE_IDLE
512 : : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
513 : : cancel->keepalives_idle))
514 : : {
515 : : strlcpy(errbuf, "PQcancel() -- setsockopt(" PG_TCP_KEEPALIVE_IDLE_STR ") failed: ", errbufsize);
516 : : goto cancel_errReturn;
517 : : }
518 : : #endif
519 : :
520 : : #ifdef TCP_KEEPINTVL
521 : : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPINTVL,
522 : : cancel->keepalives_interval))
523 : : {
524 : : strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPINTVL) failed: ", errbufsize);
525 : : goto cancel_errReturn;
526 : : }
527 : : #endif
528 : :
529 : : #ifdef TCP_KEEPCNT
530 : : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPCNT,
531 : : cancel->keepalives_count))
532 : : {
533 : : strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPCNT) failed: ", errbufsize);
534 : : goto cancel_errReturn;
535 : : }
536 : : #endif
537 : :
538 : : #else /* WIN32 */
539 : :
540 : : #ifdef SIO_KEEPALIVE_VALS
541 : : if (!pqSetKeepalivesWin32(tmpsock,
542 : : cancel->keepalives_idle,
543 : : cancel->keepalives_interval))
544 : : {
545 : : strlcpy(errbuf, "PQcancel() -- WSAIoctl(SIO_KEEPALIVE_VALS) failed: ", errbufsize);
546 : : goto cancel_errReturn;
547 : : }
548 : : #endif /* SIO_KEEPALIVE_VALS */
549 : : #endif /* WIN32 */
550 : :
551 : : /* TCP_USER_TIMEOUT works the same way on Unix and Windows */
552 : : #ifdef TCP_USER_TIMEOUT
553 : : if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_USER_TIMEOUT,
554 : : cancel->pgtcp_user_timeout))
555 : : {
556 : : strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_USER_TIMEOUT) failed: ", errbufsize);
557 : : goto cancel_errReturn;
558 : : }
559 : : #endif
560 : : }
561 : :
76 alvherre@alvh.no-ip. 562 :GNC 4 : retry3:
563 [ - + ]: 4 : if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
564 : : cancel->raddr.salen) < 0)
565 : : {
76 alvherre@alvh.no-ip. 566 [ # # ]:UNC 0 : if (SOCK_ERRNO == EINTR)
567 : : /* Interrupted system call - we'll just try again */
568 : 0 : goto retry3;
569 : 0 : strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
570 : 0 : goto cancel_errReturn;
571 : : }
572 : :
573 : : /* Create and send the cancel request packet. */
574 : :
76 alvherre@alvh.no-ip. 575 :GNC 4 : crp.packetlen = pg_hton32((uint32) sizeof(crp));
576 : 4 : crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
577 : 4 : crp.cp.backendPID = pg_hton32(cancel->be_pid);
578 : 4 : crp.cp.cancelAuthCode = pg_hton32(cancel->be_key);
579 : :
580 : 4 : retry4:
581 [ + - ]: 4 : if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
582 : : {
76 alvherre@alvh.no-ip. 583 [ # # ]:UNC 0 : if (SOCK_ERRNO == EINTR)
584 : : /* Interrupted system call - we'll just try again */
585 : 0 : goto retry4;
586 : 0 : strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
587 : 0 : goto cancel_errReturn;
588 : : }
589 : :
590 : : /*
591 : : * Wait for the postmaster to close the connection, which indicates that
592 : : * it's processed the request. Without this delay, we might issue another
593 : : * command only to find that our cancel zaps that command instead of the
594 : : * one we thought we were canceling. Note we don't actually expect this
595 : : * read to obtain any data, we are just waiting for EOF to be signaled.
596 : : */
76 alvherre@alvh.no-ip. 597 :GNC 4 : retry5:
598 [ - + ]: 4 : if (recv(tmpsock, (char *) &crp, 1, 0) < 0)
599 : : {
76 alvherre@alvh.no-ip. 600 [ # # ]:UNC 0 : if (SOCK_ERRNO == EINTR)
601 : : /* Interrupted system call - we'll just try again */
602 : 0 : goto retry5;
603 : : /* we ignore other error conditions */
604 : : }
605 : :
606 : : /* All done */
76 alvherre@alvh.no-ip. 607 :GNC 4 : closesocket(tmpsock);
608 : 4 : SOCK_ERRNO_SET(save_errno);
609 : 4 : return true;
610 : :
76 alvherre@alvh.no-ip. 611 :UNC 0 : cancel_errReturn:
612 : :
613 : : /*
614 : : * Make sure we don't overflow the error buffer. Leave space for the \n at
615 : : * the end, and for the terminating zero.
616 : : */
617 : 0 : maxlen = errbufsize - strlen(errbuf) - 2;
618 [ # # ]: 0 : if (maxlen >= 0)
619 : : {
620 : : /*
621 : : * We can't invoke strerror here, since it's not signal-safe. Settle
622 : : * for printing the decimal value of errno. Even that has to be done
623 : : * the hard way.
624 : : */
625 : 0 : int val = SOCK_ERRNO;
626 : : char buf[32];
627 : : char *bufp;
628 : :
629 : 0 : bufp = buf + sizeof(buf) - 1;
630 : 0 : *bufp = '\0';
631 : : do
632 : : {
633 : 0 : *(--bufp) = (val % 10) + '0';
634 : 0 : val /= 10;
635 [ # # ]: 0 : } while (val > 0);
636 : 0 : bufp -= 6;
637 : 0 : memcpy(bufp, "error ", 6);
638 : 0 : strncat(errbuf, bufp, maxlen);
639 : 0 : strcat(errbuf, "\n");
640 : : }
641 [ # # ]: 0 : if (tmpsock != PGINVALID_SOCKET)
642 : 0 : closesocket(tmpsock);
643 : 0 : SOCK_ERRNO_SET(save_errno);
644 : 0 : return false;
645 : : }
646 : :
647 : : /*
648 : : * PQrequestCancel: old, not thread-safe function for requesting query cancel
649 : : *
650 : : * Returns true if able to send the cancel request, false if not.
651 : : *
652 : : * On failure, the error message is saved in conn->errorMessage; this means
653 : : * that this can't be used when there might be other active operations on
654 : : * the connection object.
655 : : *
656 : : * NOTE: error messages will be cut off at the current size of the
657 : : * error message buffer, since we dare not try to expand conn->errorMessage!
658 : : */
659 : : int
76 alvherre@alvh.no-ip. 660 :GNC 1 : PQrequestCancel(PGconn *conn)
661 : : {
662 : : int r;
663 : : PGcancel *cancel;
664 : :
665 : : /* Check we have an open connection */
666 [ - + ]: 1 : if (!conn)
76 alvherre@alvh.no-ip. 667 :UNC 0 : return false;
668 : :
76 alvherre@alvh.no-ip. 669 [ - + ]:GNC 1 : if (conn->sock == PGINVALID_SOCKET)
670 : : {
76 alvherre@alvh.no-ip. 671 :UNC 0 : strlcpy(conn->errorMessage.data,
672 : : "PQrequestCancel() -- connection is not open\n",
673 : : conn->errorMessage.maxlen);
674 : 0 : conn->errorMessage.len = strlen(conn->errorMessage.data);
675 : 0 : conn->errorReported = 0;
676 : :
677 : 0 : return false;
678 : : }
679 : :
76 alvherre@alvh.no-ip. 680 :GNC 1 : cancel = PQgetCancel(conn);
681 [ + - ]: 1 : if (cancel)
682 : : {
683 : 1 : r = PQcancel(cancel, conn->errorMessage.data,
684 : 1 : conn->errorMessage.maxlen);
685 : 1 : PQfreeCancel(cancel);
686 : : }
687 : : else
688 : : {
76 alvherre@alvh.no-ip. 689 :UNC 0 : strlcpy(conn->errorMessage.data, "out of memory",
690 : : conn->errorMessage.maxlen);
691 : 0 : r = false;
692 : : }
693 : :
76 alvherre@alvh.no-ip. 694 [ - + ]:GNC 1 : if (!r)
695 : : {
76 alvherre@alvh.no-ip. 696 :UNC 0 : conn->errorMessage.len = strlen(conn->errorMessage.data);
697 : 0 : conn->errorReported = 0;
698 : : }
699 : :
76 alvherre@alvh.no-ip. 700 :GNC 1 : return r;
701 : : }
|