Reputation: 1698
Problem: There is a C application that calls a Bash script each time an event happens. And there is also a C++ application that needs to track down those events. The C++ application is driven by a select() event loop. What would be the most simplest IPC to implement between Bash script and C++ application?
C Application ---Each time calls Bash script---> Bash application ---???---> C++ Application
Few solutions that came into my mind:
Is there something simpler that would allow me to use only one File Descriptor in select()?
Upvotes: 5
Views: 3330
Reputation: 136435
Unix datagram or UDP socket would do. The bash script would just send a datagram to that socket (you may need a helper program that does sendmsg()
or sendto()
on that socket, such as socat or netcat/nc). The receiver does not need to accept connections for datagram sockets, once it is ready for read there must be a datagram waiting. Subject to datagram length restrictions.
Upvotes: 3
Reputation: 54081
I would do it with a unnamed pipe()
. Remember: In UNIX file descriptors remain open after fork()
and execve()
in both processes! So you can use pipe()
to get a pair of file descriptors and then write into the fd using bash's echo >&FD
where FD
is the file descriptor number.
That is very straight forward, easy and uses less resources than anything else I assume. The use of select()
is no problem, just do not block on read()
is I do in my sample but select()
on pfds[0]
.
Sample program (spawns 10 bash processes which send 'hello work, my pid: XXX', waiting 1s between spawning the processes. The sample only uses one pipe for all the children. I changed it that way because the author asked about that. In practice, I would NOT recommend it (see the note below the sample)):
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
int main(int argc, char **argv) {
int pfds[2];
pid_t p;
assert(0 == pipe(pfds));
p = fork();
if (p == 0) {
unsigned int i;
char str_fd[3]; char *env[] = {NULL};
char *args[] = { "/bin/bash", "-c", "echo >&$1 hello world, my pid: $$"
, "-s", str_fd, NULL};
snprintf(str_fd, 3, "%d", pfds[1]);
str_fd[2] = 0;
for (i = 0; i < 10; i++) {
p = fork();
if(0 == p) {
assert(0 ==
execve( "/bin/bash", (char *const*)args
, (char *const*)env));
} else if (0 > p) {
perror("fork");
exit(1);
} else {
wait(NULL);
}
sleep(1);
}
} else if(p > 0) {
char *buf = malloc(100);
ssize_t sz;
printf("fd is %d, <hit Ctrl+C to exit>\n", pfds[1]);
while(0 < ( sz = read(pfds[0], buf, 100))) {
buf[99] = 0;
printf("received: '%s'\n", buf);
}
free(buf);
if (0 == sz) {
fprintf(stderr, "EOF!");
} else {
perror("read from bash failed");
}
wait(NULL);
} else {
perror("fork failed");
exit(1);
}
return 0;
}
sample program output:
$ gcc test.c && ./a.out
fd is 4, <hit Ctrl+C to exit>
received: 'hello world, my pid: 779
'
received: 'hello world, my pid: 780
'
received: 'hello world, my pid: 781
'
received: 'hello world, my pid: 782
'
received: 'hello world, my pid: 783
'
received: 'hello world, my pid: 784
'
received: 'hello world, my pid: 785
'
received: 'hello world, my pid: 786
'
received: 'hello world, my pid: 787
'
received: 'hello world, my pid: 788
'
works, bashs send 'hello world, my pid: XXX\n' to parent process all using one pipe :-).
Nevertheless that seems to work as the demo program shows (should be ok using the POSIX semantics and tested under Linux and MacOS X), I would recommend using one pipe()
per child process. That will lead to fewer problems and running more than one child process at a time is possible, too. select()
or epoll()
(if you have MANY child processes) are your friend.
Since pipe()
is very cheap, in particular compared to bash!, I would definitely not use the same pipe for more than more than one child (as my updated sample now does).
Upvotes: 3
Reputation: 849
You could use a lightweight messaging queue like ZeroMQ. I think you would use its PUSH-PULL mechanism. Your C program or the bash script pushes events while the C++ application pulls them. ZeroMQ is written in C but, besides many others, there exists a C++ and a Python binding for it. See here for a PUSH-PULL example.
Upvotes: 1