Permalink
Browse files

patch 8.0.0744: terminal window does not use a pty

Problem:    A terminal window uses pipes instead of a pty.
Solution:   Add pty support.
  • Loading branch information...
brammool committed Jul 22, 2017
1 parent 825680f commit 5a1feb809191e236cadd2884a5f57ad26cd213a3
Showing with 102 additions and 64 deletions.
  1. +18 −3 src/channel.c
  2. +75 −57 src/os_unix.c
  3. +1 −1 src/os_win32.c
  4. +1 −1 src/proto/os_unix.pro
  5. +1 −1 src/proto/os_win32.pro
  6. +1 −0 src/structs.h
  7. +3 −1 src/terminal.c
  8. +2 −0 src/version.c
View
@@ -1013,7 +1013,16 @@ ch_close_part(channel_T *channel, ch_part_T part)
if (part == PART_SOCK)
sock_close(*fd);
else
fd_close(*fd);
{
/* When using a pty the same FD is set on multiple parts, only
* close it when the last reference is closed. */
if ((part == PART_IN || channel->ch_part[PART_IN].ch_fd != *fd)
&& (part == PART_OUT
|| channel->ch_part[PART_OUT].ch_fd != *fd)
&& (part == PART_ERR
|| channel->ch_part[PART_ERR].ch_fd != *fd))
fd_close(*fd);
}
*fd = INVALID_FD;
channel->ch_to_be_closed &= ~(1 << part);
@@ -4280,6 +4289,12 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
opt->jo_io_name[part] =
get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]);
}
else if (STRCMP(hi->hi_key, "pty") == 0)
{
if (!(supported & JO_MODE))
break;
opt->jo_pty = get_tv_number(item);
}
else if (STRCMP(hi->hi_key, "in_buf") == 0
|| STRCMP(hi->hi_key, "out_buf") == 0
|| STRCMP(hi->hi_key, "err_buf") == 0)
@@ -5074,10 +5089,10 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
ch_logs(NULL, "Starting job: %s", (char *)ga.ga_data);
ga_clear(&ga);
}
mch_start_job(argv, job, &opt);
mch_job_start(argv, job, &opt);
#else
ch_logs(NULL, "Starting job: %s", (char *)cmd);
mch_start_job((char *)cmd, job, &opt);
mch_job_start((char *)cmd, job, &opt);
#endif
/* If the channel is reading from a buffer, write lines now. */
View
@@ -4096,6 +4096,32 @@ set_default_child_environment(void)
{
set_child_environment(Rows, Columns, "dumb");
}
#endif
#if defined(FEAT_GUI) || defined(FEAT_JOB_CHANNEL)
static void
open_pty(int *pty_master_fd, int *pty_slave_fd)
{
char *tty_name;
*pty_master_fd = OpenPTY(&tty_name); /* open pty */
if (*pty_master_fd >= 0)
{
/* Leaving out O_NOCTTY may lead to waitpid() always returning
* 0 on Mac OS X 10.7 thereby causing freezes. Let's assume
* adding O_NOCTTY always works when defined. */
#ifdef O_NOCTTY
*pty_slave_fd = open(tty_name, O_RDWR | O_NOCTTY | O_EXTRA, 0);
#else
*pty_slave_fd = open(tty_name, O_RDWR | O_EXTRA, 0);
#endif
if (*pty_slave_fd < 0)
{
close(*pty_master_fd);
*pty_master_fd = -1;
}
}
}
#endif
int
@@ -4206,7 +4232,6 @@ mch_call_shell(
int pty_master_fd = -1; /* for pty's */
# ifdef FEAT_GUI
int pty_slave_fd = -1;
char *tty_name;
# endif
int fd_toshell[2]; /* for pipes */
int fd_fromshell[2];
@@ -4269,25 +4294,7 @@ mch_call_shell(
* If the slave can't be opened, close the master pty.
*/
if (p_guipty && !(options & (SHELL_READ|SHELL_WRITE)))
{
pty_master_fd = OpenPTY(&tty_name); /* open pty */
if (pty_master_fd >= 0)
{
/* Leaving out O_NOCTTY may lead to waitpid() always returning
* 0 on Mac OS X 10.7 thereby causing freezes. Let's assume
* adding O_NOCTTY always works when defined. */
#ifdef O_NOCTTY
pty_slave_fd = open(tty_name, O_RDWR | O_NOCTTY | O_EXTRA, 0);
#else
pty_slave_fd = open(tty_name, O_RDWR | O_EXTRA, 0);
#endif
if (pty_slave_fd < 0)
{
close(pty_master_fd);
pty_master_fd = -1;
}
}
}
open_pty(&pty_master_fd, &pty_slave_fd);
/*
* If not opening a pty or it didn't work, try using pipes.
*/
@@ -5100,12 +5107,14 @@ mch_call_shell(
#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
void
mch_start_job(char **argv, job_T *job, jobopt_T *options)
mch_job_start(char **argv, job_T *job, jobopt_T *options)
{
pid_t pid;
int fd_in[2]; /* for stdin */
int fd_out[2]; /* for stdout */
int fd_err[2]; /* for stderr */
int pty_master_fd = -1;
int pty_slave_fd = -1;
channel_T *channel = NULL;
int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
@@ -5128,6 +5137,9 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
fd_err[0] = -1;
fd_err[1] = -1;
if (options->jo_pty)
open_pty(&pty_master_fd, &pty_slave_fd);
/* TODO: without the channel feature connect the child to /dev/null? */
/* Open pipes for stdin, stdout, stderr. */
if (use_file_for_in)
@@ -5141,7 +5153,7 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
goto failed;
}
}
else if (!use_null_for_in && pipe(fd_in) < 0)
else if (!use_null_for_in && pty_master_fd < 0 && pipe(fd_in) < 0)
goto failed;
if (use_file_for_out)
@@ -5155,7 +5167,7 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
goto failed;
}
}
else if (!use_null_for_out && pipe(fd_out) < 0)
else if (!use_null_for_out && pty_master_fd < 0 && pipe(fd_out) < 0)
goto failed;
if (use_file_for_err)
@@ -5169,7 +5181,8 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
goto failed;
}
}
else if (!use_out_for_err && !use_null_for_err && pipe(fd_err) < 0)
else if (!use_out_for_err && !use_null_for_err
&& pty_master_fd < 0 && pipe(fd_err) < 0)
goto failed;
if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
@@ -5224,54 +5237,53 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
/* set up stdin for the child */
close(0);
if (use_null_for_in && null_fd >= 0)
{
close(0);
ignored = dup(null_fd);
}
else if (fd_in[0] < 0)
ignored = dup(pty_slave_fd);
else
{
if (!use_file_for_in)
close(fd_in[1]);
close(0);
ignored = dup(fd_in[0]);
close(fd_in[0]);
}
/* set up stderr for the child */
close(2);
if (use_null_for_err && null_fd >= 0)
{
close(2);
ignored = dup(null_fd);
stderr_works = FALSE;
}
else if (use_out_for_err)
{
close(2);
ignored = dup(fd_out[1]);
}
else if (fd_err[1] < 0)
ignored = dup(pty_slave_fd);
else
{
if (!use_file_for_err)
close(fd_err[0]);
close(2);
ignored = dup(fd_err[1]);
close(fd_err[1]);
}
/* set up stdout for the child */
close(1);
if (use_null_for_out && null_fd >= 0)
{
close(1);
ignored = dup(null_fd);
}
else if (fd_out[1] < 0)
ignored = dup(pty_slave_fd);
else
{
if (!use_file_for_out)
close(fd_out[0]);
close(1);
ignored = dup(fd_out[1]);
if (fd_in[0] >= 0)
close(fd_in[0]);
if (fd_in[1] >= 0)
close(fd_in[1]);
if (fd_out[0] >= 0)
close(fd_out[0]);
if (fd_out[1] >= 0)
close(fd_out[1]);
if (fd_err[0] >= 0)
close(fd_err[0]);
if (fd_err[1] >= 0)
close(fd_err[1]);
if (pty_master_fd >= 0)
{
close(pty_master_fd); /* not used */
close(pty_slave_fd); /* duped above */
}
if (null_fd >= 0)
@@ -5296,7 +5308,9 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
job->jv_status = JOB_STARTED;
job->jv_channel = channel; /* ch_refcount was set above */
/* child stdin, stdout and stderr */
if (pty_master_fd >= 0)
close(pty_slave_fd); /* duped above */
/* close child stdin, stdout and stderr */
if (!use_file_for_in && fd_in[0] >= 0)
close(fd_in[0]);
if (!use_file_for_out && fd_out[1] >= 0)
@@ -5306,12 +5320,12 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
if (channel != NULL)
{
channel_set_pipes(channel,
use_file_for_in || use_null_for_in
? INVALID_FD : fd_in[1],
use_file_for_out || use_null_for_out
? INVALID_FD : fd_out[0],
use_out_for_err || use_file_for_err || use_null_for_err
? INVALID_FD : fd_err[0]);
use_file_for_in || use_null_for_in
? INVALID_FD : fd_in[1] < 0 ? pty_master_fd : fd_in[1],
use_file_for_out || use_null_for_out
? INVALID_FD : fd_out[0] < 0 ? pty_master_fd : fd_out[0],
use_out_for_err || use_file_for_err || use_null_for_err
? INVALID_FD : fd_err[0] < 0 ? pty_master_fd : fd_err[0]);
channel_set_job(channel, job, options);
}
@@ -5332,6 +5346,10 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options)
close(fd_err[0]);
if (fd_err[1] >= 0)
close(fd_err[1]);
if (pty_master_fd >= 0)
close(pty_master_fd);
if (pty_slave_fd >= 0)
close(pty_slave_fd);
}
char *
View
@@ -4964,7 +4964,7 @@ job_io_file_open(
}
void
mch_start_job(char *cmd, job_T *job, jobopt_T *options)
mch_job_start(char *cmd, job_T *job, jobopt_T *options)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
View
@@ -57,7 +57,7 @@ void mch_set_shellsize(void);
void mch_new_shellsize(void);
int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc);
int mch_call_shell(char_u *cmd, int options);
void mch_start_job(char **argv, job_T *job, jobopt_T *options);
void mch_job_start(char **argv, job_T *job, jobopt_T *options);
char *mch_job_status(job_T *job);
job_T *mch_detect_ended_job(job_T *job_list);
int mch_stop_job(job_T *job, char_u *how);
View
@@ -41,7 +41,7 @@ void mch_set_shellsize(void);
void mch_new_shellsize(void);
void mch_set_winsize_now(void);
int mch_call_shell(char_u *cmd, int options);
void mch_start_job(char *cmd, job_T *job, jobopt_T *options);
void mch_job_start(char *cmd, job_T *job, jobopt_T *options);
char *mch_job_status(job_T *job);
job_T *mch_detect_ended_job(job_T *job_list);
int mch_stop_job(job_T *job, char_u *how);
View
@@ -1705,6 +1705,7 @@ typedef struct
char_u jo_io_name_buf[4][NUMBUFLEN];
char_u *jo_io_name[4]; /* not allocated! */
int jo_io_buf[4];
int jo_pty;
int jo_modifiable[4];
int jo_message[4];
channel_T *jo_channel;
View
@@ -34,7 +34,6 @@
* TODO:
* - When 'termsize' is set and dragging the separator the terminal gets messed
* up.
* - Use a pty for I/O with the job.
* - set buffer options to be scratch, hidden, nomodifiable, etc.
* - set buffer name to command, add (1) to avoid duplicates.
* - If [command] is not given the 'shell' option is used.
@@ -52,6 +51,8 @@
* - add test for giving error for invalid 'termsize' value.
* - support minimal size when 'termsize' is "rows*cols".
* - support minimal size when 'termsize' is empty?
* - implement "term" for job_start(): more job options when starting a
* terminal.
* - implement ":buf {term-buf-name}"
* - implement term_list() list of buffers with a terminal
* - implement term_getsize(buf)
@@ -673,6 +674,7 @@ setup_job_options(jobopt_T *opt, int rows, int cols)
opt->jo_set |= JO_OUT_IO + (JO_OUT_IO << (PART_ERR - PART_OUT));
opt->jo_io_buf[PART_OUT] = curbuf->b_fnum;
opt->jo_io_buf[PART_ERR] = curbuf->b_fnum;
opt->jo_pty = TRUE;
opt->jo_set |= JO_OUT_BUF + (JO_OUT_BUF << (PART_ERR - PART_OUT));
opt->jo_term_rows = rows;
opt->jo_term_cols = cols;
View
@@ -769,6 +769,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
744,
/**/
743,
/**/

