get rid of all remaining printfs from the library
[deb_libnfs.git] / lib / socket.c
CommitLineData
84004dbf
RS
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 Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include <stdio.h>
98f5fee8 19#include <stdlib.h>
84004dbf
RS
20#include <unistd.h>
21#include <fcntl.h>
22#include <poll.h>
23#include <string.h>
24#include <errno.h>
98f5fee8 25#include <rpc/rpc.h>
84004dbf
RS
26#include <rpc/xdr.h>
27#include <arpa/inet.h>
28#include <sys/ioctl.h>
29#include "libnfs.h"
30#include "libnfs-raw.h"
31#include "libnfs-private.h"
32#include "slist.h"
33
34static void set_nonblocking(int fd)
35{
36 unsigned v;
37 v = fcntl(fd, F_GETFL, 0);
38 fcntl(fd, F_SETFL, v | O_NONBLOCK);
39}
40
41int rpc_get_fd(struct rpc_context *rpc)
42{
43 return rpc->fd;
44}
45
46int rpc_which_events(struct rpc_context *rpc)
47{
912f7ad5 48 int events = rpc->is_connected ? POLLIN : POLLOUT;
84004dbf
RS
49
50 if (rpc->outqueue) {
51 events |= POLLOUT;
52 }
53 return events;
54}
55
56static int rpc_write_to_socket(struct rpc_context *rpc)
57{
58 ssize_t count;
59
60 if (rpc == NULL) {
84004dbf
RS
61 return -1;
62 }
63 if (rpc->fd == -1) {
1896d37b
RS
64 rpc_set_error(rpc, "trying to write but not connected");
65 return -1;
84004dbf
RS
66 }
67
68 while (rpc->outqueue != NULL) {
69 ssize_t total;
70
71 total = rpc->outqueue->outdata.size;
72
73 count = write(rpc->fd, rpc->outqueue->outdata.data + rpc->outqueue->written, total - rpc->outqueue->written);
74 if (count == -1) {
75 if (errno == EAGAIN || errno == EWOULDBLOCK) {
84004dbf
RS
76 return 0;
77 }
1896d37b
RS
78 rpc_set_error(rpc, "Error when writing to socket :%s(%d)", strerror(errno), errno);
79 return -1;
84004dbf
RS
80 }
81
82 rpc->outqueue->written += count;
83 if (rpc->outqueue->written == total) {
84 struct rpc_pdu *pdu = rpc->outqueue;
85
86 SLIST_REMOVE(&rpc->outqueue, pdu);
87 SLIST_ADD_END(&rpc->waitpdu, pdu);
88 }
89 }
90 return 0;
91}
92
93static int rpc_read_from_socket(struct rpc_context *rpc)
94{
95 int available;
96 int size;
97 unsigned char *buf;
98 ssize_t count;
99
100 if (ioctl(rpc->fd, FIONREAD, &available) != 0) {
101 rpc_set_error(rpc, "Ioctl FIONREAD returned error : %d. Closing socket.", errno);
102 return -1;
103 }
104 if (available == 0) {
105 rpc_set_error(rpc, "Socket has been closed");
1896d37b 106 return -1;
84004dbf
RS
107 }
108 size = rpc->insize - rpc->inpos + available;
109 buf = malloc(size);
110 if (buf == NULL) {
111 rpc_set_error(rpc, "Out of memory: failed to allocate %d bytes for input buffer. Closing socket.", size);
1896d37b 112 return -1;
84004dbf
RS
113 }
114 if (rpc->insize > rpc->inpos) {
115 memcpy(buf, rpc->inbuf + rpc->inpos, rpc->insize - rpc->inpos);
116 rpc->insize -= rpc->inpos;
117 rpc->inpos = 0;
118 }
119
120 count = read(rpc->fd, buf + rpc->insize, available);
121 if (count == -1) {
122 if (errno == EINTR) {
123 free(buf);
124 buf = NULL;
125 return 0;
126 }
127 rpc_set_error(rpc, "Read from socket failed, errno:%d. Closing socket.", errno);
128 free(buf);
129 buf = NULL;
1896d37b 130 return -1;
84004dbf
RS
131 }
132
133 if (rpc->inbuf != NULL) {
134 free(rpc->inbuf);
135 }
136 rpc->inbuf = (char *)buf;
137 rpc->insize += count;
138
139 while (1) {
140 if (rpc->insize - rpc->inpos < 4) {
141 return 0;
142 }
143 count = rpc_get_pdu_size(rpc->inbuf + rpc->inpos);
144 if (rpc->insize + rpc->inpos < count) {
145 return 0;
146 }
147 if (rpc_process_pdu(rpc, rpc->inbuf + rpc->inpos, count) != 0) {
148 rpc_set_error(rpc, "Invalid/garbage pdu received from server. Closing socket");
1896d37b 149 return -1;
84004dbf
RS
150 }
151 rpc->inpos += count;
152 if (rpc->inpos == rpc->insize) {
153 free(rpc->inbuf);
154 rpc->inbuf = NULL;
155 rpc->insize = 0;
156 rpc->inpos = 0;
157 }
158 }
159 return 0;
160}
161
162
163
164int rpc_service(struct rpc_context *rpc, int revents)
165{
166 if (revents & POLLERR) {
912f7ad5
RS
167 int err = 0;
168 socklen_t err_size = sizeof(err);
169
170 if (getsockopt(rpc->fd, SOL_SOCKET, SO_ERROR,
171 &err, &err_size) != 0 || err != 0) {
172 if (err == 0) {
173 err = errno;
174 }
175 rpc_set_error(rpc, "rpc_service: socket error "
176 "%s(%d).",
177 strerror(err), err);
84004dbf 178 } else {
912f7ad5
RS
179 rpc_set_error(rpc, "rpc_service: POLLERR, "
180 "Unknown socket error.");
84004dbf
RS
181 }
182 rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data);
183 return -1;
184 }
185 if (revents & POLLHUP) {
84004dbf
RS
186 rpc_set_error(rpc, "Socket failed with POLLHUP");
187 rpc->connect_cb(rpc, RPC_STATUS_ERROR, rpc->error_string, rpc->connect_data);
1896d37b 188 return -1;
84004dbf
RS
189 }
190
191 if (rpc->is_connected == 0 && rpc->fd != -1 && revents&POLLOUT) {
912f7ad5
RS
192 int err = 0;
193 socklen_t err_size = sizeof(err);
194
195 if (getsockopt(rpc->fd, SOL_SOCKET, SO_ERROR,
196 &err, &err_size) != 0 || err != 0) {
197 if (err == 0) {
198 err = errno;
199 }
200 rpc_set_error(rpc, "rpc_service: socket error "
201 "%s(%d) while connecting.",
202 strerror(err), err);
203 rpc->connect_cb(rpc, RPC_STATUS_ERROR,
204 NULL, rpc->connect_data);
205 return -1;
206 }
207
84004dbf
RS
208 rpc->is_connected = 1;
209 rpc->connect_cb(rpc, RPC_STATUS_SUCCESS, NULL, rpc->connect_data);
210 return 0;
211 }
212
213 if (revents & POLLOUT && rpc->outqueue != NULL) {
214 if (rpc_write_to_socket(rpc) != 0) {
1896d37b
RS
215 rpc_set_error(rpc, "write to socket failed");
216 return -1;
84004dbf
RS
217 }
218 }
219
220 if (revents & POLLIN) {
221 if (rpc_read_from_socket(rpc) != 0) {
222 rpc_disconnect(rpc, rpc_get_error(rpc));
1896d37b 223 return -1;
84004dbf
RS
224 }
225 }
226
227 return 0;
228}
229
230
231int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, rpc_cb cb, void *private_data)
232{
233 struct sockaddr_storage s;
234 struct sockaddr_in *sin = (struct sockaddr_in *)&s;
235 int socksize;
236
237 if (rpc->fd != -1) {
238 rpc_set_error(rpc, "Trying to connect while already connected");
84004dbf
RS
239 return -1;
240 }
241
242 sin->sin_family = AF_INET;
243 sin->sin_port = htons(port);
244 if (inet_pton(AF_INET, server, &sin->sin_addr) != 1) {
245 rpc_set_error(rpc, "Not a valid server ip address");
1896d37b 246 return -1;
84004dbf
RS
247 }
248
249 switch (s.ss_family) {
250 case AF_INET:
84004dbf 251 socksize = sizeof(struct sockaddr_in);
ea214e45
RS
252#ifdef HAVE_SOCK_SIN_LEN
253 sin->sin_len = socksize;
254#endif
255 rpc->fd = socket(AF_INET, SOCK_STREAM, 0);
84004dbf
RS
256 break;
257 }
258
259 if (rpc->fd == -1) {
260 rpc_set_error(rpc, "Failed to open socket");
1896d37b 261 return -1;
84004dbf
RS
262 }
263
264 rpc->connect_cb = cb;
265 rpc->connect_data = private_data;
266
267 set_nonblocking(rpc->fd);
268
269 if (connect(rpc->fd, (struct sockaddr *)&s, socksize) != 0 && errno != EINPROGRESS) {
270 rpc_set_error(rpc, "connect() to server failed");
1896d37b 271 return -1;
84004dbf
RS
272 }
273
274 return 0;
275}
276
277int rpc_disconnect(struct rpc_context *rpc, char *error)
278{
279 if (rpc->fd != -1) {
280 close(rpc->fd);
281 }
282 rpc->fd = -1;
283
284 rpc->is_connected = 0;
285
286 rpc_error_all_pdus(rpc, error);
287
288 return 0;
289}