Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf3164c0

Browse files
committed
Clean up psql's control-C handling to avoid longjmp'ing out of random
places --- that risks corrupting data structures, losing sync with thebackend, etc. We now longjmp only from calls to readline, fgets, andfread, which we assume are coded to protect themselves against interruptsat undesirable times. This requires adding explicit tests forcancel_pressed in long-running loops, but on the whole it's far cleaner.Martijn van Oosterhout and Tom Lane.
1 parentace9335 commitf3164c0

File tree

12 files changed

+352
-161
lines changed

12 files changed

+352
-161
lines changed

‎src/bin/psql/common.c

Lines changed: 74 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.118 2006/05/26 19:51:29 tgl Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.119 2006/06/14 16:49:02 tgl Exp $
77
*/
88
#include"postgres_fe.h"
99
#include"common.h"
@@ -16,7 +16,6 @@
1616
#ifndefWIN32
1717
#include<sys/time.h>
1818
#include<unistd.h>/* for write() */
19-
#include<setjmp.h>
2019
#else
2120
#include<io.h>/* for _write() */
2221
#include<win32.h>
@@ -58,8 +57,6 @@ typedef struct _timeb TimevalStruct;
5857
((T)->millitm - (U)->millitm))
5958
#endif
6059

61-
externboolprompt_state;
62-
6360

6461
staticboolcommand_no_begin(constchar*query);
6562

@@ -219,55 +216,79 @@ NoticeProcessor(void *arg, const char *message)
219216
/*
220217
* Code to support query cancellation
221218
*
222-
* Before we start a query, we enablea SIGINT signal catcherthat sends a
219+
* Before we start a query, we enablethe SIGINT signal catcherto send a
223220
* cancel request to the backend. Note that sending the cancel directly from
224221
* the signal handler is safe because PQcancel() is written to make it
225-
* so. We use write() toprint to stderr because it's better to use simple
222+
* so. We use write() toreport to stderr because it's better to use simple
226223
* facilities in a signal handler.
227224
*
228225
* On win32, the signal cancelling happens on a separate thread, because
229226
* that's how SetConsoleCtrlHandler works. The PQcancel function is safe
230227
* for this (unlike PQrequestCancel). However, a CRITICAL_SECTION is required
231-
* to protect the PGcancel structure against being changed while theother
228+
* to protect the PGcancel structure against being changed while thesignal
232229
* thread is using it.
230+
*
231+
* SIGINT is supposed to abort all long-running psql operations, not only
232+
* database queries. In most places, this is accomplished by checking
233+
* cancel_pressed during long-running loops. However, that won't work when
234+
* blocked on user input (in readline() or fgets()). In those places, we
235+
* set sigint_interrupt_enabled TRUE while blocked, instructing the signal
236+
* catcher to longjmp through sigint_interrupt_jmp. We assume readline and
237+
* fgets are coded to handle possible interruption. (XXX currently this does
238+
* not work on win32, so control-C is less useful there)
233239
*/
234-
staticPGcancel*cancelConn=NULL;
240+
volatileboolsigint_interrupt_enabled= false;
241+
242+
sigjmp_bufsigint_interrupt_jmp;
243+
244+
staticPGcancel*volatilecancelConn=NULL;
235245

236246
#ifdefWIN32
237247
staticCRITICAL_SECTIONcancelConnLock;
238248
#endif
239249

240-
volatileboolcancel_pressed= false;
241-
242250
#definewrite_stderr(str)write(fileno(stderr), str, strlen(str))
243251

244252

245253
#ifndefWIN32
246254

247-
void
255+
staticvoid
248256
handle_sigint(SIGNAL_ARGS)
249257
{
250258
intsave_errno=errno;
251259
charerrbuf[256];
252260

253-
/*Don't muck around if promptingfora password. */
254-
if (prompt_state)
255-
return;
256-
257-
if (cancelConn==NULL)
258-
siglongjmp(main_loop_jmp,1);
261+
/*if we are waitingforinput, longjmp out of it */
262+
if (sigint_interrupt_enabled)
263+
{
264+
sigint_interrupt_enabled= false;
265+
siglongjmp(sigint_interrupt_jmp,1);
266+
}
259267

268+
/* else, set cancel flag to stop any long-running loops */
260269
cancel_pressed= true;
261270

262-
if (PQcancel(cancelConn,errbuf,sizeof(errbuf)))
263-
write_stderr("Cancel request sent\n");
264-
else
271+
/* and send QueryCancel if we are processing a database query */
272+
if (cancelConn!=NULL)
265273
{
266-
write_stderr("Could not send cancel request: ");
267-
write_stderr(errbuf);
274+
if (PQcancel(cancelConn,errbuf,sizeof(errbuf)))
275+
write_stderr("Cancel request sent\n");
276+
else
277+
{
278+
write_stderr("Could not send cancel request: ");
279+
write_stderr(errbuf);
280+
}
268281
}
282+
269283
errno=save_errno;/* just in case the write changed it */
270284
}
285+
286+
void
287+
setup_cancel_handler(void)
288+
{
289+
pqsignal(SIGINT,handle_sigint);
290+
}
291+
271292
#else/* WIN32 */
272293

