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