File src/background.c changed (mode: 100644) (index 6aa8bd66a..1ce5c1836) |
... |
... |
static void report_error_msg(const char title[], const char text[]); |
128 |
128 |
#endif |
#endif |
129 |
129 |
static bg_job_t * launch_external(const char cmd[], BgJobFlags flags, |
static bg_job_t * launch_external(const char cmd[], BgJobFlags flags, |
130 |
130 |
ShellRequester by); |
ShellRequester by); |
|
131 |
|
#ifdef _WIN32 |
|
132 |
|
static int finish_startup_info(STARTUPINFOW *startup); |
|
133 |
|
#endif |
131 |
134 |
static void append_error_msg(bg_job_t *job, const char err_msg[]); |
static void append_error_msg(bg_job_t *job, const char err_msg[]); |
132 |
135 |
static void place_on_job_bar(bg_job_t *job); |
static void place_on_job_bar(bg_job_t *job); |
133 |
136 |
static void get_off_job_bar(bg_job_t *job); |
static void get_off_job_bar(bg_job_t *job); |
|
... |
... |
launch_external(const char cmd[], BgJobFlags flags, ShellRequester by) |
1154 |
1157 |
|
|
1155 |
1158 |
return job; |
return job; |
1156 |
1159 |
#else |
#else |
1157 |
|
STARTUPINFOW startup = { .dwFlags = STARTF_USESTDHANDLES }; |
|
|
1160 |
|
/* Handles are either set below or redirected to NUL in |
|
1161 |
|
* finish_startup_info(). */ |
|
1162 |
|
STARTUPINFOW startup = { |
|
1163 |
|
.dwFlags = STARTF_USESTDHANDLES, |
|
1164 |
|
.hStdInput = INVALID_HANDLE_VALUE, |
|
1165 |
|
.hStdOutput = INVALID_HANDLE_VALUE, |
|
1166 |
|
.hStdError = INVALID_HANDLE_VALUE, |
|
1167 |
|
}; |
1158 |
1168 |
PROCESS_INFORMATION pinfo; |
PROCESS_INFORMATION pinfo; |
1159 |
1169 |
char *sh_cmd; |
char *sh_cmd; |
1160 |
1170 |
wchar_t *wide_cmd; |
wchar_t *wide_cmd; |
1161 |
1171 |
|
|
1162 |
|
HANDLE hnul = CreateFileA("NUL", GENERIC_READ | GENERIC_WRITE, 0, NULL, |
|
1163 |
|
OPEN_EXISTING, 0, NULL); |
|
1164 |
|
if(hnul == INVALID_HANDLE_VALUE) |
|
1165 |
|
{ |
|
1166 |
|
return NULL; |
|
1167 |
|
} |
|
1168 |
|
startup.hStdInput = hnul; |
|
1169 |
|
startup.hStdOutput = hnul; |
|
1170 |
|
|
|
1171 |
1172 |
HANDLE herr = INVALID_HANDLE_VALUE; |
HANDLE herr = INVALID_HANDLE_VALUE; |
1172 |
1173 |
if(!merge_streams && !CreatePipe(&herr, &startup.hStdError, NULL, 16*1024)) |
if(!merge_streams && !CreatePipe(&herr, &startup.hStdError, NULL, 16*1024)) |
1173 |
1174 |
{ |
{ |
1174 |
|
CloseHandle(hnul); |
|
1175 |
1175 |
return NULL; |
return NULL; |
1176 |
1176 |
} |
} |
1177 |
1177 |
|
|
|
... |
... |
launch_external(const char cmd[], BgJobFlags flags, ShellRequester by) |
1182 |
1182 |
{ |
{ |
1183 |
1183 |
CloseHandle(herr); |
CloseHandle(herr); |
1184 |
1184 |
} |
} |
1185 |
|
CloseHandle(hnul); |
|
1186 |
1185 |
return NULL; |
return NULL; |
1187 |
1186 |
} |
} |
1188 |
1187 |
|
|
|
... |
... |
launch_external(const char cmd[], BgJobFlags flags, ShellRequester by) |
1196 |
1195 |
CloseHandle(herr); |
CloseHandle(herr); |
1197 |
1196 |
} |
} |
1198 |
1197 |
CloseHandle(hin); |
CloseHandle(hin); |
1199 |
|
CloseHandle(hnul); |
|
1200 |
1198 |
return NULL; |
return NULL; |
1201 |
1199 |
} |
} |
1202 |
1200 |
|
|
1203 |
1201 |
if(merge_streams) |
if(merge_streams) |
1204 |
1202 |
{ |
{ |
1205 |
|
startup.hStdError = startup.hStdOutput; |
|
|
1203 |
|
/* Duplicate instead of just assigning so that closing one handle keeps |
|
1204 |
|
* the other one operational. */ |
|
1205 |
|
HANDLE this_process = GetCurrentProcess(); |
|
1206 |
|
if(!DuplicateHandle(this_process, startup.hStdOutput, this_process, |
|
1207 |
|
&startup.hStdError, /*access=*/0, /*inheritable=*/TRUE, |
|
1208 |
|
DUPLICATE_SAME_ACCESS)) |
|
1209 |
|
{ |
|
1210 |
|
CloseHandle(startup.hStdOutput); |
|
1211 |
|
CloseHandle(hout); |
|
1212 |
|
CloseHandle(hin); |
|
1213 |
|
return NULL; |
|
1214 |
|
} |
1206 |
1215 |
} |
} |
1207 |
1216 |
} |
} |
1208 |
1217 |
|
|
1209 |
|
SetHandleInformation(startup.hStdInput, HANDLE_FLAG_INHERIT, 1); |
|
1210 |
|
SetHandleInformation(startup.hStdOutput, HANDLE_FLAG_INHERIT, 1); |
|
1211 |
|
SetHandleInformation(startup.hStdError, HANDLE_FLAG_INHERIT, 1); |
|
1212 |
|
|
|
|
1218 |
|
finish_startup_info(&startup); |
1213 |
1219 |
sh_cmd = win_make_sh_cmd(cmd, by); |
sh_cmd = win_make_sh_cmd(cmd, by); |
1214 |
1220 |
|
|
1215 |
1221 |
wide_cmd = to_wide(sh_cmd); |
wide_cmd = to_wide(sh_cmd); |
1216 |
1222 |
int started = CreateProcessW(NULL, wide_cmd, NULL, NULL, 1, CREATE_SUSPENDED, |
int started = CreateProcessW(NULL, wide_cmd, NULL, NULL, 1, CREATE_SUSPENDED, |
1217 |
1223 |
NULL, NULL, &startup, &pinfo); |
NULL, NULL, &startup, &pinfo); |
1218 |
1224 |
free(wide_cmd); |
free(wide_cmd); |
1219 |
|
CloseHandle(hnul); |
|
|
1225 |
|
|
1220 |
1226 |
CloseHandle(startup.hStdInput); |
CloseHandle(startup.hStdInput); |
1221 |
1227 |
CloseHandle(startup.hStdOutput); |
CloseHandle(startup.hStdOutput); |
1222 |
|
if(startup.hStdError != startup.hStdOutput) |
|
1223 |
|
{ |
|
1224 |
|
CloseHandle(startup.hStdError); |
|
1225 |
|
} |
|
|
1228 |
|
CloseHandle(startup.hStdError); |
1226 |
1229 |
|
|
1227 |
1230 |
if(!started) |
if(!started) |
1228 |
1231 |
{ |
{ |
|
... |
... |
launch_external(const char cmd[], BgJobFlags flags, ShellRequester by) |
1297 |
1300 |
#endif |
#endif |
1298 |
1301 |
} |
} |
1299 |
1302 |
|
|
|
1303 |
|
#ifdef _WIN32 |
|
1304 |
|
/* Makes sure that standard handles of startup structure which weren't |
|
1305 |
|
* initialized are redirected to NUL. Returns zero on success. */ |
|
1306 |
|
static int |
|
1307 |
|
finish_startup_info(STARTUPINFOW *startup) |
|
1308 |
|
{ |
|
1309 |
|
int missing = (startup->hStdInput == INVALID_HANDLE_VALUE) |
|
1310 |
|
+ (startup->hStdOutput == INVALID_HANDLE_VALUE) |
|
1311 |
|
+ (startup->hStdError == INVALID_HANDLE_VALUE); |
|
1312 |
|
if(missing == 0) |
|
1313 |
|
{ |
|
1314 |
|
goto no_redirects; |
|
1315 |
|
} |
|
1316 |
|
|
|
1317 |
|
/* Open up to three handles so that child process could close one of them |
|
1318 |
|
* while keep using others. */ |
|
1319 |
|
HANDLE hnul[3]; |
|
1320 |
|
hnul[0] = CreateFileA("NUL", GENERIC_READ | GENERIC_WRITE, |
|
1321 |
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, |
|
1322 |
|
FILE_ATTRIBUTE_NORMAL, NULL); |
|
1323 |
|
if(hnul[0] == INVALID_HANDLE_VALUE) |
|
1324 |
|
{ |
|
1325 |
|
return 1; |
|
1326 |
|
} |
|
1327 |
|
|
|
1328 |
|
int i; |
|
1329 |
|
HANDLE this_process = GetCurrentProcess(); |
|
1330 |
|
for(i = 1; i < missing; ++i) |
|
1331 |
|
{ |
|
1332 |
|
if(!DuplicateHandle(this_process, hnul[0], this_process, &hnul[i], |
|
1333 |
|
/*access=*/0, /*inheritable=*/TRUE, DUPLICATE_SAME_ACCESS)) |
|
1334 |
|
{ |
|
1335 |
|
int j; |
|
1336 |
|
for(j = 0; j < i; ++j) |
|
1337 |
|
{ |
|
1338 |
|
CloseHandle(hnul[j]); |
|
1339 |
|
} |
|
1340 |
|
return 1; |
|
1341 |
|
} |
|
1342 |
|
} |
|
1343 |
|
|
|
1344 |
|
if(startup->hStdInput == INVALID_HANDLE_VALUE) |
|
1345 |
|
{ |
|
1346 |
|
startup->hStdInput = hnul[--missing]; |
|
1347 |
|
} |
|
1348 |
|
if(startup->hStdOutput == INVALID_HANDLE_VALUE) |
|
1349 |
|
{ |
|
1350 |
|
startup->hStdOutput = hnul[--missing]; |
|
1351 |
|
} |
|
1352 |
|
if(startup->hStdError == INVALID_HANDLE_VALUE) |
|
1353 |
|
{ |
|
1354 |
|
startup->hStdError = hnul[--missing]; |
|
1355 |
|
} |
|
1356 |
|
|
|
1357 |
|
no_redirects: |
|
1358 |
|
SetHandleInformation(startup->hStdInput, HANDLE_FLAG_INHERIT, 1); |
|
1359 |
|
SetHandleInformation(startup->hStdOutput, HANDLE_FLAG_INHERIT, 1); |
|
1360 |
|
SetHandleInformation(startup->hStdError, HANDLE_FLAG_INHERIT, 1); |
|
1361 |
|
|
|
1362 |
|
return 0; |
|
1363 |
|
} |
|
1364 |
|
#endif |
|
1365 |
|
|
1300 |
1366 |
int |
int |
1301 |
1367 |
bg_execute(const char descr[], const char op_descr[], int total, int important, |
bg_execute(const char descr[], const char op_descr[], int total, int important, |
1302 |
1368 |
bg_task_func task_func, void *args) |
bg_task_func task_func, void *args) |
File tests/io_tester_app/io_tester.c added (mode: 100644) (index 000000000..35144668e) |
|
1 |
|
#include <stdio.h> /* EOF FILE fclose() fflush() fgets() */ |
|
2 |
|
#include <string.h> /* strcmp() strlen() strspn() */ |
|
3 |
|
|
|
4 |
|
/* |
|
5 |
|
* Usage: io_tester modes |
|
6 |
|
* |
|
7 |
|
* `modes` must be a three-letter sequence of: |
|
8 |
|
* * c -- close |
|
9 |
|
* * k -- keep |
|
10 |
|
* * u -- use |
|
11 |
|
* |
|
12 |
|
* A letter corresponds to stdin, stdout and stderr in this order. |
|
13 |
|
* |
|
14 |
|
* Example modes: kcc, cuc, ccu |
|
15 |
|
* |
|
16 |
|
* When a file descriptor is being used, stdin expects "stdin" string and |
|
17 |
|
* stdout and stderr print "stdout" and "stderr" respectively. |
|
18 |
|
*/ |
|
19 |
|
|
|
20 |
|
int |
|
21 |
|
main(int argc, char *argv[]) |
|
22 |
|
{ |
|
23 |
|
if(argc != 2) |
|
24 |
|
{ |
|
25 |
|
return 10; |
|
26 |
|
} |
|
27 |
|
|
|
28 |
|
const char *modes = argv[1]; |
|
29 |
|
if(strlen(modes) != 3) |
|
30 |
|
{ |
|
31 |
|
return 11; |
|
32 |
|
} |
|
33 |
|
if(strspn(modes, "cku") != 3) |
|
34 |
|
{ |
|
35 |
|
return 12; |
|
36 |
|
} |
|
37 |
|
|
|
38 |
|
FILE *fps[3] = { stdin, stdout, stderr }; |
|
39 |
|
const char *data[3] = { "stdin", "stdout", "stderr" }; |
|
40 |
|
int i; |
|
41 |
|
|
|
42 |
|
/* Start by closing descriptors to make sure others can still be used. */ |
|
43 |
|
for(i = 0; i < 3; ++i) |
|
44 |
|
{ |
|
45 |
|
if(modes[i] == 'c') |
|
46 |
|
{ |
|
47 |
|
if(fclose(fps[i]) != 0) |
|
48 |
|
{ |
|
49 |
|
return 20 + i; |
|
50 |
|
} |
|
51 |
|
} |
|
52 |
|
} |
|
53 |
|
|
|
54 |
|
for(i = 0; i < 3; ++i) |
|
55 |
|
{ |
|
56 |
|
if(modes[i] != 'u') |
|
57 |
|
{ |
|
58 |
|
continue; |
|
59 |
|
} |
|
60 |
|
|
|
61 |
|
if(i == 0) |
|
62 |
|
{ |
|
63 |
|
char buf[16]; |
|
64 |
|
if(fgets(buf, sizeof(buf), fps[i]) != buf) |
|
65 |
|
{ |
|
66 |
|
return 30; |
|
67 |
|
} |
|
68 |
|
if(strcmp(buf, data[i]) != 0) |
|
69 |
|
{ |
|
70 |
|
return 31; |
|
71 |
|
} |
|
72 |
|
} |
|
73 |
|
else |
|
74 |
|
{ |
|
75 |
|
if(fputs(data[i], fps[i]) == EOF) |
|
76 |
|
{ |
|
77 |
|
return 40 + i; |
|
78 |
|
} |
|
79 |
|
if(fflush(fps[i]) == EOF) |
|
80 |
|
{ |
|
81 |
|
return 50 + i; |
|
82 |
|
} |
|
83 |
|
} |
|
84 |
|
} |
|
85 |
|
|
|
86 |
|
return 0; |
|
87 |
|
} |
|
88 |
|
|
|
89 |
|
/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */ |
|
90 |
|
/* vim: set cinoptions+=t0 filetype=c : */ |
File tests/lua/api_jobs.c changed (mode: 100644) (index 2d53df28c..e393282a8) |
2 |
2 |
|
|
3 |
3 |
#include <unistd.h> /* usleep() */ |
#include <unistd.h> /* usleep() */ |
4 |
4 |
|
|
|
5 |
|
#include <stdlib.h> /* getenv() */ |
|
6 |
|
|
5 |
7 |
#include "../../src/engine/var.h" |
#include "../../src/engine/var.h" |
6 |
8 |
#include "../../src/engine/variables.h" |
#include "../../src/engine/variables.h" |
7 |
9 |
#include "../../src/lua/vlua.h" |
#include "../../src/lua/vlua.h" |
|
12 |
14 |
|
|
13 |
15 |
#include "asserts.h" |
#include "asserts.h" |
14 |
16 |
|
|
|
17 |
|
static void setup_io_tester(void); |
15 |
18 |
static void wait_for_job(void); |
static void wait_for_job(void); |
16 |
19 |
|
|
17 |
20 |
static vlua_t *vlua; |
static vlua_t *vlua; |
|
... |
... |
SETUP() |
25 |
28 |
TEARDOWN() |
TEARDOWN() |
26 |
29 |
{ |
{ |
27 |
30 |
vlua_finish(vlua); |
vlua_finish(vlua); |
|
31 |
|
/* Without waiting for jobs to finish tests hang in Wine but not on Windows |
|
32 |
|
* which makes it hard to debug. Unclear whether this indicates a bug in Vifm |
|
33 |
|
* or in Wine. */ |
|
34 |
|
wait_for_all_bg(); |
28 |
35 |
conf_teardown(); |
conf_teardown(); |
29 |
36 |
} |
} |
30 |
37 |
|
|
|
... |
... |
TEST(vifmjob_no_in) |
146 |
153 |
"print(job:stdin() and 'FAIL')"); |
"print(job:stdin() and 'FAIL')"); |
147 |
154 |
} |
} |
148 |
155 |
|
|
|
156 |
|
TEST(vifmjob_io_close_none) |
|
157 |
|
{ |
|
158 |
|
setup_io_tester(); |
|
159 |
|
GLUA_EQ(vlua, "", |
|
160 |
|
"job = vifm.startjob { cmd = io_tester..' uuu', iomode = 'w' }"); |
|
161 |
|
GLUA_EQ(vlua, "true", "print(job:stdin():write('stdin') == job:stdin())"); |
|
162 |
|
GLUA_EQ(vlua, "0", "print(job:exitcode())"); |
|
163 |
|
GLUA_EQ(vlua, "stderr", "print(job:errors())"); |
|
164 |
|
} |
|
165 |
|
|
|
166 |
|
TEST(vifmjob_io_close_all) |
|
167 |
|
{ |
|
168 |
|
setup_io_tester(); |
|
169 |
|
GLUA_EQ(vlua, "0", |
|
170 |
|
"job = vifm.startjob { cmd = io_tester..' ccc', iomode = '' }" |
|
171 |
|
"print(job:exitcode())"); // got 21 |
|
172 |
|
} |
|
173 |
|
|
|
174 |
|
TEST(vifmjob_io_close_in) |
|
175 |
|
{ |
|
176 |
|
setup_io_tester(); |
|
177 |
|
GLUA_EQ(vlua, "", "job = vifm.startjob { cmd = io_tester..' cuu' }"); |
|
178 |
|
GLUA_EQ(vlua, "stdout", "print(job:stdout():lines()())"); |
|
179 |
|
GLUA_EQ(vlua, "0", "print(job:exitcode())"); |
|
180 |
|
GLUA_EQ(vlua, "stderr", "print(job:errors())"); |
|
181 |
|
} |
|
182 |
|
|
|
183 |
|
TEST(vifmjob_io_close_out) |
|
184 |
|
{ |
|
185 |
|
setup_io_tester(); |
|
186 |
|
GLUA_EQ(vlua, "", |
|
187 |
|
"job = vifm.startjob { cmd = io_tester..' ucu', iomode = 'w' }"); |
|
188 |
|
GLUA_EQ(vlua, "true", "print(job:stdin():write('stdin') == job:stdin())"); |
|
189 |
|
GLUA_EQ(vlua, "0", "print(job:exitcode())"); |
|
190 |
|
GLUA_EQ(vlua, "stderr", "print(job:errors())"); |
|
191 |
|
} |
|
192 |
|
|
|
193 |
|
TEST(vifmjob_io_close_err) |
|
194 |
|
{ |
|
195 |
|
setup_io_tester(); |
|
196 |
|
GLUA_EQ(vlua, "", |
|
197 |
|
"job = vifm.startjob { cmd = io_tester..' uuc', iomode = 'w' }"); |
|
198 |
|
GLUA_EQ(vlua, "true", "print(job:stdin():write('stdin') == job:stdin())"); |
|
199 |
|
GLUA_EQ(vlua, "0", "print(job:exitcode())"); |
|
200 |
|
GLUA_EQ(vlua, "", "print(job:errors())"); |
|
201 |
|
} |
|
202 |
|
|
|
203 |
|
TEST(vifmjob_io_keep_in) |
|
204 |
|
{ |
|
205 |
|
setup_io_tester(); |
|
206 |
|
GLUA_EQ(vlua, "", |
|
207 |
|
"job = vifm.startjob { cmd = io_tester..' ucc', iomode = 'w' }"); |
|
208 |
|
GLUA_EQ(vlua, "true", "print(job:stdin():write('stdin') == job:stdin())"); |
|
209 |
|
GLUA_EQ(vlua, "0", "print(job:exitcode())"); |
|
210 |
|
GLUA_EQ(vlua, "", "print(job:errors())"); |
|
211 |
|
} |
|
212 |
|
|
|
213 |
|
TEST(vifmjob_io_keep_out) |
|
214 |
|
{ |
|
215 |
|
setup_io_tester(); |
|
216 |
|
GLUA_EQ(vlua, "", "job = vifm.startjob { cmd = io_tester..' cuc' }"); |
|
217 |
|
GLUA_EQ(vlua, "stdout", "print(job:stdout():lines()())"); |
|
218 |
|
GLUA_EQ(vlua, "0", "print(job:exitcode())"); |
|
219 |
|
GLUA_EQ(vlua, "", "print(job:errors())"); |
|
220 |
|
} |
|
221 |
|
|
|
222 |
|
TEST(vifmjob_io_keep_err) |
|
223 |
|
{ |
|
224 |
|
setup_io_tester(); |
|
225 |
|
GLUA_EQ(vlua, "", "job = vifm.startjob { cmd = io_tester..' ccu' }"); |
|
226 |
|
GLUA_EQ(vlua, "0", "print(job:exitcode())"); |
|
227 |
|
GLUA_EQ(vlua, "stderr", "print(job:errors())"); |
|
228 |
|
} |
|
229 |
|
|
149 |
230 |
TEST(vifmjob_onexit_good) |
TEST(vifmjob_onexit_good) |
150 |
231 |
{ |
{ |
151 |
232 |
var_t var = var_from_int(0); |
var_t var = var_from_int(0); |
|
... |
... |
TEST(vifmjob_onexit_bad) |
181 |
262 |
ui_sb_last()); |
ui_sb_last()); |
182 |
263 |
} |
} |
183 |
264 |
|
|
|
265 |
|
static void |
|
266 |
|
setup_io_tester(void) |
|
267 |
|
{ |
|
268 |
|
const int debug = (getenv("DEBUG") != NULL); |
|
269 |
|
const char *bin_path = (debug ? "bin/debug" : "bin"); |
|
270 |
|
|
|
271 |
|
char set_cmd_lua[64]; |
|
272 |
|
snprintf(set_cmd_lua, sizeof(set_cmd_lua), |
|
273 |
|
"io_tester = '%s" SL "io_tester_app'", bin_path); |
|
274 |
|
assert_success(vlua_run_string(vlua, set_cmd_lua)); |
|
275 |
|
} |
|
276 |
|
|
184 |
277 |
static void |
static void |
185 |
278 |
wait_for_job(void) |
wait_for_job(void) |
186 |
279 |
{ |
{ |