diff options
author | Harald Hoyer <harald@redhat.com> | 2012-06-29 11:59:09 +0200 |
---|---|---|
committer | Harald Hoyer <harald@redhat.com> | 2012-06-29 12:54:38 +0200 |
commit | 026b81e980507289ec8c325bc26ce1e3ec62f3b0 (patch) | |
tree | e5fc99a33cb0cbb857640a3be95ef928bf48d5e3 /install | |
parent | 6571cd4073c0581cda6919fa1565b1edaa46d7bd (diff) | |
download | dracut-026b81e980507289ec8c325bc26ce1e3ec62f3b0.tar.gz dracut-026b81e980507289ec8c325bc26ce1e3ec62f3b0.tar.bz2 dracut-026b81e980507289ec8c325bc26ce1e3ec62f3b0.zip |
install/*: add dracut-install tool
Diffstat (limited to 'install')
-rw-r--r-- | install/Makefile | 17 | ||||
-rw-r--r-- | install/dracut-install.c | 736 | ||||
-rw-r--r-- | install/hashmap.c | 731 | ||||
-rw-r--r-- | install/hashmap.h | 91 | ||||
-rw-r--r-- | install/hashmap.lo | 12 | ||||
-rw-r--r-- | install/hashmap.o | bin | 0 -> 46384 bytes | |||
-rw-r--r-- | install/log.c | 294 | ||||
-rw-r--r-- | install/log.h | 115 | ||||
-rw-r--r-- | install/macro.h | 192 | ||||
-rw-r--r-- | install/util.c | 187 | ||||
-rw-r--r-- | install/util.h | 527 |
11 files changed, 2902 insertions, 0 deletions
diff --git a/install/Makefile b/install/Makefile new file mode 100644 index 00000000..59532a83 --- /dev/null +++ b/install/Makefile @@ -0,0 +1,17 @@ +prefix ?= /usr +bindir ?= ${prefix}/bin +strip ?= -s + +all: dracut-install + +dracut-install: dracut-install.c hashmap.c log.c util.c + gcc -std=gnu99 -O2 -g -Wall -o dracut-install dracut-install.c hashmap.c log.c util.c + +install: dracut-install + install $(strip) -m 0755 dracut-install $(DESTDIR)$(bindir)/dracut-install + +clean: + rm -f dracut-install *~ + +indent: + indent -i8 -nut -br -linux -l120 dracut-install.c diff --git a/install/dracut-install.c b/install/dracut-install.c new file mode 100644 index 00000000..ccd4ba49 --- /dev/null +++ b/install/dracut-install.c @@ -0,0 +1,736 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/* dracut-install.c -- install files and executables + + Copyright (C) 2012 Harald Hoyer + Copyright (C) 2012 Red Hat, Inc. All rights reserved. + + This program is free software: you can redistribute it and/or modify + 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 program 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 program; If not, see <http://www.gnu.org/licenses/>. +*/ + +#define PROGRAM_VERSION_STRING "1" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <libgen.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "log.h" +#include "hashmap.h" +#include "util.h" + +static bool arg_hmac = false; +static bool arg_createdir = false; +static int arg_loglevel = -1; +static bool arg_optional = false; +static bool arg_all = false; +static bool arg_resolvelazy = false; +static bool arg_resolvedeps = false; +static char *destrootdir = NULL; + +static Hashmap *items = NULL; +static Hashmap *items_failed = NULL; + +static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps); + +static size_t dir_len(char const *file) +{ + size_t length; + /* Strip the basename and any redundant slashes before it. */ + for (length = strlen(file); 0 < length; length--) + if (file[length] == '/') + break; + return length; +} + +static char *convert_abs_rel(const char *from, const char *target) +{ + /* we use the 4*MAXPATHLEN, which should not overrun */ + char relative_from[MAXPATHLEN * 4]; + char *realtarget = NULL; + char *p, *q; + const char *realfrom = from; + int level = 0, fromlevel = 0, targetlevel = 0; + int l, i, rl; + int dirlen; + + p = strdup(target); + dirlen = dir_len(p); + p[dirlen] = '\0'; + q = realpath(p, NULL); + + if (q == NULL) { + free(p); + log_warning("convert_abs_rel(): target '%s' directory has no realpath.", target); + return strdup(from); + } + + asprintf(&realtarget, "%s/%s", q, &p[dirlen + 1]); + free(p); + free(q); + + /* now calculate the relative path from <from> to <target> and + store it in <relative_from> + */ + relative_from[0] = 0; + rl = 0; + + /* count the pathname elements of realtarget */ + for (targetlevel = 0, i = 0; realtarget[i]; i++) + if (realtarget[i] == '/') + targetlevel++; + + /* count the pathname elements of realfrom */ + for (fromlevel = 0, i = 0; realfrom[i]; i++) + if (realfrom[i] == '/') + fromlevel++; + + /* count the pathname elements, which are common for both paths */ + for (level = 0, i = 0; realtarget[i] && (realtarget[i] == realfrom[i]); i++) + if (realtarget[i] == '/') + level++; + + free(realtarget); + + /* add "../" to the relative_from path, until the common pathname is + reached */ + for (i = level; i < targetlevel; i++) { + if (i != level) + relative_from[rl++] = '/'; + relative_from[rl++] = '.'; + relative_from[rl++] = '.'; + } + + /* set l to the next uncommon pathname element in realfrom */ + for (l = 1, i = 1; i < level; i++) + for (l++; realfrom[l] && realfrom[l] != '/'; l++) ; + /* skip next '/' */ + l++; + + /* append the uncommon rest of realfrom to the relative_from path */ + for (i = level; i <= fromlevel; i++) { + if (rl) + relative_from[rl++] = '/'; + while (realfrom[l] && realfrom[l] != '/') + relative_from[rl++] = realfrom[l++]; + l++; + } + + relative_from[rl] = 0; + return strdup(relative_from); +} + +static int ln_r(const char *src, const char *dst) +{ + int ret; + const char *points_to = convert_abs_rel(src, dst); + log_info("ln -s '%s' '%s'", points_to, dst); + ret = symlink(points_to, dst); + + if (ret != 0) { + log_error("ERROR: ln -s '%s' '%s': %m", points_to, dst); + free((char *)points_to); + return 1; + } + + free((char *)points_to); + + return 0; +} + +static int cp(const char *src, const char *dst) +{ + int pid; + int status; + + pid = fork(); + if (pid == 0) { + execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode", "-fL", src, dst, NULL); + _exit(EXIT_FAILURE); + } + + while (waitpid(pid, &status, 0) < 0) { + if (errno != EINTR) { + status = -1; + break; + } + } + + return status; +} + +static int resolve_deps(const char *src) +{ + int ret = 0; + + char *buf = malloc(LINE_MAX); + size_t linesize = LINE_MAX; + FILE *fptr; + char *cmd; + + if (strstr(src, ".so") == 0) { + int fd; + fd = open(src, O_RDONLY | O_CLOEXEC); + read(fd, buf, LINE_MAX); + buf[LINE_MAX - 1] = '\0'; + close(fd); + if (buf[0] == '#' && buf[1] == '!') { + /* we have a shebang */ + char *p, *q; + for (p = &buf[2]; *p && isspace(*p); p++) ; + for (q = p; *q && (!isspace(*q)); q++) ; + *q = '\0'; + log_debug("Script install: '%s'", p); + ret = dracut_install(p, p, false, true); + if (ret != 0) + log_error("ERROR: failed to install '%s'", p); + return ret; + } + } + + /* run ldd */ + asprintf(&cmd, "ldd %s", src); + fptr = popen(cmd, "r"); + + while (!feof(fptr)) { + char *p, *q; + + if (getline(&buf, &linesize, fptr) <= 0) + continue; + + log_debug("ldd: '%s'", buf); + + if (strstr(buf, "not a dynamic executable")) + break; + + p = strstr(buf, "/"); + if (p) { + int r; + for (q = p; *q && *q != ' ' && *q != '\n'; q++) ; + *q = '\0'; + r = dracut_install(p, p, false, false); + if (r != 0) + log_error("ERROR: failed to install '%s' for '%s'", p, src); + else + log_debug("Lib install: '%s'", p); + ret += r; + + /* also install lib.so for lib.so.* files */ + q = strstr(p, ".so."); + if (q) { + q += 3; + *q = '\0'; + + /* ignore errors for base lib symlink */ + if (dracut_install(p, p, false, false) == 0) + log_debug("Lib install: '%s'", p); + } + } + } + pclose(fptr); + + return ret; +} + +/* Install ".<filename>.hmac" file for FIPS self-checks */ +static int hmac_install(const char *src, const char *dst) +{ + char *srcpath = strdup(src); + char *dstpath = strdup(dst); + char *srchmacname = NULL; + char *dsthmacname = NULL; + size_t dlen = dir_len(src); + + if (endswith(src, ".hmac")) + return 0; + + srcpath[dlen] = '\0'; + dstpath[dir_len(dst)] = '\0'; + asprintf(&srchmacname, "%s/.%s.hmac", srcpath, &src[dlen + 1]); + asprintf(&dsthmacname, "%s/.%s.hmac", dstpath, &src[dlen + 1]); + log_debug("hmac cp '%s' '%s')", srchmacname, dsthmacname); + dracut_install(srchmacname, dsthmacname, false, false); + free(dsthmacname); + free(srchmacname); + free(srcpath); + free(dstpath); + return 0; +} + +static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps) +{ + struct stat sb, db; + char *dname = NULL; + char *fulldstpath = NULL; + char *fulldstdir = NULL; + int ret; + bool src_exists = true; + char *i, *existing; + + log_debug("dracut_install('%s', '%s')", src, dst); + + existing = hashmap_get(items_failed, src); + if (existing) { + if (strcmp(existing, src) == 0) { + log_debug("hash hit items_failed for '%s'", src); + return 1; + } + } + + existing = hashmap_get(items, dst); + if (existing) { + if (strcmp(existing, dst) == 0) { + log_debug("hash hit items for '%s'", dst); + return 0; + } + } + + if (lstat(src, &sb) < 0) { + src_exists = false; + if (!isdir) { + i = strdup(src); + hashmap_put(items_failed, i, i); + /* src does not exist */ + return 1; + } + } + + i = strdup(dst); + hashmap_put(items, i, i); + + asprintf(&fulldstpath, "%s%s", destrootdir, dst); + + ret = stat(fulldstpath, &sb); + + if (ret != 0 && (errno != ENOENT)) { + log_error("ERROR: stat '%s': %m", fulldstpath); + return 1; + } + + if (ret == 0) { + log_debug("'%s' already exists", fulldstpath); + free(fulldstpath); + /* dst does already exist */ + return 0; + } + + /* check destination directory */ + fulldstdir = strdup(fulldstpath); + fulldstdir[dir_len(fulldstdir)] = '\0'; + + ret = stat(fulldstdir, &db); + + if (ret < 0) { + if (errno != ENOENT) { + log_error("ERROR: stat '%s': %m", fulldstdir); + return 1; + } + /* create destination directory */ + log_debug("dest dir '%s' does not exist", fulldstdir); + dname = strdup(dst); + dname[dir_len(dname)] = '\0'; + ret = dracut_install(dname, dname, true, false); + + free(dname); + + if (ret != 0) { + log_error("ERROR: failed to create directory '%s'", fulldstdir); + free(fulldstdir); + return 1; + } + } + + free(fulldstdir); + + if (isdir && !src_exists) { + log_info("mkdir '%s'", fulldstpath); + return mkdir(fulldstpath, 0755); + } + + /* ready to install src */ + + if (S_ISDIR(sb.st_mode)) { + log_info("mkdir '%s'", fulldstpath); + return mkdir(fulldstpath, sb.st_mode | S_IWUSR); + } + + if (S_ISLNK(sb.st_mode)) { + char *abspath; + char *absdestpath = NULL; + + abspath = realpath(src, NULL); + + if (abspath == NULL) + return 1; + + if (dracut_install(abspath, abspath, false, resolvedeps)) { + log_debug("'%s' install error", abspath); + return 1; + } + + if (lstat(abspath, &sb) != 0) { + log_debug("lstat '%s': %m", abspath); + return 1; + } + + if (lstat(fulldstpath, &sb) != 0) { + + asprintf(&absdestpath, "%s%s", destrootdir, abspath); + + ln_r(absdestpath, fulldstpath); + + free(absdestpath); + } + + free(abspath); + if (arg_hmac) { + /* copy .hmac files also */ + hmac_install(src, dst); + } + + return 0; + } + + if (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + if (resolvedeps) + ret += resolve_deps(src); + if (arg_hmac) { + /* copy .hmac files also */ + hmac_install(src, dst); + } + } + + log_info("cp '%s' '%s'", src, fulldstpath); + ret += cp(src, fulldstpath); + return ret; +} + +static void item_free(char *i) +{ + assert(i); + free(i); +} + +static void usage(int status) +{ + /* */ + printf("\ +Usage: %s -D DESTROOTDIR [OPTION]... -a SOURCE...\n\ + or: %s -D DESTROOTDIR [OPTION]... SOURCE DEST\n\ +\n\ +Install SOURCE to DEST in DESTROOTDIR with all needed dependencies.\n\ +\n\ + -D --destrootdir Install all files to DESTROOTDIR as the root\n\ + -a --all Install all SOURCE arguments to DESTROOTDIR\n\ + -o --optional If SOURCE does not exist, do not fail\n\ + -d --dir SOURCE is a directory\n\ + -l --ldd Also install shebang executables and libraries\n\ + -R --resolvelazy Only install shebang executables and libraries for all SOURCE files\n\ + -f --fips Also install all '.SOURCE.hmac' files\n\ + -v --verbose Show more output\n\ + --debug Show debug output\n\ + --version Show package version\n\ + -h --help Show this help\n\ +\n\ +Example:\n\ +# %s -D /var/tmp/test-root --ldd -a sh tr\n\ +# tree /var/tmp/test-root\n\ +/var/tmp/test-root\n\ +|-- lib64 -> usr/lib64\n\ +`-- usr\n\ + |-- bin\n\ + | |-- bash\n\ + | |-- sh -> bash\n\ + | `-- tr\n\ + `-- lib64\n\ + |-- ld-2.15.90.so\n\ + |-- ld-linux-x86-64.so.2 -> ld-2.15.90.so\n\ + |-- libc-2.15.90.so\n\ + |-- libc.so\n\ + |-- libc.so.6 -> libc-2.15.90.so\n\ + |-- libdl-2.15.90.so\n\ + |-- libdl.so -> libdl-2.15.90.so\n\ + |-- libdl.so.2 -> libdl-2.15.90.so\n\ + |-- libtinfo.so.5 -> libtinfo.so.5.9\n\ + `-- libtinfo.so.5.9\n\ +", program_invocation_short_name, program_invocation_short_name, program_invocation_short_name); + exit(status); +} + +static int parse_argv(int argc, char *argv[]) +{ + int c; + + enum { + ARG_VERSION = 0x100, + ARG_DEBUG + }; + + static const struct option const options[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, ARG_VERSION}, + {"dir", no_argument, NULL, 'd'}, + {"debug", no_argument, NULL, ARG_DEBUG}, + {"verbose", no_argument, NULL, 'v'}, + {"ldd", no_argument, NULL, 'l'}, + {"resolvelazy", no_argument, NULL, 'R'}, + {"optional", no_argument, NULL, 'o'}, + {"all", no_argument, NULL, 'a'}, + {"fips", no_argument, NULL, 'H'}, + {"destrootdir", required_argument, NULL, 'D'}, + {NULL, 0, NULL, 0} + }; + + while ((c = getopt_long(argc, argv, "adhloD:DHILR", options, NULL)) != -1) { + switch (c) { + case ARG_VERSION: + puts(PROGRAM_VERSION_STRING); + return 0; + case 'd': + arg_createdir = true; + break; + case ARG_DEBUG: + arg_loglevel = LOG_DEBUG; + break; + case 'v': + arg_loglevel = LOG_INFO; + break; + case 'o': + arg_optional = true; + break; + case 'l': + arg_resolvedeps = true; + break; + case 'R': + arg_resolvelazy = true; + break; + case 'a': + arg_all = true; + break; + case 'D': + destrootdir = strdup(optarg); + break; + case 'H': + arg_hmac = true; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + } + } + + if (!optind || optind == argc) { + usage(EXIT_FAILURE); + } + + return 1; +} + +static int resolve_lazy(int argc, char **argv) +{ + int i; + int destrootdirlen = strlen(destrootdir); + int ret = 0; + char *item; + for (i = 0; i < argc; i++) { + const char *src = argv[i]; + char *p = argv[i]; + char *existing; + + log_debug("resolve_deps('%s')", src); + + if (strstr(src, destrootdir)) { + p = &argv[i][destrootdirlen]; + } + + existing = hashmap_get(items, p); + if (existing) { + if (strcmp(existing, p) == 0) + continue; + } + + item = strdup(p); + hashmap_put(items, item, item); + + ret += resolve_deps(src); + } + return ret; +} + +static int install_all(int argc, char **argv) +{ + int r = 0; + int i; + for (i = 0; i < argc; i++) { + int ret; + log_debug("Handle '%s'", argv[i]); + + if (strchr(argv[i], '/') == NULL) { + char *path; + char *p, *q; + bool end = false; + path = getenv("PATH"); + if (path == NULL) { + log_error("PATH is not set"); + exit(EXIT_FAILURE); + } + path = strdup(path); + p = path; + log_debug("PATH=%s", path); + do { + char *newsrc = NULL; + char *dest; + struct stat sb; + + for (q = p; *q && *q != ':'; q++) ; + + if (*q == '\0') + end = true; + else + *q = '\0'; + + asprintf(&newsrc, "%s/%s", p, argv[i]); + p = q + 1; + + if (stat(newsrc, &sb) != 0) { + free(newsrc); + ret = -1; + continue; + } + + dest = strdup(newsrc); + + log_debug("dracut_install '%s'", newsrc); + ret = dracut_install(newsrc, dest, arg_createdir, arg_resolvedeps); + if (ret == 0) { + end = true; + log_debug("dracut_install '%s' OK", newsrc); + } + free(newsrc); + free(dest); + } while (!end); + free(path); + } else { + char *dest = strdup(argv[i]); + ret = dracut_install(argv[i], dest, arg_createdir, arg_resolvedeps); + free(dest); + } + + if ((ret != 0) && (!arg_optional)) { + log_error("ERROR: installing '%s'", argv[i]); + r = EXIT_FAILURE; + } + } + return r; +} + +int main(int argc, char **argv) +{ + int r; + char *i; + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + log_set_target(LOG_TARGET_CONSOLE); + log_parse_environment(); + + if (arg_loglevel >= 0) + log_set_max_level(arg_loglevel); + + log_open(); + + umask(0022); + + if (destrootdir == NULL) { + destrootdir = getenv("DESTROOTDIR"); + if (destrootdir == NULL) { + log_error("Environment DESTROOTDIR or argument -D is not set!"); + usage(EXIT_FAILURE); + } + destrootdir = strdup(destrootdir); + } + + items = hashmap_new(string_hash_func, string_compare_func); + items_failed = hashmap_new(string_hash_func, string_compare_func); + + if (!items || !items_failed) { + log_error("Out of memory"); + r = EXIT_FAILURE; + goto finish; + } + + r = EXIT_SUCCESS; + + if (((optind + 1) < argc) && (strcmp(argv[optind + 1], destrootdir) == 0)) { + /* ugly hack for compat mode "inst src $destrootdir" */ + if ((optind + 2) == argc) { + argc--; + } else { + /* ugly hack for compat mode "inst src $destrootdir dst" */ + if ((optind + 3) == argc) { + argc--; + argv[optind + 1] = argv[optind + 2]; + } + } + } + + if (arg_resolvelazy) { + r = resolve_lazy(argc - optind, &argv[optind]); + } else if (arg_all || (argc - optind > 2) || ((argc - optind) == 1)) { + r = install_all(argc - optind, &argv[optind]); + } else { + /* simple "inst src dst" */ + r = dracut_install(argv[optind], argv[optind + 1], arg_createdir, arg_resolvedeps); + if ((r != 0) && (!arg_optional)) { + log_error("ERROR: installing '%s' to '%s'", argv[optind], argv[optind + 1]); + r = EXIT_FAILURE; + } + } + + if (arg_optional) + r = EXIT_SUCCESS; + + finish: + + while ((i = hashmap_steal_first(items))) + item_free(i); + + while ((i = hashmap_steal_first(items_failed))) + item_free(i); + + hashmap_free(items); + hashmap_free(items_failed); + + free(destrootdir); + + return r; +} diff --git a/install/hashmap.c b/install/hashmap.c new file mode 100644 index 00000000..5d1e6320 --- /dev/null +++ b/install/hashmap.c @@ -0,0 +1,731 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "util.h" +#include "hashmap.h" +#include "macro.h" + +#define NBUCKETS 127 + +struct hashmap_entry { + const void *key; + void *value; + struct hashmap_entry *bucket_next, *bucket_previous; + struct hashmap_entry *iterate_next, *iterate_previous; +}; + +struct Hashmap { + hash_func_t hash_func; + compare_func_t compare_func; + + struct hashmap_entry *iterate_list_head, *iterate_list_tail; + unsigned n_entries; + + bool from_pool; +}; + +#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap)))) + +struct pool { + struct pool *next; + unsigned n_tiles; + unsigned n_used; +}; + +static struct pool *first_hashmap_pool = NULL; +static void *first_hashmap_tile = NULL; + +static struct pool *first_entry_pool = NULL; +static void *first_entry_tile = NULL; + +static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) { + unsigned i; + + if (*first_tile) { + void *r; + + r = *first_tile; + *first_tile = * (void**) (*first_tile); + return r; + } + + if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) { + unsigned n; + size_t size; + struct pool *p; + + n = *first_pool ? (*first_pool)->n_tiles : 0; + n = MAX(512U, n * 2); + size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size); + n = (size - ALIGN(sizeof(struct pool))) / tile_size; + + p = malloc(size); + if (!p) + return NULL; + + p->next = *first_pool; + p->n_tiles = n; + p->n_used = 0; + + *first_pool = p; + } + + i = (*first_pool)->n_used++; + + return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size; +} + +static void deallocate_tile(void **first_tile, void *p) { + * (void**) p = *first_tile; + *first_tile = p; +} + +#ifndef __OPTIMIZE__ + +static void drop_pool(struct pool *p) { + while (p) { + struct pool *n; + n = p->next; + free(p); + p = n; + } +} + +__attribute__((destructor)) static void cleanup_pool(void) { + /* Be nice to valgrind */ + + drop_pool(first_hashmap_pool); + drop_pool(first_entry_pool); +} + +#endif + +unsigned string_hash_func(const void *p) { + unsigned hash = 5381; + const signed char *c; + + /* DJB's hash function */ + + for (c = p; *c; c++) + hash = (hash << 5) + hash + (unsigned) *c; + + return hash; +} + +int string_compare_func(const void *a, const void *b) { + return strcmp(a, b); +} + +unsigned trivial_hash_func(const void *p) { + return PTR_TO_UINT(p); +} + +int trivial_compare_func(const void *a, const void *b) { + return a < b ? -1 : (a > b ? 1 : 0); +} + +Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { + bool b; + Hashmap *h; + size_t size; + + b = is_main_thread(); + + size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*); + + if (b) { + h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size); + if (!h) + return NULL; + + memset(h, 0, size); + } else { + h = malloc0(size); + + if (!h) + return NULL; + } + + h->hash_func = hash_func ? hash_func : trivial_hash_func; + h->compare_func = compare_func ? compare_func : trivial_compare_func; + + h->n_entries = 0; + h->iterate_list_head = h->iterate_list_tail = NULL; + + h->from_pool = b; + + return h; +} + +int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func) { + assert(h); + + if (*h) + return 0; + + if (!(*h = hashmap_new(hash_func, compare_func))) + return -ENOMEM; + + return 0; +} + +static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) { + assert(h); + assert(e); + + /* Insert into hash table */ + e->bucket_next = BY_HASH(h)[hash]; + e->bucket_previous = NULL; + if (BY_HASH(h)[hash]) + BY_HASH(h)[hash]->bucket_previous = e; + BY_HASH(h)[hash] = e; + + /* Insert into iteration list */ + e->iterate_previous = h->iterate_list_tail; + e->iterate_next = NULL; + if (h->iterate_list_tail) { + assert(h->iterate_list_head); + h->iterate_list_tail->iterate_next = e; + } else { + assert(!h->iterate_list_head); + h->iterate_list_head = e; + } + h->iterate_list_tail = e; + + h->n_entries++; + assert(h->n_entries >= 1); +} + +static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) { + assert(h); + assert(e); + + /* Remove from iteration list */ + if (e->iterate_next) + e->iterate_next->iterate_previous = e->iterate_previous; + else + h->iterate_list_tail = e->iterate_previous; + + if (e->iterate_previous) + e->iterate_previous->iterate_next = e->iterate_next; + else + h->iterate_list_head = e->iterate_next; + + /* Remove from hash table bucket list */ + if (e->bucket_next) + e->bucket_next->bucket_previous = e->bucket_previous; + + if (e->bucket_previous) + e->bucket_previous->bucket_next = e->bucket_next; + else + BY_HASH(h)[hash] = e->bucket_next; + + assert(h->n_entries >= 1); + h->n_entries--; +} + +static void remove_entry(Hashmap *h, struct hashmap_entry *e) { + unsigned hash; + + assert(h); + assert(e); + + hash = h->hash_func(e->key) % NBUCKETS; + + unlink_entry(h, e, hash); + + if (h->from_pool) + deallocate_tile(&first_entry_tile, e); + else + free(e); +} + +void hashmap_free(Hashmap*h) { + + if (!h) + return; + + hashmap_clear(h); + + if (h->from_pool) + deallocate_tile(&first_hashmap_tile, h); + else + free(h); +} + +void hashmap_free_free(Hashmap *h) { + void *p; + + while ((p = hashmap_steal_first(h))) + free(p); + + hashmap_free(h); +} + +void hashmap_clear(Hashmap *h) { + if (!h) + return; + + while (h->iterate_list_head) + remove_entry(h, h->iterate_list_head); +} + +static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) { + struct hashmap_entry *e; + assert(h); + assert(hash < NBUCKETS); + + for (e = BY_HASH(h)[hash]; e; e = e->bucket_next) + if (h->compare_func(e->key, key) == 0) + return e; + + return NULL; +} + +int hashmap_put(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + assert(h); + + hash = h->hash_func(key) % NBUCKETS; + + if ((e = hash_scan(h, hash, key))) { + + if (e->value == value) + return 0; + + return -EEXIST; + } + + if (h->from_pool) + e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry)); + else + e = new(struct hashmap_entry, 1); + + if (!e) + return -ENOMEM; + + e->key = key; + e->value = value; + + link_entry(h, e, hash); + + return 1; +} + +int hashmap_replace(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + assert(h); + + hash = h->hash_func(key) % NBUCKETS; + + if ((e = hash_scan(h, hash, key))) { + e->key = key; + e->value = value; + return 0; + } + + return hashmap_put(h, key, value); +} + +void* hashmap_get(Hashmap *h, const void *key) { + unsigned hash; + struct hashmap_entry *e; + + if (!h) + return NULL; + + hash = h->hash_func(key) % NBUCKETS; + + if (!(e = hash_scan(h, hash, key))) + return NULL; + + return e->value; +} + +void* hashmap_remove(Hashmap *h, const void *key) { + struct hashmap_entry *e; + unsigned hash; + void *data; + + if (!h) + return NULL; + + hash = h->hash_func(key) % NBUCKETS; + + if (!(e = hash_scan(h, hash, key))) + return NULL; + + data = e->value; + remove_entry(h, e); + + return data; +} + +int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { + struct hashmap_entry *e; + unsigned old_hash, new_hash; + + if (!h) + return -ENOENT; + + old_hash = h->hash_func(old_key) % NBUCKETS; + if (!(e = hash_scan(h, old_hash, old_key))) + return -ENOENT; + + new_hash = h->hash_func(new_key) % NBUCKETS; + if (hash_scan(h, new_hash, new_key)) + return -EEXIST; + + unlink_entry(h, e, old_hash); + + e->key = new_key; + e->value = value; + + link_entry(h, e, new_hash); + + return 0; +} + +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { + struct hashmap_entry *e, *k; + unsigned old_hash, new_hash; + + if (!h) + return -ENOENT; + + old_hash = h->hash_func(old_key) % NBUCKETS; + if (!(e = hash_scan(h, old_hash, old_key))) + return -ENOENT; + + new_hash = h->hash_func(new_key) % NBUCKETS; + + if ((k = hash_scan(h, new_hash, new_key))) + if (e != k) + remove_entry(h, k); + + unlink_entry(h, e, old_hash); + + e->key = new_key; + e->value = value; + + link_entry(h, e, new_hash); + + return 0; +} + +void* hashmap_remove_value(Hashmap *h, const void *key, void *value) { + struct hashmap_entry *e; + unsigned hash; + + if (!h) + return NULL; + + hash = h->hash_func(key) % NBUCKETS; + + if (!(e = hash_scan(h, hash, key))) + return NULL; + + if (e->value != value) + return NULL; + + remove_entry(h, e); + + return value; +} + +void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) { + struct hashmap_entry *e; + + assert(i); + + if (!h) + goto at_end; + + if (*i == ITERATOR_LAST) + goto at_end; + + if (*i == ITERATOR_FIRST && !h->iterate_list_head) + goto at_end; + + e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i; + + if (e->iterate_next) + *i = (Iterator) e->iterate_next; + else + *i = ITERATOR_LAST; + + if (key) + *key = e->key; + + return e->value; + +at_end: + *i = ITERATOR_LAST; + + if (key) + *key = NULL; + + return NULL; +} + +void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key) { + struct hashmap_entry *e; + + assert(i); + + if (!h) + goto at_beginning; + + if (*i == ITERATOR_FIRST) + goto at_beginning; + + if (*i == ITERATOR_LAST && !h->iterate_list_tail) + goto at_beginning; + + e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry*) *i; + + if (e->iterate_previous) + *i = (Iterator) e->iterate_previous; + else + *i = ITERATOR_FIRST; + + if (key) + *key = e->key; + + return e->value; + +at_beginning: + *i = ITERATOR_FIRST; + + if (key) + *key = NULL; + + return NULL; +} + +void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i) { + unsigned hash; + struct hashmap_entry *e; + + if (!h) + return NULL; + + hash = h->hash_func(key) % NBUCKETS; + + if (!(e = hash_scan(h, hash, key))) + return NULL; + + *i = (Iterator) e; + + return e->value; +} + +void* hashmap_first(Hashmap *h) { + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + return h->iterate_list_head->value; +} + +void* hashmap_first_key(Hashmap *h) { + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + return (void*) h->iterate_list_head->key; +} + +void* hashmap_last(Hashmap *h) { + + if (!h) + return NULL; + + if (!h->iterate_list_tail) + return NULL; + + return h->iterate_list_tail->value; +} + +void* hashmap_steal_first(Hashmap *h) { + void *data; + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + data = h->iterate_list_head->value; + remove_entry(h, h->iterate_list_head); + + return data; +} + +void* hashmap_steal_first_key(Hashmap *h) { + void *key; + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + key = (void*) h->iterate_list_head->key; + remove_entry(h, h->iterate_list_head); + + return key; +} + +unsigned hashmap_size(Hashmap *h) { + + if (!h) + return 0; + + return h->n_entries; +} + +bool hashmap_isempty(Hashmap *h) { + + if (!h) + return true; + + return h->n_entries == 0; +} + +int hashmap_merge(Hashmap *h, Hashmap *other) { + struct hashmap_entry *e; + + assert(h); + + if (!other) + return 0; + + for (e = other->iterate_list_head; e; e = e->iterate_next) { + int r; + + if ((r = hashmap_put(h, e->key, e->value)) < 0) + if (r != -EEXIST) + return r; + } + + return 0; +} + +void hashmap_move(Hashmap *h, Hashmap *other) { + struct hashmap_entry *e, *n; + + assert(h); + + /* The same as hashmap_merge(), but every new item from other + * is moved to h. This function is guaranteed to succeed. */ + + if (!other) + return; + + for (e = other->iterate_list_head; e; e = n) { + unsigned h_hash, other_hash; + + n = e->iterate_next; + + h_hash = h->hash_func(e->key) % NBUCKETS; + + if (hash_scan(h, h_hash, e->key)) + continue; + + other_hash = other->hash_func(e->key) % NBUCKETS; + + unlink_entry(other, e, other_hash); + link_entry(h, e, h_hash); + } +} + +int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { + unsigned h_hash, other_hash; + struct hashmap_entry *e; + + if (!other) + return 0; + + assert(h); + + h_hash = h->hash_func(key) % NBUCKETS; + if (hash_scan(h, h_hash, key)) + return -EEXIST; + + other_hash = other->hash_func(key) % NBUCKETS; + if (!(e = hash_scan(other, other_hash, key))) + return -ENOENT; + + unlink_entry(other, e, other_hash); + link_entry(h, e, h_hash); + + return 0; +} + +Hashmap *hashmap_copy(Hashmap *h) { + Hashmap *copy; + + assert(h); + + if (!(copy = hashmap_new(h->hash_func, h->compare_func))) + return NULL; + + if (hashmap_merge(copy, h) < 0) { + hashmap_free(copy); + return NULL; + } + + return copy; +} + +char **hashmap_get_strv(Hashmap *h) { + char **sv; + Iterator it; + char *item; + int n; + + sv = new(char*, h->n_entries+1); + if (!sv) + return NULL; + + n = 0; + HASHMAP_FOREACH(item, h, it) + sv[n++] = item; + sv[n] = NULL; + + return sv; +} diff --git a/install/hashmap.h b/install/hashmap.h new file mode 100644 index 00000000..fcf2cb1c --- /dev/null +++ b/install/hashmap.h @@ -0,0 +1,91 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foohashmaphfoo +#define foohashmaphfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> + +/* Pretty straightforward hash table implementation. As a minor + * optimization a NULL hashmap object will be treated as empty hashmap + * for all read operations. That way it is not necessary to + * instantiate an object for each Hashmap use. */ + +typedef struct Hashmap Hashmap; +typedef struct _IteratorStruct _IteratorStruct; +typedef _IteratorStruct* Iterator; + +#define ITERATOR_FIRST ((Iterator) 0) +#define ITERATOR_LAST ((Iterator) -1) + +typedef unsigned (*hash_func_t)(const void *p); +typedef int (*compare_func_t)(const void *a, const void *b); + +unsigned string_hash_func(const void *p); +int string_compare_func(const void *a, const void *b); + +unsigned trivial_hash_func(const void *p); +int trivial_compare_func(const void *a, const void *b); + +Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func); +void hashmap_free(Hashmap *h); +void hashmap_free_free(Hashmap *h); +Hashmap *hashmap_copy(Hashmap *h); +int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func); + +int hashmap_put(Hashmap *h, const void *key, void *value); +int hashmap_replace(Hashmap *h, const void *key, void *value); +void* hashmap_get(Hashmap *h, const void *key); +void* hashmap_remove(Hashmap *h, const void *key); +void* hashmap_remove_value(Hashmap *h, const void *key, void *value); +int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); +int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); + +int hashmap_merge(Hashmap *h, Hashmap *other); +void hashmap_move(Hashmap *h, Hashmap *other); +int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key); + +unsigned hashmap_size(Hashmap *h); +bool hashmap_isempty(Hashmap *h); + +void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key); +void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key); +void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i); + +void hashmap_clear(Hashmap *h); +void *hashmap_steal_first(Hashmap *h); +void *hashmap_steal_first_key(Hashmap *h); +void* hashmap_first(Hashmap *h); +void* hashmap_first_key(Hashmap *h); +void* hashmap_last(Hashmap *h); + +char **hashmap_get_strv(Hashmap *h); + +#define HASHMAP_FOREACH(e, h, i) \ + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL)) + +#define HASHMAP_FOREACH_KEY(e, k, h, i) \ + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k))) + +#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \ + for ((i) = ITERATOR_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL)) + +#endif diff --git a/install/hashmap.lo b/install/hashmap.lo new file mode 100644 index 00000000..c7260f22 --- /dev/null +++ b/install/hashmap.lo @@ -0,0 +1,12 @@ +# src/shared/hashmap.lo - a libtool object file +# Generated by libtool (GNU libtool) 2.4.2 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +pic_object='.libs/hashmap.o' + +# Name of the non-PIC object +non_pic_object='hashmap.o' + diff --git a/install/hashmap.o b/install/hashmap.o Binary files differnew file mode 100644 index 00000000..70262be3 --- /dev/null +++ b/install/hashmap.o diff --git a/install/log.c b/install/log.c new file mode 100644 index 00000000..127774f6 --- /dev/null +++ b/install/log.c @@ -0,0 +1,294 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <stddef.h> + +#include "log.h" +#include "util.h" +#include "macro.h" + +#define SNDBUF_SIZE (8*1024*1024) + +static LogTarget log_target = LOG_TARGET_CONSOLE; +static int log_max_level = LOG_WARNING; +static int log_facility = LOG_DAEMON; + +static int console_fd = STDERR_FILENO; + +static bool show_location = false; + +/* Akin to glibc's __abort_msg; which is private and we hence cannot + * use here. */ +static char *log_abort_msg = NULL; + +void log_close_console(void) { + + if (console_fd < 0) + return; + + if (getpid() == 1) { + if (console_fd >= 3) + close_nointr_nofail(console_fd); + + console_fd = -1; + } +} + +static int log_open_console(void) { + + if (console_fd >= 0) + return 0; + + if (getpid() == 1) { + + console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (console_fd < 0) { + log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd)); + return console_fd; + } + + log_debug("Successfully opened /dev/console for logging."); + } else + console_fd = STDERR_FILENO; + + return 0; +} + + +int log_open(void) { + return log_open_console(); +} + + +void log_close(void) { + log_close_console(); +} + + +void log_set_max_level(int level) { + assert((level & LOG_PRIMASK) == level); + + log_max_level = level; +} + +void log_set_facility(int facility) { + log_facility = facility; +} + +static int write_to_console( + int level, + const char*file, + int line, + const char *func, + const char *buffer) { + + char location[64]; + struct iovec iovec[5]; + unsigned n = 0; + + if (console_fd < 0) + return 0; + + zero(iovec); + + IOVEC_SET_STRING(iovec[n++], "dracut-install: "); + + if (show_location) { + snprintf(location, sizeof(location), "(%s:%u) ", file, line); + IOVEC_SET_STRING(iovec[n++], location); + } + + IOVEC_SET_STRING(iovec[n++], buffer); + IOVEC_SET_STRING(iovec[n++], "\n"); + + if (writev(console_fd, iovec, n) < 0) + return -errno; + + return 1; +} + +static int log_dispatch( + int level, + const char*file, + int line, + const char *func, + char *buffer) { + + int r = 0; + + if (log_target == LOG_TARGET_NULL) + return 0; + + /* Patch in LOG_DAEMON facility if necessary */ + if ((level & LOG_FACMASK) == 0) + level = log_facility | LOG_PRI(level); + + do { + char *e; + int k = 0; + + buffer += strspn(buffer, NEWLINE); + + if (buffer[0] == 0) + break; + + if ((e = strpbrk(buffer, NEWLINE))) + *(e++) = 0; + + k = write_to_console(level, file, line, func, buffer); + if (k < 0) + return k; + buffer = e; + } while (buffer); + + return r; +} + +int log_metav( + int level, + const char*file, + int line, + const char *func, + const char *format, + va_list ap) { + + char buffer[LINE_MAX]; + int saved_errno, r; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return 0; + + saved_errno = errno; + vsnprintf(buffer, sizeof(buffer), format, ap); + char_array_0(buffer); + + r = log_dispatch(level, file, line, func, buffer); + errno = saved_errno; + + return r; +} + +int log_meta( + int level, + const char*file, + int line, + const char *func, + const char *format, ...) { + + int r; + va_list ap; + + va_start(ap, format); + r = log_metav(level, file, line, func, format, ap); + va_end(ap); + + return r; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +_noreturn_ static void log_assert(const char *text, const char *file, int line, const char *func, const char *format) { + static char buffer[LINE_MAX]; + + snprintf(buffer, sizeof(buffer), format, text, file, line, func); + + char_array_0(buffer); + log_abort_msg = buffer; + + log_dispatch(LOG_CRIT, file, line, func, buffer); + abort(); +} +#pragma GCC diagnostic pop + +_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func) { + log_assert(text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); +} + +_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) { + log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); +} + +void log_set_target(LogTarget target) { + assert(target >= 0); + assert(target < _LOG_TARGET_MAX); + + log_target = target; +} + +int log_set_target_from_string(const char *e) { + LogTarget t; + + t = log_target_from_string(e); + if (t < 0) + return -EINVAL; + + log_set_target(t); + return 0; +} + +int log_set_max_level_from_string(const char *e) { + int t; + + t = log_level_from_string(e); + if (t < 0) + return t; + + log_set_max_level(t); + return 0; +} + +void log_parse_environment(void) { + const char *e; + + if ((e = getenv("DRACUT_LOG_TARGET"))) + if (log_set_target_from_string(e) < 0) + log_warning("Failed to parse log target %s. Ignoring.", e); + + if ((e = getenv("DRACUT_LOG_LEVEL"))) + if (log_set_max_level_from_string(e) < 0) + log_warning("Failed to parse log level %s. Ignoring.", e); + +} + +LogTarget log_get_target(void) { + return log_target; +} + +int log_get_max_level(void) { + return log_max_level; +} + + +static const char *const log_target_table[] = { + [LOG_TARGET_CONSOLE] = "console", + [LOG_TARGET_AUTO] = "auto", + [LOG_TARGET_SAFE] = "safe", + [LOG_TARGET_NULL] = "null" +}; + +DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget); diff --git a/install/log.h b/install/log.h new file mode 100644 index 00000000..ad1ca331 --- /dev/null +++ b/install/log.h @@ -0,0 +1,115 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologhfoo +#define foologhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <syslog.h> +#include <stdbool.h> +#include <stdarg.h> + +#include "macro.h" + +typedef enum LogTarget{ + LOG_TARGET_CONSOLE, + LOG_TARGET_KMSG, + LOG_TARGET_JOURNAL, + LOG_TARGET_JOURNAL_OR_KMSG, + LOG_TARGET_SYSLOG, + LOG_TARGET_SYSLOG_OR_KMSG, + LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ + LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ + LOG_TARGET_NULL, + _LOG_TARGET_MAX, + _LOG_TARGET_INVALID = -1 +} LogTarget; + +void log_set_target(LogTarget target); +void log_set_max_level(int level); +void log_set_facility(int facility); + +int log_set_target_from_string(const char *e); +int log_set_max_level_from_string(const char *e); + +void log_show_color(bool b); +void log_show_location(bool b); + +int log_show_color_from_string(const char *e); +int log_show_location_from_string(const char *e); + +LogTarget log_get_target(void); +int log_get_max_level(void); + +int log_open(void); +void log_close(void); +void log_forget_fds(void); + +void log_close_syslog(void); +void log_close_journal(void); +void log_close_kmsg(void); +void log_close_console(void); + +void log_parse_environment(void); + +int log_meta( + int level, + const char*file, + int line, + const char *func, + const char *format, ...) _printf_attr_(5,6); + +int log_metav( + int level, + const char*file, + int line, + const char *func, + const char *format, + va_list ap); + +_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func); +_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func); + +/* This modifies the buffer passed! */ +int log_dump_internal( + int level, + const char*file, + int line, + const char *func, + char *buffer); + +#define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__) + +#define log_debug(...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_info(...) log_meta(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_notice(...) log_meta(LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_warning(...) log_meta(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_error(...) log_meta(LOG_ERR, __FILE__, __LINE__, __func__, __VA_ARGS__) + +/* This modifies the buffer passed! */ +#define log_dump(level, buffer) log_dump_internal(level, __FILE__, __LINE__, __func__, buffer) + +const char *log_target_to_string(LogTarget target); +LogTarget log_target_from_string(const char *s); + +const char *log_level_to_string(int i); +int log_level_from_string(const char *s); + +#endif diff --git a/install/macro.h b/install/macro.h new file mode 100644 index 00000000..1c0aa915 --- /dev/null +++ b/install/macro.h @@ -0,0 +1,192 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foomacrohfoo +#define foomacrohfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <assert.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <inttypes.h> + +#define _printf_attr_(a,b) __attribute__ ((format (printf, a, b))) +#define _sentinel_ __attribute__ ((sentinel)) +#define _noreturn_ __attribute__((noreturn)) +#define _unused_ __attribute__ ((unused)) +#define _destructor_ __attribute__ ((destructor)) +#define _pure_ __attribute__ ((pure)) +#define _const_ __attribute__ ((const)) +#define _deprecated_ __attribute__ ((deprecated)) +#define _packed_ __attribute__ ((packed)) +#define _malloc_ __attribute__ ((malloc)) +#define _weak_ __attribute__ ((weak)) +#define _likely_(x) (__builtin_expect(!!(x),1)) +#define _unlikely_(x) (__builtin_expect(!!(x),0)) +#define _public_ __attribute__ ((visibility("default"))) +#define _hidden_ __attribute__ ((visibility("hidden"))) +#define _weakref_(x) __attribute__((weakref(#x))) +#define _introspect_(x) __attribute__((section("introspect." x))) + +#define XSTRINGIFY(x) #x +#define STRINGIFY(x) XSTRINGIFY(x) + +/* Rounds up */ +#define ALIGN(l) ALIGN_TO((l), sizeof(void*)) +static inline size_t ALIGN_TO(size_t l, size_t ali) { + return ((l + ali - 1) & ~(ali - 1)); +} + +#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +/* + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#ifndef MAX +#define MAX(a,b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif + +#define MAX3(a,b,c) \ + MAX(MAX(a,b),c) + +#ifndef MIN +#define MIN(a,b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif + +#define MIN3(a,b,c) \ + MIN(MIN(a,b),c) + +#define CLAMP(x, low, high) \ + __extension__ ({ \ + typeof(x) _x = (x); \ + typeof(low) _low = (low); \ + typeof(high) _high = (high); \ + ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ + }) + +#define assert_se(expr) \ + do { \ + if (_unlikely_(!(expr))) \ + log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (false) \ + +/* We override the glibc assert() here. */ +#undef assert +#ifdef NDEBUG +#define assert(expr) do {} while(false) +#else +#define assert(expr) assert_se(expr) +#endif + +#define assert_not_reached(t) \ + do { \ + log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } while (false) + +#define assert_cc(expr) \ + do { \ + switch (0) { \ + case 0: \ + case !!(expr): \ + ; \ + } \ + } while (false) + +#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) +#define UINT_TO_PTR(u) ((void*) ((uintptr_t) (u))) + +#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) +#define UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u))) + +#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) +#define ULONG_TO_PTR(u) ((void*) ((uintptr_t) (u))) + +#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) +#define INT_TO_PTR(u) ((void*) ((intptr_t) (u))) + +#define TO_INT32(p) ((int32_t) ((intptr_t) (p))) +#define INT32_TO_PTR(u) ((void*) ((intptr_t) (u))) + +#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) +#define LONG_TO_PTR(u) ((void*) ((intptr_t) (u))) + +#define memzero(x,l) (memset((x), 0, (l))) +#define zero(x) (memzero(&(x), sizeof(x))) + +#define char_array_0(x) x[sizeof(x)-1] = 0; + +#define IOVEC_SET_STRING(i, s) \ + do { \ + struct iovec *_i = &(i); \ + char *_s = (char *)(s); \ + _i->iov_base = _s; \ + _i->iov_len = strlen(_s); \ + } while(false) + +static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { + unsigned j; + size_t r = 0; + + for (j = 0; j < n; j++) + r += i[j].iov_len; + + return r; +} + +static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { + unsigned j; + + for (j = 0; j < n; j++) { + size_t sub; + + if (_unlikely_(k <= 0)) + break; + + sub = MIN(i[j].iov_len, k); + i[j].iov_len -= sub; + i[j].iov_base = (uint8_t*) i[j].iov_base + sub; + k -= sub; + } + + return k; +} + +#include "log.h" + +#endif diff --git a/install/util.c b/install/util.c new file mode 100644 index 00000000..0247184b --- /dev/null +++ b/install/util.c @@ -0,0 +1,187 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/syscall.h> + +#include "util.h" + +static inline pid_t gettid(void) { + return (pid_t) syscall(SYS_gettid); +} + +size_t page_size(void) { + static __thread size_t pgsz = 0; + long r; + + if (_likely_(pgsz > 0)) + return pgsz; + + assert_se((r = sysconf(_SC_PAGESIZE)) > 0); + + pgsz = (size_t) r; + + return pgsz; +} + +bool endswith(const char *s, const char *postfix) { + size_t sl, pl; + + assert(s); + assert(postfix); + + sl = strlen(s); + pl = strlen(postfix); + + if (pl == 0) + return true; + + if (sl < pl) + return false; + + return memcmp(s + sl - pl, postfix, pl) == 0; +} +int close_nointr(int fd) { + assert(fd >= 0); + + for (;;) { + int r; + + r = close(fd); + if (r >= 0) + return r; + + if (errno != EINTR) + return -errno; + } +} + +void close_nointr_nofail(int fd) { + int saved_errno = errno; + + /* like close_nointr() but cannot fail, and guarantees errno + * is unchanged */ + + assert_se(close_nointr(fd) == 0); + + errno = saved_errno; +} + +int open_terminal(const char *name, int mode) { + int fd, r; + unsigned c = 0; + + /* + * If a TTY is in the process of being closed opening it might + * cause EIO. This is horribly awful, but unlikely to be + * changed in the kernel. Hence we work around this problem by + * retrying a couple of times. + * + * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 + */ + + for (;;) { + if ((fd = open(name, mode)) >= 0) + break; + + if (errno != EIO) + return -errno; + + if (c >= 20) + return -errno; + + usleep(50 * USEC_PER_MSEC); + c++; + } + + if (fd < 0) + return -errno; + + if ((r = isatty(fd)) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + if (!r) { + close_nointr_nofail(fd); + return -ENOTTY; + } + + return fd; +} + +bool streq_ptr(const char *a, const char *b) { + + /* Like streq(), but tries to make sense of NULL pointers */ + + if (a && b) + return streq(a, b); + + if (!a && !b) + return true; + + return false; +} +bool is_main_thread(void) { + static __thread int cached = 0; + + if (_unlikely_(cached == 0)) + cached = getpid() == gettid() ? 1 : -1; + + return cached > 0; +} + +int safe_atou(const char *s, unsigned *ret_u) { + char *x = NULL; + unsigned long l; + + assert(s); + assert(ret_u); + + errno = 0; + l = strtoul(s, &x, 0); + + if (!x || *x || errno) + return errno ? -errno : -EINVAL; + + if ((unsigned long) (unsigned) l != l) + return -ERANGE; + + *ret_u = (unsigned) l; + return 0; +} + +static const char *const log_level_table[] = { + [LOG_EMERG] = "emerg", + [LOG_ALERT] = "alert", + [LOG_CRIT] = "crit", + [LOG_ERR] = "err", + [LOG_WARNING] = "warning", + [LOG_NOTICE] = "notice", + [LOG_INFO] = "info", + [LOG_DEBUG] = "debug" +}; + +DEFINE_STRING_TABLE_LOOKUP(log_level, int); diff --git a/install/util.h b/install/util.h new file mode 100644 index 00000000..90859356 --- /dev/null +++ b/install/util.h @@ -0,0 +1,527 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooutilhfoo +#define fooutilhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <time.h> +#include <sys/time.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <sched.h> +#include <limits.h> +#include <sys/stat.h> +#include <dirent.h> +#include <sys/resource.h> + +#include "macro.h" + +typedef uint64_t usec_t; +typedef uint64_t nsec_t; + +typedef struct dual_timestamp { + usec_t realtime; + usec_t monotonic; +} dual_timestamp; + +#define MSEC_PER_SEC 1000ULL +#define USEC_PER_SEC 1000000ULL +#define USEC_PER_MSEC 1000ULL +#define NSEC_PER_SEC 1000000000ULL +#define NSEC_PER_MSEC 1000000ULL +#define NSEC_PER_USEC 1000ULL + +#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC) +#define NSEC_PER_MINUTE (60ULL*NSEC_PER_SEC) +#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE) +#define NSEC_PER_HOUR (60ULL*NSEC_PER_MINUTE) +#define USEC_PER_DAY (24ULL*USEC_PER_HOUR) +#define NSEC_PER_DAY (24ULL*NSEC_PER_HOUR) +#define USEC_PER_WEEK (7ULL*USEC_PER_DAY) +#define NSEC_PER_WEEK (7ULL*NSEC_PER_DAY) +#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC) +#define NSEC_PER_MONTH (2629800ULL*NSEC_PER_SEC) +#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC) +#define NSEC_PER_YEAR (31557600ULL*NSEC_PER_SEC) + +/* What is interpreted as whitespace? */ +#define WHITESPACE " \t\n\r" +#define NEWLINE "\n\r" +#define QUOTES "\"\'" +#define COMMENTS "#;\n" + +#define FORMAT_TIMESTAMP_MAX 64 +#define FORMAT_TIMESTAMP_PRETTY_MAX 256 +#define FORMAT_TIMESPAN_MAX 64 +#define FORMAT_BYTES_MAX 8 + +#define ANSI_HIGHLIGHT_ON "\x1B[1;39m" +#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m" +#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m" +#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m" +#define ANSI_HIGHLIGHT_OFF "\x1B[0m" + +usec_t now(clockid_t clock); + +dual_timestamp* dual_timestamp_get(dual_timestamp *ts); +dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); + +#define dual_timestamp_is_set(ts) ((ts)->realtime > 0) + +usec_t timespec_load(const struct timespec *ts); +struct timespec *timespec_store(struct timespec *ts, usec_t u); + +usec_t timeval_load(const struct timeval *tv); +struct timeval *timeval_store(struct timeval *tv, usec_t u); + +size_t page_size(void); +#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) + +#define streq(a,b) (strcmp((a),(b)) == 0) +#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) + +bool streq_ptr(const char *a, const char *b); + +#define new(t, n) ((t*) malloc(sizeof(t)*(n))) + +#define new0(t, n) ((t*) calloc((n), sizeof(t))) + +#define newa(t, n) ((t*) alloca(sizeof(t)*(n))) + +#define newdup(t, p, n) ((t*) memdup(p, sizeof(t)*(n))) + +#define malloc0(n) (calloc((n), 1)) + +static inline const char* yes_no(bool b) { + return b ? "yes" : "no"; +} + +static inline const char* strempty(const char *s) { + return s ? s : ""; +} + +static inline const char* strnull(const char *s) { + return s ? s : "(null)"; +} + +static inline const char *strna(const char *s) { + return s ? s : "n/a"; +} + +static inline bool isempty(const char *p) { + return !p || !p[0]; +} + +bool endswith(const char *s, const char *postfix); +bool startswith(const char *s, const char *prefix); +bool startswith_no_case(const char *s, const char *prefix); + +bool first_word(const char *s, const char *word); + +int close_nointr(int fd); +void close_nointr_nofail(int fd); +void close_many(const int fds[], unsigned n_fd); + +int parse_boolean(const char *v); +int parse_usec(const char *t, usec_t *usec); +int parse_nsec(const char *t, nsec_t *nsec); +int parse_bytes(const char *t, off_t *bytes); +int parse_pid(const char *s, pid_t* ret_pid); +int parse_uid(const char *s, uid_t* ret_uid); +#define parse_gid(s, ret_uid) parse_uid(s, ret_uid) + +int safe_atou(const char *s, unsigned *ret_u); +int safe_atoi(const char *s, int *ret_i); + +int safe_atollu(const char *s, unsigned long long *ret_u); +int safe_atolli(const char *s, long long int *ret_i); + +#if __WORDSIZE == 32 +static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned)); + return safe_atou(s, (unsigned*) ret_u); +} +static inline int safe_atoli(const char *s, long int *ret_u) { + assert_cc(sizeof(long int) == sizeof(int)); + return safe_atoi(s, (int*) ret_u); +} +#else +static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +} +static inline int safe_atoli(const char *s, long int *ret_u) { + assert_cc(sizeof(long int) == sizeof(long long int)); + return safe_atolli(s, (long long int*) ret_u); +} +#endif + +static inline int safe_atou32(const char *s, uint32_t *ret_u) { + assert_cc(sizeof(uint32_t) == sizeof(unsigned)); + return safe_atou(s, (unsigned*) ret_u); +} + +static inline int safe_atoi32(const char *s, int32_t *ret_i) { + assert_cc(sizeof(int32_t) == sizeof(int)); + return safe_atoi(s, (int*) ret_i); +} + +static inline int safe_atou64(const char *s, uint64_t *ret_u) { + assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +} + +static inline int safe_atoi64(const char *s, int64_t *ret_i) { + assert_cc(sizeof(int64_t) == sizeof(long long int)); + return safe_atolli(s, (long long int*) ret_i); +} + +char *split(const char *c, size_t *l, const char *separator, char **state); +char *split_quoted(const char *c, size_t *l, char **state); + +#define FOREACH_WORD(word, length, s, state) \ + for ((state) = NULL, (word) = split((s), &(length), WHITESPACE, &(state)); (word); (word) = split((s), &(length), WHITESPACE, &(state))) + +#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ + for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); (word); (word) = split((s), &(length), (separator), &(state))) + +#define FOREACH_WORD_QUOTED(word, length, s, state) \ + for ((state) = NULL, (word) = split_quoted((s), &(length), &(state)); (word); (word) = split_quoted((s), &(length), &(state))) + +pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); +int get_starttime_of_pid(pid_t pid, unsigned long long *st); + +int write_one_line_file(const char *fn, const char *line); +int write_one_line_file_atomic(const char *fn, const char *line); +int read_one_line_file(const char *fn, char **line); +int read_full_file(const char *fn, char **contents, size_t *size); + +int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; +int load_env_file(const char *fname, char ***l); +int write_env_file(const char *fname, char **l); + +char *strappend(const char *s, const char *suffix); +char *strnappend(const char *s, const char *suffix, size_t length); + +char *replace_env(const char *format, char **env); +char **replace_env_argv(char **argv, char **env); + +int readlink_malloc(const char *p, char **r); +int readlink_and_make_absolute(const char *p, char **r); +int readlink_and_canonicalize(const char *p, char **r); + +int reset_all_signal_handlers(void); + +char *strstrip(char *s); +char *delete_chars(char *s, const char *bad); +char *truncate_nl(char *s); + +char *file_in_same_dir(const char *path, const char *filename); + +int rmdir_parents(const char *path, const char *stop); + +int get_process_comm(pid_t pid, char **name); +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); +int get_process_exe(pid_t pid, char **name); +int get_process_uid(pid_t pid, uid_t *uid); + +char hexchar(int x); +int unhexchar(char c); +char octchar(int x); +int unoctchar(char c); +char decchar(int x); +int undecchar(char c); + +char *cescape(const char *s); +char *cunescape(const char *s); +char *cunescape_length(const char *s, size_t length); + +char *xescape(const char *s, const char *bad); + +char *bus_path_escape(const char *s); +char *bus_path_unescape(const char *s); + +char *ascii_strlower(char *path); + +bool dirent_is_file(const struct dirent *de); +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix); + +bool ignore_file(const char *filename); + +bool chars_intersect(const char *a, const char *b); + +char *format_timestamp(char *buf, size_t l, usec_t t); +char *format_timestamp_pretty(char *buf, size_t l, usec_t t); +char *format_timespan(char *buf, size_t l, usec_t t); + +int make_stdio(int fd); +int make_null_stdio(void); + +unsigned long long random_ull(void); + +#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ + scope const char *name##_to_string(type i) { \ + if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ + return NULL; \ + return name##_table[i]; \ + } \ + scope type name##_from_string(const char *s) { \ + type i; \ + unsigned u = 0; \ + assert(s); \ + for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \ + if (name##_table[i] && \ + streq(name##_table[i], s)) \ + return i; \ + if (safe_atou(s, &u) >= 0 && \ + u < ELEMENTSOF(name##_table)) \ + return (type) u; \ + return (type) -1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static) + +int fd_nonblock(int fd, bool nonblock); +int fd_cloexec(int fd, bool cloexec); + +int close_all_fds(const int except[], unsigned n_except); + +bool fstype_is_network(const char *fstype); + +int chvt(int vt); + +int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); +int ask(char *ret, const char *replies, const char *text, ...); + +int reset_terminal_fd(int fd, bool switch_to_text); +int reset_terminal(const char *name); + +int open_terminal(const char *name, int mode); +int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm); +int release_terminal(void); + +int flush_fd(int fd); + +int ignore_signals(int sig, ...); +int default_signals(int sig, ...); +int sigaction_many(const struct sigaction *sa, ...); + +int close_pipe(int p[]); +int fopen_temporary(const char *path, FILE **_f, char **_temp_path); + +ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); +ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); + +bool is_device_path(const char *path); + +int dir_is_empty(const char *path); + +void rename_process(const char name[8]); + +void sigset_add_many(sigset_t *ss, ...); + +char* gethostname_malloc(void); +bool hostname_is_set(void); +char* getlogname_malloc(void); + +int getttyname_malloc(int fd, char **r); +int getttyname_harder(int fd, char **r); + +int get_ctty_devnr(pid_t pid, dev_t *d); +int get_ctty(pid_t, dev_t *_devnr, char **r); + +int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); + +int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev); +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); + +int pipe_eof(int fd); + +cpu_set_t* cpu_set_malloc(unsigned *ncpus); + +void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap); +void status_printf(const char *status, bool ellipse, const char *format, ...); +void status_welcome(void); + +int fd_columns(int fd); +unsigned columns(void); + +int fd_lines(int fd); +unsigned lines(void); + +int running_in_chroot(void); + +char *ellipsize(const char *s, size_t length, unsigned percent); +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent); + +int touch(const char *path); + +char *unquote(const char *s, const char *quotes); +char *normalize_env_assignment(const char *s); + +int wait_for_terminate(pid_t pid, siginfo_t *status); +int wait_for_terminate_and_warn(const char *name, pid_t pid); + +_noreturn_ void freeze(void); + +bool null_or_empty(struct stat *st); +int null_or_empty_path(const char *fn); + +DIR *xopendirat(int dirfd, const char *name, int flags); + +void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); +void dual_timestamp_deserialize(const char *value, dual_timestamp *t); + +char *fstab_node_to_udev_node(const char *p); + +bool tty_is_vc(const char *tty); +bool tty_is_vc_resolve(const char *tty); +bool tty_is_console(const char *tty); +int vtnr_from_tty(const char *tty); +const char *default_term_for_tty(const char *tty); + +void execute_directory(const char *directory, DIR *_d, char *argv[]); + +int kill_and_sigcont(pid_t pid, int sig); + +bool nulstr_contains(const char*nulstr, const char *needle); + +bool plymouth_running(void); + +void parse_syslog_priority(char **p, int *priority); +void skip_syslog_pid(char **buf); +void skip_syslog_date(char **buf); + +bool hostname_is_valid(const char *s); +char* hostname_cleanup(char *s); + +char* strshorten(char *s, size_t l); + +int terminal_vhangup_fd(int fd); +int terminal_vhangup(const char *name); + +int vt_disallocate(const char *name); + +int copy_file(const char *from, const char *to); +int symlink_or_copy(const char *from, const char *to); +int symlink_or_copy_atomic(const char *from, const char *to); + +int fchmod_umask(int fd, mode_t mode); + +bool display_is_local(const char *display); +int socket_from_display(const char *display, char **path); + +int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home); +int get_group_creds(const char **groupname, gid_t *gid); + +int in_group(const char *name); + +int glob_exists(const char *path); + +int dirent_ensure_type(DIR *d, struct dirent *de); + +int in_search_path(const char *path, char **search); +int get_files_in_directory(const char *path, char ***list); + +char *join(const char *x, ...) _sentinel_; + +bool is_main_thread(void); + +bool in_charset(const char *s, const char* charset); + +int block_get_whole_disk(dev_t d, dev_t *ret); + +int file_is_priv_sticky(const char *p); + +int strdup_or_null(const char *a, char **b); + +#define NULSTR_FOREACH(i, l) \ + for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) + +#define NULSTR_FOREACH_PAIR(i, j, l) \ + for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) + +const char *ioprio_class_to_string(int i); +int ioprio_class_from_string(const char *s); + +const char *sigchld_code_to_string(int i); +int sigchld_code_from_string(const char *s); + +const char *log_facility_unshifted_to_string(int i); +int log_facility_unshifted_from_string(const char *s); + +const char *log_level_to_string(int i); +int log_level_from_string(const char *s); + +const char *sched_policy_to_string(int i); +int sched_policy_from_string(const char *s); + +const char *rlimit_to_string(int i); +int rlimit_from_string(const char *s); + +const char *ip_tos_to_string(int i); +int ip_tos_from_string(const char *s); + +const char *signal_to_string(int i); +int signal_from_string(const char *s); + +int signal_from_string_try_harder(const char *s); + +extern int saved_argc; +extern char **saved_argv; + +bool kexec_loaded(void); + +int prot_from_flags(int flags); + +char *format_bytes(char *buf, size_t l, off_t t); + +int fd_wait_for_event(int fd, int event, usec_t timeout); + +void* memdup(const void *p, size_t l); + +int is_kernel_thread(pid_t pid); + +int fd_inc_sndbuf(int fd, size_t n); +int fd_inc_rcvbuf(int fd, size_t n); + +int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); + +int setrlimit_closest(int resource, const struct rlimit *rlim); + +int getenv_for_pid(pid_t pid, const char *field, char **_value); + +int can_sleep(const char *type); + +bool is_valid_documentation_url(const char *url); + +bool in_initrd(void); + +void warn_melody(void); + +#endif |