diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 20:34:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 20:34:44 +0000 |
commit | e3be059d4da38aa36f1aee1d56f8ceb943d92f1c (patch) | |
tree | 26edef31e4e503dd1c92a112de174f366dd61802 /local/procio.c | |
parent | Initial commit. (diff) | |
download | procps-e3be059d4da38aa36f1aee1d56f8ceb943d92f1c.tar.xz procps-e3be059d4da38aa36f1aee1d56f8ceb943d92f1c.zip |
Adding upstream version 2:4.0.4.upstream/2%4.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'local/procio.c')
-rw-r--r-- | local/procio.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/local/procio.c b/local/procio.c new file mode 100644 index 0000000..bbd7c84 --- /dev/null +++ b/local/procio.c @@ -0,0 +1,292 @@ +/* + * procio.c -- Replace stdio for read and write on files below + * proc to be able to read and write large buffers as well. + * + * Copyright (C) 2017 Werner Fink + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +typedef struct pcookie { + char *buf; + size_t count; + size_t length; + off_t offset; + int fd; + int delim; + int final:1; +} pcookie_t; + +static ssize_t proc_read(void *, char *, size_t); +static ssize_t proc_write(void *, const char *, size_t); +static int proc_close(void *); + +__extension__ +static cookie_io_functions_t procio = { + .read = proc_read, + .write = proc_write, + .seek = NULL, + .close = proc_close, +}; + +FILE *fprocopen(const char *path, const char *mode) +{ + pcookie_t *cookie = NULL; + FILE *handle = NULL; + mode_t flags = 0; + size_t len = 0; + int c, delim; + + if (!mode || !(len = strlen(mode))) { + errno = EINVAL; + goto out; + } + + /* No append mode possible */ + switch (mode[0]) { + case 'r': + flags |= O_RDONLY; + break; + case 'w': + flags |= O_WRONLY|O_TRUNC; + break; + default: + errno = EINVAL; + goto out; + } + + delim = ','; /* default delimeter is the comma */ + for (c = 1; c < len; c++) { + switch (mode[c]) { + case '\0': + break; + case '+': + errno = EINVAL; + goto out; + case 'e': + flags |= O_CLOEXEC; + continue; + case 'b': + case 'm': + case 'x': + /* ignore this */ + continue; + default: + if (mode[c] == ' ' || (mode[c] >= ',' && mode[c] <= '.') || mode[c] == ':') + delim = mode[c]; + else { + errno = EINVAL; + goto out; + } + break; + } + break; + } + + cookie = (pcookie_t *)malloc(sizeof(pcookie_t)); + if (!cookie) + goto out; + cookie->count = BUFSIZ; + cookie->buf = (char *)malloc(cookie->count); + if (!cookie->buf) { + int errsv = errno; + free(cookie); + errno = errsv; + goto out; + } + cookie->final = 0; + cookie->offset = 0; + cookie->length = 0; + cookie->delim = delim; + + cookie->fd = openat(AT_FDCWD, path, flags); + if (cookie->fd < 0) { + int errsv = errno; + free(cookie->buf); + free(cookie); + errno = errsv; + goto out; + } + + handle = fopencookie(cookie, mode, procio); + if (!handle) { + int errsv = errno; + close(cookie->fd); + free(cookie->buf); + free(cookie); + errno = errsv; + goto out; + } +out: + return handle; +} + +static +ssize_t proc_read(void *c, char *buf, size_t count) +{ + pcookie_t *cookie = c; + ssize_t len = -1; + void *ptr; + + if (cookie->count < count) { + ptr = realloc(cookie->buf, count); + if (!ptr) + goto out; + cookie->buf = ptr; + cookie->count = count; + } + + while (!cookie->final) { + len = read(cookie->fd, cookie->buf, cookie->count); + + if (len <= 0) { + if (len == 0) { + /* EOF */ + cookie->final = 1; + cookie->buf[cookie->length] = '\0'; + break; + } + goto out; /* error or done */ + } + + cookie->length = len; + + if (cookie->length < cookie->count) + continue; + + /* Likly to small buffer here */ + + lseek(cookie->fd, 0, SEEK_SET); /* reset for a retry */ + + ptr = realloc(cookie->buf, cookie->count += BUFSIZ); + if (!ptr) + goto out; + cookie->buf = ptr; + } + + len = count; + if (cookie->length - cookie->offset < len) + len = cookie->length - cookie->offset; + + if (len < 0) + len = 0; + + if (len) { + (void)memcpy(buf, cookie->buf+cookie->offset, len); + cookie->offset += len; + } else + len = EOF; +out: + return len; +} + +#define LINELEN 4096 + +static +ssize_t proc_write(void *c, const char *buf, size_t count) +{ + pcookie_t *cookie = c; + ssize_t len = -1; + void *ptr; + + if (!count) { + len = 0; + goto out; + } + + /* NL is the final input */ + cookie->final = memrchr(buf, '\n', count) ? 1 : 0; + + while (cookie->count < cookie->offset + count) { + ptr = realloc(cookie->buf, cookie->count += count); + if (!ptr) + goto out; + cookie->buf = ptr; + } + + len = count; + (void)memcpy(cookie->buf+cookie->offset, buf, count); + cookie->offset += count; + + if (cookie->final) { + len = write(cookie->fd, cookie->buf, cookie->offset); + if (len < 0 && errno == EINVAL) { + size_t offset; + off_t amount; + char *token; + /* + * Oops buffer might be to large, split buffer into + * pieces at delimeter if provided + */ + if (!cookie->delim) + goto out; /* Hey dude?! */ + offset = 0; + do { + token = NULL; + if (cookie->offset > LINELEN) + token = (char*)memrchr(cookie->buf+offset, cookie->delim, LINELEN); + else + token = (char*)memrchr(cookie->buf+offset, '\n', cookie->offset); + if (token) + *token = '\n'; + else { + errno = EINVAL; + len = -1; + goto out; /* Wrong/Missing delimeter? */ + } + if (offset > 0) + lseek(cookie->fd, 1, SEEK_CUR); + + amount = token-(cookie->buf+offset)+1; + ptr = cookie->buf+offset; + + len = write(cookie->fd, ptr, amount); + if (len < 1 || len >= cookie->offset) + break; + + offset += len; + cookie->offset -= len; + + } while (cookie->offset > 0); + } + if (len > 0) + len = count; + } +out: + return len; +} + +static +int proc_close(void *c) +{ + pcookie_t *cookie = c; + close(cookie->fd); + free(cookie->buf); + free(cookie); + return 0; +} |