Mercurial > vim
comparison src/terminal.c @ 11939:ef1febf04d03 v8.0.0849
patch 8.0.0849: crash when job exit callback wipes the terminal
commit https://github.com/vim/vim/commit/3c3a80dc59ccc0e0aabb9c8bd58ea84a801dbfc1
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Aug 3 17:06:45 2017 +0200
patch 8.0.0849: crash when job exit callback wipes the terminal
Problem: Crash when job exit callback wipes the terminal.
Solution: Check for b_term to be NULL. (Yasuhiro Matsumoto, closes https://github.com/vim/vim/issues/1922)
Implement options for term_start() to be able to test.
Make term_wait() more reliable.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 03 Aug 2017 17:15:04 +0200 |
parents | c893d6c00497 |
children | 8b9a1be7bb82 |
comparison
equal
deleted
inserted
replaced
11938:4aeeb22b875d | 11939:ef1febf04d03 |
---|---|
38 * TODO: | 38 * TODO: |
39 * - don't allow exiting Vim when a terminal is still running a job | 39 * - don't allow exiting Vim when a terminal is still running a job |
40 * - MS-Windows: no redraw for 'updatetime' #1915 | 40 * - MS-Windows: no redraw for 'updatetime' #1915 |
41 * - in bash mouse clicks are inserting characters. | 41 * - in bash mouse clicks are inserting characters. |
42 * - mouse scroll: when over other window, scroll that window. | 42 * - mouse scroll: when over other window, scroll that window. |
43 * - add argument to term_wait() for waiting time. | |
43 * - For the scrollback buffer store lines in the buffer, only attributes in | 44 * - For the scrollback buffer store lines in the buffer, only attributes in |
44 * tl_scrollback. | 45 * tl_scrollback. |
45 * - When the job ends: | 46 * - When the job ends: |
46 * - Need an option or argument to drop the window+buffer right away, to be | 47 * - Need an option or argument to drop the window+buffer right away, to be |
47 * used for a shell or Vim. 'termfinish'; "close", "open" (open window when | 48 * used for a shell or Vim. 'termfinish'; "close", "open" (open window when |
144 #define KEY_BUF_LEN 200 | 145 #define KEY_BUF_LEN 200 |
145 | 146 |
146 /* | 147 /* |
147 * Functions with separate implementation for MS-Windows and Unix-like systems. | 148 * Functions with separate implementation for MS-Windows and Unix-like systems. |
148 */ | 149 */ |
149 static int term_and_job_init(term_T *term, int rows, int cols, char_u *cmd); | 150 static int term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt); |
150 static void term_report_winsize(term_T *term, int rows, int cols); | 151 static void term_report_winsize(term_T *term, int rows, int cols); |
151 static void term_free_vterm(term_T *term); | 152 static void term_free_vterm(term_T *term); |
152 | 153 |
153 /************************************** | 154 /************************************** |
154 * 1. Generic code for all systems. | 155 * 1. Generic code for all systems. |
183 term->tl_cols_fixed = TRUE; | 184 term->tl_cols_fixed = TRUE; |
184 } | 185 } |
185 } | 186 } |
186 | 187 |
187 /* | 188 /* |
188 * ":terminal": open a terminal window and execute a job in it. | 189 * Initialize job options for a terminal job. |
189 */ | 190 * Caller may overrule some of them. |
190 void | 191 */ |
191 ex_terminal(exarg_T *eap) | 192 static void |
193 init_job_options(jobopt_T *opt) | |
194 { | |
195 clear_job_options(opt); | |
196 | |
197 opt->jo_mode = MODE_RAW; | |
198 opt->jo_out_mode = MODE_RAW; | |
199 opt->jo_err_mode = MODE_RAW; | |
200 opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE; | |
201 | |
202 opt->jo_io[PART_OUT] = JIO_BUFFER; | |
203 opt->jo_io[PART_ERR] = JIO_BUFFER; | |
204 opt->jo_set |= JO_OUT_IO + JO_ERR_IO; | |
205 | |
206 opt->jo_modifiable[PART_OUT] = 0; | |
207 opt->jo_modifiable[PART_ERR] = 0; | |
208 opt->jo_set |= JO_OUT_MODIFIABLE + JO_ERR_MODIFIABLE; | |
209 | |
210 opt->jo_set |= JO_OUT_BUF + JO_ERR_BUF; | |
211 } | |
212 | |
213 /* | |
214 * Set job options mandatory for a terminal job. | |
215 */ | |
216 static void | |
217 setup_job_options(jobopt_T *opt, int rows, int cols) | |
218 { | |
219 opt->jo_io_buf[PART_OUT] = curbuf->b_fnum; | |
220 opt->jo_io_buf[PART_ERR] = curbuf->b_fnum; | |
221 opt->jo_pty = TRUE; | |
222 opt->jo_term_rows = rows; | |
223 opt->jo_term_cols = cols; | |
224 } | |
225 | |
226 static void | |
227 term_start(char_u *cmd, jobopt_T *opt) | |
192 { | 228 { |
193 exarg_T split_ea; | 229 exarg_T split_ea; |
194 win_T *old_curwin = curwin; | 230 win_T *old_curwin = curwin; |
195 term_T *term; | 231 term_T *term; |
196 char_u *cmd = eap->arg; | |
197 | 232 |
198 if (check_restricted() || check_secure()) | 233 if (check_restricted() || check_secure()) |
199 return; | 234 return; |
200 | 235 |
201 term = (term_T *)alloc_clear(sizeof(term_T)); | 236 term = (term_T *)alloc_clear(sizeof(term_T)); |
254 curbuf->b_p_ma = FALSE; | 289 curbuf->b_p_ma = FALSE; |
255 set_string_option_direct((char_u *)"buftype", -1, | 290 set_string_option_direct((char_u *)"buftype", -1, |
256 (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0); | 291 (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0); |
257 | 292 |
258 set_term_and_win_size(term); | 293 set_term_and_win_size(term); |
294 setup_job_options(opt, term->tl_rows, term->tl_cols); | |
259 | 295 |
260 /* System dependent: setup the vterm and start the job in it. */ | 296 /* System dependent: setup the vterm and start the job in it. */ |
261 if (term_and_job_init(term, term->tl_rows, term->tl_cols, cmd) == OK) | 297 if (term_and_job_init(term, term->tl_rows, term->tl_cols, cmd, opt) == OK) |
262 { | 298 { |
263 /* store the size we ended up with */ | 299 /* store the size we ended up with */ |
264 vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols); | 300 vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols); |
265 } | 301 } |
266 else | 302 else |
269 | 305 |
270 /* Wiping out the buffer will also close the window and call | 306 /* Wiping out the buffer will also close the window and call |
271 * free_terminal(). */ | 307 * free_terminal(). */ |
272 do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE); | 308 do_buffer(DOBUF_WIPE, DOBUF_CURRENT, FORWARD, 0, TRUE); |
273 } | 309 } |
310 } | |
311 | |
312 /* | |
313 * ":terminal": open a terminal window and execute a job in it. | |
314 */ | |
315 void | |
316 ex_terminal(exarg_T *eap) | |
317 { | |
318 jobopt_T opt; | |
319 | |
320 init_job_options(&opt); | |
321 /* TODO: get options from before the command */ | |
322 | |
323 term_start(eap->arg, &opt); | |
274 } | 324 } |
275 | 325 |
276 /* | 326 /* |
277 * Free the scrollback buffer for "term". | 327 * Free the scrollback buffer for "term". |
278 */ | 328 */ |
963 while (curwin->w_redr_type != 0) | 1013 while (curwin->w_redr_type != 0) |
964 update_screen(0); | 1014 update_screen(0); |
965 update_cursor(curbuf->b_term, FALSE); | 1015 update_cursor(curbuf->b_term, FALSE); |
966 | 1016 |
967 c = term_vgetc(); | 1017 c = term_vgetc(); |
968 if (curbuf->b_term->tl_vterm == NULL | 1018 if (!term_use_loop()) |
969 || !term_job_running(curbuf->b_term)) | |
970 /* job finished while waiting for a character */ | 1019 /* job finished while waiting for a character */ |
971 break; | 1020 break; |
972 | 1021 |
973 #ifdef UNIX | 1022 #ifdef UNIX |
974 may_send_sigint(c, curbuf->b_term->tl_job->jv_pid, 0); | 1023 may_send_sigint(c, curbuf->b_term->tl_job->jv_pid, 0); |
991 #endif | 1040 #endif |
992 c = term_vgetc(); | 1041 c = term_vgetc(); |
993 #ifdef FEAT_CMDL_INFO | 1042 #ifdef FEAT_CMDL_INFO |
994 clear_showcmd(); | 1043 clear_showcmd(); |
995 #endif | 1044 #endif |
996 if (curbuf->b_term->tl_vterm == NULL | 1045 if (!term_use_loop()) |
997 || !term_job_running(curbuf->b_term)) | |
998 /* job finished while waiting for a character */ | 1046 /* job finished while waiting for a character */ |
999 break; | 1047 break; |
1000 | 1048 |
1001 if (termkey == 0 && c == '.') | 1049 if (termkey == 0 && c == '.') |
1002 { | 1050 { |
1612 return 0; | 1660 return 0; |
1613 return cell2attr(line->sb_cells + col); | 1661 return cell2attr(line->sb_cells + col); |
1614 } | 1662 } |
1615 | 1663 |
1616 /* | 1664 /* |
1617 * Set job options common for Unix and MS-Windows. | |
1618 */ | |
1619 static void | |
1620 setup_job_options(jobopt_T *opt, int rows, int cols) | |
1621 { | |
1622 clear_job_options(opt); | |
1623 opt->jo_mode = MODE_RAW; | |
1624 opt->jo_out_mode = MODE_RAW; | |
1625 opt->jo_err_mode = MODE_RAW; | |
1626 opt->jo_set = JO_MODE | JO_OUT_MODE | JO_ERR_MODE; | |
1627 | |
1628 opt->jo_io[PART_OUT] = JIO_BUFFER; | |
1629 opt->jo_io[PART_ERR] = JIO_BUFFER; | |
1630 opt->jo_set |= JO_OUT_IO + JO_ERR_IO; | |
1631 | |
1632 opt->jo_modifiable[PART_OUT] = 0; | |
1633 opt->jo_modifiable[PART_ERR] = 0; | |
1634 opt->jo_set |= JO_OUT_MODIFIABLE + JO_ERR_MODIFIABLE; | |
1635 | |
1636 opt->jo_io_buf[PART_OUT] = curbuf->b_fnum; | |
1637 opt->jo_io_buf[PART_ERR] = curbuf->b_fnum; | |
1638 opt->jo_pty = TRUE; | |
1639 opt->jo_set |= JO_OUT_BUF + JO_ERR_BUF; | |
1640 | |
1641 opt->jo_term_rows = rows; | |
1642 opt->jo_term_cols = cols; | |
1643 } | |
1644 | |
1645 /* | |
1646 * Create a new vterm and initialize it. | 1665 * Create a new vterm and initialize it. |
1647 */ | 1666 */ |
1648 static void | 1667 static void |
1649 create_vterm(term_T *term, int rows, int cols) | 1668 create_vterm(term_T *term, int rows, int cols) |
1650 { | 1669 { |
2087 */ | 2106 */ |
2088 void | 2107 void |
2089 f_term_start(typval_T *argvars, typval_T *rettv) | 2108 f_term_start(typval_T *argvars, typval_T *rettv) |
2090 { | 2109 { |
2091 char_u *cmd = get_tv_string_chk(&argvars[0]); | 2110 char_u *cmd = get_tv_string_chk(&argvars[0]); |
2092 exarg_T ea; | 2111 jobopt_T opt; |
2093 | 2112 |
2094 if (cmd == NULL) | 2113 if (cmd == NULL) |
2095 return; | 2114 return; |
2096 ea.arg = cmd; | 2115 init_job_options(&opt); |
2097 ex_terminal(&ea); | 2116 /* TODO: allow more job options */ |
2117 if (argvars[1].v_type != VAR_UNKNOWN | |
2118 && get_job_options(&argvars[1], &opt, | |
2119 JO_TIMEOUT_ALL + JO_STOPONEXIT | |
2120 + JO_EXIT_CB + JO_CLOSE_CALLBACK) == FAIL) | |
2121 return; | |
2122 | |
2123 term_start(cmd, &opt); | |
2098 | 2124 |
2099 if (curbuf->b_term != NULL) | 2125 if (curbuf->b_term != NULL) |
2100 rettv->vval.v_number = curbuf->b_fnum; | 2126 rettv->vval.v_number = curbuf->b_fnum; |
2101 } | 2127 } |
2102 | 2128 |
2107 f_term_wait(typval_T *argvars, typval_T *rettv UNUSED) | 2133 f_term_wait(typval_T *argvars, typval_T *rettv UNUSED) |
2108 { | 2134 { |
2109 buf_T *buf = term_get_buf(argvars); | 2135 buf_T *buf = term_get_buf(argvars); |
2110 | 2136 |
2111 if (buf == NULL) | 2137 if (buf == NULL) |
2112 return; | 2138 { |
2139 ch_log(NULL, "term_wait(): invalid argument"); | |
2140 return; | |
2141 } | |
2113 | 2142 |
2114 /* Get the job status, this will detect a job that finished. */ | 2143 /* Get the job status, this will detect a job that finished. */ |
2115 if (buf->b_term->tl_job != NULL) | 2144 if (buf->b_term->tl_job == NULL |
2116 (void)job_status(buf->b_term->tl_job); | 2145 || STRCMP(job_status(buf->b_term->tl_job), "dead") == 0) |
2117 | 2146 { |
2118 /* Check for any pending channel I/O. */ | 2147 /* The job is dead, keep reading channel I/O until the channel is |
2119 vpeekc_any(); | 2148 * closed. */ |
2120 ui_delay(10L, FALSE); | 2149 while (buf->b_term != NULL && !buf->b_term->tl_channel_closed) |
2121 | 2150 { |
2122 /* Flushing messages on channels is hopefully sufficient. | 2151 mch_char_avail(); |
2123 * TODO: is there a better way? */ | 2152 parse_queued_messages(); |
2124 parse_queued_messages(); | 2153 ui_delay(10L, FALSE); |
2154 } | |
2155 mch_char_avail(); | |
2156 parse_queued_messages(); | |
2157 } | |
2158 else | |
2159 { | |
2160 mch_char_avail(); | |
2161 parse_queued_messages(); | |
2162 | |
2163 /* Wait for 10 msec for any channel I/O. */ | |
2164 /* TODO: use delay from optional argument */ | |
2165 ui_delay(10L, TRUE); | |
2166 mch_char_avail(); | |
2167 | |
2168 /* Flushing messages on channels is hopefully sufficient. | |
2169 * TODO: is there a better way? */ | |
2170 parse_queued_messages(); | |
2171 } | |
2125 } | 2172 } |
2126 | 2173 |
2127 # ifdef WIN3264 | 2174 # ifdef WIN3264 |
2128 | 2175 |
2129 /************************************** | 2176 /************************************** |
2207 * Create a new terminal of "rows" by "cols" cells. | 2254 * Create a new terminal of "rows" by "cols" cells. |
2208 * Store a reference in "term". | 2255 * Store a reference in "term". |
2209 * Return OK or FAIL. | 2256 * Return OK or FAIL. |
2210 */ | 2257 */ |
2211 static int | 2258 static int |
2212 term_and_job_init(term_T *term, int rows, int cols, char_u *cmd) | 2259 term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt) |
2213 { | 2260 { |
2214 WCHAR *p; | 2261 WCHAR *p; |
2215 channel_T *channel = NULL; | 2262 channel_T *channel = NULL; |
2216 job_T *job = NULL; | 2263 job_T *job = NULL; |
2217 jobopt_T opt; | |
2218 DWORD error; | 2264 DWORD error; |
2219 HANDLE jo = NULL, child_process_handle, child_thread_handle; | 2265 HANDLE jo = NULL, child_process_handle, child_thread_handle; |
2220 void *winpty_err; | 2266 void *winpty_err; |
2221 void *spawn_config = NULL; | 2267 void *spawn_config = NULL; |
2222 | 2268 |
2296 winpty_spawn_config_free(spawn_config); | 2342 winpty_spawn_config_free(spawn_config); |
2297 vim_free(p); | 2343 vim_free(p); |
2298 | 2344 |
2299 create_vterm(term, rows, cols); | 2345 create_vterm(term, rows, cols); |
2300 | 2346 |
2301 setup_job_options(&opt, rows, cols); | 2347 channel_set_job(channel, job, opt); |
2302 channel_set_job(channel, job, &opt); | |
2303 | 2348 |
2304 job->jv_channel = channel; | 2349 job->jv_channel = channel; |
2305 job->jv_proc_info.hProcess = child_process_handle; | 2350 job->jv_proc_info.hProcess = child_process_handle; |
2306 job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle); | 2351 job->jv_proc_info.dwProcessId = GetProcessId(child_process_handle); |
2307 job->jv_job_object = jo; | 2352 job->jv_job_object = jo; |
2379 * Start job for "cmd". | 2424 * Start job for "cmd". |
2380 * Store the pointers in "term". | 2425 * Store the pointers in "term". |
2381 * Return OK or FAIL. | 2426 * Return OK or FAIL. |
2382 */ | 2427 */ |
2383 static int | 2428 static int |
2384 term_and_job_init(term_T *term, int rows, int cols, char_u *cmd) | 2429 term_and_job_init(term_T *term, int rows, int cols, char_u *cmd, jobopt_T *opt) |
2385 { | 2430 { |
2386 typval_T argvars[2]; | 2431 typval_T argvars[2]; |
2387 jobopt_T opt; | |
2388 | 2432 |
2389 create_vterm(term, rows, cols); | 2433 create_vterm(term, rows, cols); |
2390 | 2434 |
2391 /* TODO: if the command is "NONE" only create a pty. */ | 2435 /* TODO: if the command is "NONE" only create a pty. */ |
2392 argvars[0].v_type = VAR_STRING; | 2436 argvars[0].v_type = VAR_STRING; |
2393 argvars[0].vval.v_string = cmd; | 2437 argvars[0].vval.v_string = cmd; |
2394 setup_job_options(&opt, rows, cols); | 2438 |
2395 term->tl_job = job_start(argvars, &opt); | 2439 term->tl_job = job_start(argvars, opt); |
2396 if (term->tl_job != NULL) | 2440 if (term->tl_job != NULL) |
2397 ++term->tl_job->jv_refcount; | 2441 ++term->tl_job->jv_refcount; |
2398 | 2442 |
2399 return term->tl_job != NULL | 2443 return term->tl_job != NULL |
2400 && term->tl_job->jv_channel != NULL | 2444 && term->tl_job->jv_channel != NULL |