]> git.pond.sub.org Git - empserver/blob - src/lib/gen/ioqueue.c
Err, the race in io_output() doesn't double-free
[empserver] / src / lib / gen / ioqueue.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2010, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  ioqueue.c: Read and write i/o queues
29  *
30  *  Known contributors to this file:
31  *
32  */
33
34 /*
35  * Read and write onto io queues.  Note that
36  * the io queues don't actually do any writing;
37  * that is left for a higher level.
38  */
39
40 #include <config.h>
41
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/uio.h>
45 #include "ioqueue.h"
46 #include "misc.h"
47 #include "queue.h"
48
49 struct io {
50     struct emp_qelem queue;
51     int size;
52     int nbytes;
53     int offset;
54     char *data;
55 };
56
57 struct ioqueue {
58     struct io list;
59     int bufsize;
60     int cc;
61 };
62
63 static int ioqtocbuf(struct ioqueue *ioq, char *buf, int cc, int stopc);
64 static int ioqtoiov(struct ioqueue *ioq, struct iovec *iov, int max);
65 static int ioqtobuf(struct ioqueue *ioq, char *buf, int cc);
66 static int appendcc(struct ioqueue *ioq, char *buf, int cc);
67 static int removecc(struct ioqueue *ioq, int cc);
68
69 struct ioqueue *
70 ioq_create(int size)
71 {
72     struct ioqueue *ioq;
73
74     ioq = malloc(sizeof(struct ioqueue));
75     emp_initque(&ioq->list.queue);
76     ioq->list.nbytes = 0;
77     ioq->list.offset = 0;
78     ioq->list.size = 0;
79     ioq->list.data = NULL;
80     ioq->bufsize = size;
81     ioq->cc = 0;
82     return ioq;
83 }
84
85 void
86 ioq_destroy(struct ioqueue *ioq)
87 {
88     ioq_drain(ioq);
89     free(ioq);
90 }
91
92 void
93 ioq_drain(struct ioqueue *ioq)
94 {
95     struct emp_qelem *qp;
96     struct io *io;
97
98     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
99         io = (struct io *)qp;
100         emp_remque(&io->queue);
101         free(io->data);
102         free(io);
103     }
104
105     ioq->cc = 0;
106 }
107
108 /*
109  * copy batch of pointers into the passed
110  * iovec, but don't actually dequeue the data.
111  * return # of iovec initialized.
112  */
113 int
114 ioq_makeiov(struct ioqueue *ioq, struct iovec *iov, int cc)
115 {
116     if (ioq->cc <= 0)
117         return 0;
118     return ioqtoiov(ioq, iov, cc);
119 }
120
121 /*
122  * Copy the specified number of characters into the buffer
123  * provided, without actually dequeueing the data.  Return
124  * number of bytes actually found.
125  */
126 int
127 ioq_peek(struct ioqueue *ioq, char *buf, int cc)
128 {
129     return ioqtobuf(ioq, buf, cc);
130 }
131
132 int
133 ioq_dequeue(struct ioqueue *ioq, int cc)
134 {
135     int res = removecc(ioq, cc);
136     CANT_HAPPEN(res != cc);
137     return res;
138 }
139
140 void
141 ioq_append(struct ioqueue *ioq, char *buf, int cc)
142 {
143     appendcc(ioq, buf, cc);
144 }
145
146 int
147 ioq_qsize(struct ioqueue *ioq)
148 {
149     return ioq->cc;
150 }
151
152 /*
153  * read a line of text up to (but not including)
154  * the newline.  return -1 and read nothing if
155  * no input is available
156  */
157 int
158 ioq_gets(struct ioqueue *ioq, char *buf, int cc)
159 {
160     int nbytes;
161     int actual;
162
163     nbytes = ioqtocbuf(ioq, buf, cc - 1, '\n');
164     if (nbytes >= 0) {
165         actual = nbytes;
166         if (actual > cc - 1)
167             actual = cc - 1;
168         /* telnet terminates lines with "\r\n", get rid of \r */
169         if (actual > 0 && buf[actual-1] == '\r')
170             actual--;
171         buf[actual] = '\0';
172         /* remove the newline too */
173         removecc(ioq, nbytes + 1);
174     }
175     return nbytes;
176 }
177
178 int
179 ioq_puts(struct ioqueue *ioq, char *buf)
180 {
181     return appendcc(ioq, buf, strlen(buf));
182 }
183
184 /*
185  * copy cc bytes from ioq to buf.
186  * this routine doesn't free memory; this is
187  * left for a higher level.
188  */
189 static int
190 ioqtobuf(struct ioqueue *ioq, char *buf, int cc)
191 {
192     struct io *io;
193     struct emp_qelem *qp;
194     struct emp_qelem *head;
195     int nbytes, nleft;
196     char *offset;
197
198     nleft = cc;
199     offset = buf;
200     head = &ioq->list.queue;
201     for (qp = head->q_forw; qp != head && nleft > 0; qp = qp->q_forw) {
202         io = (struct io *)qp;
203         if ((nbytes = io->nbytes - io->offset) < 0) {
204             /* XXX log something here */
205             continue;
206         }
207         if (nbytes > 0) {
208             if (nleft < nbytes)
209                 nbytes = nleft;
210             memcpy(offset, io->data + io->offset, nbytes);
211             offset += nbytes;
212             nleft -= nbytes;
213         }
214     }
215     return offset - buf;
216 }
217
218 /*
219  * copy at most cc bytes from ioq to buf,
220  * terminating on the stop character.
221  */
222 static int
223 ioqtocbuf(struct ioqueue *ioq, char *buf, int cc, int stopc)
224 {
225     int nbytes;
226     char *p;
227     int n;
228     struct io *io;
229     struct emp_qelem *qp;
230     struct emp_qelem *head;
231     int total;
232     int found;
233
234     head = &ioq->list.queue;
235     found = 0;
236     total = 0;
237     for (qp = head->q_forw; qp != head; qp = qp->q_forw) {
238         io = (struct io *)qp;
239         if ((nbytes = io->nbytes - io->offset) <= 0)
240             continue;
241         p = io->data + io->offset;
242         for (n = 0; n < nbytes && p[n] != stopc; n++) ;
243         total += n;
244         if (n < nbytes) {
245             found++;
246             break;
247         }
248     }
249     if (found == 0)
250         return -1;
251     ioqtobuf(ioq, buf, cc < total ? cc : total);
252     return total;
253 }
254
255 /*
256  * initialize an iovec to point at max bytes worth
257  * of data from the ioqueue.
258  */
259 static int
260 ioqtoiov(struct ioqueue *ioq, struct iovec *iov, int max)
261 {
262     struct io *io;
263     int cc, niov, len;
264     struct emp_qelem *qp;
265
266     cc = max;
267     niov = 0;
268     qp = ioq->list.queue.q_forw;
269     while (qp != &ioq->list.queue && cc > 0) {
270         io = (struct io *)qp;
271         len = io->nbytes - io->offset;
272         if (len > cc)
273             len = cc;
274         iov->iov_base = io->data + io->offset;
275         iov->iov_len = len;
276         cc -= len;
277         niov++;
278         iov++;
279         qp = qp->q_forw;
280         if (niov >= 16)
281             break;
282     }
283     return niov;
284 }
285
286 /*
287  * append a buffer to the end of the ioq.
288  */
289 static int
290 appendcc(struct ioqueue *ioq, char *buf, int cc)
291 {
292     struct io *io;
293     int len;
294     char *ptr;
295     int avail;
296
297     /* determine if any space is left */
298     io = (struct io *)ioq->list.queue.q_back;
299     avail = io->size - io->nbytes;
300     if (avail > 0) {
301         /* append to existing buffer */
302         len = cc > avail ? avail : cc;
303         memcpy(io->data + io->nbytes, buf, len);
304         io->nbytes += len;
305         ioq->cc += len;
306         if (avail < cc)
307             appendcc(ioq, buf + len, cc - len);
308     } else {
309         /* create a new buffer, minimum bufsize bytes */
310         len = cc > ioq->bufsize ? cc : ioq->bufsize;
311         ptr = malloc(len);
312         memcpy(ptr, buf, cc);
313         io = malloc(sizeof(struct io));
314         io->nbytes = cc;
315         io->size = len;
316         io->offset = 0;
317         io->data = ptr;
318         emp_insque(&io->queue, ioq->list.queue.q_back);
319         ioq->cc += cc;
320     }
321     return cc;
322 }
323
324 /*
325  * remove cc bytes from ioqueue ioq
326  * free memory, dequeue io elements
327  * which are no longer used.
328  */
329 static int
330 removecc(struct ioqueue *ioq, int cc)
331 {
332     struct io *io;
333     struct emp_qelem *qp;
334     int nbytes, there, remain;
335
336     nbytes = 0;
337     remain = cc;
338     while ((qp = ioq->list.queue.q_forw) != &ioq->list.queue) {
339         io = (struct io *)qp;
340         there = io->nbytes - io->offset;
341         if (there < 0) {
342             /* error */
343             emp_remque(&io->queue);
344             free(io);
345             continue;
346         }
347         if (remain >= there) {
348             /* not enough or exact; free entry */
349             nbytes += there;
350             remain -= there;
351             emp_remque(&io->queue);
352             free(io->data);
353             free(io);
354         } else {
355             /* too much; increment offset */
356             io->offset += remain;
357             nbytes += remain;
358             remain = 0;
359         }
360         if (remain <= 0)
361             break;
362     }
363     ioq->cc -= nbytes;
364     return nbytes;
365 }