master uint/libule / source / ulepipe.c
  1/* Copyright (c) uint
  2
  3   Permission to use, copy, modify, and distribute this software for any
  4   purpose with or without fee is hereby granted, provided that the above
  5   copyright notice and this permission notice appear in all copies.
  6
  7   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 10   SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 11   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 12   OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 13   CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 14
 15#include <errno.h>
 16#include <stdio.h>
 17#include <sys/socket.h>
 18#include <sys/stat.h>
 19#include <sys/time.h>
 20#include <sys/un.h>
 21#include <string.h>
 22#include <unistd.h>
 23
 24#include "ulepipe.h"
 25
 26int
 27ulepipe_open(ulepipe* conn, const char* path)
 28{
 29	int sverrno = 0;
 30	int fd = -1;
 31
 32	if (!conn || !path) {
 33		errno = EINVAL;
 34		return -1;
 35	}
 36
 37	conn->fd = -1;
 38	conn->peer_len = 0;
 39
 40	if (strlen(path) >= sizeof(conn->addr.sun_path)) {
 41		errno = ENAMETOOLONG;
 42		return -1;
 43	}
 44
 45	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
 46	if (fd == -1)
 47		return -1;
 48
 49	memset(&conn->addr, 0, sizeof(conn->addr));
 50	conn->addr.sun_family = AF_UNIX;
 51	memcpy(conn->addr.sun_path, path, strlen(path) + 1);
 52
 53	if (bind(fd, (struct sockaddr*)&conn->addr, sizeof(conn->addr)) == -1) {
 54		sverrno = errno;
 55		close(fd);
 56		errno = sverrno;
 57		return -1;
 58	}
 59
 60	conn->fd = fd;
 61	return 0;
 62}
 63
 64void
 65ulepipe_close(ulepipe* conn)
 66{
 67	if (!conn || conn->fd < 0)
 68		return;
 69
 70	close(conn->fd);
 71	unlink(conn->addr.sun_path);
 72	conn->fd = -1;
 73	conn->peer_len = 0;
 74}
 75
 76ssize_t
 77ulepipe_send(const char* path, void* data, size_t size)
 78{
 79	struct sockaddr_un addr = { 0 };
 80	struct sockaddr_un client = { 0 };
 81	struct msghdr msg = { 0 };
 82	struct iovec iov = { 0 };
 83	struct timeval timeout = { ULEPIPE_TIMEOUT, 0 };
 84	char dir[64] = { 0 };
 85	int sverrno = 0;
 86	ssize_t n = -1;
 87	int fd = -1;
 88	unsigned int i;
 89
 90	if (!path || (!data && size)) {
 91		errno = EINVAL;
 92		return -1;
 93	}
 94
 95	if (strlen(path) >= sizeof(addr.sun_path)) {
 96		errno = ENAMETOOLONG;
 97		return -1;
 98	}
 99
100	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
101	if (fd == -1)
102		return -1;
103
104	for (i = 0; i < 1000; i++) {
105		snprintf(dir, sizeof(dir), "/tmp/ulepipe.%ld.%u", (long)getpid(), i);
106		if (mkdir(dir, 0700) == 0)
107			break;
108		if (errno != EEXIST)
109			goto fail;
110	}
111	if (i == 1000) {
112		errno = EEXIST;
113		goto fail;
114	}
115
116	client.sun_family = AF_UNIX;
117	snprintf(client.sun_path, sizeof(client.sun_path), "%s/socket", dir);
118	if (bind(fd, (struct sockaddr*)&client, sizeof(client)) == -1 ||
119	    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1)
120		goto done;
121
122	addr.sun_family = AF_UNIX;
123	memcpy(addr.sun_path, path, strlen(path) + 1);
124
125	do
126		n = sendto(fd, data, size, 0, (struct sockaddr*)&addr, sizeof(addr));
127	while (n == -1 && errno == EINTR);
128
129	if (n == -1)
130		goto done;
131	if ((size_t)n != size) {
132		errno = EIO;
133		n = -1;
134		goto done;
135	}
136
137	iov.iov_base = data;
138	iov.iov_len = size;
139	msg.msg_iov = &iov;
140	msg.msg_iovlen = 1;
141
142	do
143		n = recvmsg(fd, &msg, 0);
144	while (n == -1 && errno == EINTR);
145
146	if (n != -1 && (msg.msg_flags & MSG_TRUNC)) {
147		errno = EMSGSIZE;
148		n = -1;
149	}
150
151done:
152	sverrno = errno;
153	close(fd);
154	unlink(client.sun_path);
155	rmdir(dir);
156	errno = sverrno;
157	return n;
158
159fail:
160	sverrno = errno;
161	close(fd);
162	errno = sverrno;
163	return -1;
164}
165
166ssize_t
167ulepipe_reply(const ulepipe* conn, const void* data, size_t size)
168{
169	ssize_t n = -1;
170
171	if (!conn || conn->fd < 0 || !conn->peer_len || (!data && size)) {
172		errno = EINVAL;
173		return -1;
174	}
175
176	do
177		n = sendto(conn->fd, data, size, 0,
178		    (struct sockaddr*)&conn->peer, conn->peer_len);
179	while (n == -1 && errno == EINTR);
180
181	return n;
182}
183
184ssize_t
185ulepipe_receive(ulepipe* conn, void* data, size_t size)
186{
187	struct msghdr msg = { 0 };
188	struct iovec iov = { 0 };
189	ssize_t n = -1;
190
191	if (!conn || conn->fd < 0 || (!data && size)) {
192		errno = EINVAL;
193		return -1;
194	}
195
196	conn->peer_len = 0;
197	iov.iov_base = data;
198	iov.iov_len = size;
199	msg.msg_iov = &iov;
200	msg.msg_iovlen = 1;
201	msg.msg_name = &conn->peer;
202	msg.msg_namelen = sizeof(conn->peer);
203
204	do
205		n = recvmsg(conn->fd, &msg, 0);
206	while (n == -1 && errno == EINTR);
207
208	if (n != -1 && (msg.msg_flags & MSG_TRUNC)) {
209		errno = EMSGSIZE;
210		return -1;
211	}
212
213	if (n != -1)
214		conn->peer_len = msg.msg_namelen;
215
216	return n;
217}
218