
13 changed files with 2914 additions and 7 deletions
@ -0,0 +1,17 @@
@@ -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 |
@ -0,0 +1,736 @@
@@ -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; |
||||
} |
@ -0,0 +1,731 @@
@@ -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; |
||||
} |
@ -0,0 +1,91 @@
@@ -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 |
@ -0,0 +1,12 @@
@@ -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' |
||||
|
Binary file not shown.
@ -0,0 +1,294 @@
@@ -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); |
@ -0,0 +1,115 @@
@@ -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 |
@ -0,0 +1,192 @@
@@ -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 |
@ -0,0 +1,187 @@
@@ -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); |
@ -0,0 +1,527 @@
@@ -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 |
Loading…
Reference in new issue