libpq: Move pg_cancel to fe-cancel.c
[pgsql.git] / src / interfaces / libpq / fe-cancel.c
blobd69b8f9f9f4fee2dbf8ba49af11f351d6d663e33
1 /*-------------------------------------------------------------------------
3 * fe-cancel.c
4 * functions related to setting up a connection to the backend
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * src/interfaces/libpq/fe-cancel.c
13 *-------------------------------------------------------------------------
16 #include "postgres_fe.h"
18 #include <unistd.h>
20 #include "libpq-fe.h"
21 #include "libpq-int.h"
22 #include "port/pg_bswap.h"
26 * pg_cancel (backing struct for PGcancel) stores all data necessary to send a
27 * cancel request.
29 struct pg_cancel
31 SockAddr raddr; /* Remote address */
32 int be_pid; /* PID of to-be-canceled backend */
33 int be_key; /* cancel key of to-be-canceled backend */
34 int pgtcp_user_timeout; /* tcp user timeout */
35 int keepalives; /* use TCP keepalives? */
36 int keepalives_idle; /* time between TCP keepalives */
37 int keepalives_interval; /* time between TCP keepalive
38 * retransmits */
39 int keepalives_count; /* maximum number of TCP keepalive
40 * retransmits */
45 * PQgetCancel: get a PGcancel structure corresponding to a connection.
47 * A copy is needed to be able to cancel a running query from a different
48 * thread. If the same structure is used all structure members would have
49 * to be individually locked (if the entire structure was locked, it would
50 * be impossible to cancel a synchronous query because the structure would
51 * have to stay locked for the duration of the query).
53 PGcancel *
54 PQgetCancel(PGconn *conn)
56 PGcancel *cancel;
58 if (!conn)
59 return NULL;
61 if (conn->sock == PGINVALID_SOCKET)
62 return NULL;
64 cancel = malloc(sizeof(PGcancel));
65 if (cancel == NULL)
66 return NULL;
68 memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
69 cancel->be_pid = conn->be_pid;
70 cancel->be_key = conn->be_key;
71 /* We use -1 to indicate an unset connection option */
72 cancel->pgtcp_user_timeout = -1;
73 cancel->keepalives = -1;
74 cancel->keepalives_idle = -1;
75 cancel->keepalives_interval = -1;
76 cancel->keepalives_count = -1;
77 if (conn->pgtcp_user_timeout != NULL)
79 if (!pqParseIntParam(conn->pgtcp_user_timeout,
80 &cancel->pgtcp_user_timeout,
81 conn, "tcp_user_timeout"))
82 goto fail;
84 if (conn->keepalives != NULL)
86 if (!pqParseIntParam(conn->keepalives,
87 &cancel->keepalives,
88 conn, "keepalives"))
89 goto fail;
91 if (conn->keepalives_idle != NULL)
93 if (!pqParseIntParam(conn->keepalives_idle,
94 &cancel->keepalives_idle,
95 conn, "keepalives_idle"))
96 goto fail;
98 if (conn->keepalives_interval != NULL)
100 if (!pqParseIntParam(conn->keepalives_interval,
101 &cancel->keepalives_interval,
102 conn, "keepalives_interval"))
103 goto fail;
105 if (conn->keepalives_count != NULL)
107 if (!pqParseIntParam(conn->keepalives_count,
108 &cancel->keepalives_count,
109 conn, "keepalives_count"))
110 goto fail;
113 return cancel;
115 fail:
116 free(cancel);
117 return NULL;
120 /* PQfreeCancel: free a cancel structure */
121 void
122 PQfreeCancel(PGcancel *cancel)
124 free(cancel);
129 * Sets an integer socket option on a TCP socket, if the provided value is
130 * not negative. Returns false if setsockopt fails for some reason.
132 * CAUTION: This needs to be signal safe, since it's used by PQcancel.
134 #if defined(TCP_USER_TIMEOUT) || !defined(WIN32)
135 static bool
136 optional_setsockopt(int fd, int protoid, int optid, int value)
138 if (value < 0)
139 return true;
140 if (setsockopt(fd, protoid, optid, (char *) &value, sizeof(value)) < 0)
141 return false;
142 return true;
144 #endif
148 * PQcancel: request query cancel
150 * The return value is true if the cancel request was successfully
151 * dispatched, false if not (in which case an error message is available).
152 * Note: successful dispatch is no guarantee that there will be any effect at
153 * the backend. The application must read the operation result as usual.
155 * On failure, an error message is stored in *errbuf, which must be of size
156 * errbufsize (recommended size is 256 bytes). *errbuf is not changed on
157 * success return.
159 * CAUTION: we want this routine to be safely callable from a signal handler
160 * (for example, an application might want to call it in a SIGINT handler).
161 * This means we cannot use any C library routine that might be non-reentrant.
162 * malloc/free are often non-reentrant, and anything that might call them is
163 * just as dangerous. We avoid sprintf here for that reason. Building up
164 * error messages with strcpy/strcat is tedious but should be quite safe.
165 * We also save/restore errno in case the signal handler support doesn't.
168 PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
170 int save_errno = SOCK_ERRNO;
171 pgsocket tmpsock = PGINVALID_SOCKET;
172 int maxlen;
173 struct
175 uint32 packetlen;
176 CancelRequestPacket cp;
177 } crp;
179 if (!cancel)
181 strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
182 /* strlcpy probably doesn't change errno, but be paranoid */
183 SOCK_ERRNO_SET(save_errno);
184 return false;
188 * We need to open a temporary connection to the postmaster. Do this with
189 * only kernel calls.
191 if ((tmpsock = socket(cancel->raddr.addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
193 strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
194 goto cancel_errReturn;
198 * Since this connection will only be used to send a single packet of
199 * data, we don't need NODELAY. We also don't set the socket to
200 * nonblocking mode, because the API definition of PQcancel requires the
201 * cancel to be sent in a blocking way.
203 * We do set socket options related to keepalives and other TCP timeouts.
204 * This ensures that this function does not block indefinitely when
205 * reasonable keepalive and timeout settings have been provided.
207 if (cancel->raddr.addr.ss_family != AF_UNIX &&
208 cancel->keepalives != 0)
210 #ifndef WIN32
211 if (!optional_setsockopt(tmpsock, SOL_SOCKET, SO_KEEPALIVE, 1))
213 strlcpy(errbuf, "PQcancel() -- setsockopt(SO_KEEPALIVE) failed: ", errbufsize);
214 goto cancel_errReturn;
217 #ifdef PG_TCP_KEEPALIVE_IDLE
218 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
219 cancel->keepalives_idle))
221 strlcpy(errbuf, "PQcancel() -- setsockopt(" PG_TCP_KEEPALIVE_IDLE_STR ") failed: ", errbufsize);
222 goto cancel_errReturn;
224 #endif
226 #ifdef TCP_KEEPINTVL
227 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPINTVL,
228 cancel->keepalives_interval))
230 strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPINTVL) failed: ", errbufsize);
231 goto cancel_errReturn;
233 #endif
235 #ifdef TCP_KEEPCNT
236 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_KEEPCNT,
237 cancel->keepalives_count))
239 strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_KEEPCNT) failed: ", errbufsize);
240 goto cancel_errReturn;
242 #endif
244 #else /* WIN32 */
246 #ifdef SIO_KEEPALIVE_VALS
247 if (!pqSetKeepalivesWin32(tmpsock,
248 cancel->keepalives_idle,
249 cancel->keepalives_interval))
251 strlcpy(errbuf, "PQcancel() -- WSAIoctl(SIO_KEEPALIVE_VALS) failed: ", errbufsize);
252 goto cancel_errReturn;
254 #endif /* SIO_KEEPALIVE_VALS */
255 #endif /* WIN32 */
257 /* TCP_USER_TIMEOUT works the same way on Unix and Windows */
258 #ifdef TCP_USER_TIMEOUT
259 if (!optional_setsockopt(tmpsock, IPPROTO_TCP, TCP_USER_TIMEOUT,
260 cancel->pgtcp_user_timeout))
262 strlcpy(errbuf, "PQcancel() -- setsockopt(TCP_USER_TIMEOUT) failed: ", errbufsize);
263 goto cancel_errReturn;
265 #endif
268 retry3:
269 if (connect(tmpsock, (struct sockaddr *) &cancel->raddr.addr,
270 cancel->raddr.salen) < 0)
272 if (SOCK_ERRNO == EINTR)
273 /* Interrupted system call - we'll just try again */
274 goto retry3;
275 strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
276 goto cancel_errReturn;
279 /* Create and send the cancel request packet. */
281 crp.packetlen = pg_hton32((uint32) sizeof(crp));
282 crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
283 crp.cp.backendPID = pg_hton32(cancel->be_pid);
284 crp.cp.cancelAuthCode = pg_hton32(cancel->be_key);
286 retry4:
287 if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
289 if (SOCK_ERRNO == EINTR)
290 /* Interrupted system call - we'll just try again */
291 goto retry4;
292 strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
293 goto cancel_errReturn;
297 * Wait for the postmaster to close the connection, which indicates that
298 * it's processed the request. Without this delay, we might issue another
299 * command only to find that our cancel zaps that command instead of the
300 * one we thought we were canceling. Note we don't actually expect this
301 * read to obtain any data, we are just waiting for EOF to be signaled.
303 retry5:
304 if (recv(tmpsock, (char *) &crp, 1, 0) < 0)
306 if (SOCK_ERRNO == EINTR)
307 /* Interrupted system call - we'll just try again */
308 goto retry5;
309 /* we ignore other error conditions */
312 /* All done */
313 closesocket(tmpsock);
314 SOCK_ERRNO_SET(save_errno);
315 return true;
317 cancel_errReturn:
320 * Make sure we don't overflow the error buffer. Leave space for the \n at
321 * the end, and for the terminating zero.
323 maxlen = errbufsize - strlen(errbuf) - 2;
324 if (maxlen >= 0)
327 * We can't invoke strerror here, since it's not signal-safe. Settle
328 * for printing the decimal value of errno. Even that has to be done
329 * the hard way.
331 int val = SOCK_ERRNO;
332 char buf[32];
333 char *bufp;
335 bufp = buf + sizeof(buf) - 1;
336 *bufp = '\0';
339 *(--bufp) = (val % 10) + '0';
340 val /= 10;
341 } while (val > 0);
342 bufp -= 6;
343 memcpy(bufp, "error ", 6);
344 strncat(errbuf, bufp, maxlen);
345 strcat(errbuf, "\n");
347 if (tmpsock != PGINVALID_SOCKET)
348 closesocket(tmpsock);
349 SOCK_ERRNO_SET(save_errno);
350 return false;
354 * PQrequestCancel: old, not thread-safe function for requesting query cancel
356 * Returns true if able to send the cancel request, false if not.
358 * On failure, the error message is saved in conn->errorMessage; this means
359 * that this can't be used when there might be other active operations on
360 * the connection object.
362 * NOTE: error messages will be cut off at the current size of the
363 * error message buffer, since we dare not try to expand conn->errorMessage!
366 PQrequestCancel(PGconn *conn)
368 int r;
369 PGcancel *cancel;
371 /* Check we have an open connection */
372 if (!conn)
373 return false;
375 if (conn->sock == PGINVALID_SOCKET)
377 strlcpy(conn->errorMessage.data,
378 "PQrequestCancel() -- connection is not open\n",
379 conn->errorMessage.maxlen);
380 conn->errorMessage.len = strlen(conn->errorMessage.data);
381 conn->errorReported = 0;
383 return false;
386 cancel = PQgetCancel(conn);
387 if (cancel)
389 r = PQcancel(cancel, conn->errorMessage.data,
390 conn->errorMessage.maxlen);
391 PQfreeCancel(cancel);
393 else
395 strlcpy(conn->errorMessage.data, "out of memory",
396 conn->errorMessage.maxlen);
397 r = false;
400 if (!r)
402 conn->errorMessage.len = strlen(conn->errorMessage.data);
403 conn->errorReported = 0;
406 return r;