I am having trouble in creating named pipe in Android and the example below illustrates my dilemma:
res = mkfifo("/sdcard/fifo9000", S_IRWXO);
if (res != 0)
{
LOG("Error while creating a pipe (return:%d, errno:%d)", res, errno);
}
The code always prints:
Error while creating a pipe (return:-1, errno:1)
I can't figure out exactly why this fails. The application has android.permission.WRITE_EXTERNAL_STORAGE permissions. I can create normal files with exactly the same name in the same location, but pipe creation fails. The pipe in question should be accessible from multiple applications.
- I suspect that noone can create pipes in /sdcard. Where would it be the best location to do so?
- What mode mast should I set (2nd parameter)?
- Does application need any extra permissions?
Roosmaa's answer is correct -- mkfifo() just calls mknod() to create a special file, and FAT32 doesn't support that.
As an alternative you may want to consider using Linux's "abstract namespace" UNIX-domain sockets. They should be roughly equivalent to a named pipe. You can access them by name, but they're not part of the filesystem, so you don't have to deal with various permission issues. Note the socket is bi-directional.
Since it's a socket, you may need INTERNET permission. Not sure about that.
Here's a quick bit of client/server sample code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
/*
* Create a UNIX-domain socket address in the Linux "abstract namespace".
*
* The socket code doesn't require null termination on the filename, but
* we do it anyway so string functions work.
*/
int makeAddr(const char* name, struct sockaddr_un* pAddr, socklen_t* pSockLen)
{
int nameLen = strlen(name);
if (nameLen >= (int) sizeof(pAddr->sun_path) -1) /* too long? */
return -1;
pAddr->sun_path[0] = '\0'; /* abstract namespace */
strcpy(pAddr->sun_path+1, name);
pAddr->sun_family = AF_LOCAL;
*pSockLen = 1 + nameLen + offsetof(struct sockaddr_un, sun_path);
return 0;
}
int main(int argc, char** argv)
{
static const char* message = "hello, world!";
struct sockaddr_un sockAddr;
socklen_t sockLen;
int result = 1;
if (argc != 2 || (argv[1][0] != 'c' && argv[1][0] != 's')) {
printf("Usage: {c|s}\n");
return 2;
}
if (makeAddr("com.whoever.xfer", &sockAddr, &sockLen) < 0)
return 1;
int fd = socket(AF_LOCAL, SOCK_STREAM, PF_UNIX);
if (fd < 0) {
perror("client socket()");
return 1;
}
if (argv[1][0] == 'c') {
printf("CLIENT %s\n", sockAddr.sun_path+1);
if (connect(fd, (const struct sockaddr*) &sockAddr, sockLen) < 0) {
perror("client connect()");
goto bail;
}
if (write(fd, message, strlen(message)+1) < 0) {
perror("client write()");
goto bail;
}
} else if (argv[1][0] == 's') {
printf("SERVER %s\n", sockAddr.sun_path+1);
if (bind(fd, (const struct sockaddr*) &sockAddr, sockLen) < 0) {
perror("server bind()");
goto bail;
}
if (listen(fd, 5) < 0) {
perror("server listen()");
goto bail;
}
int clientSock = accept(fd, NULL, NULL);
if (clientSock < 0) {
perror("server accept");
goto bail;
}
char buf[64];
int count = read(clientSock, buf, sizeof(buf));
close(clientSock);
if (count < 0) {
perror("server read");
goto bail;
}
printf("GOT: '%s'\n", buf);
}
result = 0;
bail:
close(fd);
return result;
}
The default filesystem of /sdcard is FAT32, which doesn't support named pipes.
On a non-rooted device the only possible place you could try to create those pipes would be the application data directory /data/data/com.example/ .
Note: You shouldn't hardcode that value, use the Context.getApplicationInfo().dataDir .
But be aware that whenever the user is using Apps2SD or whenever Google implements that support officially you need to make sure to let the user know that the app can't be stored on vfat files system.
there's also /sqlite_stmt_journals
(we use it for testing, I don't know how long this directory will survive OS updates)
If you need IPC, the best practices are to use the Binders
If you only need inter-thread communication, you can use unnamed pipes through JNI (this works fine)
I would like to append to the accepted answer:
1) I am able to use this method to connect a socket between two native modules of an Android app.
2) write()
should be in a loop since it may not write the full amount requested in the first go. For example, it should read something like:
void *p = buffer;
count = 0;
while ((count += write(clientSock, buffer, num_bytes - count)) < num_bytes)
{
if (count < 0)
{
close(clientSock);
errCode = count;
break;
}
p += count;
}
The error handling shown above is insufficient, since several error codes simply indicate to try again. See the documentation for write.
If you're coding this in Java, you should just use PipedInputStream and PipedOutputStream.