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