PostgreSQL Source Code
git master
Loading...
Searching...
No Matches
test_cloexec.c
Go to the documentation of this file.
1
/*-------------------------------------------------------------------------
2
*
3
* test_cloexec.c
4
* Test O_CLOEXEC flag handling on Windows
5
*
6
* This program tests that:
7
* 1. File handles opened with O_CLOEXEC are NOT inherited by child processes
8
* 2. File handles opened without O_CLOEXEC ARE inherited by child processes
9
*
10
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
11
*
12
*-------------------------------------------------------------------------
13
*/
14
15
#include "
postgres.h
"
16
17
#include <fcntl.h>
18
#include <sys/stat.h>
19
20
#ifdef WIN32
21
#include <windows.h>
22
#endif
23
24
#include "
common/file_utils.h
"
25
#include "
port.h
"
26
27
#ifdef WIN32
28
static
void
run_parent_tests
(
const
char
*
testfile1
,
const
char
*
testfile2
);
29
static
void
run_child_tests
(
const
char
*
handle1_str
,
const
char
*
handle2_str
);
30
static
bool
try_write_to_handle
(
HANDLE
h,
const
char
*
label
);
31
#endif
32
33
int
34
main
(
int
argc,
char
*argv[])
35
{
36
/* Windows-only test */
37
#ifndef WIN32
38
fprintf
(
stderr
,
"This test only runs on Windows\n"
);
39
return
0;
40
#else
41
char
testfile1
[
MAXPGPATH
];
42
char
testfile2
[
MAXPGPATH
];
43
44
if
(argc == 3)
45
{
46
/*
47
* Child mode: receives two handle values as hex strings and attempts
48
* to write to them.
49
*/
50
run_child_tests
(argv[1], argv[2]);
51
return
0;
52
}
53
else
if
(argc == 1)
54
{
55
/* Parent mode: opens files and spawns child */
56
snprintf
(
testfile1
,
sizeof
(
testfile1
),
"test_cloexec_1_%d.tmp"
, (
int
)
getpid
());
57
snprintf
(
testfile2
,
sizeof
(
testfile2
),
"test_cloexec_2_%d.tmp"
, (
int
)
getpid
());
58
59
run_parent_tests
(
testfile1
,
testfile2
);
60
61
/* Clean up test files */
62
unlink
(
testfile1
);
63
unlink
(
testfile2
);
64
65
return
0;
66
}
67
else
68
{
69
fprintf
(
stderr
,
"Usage: %s [handle1_hex handle2_hex]\n"
, argv[0]);
70
return
1;
71
}
72
#endif
73
}
74
75
#ifdef WIN32
76
static
void
77
run_parent_tests
(
const
char
*
testfile1
,
const
char
*
testfile2
)
78
{
79
int
fd1
,
80
fd2
;
81
HANDLE
h1
,
82
h2
;
83
char
exe_path
[
MAXPGPATH
];
84
char
cmdline
[
MAXPGPATH
+ 100];
85
STARTUPINFO
si
= {.cb =
sizeof
(
si
)};
86
PROCESS_INFORMATION
pi
= {0};
87
DWORD
exit_code
;
88
89
printf
(
"Parent: Opening test files...\n"
);
90
91
/* Open first file WITH O_CLOEXEC - should NOT be inherited */
92
fd1
=
open
(
testfile1
,
O_RDWR
|
O_CREAT
|
O_TRUNC
|
O_CLOEXEC
, 0600);
93
if
(
fd1
< 0)
94
{
95
fprintf
(
stderr
,
"Failed to open %s: %s\n"
,
testfile1
,
strerror
(
errno
));
96
exit
(1);
97
}
98
99
/* Open second file WITHOUT O_CLOEXEC - should be inherited */
100
fd2
=
open
(
testfile2
,
O_RDWR
|
O_CREAT
|
O_TRUNC
, 0600);
101
if
(
fd2
< 0)
102
{
103
fprintf
(
stderr
,
"Failed to open %s: %s\n"
,
testfile2
,
strerror
(
errno
));
104
close
(
fd1
);
105
exit
(1);
106
}
107
108
/* Get Windows HANDLEs from file descriptors */
109
h1
= (
HANDLE
)
_get_osfhandle
(
fd1
);
110
h2
= (
HANDLE
)
_get_osfhandle
(
fd2
);
111
112
if
(
h1
==
INVALID_HANDLE_VALUE
||
h2
==
INVALID_HANDLE_VALUE
)
113
{
114
fprintf
(
stderr
,
"Failed to get OS handles\n"
);
115
close
(
fd1
);
116
close
(
fd2
);
117
exit
(1);
118
}
119
120
printf
(
"Parent: fd1=%d (O_CLOEXEC) -> HANDLE=%p\n"
,
fd1
,
h1
);
121
printf
(
"Parent: fd2=%d (no O_CLOEXEC) -> HANDLE=%p\n"
,
fd2
,
h2
);
122
123
/*
124
* Find the actual executable path by removing any arguments from
125
* GetCommandLine(), and add our new arguments.
126
*/
127
GetModuleFileName
(
NULL
,
exe_path
,
sizeof
(
exe_path
));
128
snprintf
(
cmdline
,
sizeof
(
cmdline
),
"\"%s\" %p %p"
,
exe_path
,
h1
,
h2
);
129
130
printf
(
"Parent: Spawning child process...\n"
);
131
printf
(
"Parent: Command line: %s\n"
,
cmdline
);
132
133
if
(!
CreateProcess
(
NULL
,
/* application name */
134
cmdline
,
/* command line */
135
NULL
,
/* process security attributes */
136
NULL
,
/* thread security attributes */
137
TRUE
,
/* bInheritHandles - CRITICAL! */
138
0,
/* creation flags */
139
NULL
,
/* environment */
140
NULL
,
/* current directory */
141
&
si
,
/* startup info */
142
&
pi
))
/* process information */
143
{
144
fprintf
(
stderr
,
"CreateProcess failed: %lu\n"
,
GetLastError
());
145
close
(
fd1
);
146
close
(
fd2
);
147
exit
(1);
148
}
149
150
printf
(
"Parent: Waiting for child process...\n"
);
151
152
/* Wait for child to complete */
153
WaitForSingleObject
(
pi
.hProcess,
INFINITE
);
154
GetExitCodeProcess
(
pi
.hProcess, &
exit_code
);
155
156
CloseHandle
(
pi
.hProcess);
157
CloseHandle
(
pi
.hThread);
158
159
close
(
fd1
);
160
close
(
fd2
);
161
162
printf
(
"Parent: Child exit code: %lu\n"
,
exit_code
);
163
164
if
(
exit_code
== 0)
165
{
166
printf
(
"Parent: SUCCESS - O_CLOEXEC behavior verified\n"
);
167
}
168
else
169
{
170
printf
(
"Parent: FAILURE - O_CLOEXEC not working correctly\n"
);
171
exit
(1);
172
}
173
}
174
175
static
void
176
run_child_tests
(
const
char
*
handle1_str
,
const
char
*
handle2_str
)
177
{
178
HANDLE
h1
,
179
h2
;
180
bool
h1_worked
,
181
h2_worked
;
182
183
/* Parse handle values from hex strings */
184
if
(
sscanf
(
handle1_str
,
"%p"
, &
h1
) != 1 ||
185
sscanf
(
handle2_str
,
"%p"
, &
h2
) != 1)
186
{
187
fprintf
(
stderr
,
"Child: Failed to parse handle values\n"
);
188
exit
(1);
189
}
190
191
printf
(
"Child: Received HANDLE1=%p (should fail - O_CLOEXEC)\n"
,
h1
);
192
printf
(
"Child: Received HANDLE2=%p (should work - no O_CLOEXEC)\n"
,
h2
);
193
194
/* Try to write to both handles */
195
h1_worked
=
try_write_to_handle
(
h1
,
"HANDLE1"
);
196
h2_worked
=
try_write_to_handle
(
h2
,
"HANDLE2"
);
197
198
printf
(
"Child: HANDLE1 (O_CLOEXEC): %s\n"
,
199
h1_worked
?
"ACCESSIBLE (BAD!)"
:
"NOT ACCESSIBLE (GOOD!)"
);
200
printf
(
"Child: HANDLE2 (no O_CLOEXEC): %s\n"
,
201
h2_worked
?
"ACCESSIBLE (GOOD!)"
:
"NOT ACCESSIBLE (BAD!)"
);
202
203
/*
204
* For O_CLOEXEC to work correctly, h1 should NOT be accessible (h1_worked
205
* == false) and h2 SHOULD be accessible (h2_worked == true).
206
*/
207
if
(!
h1_worked
&&
h2_worked
)
208
{
209
printf
(
"Child: Test PASSED - O_CLOEXEC working correctly\n"
);
210
exit
(0);
211
}
212
else
213
{
214
printf
(
"Child: Test FAILED - O_CLOEXEC not working correctly\n"
);
215
exit
(1);
216
}
217
}
218
219
static
bool
220
try_write_to_handle
(
HANDLE
h,
const
char
*
label
)
221
{
222
const
char
*
test_data
=
"test\n"
;
223
DWORD
bytes_written
;
224
BOOL
result;
225
226
result =
WriteFile
(h,
test_data
,
strlen
(
test_data
), &
bytes_written
,
NULL
);
227
228
if
(result &&
bytes_written
==
strlen
(
test_data
))
229
{
230
printf
(
"Child: Successfully wrote to %s\n"
,
label
);
231
return
true
;
232
}
233
else
234
{
235
printf
(
"Child: Failed to write to %s (error %lu)\n"
,
236
label
,
GetLastError
());
237
return
false
;
238
}
239
}
240
#endif
main
int main(void)
Definition
compat_informix-charfuncs.c:16
fprintf
#define fprintf(file, fmt, msg)
Definition
cubescan.l:21
file_utils.h
close
#define close(a)
Definition
win32.h:12
label
static char * label
Definition
pg_basebackup.c:135
MAXPGPATH
#define MAXPGPATH
Definition
pg_config_manual.h:105
port.h
strerror
#define strerror
Definition
port.h:273
snprintf
#define snprintf
Definition
port.h:260
printf
#define printf(...)
Definition
port.h:266
postgres.h
fb
static int fb(int x)
Definition
preproc-init.c:92
O_CLOEXEC
#define O_CLOEXEC
Definition
win32_port.h:344
src
test
modules
test_cloexec
test_cloexec.c
Generated on Sat Feb 7 2026 12:13:18 for PostgreSQL Source Code by
1.9.8