4 comments on commit 5a1feb8

@mattn

This comment has been minimized.

Show comment
Hide comment
@mattn

mattn Jul 22, 2017

I got build error on Linux

gcc -c -I. -Iproto -DHAVE_CONFIG_H     -g -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1        version.c -o objects/version.o
link.sh: $LINK_AS_NEEDED set to 'yes': invoking linker directly.
  gcc   -L/usr/local/lib -Wl,--as-needed        -o vim objects/arabic.o objects/buffer.o objects/blowfish.o objects/crypt.o objects/crypt_zip.o objects/dict.o objects/diff.o objects/digraph.o objects/edit.o objects/eval.o objects/evalfunc.o objects/ex_cmds.o objects/ex_cmds2.o objects/ex_docmd.o objects/ex_eval.o objects/ex_getln.o objects/farsi.o objects/fileio.o objects/fold.o objects/getchar.o objects/hardcopy.o objects/hashtab.o  objects/if_cscope.o objects/if_xcmdsrv.o objects/list.o objects/mark.o objects/memline.o objects/menu.o objects/misc1.o objects/misc2.o objects/move.o objects/mbyte.o objects/normal.o objects/ops.o objects/option.o objects/os_unix.o objects/pathdef.o objects/popupmnu.o objects/quickfix.o objects/regexp.o objects/screen.o objects/search.o objects/sha256.o objects/spell.o objects/spellfile.o objects/syntax.o objects/tag.o objects/term.o objects/terminal.o objects/ui.o objects/undo.o objects/userfunc.o objects/version.o objects/window.o  objects/term_encoding.o objects/term_keyboard.o objects/term_mouse.o objects/term_parser.o objects/term_pen.o objects/term_screen.o objects/term_state.o objects/term_unicode.o objects/term_vterm.o          objects/netbeans.o objects/channel.o  objects/charset.o objects/json.o objects/main.o objects/memfile.o objects/message.o    -lSM -lICE -lXpm -lXt -lX11 -lXdmcp -lSM -lICE  -lm -ltinfo -lnsl  -ldl
