layer1/l23_api: Use the fn51 given in the l1a_rach_req
[osmocom-bb.git] / src / shared / libosmocore / src / vty / buffer.c
1 /*
2  * Buffering of output and input.
3  * Copyright (C) 1998 Kunihiro Ishiguro
4  *
5  * This file is part of GNU Zebra.
6  *
7  * GNU Zebra is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published
9  * by the Free Software Foundation; either version 2, or (at your
10  * option) any later version.
11  *
12  * GNU Zebra is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Zebra; see the file COPYING.  If not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <stddef.h>
29 #include <sys/uio.h>
30
31 #include <osmocore/talloc.h>
32 #include <osmocom/vty/buffer.h>
33 #include <osmocom/vty/vty.h>
34
35 /* Buffer master. */
36 struct buffer {
37         /* Data list. */
38         struct buffer_data *head;
39         struct buffer_data *tail;
40
41         /* Size of each buffer_data chunk. */
42         size_t size;
43 };
44
45 /* Data container. */
46 struct buffer_data {
47         struct buffer_data *next;
48
49         /* Location to add new data. */
50         size_t cp;
51
52         /* Pointer to data not yet flushed. */
53         size_t sp;
54
55         /* Actual data stream (variable length). */
56         unsigned char data[0];  /* real dimension is buffer->size */
57 };
58
59 /* It should always be true that: 0 <= sp <= cp <= size */
60
61 /* Default buffer size (used if none specified).  It is rounded up to the
62    next page boundery. */
63 #define BUFFER_SIZE_DEFAULT             4096
64
65 #define BUFFER_DATA_FREE(D) talloc_free((D))
66
67 /* Make new buffer. */
68 struct buffer *buffer_new(void *ctx, size_t size)
69 {
70         struct buffer *b;
71
72         b = talloc_zero(ctx, struct buffer);
73
74         if (size)
75                 b->size = size;
76         else {
77                 static size_t default_size;
78                 if (!default_size) {
79                         long pgsz = sysconf(_SC_PAGESIZE);
80                         default_size =
81                             ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
82                 }
83                 b->size = default_size;
84         }
85
86         return b;
87 }
88
89 /* Free buffer. */
90 void buffer_free(struct buffer *b)
91 {
92         buffer_reset(b);
93         talloc_free(b);
94 }
95
96 /* Make string clone. */
97 char *buffer_getstr(struct buffer *b)
98 {
99         size_t totlen = 0;
100         struct buffer_data *data;
101         char *s;
102         char *p;
103
104         for (data = b->head; data; data = data->next)
105                 totlen += data->cp - data->sp;
106         if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr")))
107                 return NULL;
108         p = s;
109         for (data = b->head; data; data = data->next) {
110                 memcpy(p, data->data + data->sp, data->cp - data->sp);
111                 p += data->cp - data->sp;
112         }
113         *p = '\0';
114         return s;
115 }
116
117 /* Return 1 if buffer is empty. */
118 int buffer_empty(struct buffer *b)
119 {
120         return (b->head == NULL);
121 }
122
123 /* Clear and free all allocated data. */
124 void buffer_reset(struct buffer *b)
125 {
126         struct buffer_data *data;
127         struct buffer_data *next;
128
129         for (data = b->head; data; data = next) {
130                 next = data->next;
131                 BUFFER_DATA_FREE(data);
132         }
133         b->head = b->tail = NULL;
134 }
135
136 /* Add buffer_data to the end of buffer. */
137 static struct buffer_data *buffer_add(struct buffer *b)
138 {
139         struct buffer_data *d;
140
141         d = _talloc_zero(b,
142                          offsetof(struct buffer_data, data[b->size]),
143                          "buffer_add");
144         if (!d)
145                 return NULL;
146         d->cp = d->sp = 0;
147         d->next = NULL;
148
149         if (b->tail)
150                 b->tail->next = d;
151         else
152                 b->head = d;
153         b->tail = d;
154
155         return d;
156 }
157
158 /* Write data to buffer. */
159 void buffer_put(struct buffer *b, const void *p, size_t size)
160 {
161         struct buffer_data *data = b->tail;
162         const char *ptr = p;
163
164         /* We use even last one byte of data buffer. */
165         while (size) {
166                 size_t chunk;
167
168                 /* If there is no data buffer add it. */
169                 if (data == NULL || data->cp == b->size)
170                         data = buffer_add(b);
171
172                 chunk =
173                     ((size <=
174                       (b->size - data->cp)) ? size : (b->size - data->cp));
175                 memcpy((data->data + data->cp), ptr, chunk);
176                 size -= chunk;
177                 ptr += chunk;
178                 data->cp += chunk;
179         }
180 }
181
182 /* Insert character into the buffer. */
183 void buffer_putc(struct buffer *b, u_char c)
184 {
185         buffer_put(b, &c, 1);
186 }
187
188 /* Put string to the buffer. */
189 void buffer_putstr(struct buffer *b, const char *c)
190 {
191         buffer_put(b, c, strlen(c));
192 }
193
194 /* Keep flushing data to the fd until the buffer is empty or an error is
195    encountered or the operation would block. */
196 buffer_status_t buffer_flush_all(struct buffer *b, int fd)
197 {
198         buffer_status_t ret;
199         struct buffer_data *head;
200         size_t head_sp;
201
202         if (!b->head)
203                 return BUFFER_EMPTY;
204         head_sp = (head = b->head)->sp;
205         /* Flush all data. */
206         while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
207                 if ((b->head == head) && (head_sp == head->sp)
208                     && (errno != EINTR))
209                         /* No data was flushed, so kernel buffer must be full. */
210                         return ret;
211                 head_sp = (head = b->head)->sp;
212         }
213
214         return ret;
215 }
216
217 #if 0
218 /* Flush enough data to fill a terminal window of the given scene (used only
219    by vty telnet interface). */
220 buffer_status_t
221 buffer_flush_window(struct buffer * b, int fd, int width, int height,
222                     int erase_flag, int no_more_flag)
223 {
224         int nbytes;
225         int iov_alloc;
226         int iov_index;
227         struct iovec *iov;
228         struct iovec small_iov[3];
229         char more[] = " --More-- ";
230         char erase[] =
231             { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
232                 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
233                 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
234         };
235         struct buffer_data *data;
236         int column;
237
238         if (!b->head)
239                 return BUFFER_EMPTY;
240
241         if (height < 1) {
242                 zlog_warn
243                     ("%s called with non-positive window height %d, forcing to 1",
244                      __func__, height);
245                 height = 1;
246         } else if (height >= 2)
247                 height--;
248         if (width < 1) {
249                 zlog_warn
250                     ("%s called with non-positive window width %d, forcing to 1",
251                      __func__, width);
252                 width = 1;
253         }
254
255         /* For erase and more data add two to b's buffer_data count. */
256         if (b->head->next == NULL) {
257                 iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
258                 iov = small_iov;
259         } else {
260                 iov_alloc = ((height * (width + 2)) / b->size) + 10;
261                 iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
262         }
263         iov_index = 0;
264
265         /* Previously print out is performed. */
266         if (erase_flag) {
267                 iov[iov_index].iov_base = erase;
268                 iov[iov_index].iov_len = sizeof erase;
269                 iov_index++;
270         }
271
272         /* Output data. */
273         column = 1;             /* Column position of next character displayed. */
274         for (data = b->head; data && (height > 0); data = data->next) {
275                 size_t cp;
276
277                 cp = data->sp;
278                 while ((cp < data->cp) && (height > 0)) {
279                         /* Calculate lines remaining and column position after displaying
280                            this character. */
281                         if (data->data[cp] == '\r')
282                                 column = 1;
283                         else if ((data->data[cp] == '\n') || (column == width)) {
284                                 column = 1;
285                                 height--;
286                         } else
287                                 column++;
288                         cp++;
289                 }
290                 iov[iov_index].iov_base = (char *)(data->data + data->sp);
291                 iov[iov_index++].iov_len = cp - data->sp;
292                 data->sp = cp;
293
294                 if (iov_index == iov_alloc)
295                         /* This should not ordinarily happen. */
296                 {
297                         iov_alloc *= 2;
298                         if (iov != small_iov) {
299                                 zlog_warn("%s: growing iov array to %d; "
300                                           "width %d, height %d, size %lu",
301                                           __func__, iov_alloc, width, height,
302                                           (u_long) b->size);
303                                 iov =
304                                     XREALLOC(MTYPE_TMP, iov,
305                                              iov_alloc * sizeof(*iov));
306                         } else {
307                                 /* This should absolutely never occur. */
308                                 zlog_err
309                                     ("%s: corruption detected: iov_small overflowed; "
310                                      "head %p, tail %p, head->next %p",
311                                      __func__, b->head, b->tail, b->head->next);
312                                 iov =
313                                     XMALLOC(MTYPE_TMP,
314                                             iov_alloc * sizeof(*iov));
315                                 memcpy(iov, small_iov, sizeof(small_iov));
316                         }
317                 }
318         }
319
320         /* In case of `more' display need. */
321         if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
322                 iov[iov_index].iov_base = more;
323                 iov[iov_index].iov_len = sizeof more;
324                 iov_index++;
325         }
326 #ifdef IOV_MAX
327         /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
328            example: Solaris2.6 are defined IOV_MAX size at 16.     */
329         {
330                 struct iovec *c_iov = iov;
331                 nbytes = 0;     /* Make sure it's initialized. */
332
333                 while (iov_index > 0) {
334                         int iov_size;
335
336                         iov_size =
337                             ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
338                         if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
339                                 zlog_warn("%s: writev to fd %d failed: %s",
340                                           __func__, fd, safe_strerror(errno));
341                                 break;
342                         }
343
344                         /* move pointer io-vector */
345                         c_iov += iov_size;
346                         iov_index -= iov_size;
347                 }
348         }
349 #else                           /* IOV_MAX */
350         if ((nbytes = writev(fd, iov, iov_index)) < 0)
351                 zlog_warn("%s: writev to fd %d failed: %s",
352                           __func__, fd, safe_strerror(errno));
353 #endif                          /* IOV_MAX */
354
355         /* Free printed buffer data. */
356         while (b->head && (b->head->sp == b->head->cp)) {
357                 struct buffer_data *del;
358                 if (!(b->head = (del = b->head)->next))
359                         b->tail = NULL;
360                 BUFFER_DATA_FREE(del);
361         }
362
363         if (iov != small_iov)
364                 XFREE(MTYPE_TMP, iov);
365
366         return (nbytes < 0) ? BUFFER_ERROR :
367             (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
368 }
369 #endif
370
371 /* This function (unlike other buffer_flush* functions above) is designed
372 to work with non-blocking sockets.  It does not attempt to write out
373 all of the queued data, just a "big" chunk.  It returns 0 if it was
374 able to empty out the buffers completely, 1 if more flushing is
375 required later, or -1 on a fatal write error. */
376 buffer_status_t buffer_flush_available(struct buffer * b, int fd)
377 {
378
379 /* These are just reasonable values to make sure a significant amount of
380 data is written.  There's no need to go crazy and try to write it all
381 in one shot. */
382 #ifdef IOV_MAX
383 #define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
384 #else
385 #define MAX_CHUNKS 16
386 #endif
387 #define MAX_FLUSH 131072
388
389         struct buffer_data *d;
390         size_t written;
391         struct iovec iov[MAX_CHUNKS];
392         size_t iovcnt = 0;
393         size_t nbyte = 0;
394
395         for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
396              d = d->next, iovcnt++) {
397                 iov[iovcnt].iov_base = d->data + d->sp;
398                 nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
399         }
400
401         if (!nbyte)
402                 /* No data to flush: should we issue a warning message? */
403                 return BUFFER_EMPTY;
404
405         /* only place where written should be sign compared */
406         if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
407                 if (ERRNO_IO_RETRY(errno))
408                         /* Calling code should try again later. */
409                         return BUFFER_PENDING;
410                 return BUFFER_ERROR;
411         }
412
413         /* Free printed buffer data. */
414         while (written > 0) {
415                 struct buffer_data *d;
416                 if (!(d = b->head))
417                         break;
418                 if (written < d->cp - d->sp) {
419                         d->sp += written;
420                         return BUFFER_PENDING;
421                 }
422
423                 written -= (d->cp - d->sp);
424                 if (!(b->head = d->next))
425                         b->tail = NULL;
426                 BUFFER_DATA_FREE(d);
427         }
428
429         return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
430
431 #undef MAX_CHUNKS
432 #undef MAX_FLUSH
433 }
434
435 buffer_status_t
436 buffer_write(struct buffer * b, int fd, const void *p, size_t size)
437 {
438         ssize_t nbytes;
439
440 #if 0
441         /* Should we attempt to drain any previously buffered data?  This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
442
443         if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
444                 return BUFFER_ERROR;
445 #endif
446         if (b->head)
447                 /* Buffer is not empty, so do not attempt to write the new data. */
448                 nbytes = 0;
449         else if ((nbytes = write(fd, p, size)) < 0) {
450                 if (ERRNO_IO_RETRY(errno))
451                         nbytes = 0;
452                 else
453                         return BUFFER_ERROR;
454         }
455         /* Add any remaining data to the buffer. */
456         {
457                 size_t written = nbytes;
458                 if (written < size)
459                         buffer_put(b, ((const char *)p) + written,
460                                    size - written);
461         }
462         return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
463 }