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