objects/os_unix.o: In function `open_pty':
/home/mattn/dev/vim/src/os_unix.c:4107: undefined reference to `OpenPTY'
collect2: error: ld returned 1 exit status
link.sh: Linking failed
Makefile:1930: recipe for target 'vim' failed
make: *** [vim] Error 1

mattn replied Jul 22, 2017

I got build error on Linux

gcc -c -I. -Iproto -DHAVE_CONFIG_H     -g -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1        version.c -o objects/version.o
link.sh: $LINK_AS_NEEDED set to 'yes': invoking linker directly.
  gcc   -L/usr/local/lib -Wl,--as-needed        -o vim objects/arabic.o objects/buffer.o objects/blowfish.o objects/crypt.o objects/crypt_zip.o objects/dict.o objects/diff.o objects/digraph.o objects/edit.o objects/eval.o objects/evalfunc.o objects/ex_cmds.o objects/ex_cmds2.o objects/ex_docmd.o objects/ex_eval.o objects/ex_getln.o objects/farsi.o objects/fileio.o objects/fold.o objects/getchar.o objects/hardcopy.o objects/hashtab.o  objects/if_cscope.o objects/if_xcmdsrv.o objects/list.o objects/mark.o objects/memline.o objects/menu.o objects/misc1.o objects/misc2.o objects/move.o objects/mbyte.o objects/normal.o objects/ops.o objects/option.o objects/os_unix.o objects/pathdef.o objects/popupmnu.o objects/quickfix.o objects/regexp.o objects/screen.o objects/search.o objects/sha256.o objects/spell.o objects/spellfile.o objects/syntax.o objects/tag.o objects/term.o objects/terminal.o objects/ui.o objects/undo.o objects/userfunc.o objects/version.o objects/window.o  objects/term_encoding.o objects/term_keyboard.o objects/term_mouse.o objects/term_parser.o objects/term_pen.o objects/term_screen.o objects/term_state.o objects/term_unicode.o objects/term_vterm.o          objects/netbeans.o objects/channel.o  objects/charset.o objects/json.o objects/main.o objects/memfile.o objects/message.o    -lSM -lICE -lXpm -lXt -lX11 -lXdmcp -lSM -lICE  -lm -ltinfo -lnsl  -ldl
