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