f79b31692b764dffb3e5136e1a1fb504a15c3c30
[ccan] / ccan / iscsi / socket.c
1 /*
2    Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <strings.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <netinet/in.h>
27 #include <poll.h>
28 #include <sys/ioctl.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <arpa/inet.h>
32 #include "iscsi.h"
33 #include "iscsi-private.h"
34 #include "dlinklist.h"
35 #if HAVE_SYS_FILIO_H
36 #include <sys/filio.h>
37 #endif
38
39 static void set_nonblocking(int fd)
40 {
41         unsigned v;
42         v = fcntl(fd, F_GETFL, 0);
43         fcntl(fd, F_SETFL, v | O_NONBLOCK);
44 }
45
46 int iscsi_connect_async(struct iscsi_context *iscsi, const char *target, iscsi_command_cb cb, void *private_data)
47 {
48         int tpgt = -1;
49         int port = 3260;
50         char *str;
51         char *addr;
52         union {
53                 struct sockaddr sa;
54                 struct sockaddr_storage ss;
55                 struct sockaddr_in sin;
56         } s;
57         int socksize;
58
59         if (iscsi == NULL) {
60                 printf("Trying to connect NULL context\n");
61                 return -1;
62         }
63         if (iscsi->fd != -1) {
64                 printf("Trying to connect but already connected\n");
65                 return -2;
66         }
67
68         addr = strdup(target);
69         if (addr == NULL) {
70                 printf("failed to strdup target address\n");
71                 return -3;
72         }
73         
74         /* check if we have a target portal group tag */
75         if ((str = rindex(addr, ',')) != NULL) {
76                 tpgt = atoi(str+1);
77                 str[0] = 0;
78         }
79
80         /* XXX need handling for {ipv6 addresses} */
81         /* for now, assume all is ipv4 */
82         if ((str = rindex(addr, ':')) != NULL) {
83                 port = atoi(str+1);
84                 str[0] = 0;
85         }
86
87         s.sin.sin_family = AF_INET;
88         s.sin.sin_port   = htons(port);
89         if (inet_pton(AF_INET, addr, &s.sin.sin_addr) != 1) {
90                 printf("failed to convert to ip address\n");
91                 free(addr);
92                 return -4;
93         }
94         free(addr);
95
96         switch (s.ss.ss_family) {
97         case AF_INET:
98                 iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
99                 socksize = sizeof(struct sockaddr_in);
100                 break;
101         default:
102                 printf("Unknown family :%d\n", s.ss.ss_family);
103                 return -5;
104
105         }
106
107         if (iscsi->fd == -1) {
108                 printf("Failed to open socket\n");
109                 return -6;
110
111         }
112
113         iscsi->connect_cb  = cb;
114         iscsi->connect_data = private_data;
115
116         set_nonblocking(iscsi->fd);
117
118         if (connect(iscsi->fd, &s.sa, socksize) != 0 && errno != EINPROGRESS) {
119                 printf("Connect failed errno : %s (%d)\n", strerror(errno), errno);
120                 return -7;
121         }
122
123         return 0;
124 }
125
126 int iscsi_disconnect(struct iscsi_context *iscsi)
127 {
128         if (iscsi == NULL) {
129                 printf("Trying to disconnect NULL context\n");
130                 return -1;
131         }
132         if (iscsi->is_loggedin != 0) {
133                 printf("Trying to disconnect while logged in\n");
134                 return -2;
135         }
136         if (iscsi->fd == -1) {
137                 printf("Trying to disconnect but not connected\n");
138                 return -3;
139         }
140
141         close(iscsi->fd);
142         iscsi->fd  = -1;
143
144         iscsi->is_connected = 0;
145
146         return 0;
147 }
148
149 int iscsi_get_fd(struct iscsi_context *iscsi)
150 {
151         if (iscsi == NULL) {
152                 printf("Trying to get fd for NULL context\n");
153                 return -1;
154         }
155
156         return iscsi->fd;
157 }
158
159 int iscsi_which_events(struct iscsi_context *iscsi)
160 {
161         int events = POLLIN;
162
163         if (iscsi->is_connected == 0) {
164                 events |= POLLOUT;
165         }
166
167         if (iscsi->outqueue) {
168                 events |= POLLOUT;
169         }
170         return events;
171 }
172
173 static int iscsi_read_from_socket(struct iscsi_context *iscsi)
174 {
175         int available;
176         int size;
177         unsigned char *buf;
178         ssize_t count;
179
180         if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
181                 printf("ioctl FIONREAD returned error : %d\n", errno);
182                 return -1;
183         }
184         if (available == 0) {
185                 printf("no data readable in socket, socket is closed\n");
186                 return -2;
187         }
188         size = iscsi->insize - iscsi->inpos + available;
189         buf = malloc(size);
190         if (buf == NULL) {
191                 printf("failed to allocate %d bytes for input buffer\n", size);
192                 return -3;
193         }
194         if (iscsi->insize > iscsi->inpos) {
195                 memcpy(buf, iscsi->inbuf + iscsi->inpos, iscsi->insize - iscsi->inpos);
196                 iscsi->insize -= iscsi->inpos;
197                 iscsi->inpos   = 0;
198         }
199
200         count = read(iscsi->fd, buf + iscsi->insize, available);
201         if (count == -1) {
202                 if (errno == EINTR) {
203                         free(buf);
204                         buf = NULL;
205                         return 0;
206                 }
207                 printf("read from socket failed, errno:%d\n", errno);
208                 free(buf);
209                 buf = NULL;
210                 return -4;
211         }
212
213         if (iscsi->inbuf != NULL) {
214                 free(iscsi->inbuf);
215         }
216         iscsi->inbuf   = buf;
217         iscsi->insize += count;
218
219         while (1) {
220                 if (iscsi->insize - iscsi->inpos < 48) {
221                         return 0;
222                 }
223                 count = iscsi_get_pdu_size(iscsi->inbuf + iscsi->inpos);
224                 if (iscsi->insize + iscsi->inpos < count) {
225                         return 0;
226                 }
227                 if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos, count) != 0) {
228                         printf("failed to process pdu\n");
229                         return -5;
230                 }
231                 iscsi->inpos += count;
232                 if (iscsi->inpos == iscsi->insize) {
233                         free(iscsi->inbuf);
234                         iscsi->inbuf = NULL;
235                         iscsi->insize = 0;
236                         iscsi->inpos = 0;
237                 }
238                 if (iscsi->inpos > iscsi->insize) {
239                         printf("inpos > insize. bug!\n");
240                         return -6;
241                 }
242         }
243
244         return 0;
245 }
246
247 static int iscsi_write_to_socket(struct iscsi_context *iscsi)
248 {
249         ssize_t count;
250
251         if (iscsi == NULL) {
252                 printf("trying to write to socket for NULL context\n");
253                 return -1;
254         }
255         if (iscsi->fd == -1) {
256                 printf("trying to write but not connected\n");
257                 return -2;
258         }
259
260         while (iscsi->outqueue != NULL) {
261                 ssize_t total;
262
263                 total = iscsi->outqueue->outdata.size;
264                 total = (total +3) & 0xfffffffc;
265
266                 count = write(iscsi->fd, iscsi->outqueue->outdata.data + iscsi->outqueue->written, total - iscsi->outqueue->written);
267                 if (count == -1) {
268                         if (errno == EAGAIN || errno == EWOULDBLOCK) {
269                                 printf("socket would block, return from write to socket\n");
270                                 return 0;
271                         }
272                         printf("Error when writing to socket :%d\n", errno);
273                         return -3;
274                 }
275
276                 iscsi->outqueue->written += count;
277                 if (iscsi->outqueue->written == total) {
278                         struct iscsi_pdu *pdu = iscsi->outqueue;
279
280                         DLIST_REMOVE(iscsi->outqueue, pdu);
281                         DLIST_ADD_END(iscsi->waitpdu, pdu, NULL);
282                 }
283         }
284         return 0;
285 }
286
287 int iscsi_service(struct iscsi_context *iscsi, int revents)
288 {
289         if (revents & POLLERR) {
290                 printf("iscsi_service: POLLERR, socket error\n");
291                 iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
292                 return -1;
293         }
294         if (revents & POLLHUP) {
295                 printf("iscsi_service: POLLHUP, socket error\n");
296                 iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
297                 return -2;
298         }
299
300         if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
301                 iscsi->is_connected = 1;
302                 iscsi->connect_cb(iscsi, ISCSI_STATUS_GOOD, NULL, iscsi->connect_data);
303                 return 0;
304         }
305
306         if (revents & POLLOUT && iscsi->outqueue != NULL) {
307                 if (iscsi_write_to_socket(iscsi) != 0) {
308                         printf("write to socket failed\n");
309                         return -3;
310                 }
311         }
312         if (revents & POLLIN) {
313                 if (iscsi_read_from_socket(iscsi) != 0) {
314                         printf("read from socket failed\n");
315                         return -4;
316                 }
317         }
318
319         return 0;
320 }
321
322 int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
323 {
324         if (iscsi == NULL) {
325                 printf("trying to queue to NULL context\n");
326                 return -1;
327         }
328         if (pdu == NULL) {
329                 printf("trying to queue NULL pdu\n");
330                 return -2;
331         }
332         DLIST_ADD_END(iscsi->outqueue, pdu, NULL);
333
334         return 0;
335 }
336
337
338