objects/os_unix.o: In function `open_pty':
/home/mattn/dev/vim/src/os_unix.c:4107: undefined reference to `OpenPTY'
collect2: error: ld returned 1 exit status
link.sh: Linking failed
Makefile:1930: recipe for target 'vim' failed
make: *** [vim] Error 1
@mattn

This comment has been minimized.

Show comment
Hide comment
@mattn

mattn Jul 22, 2017

Ah, sorry. cnofigure script should be re-generated again.

mattn replied Jul 22, 2017

Ah, sorry. cnofigure script should be re-generated again.

@k-takata

This comment has been minimized.

Show comment
Hide comment
@k-takata

k-takata Jul 22, 2017

Member

Currently, pty.c is compiled/linked when only GUI is enabled.
So, this error occurs when GUI is disabled. src/Makefile should be updated.

Member

k-takata replied Jul 22, 2017

Currently, pty.c is compiled/linked when only GUI is enabled.
So, this error occurs when GUI is disabled. src/Makefile should be updated.

@nuko8

This comment has been minimized.

Show comment
Hide comment
@nuko8

nuko8 Jul 22, 2017

So, this error occurs when GUI is disabled. src/Makefile should be updated.

Looks like objects/pty.o needs to be added to OBJ_COMMON

Plus, since

os_unix.c:4107:22: warning: implicit declaration of function 'OpenPTY' [-Wimplicit-function- declaration]
pty_master_fd = OpenPTY(&tty_name); / open pty */
^
1 warning generated.

the prototype of OpenPTY() needs to be declared somewhere in a header.

nuko8 replied Jul 22, 2017

So, this error occurs when GUI is disabled. src/Makefile should be updated.

Looks like objects/pty.o needs to be added to OBJ_COMMON

Plus, since

os_unix.c:4107:22: warning: implicit declaration of function 'OpenPTY' [-Wimplicit-function- declaration]
pty_master_fd = OpenPTY(&tty_name); / open pty */
^
1 warning generated.

the prototype of OpenPTY() needs to be declared somewhere in a header.

Please sign in to comment.