+ cmsgptr = CMSG_FIRSTHDR(&msg);
+
+ if(cmsgptr->cmsg_level != SOL_SOCKET) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Wrong CMSG level: %d, expected %d!",
+ cmsgptr->cmsg_level, SOL_SOCKET);
+ return -1;
+ }
+
+ if(cmsgptr->cmsg_type != SCM_RIGHTS) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Wrong CMSG type: %d, expected %d!",
+ cmsgptr->cmsg_type, SCM_RIGHTS);
+ return -1;
+ }
+
+ if(cmsgptr->cmsg_len != CMSG_LEN(sizeof(device_fd))) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Wrong CMSG data length: %lu, expected %lu!",
+ (unsigned long)cmsgptr->cmsg_len, (unsigned long)CMSG_LEN(sizeof(device_fd)));
+ return -1;
+ }
+
+ return *(int *) CMSG_DATA(cmsgptr);
+}
+
+static int receive_fd(struct unix_socket_addr socket_addr) {
+ int socketfd;
+ int ret;
+ int result;
+
+ if((socketfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not open stream socket (error %d)!", socketfd);
+ return -1;
+ }
+
+ if((ret = connect(socketfd, (struct sockaddr *) &socket_addr.addr, socket_addr.size)) < 0) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not connect to Unix socket (error %d)!", ret);
+ result = -1;
+ goto end;
+ }
+
+ result = read_fd(socketfd);
+
+end:
+ close(socketfd);
+ return result;
+}
+
+static struct unix_socket_addr parse_socket_addr(const char *path) {
+ struct sockaddr_un socket_addr = {
+ .sun_family = AF_UNIX,
+ };
+ size_t path_length;
+
+ if(strlen(path) >= sizeof(socket_addr.sun_path)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Unix socket path too long!");
+ return (struct unix_socket_addr) {
+ 0
+ };
+ }
+
+ strncpy(socket_addr.sun_path, path, sizeof(socket_addr.sun_path));
+
+ if(path[0] == '@') {
+ /* abstract namespace socket */
+ socket_addr.sun_path[0] = '\0';
+ path_length = strlen(path);
+ } else {
+ /* filesystem path with NUL terminator */
+ path_length = strlen(path) + 1;
+ }
+
+ return (struct unix_socket_addr) {
+ .size = offsetof(struct sockaddr_un, sun_path) + path_length,
+ .addr = socket_addr
+ };