273294
staticBOOLWINAPI
@@ -278,15 +299,17 @@ consoleHandler(DWORD dwCtrlType)
278299
if (dwCtrlType==CTRL_C_EVENT||
279300
dwCtrlType==CTRL_BREAK_EVENT)
280301
{
281-
if (prompt_state)
282-
return TRUE;
302+
/*
303+
* Can't longjmp here, because we are in wrong thread :-(
304+
*/
305+
306+
/* set cancel flag to stop any long-running loops */
307+
cancel_pressed= true;
283308

284-
/*Perform query cancel */
309+
/*and send QueryCancel if we are processing a database query */
285310
EnterCriticalSection(&cancelConnLock);
286311
if (cancelConn!=NULL)
287312
{
288-
cancel_pressed= true;
289-
290313
if (PQcancel(cancelConn,errbuf,sizeof(errbuf)))
291314
write_stderr("Cancel request sent\n");
292315
else
@@ -304,24 +327,14 @@ consoleHandler(DWORD dwCtrlType)
304327
return FALSE;
305328
}
306329

307-
void
308-
setup_win32_locks(void)
309-
{
310-
InitializeCriticalSection(&cancelConnLock);
311-
}
312-
313330
void
314331
setup_cancel_handler(void)
315332
{
316-
staticbooldone= false;
333+
InitializeCriticalSection(&cancelConnLock);
317334

318-
/* only need one handler per process */
319-
if (!done)
320-
{
321-
SetConsoleCtrlHandler(consoleHandler, TRUE);
322-
done= true;
323-
}
335+
SetConsoleCtrlHandler(consoleHandler, TRUE);
324336
}
337+
325338
#endif/* WIN32 */
326339

327340

@@ -386,16 +399,22 @@ CheckConnection(void)
386399
*
387400
* Set cancelConn to point to the current database connection.
388401
*/
389-
staticvoid
402+
void
390403
SetCancelConn(void)
391404
{
405+
PGcancel*oldCancelConn;
406+
392407
#ifdefWIN32
393408
EnterCriticalSection(&cancelConnLock);
394409
#endif
395410

396411
/* Free the old one if we have one */
397-
if (cancelConn!=NULL)
398-
PQfreeCancel(cancelConn);
412+
oldCancelConn=cancelConn;
413+
/* be sure handle_sigint doesn't use pointer while freeing */
414+
cancelConn=NULL;
415+
416+
if (oldCancelConn!=NULL)
417+
PQfreeCancel(oldCancelConn);
399418

400419
cancelConn=PQgetCancel(pset.db);
401420

@@ -413,15 +432,19 @@ SetCancelConn(void)
413432
void
414433
ResetCancelConn(void)
415434
{
435+
PGcancel*oldCancelConn;
436+
416437
#ifdefWIN32
417438
EnterCriticalSection(&cancelConnLock);
418439
#endif
419440

420-
if (cancelConn)
421-
PQfreeCancel(cancelConn);
422-
441+
oldCancelConn=cancelConn;
442+
/* be sure handle_sigint doesn't use pointer while freeing */
423443
cancelConn=NULL;
424444

445+
if (oldCancelConn!=NULL)
446+
PQfreeCancel(oldCancelConn);
447+
425448
#ifdefWIN32
426449
LeaveCriticalSection(&cancelConnLock);
427450
#endif
@@ -453,15 +476,8 @@ AcceptResult(const PGresult *result, const char *query)
453476
casePGRES_TUPLES_OK:
454477
casePGRES_EMPTY_QUERY:
455478
casePGRES_COPY_IN:
456-
/* Fine, do nothing */
457-
break;
458-
459479
casePGRES_COPY_OUT:
460-
/*
461-
* Keep cancel connection active during copy out state.
462-
* The matching ResetCancelConn() is in handleCopyOut.
463-
*/
464-
SetCancelConn();
480+
/* Fine, do nothing */
465481
break;
466482

467483
default:
@@ -648,12 +664,16 @@ ProcessCopyResult(PGresult *results)
648664
break;
649665

650666
casePGRES_COPY_OUT:
667+
SetCancelConn();
651668
success=handleCopyOut(pset.db,pset.queryFout);
669+
ResetCancelConn();
652670
break;
653671

654672
casePGRES_COPY_IN:
673+
SetCancelConn();
655674
success=handleCopyIn(pset.db,pset.cur_cmd_source,
656675
PQbinaryTuples(results));
676+
ResetCancelConn();
657677
break;
658678

659679
default:

‎src/bin/psql/common.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
*
44
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
55
*
6-
* $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.49 2006/06/01 00:15:36 tgl Exp $
6+
* $PostgreSQL: pgsql/src/bin/psql/common.h,v 1.50 2006/06/14 16:49:02 tgl Exp $
77
*/
88
#ifndefCOMMON_H
99
#defineCOMMON_H
1010

1111
#include"postgres_fe.h"
12-
#include<signal.h>
13-
#include"pqsignal.h"
12+
#include<setjmp.h>
1413
#include"libpq-fe.h"
1514

1615
#ifdefUSE_ASSERT_CHECKING
@@ -41,16 +40,17 @@ __attribute__((format(printf, 1, 2)));
4140

4241
externvoidNoticeProcessor(void*arg,constchar*message);
4342

44-
externvolatileboolcancel_pressed;
43+
externvolatileboolsigint_interrupt_enabled;
4544

46-
externvoidResetCancelConn(void);
45+
externsigjmp_bufsigint_interrupt_jmp;
46+
47+
externvolatileboolcancel_pressed;
48+
/* Note: cancel_pressed is defined in print.c, see that file for reasons */
4749

48-
#ifndefWIN32
49-
externvoidhandle_sigint(SIGNAL_ARGS);
50-
#else
51-
externvoidsetup_win32_locks(void);
5250
externvoidsetup_cancel_handler(void);
53-
#endif
51+
52+
externvoidSetCancelConn(void);
53+
externvoidResetCancelConn(void);
5454

5555
externPGresult*PSQLexec(constchar*query,boolstart_xact);
5656

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp