util: move all to shared/ and split external dependencies in separate internal libraries
Before: $ ldd /lib/systemd/systemd-timestamp linux-vdso.so.1 => (0x00007fffb05ff000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f90aac57000) libcap.so.2 => /lib64/libcap.so.2 (0x00007f90aaa53000) librt.so.1 => /lib64/librt.so.1 (0x00007f90aa84a000) libc.so.6 => /lib64/libc.so.6 (0x00007f90aa494000) /lib64/ld-linux-x86-64.so.2 (0x00007f90aae90000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f90aa290000) libattr.so.1 => /lib64/libattr.so.1 (0x00007f90aa08a000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f90a9e6e000) After: $ ldd systemd-timestamp linux-vdso.so.1 => (0x00007fff3cbff000) libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f5eaa1c3000) librt.so.1 => /lib64/librt.so.1 (0x00007f5ea9fbb000) libc.so.6 => /lib64/libc.so.6 (0x00007f5ea9c04000) /lib64/ld-linux-x86-64.so.2 (0x00007f5eaa3fc000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f5ea9a00000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f5ea97e4000)
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/capability.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "audit.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
|
||||
int audit_session_from_pid(pid_t pid, uint32_t *id) {
|
||||
char *s;
|
||||
uint32_t u;
|
||||
int r;
|
||||
|
||||
assert(id);
|
||||
|
||||
if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
|
||||
return -ENOENT;
|
||||
|
||||
if (pid == 0)
|
||||
r = read_one_line_file("/proc/self/sessionid", &s);
|
||||
else {
|
||||
char *p;
|
||||
|
||||
if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = read_one_line_file(p, &s);
|
||||
free(p);
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = safe_atou32(s, &u);
|
||||
free(s);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u == (uint32_t) -1 || u <= 0)
|
||||
return -ENOENT;
|
||||
|
||||
*id = u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
|
||||
char *s;
|
||||
uid_t u;
|
||||
int r;
|
||||
|
||||
assert(uid);
|
||||
|
||||
/* Only use audit login uid if we are executed with sufficient
|
||||
* capabilities so that pam_loginuid could do its job. If we
|
||||
* are lacking the CAP_AUDIT_CONTROL capabality we most likely
|
||||
* are being run in a container and /proc/self/loginuid is
|
||||
* useless since it probably contains a uid of the host
|
||||
* system. */
|
||||
|
||||
if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
|
||||
return -ENOENT;
|
||||
|
||||
if (pid == 0)
|
||||
r = read_one_line_file("/proc/self/loginuid", &s);
|
||||
else {
|
||||
char *p;
|
||||
|
||||
if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
r = read_one_line_file(p, &s);
|
||||
free(p);
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = parse_uid(s, &u);
|
||||
free(s);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (u == (uid_t) -1)
|
||||
return -ENOENT;
|
||||
|
||||
*uid = (uid_t) u;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef fooaudithfoo
|
||||
#define fooaudithfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "capability.h"
|
||||
|
||||
int audit_session_from_pid(pid_t pid, uint32_t *id);
|
||||
int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "capability.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
|
||||
int have_effective_cap(int value) {
|
||||
cap_t cap;
|
||||
cap_flag_value_t fv;
|
||||
int r;
|
||||
|
||||
if (!(cap = cap_get_proc()))
|
||||
return -errno;
|
||||
|
||||
if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
|
||||
r = -errno;
|
||||
else
|
||||
r = fv == CAP_SET;
|
||||
|
||||
cap_free(cap);
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned long cap_last_cap(void) {
|
||||
static __thread unsigned long saved;
|
||||
static __thread bool valid = false;
|
||||
unsigned long p;
|
||||
|
||||
if (valid)
|
||||
return saved;
|
||||
|
||||
p = (unsigned long) CAP_LAST_CAP;
|
||||
|
||||
if (prctl(PR_CAPBSET_READ, p) < 0) {
|
||||
|
||||
/* Hmm, look downwards, until we find one that
|
||||
* works */
|
||||
for (p--; p > 0; p --)
|
||||
if (prctl(PR_CAPBSET_READ, p) >= 0)
|
||||
break;
|
||||
|
||||
} else {
|
||||
|
||||
/* Hmm, look upwards, until we find one that doesn't
|
||||
* work */
|
||||
for (;; p++)
|
||||
if (prctl(PR_CAPBSET_READ, p+1) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
saved = p;
|
||||
valid = true;
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foocapabilityhfoo
|
||||
#define foocapabilityhfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
unsigned long cap_last_cap(void);
|
||||
int have_effective_cap(int value);
|
||||
#endif
|
||||
@@ -0,0 +1,852 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "util.h"
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
#include "log.h"
|
||||
#include "utf8.h"
|
||||
|
||||
int config_item_table_lookup(
|
||||
void *table,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
ConfigParserCallback *func,
|
||||
int *ltype,
|
||||
void **data,
|
||||
void *userdata) {
|
||||
|
||||
ConfigTableItem *t;
|
||||
|
||||
assert(table);
|
||||
assert(lvalue);
|
||||
assert(func);
|
||||
assert(ltype);
|
||||
assert(data);
|
||||
|
||||
for (t = table; t->lvalue; t++) {
|
||||
|
||||
if (!streq(lvalue, t->lvalue))
|
||||
continue;
|
||||
|
||||
if (!streq_ptr(section, t->section))
|
||||
continue;
|
||||
|
||||
*func = t->parse;
|
||||
*ltype = t->ltype;
|
||||
*data = t->data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_item_perf_lookup(
|
||||
void *table,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
ConfigParserCallback *func,
|
||||
int *ltype,
|
||||
void **data,
|
||||
void *userdata) {
|
||||
|
||||
ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
|
||||
const ConfigPerfItem *p;
|
||||
|
||||
assert(table);
|
||||
assert(lvalue);
|
||||
assert(func);
|
||||
assert(ltype);
|
||||
assert(data);
|
||||
|
||||
if (!section)
|
||||
p = lookup(lvalue, strlen(lvalue));
|
||||
else {
|
||||
char *key;
|
||||
|
||||
key = join(section, ".", lvalue, NULL);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
p = lookup(key, strlen(key));
|
||||
free(key);
|
||||
}
|
||||
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
*func = p->parse;
|
||||
*ltype = p->ltype;
|
||||
*data = (uint8_t*) userdata + p->offset;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Run the user supplied parser for an assignment */
|
||||
static int next_assignment(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
ConfigItemLookup lookup,
|
||||
void *table,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
const char *rvalue,
|
||||
bool relaxed,
|
||||
void *userdata) {
|
||||
|
||||
ConfigParserCallback func = NULL;
|
||||
int ltype = 0;
|
||||
void *data = NULL;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(line > 0);
|
||||
assert(lookup);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
r = lookup(table, section, lvalue, &func, <ype, &data, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r > 0) {
|
||||
if (func)
|
||||
return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Warn about unknown non-extension fields. */
|
||||
if (!relaxed && !startswith(lvalue, "X-"))
|
||||
log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a variable assignment line */
|
||||
static int parse_line(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *sections,
|
||||
ConfigItemLookup lookup,
|
||||
void *table,
|
||||
bool relaxed,
|
||||
char **section,
|
||||
char *l,
|
||||
void *userdata) {
|
||||
|
||||
char *e;
|
||||
|
||||
assert(filename);
|
||||
assert(line > 0);
|
||||
assert(lookup);
|
||||
assert(l);
|
||||
|
||||
l = strstrip(l);
|
||||
|
||||
if (!*l)
|
||||
return 0;
|
||||
|
||||
if (strchr(COMMENTS, *l))
|
||||
return 0;
|
||||
|
||||
if (startswith(l, ".include ")) {
|
||||
char *fn;
|
||||
int r;
|
||||
|
||||
fn = file_in_same_dir(filename, strstrip(l+9));
|
||||
if (!fn)
|
||||
return -ENOMEM;
|
||||
|
||||
r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
|
||||
free(fn);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if (*l == '[') {
|
||||
size_t k;
|
||||
char *n;
|
||||
|
||||
k = strlen(l);
|
||||
assert(k > 0);
|
||||
|
||||
if (l[k-1] != ']') {
|
||||
log_error("[%s:%u] Invalid section header.", filename, line);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
n = strndup(l+1, k-2);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sections && !nulstr_contains(sections, n)) {
|
||||
|
||||
if (!relaxed)
|
||||
log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
|
||||
|
||||
free(n);
|
||||
*section = NULL;
|
||||
} else {
|
||||
free(*section);
|
||||
*section = n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sections && !*section) {
|
||||
|
||||
if (!relaxed)
|
||||
log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
e = strchr(l, '=');
|
||||
if (!e) {
|
||||
log_error("[%s:%u] Missing '='.", filename, line);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
*e = 0;
|
||||
e++;
|
||||
|
||||
return next_assignment(
|
||||
filename,
|
||||
line,
|
||||
lookup,
|
||||
table,
|
||||
*section,
|
||||
strstrip(l),
|
||||
strstrip(e),
|
||||
relaxed,
|
||||
userdata);
|
||||
}
|
||||
|
||||
/* Go through the file and parse each line */
|
||||
int config_parse(
|
||||
const char *filename,
|
||||
FILE *f,
|
||||
const char *sections,
|
||||
ConfigItemLookup lookup,
|
||||
void *table,
|
||||
bool relaxed,
|
||||
void *userdata) {
|
||||
|
||||
unsigned line = 0;
|
||||
char *section = NULL;
|
||||
int r;
|
||||
bool ours = false;
|
||||
char *continuation = NULL;
|
||||
|
||||
assert(filename);
|
||||
assert(lookup);
|
||||
|
||||
if (!f) {
|
||||
f = fopen(filename, "re");
|
||||
if (!f) {
|
||||
r = -errno;
|
||||
log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ours = true;
|
||||
}
|
||||
|
||||
while (!feof(f)) {
|
||||
char l[LINE_MAX], *p, *c = NULL, *e;
|
||||
bool escaped = false;
|
||||
|
||||
if (!fgets(l, sizeof(l), f)) {
|
||||
if (feof(f))
|
||||
break;
|
||||
|
||||
r = -errno;
|
||||
log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
truncate_nl(l);
|
||||
|
||||
if (continuation) {
|
||||
c = strappend(continuation, l);
|
||||
if (!c) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
free(continuation);
|
||||
continuation = NULL;
|
||||
p = c;
|
||||
} else
|
||||
p = l;
|
||||
|
||||
for (e = p; *e; e++) {
|
||||
if (escaped)
|
||||
escaped = false;
|
||||
else if (*e == '\\')
|
||||
escaped = true;
|
||||
}
|
||||
|
||||
if (escaped) {
|
||||
*(e-1) = ' ';
|
||||
|
||||
if (c)
|
||||
continuation = c;
|
||||
else {
|
||||
continuation = strdup(l);
|
||||
if (!continuation) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
r = parse_line(filename,
|
||||
++line,
|
||||
sections,
|
||||
lookup,
|
||||
table,
|
||||
relaxed,
|
||||
§ion,
|
||||
p,
|
||||
userdata);
|
||||
free(c);
|
||||
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
free(section);
|
||||
free(continuation);
|
||||
|
||||
if (f && ours)
|
||||
fclose(f);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int config_parse_int(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
int *i = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atoi(rvalue, i)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_long(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
long *i = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atoli(rvalue, i)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_uint64(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
uint64_t *u = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atou64(rvalue, u)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_unsigned(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
unsigned *u = data;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((r = safe_atou(rvalue, u)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_bytes_size(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
size_t *sz = data;
|
||||
off_t o;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
|
||||
log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*sz = (size_t) o;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int config_parse_bytes_off(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
off_t *bytes = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
assert_cc(sizeof(off_t) == sizeof(uint64_t));
|
||||
|
||||
if (parse_bytes(rvalue, bytes) < 0) {
|
||||
log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_bool(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
int k;
|
||||
bool *b = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if ((k = parse_boolean(rvalue)) < 0) {
|
||||
log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*b = !!k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_tristate(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
int k;
|
||||
int *b = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
/* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
|
||||
|
||||
k = parse_boolean(rvalue);
|
||||
if (k < 0) {
|
||||
log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*b = !!k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_string(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char **s = data;
|
||||
char *n;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
n = cunescape(rvalue);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!utf8_is_valid(n)) {
|
||||
log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
|
||||
free(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(*s);
|
||||
if (*n)
|
||||
*s = n;
|
||||
else {
|
||||
free(n);
|
||||
*s = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_path(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char **s = data;
|
||||
char *n;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (!utf8_is_valid(rvalue)) {
|
||||
log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(rvalue)) {
|
||||
log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = strdup(rvalue);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
path_kill_slashes(n);
|
||||
|
||||
free(*s);
|
||||
*s = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_strv(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char*** sv = data;
|
||||
char **n;
|
||||
char *w;
|
||||
unsigned k;
|
||||
size_t l;
|
||||
char *state;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
k = strv_length(*sv);
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
k++;
|
||||
|
||||
n = new(char*, k+1);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
if (*sv)
|
||||
for (k = 0; (*sv)[k]; k++)
|
||||
n[k] = (*sv)[k];
|
||||
else
|
||||
k = 0;
|
||||
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
|
||||
n[k] = cunescape_length(w, l);
|
||||
if (!n[k]) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!utf8_is_valid(n[k])) {
|
||||
log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
|
||||
free(n[k]);
|
||||
continue;
|
||||
}
|
||||
|
||||
k++;
|
||||
}
|
||||
|
||||
n[k] = NULL;
|
||||
free(*sv);
|
||||
*sv = n;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (; k > 0; k--)
|
||||
free(n[k-1]);
|
||||
free(n);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int config_parse_path_strv(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
char*** sv = data;
|
||||
char **n;
|
||||
char *w;
|
||||
unsigned k;
|
||||
size_t l;
|
||||
char *state;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
k = strv_length(*sv);
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state)
|
||||
k++;
|
||||
|
||||
n = new(char*, k+1);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
k = 0;
|
||||
if (*sv)
|
||||
for (; (*sv)[k]; k++)
|
||||
n[k] = (*sv)[k];
|
||||
|
||||
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
|
||||
n[k] = strndup(w, l);
|
||||
if (!n[k]) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!utf8_is_valid(n[k])) {
|
||||
log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
|
||||
free(n[k]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(n[k])) {
|
||||
log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
|
||||
free(n[k]);
|
||||
continue;
|
||||
}
|
||||
|
||||
path_kill_slashes(n[k]);
|
||||
k++;
|
||||
}
|
||||
|
||||
n[k] = NULL;
|
||||
free(*sv);
|
||||
*sv = n;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (; k > 0; k--)
|
||||
free(n[k-1]);
|
||||
free(n);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int config_parse_usec(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
usec_t *usec = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
if (parse_usec(rvalue, usec) < 0) {
|
||||
log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_mode(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
mode_t *m = data;
|
||||
long l;
|
||||
char *x = NULL;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
errno = 0;
|
||||
l = strtol(rvalue, &x, 8);
|
||||
if (!x || *x || errno) {
|
||||
log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (l < 0000 || l > 07777) {
|
||||
log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*m = (mode_t) l;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef fooconfparserhfoo
|
||||
#define fooconfparserhfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* An abstract parser for simple, line based, shallow configuration
|
||||
* files consisting of variable assignments only. */
|
||||
|
||||
/* Prototype for a parser for a specific configuration setting */
|
||||
typedef int (*ConfigParserCallback)(
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata);
|
||||
|
||||
/* Wraps information for parsing a specific configuration variable, to
|
||||
* be stored in a simple array */
|
||||
typedef struct ConfigTableItem {
|
||||
const char *section; /* Section */
|
||||
const char *lvalue; /* Name of the variable */
|
||||
ConfigParserCallback parse; /* Function that is called to parse the variable's value */
|
||||
int ltype; /* Distinguish different variables passed to the same callback */
|
||||
void *data; /* Where to store the variable's data */
|
||||
} ConfigTableItem;
|
||||
|
||||
/* Wraps information for parsing a specific configuration variable, to
|
||||
* ve srored in a gperf perfect hashtable */
|
||||
typedef struct ConfigPerfItem {
|
||||
const char *section_and_lvalue; /* Section + "." + name of the variable */
|
||||
ConfigParserCallback parse; /* Function that is called to parse the variable's value */
|
||||
int ltype; /* Distinguish different variables passed to the same callback */
|
||||
size_t offset; /* Offset where to store data, from the beginning of userdata */
|
||||
} ConfigPerfItem;
|
||||
|
||||
/* Prototype for a low-level gperf lookup function */
|
||||
typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
|
||||
|
||||
/* Prototype for a generic high-level lookup function */
|
||||
typedef int (*ConfigItemLookup)(
|
||||
void *table,
|
||||
const char *section,
|
||||
const char *lvalue,
|
||||
ConfigParserCallback *func,
|
||||
int *ltype,
|
||||
void **data,
|
||||
void *userdata);
|
||||
|
||||
/* Linear table search implementation of ConfigItemLookup, based on
|
||||
* ConfigTableItem arrays */
|
||||
int config_item_table_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
|
||||
|
||||
/* gperf implementation of ConfigItemLookup, based on gperf
|
||||
* ConfigPerfItem tables */
|
||||
int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
|
||||
|
||||
int config_parse(
|
||||
const char *filename,
|
||||
FILE *f,
|
||||
const char *sections, /* nulstr */
|
||||
ConfigItemLookup lookup,
|
||||
void *table,
|
||||
bool relaxed,
|
||||
void *userdata);
|
||||
|
||||
/* Generic parsers */
|
||||
int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_long(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_bytes_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_bytes_off(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||
|
||||
#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
|
||||
int function( \
|
||||
const char *filename, \
|
||||
unsigned line, \
|
||||
const char *section, \
|
||||
const char *lvalue, \
|
||||
int ltype, \
|
||||
const char *rvalue, \
|
||||
void *data, \
|
||||
void *userdata) { \
|
||||
\
|
||||
type *i = data, x; \
|
||||
\
|
||||
assert(filename); \
|
||||
assert(lvalue); \
|
||||
assert(rvalue); \
|
||||
assert(data); \
|
||||
\
|
||||
if ((x = name##_from_string(rvalue)) < 0) { \
|
||||
log_error("[%s:%u] " msg ", ignoring: %s", filename, line, rvalue); \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
*i = x; \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,180 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "exit-status.h"
|
||||
|
||||
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
|
||||
|
||||
/* We cast to int here, so that -Wenum doesn't complain that
|
||||
* EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
|
||||
|
||||
switch ((int) status) {
|
||||
|
||||
case EXIT_SUCCESS:
|
||||
return "SUCCESS";
|
||||
|
||||
case EXIT_FAILURE:
|
||||
return "FAILURE";
|
||||
}
|
||||
|
||||
|
||||
if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
|
||||
switch ((int) status) {
|
||||
|
||||
case EXIT_CHDIR:
|
||||
return "CHDIR";
|
||||
|
||||
case EXIT_NICE:
|
||||
return "NICE";
|
||||
|
||||
case EXIT_FDS:
|
||||
return "FDS";
|
||||
|
||||
case EXIT_EXEC:
|
||||
return "EXEC";
|
||||
|
||||
case EXIT_MEMORY:
|
||||
return "MEMORY";
|
||||
|
||||
case EXIT_LIMITS:
|
||||
return "LIMITS";
|
||||
|
||||
case EXIT_OOM_ADJUST:
|
||||
return "OOM_ADJUST";
|
||||
|
||||
case EXIT_SIGNAL_MASK:
|
||||
return "SIGNAL_MASK";
|
||||
|
||||
case EXIT_STDIN:
|
||||
return "STDIN";
|
||||
|
||||
case EXIT_STDOUT:
|
||||
return "STDOUT";
|
||||
|
||||
case EXIT_CHROOT:
|
||||
return "CHROOT";
|
||||
|
||||
case EXIT_IOPRIO:
|
||||
return "IOPRIO";
|
||||
|
||||
case EXIT_TIMERSLACK:
|
||||
return "TIMERSLACK";
|
||||
|
||||
case EXIT_SECUREBITS:
|
||||
return "SECUREBITS";
|
||||
|
||||
case EXIT_SETSCHEDULER:
|
||||
return "SETSCHEDULER";
|
||||
|
||||
case EXIT_CPUAFFINITY:
|
||||
return "CPUAFFINITY";
|
||||
|
||||
case EXIT_GROUP:
|
||||
return "GROUP";
|
||||
|
||||
case EXIT_USER:
|
||||
return "USER";
|
||||
|
||||
case EXIT_CAPABILITIES:
|
||||
return "CAPABILITIES";
|
||||
|
||||
case EXIT_CGROUP:
|
||||
return "CGROUP";
|
||||
|
||||
case EXIT_SETSID:
|
||||
return "SETSID";
|
||||
|
||||
case EXIT_CONFIRM:
|
||||
return "CONFIRM";
|
||||
|
||||
case EXIT_STDERR:
|
||||
return "STDERR";
|
||||
|
||||
case EXIT_TCPWRAP:
|
||||
return "TCPWRAP";
|
||||
|
||||
case EXIT_PAM:
|
||||
return "PAM";
|
||||
|
||||
case EXIT_NETWORK:
|
||||
return "NETWORK";
|
||||
|
||||
case EXIT_NAMESPACE:
|
||||
return "NAMESPACE";
|
||||
}
|
||||
}
|
||||
|
||||
if (level == EXIT_STATUS_LSB) {
|
||||
switch ((int) status) {
|
||||
|
||||
case EXIT_INVALIDARGUMENT:
|
||||
return "INVALIDARGUMENT";
|
||||
|
||||
case EXIT_NOTIMPLEMENTED:
|
||||
return "NOTIMPLEMENTED";
|
||||
|
||||
case EXIT_NOPERMISSION:
|
||||
return "NOPERMISSION";
|
||||
|
||||
case EXIT_NOTINSTALLED:
|
||||
return "NOTINSSTALLED";
|
||||
|
||||
case EXIT_NOTCONFIGURED:
|
||||
return "NOTCONFIGURED";
|
||||
|
||||
case EXIT_NOTRUNNING:
|
||||
return "NOTRUNNING";
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool is_clean_exit(int code, int status) {
|
||||
|
||||
if (code == CLD_EXITED)
|
||||
return status == 0;
|
||||
|
||||
/* If a daemon does not implement handlers for some of the
|
||||
* signals that's not considered an unclean shutdown */
|
||||
if (code == CLD_KILLED)
|
||||
return
|
||||
status == SIGHUP ||
|
||||
status == SIGINT ||
|
||||
status == SIGTERM ||
|
||||
status == SIGPIPE;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_clean_exit_lsb(int code, int status) {
|
||||
|
||||
if (is_clean_exit(code, status))
|
||||
return true;
|
||||
|
||||
return
|
||||
code == CLD_EXITED &&
|
||||
(status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef fooexitstatushfoo
|
||||
#define fooexitstatushfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum ExitStatus {
|
||||
/* EXIT_SUCCESS defined by libc */
|
||||
/* EXIT_FAILURE defined by libc */
|
||||
EXIT_INVALIDARGUMENT = 2,
|
||||
EXIT_NOTIMPLEMENTED = 3,
|
||||
EXIT_NOPERMISSION = 4,
|
||||
EXIT_NOTINSTALLED = 5,
|
||||
EXIT_NOTCONFIGURED = 6,
|
||||
EXIT_NOTRUNNING = 7,
|
||||
|
||||
/* The LSB suggests that error codes >= 200 are "reserved". We
|
||||
* use them here under the assumption that they hence are
|
||||
* unused by init scripts.
|
||||
*
|
||||
* http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
|
||||
|
||||
EXIT_CHDIR = 200,
|
||||
EXIT_NICE,
|
||||
EXIT_FDS,
|
||||
EXIT_EXEC,
|
||||
EXIT_MEMORY,
|
||||
EXIT_LIMITS,
|
||||
EXIT_OOM_ADJUST,
|
||||
EXIT_SIGNAL_MASK,
|
||||
EXIT_STDIN,
|
||||
EXIT_STDOUT,
|
||||
EXIT_CHROOT, /* 210 */
|
||||
EXIT_IOPRIO,
|
||||
EXIT_TIMERSLACK,
|
||||
EXIT_SECUREBITS,
|
||||
EXIT_SETSCHEDULER,
|
||||
EXIT_CPUAFFINITY,
|
||||
EXIT_GROUP,
|
||||
EXIT_USER,
|
||||
EXIT_CAPABILITIES,
|
||||
EXIT_CGROUP,
|
||||
EXIT_SETSID, /* 220 */
|
||||
EXIT_CONFIRM,
|
||||
EXIT_STDERR,
|
||||
EXIT_TCPWRAP,
|
||||
EXIT_PAM,
|
||||
EXIT_NETWORK,
|
||||
EXIT_NAMESPACE
|
||||
|
||||
} ExitStatus;
|
||||
|
||||
typedef enum ExitStatusLevel {
|
||||
EXIT_STATUS_MINIMAL,
|
||||
EXIT_STATUS_SYSTEMD,
|
||||
EXIT_STATUS_LSB,
|
||||
EXIT_STATUS_FULL = EXIT_STATUS_LSB
|
||||
} ExitStatusLevel;
|
||||
|
||||
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
|
||||
|
||||
bool is_clean_exit(int code, int status);
|
||||
bool is_clean_exit_lsb(int code, int status);
|
||||
|
||||
#endif
|
||||
@@ -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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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,413 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "label.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
#include <selinux/selinux.h>
|
||||
#include <selinux/label.h>
|
||||
|
||||
static struct selabel_handle *label_hnd = NULL;
|
||||
|
||||
static int use_selinux_cached = -1;
|
||||
|
||||
static inline bool use_selinux(void) {
|
||||
|
||||
if (use_selinux_cached < 0)
|
||||
use_selinux_cached = is_selinux_enabled() > 0;
|
||||
|
||||
return use_selinux_cached;
|
||||
}
|
||||
|
||||
void label_retest_selinux(void) {
|
||||
use_selinux_cached = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int label_init(void) {
|
||||
int r = 0;
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
usec_t before_timestamp, after_timestamp;
|
||||
struct mallinfo before_mallinfo, after_mallinfo;
|
||||
|
||||
if (!use_selinux())
|
||||
return 0;
|
||||
|
||||
if (label_hnd)
|
||||
return 0;
|
||||
|
||||
before_mallinfo = mallinfo();
|
||||
before_timestamp = now(CLOCK_MONOTONIC);
|
||||
|
||||
label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
|
||||
if (!label_hnd) {
|
||||
log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
|
||||
"Failed to initialize SELinux context: %m");
|
||||
r = security_getenforce() == 1 ? -errno : 0;
|
||||
} else {
|
||||
char timespan[FORMAT_TIMESPAN_MAX];
|
||||
int l;
|
||||
|
||||
after_timestamp = now(CLOCK_MONOTONIC);
|
||||
after_mallinfo = mallinfo();
|
||||
|
||||
l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
|
||||
|
||||
log_info("Successfully loaded SELinux database in %s, size on heap is %iK.",
|
||||
format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
|
||||
(l+1023)/1024);
|
||||
}
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int label_fix(const char *path, bool ignore_enoent) {
|
||||
int r = 0;
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
struct stat st;
|
||||
security_context_t fcon;
|
||||
|
||||
if (!use_selinux() || !label_hnd)
|
||||
return 0;
|
||||
|
||||
r = lstat(path, &st);
|
||||
if (r == 0) {
|
||||
r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
|
||||
|
||||
/* If there's no label to set, then exit without warning */
|
||||
if (r < 0 && errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
if (r == 0) {
|
||||
r = lsetfilecon(path, fcon);
|
||||
freecon(fcon);
|
||||
|
||||
/* If the FS doesn't support labels, then exit without warning */
|
||||
if (r < 0 && errno == ENOTSUP)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
/* Ignore ENOENT in some cases */
|
||||
if (ignore_enoent && errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
|
||||
"Unable to fix label of %s: %m", path);
|
||||
r = security_getenforce() == 1 ? -errno : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void label_finish(void) {
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (use_selinux() && label_hnd)
|
||||
selabel_close(label_hnd);
|
||||
#endif
|
||||
}
|
||||
|
||||
int label_get_create_label_from_exe(const char *exe, char **label) {
|
||||
|
||||
int r = 0;
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
security_context_t mycon = NULL, fcon = NULL;
|
||||
security_class_t sclass;
|
||||
|
||||
if (!use_selinux()) {
|
||||
*label = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = getcon(&mycon);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = getfilecon(exe, &fcon);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
sclass = string_to_security_class("process");
|
||||
r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
|
||||
if (r == 0)
|
||||
log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
|
||||
|
||||
fail:
|
||||
if (r < 0 && security_getenforce() == 1)
|
||||
r = -errno;
|
||||
|
||||
freecon(mycon);
|
||||
freecon(fcon);
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int label_fifofile_set(const char *path) {
|
||||
int r = 0;
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
security_context_t filecon = NULL;
|
||||
|
||||
if (!use_selinux() || !label_hnd)
|
||||
return 0;
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
else if (r == 0) {
|
||||
r = setfscreatecon(filecon);
|
||||
if (r < 0) {
|
||||
log_error("Failed to set SELinux file context on %s: %m", path);
|
||||
r = -errno;
|
||||
}
|
||||
|
||||
freecon(filecon);
|
||||
}
|
||||
|
||||
if (r < 0 && security_getenforce() == 0)
|
||||
r = 0;
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int label_symlinkfile_set(const char *path) {
|
||||
int r = 0;
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
security_context_t filecon = NULL;
|
||||
|
||||
if (!use_selinux() || !label_hnd)
|
||||
return 0;
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
else if (r == 0) {
|
||||
r = setfscreatecon(filecon);
|
||||
if (r < 0) {
|
||||
log_error("Failed to set SELinux file context on %s: %m", path);
|
||||
r = -errno;
|
||||
}
|
||||
|
||||
freecon(filecon);
|
||||
}
|
||||
|
||||
if (r < 0 && security_getenforce() == 0)
|
||||
r = 0;
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int label_socket_set(const char *label) {
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (!use_selinux())
|
||||
return 0;
|
||||
|
||||
if (setsockcreatecon((security_context_t) label) < 0) {
|
||||
log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
|
||||
"Failed to set SELinux context (%s) on socket: %m", label);
|
||||
|
||||
if (security_getenforce() == 1)
|
||||
return -errno;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void label_file_clear(void) {
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (!use_selinux())
|
||||
return;
|
||||
|
||||
setfscreatecon(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void label_socket_clear(void) {
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (!use_selinux())
|
||||
return;
|
||||
|
||||
setsockcreatecon(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void label_free(const char *label) {
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (!use_selinux())
|
||||
return;
|
||||
|
||||
freecon((security_context_t) label);
|
||||
#endif
|
||||
}
|
||||
|
||||
int label_mkdir(const char *path, mode_t mode) {
|
||||
|
||||
/* Creates a directory and labels it according to the SELinux policy */
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
int r;
|
||||
security_context_t fcon = NULL;
|
||||
|
||||
if (!use_selinux() || !label_hnd)
|
||||
goto skipped;
|
||||
|
||||
if (path_is_absolute(path))
|
||||
r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
|
||||
else {
|
||||
char *newpath;
|
||||
|
||||
newpath = path_make_absolute_cwd(path);
|
||||
if (!newpath)
|
||||
return -ENOMEM;
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
|
||||
free(newpath);
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
r = setfscreatecon(fcon);
|
||||
|
||||
if (r < 0 && errno != ENOENT) {
|
||||
log_error("Failed to set security context %s for %s: %m", fcon, path);
|
||||
|
||||
if (security_getenforce() == 1) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
r = mkdir(path, mode);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
|
||||
finish:
|
||||
setfscreatecon(NULL);
|
||||
freecon(fcon);
|
||||
|
||||
return r;
|
||||
|
||||
skipped:
|
||||
#endif
|
||||
return mkdir(path, mode) < 0 ? -errno : 0;
|
||||
}
|
||||
|
||||
int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
|
||||
/* Binds a socket and label its file system object according to the SELinux policy */
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
int r;
|
||||
security_context_t fcon = NULL;
|
||||
const struct sockaddr_un *un;
|
||||
char *path = NULL;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(addr);
|
||||
assert(addrlen >= sizeof(sa_family_t));
|
||||
|
||||
if (!use_selinux() || !label_hnd)
|
||||
goto skipped;
|
||||
|
||||
/* Filter out non-local sockets */
|
||||
if (addr->sa_family != AF_UNIX)
|
||||
goto skipped;
|
||||
|
||||
/* Filter out anonymous sockets */
|
||||
if (addrlen < sizeof(sa_family_t) + 1)
|
||||
goto skipped;
|
||||
|
||||
/* Filter out abstract namespace sockets */
|
||||
un = (const struct sockaddr_un*) addr;
|
||||
if (un->sun_path[0] == 0)
|
||||
goto skipped;
|
||||
|
||||
path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
if (path_is_absolute(path))
|
||||
r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
|
||||
else {
|
||||
char *newpath;
|
||||
|
||||
newpath = path_make_absolute_cwd(path);
|
||||
|
||||
if (!newpath) {
|
||||
free(path);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
|
||||
free(newpath);
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
r = setfscreatecon(fcon);
|
||||
|
||||
if (r < 0 && errno != ENOENT) {
|
||||
log_error("Failed to set security context %s for %s: %m", fcon, path);
|
||||
|
||||
if (security_getenforce() == 1) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
r = bind(fd, addr, addrlen);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
|
||||
finish:
|
||||
setfscreatecon(NULL);
|
||||
freecon(fcon);
|
||||
free(path);
|
||||
|
||||
return r;
|
||||
|
||||
skipped:
|
||||
#endif
|
||||
return bind(fd, addr, addrlen) < 0 ? -errno : 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foolabelhfoo
|
||||
#define foolabelhfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int label_init(void);
|
||||
void label_finish(void);
|
||||
|
||||
int label_fix(const char *path, bool ignore_enoent);
|
||||
|
||||
int label_socket_set(const char *label);
|
||||
void label_socket_clear(void);
|
||||
|
||||
int label_fifofile_set(const char *path);
|
||||
int label_symlinkfile_set(const char *path);
|
||||
void label_file_clear(void);
|
||||
|
||||
void label_free(const char *label);
|
||||
|
||||
int label_get_create_label_from_exe(const char *exe, char **label);
|
||||
|
||||
int label_mkdir(const char *path, mode_t mode);
|
||||
|
||||
void label_retest_selinux(void);
|
||||
|
||||
int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,747 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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"
|
||||
#include "socket-util.h"
|
||||
|
||||
#define SNDBUF_SIZE (8*1024*1024)
|
||||
|
||||
static LogTarget log_target = LOG_TARGET_CONSOLE;
|
||||
static int log_max_level = LOG_INFO;
|
||||
static int log_facility = LOG_DAEMON;
|
||||
|
||||
static int console_fd = STDERR_FILENO;
|
||||
static int syslog_fd = -1;
|
||||
static int kmsg_fd = -1;
|
||||
static int journal_fd = -1;
|
||||
|
||||
static bool syslog_is_stream = false;
|
||||
|
||||
static bool show_color = false;
|
||||
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;
|
||||
}
|
||||
|
||||
void log_close_kmsg(void) {
|
||||
|
||||
if (kmsg_fd < 0)
|
||||
return;
|
||||
|
||||
close_nointr_nofail(kmsg_fd);
|
||||
kmsg_fd = -1;
|
||||
}
|
||||
|
||||
static int log_open_kmsg(void) {
|
||||
|
||||
if (kmsg_fd >= 0)
|
||||
return 0;
|
||||
|
||||
kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (kmsg_fd < 0) {
|
||||
log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
log_debug("Successfully opened /dev/kmsg for logging.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_close_syslog(void) {
|
||||
|
||||
if (syslog_fd < 0)
|
||||
return;
|
||||
|
||||
close_nointr_nofail(syslog_fd);
|
||||
syslog_fd = -1;
|
||||
}
|
||||
|
||||
static int create_log_socket(int type) {
|
||||
int fd;
|
||||
|
||||
/* All output to the syslog/journal fds we do asynchronously,
|
||||
* and if the buffers are full we just drop the messages */
|
||||
|
||||
fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd_inc_sndbuf(fd, SNDBUF_SIZE);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int log_open_syslog(void) {
|
||||
union sockaddr_union sa;
|
||||
int r;
|
||||
|
||||
if (syslog_fd >= 0)
|
||||
return 0;
|
||||
|
||||
zero(sa);
|
||||
sa.un.sun_family = AF_UNIX;
|
||||
strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
|
||||
|
||||
syslog_fd = create_log_socket(SOCK_DGRAM);
|
||||
if (syslog_fd < 0) {
|
||||
r = syslog_fd;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
|
||||
close_nointr_nofail(syslog_fd);
|
||||
|
||||
/* Some legacy syslog systems still use stream
|
||||
* sockets. They really shouldn't. But what can we
|
||||
* do... */
|
||||
syslog_fd = create_log_socket(SOCK_STREAM);
|
||||
if (syslog_fd < 0) {
|
||||
r = syslog_fd;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
syslog_is_stream = true;
|
||||
} else
|
||||
syslog_is_stream = false;
|
||||
|
||||
log_debug("Successfully opened syslog for logging.");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
log_close_syslog();
|
||||
log_debug("Failed to open syslog for logging: %s", strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
void log_close_journal(void) {
|
||||
|
||||
if (journal_fd < 0)
|
||||
return;
|
||||
|
||||
close_nointr_nofail(journal_fd);
|
||||
journal_fd = -1;
|
||||
}
|
||||
|
||||
static int log_open_journal(void) {
|
||||
union sockaddr_union sa;
|
||||
int r;
|
||||
|
||||
if (journal_fd >= 0)
|
||||
return 0;
|
||||
|
||||
journal_fd = create_log_socket(SOCK_DGRAM);
|
||||
if (journal_fd < 0) {
|
||||
r = journal_fd;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
zero(sa);
|
||||
sa.un.sun_family = AF_UNIX;
|
||||
strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
|
||||
|
||||
if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_debug("Successfully opened journal for logging.");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
log_close_journal();
|
||||
log_debug("Failed to open journal for logging: %s", strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
int log_open(void) {
|
||||
int r;
|
||||
|
||||
/* If we don't use the console we close it here, to not get
|
||||
* killed by SAK. If we don't use syslog we close it here so
|
||||
* that we are not confused by somebody deleting the socket in
|
||||
* the fs. If we don't use /dev/kmsg we still keep it open,
|
||||
* because there is no reason to close it. */
|
||||
|
||||
if (log_target == LOG_TARGET_NULL) {
|
||||
log_close_journal();
|
||||
log_close_syslog();
|
||||
log_close_console();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (log_target != LOG_TARGET_AUTO ||
|
||||
getpid() == 1 ||
|
||||
isatty(STDERR_FILENO) <= 0) {
|
||||
|
||||
if (log_target == LOG_TARGET_AUTO ||
|
||||
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
|
||||
log_target == LOG_TARGET_JOURNAL) {
|
||||
r = log_open_journal();
|
||||
if (r >= 0) {
|
||||
log_close_syslog();
|
||||
log_close_console();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_SYSLOG) {
|
||||
r = log_open_syslog();
|
||||
if (r >= 0) {
|
||||
log_close_journal();
|
||||
log_close_console();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (log_target == LOG_TARGET_AUTO ||
|
||||
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
|
||||
log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_KMSG) {
|
||||
r = log_open_kmsg();
|
||||
if (r >= 0) {
|
||||
log_close_journal();
|
||||
log_close_syslog();
|
||||
log_close_console();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_close_journal();
|
||||
log_close_syslog();
|
||||
|
||||
/* Get the real /dev/console if we are PID=1, hence reopen */
|
||||
log_close_console();
|
||||
return log_open_console();
|
||||
}
|
||||
|
||||
void log_set_target(LogTarget target) {
|
||||
assert(target >= 0);
|
||||
assert(target < _LOG_TARGET_MAX);
|
||||
|
||||
log_target = target;
|
||||
}
|
||||
|
||||
void log_close(void) {
|
||||
log_close_journal();
|
||||
log_close_syslog();
|
||||
log_close_kmsg();
|
||||
log_close_console();
|
||||
}
|
||||
|
||||
void log_forget_fds(void) {
|
||||
console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
|
||||
}
|
||||
|
||||
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;
|
||||
bool highlight;
|
||||
|
||||
if (console_fd < 0)
|
||||
return 0;
|
||||
|
||||
highlight = LOG_PRI(level) <= LOG_ERR && show_color;
|
||||
|
||||
zero(iovec);
|
||||
|
||||
if (show_location) {
|
||||
snprintf(location, sizeof(location), "(%s:%u) ", file, line);
|
||||
char_array_0(location);
|
||||
IOVEC_SET_STRING(iovec[n++], location);
|
||||
}
|
||||
|
||||
if (highlight)
|
||||
IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
|
||||
IOVEC_SET_STRING(iovec[n++], buffer);
|
||||
if (highlight)
|
||||
IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
|
||||
IOVEC_SET_STRING(iovec[n++], "\n");
|
||||
|
||||
if (writev(console_fd, iovec, n) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_to_syslog(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *buffer) {
|
||||
|
||||
char header_priority[16], header_time[64], header_pid[16];
|
||||
struct iovec iovec[5];
|
||||
struct msghdr msghdr;
|
||||
time_t t;
|
||||
struct tm *tm;
|
||||
|
||||
if (syslog_fd < 0)
|
||||
return 0;
|
||||
|
||||
snprintf(header_priority, sizeof(header_priority), "<%i>", level);
|
||||
char_array_0(header_priority);
|
||||
|
||||
t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
|
||||
if (!(tm = localtime(&t)))
|
||||
return -EINVAL;
|
||||
|
||||
if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
|
||||
char_array_0(header_pid);
|
||||
|
||||
zero(iovec);
|
||||
IOVEC_SET_STRING(iovec[0], header_priority);
|
||||
IOVEC_SET_STRING(iovec[1], header_time);
|
||||
IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
|
||||
IOVEC_SET_STRING(iovec[3], header_pid);
|
||||
IOVEC_SET_STRING(iovec[4], buffer);
|
||||
|
||||
/* When using syslog via SOCK_STREAM separate the messages by NUL chars */
|
||||
if (syslog_is_stream)
|
||||
iovec[4].iov_len++;
|
||||
|
||||
zero(msghdr);
|
||||
msghdr.msg_iov = iovec;
|
||||
msghdr.msg_iovlen = ELEMENTSOF(iovec);
|
||||
|
||||
for (;;) {
|
||||
ssize_t n;
|
||||
|
||||
n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
|
||||
if (!syslog_is_stream ||
|
||||
(size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
|
||||
break;
|
||||
|
||||
IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_to_kmsg(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *buffer) {
|
||||
|
||||
char header_priority[16], header_pid[16];
|
||||
struct iovec iovec[5];
|
||||
|
||||
if (kmsg_fd < 0)
|
||||
return 0;
|
||||
|
||||
snprintf(header_priority, sizeof(header_priority), "<%i>", level);
|
||||
char_array_0(header_priority);
|
||||
|
||||
snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
|
||||
char_array_0(header_pid);
|
||||
|
||||
zero(iovec);
|
||||
IOVEC_SET_STRING(iovec[0], header_priority);
|
||||
IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
|
||||
IOVEC_SET_STRING(iovec[2], header_pid);
|
||||
IOVEC_SET_STRING(iovec[3], buffer);
|
||||
IOVEC_SET_STRING(iovec[4], "\n");
|
||||
|
||||
if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_to_journal(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
const char *buffer) {
|
||||
|
||||
char header[LINE_MAX];
|
||||
struct iovec iovec[3];
|
||||
struct msghdr mh;
|
||||
|
||||
if (journal_fd < 0)
|
||||
return 0;
|
||||
|
||||
snprintf(header, sizeof(header),
|
||||
"PRIORITY=%i\n"
|
||||
"SYSLOG_FACILITY=%i\n"
|
||||
"CODE_FILE=%s\n"
|
||||
"CODE_LINE=%i\n"
|
||||
"CODE_FUNCTION=%s\n"
|
||||
"MESSAGE=",
|
||||
LOG_PRI(level),
|
||||
LOG_FAC(level),
|
||||
file,
|
||||
line,
|
||||
func);
|
||||
|
||||
char_array_0(header);
|
||||
|
||||
zero(iovec);
|
||||
IOVEC_SET_STRING(iovec[0], header);
|
||||
IOVEC_SET_STRING(iovec[1], buffer);
|
||||
IOVEC_SET_STRING(iovec[2], "\n");
|
||||
|
||||
zero(mh);
|
||||
mh.msg_iov = iovec;
|
||||
mh.msg_iovlen = ELEMENTSOF(iovec);
|
||||
|
||||
if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 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;
|
||||
|
||||
if (log_target == LOG_TARGET_AUTO ||
|
||||
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
|
||||
log_target == LOG_TARGET_JOURNAL) {
|
||||
|
||||
k = write_to_journal(level, file, line, func, buffer);
|
||||
if (k < 0) {
|
||||
if (k != -EAGAIN)
|
||||
log_close_journal();
|
||||
log_open_kmsg();
|
||||
} else if (k > 0)
|
||||
r++;
|
||||
}
|
||||
|
||||
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_SYSLOG) {
|
||||
|
||||
k = write_to_syslog(level, file, line, func, buffer);
|
||||
if (k < 0) {
|
||||
if (k != -EAGAIN)
|
||||
log_close_syslog();
|
||||
log_open_kmsg();
|
||||
} else if (k > 0)
|
||||
r++;
|
||||
}
|
||||
|
||||
if (k <= 0 &&
|
||||
(log_target == LOG_TARGET_AUTO ||
|
||||
log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
|
||||
log_target == LOG_TARGET_KMSG)) {
|
||||
|
||||
k = write_to_kmsg(level, file, line, func, buffer);
|
||||
if (k < 0) {
|
||||
log_close_kmsg();
|
||||
log_open_console();
|
||||
} else if (k > 0)
|
||||
r++;
|
||||
}
|
||||
|
||||
if (k <= 0) {
|
||||
k = write_to_console(level, file, line, func, buffer);
|
||||
if (k < 0)
|
||||
return k;
|
||||
}
|
||||
|
||||
buffer = e;
|
||||
} while (buffer);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int log_dump_internal(
|
||||
int level,
|
||||
const char*file,
|
||||
int line,
|
||||
const char *func,
|
||||
char *buffer) {
|
||||
|
||||
int saved_errno, r;
|
||||
|
||||
/* This modifies the buffer... */
|
||||
|
||||
if (_likely_(LOG_PRI(level) > log_max_level))
|
||||
return 0;
|
||||
|
||||
saved_errno = errno;
|
||||
r = log_dispatch(level, file, line, func, buffer);
|
||||
errno = saved_errno;
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
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("SYSTEMD_LOG_TARGET")))
|
||||
if (log_set_target_from_string(e) < 0)
|
||||
log_warning("Failed to parse log target %s. Ignoring.", e);
|
||||
|
||||
if ((e = getenv("SYSTEMD_LOG_LEVEL")))
|
||||
if (log_set_max_level_from_string(e) < 0)
|
||||
log_warning("Failed to parse log level %s. Ignoring.", e);
|
||||
|
||||
if ((e = getenv("SYSTEMD_LOG_COLOR")))
|
||||
if (log_show_color_from_string(e) < 0)
|
||||
log_warning("Failed to parse bool %s. Ignoring.", e);
|
||||
|
||||
if ((e = getenv("SYSTEMD_LOG_LOCATION")))
|
||||
if (log_show_location_from_string(e) < 0)
|
||||
log_warning("Failed to parse bool %s. Ignoring.", e);
|
||||
}
|
||||
|
||||
LogTarget log_get_target(void) {
|
||||
return log_target;
|
||||
}
|
||||
|
||||
int log_get_max_level(void) {
|
||||
return log_max_level;
|
||||
}
|
||||
|
||||
void log_show_color(bool b) {
|
||||
show_color = b;
|
||||
}
|
||||
|
||||
void log_show_location(bool b) {
|
||||
show_location = b;
|
||||
}
|
||||
|
||||
int log_show_color_from_string(const char *e) {
|
||||
int t;
|
||||
|
||||
t = parse_boolean(e);
|
||||
if (t < 0)
|
||||
return t;
|
||||
|
||||
log_show_color(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_show_location_from_string(const char *e) {
|
||||
int t;
|
||||
|
||||
t = parse_boolean(e);
|
||||
if (t < 0)
|
||||
return t;
|
||||
|
||||
log_show_location(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const log_target_table[] = {
|
||||
[LOG_TARGET_CONSOLE] = "console",
|
||||
[LOG_TARGET_KMSG] = "kmsg",
|
||||
[LOG_TARGET_JOURNAL] = "journal",
|
||||
[LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
|
||||
[LOG_TARGET_SYSLOG] = "syslog",
|
||||
[LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
|
||||
[LOG_TARGET_AUTO] = "auto",
|
||||
[LOG_TARGET_NULL] = "null"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
|
||||
@@ -0,0 +1,111 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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_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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "ratelimit.h"
|
||||
#include "log.h"
|
||||
|
||||
/* Modelled after Linux' lib/ratelimit.c by Dave Young
|
||||
* <hidave.darkstar@gmail.com>, which is licensed GPLv2. */
|
||||
|
||||
bool ratelimit_test(RateLimit *r) {
|
||||
usec_t ts;
|
||||
|
||||
assert(r);
|
||||
|
||||
if (r->interval <= 0 || r->burst <= 0)
|
||||
return true;
|
||||
|
||||
ts = now(CLOCK_MONOTONIC);
|
||||
|
||||
if (r->begin <= 0 ||
|
||||
r->begin + r->interval < ts) {
|
||||
r->begin = ts;
|
||||
|
||||
/* Reset counter */
|
||||
r->num = 0;
|
||||
goto good;
|
||||
}
|
||||
|
||||
if (r->num <= r->burst)
|
||||
goto good;
|
||||
|
||||
return false;
|
||||
|
||||
good:
|
||||
r->num++;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef fooratelimithfoo
|
||||
#define fooratelimithfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "util.h"
|
||||
|
||||
typedef struct RateLimit {
|
||||
usec_t interval;
|
||||
usec_t begin;
|
||||
unsigned burst;
|
||||
unsigned num;
|
||||
} RateLimit;
|
||||
|
||||
#define RATELIMIT_DEFINE(_name, _interval, _burst) \
|
||||
RateLimit _name = { \
|
||||
.interval = (_interval), \
|
||||
.burst = (_burst), \
|
||||
.num = 0, \
|
||||
.begin = 0 \
|
||||
}
|
||||
|
||||
#define RATELIMIT_INIT(v, _interval, _burst) \
|
||||
do { \
|
||||
RateLimit *_r = &(v); \
|
||||
_r->interval = (_interval); \
|
||||
_r->burst = (_burst); \
|
||||
_r->num = 0; \
|
||||
_r->begin = 0; \
|
||||
} while (false)
|
||||
|
||||
bool ratelimit_test(RateLimit *r);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "set.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
#define MAKE_SET(h) ((Set*) (h))
|
||||
#define MAKE_HASHMAP(s) ((Hashmap*) (s))
|
||||
|
||||
/* For now this is not much more than a wrapper around a hashmap */
|
||||
|
||||
Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
|
||||
return MAKE_SET(hashmap_new(hash_func, compare_func));
|
||||
}
|
||||
|
||||
void set_free(Set* s) {
|
||||
hashmap_free(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
void set_free_free(Set *s) {
|
||||
hashmap_free_free(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func) {
|
||||
return hashmap_ensure_allocated((Hashmap**) s, hash_func, compare_func);
|
||||
}
|
||||
|
||||
int set_put(Set *s, void *value) {
|
||||
return hashmap_put(MAKE_HASHMAP(s), value, value);
|
||||
}
|
||||
|
||||
int set_replace(Set *s, void *value) {
|
||||
return hashmap_replace(MAKE_HASHMAP(s), value, value);
|
||||
}
|
||||
|
||||
void *set_get(Set *s, void *value) {
|
||||
return hashmap_get(MAKE_HASHMAP(s), value);
|
||||
}
|
||||
|
||||
void *set_remove(Set *s, void *value) {
|
||||
return hashmap_remove(MAKE_HASHMAP(s), value);
|
||||
}
|
||||
|
||||
int set_remove_and_put(Set *s, void *old_value, void *new_value) {
|
||||
return hashmap_remove_and_put(MAKE_HASHMAP(s), old_value, new_value, new_value);
|
||||
}
|
||||
|
||||
unsigned set_size(Set *s) {
|
||||
return hashmap_size(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
bool set_isempty(Set *s) {
|
||||
return hashmap_isempty(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
void *set_iterate(Set *s, Iterator *i) {
|
||||
return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
|
||||
}
|
||||
|
||||
void *set_iterate_backwards(Set *s, Iterator *i) {
|
||||
return hashmap_iterate_backwards(MAKE_HASHMAP(s), i, NULL);
|
||||
}
|
||||
|
||||
void *set_iterate_skip(Set *s, void *value, Iterator *i) {
|
||||
return hashmap_iterate_skip(MAKE_HASHMAP(s), value, i);
|
||||
}
|
||||
|
||||
void *set_steal_first(Set *s) {
|
||||
return hashmap_steal_first(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
void* set_first(Set *s) {
|
||||
return hashmap_first(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
void* set_last(Set *s) {
|
||||
return hashmap_last(MAKE_HASHMAP(s));
|
||||
}
|
||||
|
||||
int set_merge(Set *s, Set *other) {
|
||||
return hashmap_merge(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
|
||||
}
|
||||
|
||||
void set_move(Set *s, Set *other) {
|
||||
return hashmap_move(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
|
||||
}
|
||||
|
||||
int set_move_one(Set *s, Set *other, void *value) {
|
||||
return hashmap_move_one(MAKE_HASHMAP(s), MAKE_HASHMAP(other), value);
|
||||
}
|
||||
|
||||
Set* set_copy(Set *s) {
|
||||
return MAKE_SET(hashmap_copy(MAKE_HASHMAP(s)));
|
||||
}
|
||||
|
||||
void set_clear(Set *s) {
|
||||
hashmap_clear(MAKE_HASHMAP(s));
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foosethfoo
|
||||
#define foosethfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/* Pretty straightforward set implementation. Internally based on the
|
||||
* hashmap. That means that as a minor optimization a NULL set
|
||||
* object will be treated as empty set for all read
|
||||
* operations. That way it is not necessary to instantiate an object
|
||||
* for each set use. */
|
||||
|
||||
#include "hashmap.h"
|
||||
|
||||
typedef struct Set Set;
|
||||
|
||||
Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
|
||||
void set_free(Set* s);
|
||||
void set_free_free(Set *s);
|
||||
Set* set_copy(Set *s);
|
||||
int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func);
|
||||
|
||||
int set_put(Set *s, void *value);
|
||||
int set_replace(Set *s, void *value);
|
||||
void *set_get(Set *s, void *value);
|
||||
void *set_remove(Set *s, void *value);
|
||||
int set_remove_and_put(Set *s, void *old_value, void *new_value);
|
||||
|
||||
int set_merge(Set *s, Set *other);
|
||||
void set_move(Set *s, Set *other);
|
||||
int set_move_one(Set *s, Set *other, void *value);
|
||||
|
||||
unsigned set_size(Set *s);
|
||||
bool set_isempty(Set *s);
|
||||
|
||||
void *set_iterate(Set *s, Iterator *i);
|
||||
void *set_iterate_backwards(Set *s, Iterator *i);
|
||||
void *set_iterate_skip(Set *s, void *value, Iterator *i);
|
||||
|
||||
void set_clear(Set *s);
|
||||
void *set_steal_first(Set *s);
|
||||
void* set_first(Set *s);
|
||||
void* set_last(Set *s);
|
||||
|
||||
#define SET_FOREACH(e, s, i) \
|
||||
for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
|
||||
|
||||
#define SET_FOREACH_BACKWARDS(e, s, i) \
|
||||
for ((i) = ITERATOR_LAST, (e) = set_iterate_backwards((s), &(i)); (e); (e) = set_iterate_backwards((s), &(i)))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,652 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdio.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
#include "socket-util.h"
|
||||
#include "missing.h"
|
||||
#include "label.h"
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s) {
|
||||
int r;
|
||||
char *e, *n;
|
||||
unsigned u;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
zero(*a);
|
||||
a->type = SOCK_STREAM;
|
||||
|
||||
if (*s == '[') {
|
||||
/* IPv6 in [x:.....:z]:p notation */
|
||||
|
||||
if (!socket_ipv6_is_supported()) {
|
||||
log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
if (!(e = strchr(s+1, ']')))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(n = strndup(s+1, e-s-1)))
|
||||
return -ENOMEM;
|
||||
|
||||
errno = 0;
|
||||
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
|
||||
free(n);
|
||||
return errno != 0 ? -errno : -EINVAL;
|
||||
}
|
||||
|
||||
free(n);
|
||||
|
||||
e++;
|
||||
if (*e != ':')
|
||||
return -EINVAL;
|
||||
|
||||
e++;
|
||||
if ((r = safe_atou(e, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
|
||||
} else if (*s == '/') {
|
||||
/* AF_UNIX socket */
|
||||
|
||||
size_t l;
|
||||
|
||||
l = strlen(s);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path))
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path, s, l);
|
||||
a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
|
||||
|
||||
} else if (*s == '@') {
|
||||
/* Abstract AF_UNIX socket */
|
||||
size_t l;
|
||||
|
||||
l = strlen(s+1);
|
||||
if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
|
||||
return -EINVAL;
|
||||
|
||||
a->sockaddr.un.sun_family = AF_UNIX;
|
||||
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
|
||||
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
|
||||
|
||||
} else {
|
||||
|
||||
if ((e = strchr(s, ':'))) {
|
||||
|
||||
if ((r = safe_atou(e+1, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(n = strndup(s, e-s)))
|
||||
return -ENOMEM;
|
||||
|
||||
/* IPv4 in w.x.y.z:p notation? */
|
||||
if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
|
||||
free(n);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (r > 0) {
|
||||
/* Gotcha, it's a traditional IPv4 address */
|
||||
free(n);
|
||||
|
||||
a->sockaddr.in4.sin_family = AF_INET;
|
||||
a->sockaddr.in4.sin_port = htons((uint16_t) u);
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
} else {
|
||||
unsigned idx;
|
||||
|
||||
if (strlen(n) > IF_NAMESIZE-1) {
|
||||
free(n);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Uh, our last resort, an interface name */
|
||||
idx = if_nametoindex(n);
|
||||
free(n);
|
||||
|
||||
if (idx == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!socket_ipv6_is_supported()) {
|
||||
log_warning("Binding to interface is not available since kernel does not support IPv6.");
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
|
||||
a->sockaddr.in6.sin6_scope_id = idx;
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Just a port */
|
||||
if ((r = safe_atou(s, &u)) < 0)
|
||||
return r;
|
||||
|
||||
if (u <= 0 || u > 0xFFFF)
|
||||
return -EINVAL;
|
||||
|
||||
if (socket_ipv6_is_supported()) {
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = htons((uint16_t) u);
|
||||
a->sockaddr.in6.sin6_addr = in6addr_any;
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
} else {
|
||||
a->sockaddr.in4.sin_family = AF_INET;
|
||||
a->sockaddr.in4.sin_port = htons((uint16_t) u);
|
||||
a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
|
||||
int family;
|
||||
unsigned group = 0;
|
||||
char* sfamily = NULL;
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
zero(*a);
|
||||
a->type = SOCK_RAW;
|
||||
|
||||
errno = 0;
|
||||
if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
|
||||
return errno ? -errno : -EINVAL;
|
||||
|
||||
if ((family = netlink_family_from_string(sfamily)) < 0)
|
||||
if (safe_atoi(sfamily, &family) < 0) {
|
||||
free(sfamily);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free(sfamily);
|
||||
|
||||
a->sockaddr.nl.nl_family = AF_NETLINK;
|
||||
a->sockaddr.nl.nl_groups = group;
|
||||
|
||||
a->type = SOCK_RAW;
|
||||
a->size = sizeof(struct sockaddr_nl);
|
||||
a->protocol = family;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_verify(const SocketAddress *a) {
|
||||
assert(a);
|
||||
|
||||
switch (socket_address_family(a)) {
|
||||
|
||||
case AF_INET:
|
||||
if (a->size != sizeof(struct sockaddr_in))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->sockaddr.in4.sin_port == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
case AF_INET6:
|
||||
if (a->size != sizeof(struct sockaddr_in6))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->sockaddr.in6.sin6_port == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
case AF_UNIX:
|
||||
if (a->size < offsetof(struct sockaddr_un, sun_path))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->size > offsetof(struct sockaddr_un, sun_path)) {
|
||||
|
||||
if (a->sockaddr.un.sun_path[0] != 0) {
|
||||
char *e;
|
||||
|
||||
/* path */
|
||||
if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
case AF_NETLINK:
|
||||
|
||||
if (a->size != sizeof(struct sockaddr_nl))
|
||||
return -EINVAL;
|
||||
|
||||
if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
}
|
||||
|
||||
int socket_address_print(const SocketAddress *a, char **p) {
|
||||
int r;
|
||||
assert(a);
|
||||
assert(p);
|
||||
|
||||
if ((r = socket_address_verify(a)) < 0)
|
||||
return r;
|
||||
|
||||
switch (socket_address_family(a)) {
|
||||
|
||||
case AF_INET: {
|
||||
char *ret;
|
||||
|
||||
if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
|
||||
free(ret);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
|
||||
*p = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case AF_INET6: {
|
||||
char *ret;
|
||||
|
||||
if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
ret[0] = '[';
|
||||
if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
|
||||
free(ret);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
|
||||
*p = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case AF_UNIX: {
|
||||
char *ret;
|
||||
|
||||
if (a->size <= offsetof(struct sockaddr_un, sun_path)) {
|
||||
|
||||
if (!(ret = strdup("<unnamed>")))
|
||||
return -ENOMEM;
|
||||
|
||||
} else if (a->sockaddr.un.sun_path[0] == 0) {
|
||||
/* abstract */
|
||||
|
||||
/* FIXME: We assume we can print the
|
||||
* socket path here and that it hasn't
|
||||
* more than one NUL byte. That is
|
||||
* actually an invalid assumption */
|
||||
|
||||
if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
|
||||
return -ENOMEM;
|
||||
|
||||
ret[0] = '@';
|
||||
memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
|
||||
ret[sizeof(a->sockaddr.un.sun_path)] = 0;
|
||||
|
||||
} else {
|
||||
|
||||
if (!(ret = strdup(a->sockaddr.un.sun_path)))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*p = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case AF_NETLINK: {
|
||||
const char *sfamily;
|
||||
|
||||
if ((sfamily = netlink_family_to_string(a->protocol)))
|
||||
r = asprintf(p, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
|
||||
else
|
||||
r = asprintf(p, "%i %u", a->protocol, a->sockaddr.nl.nl_groups);
|
||||
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int socket_address_listen(
|
||||
const SocketAddress *a,
|
||||
int backlog,
|
||||
SocketAddressBindIPv6Only only,
|
||||
const char *bind_to_device,
|
||||
bool free_bind,
|
||||
bool transparent,
|
||||
mode_t directory_mode,
|
||||
mode_t socket_mode,
|
||||
const char *label,
|
||||
int *ret) {
|
||||
|
||||
int r, fd, one;
|
||||
assert(a);
|
||||
assert(ret);
|
||||
|
||||
if ((r = socket_address_verify(a)) < 0)
|
||||
return r;
|
||||
|
||||
if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
r = label_socket_set(label);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, a->protocol);
|
||||
r = fd < 0 ? -errno : 0;
|
||||
|
||||
label_socket_clear();
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
|
||||
int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
|
||||
|
||||
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
|
||||
if (bind_to_device)
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
|
||||
goto fail;
|
||||
|
||||
if (free_bind) {
|
||||
one = 1;
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
|
||||
log_warning("IP_FREEBIND failed: %m");
|
||||
}
|
||||
|
||||
if (transparent) {
|
||||
one = 1;
|
||||
if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
|
||||
log_warning("IP_TRANSPARENT failed: %m");
|
||||
}
|
||||
}
|
||||
|
||||
one = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
|
||||
mode_t old_mask;
|
||||
|
||||
/* Create parents */
|
||||
mkdir_parents(a->sockaddr.un.sun_path, directory_mode);
|
||||
|
||||
/* Enforce the right access mode for the socket*/
|
||||
old_mask = umask(~ socket_mode);
|
||||
|
||||
/* Include the original umask in our mask */
|
||||
umask(~socket_mode | old_mask);
|
||||
|
||||
r = label_bind(fd, &a->sockaddr.sa, a->size);
|
||||
|
||||
if (r < 0 && errno == EADDRINUSE) {
|
||||
/* Unlink and try again */
|
||||
unlink(a->sockaddr.un.sun_path);
|
||||
r = bind(fd, &a->sockaddr.sa, a->size);
|
||||
}
|
||||
|
||||
umask(old_mask);
|
||||
} else
|
||||
r = bind(fd, &a->sockaddr.sa, a->size);
|
||||
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (socket_address_can_accept(a))
|
||||
if (listen(fd, backlog) < 0)
|
||||
goto fail;
|
||||
|
||||
*ret = fd;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
r = -errno;
|
||||
close_nointr_nofail(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool socket_address_can_accept(const SocketAddress *a) {
|
||||
assert(a);
|
||||
|
||||
return
|
||||
a->type == SOCK_STREAM ||
|
||||
a->type == SOCK_SEQPACKET;
|
||||
}
|
||||
|
||||
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
|
||||
/* Invalid addresses are unequal to all */
|
||||
if (socket_address_verify(a) < 0 ||
|
||||
socket_address_verify(b) < 0)
|
||||
return false;
|
||||
|
||||
if (a->type != b->type)
|
||||
return false;
|
||||
|
||||
if (a->size != b->size)
|
||||
return false;
|
||||
|
||||
if (socket_address_family(a) != socket_address_family(b))
|
||||
return false;
|
||||
|
||||
switch (socket_address_family(a)) {
|
||||
|
||||
case AF_INET:
|
||||
if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
case AF_UNIX:
|
||||
|
||||
if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.un.sun_path[0]) {
|
||||
if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
|
||||
return false;
|
||||
} else {
|
||||
if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AF_NETLINK:
|
||||
|
||||
if (a->protocol != b->protocol)
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Cannot compare, so we assume the addresses are different */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s, int type) {
|
||||
struct SocketAddress b;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
if (socket_address_parse(&b, s) < 0)
|
||||
return false;
|
||||
|
||||
b.type = type;
|
||||
|
||||
return socket_address_equal(a, &b);
|
||||
}
|
||||
|
||||
bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
|
||||
struct SocketAddress b;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
if (socket_address_parse_netlink(&b, s) < 0)
|
||||
return false;
|
||||
|
||||
return socket_address_equal(a, &b);
|
||||
}
|
||||
|
||||
bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
|
||||
assert(a);
|
||||
|
||||
if (socket_address_family(a) != AF_UNIX)
|
||||
return false;
|
||||
|
||||
if (a->sockaddr.un.sun_path[0] == 0)
|
||||
return false;
|
||||
|
||||
return path_startswith(a->sockaddr.un.sun_path, prefix);
|
||||
}
|
||||
|
||||
bool socket_ipv6_is_supported(void) {
|
||||
char *l = 0;
|
||||
bool enabled;
|
||||
|
||||
if (access("/sys/module/ipv6", F_OK) != 0)
|
||||
return 0;
|
||||
|
||||
/* If we can't check "disable" parameter, assume enabled */
|
||||
if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
|
||||
return 1;
|
||||
|
||||
/* If module was loaded with disable=1 no IPv6 available */
|
||||
enabled = l[0] == '0';
|
||||
free(l);
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static const char* const netlink_family_table[] = {
|
||||
[NETLINK_ROUTE] = "route",
|
||||
[NETLINK_FIREWALL] = "firewall",
|
||||
[NETLINK_INET_DIAG] = "inet-diag",
|
||||
[NETLINK_NFLOG] = "nflog",
|
||||
[NETLINK_XFRM] = "xfrm",
|
||||
[NETLINK_SELINUX] = "selinux",
|
||||
[NETLINK_ISCSI] = "iscsi",
|
||||
[NETLINK_AUDIT] = "audit",
|
||||
[NETLINK_FIB_LOOKUP] = "fib-lookup",
|
||||
[NETLINK_CONNECTOR] = "connector",
|
||||
[NETLINK_NETFILTER] = "netfilter",
|
||||
[NETLINK_IP6_FW] = "ip6-fw",
|
||||
[NETLINK_DNRTMSG] = "dnrtmsg",
|
||||
[NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
|
||||
[NETLINK_GENERIC] = "generic",
|
||||
[NETLINK_SCSITRANSPORT] = "scsitransport",
|
||||
[NETLINK_ECRYPTFS] = "ecryptfs"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(netlink_family, int);
|
||||
|
||||
static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
|
||||
[SOCKET_ADDRESS_DEFAULT] = "default",
|
||||
[SOCKET_ADDRESS_BOTH] = "both",
|
||||
[SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
|
||||
@@ -0,0 +1,102 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foosocketutilhfoo
|
||||
#define foosocketutilhfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/un.h>
|
||||
#include <net/if.h>
|
||||
#include <asm/types.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "util.h"
|
||||
|
||||
union sockaddr_union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in in4;
|
||||
struct sockaddr_in6 in6;
|
||||
struct sockaddr_un un;
|
||||
struct sockaddr_nl nl;
|
||||
struct sockaddr_storage storage;
|
||||
};
|
||||
|
||||
typedef struct SocketAddress {
|
||||
union sockaddr_union sockaddr;
|
||||
|
||||
/* We store the size here explicitly due to the weird
|
||||
* sockaddr_un semantics for abstract sockets */
|
||||
socklen_t size;
|
||||
|
||||
/* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
|
||||
int type;
|
||||
|
||||
/* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
|
||||
int protocol;
|
||||
} SocketAddress;
|
||||
|
||||
typedef enum SocketAddressBindIPv6Only {
|
||||
SOCKET_ADDRESS_DEFAULT,
|
||||
SOCKET_ADDRESS_BOTH,
|
||||
SOCKET_ADDRESS_IPV6_ONLY,
|
||||
_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
|
||||
_SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
|
||||
} SocketAddressBindIPv6Only;
|
||||
|
||||
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
|
||||
|
||||
int socket_address_parse(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s);
|
||||
int socket_address_print(const SocketAddress *a, char **p);
|
||||
int socket_address_verify(const SocketAddress *a);
|
||||
|
||||
bool socket_address_can_accept(const SocketAddress *a);
|
||||
|
||||
int socket_address_listen(
|
||||
const SocketAddress *a,
|
||||
int backlog,
|
||||
SocketAddressBindIPv6Only only,
|
||||
const char *bind_to_device,
|
||||
bool free_bind,
|
||||
bool transparent,
|
||||
mode_t directory_mode,
|
||||
mode_t socket_mode,
|
||||
const char *label,
|
||||
int *ret);
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s, int type);
|
||||
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
|
||||
|
||||
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
|
||||
|
||||
bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
|
||||
|
||||
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
|
||||
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
|
||||
|
||||
const char* netlink_family_to_string(int b);
|
||||
int netlink_family_from_string(const char *s);
|
||||
|
||||
bool socket_ipv6_is_supported(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,690 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "strv.h"
|
||||
|
||||
char *strv_find(char **l, const char *name) {
|
||||
char **i;
|
||||
|
||||
assert(name);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
if (streq(*i, name))
|
||||
return *i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *strv_find_prefix(char **l, const char *name) {
|
||||
char **i;
|
||||
|
||||
assert(name);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
if (startswith(*i, name))
|
||||
return *i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void strv_free(char **l) {
|
||||
char **k;
|
||||
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
for (k = l; *k; k++)
|
||||
free(*k);
|
||||
|
||||
free(l);
|
||||
}
|
||||
|
||||
char **strv_copy(char **l) {
|
||||
char **r, **k;
|
||||
|
||||
k = r = new(char*, strv_length(l)+1);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
if (l)
|
||||
for (; *l; k++, l++)
|
||||
if (!(*k = strdup(*l)))
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned strv_length(char **l) {
|
||||
unsigned n = 0;
|
||||
|
||||
if (!l)
|
||||
return 0;
|
||||
|
||||
for (; *l; l++)
|
||||
n++;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
char **strv_new_ap(const char *x, va_list ap) {
|
||||
const char *s;
|
||||
char **a;
|
||||
unsigned n = 0, i = 0;
|
||||
va_list aq;
|
||||
|
||||
if (x) {
|
||||
n = 1;
|
||||
|
||||
va_copy(aq, ap);
|
||||
while (va_arg(aq, const char*))
|
||||
n++;
|
||||
va_end(aq);
|
||||
}
|
||||
|
||||
if (!(a = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
if (x) {
|
||||
if (!(a[i] = strdup(x))) {
|
||||
free(a);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
while ((s = va_arg(ap, const char*))) {
|
||||
if (!(a[i] = strdup(s)))
|
||||
goto fail;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
a[i] = NULL;
|
||||
|
||||
return a;
|
||||
|
||||
fail:
|
||||
|
||||
for (; i > 0; i--)
|
||||
if (a[i-1])
|
||||
free(a[i-1]);
|
||||
|
||||
free(a);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **strv_new(const char *x, ...) {
|
||||
char **r;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, x);
|
||||
r = strv_new_ap(x, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_merge(char **a, char **b) {
|
||||
char **r, **k;
|
||||
|
||||
if (!a)
|
||||
return strv_copy(b);
|
||||
|
||||
if (!b)
|
||||
return strv_copy(a);
|
||||
|
||||
if (!(r = new(char*, strv_length(a)+strv_length(b)+1)))
|
||||
return NULL;
|
||||
|
||||
for (k = r; *a; k++, a++)
|
||||
if (!(*k = strdup(*a)))
|
||||
goto fail;
|
||||
for (; *b; k++, b++)
|
||||
if (!(*k = strdup(*b)))
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **strv_merge_concat(char **a, char **b, const char *suffix) {
|
||||
char **r, **k;
|
||||
|
||||
/* Like strv_merge(), but appends suffix to all strings in b, before adding */
|
||||
|
||||
if (!b)
|
||||
return strv_copy(a);
|
||||
|
||||
r = new(char*, strv_length(a) + strv_length(b) + 1);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
k = r;
|
||||
if (a)
|
||||
for (; *a; k++, a++) {
|
||||
*k = strdup(*a);
|
||||
if (!*k)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (; *b; k++, b++) {
|
||||
*k = strappend(*b, suffix);
|
||||
if (!*k)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
char **strv_split(const char *s, const char *separator) {
|
||||
char *state;
|
||||
char *w;
|
||||
size_t l;
|
||||
unsigned n, i;
|
||||
char **r;
|
||||
|
||||
assert(s);
|
||||
|
||||
n = 0;
|
||||
FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
|
||||
n++;
|
||||
|
||||
if (!(r = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
|
||||
if (!(r[i++] = strndup(w, l))) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r[i] = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_split_quoted(const char *s) {
|
||||
char *state;
|
||||
char *w;
|
||||
size_t l;
|
||||
unsigned n, i;
|
||||
char **r;
|
||||
|
||||
assert(s);
|
||||
|
||||
n = 0;
|
||||
FOREACH_WORD_QUOTED(w, l, s, state)
|
||||
n++;
|
||||
|
||||
if (!(r = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
FOREACH_WORD_QUOTED(w, l, s, state)
|
||||
if (!(r[i++] = cunescape_length(w, l))) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r[i] = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
char *strv_join(char **l, const char *separator) {
|
||||
char *r, *e;
|
||||
char **s;
|
||||
size_t n, k;
|
||||
|
||||
if (!separator)
|
||||
separator = " ";
|
||||
|
||||
k = strlen(separator);
|
||||
|
||||
n = 0;
|
||||
STRV_FOREACH(s, l) {
|
||||
if (n != 0)
|
||||
n += k;
|
||||
n += strlen(*s);
|
||||
}
|
||||
|
||||
if (!(r = new(char, n+1)))
|
||||
return NULL;
|
||||
|
||||
e = r;
|
||||
STRV_FOREACH(s, l) {
|
||||
if (e != r)
|
||||
e = stpcpy(e, separator);
|
||||
|
||||
e = stpcpy(e, *s);
|
||||
}
|
||||
|
||||
*e = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_append(char **l, const char *s) {
|
||||
char **r, **k;
|
||||
|
||||
if (!l)
|
||||
return strv_new(s, NULL);
|
||||
|
||||
if (!s)
|
||||
return strv_copy(l);
|
||||
|
||||
r = new(char*, strv_length(l)+2);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
for (k = r; *l; k++, l++)
|
||||
if (!(*k = strdup(*l)))
|
||||
goto fail;
|
||||
|
||||
if (!(*(k++) = strdup(s)))
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **strv_uniq(char **l) {
|
||||
char **i;
|
||||
|
||||
/* Drops duplicate entries. The first identical string will be
|
||||
* kept, the others dropped */
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
strv_remove(i+1, *i);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
char **strv_remove(char **l, const char *s) {
|
||||
char **f, **t;
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Drops every occurrence of s in the string list, edits
|
||||
* in-place. */
|
||||
|
||||
for (f = t = l; *f; f++) {
|
||||
|
||||
if (streq(*f, s)) {
|
||||
free(*f);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(t++) = *f;
|
||||
}
|
||||
|
||||
*t = NULL;
|
||||
return l;
|
||||
}
|
||||
|
||||
static int env_append(char **r, char ***k, char **a) {
|
||||
assert(r);
|
||||
assert(k);
|
||||
|
||||
if (!a)
|
||||
return 0;
|
||||
|
||||
/* Add the entries of a to *k unless they already exist in *r
|
||||
* in which case they are overridden instead. This assumes
|
||||
* there is enough space in the r array. */
|
||||
|
||||
for (; *a; a++) {
|
||||
char **j;
|
||||
size_t n;
|
||||
|
||||
n = strcspn(*a, "=");
|
||||
|
||||
if ((*a)[n] == '=')
|
||||
n++;
|
||||
|
||||
for (j = r; j < *k; j++)
|
||||
if (strncmp(*j, *a, n) == 0)
|
||||
break;
|
||||
|
||||
if (j >= *k)
|
||||
(*k)++;
|
||||
else
|
||||
free(*j);
|
||||
|
||||
if (!(*j = strdup(*a)))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **strv_env_merge(unsigned n_lists, ...) {
|
||||
size_t n = 0;
|
||||
char **l, **k, **r;
|
||||
va_list ap;
|
||||
unsigned i;
|
||||
|
||||
/* Merges an arbitrary number of environment sets */
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (i = 0; i < n_lists; i++) {
|
||||
l = va_arg(ap, char**);
|
||||
n += strv_length(l);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (!(r = new(char*, n+1)))
|
||||
return NULL;
|
||||
|
||||
k = r;
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (i = 0; i < n_lists; i++) {
|
||||
l = va_arg(ap, char**);
|
||||
if (env_append(r, &k, l) < 0)
|
||||
goto fail;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
*k = NULL;
|
||||
|
||||
return r;
|
||||
|
||||
fail:
|
||||
va_end(ap);
|
||||
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool env_match(const char *t, const char *pattern) {
|
||||
assert(t);
|
||||
assert(pattern);
|
||||
|
||||
/* pattern a matches string a
|
||||
* a matches a=
|
||||
* a matches a=b
|
||||
* a= matches a=
|
||||
* a=b matches a=b
|
||||
* a= does not match a
|
||||
* a=b does not match a=
|
||||
* a=b does not match a
|
||||
* a=b does not match a=c */
|
||||
|
||||
if (streq(t, pattern))
|
||||
return true;
|
||||
|
||||
if (!strchr(pattern, '=')) {
|
||||
size_t l = strlen(pattern);
|
||||
|
||||
return strncmp(t, pattern, l) == 0 && t[l] == '=';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char **strv_env_delete(char **x, unsigned n_lists, ...) {
|
||||
size_t n, i = 0;
|
||||
char **k, **r;
|
||||
va_list ap;
|
||||
|
||||
/* Deletes every entry from x that is mentioned in the other
|
||||
* string lists */
|
||||
|
||||
n = strv_length(x);
|
||||
|
||||
r = new(char*, n+1);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
STRV_FOREACH(k, x) {
|
||||
unsigned v;
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (v = 0; v < n_lists; v++) {
|
||||
char **l, **j;
|
||||
|
||||
l = va_arg(ap, char**);
|
||||
STRV_FOREACH(j, l)
|
||||
if (env_match(*k, *j))
|
||||
goto skip;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
r[i] = strdup(*k);
|
||||
if (!r[i]) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i++;
|
||||
continue;
|
||||
|
||||
skip:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
r[i] = NULL;
|
||||
|
||||
assert(i <= n);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_env_unset(char **l, const char *p) {
|
||||
|
||||
char **f, **t;
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* Drops every occurrence of the env var setting p in the
|
||||
* string list. edits in-place. */
|
||||
|
||||
for (f = t = l; *f; f++) {
|
||||
|
||||
if (env_match(*f, p)) {
|
||||
free(*f);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(t++) = *f;
|
||||
}
|
||||
|
||||
*t = NULL;
|
||||
return l;
|
||||
}
|
||||
|
||||
char **strv_env_set(char **x, const char *p) {
|
||||
|
||||
char **k, **r;
|
||||
char* m[2] = { (char*) p, NULL };
|
||||
|
||||
/* Overrides the env var setting of p, returns a new copy */
|
||||
|
||||
if (!(r = new(char*, strv_length(x)+2)))
|
||||
return NULL;
|
||||
|
||||
k = r;
|
||||
if (env_append(r, &k, x) < 0)
|
||||
goto fail;
|
||||
|
||||
if (env_append(r, &k, m) < 0)
|
||||
goto fail;
|
||||
|
||||
*k = NULL;
|
||||
|
||||
return r;
|
||||
|
||||
fail:
|
||||
for (k--; k >= r; k--)
|
||||
free(*k);
|
||||
|
||||
free(r);
|
||||
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
char *strv_env_get_with_length(char **l, const char *name, size_t k) {
|
||||
char **i;
|
||||
|
||||
assert(name);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
if (strncmp(*i, name, k) == 0 &&
|
||||
(*i)[k] == '=')
|
||||
return *i + k + 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *strv_env_get(char **l, const char *name) {
|
||||
return strv_env_get_with_length(l, name, strlen(name));
|
||||
}
|
||||
|
||||
char **strv_env_clean(char **l) {
|
||||
char **r, **ret;
|
||||
|
||||
for (r = ret = l; *l; l++) {
|
||||
const char *equal;
|
||||
|
||||
equal = strchr(*l, '=');
|
||||
|
||||
if (equal && equal[1] == 0) {
|
||||
free(*l);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(r++) = *l;
|
||||
}
|
||||
|
||||
*r = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char **strv_parse_nulstr(const char *s, size_t l) {
|
||||
const char *p;
|
||||
unsigned c = 0, i = 0;
|
||||
char **v;
|
||||
|
||||
assert(s || l <= 0);
|
||||
|
||||
if (l <= 0)
|
||||
return strv_new(NULL, NULL);
|
||||
|
||||
for (p = s; p < s + l; p++)
|
||||
if (*p == 0)
|
||||
c++;
|
||||
|
||||
if (s[l-1] != 0)
|
||||
c++;
|
||||
|
||||
if (!(v = new0(char*, c+1)))
|
||||
return NULL;
|
||||
|
||||
p = s;
|
||||
while (p < s + l) {
|
||||
const char *e;
|
||||
|
||||
e = memchr(p, 0, s + l - p);
|
||||
|
||||
if (!(v[i++] = strndup(p, e ? e - p : s + l - p))) {
|
||||
strv_free(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!e)
|
||||
break;
|
||||
|
||||
p = e + 1;
|
||||
}
|
||||
|
||||
assert(i == c);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
bool strv_overlap(char **a, char **b) {
|
||||
char **i, **j;
|
||||
|
||||
STRV_FOREACH(i, a) {
|
||||
STRV_FOREACH(j, b) {
|
||||
if (streq(*i, *j))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foostrvhfoo
|
||||
#define foostrvhfoo
|
||||
|
||||
/***
|
||||
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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
char *strv_find(char **l, const char *name);
|
||||
char *strv_find_prefix(char **l, const char *name);
|
||||
|
||||
void strv_free(char **l);
|
||||
char **strv_copy(char **l) _malloc_;
|
||||
unsigned strv_length(char **l);
|
||||
|
||||
char **strv_merge(char **a, char **b);
|
||||
char **strv_merge_concat(char **a, char **b, const char *suffix);
|
||||
char **strv_append(char **l, const char *s);
|
||||
|
||||
char **strv_remove(char **l, const char *s);
|
||||
char **strv_uniq(char **l);
|
||||
|
||||
#define strv_contains(l, s) (!!strv_find((l), (s)))
|
||||
|
||||
char **strv_new(const char *x, ...) _sentinel_ _malloc_;
|
||||
char **strv_new_ap(const char *x, va_list ap) _malloc_;
|
||||
|
||||
static inline bool strv_isempty(char **l) {
|
||||
return !l || !*l;
|
||||
}
|
||||
|
||||
char **strv_split(const char *s, const char *separator) _malloc_;
|
||||
char **strv_split_quoted(const char *s) _malloc_;
|
||||
|
||||
char *strv_join(char **l, const char *separator) _malloc_;
|
||||
|
||||
char **strv_env_merge(unsigned n_lists, ...);
|
||||
char **strv_env_delete(char **x, unsigned n_lists, ...);
|
||||
|
||||
char **strv_env_set(char **x, const char *p);
|
||||
char **strv_env_unset(char **l, const char *p);
|
||||
|
||||
char *strv_env_get_with_length(char **l, const char *name, size_t k);
|
||||
char *strv_env_get(char **x, const char *n);
|
||||
|
||||
char **strv_env_clean(char **l);
|
||||
|
||||
char **strv_parse_nulstr(const char *s, size_t l);
|
||||
|
||||
bool strv_overlap(char **a, char **b);
|
||||
|
||||
#define STRV_FOREACH(s, l) \
|
||||
for ((s) = (l); (s) && *(s); (s)++)
|
||||
|
||||
#define STRV_FOREACH_BACKWARDS(s, l) \
|
||||
for (; (l) && ((s) >= (l)); (s)--)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,214 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2012 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/* This file is based on the GLIB utf8 validation functions. The
|
||||
* original license text follows. */
|
||||
|
||||
/* gutf8.c - Operations on UTF-8 strings.
|
||||
*
|
||||
* Copyright (C) 1999 Tom Tromey
|
||||
* Copyright (C) 2000 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "utf8.h"
|
||||
|
||||
#define FILTER_CHAR '_'
|
||||
|
||||
static inline bool is_unicode_valid(uint32_t ch) {
|
||||
|
||||
if (ch >= 0x110000) /* End of unicode space */
|
||||
return false;
|
||||
if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
|
||||
return false;
|
||||
if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
|
||||
return false;
|
||||
if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool is_continuation_char(uint8_t ch) {
|
||||
if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
|
||||
*u_ch <<= 6;
|
||||
*u_ch |= ch & 0x3f;
|
||||
}
|
||||
|
||||
static char* utf8_validate(const char *str, char *output) {
|
||||
uint32_t val = 0;
|
||||
uint32_t min = 0;
|
||||
const uint8_t *p, *last;
|
||||
int size;
|
||||
uint8_t *o;
|
||||
|
||||
assert(str);
|
||||
|
||||
o = (uint8_t*) output;
|
||||
for (p = (const uint8_t*) str; *p; p++) {
|
||||
if (*p < 128) {
|
||||
if (o)
|
||||
*o = *p;
|
||||
} else {
|
||||
last = p;
|
||||
|
||||
if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
|
||||
size = 2;
|
||||
min = 128;
|
||||
val = (uint32_t) (*p & 0x1e);
|
||||
goto ONE_REMAINING;
|
||||
} else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
|
||||
size = 3;
|
||||
min = (1 << 11);
|
||||
val = (uint32_t) (*p & 0x0f);
|
||||
goto TWO_REMAINING;
|
||||
} else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
|
||||
size = 4;
|
||||
min = (1 << 16);
|
||||
val = (uint32_t) (*p & 0x07);
|
||||
} else
|
||||
goto error;
|
||||
|
||||
p++;
|
||||
if (!is_continuation_char(*p))
|
||||
goto error;
|
||||
merge_continuation_char(&val, *p);
|
||||
|
||||
TWO_REMAINING:
|
||||
p++;
|
||||
if (!is_continuation_char(*p))
|
||||
goto error;
|
||||
merge_continuation_char(&val, *p);
|
||||
|
||||
ONE_REMAINING:
|
||||
p++;
|
||||
if (!is_continuation_char(*p))
|
||||
goto error;
|
||||
merge_continuation_char(&val, *p);
|
||||
|
||||
if (val < min)
|
||||
goto error;
|
||||
|
||||
if (!is_unicode_valid(val))
|
||||
goto error;
|
||||
|
||||
if (o) {
|
||||
memcpy(o, last, (size_t) size);
|
||||
o += size;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
error:
|
||||
if (o) {
|
||||
*o = FILTER_CHAR;
|
||||
p = last; /* We retry at the next character */
|
||||
} else
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (o)
|
||||
o++;
|
||||
}
|
||||
|
||||
if (o) {
|
||||
*o = '\0';
|
||||
return output;
|
||||
}
|
||||
|
||||
return (char*) str;
|
||||
|
||||
failure:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* utf8_is_valid (const char *str) {
|
||||
return utf8_validate(str, NULL);
|
||||
}
|
||||
|
||||
char* utf8_filter (const char *str) {
|
||||
char *new_str;
|
||||
|
||||
assert(str);
|
||||
|
||||
new_str = malloc(strlen(str) + 1);
|
||||
if (!new_str)
|
||||
return NULL;
|
||||
|
||||
return utf8_validate(str, new_str);
|
||||
}
|
||||
|
||||
char *ascii_is_valid(const char *str) {
|
||||
const char *p;
|
||||
|
||||
assert(str);
|
||||
|
||||
for (p = str; *p; p++)
|
||||
if ((unsigned char) *p >= 128)
|
||||
return NULL;
|
||||
|
||||
return (char*) str;
|
||||
}
|
||||
|
||||
char *ascii_filter(const char *str) {
|
||||
char *r, *s, *d;
|
||||
size_t l;
|
||||
|
||||
assert(str);
|
||||
|
||||
l = strlen(str);
|
||||
r = malloc(l + 1);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
for (s = r, d = r; *s; s++)
|
||||
if ((unsigned char) *s < 128)
|
||||
*(d++) = *s;
|
||||
|
||||
*d = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef fooutf8hfoo
|
||||
#define fooutf8hfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2012 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
char *utf8_is_valid(const char *s) _pure_;
|
||||
char *ascii_is_valid(const char *s) _pure_;
|
||||
|
||||
char *utf8_filter(const char *s);
|
||||
char *ascii_filter(const char *s);
|
||||
|
||||
#endif
|
||||
+6123
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,536 @@
|
||||
/*-*- 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 General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 "macro.h"
|
||||
|
||||
typedef uint64_t usec_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 USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
|
||||
#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
|
||||
#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
|
||||
#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
|
||||
#define USEC_PER_YEAR (31557600ULL*USEC_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_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 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 is_path_absolute(const char *p) {
|
||||
return *p == '/';
|
||||
}
|
||||
|
||||
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_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)))
|
||||
|
||||
char **split_path_and_make_absolute(const char *p);
|
||||
|
||||
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);
|
||||
|
||||
char *file_name_from_path(const char *p);
|
||||
bool is_path(const char *p);
|
||||
|
||||
bool path_is_absolute(const char *p);
|
||||
char *path_make_absolute(const char *p, const char *prefix);
|
||||
char *path_make_absolute_cwd(const char *p);
|
||||
|
||||
char **strv_path_make_absolute_cwd(char **l);
|
||||
char **strv_path_canonicalize(char **l);
|
||||
char **strv_path_remove_empty(char **l);
|
||||
|
||||
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 safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||
int mkdir_parents(const char *path, mode_t mode);
|
||||
int mkdir_p(const char *path, mode_t mode);
|
||||
|
||||
int parent_of_path(const char *path, char **parent);
|
||||
|
||||
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 *path_kill_slashes(char *path);
|
||||
|
||||
bool path_startswith(const char *path, const char *prefix);
|
||||
bool path_equal(const char *a, const char *b);
|
||||
|
||||
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);
|
||||
|
||||
int path_is_mount_point(const char *path, bool allow_symlink);
|
||||
|
||||
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);
|
||||
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(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);
|
||||
|
||||
void filter_environ(const char *prefix);
|
||||
|
||||
bool tty_is_vc(const char *tty);
|
||||
bool tty_is_vc_resolve(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);
|
||||
|
||||
int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
|
||||
|
||||
int hwclock_is_localtime(void);
|
||||
int hwclock_apply_localtime_delta(int *min);
|
||||
int hwclock_reset_localtime_delta(void);
|
||||
int hwclock_get_time(struct tm *tm);
|
||||
int hwclock_set_time(const struct tm *tm);
|
||||
|
||||
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 rtc_open(int flags);
|
||||
|
||||
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);
|
||||
#endif
|
||||
@@ -0,0 +1,292 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2011 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "virt.h"
|
||||
|
||||
/* Returns a short identifier for the various VM implementations */
|
||||
int detect_vm(const char **id) {
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
/* Both CPUID and DMI are x86 specific interfaces... */
|
||||
|
||||
static const char *const dmi_vendors[] = {
|
||||
"/sys/class/dmi/id/sys_vendor",
|
||||
"/sys/class/dmi/id/board_vendor",
|
||||
"/sys/class/dmi/id/bios_vendor"
|
||||
};
|
||||
|
||||
static const char dmi_vendor_table[] =
|
||||
"QEMU\0" "qemu\0"
|
||||
/* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
|
||||
"VMware\0" "vmware\0"
|
||||
"VMW\0" "vmware\0"
|
||||
"Microsoft Corporation\0" "microsoft\0"
|
||||
"innotek GmbH\0" "oracle\0"
|
||||
"Xen\0" "xen\0"
|
||||
"Bochs\0" "bochs\0";
|
||||
|
||||
static const char cpuid_vendor_table[] =
|
||||
"XenVMMXenVMM\0" "xen\0"
|
||||
"KVMKVMKVM\0" "kvm\0"
|
||||
/* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
|
||||
"VMwareVMware\0" "vmware\0"
|
||||
/* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
|
||||
"Microsoft Hv\0" "microsoft\0";
|
||||
|
||||
uint32_t eax, ecx;
|
||||
union {
|
||||
uint32_t sig32[3];
|
||||
char text[13];
|
||||
} sig;
|
||||
unsigned i;
|
||||
const char *j, *k;
|
||||
bool hypervisor;
|
||||
|
||||
/* http://lwn.net/Articles/301888/ */
|
||||
zero(sig);
|
||||
|
||||
#if defined (__i386__)
|
||||
#define REG_a "eax"
|
||||
#define REG_b "ebx"
|
||||
#elif defined (__amd64__)
|
||||
#define REG_a "rax"
|
||||
#define REG_b "rbx"
|
||||
#endif
|
||||
|
||||
/* First detect whether there is a hypervisor */
|
||||
eax = 1;
|
||||
__asm__ __volatile__ (
|
||||
/* ebx/rbx is being used for PIC! */
|
||||
" push %%"REG_b" \n\t"
|
||||
" cpuid \n\t"
|
||||
" pop %%"REG_b" \n\t"
|
||||
|
||||
: "=a" (eax), "=c" (ecx)
|
||||
: "0" (eax)
|
||||
);
|
||||
|
||||
hypervisor = !!(ecx & 0x80000000U);
|
||||
|
||||
if (hypervisor) {
|
||||
|
||||
/* There is a hypervisor, see what it is */
|
||||
eax = 0x40000000U;
|
||||
__asm__ __volatile__ (
|
||||
/* ebx/rbx is being used for PIC! */
|
||||
" push %%"REG_b" \n\t"
|
||||
" cpuid \n\t"
|
||||
" mov %%ebx, %1 \n\t"
|
||||
" pop %%"REG_b" \n\t"
|
||||
|
||||
: "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
|
||||
: "0" (eax)
|
||||
);
|
||||
|
||||
NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
|
||||
if (streq(sig.text, j)) {
|
||||
|
||||
if (id)
|
||||
*id = k;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
|
||||
char *s;
|
||||
int r;
|
||||
const char *found = NULL;
|
||||
|
||||
if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
|
||||
if (r != -ENOENT)
|
||||
return r;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
|
||||
if (startswith(s, j))
|
||||
found = k;
|
||||
free(s);
|
||||
|
||||
if (found) {
|
||||
if (id)
|
||||
*id = found;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (hypervisor) {
|
||||
if (id)
|
||||
*id = "other";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int detect_container(const char **id) {
|
||||
FILE *f;
|
||||
|
||||
/* Unfortunately many of these operations require root access
|
||||
* in one way or another */
|
||||
|
||||
if (geteuid() != 0)
|
||||
return -EPERM;
|
||||
|
||||
if (running_in_chroot() > 0) {
|
||||
|
||||
if (id)
|
||||
*id = "chroot";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* /proc/vz exists in container and outside of the container,
|
||||
* /proc/bc only outside of the container. */
|
||||
if (access("/proc/vz", F_OK) >= 0 &&
|
||||
access("/proc/bc", F_OK) < 0) {
|
||||
|
||||
if (id)
|
||||
*id = "openvz";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
f = fopen("/proc/1/environ", "re");
|
||||
if (f) {
|
||||
bool done = false;
|
||||
|
||||
do {
|
||||
char line[LINE_MAX];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < sizeof(line)-1; i++) {
|
||||
int c;
|
||||
|
||||
c = getc(f);
|
||||
if (_unlikely_(c == EOF)) {
|
||||
done = true;
|
||||
break;
|
||||
} else if (c == 0)
|
||||
break;
|
||||
|
||||
line[i] = c;
|
||||
}
|
||||
line[i] = 0;
|
||||
|
||||
if (streq(line, "container=lxc")) {
|
||||
fclose(f);
|
||||
|
||||
if (id)
|
||||
*id = "lxc";
|
||||
return 1;
|
||||
|
||||
} else if (streq(line, "container=lxc-libvirt")) {
|
||||
fclose(f);
|
||||
|
||||
if (id)
|
||||
*id = "lxc-libvirt";
|
||||
return 1;
|
||||
|
||||
} else if (streq(line, "container=systemd-nspawn")) {
|
||||
fclose(f);
|
||||
|
||||
if (id)
|
||||
*id = "systemd-nspawn";
|
||||
return 1;
|
||||
|
||||
} else if (startswith(line, "container=")) {
|
||||
fclose(f);
|
||||
|
||||
if (id)
|
||||
*id = "other";
|
||||
return 1;
|
||||
}
|
||||
|
||||
} while (!done);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns a short identifier for the various VM/container implementations */
|
||||
Virtualization detect_virtualization(const char **id) {
|
||||
|
||||
static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
|
||||
static __thread const char *cached_id = NULL;
|
||||
|
||||
const char *_id;
|
||||
int r;
|
||||
Virtualization v;
|
||||
|
||||
if (_likely_(cached_virt >= 0)) {
|
||||
|
||||
if (id && cached_virt > 0)
|
||||
*id = cached_id;
|
||||
|
||||
return cached_virt;
|
||||
}
|
||||
|
||||
r = detect_container(&_id);
|
||||
if (r < 0) {
|
||||
v = r;
|
||||
goto finish;
|
||||
} else if (r > 0) {
|
||||
v = VIRTUALIZATION_CONTAINER;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = detect_vm(&_id);
|
||||
if (r < 0) {
|
||||
v = r;
|
||||
goto finish;
|
||||
} else if (r > 0) {
|
||||
v = VIRTUALIZATION_VM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
v = VIRTUALIZATION_NONE;
|
||||
|
||||
finish:
|
||||
if (v > 0) {
|
||||
cached_id = _id;
|
||||
|
||||
if (id)
|
||||
*id = _id;
|
||||
}
|
||||
|
||||
if (v >= 0)
|
||||
cached_virt = v;
|
||||
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foovirthfoo
|
||||
#define foovirthfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2011 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 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
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
int detect_vm(const char **id);
|
||||
int detect_container(const char **id);
|
||||
|
||||
typedef enum Virtualization {
|
||||
VIRTUALIZATION_NONE = 0,
|
||||
VIRTUALIZATION_VM,
|
||||
VIRTUALIZATION_CONTAINER,
|
||||
_VIRTUALIZATION_MAX,
|
||||
_VIRTUALIZATION_INVALID = -1
|
||||
} Virtualization;
|
||||
|
||||
Virtualization detect_virtualization(const char **id);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user