Age Owner Branch data TLA 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 *
543 alvherre@alvh.no-ip. 68 :CBC 7 : PQcancelCreate(PGconn *conn)
69 : : {
70 : 7 : PGconn *cancelConn = pqMakeEmptyPGconn();
71 : : pg_conn_host originalHost;
72 : :
73 [ - + ]: 7 : if (cancelConn == NULL)
543 alvherre@alvh.no-ip. 74 :UBC 0 : return NULL;
75 : :
76 : : /* Check we have an open connection */
543 alvherre@alvh.no-ip. 77 [ - + ]:CBC 7 : if (!conn)
78 : : {
450 peter@eisentraut.org 79 :UBC 0 : libpq_append_conn_error(cancelConn, "connection pointer is NULL");
543 alvherre@alvh.no-ip. 80 : 0 : return (PGcancelConn *) cancelConn;
81 : : }
82 : :
543 alvherre@alvh.no-ip. 83 [ - + ]:CBC 7 : if (conn->sock == PGINVALID_SOCKET)
84 : : {
450 peter@eisentraut.org 85 :UBC 0 : libpq_append_conn_error(cancelConn, "connection not open");
543 alvherre@alvh.no-ip. 86 : 0 : return (PGcancelConn *) cancelConn;
87 : : }
88 : :
89 : : /* Check that we have received a cancellation key */
157 heikki.linnakangas@i 90 [ - + ]:CBC 7 : if (conn->be_cancel_key_len == 0)
91 : : {
157 heikki.linnakangas@i 92 :UBC 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 : : */
543 alvherre@alvh.no-ip. 99 :CBC 7 : cancelConn->cancelRequest = true;
100 : :
101 [ - + ]: 7 : if (!pqCopyPGconn(conn, cancelConn))
543 alvherre@alvh.no-ip. 102 :UBC 0 : return (PGcancelConn *) cancelConn;
103 : :
104 : : /*
105 : : * Compute derived options
106 : : */
543 alvherre@alvh.no-ip. 107 [ - + ]:CBC 7 : if (!pqConnectOptions2(cancelConn))
543 alvherre@alvh.no-ip. 108 :UBC 0 : return (PGcancelConn *) cancelConn;
109 : :
110 : : /*
111 : : * Copy cancellation token data from the original connection
112 : : */
543 alvherre@alvh.no-ip. 113 :CBC 7 : cancelConn->be_pid = conn->be_pid;
157 heikki.linnakangas@i 114 [ + - ]: 7 : if (conn->be_cancel_key != NULL)
115 : : {
116 : 7 : cancelConn->be_cancel_key = malloc(conn->be_cancel_key_len);
81 dgustafsson@postgres 117 [ - + ]: 7 : if (cancelConn->be_cancel_key == NULL)
157 heikki.linnakangas@i 118 :UBC 0 : goto oom_error;
157 heikki.linnakangas@i 119 :CBC 7 : memcpy(cancelConn->be_cancel_key, conn->be_cancel_key, conn->be_cancel_key_len);
120 : : }
121 : 7 : cancelConn->be_cancel_key_len = conn->be_cancel_key_len;
122 : 7 : cancelConn->pversion = conn->pversion;
123 : :
124 : : /*
125 : : * Cancel requests should not iterate over all possible hosts. The request
126 : : * needs to be sent to the exact host and address that the original
127 : : * connection used. So we manually create the host and address arrays with
128 : : * a single element after freeing the host array that we generated from
129 : : * the connection options.
130 : : */
543 alvherre@alvh.no-ip. 131 : 7 : pqReleaseConnHosts(cancelConn);
132 : 7 : cancelConn->nconnhost = 1;
133 : 7 : cancelConn->naddr = 1;
134 : :
135 : 7 : cancelConn->connhost = calloc(cancelConn->nconnhost, sizeof(pg_conn_host));
136 [ - + ]: 7 : if (!cancelConn->connhost)
543 alvherre@alvh.no-ip. 137 :UBC 0 : goto oom_error;
138 : :
543 alvherre@alvh.no-ip. 139 :CBC 7 : originalHost = conn->connhost[conn->whichhost];
66 tgl@sss.pgh.pa.us 140 : 7 : cancelConn->connhost[0].type = originalHost.type;
543 alvherre@alvh.no-ip. 141 [ + - ]: 7 : if (originalHost.host)
142 : : {
143 : 7 : cancelConn->connhost[0].host = strdup(originalHost.host);
144 [ - + ]: 7 : if (!cancelConn->connhost[0].host)
543 alvherre@alvh.no-ip. 145 :UBC 0 : goto oom_error;
146 : : }
543 alvherre@alvh.no-ip. 147 [ - + ]:CBC 7 : if (originalHost.hostaddr)
148 : : {
543 alvherre@alvh.no-ip. 149 :UBC 0 : cancelConn->connhost[0].hostaddr = strdup(originalHost.hostaddr);
150 [ # # ]: 0 : if (!cancelConn->connhost[0].hostaddr)
151 : 0 : goto oom_error;
152 : : }
543 alvherre@alvh.no-ip. 153 [ + - ]:CBC 7 : if (originalHost.port)
154 : : {
155 : 7 : cancelConn->connhost[0].port = strdup(originalHost.port);
156 [ - + ]: 7 : if (!cancelConn->connhost[0].port)
543 alvherre@alvh.no-ip. 157 :UBC 0 : goto oom_error;
158 : : }
543 alvherre@alvh.no-ip. 159 [ - + ]:CBC 7 : if (originalHost.password)
160 : : {
543 alvherre@alvh.no-ip. 161 :UBC 0 : cancelConn->connhost[0].password = strdup(originalHost.password);
162 [ # # ]: 0 : if (!cancelConn->connhost[0].password)
163 : 0 : goto oom_error;
164 : : }
165 : :
543 alvherre@alvh.no-ip. 166 :CBC 7 : cancelConn->addr = calloc(cancelConn->naddr, sizeof(AddrInfo));
467 dgustafsson@postgres 167 [ - + ]: 7 : if (!cancelConn->addr)
543 alvherre@alvh.no-ip. 168 :UBC 0 : goto oom_error;
169 : :
543 alvherre@alvh.no-ip. 170 :CBC 7 : cancelConn->addr[0].addr = conn->raddr;
171 : 7 : cancelConn->addr[0].family = conn->raddr.addr.ss_family;
172 : :
173 : 7 : cancelConn->status = CONNECTION_ALLOCATED;
174 : 7 : return (PGcancelConn *) cancelConn;
175 : :
543 alvherre@alvh.no-ip. 176 :UBC 0 : oom_error:
432 177 : 0 : cancelConn->status = CONNECTION_BAD;
543 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
543 alvherre@alvh.no-ip. 190 :CBC 2 : PQcancelBlocking(PGcancelConn *cancelConn)
191 : : {
192 [ - + ]: 2 : if (!PQcancelStart(cancelConn))
543 alvherre@alvh.no-ip. 193 :UBC 0 : return 0;
543 alvherre@alvh.no-ip. 194 :CBC 2 : return pqConnectDBComplete(&cancelConn->conn);
195 : : }
196 : :
197 : : /*
198 : : * PQcancelStart
199 : : *
200 : : * Starts sending a cancellation request in a non-blocking fashion. Returns
201 : : * 1 if successful 0 if not.
202 : : */
203 : : int
204 : 9 : PQcancelStart(PGcancelConn *cancelConn)
205 : : {
206 [ + - - + ]: 9 : if (!cancelConn || cancelConn->conn.status == CONNECTION_BAD)
543 alvherre@alvh.no-ip. 207 :UBC 0 : return 0;
208 : :
543 alvherre@alvh.no-ip. 209 [ - + ]:CBC 9 : if (cancelConn->conn.status != CONNECTION_ALLOCATED)
210 : : {
543 alvherre@alvh.no-ip. 211 :UBC 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 : :
543 alvherre@alvh.no-ip. 217 :CBC 9 : return pqConnectDBStart(&cancelConn->conn);
218 : : }
219 : :
220 : : /*
221 : : * PQcancelPoll
222 : : *
223 : : * Poll a cancel connection. For usage details see PQconnectPoll.
224 : : */
225 : : PostgresPollingStatusType
226 : 18 : PQcancelPoll(PGcancelConn *cancelConn)
227 : : {
228 : 18 : PGconn *conn = &cancelConn->conn;
229 : : int n;
230 : :
231 : : /*
232 : : * We leave most of the connection establishment to PQconnectPoll, since
233 : : * it's very similar to normal connection establishment. But once we get
234 : : * to the CONNECTION_AWAITING_RESPONSE we need to start doing our own
235 : : * thing.
236 : : */
237 [ + + ]: 18 : if (conn->status != CONNECTION_AWAITING_RESPONSE)
238 : : {
239 : 9 : return PQconnectPoll(conn);
240 : : }
241 : :
242 : : /*
243 : : * At this point we are waiting on the server to close the connection,
244 : : * which is its way of communicating that the cancel has been handled.
245 : : */
246 : :
247 : 9 : n = pqReadData(conn);
248 : :
249 [ - + ]: 9 : if (n == 0)
543 alvherre@alvh.no-ip. 250 :UBC 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 : : */
543 alvherre@alvh.no-ip. 269 [ + - - + ]:CBC 9 : if (n < 0 && errno != 0)
270 : : {
543 alvherre@alvh.no-ip. 271 :UBC 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 : : */
543 alvherre@alvh.no-ip. 280 [ - + ]:CBC 9 : if (n > 0)
281 : : {
450 peter@eisentraut.org 282 :UBC 0 : libpq_append_conn_error(conn, "unexpected response from server");
543 alvherre@alvh.no-ip. 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 : : */
543 alvherre@alvh.no-ip. 291 :CBC 9 : cancelConn->conn.status = CONNECTION_OK;
292 : 9 : resetPQExpBuffer(&conn->errorMessage);
293 : 9 : return PGRES_POLLING_OK;
294 : : }
295 : :
296 : : /*
297 : : * PQcancelStatus
298 : : *
299 : : * Get the status of a cancel connection.
300 : : */
301 : : ConnStatusType
302 : 4 : PQcancelStatus(const PGcancelConn *cancelConn)
303 : : {
304 : 4 : return PQstatus(&cancelConn->conn);
305 : : }
306 : :
307 : : /*
308 : : * PQcancelSocket
309 : : *
310 : : * Get the socket of the cancel connection.
311 : : */
312 : : int
313 : 11 : PQcancelSocket(const PGcancelConn *cancelConn)
314 : : {
315 : 11 : return PQsocket(&cancelConn->conn);
316 : : }
317 : :
318 : : /*
319 : : * PQcancelErrorMessage
320 : : *
321 : : * Returns the error message most recently generated by an operation on the
322 : : * cancel connection.
323 : : */
324 : : char *
543 alvherre@alvh.no-ip. 325 :UBC 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
543 alvherre@alvh.no-ip. 337 :CBC 2 : PQcancelReset(PGcancelConn *cancelConn)
338 : : {
339 : 2 : pqClosePGconn(&cancelConn->conn);
340 : 2 : cancelConn->conn.status = CONNECTION_ALLOCATED;
341 : 2 : cancelConn->conn.whichhost = 0;
342 : 2 : cancelConn->conn.whichaddr = 0;
343 : 2 : cancelConn->conn.try_next_host = false;
344 : 2 : cancelConn->conn.try_next_addr = false;
345 : 2 : }
346 : :
347 : : /*
348 : : * PQcancelFinish
349 : : *
350 : : * Closes and frees the cancel connection.
351 : : */
352 : : void
353 : 7 : PQcancelFinish(PGcancelConn *cancelConn)
354 : : {
355 : 7 : PQfinish(&cancelConn->conn);
356 : 7 : }
357 : :
358 : : /*
359 : : * PQgetCancel: get a PGcancel structure corresponding to a connection.
360 : : *
361 : : * A copy is needed to be able to cancel a running query from a different
362 : : * thread. If the same structure is used all structure members would have
363 : : * to be individually locked (if the entire structure was locked, it would
364 : : * be impossible to cancel a synchronous query because the structure would
365 : : * have to stay locked for the duration of the query).
366 : : */
367 : : PGcancel *
586 368 : 207581 : PQgetCancel(PGconn *conn)
369 : : {
370 : : PGcancel *cancel;
371 : : int cancel_req_len;
372 : : CancelRequestPacket *req;
373 : :
374 [ + + ]: 207581 : if (!conn)
375 : 22 : return NULL;
376 : :
377 [ - + ]: 207559 : if (conn->sock == PGINVALID_SOCKET)
586 alvherre@alvh.no-ip. 378 :UBC 0 : return NULL;
379 : :
380 : : /* Check that we have received a cancellation key */
157 heikki.linnakangas@i 381 [ - + ]:CBC 207559 : 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 : : */
36 heikki.linnakangas@i 398 :UBC 0 : return calloc(1, sizeof(PGcancel));
399 : : }
400 : :
157 heikki.linnakangas@i 401 :CBC 207559 : cancel_req_len = offsetof(CancelRequestPacket, cancelAuthCode) + conn->be_cancel_key_len;
402 : 207559 : cancel = malloc(offsetof(PGcancel, cancel_req) + cancel_req_len);
586 alvherre@alvh.no-ip. 403 [ - + ]: 207559 : if (cancel == NULL)
586 alvherre@alvh.no-ip. 404 :UBC 0 : return NULL;
405 : :
586 alvherre@alvh.no-ip. 406 :CBC 207559 : memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
407 : :
408 : : /* We use -1 to indicate an unset connection option */
409 : 207559 : cancel->pgtcp_user_timeout = -1;
410 : 207559 : cancel->keepalives = -1;
411 : 207559 : cancel->keepalives_idle = -1;
412 : 207559 : cancel->keepalives_interval = -1;
413 : 207559 : cancel->keepalives_count = -1;
414 [ - + ]: 207559 : if (conn->pgtcp_user_timeout != NULL)
415 : : {
586 alvherre@alvh.no-ip. 416 [ # # ]:UBC 0 : if (!pqParseIntParam(conn->pgtcp_user_timeout,
417 : : &cancel->pgtcp_user_timeout,
418 : : conn, "tcp_user_timeout"))
419 : 0 : goto fail;
420 : : }
586 alvherre@alvh.no-ip. 421 [ - + ]:CBC 207559 : if (conn->keepalives != NULL)
422 : : {
586 alvherre@alvh.no-ip. 423 [ # # ]:UBC 0 : if (!pqParseIntParam(conn->keepalives,
424 : : &cancel->keepalives,
425 : : conn, "keepalives"))
426 : 0 : goto fail;
427 : : }
586 alvherre@alvh.no-ip. 428 [ - + ]:CBC 207559 : if (conn->keepalives_idle != NULL)
429 : : {
586 alvherre@alvh.no-ip. 430 [ # # ]:UBC 0 : if (!pqParseIntParam(conn->keepalives_idle,
431 : : &cancel->keepalives_idle,
432 : : conn, "keepalives_idle"))
433 : 0 : goto fail;
434 : : }
586 alvherre@alvh.no-ip. 435 [ - + ]:CBC 207559 : if (conn->keepalives_interval != NULL)
436 : : {
586 alvherre@alvh.no-ip. 437 [ # # ]:UBC 0 : if (!pqParseIntParam(conn->keepalives_interval,
438 : : &cancel->keepalives_interval,
439 : : conn, "keepalives_interval"))
440 : 0 : goto fail;
441 : : }
586 alvherre@alvh.no-ip. 442 [ - + ]:CBC 207559 : if (conn->keepalives_count != NULL)
443 : : {
586 alvherre@alvh.no-ip. 444 [ # # ]:UBC 0 : if (!pqParseIntParam(conn->keepalives_count,
445 : : &cancel->keepalives_count,
446 : : conn, "keepalives_count"))
447 : 0 : goto fail;
448 : : }
449 : :
157 heikki.linnakangas@i 450 :CBC 207559 : req = (CancelRequestPacket *) &cancel->cancel_req;
451 : 207559 : req->cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
452 : 207559 : req->backendPID = pg_hton32(conn->be_pid);
453 : 207559 : memcpy(req->cancelAuthCode, conn->be_cancel_key, conn->be_cancel_key_len);
454 : : /* include the length field itself in the length */
455 : 207559 : cancel->cancel_pkt_len = pg_hton32(cancel_req_len + 4);
456 : :
586 alvherre@alvh.no-ip. 457 : 207559 : return cancel;
458 : :
586 alvherre@alvh.no-ip. 459 :UBC 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
157 heikki.linnakangas@i 472 :CBC 9 : PQsendCancelRequest(PGconn *cancelConn)
473 : : {
474 : : CancelRequestPacket req;
475 : :
476 : : /* Start the message. */
477 [ - + ]: 9 : if (pqPutMsgStart(0, cancelConn))
157 heikki.linnakangas@i 478 :UBC 0 : return STATUS_ERROR;
479 : :
480 : : /* Send the message body. */
157 heikki.linnakangas@i 481 :CBC 9 : memset(&req, 0, offsetof(CancelRequestPacket, cancelAuthCode));
482 : 9 : req.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
483 : 9 : req.backendPID = pg_hton32(cancelConn->be_pid);
121 484 [ - + ]: 9 : if (pqPutnchar(&req, offsetof(CancelRequestPacket, cancelAuthCode), cancelConn))
157 heikki.linnakangas@i 485 :UBC 0 : return STATUS_ERROR;
157 heikki.linnakangas@i 486 [ - + ]:CBC 9 : if (pqPutnchar(cancelConn->be_cancel_key, cancelConn->be_cancel_key_len, cancelConn))
157 heikki.linnakangas@i 487 :UBC 0 : return STATUS_ERROR;
488 : :
489 : : /* Finish the message. */
157 heikki.linnakangas@i 490 [ - + ]:CBC 9 : if (pqPutMsgEnd(cancelConn))
157 heikki.linnakangas@i 491 :UBC 0 : return STATUS_ERROR;
492 : :
493 : : /* Flush to ensure backend gets it. */
157 heikki.linnakangas@i 494 [ - + ]:CBC 9 : if (pqFlush(cancelConn))
157 heikki.linnakangas@i 495 :UBC 0 : return STATUS_ERROR;
496 : :
157 heikki.linnakangas@i 497 :CBC 9 : return STATUS_OK;
498 : : }
499 : :
500 : : /* PQfreeCancel: free a cancel structure */
501 : : void
586 alvherre@alvh.no-ip. 502 : 207545 : PQfreeCancel(PGcancel *cancel)
503 : : {
504 : 207545 : free(cancel);
505 : 207545 : }
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
586 alvherre@alvh.no-ip. 516 :UBC 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
586 alvherre@alvh.no-ip. 548 :CBC 7 : PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
549 : : {
550 : 7 : int save_errno = SOCK_ERRNO;
551 : 7 : pgsocket tmpsock = PGINVALID_SOCKET;
552 : : int maxlen;
553 : : char recvbuf;
554 : : int cancel_pkt_len;
555 : :
556 [ - + ]: 7 : if (!cancel)
557 : : {
586 alvherre@alvh.no-ip. 558 :UBC 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 : :
36 heikki.linnakangas@i 564 [ - + ]:CBC 7 : if (cancel->cancel_pkt_len == 0)
565 : : {
566 : : /* This is a dummy PGcancel object, see PQgetCancel */
36 heikki.linnakangas@i 567 :UBC 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 : : */
586 alvherre@alvh.no-ip. 577 [ - + ]:CBC 7 : if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
578 : : {
586 alvherre@alvh.no-ip. 579 :UBC 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 : : */
586 alvherre@alvh.no-ip. 593 [ + - ]:CBC 7 : if (cancel->raddr.addr.ss_family != AF_UNIX &&
586 alvherre@alvh.no-ip. 594 [ # # ]:UBC 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 : :
586 alvherre@alvh.no-ip. 654 :CBC 7 : retry3:
655 [ - + ]: 7 : if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
656 : : cancel->raddr.salen) < 0)
657 : : {
586 alvherre@alvh.no-ip. 658 [ # # ]:UBC 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 : :
157 heikki.linnakangas@i 665 :CBC 7 : cancel_pkt_len = pg_ntoh32(cancel->cancel_pkt_len);
666 : :
586 alvherre@alvh.no-ip. 667 : 7 : retry4:
668 : :
669 : : /*
670 : : * Send the cancel request packet. It starts with the message length at
671 : : * cancel_pkt_len, followed by the actual packet.
672 : : */
157 heikki.linnakangas@i 673 [ + - ]: 7 : if (send(tmpsock, (char *) &cancel->cancel_pkt_len, cancel_pkt_len, 0) != cancel_pkt_len)
674 : : {
586 alvherre@alvh.no-ip. 675 [ # # ]:UBC 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 : : */
586 alvherre@alvh.no-ip. 689 :CBC 7 : retry5:
157 heikki.linnakangas@i 690 [ - + ]: 7 : if (recv(tmpsock, &recvbuf, 1, 0) < 0)
691 : : {
586 alvherre@alvh.no-ip. 692 [ # # ]:UBC 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 */
586 alvherre@alvh.no-ip. 699 :CBC 7 : closesocket(tmpsock);
700 : 7 : SOCK_ERRNO_SET(save_errno);
701 : 7 : return true;
702 : :
586 alvherre@alvh.no-ip. 703 :UBC 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
586 alvherre@alvh.no-ip. 752 :CBC 2 : PQrequestCancel(PGconn *conn)
753 : : {
754 : : int r;
755 : : PGcancel *cancel;
756 : :
757 : : /* Check we have an open connection */
758 [ - + ]: 2 : if (!conn)
586 alvherre@alvh.no-ip. 759 :UBC 0 : return false;
760 : :
586 alvherre@alvh.no-ip. 761 [ - + ]:CBC 2 : if (conn->sock == PGINVALID_SOCKET)
762 : : {
586 alvherre@alvh.no-ip. 763 :UBC 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 : :
586 alvherre@alvh.no-ip. 772 :CBC 2 : cancel = PQgetCancel(conn);
773 [ + - ]: 2 : if (cancel)
774 : : {
775 : 2 : r = PQcancel(cancel, conn->errorMessage.data,
776 : 2 : conn->errorMessage.maxlen);
777 : 2 : PQfreeCancel(cancel);
778 : : }
779 : : else
780 : : {
586 alvherre@alvh.no-ip. 781 :UBC 0 : strlcpy(conn->errorMessage.data, "out of memory",
782 : : conn->errorMessage.maxlen);
783 : 0 : r = false;
784 : : }
785 : :
586 alvherre@alvh.no-ip. 786 [ - + ]:CBC 2 : if (!r)
787 : : {
586 alvherre@alvh.no-ip. 788 :UBC 0 : conn->errorMessage.len = strlen(conn->errorMessage.data);
789 : 0 : conn->errorReported = 0;
790 : : }
791 : :
586 alvherre@alvh.no-ip. 792 :CBC 2 : return r;
793 : : }
|