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

Raspunde prin e-mail lui