From aa4ba5d283d641da25722af7a966c31d61a9686b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 25 Nov 2019 12:26:36 +0000 Subject: Import source, backport to libweston 6.0 Signed-off-by: Daniel Stone Change-Id: I7705fa82dab29a27e4913cd548a2f5c3247dc5ad --- shared/option-parser.c | 202 +++++++++++++++++++++++ shared/os-compatibility.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++ shared/os-compatibility.h | 72 +++++++++ shared/string-helpers.h | 71 ++++++++ 4 files changed, 750 insertions(+) create mode 100644 shared/option-parser.c create mode 100644 shared/os-compatibility.c create mode 100644 shared/os-compatibility.h create mode 100644 shared/string-helpers.h (limited to 'shared') diff --git a/shared/option-parser.c b/shared/option-parser.c new file mode 100644 index 0000000..81222f3 --- /dev/null +++ b/shared/option-parser.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2012 Kristian Høgsberg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "shared/string-helpers.h" + +static bool +handle_option(const struct weston_option *option, char *value) +{ + char* p; + + switch (option->type) { + case WESTON_OPTION_INTEGER: + if (!safe_strtoint(value, option->data)) + return false; + return true; + case WESTON_OPTION_UNSIGNED_INTEGER: + errno = 0; + * (uint32_t *) option->data = strtoul(value, &p, 10); + if (errno != 0 || p == value || *p != '\0') + return false; + return true; + case WESTON_OPTION_STRING: + * (char **) option->data = strdup(value); + return true; + default: + assert(0); + return false; + } +} + +static bool +long_option(const struct weston_option *options, int count, char *arg) +{ + int k, len; + + for (k = 0; k < count; k++) { + if (!options[k].name) + continue; + + len = strlen(options[k].name); + if (strncmp(options[k].name, arg + 2, len) != 0) + continue; + + if (options[k].type == WESTON_OPTION_BOOLEAN) { + if (!arg[len + 2]) { + * (int32_t *) options[k].data = 1; + + return true; + } + } else if (arg[len+2] == '=') { + return handle_option(options + k, arg + len + 3); + } + } + + return false; +} + +static bool +long_option_with_arg(const struct weston_option *options, int count, char *arg, + char *param) +{ + int k, len; + + for (k = 0; k < count; k++) { + if (!options[k].name) + continue; + + len = strlen(options[k].name); + if (strncmp(options[k].name, arg + 2, len) != 0) + continue; + + /* Since long_option() should handle all booleans, we should + * never reach this + */ + assert(options[k].type != WESTON_OPTION_BOOLEAN); + + return handle_option(options + k, param); + } + + return false; +} + +static bool +short_option(const struct weston_option *options, int count, char *arg) +{ + int k; + + if (!arg[1]) + return false; + + for (k = 0; k < count; k++) { + if (options[k].short_name != arg[1]) + continue; + + if (options[k].type == WESTON_OPTION_BOOLEAN) { + if (!arg[2]) { + * (int32_t *) options[k].data = 1; + + return true; + } + } else if (arg[2]) { + return handle_option(options + k, arg + 2); + } else { + return false; + } + } + + return false; +} + +static bool +short_option_with_arg(const struct weston_option *options, int count, char *arg, char *param) +{ + int k; + + if (!arg[1]) + return false; + + for (k = 0; k < count; k++) { + if (options[k].short_name != arg[1]) + continue; + + if (options[k].type == WESTON_OPTION_BOOLEAN) + continue; + + return handle_option(options + k, param); + } + + return false; +} + +int +parse_options(const struct weston_option *options, + int count, int *argc, char *argv[]) +{ + int i, j; + + for (i = 1, j = 1; i < *argc; i++) { + if (argv[i][0] == '-') { + if (argv[i][1] == '-') { + /* Long option, e.g. --foo or --foo=bar */ + if (long_option(options, count, argv[i])) + continue; + + /* ...also handle --foo bar */ + if (i + 1 < *argc && + long_option_with_arg(options, count, + argv[i], argv[i+1])) { + i++; + continue; + } + } else { + /* Short option, e.g -f or -f42 */ + if (short_option(options, count, argv[i])) + continue; + + /* ...also handle -f 42 */ + if (i+1 < *argc && + short_option_with_arg(options, count, argv[i], argv[i+1])) { + i++; + continue; + } + } + } + argv[j++] = argv[i]; + } + argv[j] = NULL; + *argc = j; + + return j; +} diff --git a/shared/os-compatibility.c b/shared/os-compatibility.c new file mode 100644 index 0000000..3013f1f --- /dev/null +++ b/shared/os-compatibility.c @@ -0,0 +1,405 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_MEMFD_CREATE +#include +#endif + +#include "os-compatibility.h" + +#define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) + +int +os_fd_set_cloexec(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; + + return 0; +} + +static int +set_cloexec_or_close(int fd) +{ + if (os_fd_set_cloexec(fd) != 0) { + close(fd); + return -1; + } + return fd; +} + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); + if (ret == 0 || errno != EINVAL) + return ret; +#endif + + ret = socketpair(domain, type, protocol, sv); + if (ret < 0) + return ret; + + sv[0] = set_cloexec_or_close(sv[0]); + sv[1] = set_cloexec_or_close(sv[1]); + + if (sv[0] != -1 && sv[1] != -1) + return 0; + + close(sv[0]); + close(sv[1]); + return -1; +} + +int +os_epoll_create_cloexec(void) +{ + int fd; + +#ifdef EPOLL_CLOEXEC + fd = epoll_create1(EPOLL_CLOEXEC); + if (fd >= 0) + return fd; + if (errno != EINVAL) + return -1; +#endif + + fd = epoll_create(1); + return set_cloexec_or_close(fd); +} + +static int +create_tmpfile_cloexec(char *tmpname) +{ + int fd; + +#ifdef HAVE_MKOSTEMP + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); +#else + fd = mkstemp(tmpname); + if (fd >= 0) { + fd = set_cloexec_or_close(fd); + unlink(tmpname); + } +#endif + + return fd; +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + * + * If the C library implements posix_fallocate(), it is used to + * guarantee that disk space is available for the file at the + * given size. If disk space is insufficient, errno is set to ENOSPC. + * If posix_fallocate() is not supported, program may receive + * SIGBUS on accessing mmap()'ed file contents instead. + * + * If the C library implements memfd_create(), it is used to create the + * file purely in memory, without any backing file name on the file + * system, and then sealing off the possibility of shrinking it. This + * can then be checked before accessing mmap()'ed file contents, to + * make sure SIGBUS can't happen. It also avoids requiring + * XDG_RUNTIME_DIR. + */ +int +os_create_anonymous_file(off_t size) +{ + static const char template[] = "/weston-shared-XXXXXX"; + const char *path; + char *name; + int fd; + int ret; + +#ifdef HAVE_MEMFD_CREATE + fd = memfd_create("weston-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) { + /* We can add this seal before calling posix_fallocate(), as + * the file is currently zero-sized anyway. + * + * There is also no need to check for the return value, we + * couldn't do anything with it anyway. + */ + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK); + } else +#endif + { + path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + name = malloc(strlen(path) + sizeof(template)); + if (!name) + return -1; + + strcpy(name, path); + strcat(name, template); + + fd = create_tmpfile_cloexec(name); + + free(name); + + if (fd < 0) + return -1; + } + +#ifdef HAVE_POSIX_FALLOCATE + do { + ret = posix_fallocate(fd, 0, size); + } while (ret == EINTR); + if (ret != 0) { + close(fd); + errno = ret; + return -1; + } +#else + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + return -1; + } +#endif + + return fd; +} + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} +#endif + +struct ro_anonymous_file { + int fd; + size_t size; +}; + +/** Create a new anonymous read-only file of the given size and the given data + * + * \param size The size of \p data. + * \param data The data of the file with the size \p size. + * \return A new \c ro_anonymous_file, or NULL on failure. + * + * The intended use-case is for sending mid-sized data from the compositor + * to clients. + * If the function fails errno is set. + */ +struct ro_anonymous_file * +os_ro_anonymous_file_create(size_t size, + const char *data) +{ + struct ro_anonymous_file *file; + void *map; + + file = zalloc(sizeof *file); + if (!file) { + errno = ENOMEM; + return NULL; + } + + file->size = size; + file->fd = os_create_anonymous_file(size); + if (file->fd == -1) + goto err_free; + + map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0); + if (map == MAP_FAILED) + goto err_close; + + memcpy(map, data, size); + + munmap(map, size); + +#ifdef HAVE_MEMFD_CREATE + /* try to put seals on the file to make it read-only so that we can + * return the fd later directly when support_shared is not set. + * os_ro_anonymous_file_get_fd can handle the fd even if it is not + * sealed read-only and will instead create a new anonymous file on + * each invocation. + */ + fcntl(file->fd, F_ADD_SEALS, READONLY_SEALS); +#endif + + return file; + +err_close: + close(file->fd); +err_free: + free(file); + return NULL; +} + +/** Destroy an anonymous read-only file + * + * \param file The file to destroy. + */ +void +os_ro_anonymous_file_destroy(struct ro_anonymous_file *file) +{ + close(file->fd); + free(file); +} + +/** Get the size of an anonymous read-only file + * + * \param file The file to get the size of. + * \return The size of the file. + */ +size_t +os_ro_anonymous_file_size(struct ro_anonymous_file *file) +{ + return file->size; +} + +/** Returns a file descriptor for the given file, ready to be send to a client. + * + * \param file The file for which to get a file descriptor. + * \param mapmode Describes the ways in which the returned file descriptor can + * be used with mmap. + * \return A file descriptor for the given file that can be send to a client + * or -1 on failure. + * + * The returned file descriptor must not be shared between multiple clients. + * When \p mapmode is RO_ANONYMOUS_FILE_MAPMODE_PRIVATE the file descriptor is + * only guaranteed to be mmapable with \c MAP_PRIVATE, when \p mapmode is + * RO_ANONYMOUS_FILE_MAPMODE_SHARED the file descriptor can be mmaped with + * either MAP_PRIVATE or MAP_SHARED. + * When you're done with the fd you must call \c os_ro_anonymous_file_put_fd + * instead of calling \c close. + * If the function fails errno is set. + */ +int +os_ro_anonymous_file_get_fd(struct ro_anonymous_file *file, + enum ro_anonymous_file_mapmode mapmode) +{ + void *src, *dst; + int seals, fd; + + seals = fcntl(file->fd, F_GET_SEALS); + + /* file was sealed for read-only and we don't have to support MAP_SHARED + * so we can simply pass the memfd fd + */ + if (seals != -1 && mapmode == RO_ANONYMOUS_FILE_MAPMODE_PRIVATE && + (seals & READONLY_SEALS) == READONLY_SEALS) + return file->fd; + + /* for all other cases we create a new anonymous file that can be mapped + * with MAP_SHARED and copy the contents to it and return that instead + */ + fd = os_create_anonymous_file(file->size); + if (fd == -1) + return fd; + + src = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0); + if (src == MAP_FAILED) { + close(fd); + return -1; + } + + dst = mmap(NULL, file->size, PROT_WRITE, MAP_SHARED, fd, 0); + if (dst == MAP_FAILED) { + close(fd); + munmap(src, file->size); + return -1; + } + + memcpy(dst, src, file->size); + munmap(src, file->size); + munmap(dst, file->size); + + return fd; +} + +/** Release a file descriptor returned by \c os_ro_anonymous_file_get_fd + * + * \param fd A file descriptor returned by \c os_ro_anonymous_file_get_fd. + * \return 0 on success, or -1 on failure. + * + * This function must be called for every file descriptor created with + * \c os_ro_anonymous_file_get_fd to not leake any resources. + * If the function fails errno is set. + */ +int +os_ro_anonymous_file_put_fd(int fd) +{ + int seals = fcntl(fd, F_GET_SEALS); + if (seals == -1 && errno != EINVAL) + return -1; + + /* If the fd cannot be sealed seals is -1 at this point + * or the file can be sealed but has not been sealed for writing. + * In both cases we created a new anonymous file that we have to + * close. + */ + if (seals == -1 || !(seals & F_SEAL_WRITE)) + close(fd); + + return 0; +} diff --git a/shared/os-compatibility.h b/shared/os-compatibility.h new file mode 100644 index 0000000..1ad45d7 --- /dev/null +++ b/shared/os-compatibility.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef OS_COMPATIBILITY_H +#define OS_COMPATIBILITY_H + +#include + +int +os_fd_set_cloexec(int fd); + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv); + +int +os_epoll_create_cloexec(void); + +int +os_create_anonymous_file(off_t size); + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c); +#endif + +struct ro_anonymous_file; + +enum ro_anonymous_file_mapmode { + RO_ANONYMOUS_FILE_MAPMODE_PRIVATE, + RO_ANONYMOUS_FILE_MAPMODE_SHARED, +}; + +struct ro_anonymous_file * +os_ro_anonymous_file_create(size_t size, + const char *data); + +void +os_ro_anonymous_file_destroy(struct ro_anonymous_file *file); + +size_t +os_ro_anonymous_file_size(struct ro_anonymous_file *file); + +int +os_ro_anonymous_file_get_fd(struct ro_anonymous_file *file, + enum ro_anonymous_file_mapmode mapmode); + +int +os_ro_anonymous_file_put_fd(int fd); + +#endif /* OS_COMPATIBILITY_H */ diff --git a/shared/string-helpers.h b/shared/string-helpers.h new file mode 100644 index 0000000..c8ce449 --- /dev/null +++ b/shared/string-helpers.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2016 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WESTON_STRING_HELPERS_H +#define WESTON_STRING_HELPERS_H + +#include +#include +#include +#include +#include + +/* Convert string to integer + * + * Parses a base-10 number from the given string. Checks that the + * string is not blank, contains only numerical characters, and is + * within the range of INT32_MIN to INT32_MAX. If the validation is + * successful the result is stored in *value; otherwise *value is + * unchanged and errno is set appropriately. + * + * \return true if the number parsed successfully, false on error + */ +static inline bool +safe_strtoint(const char *str, int32_t *value) +{ + long ret; + char *end; + + assert(str != NULL); + + errno = 0; + ret = strtol(str, &end, 10); + if (errno != 0) { + return false; + } else if (end == str || *end != '\0') { + errno = EINVAL; + return false; + } + + if ((long)((int32_t)ret) != ret) { + errno = ERANGE; + return false; + } + *value = (int32_t)ret; + + return true; +} + +#endif /* WESTON_STRING_HELPERS_H */ -- cgit 1.2.3-korg