It's been a while since I've done anything with Unix domain sockets, and it's not the same on OSX!
If we take a look at the structure for sockaddr_un we see that the standard is:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};
However, on OSX it's:
struct sockaddr_un {
unsigned char sun_len; /* length including null */
sa_family_t sun_family; /* AF_UNIX */
char sun_path[104]; /* pathname */
};
Next, the standard ( or at least as far as I am aware of ) way to call bind() is as follows:
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
Didn't work;
}
In the above, local is the sockaddr_un struct, and the path is /var/tmp/socket.
Now as we all know ;) the normal practice is to call unlink() on the path before attempting the bind just in case the program has crashed and left the socket file lying around, however under OSX with the above code, unlink is unable to find the file.
This is because the name of the socket becomes truncated by one character, to /var/tmp/socke
It took some digging, but the correct way to call bind under OSX is as follows:
if (bind(s, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
perror("bind");
exit(1);
This will create the socket with the full path name, here's a complete code snippet:
#define SOCK_PATH "/var/tmp/socket"
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};
However, on OSX it's:
struct sockaddr_un {
unsigned char sun_len; /* length including null */
sa_family_t sun_family; /* AF_UNIX */
char sun_path[104]; /* pathname */
};
Next, the standard ( or at least as far as I am aware of ) way to call bind() is as follows:
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
Didn't work;
}
In the above, local is the sockaddr_un struct, and the path is /var/tmp/socket.
Now as we all know ;) the normal practice is to call unlink() on the path before attempting the bind just in case the program has crashed and left the socket file lying around, however under OSX with the above code, unlink is unable to find the file.
This is because the name of the socket becomes truncated by one character, to /var/tmp/socke
It took some digging, but the correct way to call bind under OSX is as follows:
if (bind(s, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
perror("bind");
exit(1);
This will create the socket with the full path name, here's a complete code snippet:
#define SOCK_PATH "/var/tmp/socket"
struct sockaddr_un local;
char str[100];
int s;
char str[100];
int s;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if (strlen(SOCK_PATH) >= sizeof(local.sun_path)) {
perror("path to long!");
exit(1);
}
local.sun_len = sizeof(local);
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
if (bind(s, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
perror("bind");
exit(1);
}
//GO off an do what you want here
perror("socket");
exit(1);
}
if (strlen(SOCK_PATH) >= sizeof(local.sun_path)) {
perror("path to long!");
exit(1);
}
local.sun_len = sizeof(local);
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
if (bind(s, (struct sockaddr *)&local, SUN_LEN(&local)) == -1) {
perror("bind");
exit(1);
}
//GO off an do what you want here
This caught me out with much head scratching for some time, so I hope it helps someone out there!
thank you so much for this! i was stuck trying to port over a linux program to osx and this tutorial made it all so clear.
ReplyDeleteHi Zubair, thanks for your comment, I'm glad the post helped!
ReplyDeleteIncredibly helpful, thank you!
ReplyDelete