I enhanced my test case now:
It should start a sub process and talk to it using pipes.
When sending input has finished it should close stdin of the subprocess
causing it to quit. It doesn't quit. Neither does stdin close.
What am I doing wrong here?
I'd like to make Vim talk to subprocesses.
I already spend several hours on this piece of code which still doesn't
work as expected. Can you give me a hint?
== SOURCE ==
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int PIPE_END[2];
// TODO close on exec
// fork-exec implementation
// TODO redirect stderr as well?
//
//
// returns: pid via ppid
// -1 if fork fails
// 0 on success ( TODOs !! )
int fork_interactive_process(pid_t * ppid, PIPE_END * pipe_r_w, char
*command, char *argv[])
{
pid_t pid;
pid = fork();
switch (pid) {
case -1:
return -1;
case 0: /* child */
fprintf(stderr,"fork-exec-child: started\n");
// TODO error message should be passed to Vim! (echoe !)
// use pipe as stdin:
if (dup2((*pipe_r_w)[0], STDIN_FILENO) == -1){
perror("error duplicating stdin\n");
exit(1);
}
fprintf(stderr,"fork-exec-child: stdin duped\n");
// use pipe as stdout:
if (dup2((*pipe_r_w)[1], STDOUT_FILENO) == -1){
perror("error duplicating stdout\n");
exit(1);
}
fprintf(stderr, "fork-exec-child: stdout duped\n");
fprintf(stderr, "fork-exec-child: launching subprocess\n");
// run application using *p for convinince (?)
if (-1 == execvp(command, argv)){
// TODO: print errno
fprintf(stderr,"failed running command %s\n", command);
exit(1);
}
}
if (pid)
*ppid = pid;
// parent
fprintf(stderr,"parent is continuing\n");
return 0;
}
void my_write(int fd, void *buf, size_t count){
if (!write(fd, buf, count)){
fprintf(stderr, "failed writing bytes!\n");
exit(1);
}
}
int my_read(int fd, void *buf, size_t count){
int readB = -1;
while (readB < 0){
readB = read(fd, buf, count);
if (!readB){
fprintf(stderr,"not received, sleeping %d \n", readB);
sleep(1);
}
}
if (readB <= 0)
fprintf(stderr,"read error: %d\n", readB);
return readB;
}
char nr2char(int i){
return 'A' + (i % 20);
}
void close_on_exec(int fd){
long flags = fcntl(fd, F_GETFD);
if (-1 == flags){
fprintf(stderr,"error getting fd settings: \n");
return;
}
// arg = arg || FD_CLOEXEC;
if (-1 == fcntl(fd, F_SETFD, flags | FD_CLOEXEC)){
fprintf(stderr,"error setting fd settings: \n");
}
}
int main(int argc, char *argv[])
{
// Have to use two pipes because depending on uniderectional pipes is
// not portable according to man 2 pipe
//
PIPE_END pipe_in, pipe_out; // as ssen from to be executed process
PIPE_END pipe_r_w;
unsigned int i;
char* catargs[4];
char * testcommand = "/bin/sh";
unsigned char inBuf;
if (pipe(pipe_in) == -1) {
perror("pipe in");
exit(EXIT_FAILURE);
}
if (pipe(pipe_out) == -1) {
perror("pipe out");
exit(EXIT_FAILURE);
}
close_on_exec(pipe_in[0]);
close_on_exec(pipe_in[1]);
close_on_exec(pipe_out[0]);
close_on_exec(pipe_out[1]);
//close(pipe_in[1]); /* Close unused my_write end */
//close(pipe_out[1]);
pipe_r_w[1] = pipe_out[1];
pipe_r_w[0] = pipe_in[0];
pid_t subprocess;
catargs[0] = "sh";
catargs[1] = "-c";
catargs[2] = "while read -n1 c; do echo \"got: $c\" 1>&2; echo -n
\"$c\"; done";
catargs[3] = NULL;
// catargs[0] = "cat";
// catargs[1] = NULL;
if (0 != fork_interactive_process(&subprocess, &pipe_r_w,
testcommand, &catargs)){
perror("failed running fork_interactive_process");
exit(0);
}
pid_t cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
#define MAX 10
#define WRITE_CHAR_MSG(msg, c) \
fprintf(stderr, msg); \
write(STDERR_FILENO, &c, 1); \
fprintf(stderr, "\n");
if (cpid == 0) { /* Child reads from pipe */
for (i = 0; i < MAX; i++) {
char c = nr2char(i);
WRITE_CHAR_MSG("read-process: reading char: ", c)
my_read(pipe_out[0], &inBuf, 1);
// sollten gleich sei
WRITE_CHAR_MSG("read-process: read: ", inBuf)
}
char buffer[200];
snprintf(&buffer[0], 200, "lsof -p %d", subprocess);
printf("%s\n", &buffer[0]);
system(&buffer[0]);
printf("read-process: reading additional byte\n");
my_read(pipe_out[0], &inBuf, 1);
WRITE_CHAR_MSG("read-process: read additional: ", inBuf)
exit(0);
//_exit(EXIT_SUCCESS);
}
for (i = 0; i < MAX; i++) {
char c = nr2char(i);
WRITE_CHAR_MSG("writing char: ", c)
my_write(pipe_in[1], &c, 1);
}
close(pipe_in[1]);
close(pipe_in[0]);
fprintf(stderr, "pipe_in closed\n");
int exitstatus;
fprintf(stderr,"waiting for child\n");
waitpid(cpid, &exitstatus, 0);
fprintf(stderr,"child terminated ? %d\n", exitstatus);
exit(0);
}
== OUTPUT ==
test i%./interactive_process 2>&1
/pr/tasks/vim-pipes/ws/test nixos
fork-exec-child: started
fork-exec-child: stdin duped
fork-exec-child: stdout duped
fork-exec-child: launching subprocess
parent is continuing
read-process: reading char: A
writing char: A
got: A
read-process: read: A
read-process: reading char: B
writing char: B
got: B
read-process: read: B
read-process: reading char: C
writing char: C
got: C
read-process: read: C
read-process: reading char: D
writing char: D
got: D
read-process: read: D
read-process: reading char: E
writing char: E
got: E
read-process: read: E
read-process: reading char: F
writing char: F
got: F
read-process: read: F
read-process: reading char: G
writing char: G
got: G
read-process: read: G
read-process: reading char: H
writing char: H
got: H
read-process: read: H
read-process: reading char: I
writing char: I
got: I
read-process: read: I
read-process: reading char: J
writing char: J
got: J
read-process: read: J
lsof -p 31297
pipe_in closed
waiting for child
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sh 31297 marc cwd DIR 253,2 528 8339463
/pr/tasks/vim-pipes/ws/test
sh 31297 marc rtd DIR 8,5 1024 2 /
sh 31297 marc txt REG 253,7 820522 49678735
/nix/store/lcm02rcg07hardh5isgpkbi34bpsiqxv-bash-4.1-p2/bin/bash
sh 31297 marc mem REG 253,7 98336576 49668088
/nix/store/pwmviky25cc37anyzninm1ryzrpv3hzm-glibc-locales-2.11.1/lib/locale/locale-archive
sh 31297 marc mem REG 253,7 1682768 49640440
/nix/store/4jl83jgzaacf519h3wczgbjvqi91hfk6-glibc-2.11.1/lib/libc-2.11.1.so
sh 31297 marc mem REG 253,7 19042 49640452
/nix/store/4jl83jgzaacf519h3wczgbjvqi91hfk6-glibc-2.11.1/lib/libdl-2.11.1.so
sh 31297 marc mem REG 253,7 394662 49669923
/nix/store/36013y0biydzbdq47q9zp3gfgb0kgpvl-ncurses-5.7/lib/libncursesw.so.5.7
sh 31297 marc mem REG 253,7 38812 49674167
/nix/store/qsxa7dr3n3w5hqhhjbn3hbz0ldavm818-readline-6.1/lib/libhistory.so.6.1
sh 31297 marc mem REG 253,7 303513 49674170
/nix/store/qsxa7dr3n3w5hqhhjbn3hbz0ldavm818-readline-6.1/lib/libreadline.so.6.1
sh 31297 marc mem REG 253,7 146546 49640428
/nix/store/4jl83jgzaacf519h3wczgbjvqi91hfk6-glibc-2.11.1/lib/ld-2.11.1.so
sh 31297 marc 0r FIFO 0,6 0t0 22713017 pipe
sh 31297 marc 1w FIFO 0,6 0t0 22713018 pipe
sh 31297 marc 2u CHR 136,104 0t0 106 /dev/pts/104
read-process: reading additional byte
note: close on exec did work.
Now what to do to make /bin/sh receive an EOF on stdin so that it quits?
htop shows:
`- zsh
`- ./interactive_process
`- ./interactive_process
`- sh -c while read -n1 c; do echo "got: $c" 1>&2; echo [..]
So the sh subprocess is still running, pipe_in closed, the line was
printed. I know that there is a race condition causing losf to print
results before stdin being closed. However I've run lsof manually
minutes later and the output is still the same.
So why doesn't sh quit here? Do I have to send kind of signal?
This shows that /bin/sh quits when you close stdin before starting sh:
read -n1 0<&-
bash: read: read error: 0: Bad file descripto
Any hints highly appreciated. I know there are some C gurus on this list
who can point me into the right direction.
Marc Weber
--
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php