]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-socket.c
Doc: Updated the list of contributors
[libucw.git] / ucw / fb-socket.c
1 /*
2  *      UCW Library -- Fast Buffered I/O on Sockets with Timeouts
3  *
4  *      (c) 2008 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include <ucw/lib.h>
11 #include <ucw/fastbuf.h>
12 #include <ucw/fb-socket.h>
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <poll.h>
17 #include <errno.h>
18
19 struct fb_sock {
20   struct fastbuf fb;
21   struct fbsock_params par;
22   byte buf[0];
23 };
24
25 #define FB_SOCK(f) ((struct fb_sock *)(f))
26
27 static int
28 fbs_refill(struct fastbuf *f)
29 {
30   struct fbsock_params *p = &FB_SOCK(f)->par;
31   struct pollfd pf = {
32       .fd = p->fd,
33       .events = POLLIN
34   };
35
36   for (;;)
37     {
38       int e = poll(&pf, 1, p->timeout_ms);
39       if (e < 0)
40         {
41           p->err(p->data, FBSOCK_READ, "read error");
42           return 0;
43         }
44       if (!e)
45         {
46           p->err(p->data, FBSOCK_READ | FBSOCK_TIMEOUT, "read timeout");
47           return 0;
48         }
49
50       f->bptr = f->buffer;
51       int l = read(p->fd, f->buffer, f->bufend-f->buffer);
52       if (l < 0)
53         {
54           if (errno == EINTR || errno == EAGAIN)
55             continue;
56           p->err(p->data, FBSOCK_READ, "read error");
57           return 0;
58         }
59       f->bstop = f->buffer + l;
60       f->pos += l;
61       return l;
62     }
63 }
64
65 static void
66 fbs_spout(struct fastbuf *f)
67 {
68   struct fbsock_params *p = &FB_SOCK(f)->par;
69   struct pollfd pf = {
70       .fd = p->fd,
71       .events = POLLOUT,
72   };
73
74   int l = f->bptr - f->bstop;
75   f->bptr = f->bstop;
76   char *buf = f->bstop;
77
78   while (l)
79     {
80       int e = poll(&pf, 1, p->timeout_ms);
81       if (e < 0)
82         {
83           p->err(p->data, FBSOCK_WRITE, "write error");
84           return;
85         }
86       if (!e)
87         {
88           p->err(p->data, FBSOCK_WRITE | FBSOCK_TIMEOUT, "write timeout");
89           return;
90         }
91
92       e = write(p->fd, buf, l);
93       if (e < 0)
94         {
95           if (errno == EINTR || errno == EAGAIN)
96             continue;
97           p->err(p->data, FBSOCK_WRITE, "write error");
98           return;
99         }
100       buf += e;
101       l -= e;
102     }
103 }
104
105 static void
106 fbs_close(struct fastbuf *f)
107 {
108   if (!FB_SOCK(f)->par.fd_is_shared)
109     close(FB_SOCK(f)->par.fd);
110   xfree(f);
111 }
112
113 struct fastbuf *
114 fbsock_create(struct fbsock_params *p)
115 {
116   struct fb_sock *F = xmalloc(sizeof(*F) + p->bufsize);
117   struct fastbuf *f = &F->fb;
118
119   bzero(F, sizeof(*F));
120   F->par = *p;
121   f->buffer = F->buf;
122   f->bptr = f->bstop = f->buffer;
123   f->bufend = f->buffer + p->bufsize;
124   f->name = "<socket>";
125   f->refill = fbs_refill;
126   f->spout = fbs_spout;
127   f->close = fbs_close;
128   f->can_overwrite_buffer = 1;
129   return f;
130 }
131
132 #ifdef TEST
133
134 #include <stdlib.h>
135
136 static void test_err(void *x UNUSED, uns flags, char *msg UNUSED)
137 {
138   if (flags & FBSOCK_READ)
139     printf("READ");
140   else if (flags & FBSOCK_WRITE)
141     printf("WRITE");
142   if (flags & FBSOCK_TIMEOUT)
143     printf(" TIMEOUT\n");
144   else
145     printf(" ERROR\n");
146   exit(0);
147 }
148
149 int main(void)
150 {
151   int fd[2];
152   if (pipe(fd) < 0)
153     ASSERT(0);
154
155   struct fbsock_params p = {
156       .fd = fd[0],
157       .bufsize = 16,
158       .timeout_ms = 100,
159       .err = test_err
160   };
161   struct fastbuf *f = fbsock_create(&p);
162
163   bputsn(f, "Oook!");           // This fits in PIPE_BUF
164   bflush(f);
165
166   char buf[256];
167   if (!bgets(f, buf, sizeof(buf)))
168     die("bgets failed");
169   if (strcmp(buf, "Oook!"))
170     die("Misread input");
171
172   bgets(f, buf, sizeof(buf));
173   puts("WRONG");
174   exit(0);
175 }
176
177 #endif