1 /*
2  * Copyright (c) 2008, XenSource Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of XenSource Inc. nor the names of its contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 #include <stdio.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/un.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 
38 #include "tap-ctl.h"
39 #include "blktap2.h"
40 
41 int tap_ctl_debug = 0;
42 
43 int
tap_ctl_read_message(int fd,tapdisk_message_t * message,int timeout)44 tap_ctl_read_message(int fd, tapdisk_message_t *message, int timeout)
45 {
46 	fd_set readfds;
47 	int ret, len, offset;
48 	struct timeval tv, *t;
49 
50 	t      = NULL;
51 	offset = 0;
52 	len    = sizeof(tapdisk_message_t);
53 
54 	if (timeout) {
55 		tv.tv_sec  = timeout;
56 		tv.tv_usec = 0;
57 		t = &tv;
58 	}
59 
60 	memset(message, 0, sizeof(tapdisk_message_t));
61 
62 	while (offset < len) {
63 		FD_ZERO(&readfds);
64 		FD_SET(fd, &readfds);
65 
66 		ret = select(fd + 1, &readfds, NULL, NULL, t);
67 		if (ret == -1) {
68 			if (errno == EINTR)
69 				continue;
70 			break;
71 		}
72 		else if (FD_ISSET(fd, &readfds)) {
73 			ret = read(fd, message + offset, len - offset);
74 			if (ret <= 0) {
75 				if (errno == EINTR)
76 					continue;
77 				break;
78 			}
79 			offset += ret;
80 		} else
81 			break;
82 	}
83 
84 	if (offset != len) {
85 		EPRINTF("failure reading message\n");
86 		return -EIO;
87 	}
88 
89 	DBG("received '%s' message (uuid = %u)\n",
90 	    tapdisk_message_name(message->type), message->cookie);
91 
92 	return 0;
93 }
94 
95 int
tap_ctl_write_message(int fd,tapdisk_message_t * message,int timeout)96 tap_ctl_write_message(int fd, tapdisk_message_t *message, int timeout)
97 {
98 	fd_set writefds;
99 	int ret, len, offset;
100 	struct timeval tv, *t;
101 
102 	t      = NULL;
103 	offset = 0;
104 	len    = sizeof(tapdisk_message_t);
105 
106 	if (timeout) {
107 		tv.tv_sec  = timeout;
108 		tv.tv_usec = 0;
109 		t = &tv;
110 	}
111 
112 	DBG("sending '%s' message (uuid = %u)\n",
113 	    tapdisk_message_name(message->type), message->cookie);
114 
115 	while (offset < len) {
116 		FD_ZERO(&writefds);
117 		FD_SET(fd, &writefds);
118 
119 		/* we don't bother reinitializing tv. at worst, it will wait a
120 		 * bit more time than expected. */
121 
122 		ret = select(fd + 1, NULL, &writefds, NULL, t);
123 		if (ret == -1) {
124 			if (errno == EINTR)
125 				continue;
126 			break;
127 		}
128 		else if (FD_ISSET(fd, &writefds)) {
129 			ret = write(fd, message + offset, len - offset);
130 			if (ret <= 0) {
131 				if (errno == EINTR)
132 					continue;
133 				break;
134 			}
135 			offset += ret;
136 		} else
137 			break;
138 	}
139 
140 	if (offset != len) {
141 		EPRINTF("failure writing message\n");
142 		return -EIO;
143 	}
144 
145 	return 0;
146 }
147 
148 int
tap_ctl_send_and_receive(int sfd,tapdisk_message_t * message,int timeout)149 tap_ctl_send_and_receive(int sfd, tapdisk_message_t *message, int timeout)
150 {
151 	int err;
152 
153 	err = tap_ctl_write_message(sfd, message, timeout);
154 	if (err) {
155 		EPRINTF("failed to send '%s' message\n",
156 			tapdisk_message_name(message->type));
157 		return err;
158 	}
159 
160 	err = tap_ctl_read_message(sfd, message, timeout);
161 	if (err) {
162 		EPRINTF("failed to receive '%s' message\n",
163 			tapdisk_message_name(message->type));
164 		return err;
165 	}
166 
167 	return 0;
168 }
169 
170 char *
tap_ctl_socket_name(int id)171 tap_ctl_socket_name(int id)
172 {
173 	char *name;
174 
175 	if (asprintf(&name, "%s/%s%d",
176 		     BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, id) == -1)
177 		return NULL;
178 
179 	return name;
180 }
181 
182 int
tap_ctl_connect(const char * name,int * sfd)183 tap_ctl_connect(const char *name, int *sfd)
184 {
185 	int fd, err;
186 	struct sockaddr_un saddr;
187 
188 	*sfd = -1;
189 
190 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
191 	if (fd == -1) {
192 		EPRINTF("couldn't create socket for %s: %d\n", name, errno);
193 		return -errno;
194 	}
195 
196 	memset(&saddr, 0, sizeof(saddr));
197 	saddr.sun_family = AF_UNIX;
198 	strcpy(saddr.sun_path, name);
199 
200 	err = connect(fd, (const struct sockaddr *)&saddr, sizeof(saddr));
201 	if (err) {
202 		EPRINTF("couldn't connect to %s: %d\n", name, errno);
203 		close(fd);
204 		return -errno;
205 	}
206 
207 	*sfd = fd;
208 	return 0;
209 }
210 
211 int
tap_ctl_connect_id(int id,int * sfd)212 tap_ctl_connect_id(int id, int *sfd)
213 {
214 	int err;
215 	char *name;
216 
217 	*sfd = -1;
218 
219 	if (id < 0) {
220 		EPRINTF("invalid id %d\n", id);
221 		return -EINVAL;
222 	}
223 
224 	name = tap_ctl_socket_name(id);
225 	if (!name) {
226 		EPRINTF("couldn't name socket for %d\n", id);
227 		return -ENOMEM;
228 	}
229 
230 	err = tap_ctl_connect(name, sfd);
231 	free(name);
232 
233 	return err;
234 }
235 
236 int
tap_ctl_connect_send_and_receive(int id,tapdisk_message_t * message,int timeout)237 tap_ctl_connect_send_and_receive(int id, tapdisk_message_t *message, int timeout)
238 {
239 	int err, sfd;
240 
241 	err = tap_ctl_connect_id(id, &sfd);
242 	if (err)
243 		return err;
244 
245 	err = tap_ctl_send_and_receive(sfd, message, timeout);
246 
247 	close(sfd);
248 	return err;
249 }
250