My container creates fifo pipe in the bind mounted dir but container when reads or writes to this pipe, the host cant receive it or vice versa. with similar permissions to create or open pipe works for communication within host and within container. But it does not work in between host and container.
The pipe is created and reads/writes in cpp program within container. Pipe created with both 666 and 777 permissions and opened with O_RDWR. Similar cpp programs reads/writes in the host. Again these program works fine within container and within host the problem happens only in host communication.
docker run -it --name broken-container -v /tmp:/app2 cppdocker
After reading your comments in response to questions from other users, I suspect you're running into the issue that the file permissions you supply to mknod()
(or creat()
/mkdir()
when creating regular files / directories) are still masked by your process's umask. Combine that with the fact docker runs the containers under a different user than the user you're using from the outside, you're likely running into a "permission denied" situation.
Other than "does not work" you didn't post any error message, and the example code you are using doesn't contain any error checking of the system calls you are using. Always check for errors when calling operating system functions! Even so, you'd have noticed the failures had you run your programs through strace
, which would give you some feedback as to which operations fail.
Assuming that my guess here is correct and you are really running into actual permission problems due to the umask, I've taken the example code you've posted and changed three things:
read()
and write()
system calls to properly handle the EINTR
corner case, as well as the write()
system call to loop until all bytes have been written. (This is considered best practice when using blocking reads/writes.)0777
. (I would recommend to do this over changing the umask
of the process, though you could also do that before calling mknod
.)Your receiver code adjusted:
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
int main()
{
static char const* pipeName = "/inside/container/my_pipe";
int rc = mkfifo(pipeName, 0777);
// ignore EEXIST (file already exists)
// since that isn't really an error
if (rc < 0 && errno != EEXIST) {
perror("mkfifo");
return 1;
}
int fd = open(pipeName, O_RDWR);
if (fd < 0) {
perror("open");
return 2;
}
// force 0777 permissions on the device (since the
// process's umask likely caused the actual permissions
// to be lesser)
rc = fchmod(fd, 0777);
if (rc < 0) {
perror("fchmod");
close(fd);
return 2;
}
char buffer[80];
// Properly handle "interrupted system call" in blocking mode
// with a loop
ssize_t bytesRead = -1;
errno = EINTR;
while (bytesRead < 0 && errno == EINTR)
bytesRead = read(fd, buffer, sizeof(buffer) - 1);
if (bytesRead < 0) {
perror("read");
close(fd);
return 3;
}
// For long enough messages the kernel is free to split
// up any data that was sent by the other side, so this
// initial read may only contain partial data. Keep that
// in mind when designing your application.
buffer[bytesRead] = '\0';
close(fd);
std::cout << "Received message: " << buffer << std::endl;
return 0;
}
Your sender code adjusted:
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
int main()
{
static char const* pipeName = "/outside/container/my_pipe"; // Pipe file path
int fd = open(pipeName, O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
static char const* message = "hello";
char const* ptr = message;
size_t remaining = strlen(message) + 1;
// Loop to ensure that we write all of the data
// Since write() is blocking anyway this appears
// to be the desired behavior?.
// For short messages like this example this will
// typically execute the loop only once.
while (remaining > 0) {
ssize_t bytesWritten = write(fd, ptr, remaining);
if (bytesWritten < 0) {
// Properly handle the "interrupted system call"
// condition
if (errno == EINTR)
continue;
perror("write");
close(fd);
return 2;
}
remaining -= static_cast<size_t>(bytesWritten);
ptr += static_cast<size_t>(bytesWritten);
}
close(fd);
return 0;
}
Running this on my system with a standard Docker container (no customization) works.
And if my guess is wrong and it's not the umask
you'll at least get a proper error message why the operation failed.