1. Summary
  2. Files
  3. Support
  4. Report Spam
  5. Create account
  6. Log in

root/branches/scons/src/platform/posix/io-posix.c @ 883

Revision 883, 8.0 KB (checked in by bmerry, 4 years ago)

Redo I/O socket interface

Line 
1/*  BuGLe: an OpenGL debugging tool
2 *  Copyright (C) 2009  Bruce Merry
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; version 2.
7 *
8 *  This program is distributed in the hope that it will be useful,
9 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 *  GNU General Public License for more details.
12 *
13 *  You should have received a copy of the GNU General Public License
14 *  along with this program; if not, write to the Free Software
15 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17
18#if HAVE_CONFIG_H
19# include <config.h>
20#endif
21#include "platform_config.h"
22#ifdef _GNU_SOURCE
23# undef _GNU_SOURCE /* It causes a GNU version of strerror_r to replace the POSIX version */
24#endif
25#include <unistd.h>
26#include <stdarg.h>
27#include <errno.h>
28#include <string.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <sys/types.h>
32#include <sys/time.h>
33#include <poll.h>
34#include <sys/socket.h>
35#include <netinet/in.h>
36#include <netinet/tcp.h>
37#include <netdb.h>
38#include <bugle/string.h>
39#include <bugle/io.h>
40#include <bugle/memory.h>
41#include "platform/macros.h"
42#include "common/io-impl.h"
43
44#define BUFFER_SIZE 256
45
46typedef struct bugle_io_reader_fd
47{
48    int fd;
49} bugle_io_reader_fd;
50
51static size_t fd_read(void *ptr, size_t size, size_t nmemb, void *arg)
52{
53    bugle_io_reader_fd *s;
54    size_t remain;
55    size_t received = 0;
56
57    s = (bugle_io_reader_fd *) arg;
58    remain = size * nmemb; /* FIXME handle overflow */
59    while (remain > 0)
60    {
61        ssize_t cur;
62        cur = read(s->fd, (char *)ptr + received, remain);
63        if (cur < 0)
64        {
65            if (errno == EINTR)
66                continue;
67            else
68                return received / size;
69        }
70        else
71        {
72            received += cur;
73            remain -= cur;
74        }
75    }
76    return nmemb;
77}
78
79static bugle_bool fd_has_data(void *arg)
80{
81    struct pollfd fds[1];
82    bugle_io_reader_fd *s;
83
84    s = (bugle_io_reader_fd *) arg;
85    fds[0].fd = s->fd;
86    fds[0].events = POLLIN;
87
88    while (1)
89    {
90        int ret;
91        ret = poll(fds, 1, 0);
92        if (ret == -1)
93        {
94            if (errno != EINTR)
95            {
96                /* Fatal error */
97                perror("poll");
98                exit(1);
99            }
100        }
101        else if (ret > 0 && (fds[0].revents & (POLLIN | POLLHUP)))
102        {
103            return BUGLE_TRUE;
104        }
105        else
106        {
107            /* Timeout i.e. no descriptors ready */
108            return BUGLE_FALSE;
109        }
110    }
111}
112
113static int fd_reader_close(void *arg)
114{
115    bugle_io_reader_fd *s;
116
117    s = (bugle_io_reader_fd *) arg;
118    return close(s->fd) == 0 ? 0 : EOF;
119}
120
121bugle_io_reader *bugle_io_reader_fd_new(int fd)
122{
123    bugle_io_reader *reader;
124    bugle_io_reader_fd *s;
125
126    reader = BUGLE_MALLOC(bugle_io_reader);
127    s = BUGLE_MALLOC(bugle_io_reader_fd);
128
129    reader->fn_read = fd_read;
130    reader->fn_has_data = fd_has_data;
131    reader->fn_close = fd_reader_close;
132    reader->arg = s;
133
134    s->fd = fd;
135
136    return reader;
137}
138
139bugle_io_reader *bugle_io_reader_socket_new(int sock)
140{
141    /* On POSIX, sockets are the same as file descriptors */
142    return bugle_io_reader_fd_new(sock);
143}
144
145typedef struct bugle_io_writer_fd
146{
147    char buffer[BUFFER_SIZE];   /* Buffer for holding printf output */
148    int fd;
149} bugle_io_writer_fd;
150
151static size_t fd_write(const void *ptr, size_t size, size_t nmemb, void *arg)
152{
153    bugle_io_writer_fd *s;
154    size_t written = 0;
155    ssize_t cur;
156    size_t remain;
157
158    /* FIXME: handle multiplication overflow */
159    remain = size * nmemb;
160
161    s = (bugle_io_writer_fd *) arg;
162    while (remain > 0)
163    {
164        cur = write(s->fd, (char *)ptr + written, remain);
165        if (cur < 0)
166        {
167            if (errno == EINTR)
168                continue;
169            else
170                return written / size;
171        }
172        else
173        {
174            written += cur;
175            remain -= cur;
176        }
177    }
178    return nmemb;
179}
180
181static int fd_vprintf(void *arg, const char *format, va_list ap)
182{
183    bugle_io_writer_fd *s;
184    int length;
185    char *buffer;
186    va_list ap2; /* backup copy for second pass */
187    int ret;
188
189    BUGLE_VA_COPY(ap2, ap);
190
191    s = (bugle_io_writer_fd *) arg;
192    length = bugle_vsnprintf(s->buffer, sizeof(s->buffer), format, ap);
193    if (length < sizeof(s->buffer))
194    {
195        ret = fd_write(s->buffer, sizeof(char), length, arg);
196    }
197    else
198    {
199        buffer = bugle_vasprintf(format, ap);
200        ret = fd_write(s->buffer, sizeof(char), length, arg);
201        bugle_free(buffer);
202    }
203    va_end(ap2);
204    return ret;
205}
206
207static int fd_putc(int c, void *arg)
208{
209    char ch;
210    ch = c;
211    return fd_write(&ch, 1, 1, arg);
212}
213
214static int fd_writer_close(void *arg)
215{
216    bugle_io_writer_fd *s;
217    int ret;
218
219    s = (bugle_io_writer_fd *) arg;
220    ret = close(s->fd);
221    if (ret == -1) return EOF;
222    else return ret;
223}
224
225bugle_io_writer *bugle_io_writer_fd_new(int fd)
226{
227    bugle_io_writer_fd *s;
228    bugle_io_writer *writer;
229
230    writer = BUGLE_MALLOC(bugle_io_writer);
231    s = BUGLE_MALLOC(bugle_io_writer_fd);
232
233    writer->fn_vprintf = fd_vprintf;
234    writer->fn_putc = fd_putc;
235    writer->fn_write = fd_write;
236    writer->fn_close = fd_writer_close;
237    writer->arg = s;
238
239    s->fd = fd;
240
241    return writer;
242}
243
244bugle_io_writer *bugle_io_writer_socket_new(int sock)
245{
246    /* On POSIX, sockets are the same as file descriptors */
247    return bugle_io_writer_fd_new(sock);
248}
249
250static int _bugle_strerror_r(int errnum, char *errbuf, size_t buflen)
251{
252#if _POSIX_THREAD_SAFE_FUNCTIONS > 0
253    return strerror_r(errnum, errbuf, buflen);
254#else
255    const char *err;
256
257    errno = 0;
258    err = strerror(errnum);
259    if (errno != 0)
260        return errno;
261    else
262    {
263        size_t len = strlen(err);
264        if (len > buflen)
265            return ERANGE;
266        else
267        {
268            strcpy(errbuf, err);
269            return 0;
270        }
271    }
272#endif
273}
274
275char *bugle_io_socket_listen(const char *host, const char *port, bugle_io_reader **reader, bugle_io_writer **writer)
276{
277    int listen_sock;
278    int sock;
279    int status;
280    struct addrinfo hints, *ai;
281    char err_buffer[1024];
282
283    memset(&hints, 0, sizeof(hints));
284    hints.ai_family = AF_UNSPEC;        /* supports IPv4 and IPv6 */
285    hints.ai_socktype = SOCK_STREAM;
286    hints.ai_protocol = IPPROTO_TCP;
287    hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_PASSIVE;
288    status = getaddrinfo(host, port, &hints, &ai);
289    if (status != 0 || ai == NULL)
290    {
291        return bugle_asprintf("failed to resolve %s:%s: %s",
292                              host ? host : "", port, gai_strerror(status));
293    }
294
295
296    listen_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
297    if (listen_sock == -1)
298    {
299        _bugle_strerror_r(errno, err_buffer, sizeof(err_buffer));
300        freeaddrinfo(ai);
301        return bugle_asprintf("failed to open socket: %s", err_buffer);
302    }
303
304    status = bind(listen_sock, ai->ai_addr, ai->ai_addrlen);
305    if (status == -1)
306    {
307        _bugle_strerror_r(errno, err_buffer, sizeof(err_buffer));
308        freeaddrinfo(ai);
309        return bugle_asprintf("failed to bind to %s:%s: %s",
310                              host ? host : "", port, err_buffer);
311    }
312
313    if (listen(listen_sock, 1) == -1)
314    {
315        _bugle_strerror_r(errno, err_buffer, sizeof(err_buffer));
316        freeaddrinfo(ai);
317        close(listen_sock);
318        return bugle_asprintf("failed to listen on %s:%s: %s",
319                              host ? host : "", port, err_buffer);
320    }
321
322    sock = accept(listen_sock, NULL, NULL);
323    if (sock == -1)
324    {
325        _bugle_strerror_r(errno, err_buffer, sizeof(err_buffer));
326        freeaddrinfo(ai);
327        close(listen_sock);
328        return bugle_asprintf("failed to accept a connection on %s:%s: %s",
329                              host ? host : "", port, err_buffer);
330    }
331    close(listen_sock);
332
333    *reader = bugle_io_reader_fd_new(sock);
334    *writer = bugle_io_writer_fd_new(sock);
335    return NULL;
336}
Note: See TracBrowser for help on using the browser.