Merge pull request #10701 from poettering/analyze-security

systemd-analyze: add a new "security" verb for analyzing unit sandboxing options
This commit is contained in:
Lennart Poettering
2018-11-30 19:41:59 +01:00
committed by GitHub
17 changed files with 2521 additions and 81 deletions
+7 -3
View File
@@ -25,6 +25,10 @@ Features:
* when we fork off generators and such, lower LIMIT_NOFILE soft limit to 1K
* rework seccomp/nnp logic that that even if User= is used in combination with
a seccomp option we don't have to set NNP. For that, change uid first whil
keeping CAP_SYS_ADMIN, then apply seccomp, the drop cap.
* add a concept for automatically loading per-unit secrets off disk and
inserting them into the kernel keyring. Maybe SecretsDirectory= similar to
ConfigurationDirectory=.
@@ -49,6 +53,9 @@ Features:
* set memory.oom.group in cgroupsv2 for all leaf cgroups (kernel v4.19+)
* add a new syscall group "@esoteric" for more esoteric stuff such as bpf() and
usefaultd() and make systemd-analyze check for it.
* drop umask() calls and suchlike from our generators, pid1 should set things up correctly anyway
* paranoia: whenever we process passwords, call mlock() on the memory
@@ -290,9 +297,6 @@ Features:
* beef up pam_systemd to take unit file settings such as cgroups properties as
parameters
* a new "systemd-analyze security" tool outputting a checklist of security
features a service does and does not implement
* maybe hook of xfs/ext4 quotactl() with services? i.e. automatically manage
the quota of a the user indicated in User= via unit file settings, like the
other resource management concepts. Would mix nicely with DynamicUser=1. Or
+6 -1
View File
@@ -46,9 +46,14 @@ All tools:
are understood, too (us, ms, s, min, h, d, w, month, y). If it is not set or set
to 0, then the built-in default is used.
* `$SYSTEMD_MEMPOOL=0` — if set the internal memory caching logic employed by
* `$SYSTEMD_MEMPOOL=0` — if set, the internal memory caching logic employed by
hash tables is turned off, and libc malloc() is used for all allocations.
* `$SYSTEMD_EMOJI=0` — if set, tools such as "systemd-analyze security" will
not output graphical smiley emojis, but ASCII alternatives instead. Note that
this only controls use of Unicode emoji glyphs, and has no effect on other
Unicode glyphs.
systemctl:
* `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID1's private D-Bus
+29
View File
@@ -112,6 +112,12 @@
<arg choice="plain">timespan</arg>
<arg choice="plain" rep="repeat"><replaceable>SPAN</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">security</arg>
<arg choice="plain" rep="repeat"><replaceable>UNIT</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
@@ -263,6 +269,29 @@ NAutoVTs=8
The time span should adhere to the same syntax documented in <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
Values without associated magnitudes are parsed as seconds.</para>
<para><command>systemd-analyze security</command> analyzes the security and sandboxing settings of one or more
specified service units. If at least one unit name is specified the security settings of the specified service
units are inspected and a detailed analysis is shown. If no unit name is specified, all currently loaded,
long-running service units are inspected and a terse table with results shown. The command checks for various
security-related service settings, assigning each a numeric "exposure level" value, depending on how important a
setting is. It then calculates an overall exposure level for the whole unit, which is an estimation in the range
0.0…10.0 indicating how exposed a service is security-wise. High exposure levels indicate very little applied
sandboxing. Low exposure levels indicate tight sandboxing and strongest security restrictions. Note that this only
analyzes the per-service security features systemd itself implements. This means that any additional security
mechanisms applied by the service code itself are not accounted for. The exposure level determined this way should
not be misunderstood: a high exposure level neither means that there is no effective sandboxing applied by the
service code itself, nor that the service is actually vulnerable to remote or local attacks. High exposure levels
do indicate however that most likely the service might benefit from additional settings applied to them. Please
note that many of the security and sandboxing settings individually can be circumvented — unless combined with
others. For example, if a service retains the privilege to establish or undo mount points many of the sandboxing
options can be undone by the service code itself. Due to that is essential that each service uses the most
comprehensive and strict sandboxing and security settings possible. The tool will take into account some of these
combinations and relationships between the settings, but not all. Also note that the security and sandboxing
settings analyzed here only apply to the operations executed by the service code itself. If a service has access to
an IPC system (such as D-Bus) it might request operations from other services that are not subject to the same
restrictions. Any comprehensive security and sandboxing analysis is hence incomplete if the IPC access policy is
not validated too.</para>
<para>If no command is passed, <command>systemd-analyze
time</command> is implied.</para>
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "sd-bus.h"
typedef enum AnalyzeSecurityFlags {
ANALYZE_SECURITY_SHORT = 1 << 0,
ANALYZE_SECURITY_ONLY_LOADED = 1 << 1,
ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2,
} AnalyzeSecurityFlags;
int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags);
+26 -9
View File
@@ -12,14 +12,15 @@
#include "sd-bus.h"
#include "alloc-util.h"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "bus-error.h"
#include "bus-unit-util.h"
#include "bus-util.h"
#include "calendarspec.h"
#include "def.h"
#include "conf-files.h"
#include "copy.h"
#include "def.h"
#include "fd-util.h"
#include "fileio.h"
#include "glob-util.h"
@@ -169,7 +170,7 @@ static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *in
't', val);
if (r < 0)
return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, -r));
return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r));
return 0;
}
@@ -192,7 +193,7 @@ static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char
&error,
strv);
if (r < 0)
return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
return 0;
}
@@ -355,7 +356,7 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
&error, &reply,
NULL);
if (r < 0)
return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, -r));
return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
if (r < 0)
@@ -978,7 +979,7 @@ static int list_dependencies(sd_bus *bus, const char *name) {
&reply,
"s");
if (r < 0)
return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, -r));
return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
r = sd_bus_message_read(reply, "s", &id);
if (r < 0)
@@ -1238,7 +1239,7 @@ static int dot(int argc, char *argv[], void *userdata) {
&reply,
"");
if (r < 0)
log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, -r));
log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
if (r < 0)
@@ -1340,15 +1341,16 @@ static int dump(int argc, char *argv[], void *userdata) {
}
static int cat_config(int argc, char *argv[], void *userdata) {
char **arg;
char **arg, **list;
int r;
(void) pager_open(arg_pager_flags);
STRV_FOREACH(arg, argv + 1) {
list = strv_skip(argv, 1);
STRV_FOREACH(arg, list) {
const char *t = NULL;
if (arg != argv + 1)
if (arg != list)
print_separator();
if (path_is_absolute(*arg)) {
@@ -1783,6 +1785,19 @@ static int do_verify(int argc, char *argv[], void *userdata) {
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
}
static int do_security(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
r = acquire_bus(&bus, NULL);
if (r < 0)
return log_error_errno(r, "Failed to create bus connection: %m");
(void) pager_open(arg_pager_flags);
return analyze_security(bus, strv_skip(argv, 1), 0);
}
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@@ -1827,6 +1842,7 @@ static int help(int argc, char *argv[], void *userdata) {
" calendar SPEC... Validate repetitive calendar time events\n"
" service-watchdogs [BOOL] Get/set service watchdog state\n"
" timespan SPAN... Validate a time span\n"
" security [UNIT...] Analyze security of unit\n"
"\nSee the %s for details.\n"
, program_invocation_short_name
, link
@@ -2014,6 +2030,7 @@ static int run(int argc, char *argv[]) {
{ "calendar", 2, VERB_ANY, 0, test_calendar },
{ "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
{ "timespan", 2, VERB_ANY, 0, dump_timespan },
{ "security", VERB_ANY, VERB_ANY, 0, do_security },
{}
};
+2
View File
@@ -4,4 +4,6 @@ systemd_analyze_sources = files('''
analyze.c
analyze-verify.c
analyze-verify.h
analyze-security.c
analyze-security.h
'''.split())
+62 -27
View File
@@ -16,6 +16,7 @@
#include "def.h"
#include "dirent-util.h"
#include "env-util.h"
#include "fd-util.h"
#include "hashmap.h"
#include "locale-util.h"
@@ -347,6 +348,24 @@ bool keymap_is_valid(const char *name) {
return true;
}
static bool emoji_enabled(void) {
static int cached_emoji_enabled = -1;
if (cached_emoji_enabled < 0) {
int val;
val = getenv_bool("SYSTEMD_EMOJI");
if (val < 0)
cached_emoji_enabled =
is_locale_utf8() &&
!STRPTR_IN_SET(getenv("TERM"), "dumb", "linux");
else
cached_emoji_enabled = val;
}
return cached_emoji_enabled;
}
const char *special_glyph(SpecialGlyph code) {
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
@@ -359,40 +378,56 @@ const char *special_glyph(SpecialGlyph code) {
static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = {
/* ASCII fallback */
[false] = {
[TREE_VERTICAL] = "| ",
[TREE_BRANCH] = "|-",
[TREE_RIGHT] = "`-",
[TREE_SPACE] = " ",
[TRIANGULAR_BULLET] = ">",
[BLACK_CIRCLE] = "*",
[BULLET] = "*",
[ARROW] = "->",
[MDASH] = "-",
[ELLIPSIS] = "...",
[MU] = "u",
[CHECK_MARK] = "+",
[CROSS_MARK] = "-",
[TREE_VERTICAL] = "| ",
[TREE_BRANCH] = "|-",
[TREE_RIGHT] = "`-",
[TREE_SPACE] = " ",
[TRIANGULAR_BULLET] = ">",
[BLACK_CIRCLE] = "*",
[BULLET] = "*",
[ARROW] = "->",
[MDASH] = "-",
[ELLIPSIS] = "...",
[MU] = "u",
[CHECK_MARK] = "+",
[CROSS_MARK] = "-",
[ECSTATIC_SMILEY] = ":-]",
[HAPPY_SMILEY] = ":-}",
[SLIGHTLY_HAPPY_SMILEY] = ":-)",
[NEUTRAL_SMILEY] = ":-|",
[SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
[UNHAPPY_SMILEY] = ":-{️",
[DEPRESSED_SMILEY] = ":-[",
},
/* UTF-8 */
[true] = {
[TREE_VERTICAL] = "\342\224\202 ", /* │ */
[TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
[TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
[TREE_SPACE] = " ", /* */
[TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
[BLACK_CIRCLE] = "\342\227\217", /* ● */
[BULLET] = "\342\200\242", /* • */
[ARROW] = "\342\206\222", /* → */
[MDASH] = "\342\200\223", /* – */
[ELLIPSIS] = "\342\200\246", /* … */
[MU] = "\316\274", /* μ */
[CHECK_MARK] = "\342\234\223", /* ✓ */
[CROSS_MARK] = "\342\234\227", /* ✗ */
[TREE_VERTICAL] = "\342\224\202 ", /* │ */
[TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
[TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
[TREE_SPACE] = " ", /* */
[TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */
[BLACK_CIRCLE] = "\342\227\217", /* ● */
[BULLET] = "\342\200\242", /* • */
[ARROW] = "\342\206\222", /* → */
[MDASH] = "\342\200\223", /* – */
[ELLIPSIS] = "\342\200\246", /* … */
[MU] = "\316\274", /* μ */
[CHECK_MARK] = "\342\234\223", /* ✓ */
[CROSS_MARK] = "\342\234\227", /* ✗ */
[ECSTATIC_SMILEY] = "\360\237\230\207", /* 😇 */
[HAPPY_SMILEY] = "\360\237\230\200", /* 😀 */
[SLIGHTLY_HAPPY_SMILEY] = "\360\237\231\202", /* 🙂 */
[NEUTRAL_SMILEY] = "\360\237\230\220", /* 😐 */
[SLIGHTLY_UNHAPPY_SMILEY] = "\360\237\231\201", /* 🙁 */
[UNHAPPY_SMILEY] = "\360\237\230\250", /* 😨️️ */
[DEPRESSED_SMILEY] = "\360\237\244\242", /* 🤢 */
},
};
return draw_table[is_locale_utf8()][code];
assert(code < _SPECIAL_GLYPH_MAX);
return draw_table[code >= _SPECIAL_GLYPH_FIRST_SMILEY ? emoji_enabled() : is_locale_utf8()][code];
}
void locale_variables_free(char*l[_VARIABLE_LC_MAX]) {
+8
View File
@@ -52,6 +52,14 @@ typedef enum {
MU,
CHECK_MARK,
CROSS_MARK,
_SPECIAL_GLYPH_FIRST_SMILEY,
ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_SMILEY,
HAPPY_SMILEY,
SLIGHTLY_HAPPY_SMILEY,
NEUTRAL_SMILEY,
SLIGHTLY_UNHAPPY_SMILEY,
UNHAPPY_SMILEY,
DEPRESSED_SMILEY,
_SPECIAL_GLYPH_MAX
} SpecialGlyph;
+5 -4
View File
@@ -246,11 +246,12 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
* computation should be possible in the given type. Therefore, we use
* [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the
* quotient and the remainder, so both should be equally fast. */
#define DIV_ROUND_UP(_x, _y) \
#define DIV_ROUND_UP(x, y) __DIV_ROUND_UP(UNIQ, (x), UNIQ, (y))
#define __DIV_ROUND_UP(xq, x, yq, y) \
({ \
const typeof(_x) __x = (_x); \
const typeof(_y) __y = (_y); \
(__x / __y + !!(__x % __y)); \
const typeof(x) UNIQ_T(X, xq) = (x); \
const typeof(y) UNIQ_T(Y, yq) = (y); \
(UNIQ_T(X, xq) / UNIQ_T(Y, yq) + !!(UNIQ_T(X, xq) % UNIQ_T(Y, yq))); \
})
#ifdef __COVERITY__
+2 -1
View File
@@ -20,7 +20,8 @@
#include "string-util.h"
int parse_boolean(const char *v) {
assert(v);
if (!v)
return -EINVAL;
if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
return 1;
+229 -14
View File
@@ -9,6 +9,7 @@
#include "gunicode.h"
#include "pager.h"
#include "parse-util.h"
#include "pretty-print.h"
#include "string-util.h"
#include "terminal-util.h"
#include "time-util.h"
@@ -58,6 +59,7 @@ typedef struct TableData {
unsigned align_percent; /* 0 … 100, where to pad with spaces when expanding is needed. 0: left-aligned, 100: right-aligned */
const char *color; /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */
char *url; /* A URL to use for a clickable hyperlink */
char *formatted; /* A cached textual representation of the cell data, before ellipsation/alignment */
union {
@@ -68,6 +70,8 @@ typedef struct TableData {
uint64_t size;
char string[0];
uint32_t uint32;
uint64_t uint64;
int percent; /* we use 'int' as datatype for percent values in order to match the result of parse_percent() */
/* … add more here as we start supporting more cell data types … */
};
} TableData;
@@ -103,6 +107,8 @@ struct Table {
size_t *sort_map; /* The columns to order rows by, in order of preference. */
size_t n_sort_map;
bool *reverse_map;
};
Table *table_new_raw(size_t n_columns) {
@@ -175,6 +181,8 @@ static TableData *table_data_free(TableData *d) {
assert(d);
free(d->formatted);
free(d->url);
return mfree(d);
}
@@ -193,6 +201,7 @@ Table *table_unref(Table *t) {
free(t->data);
free(t->display_map);
free(t->sort_map);
free(t->reverse_map);
return mfree(t);
}
@@ -215,11 +224,15 @@ static size_t table_data_size(TableDataType type, const void *data) {
return sizeof(usec_t);
case TABLE_SIZE:
case TABLE_UINT64:
return sizeof(uint64_t);
case TABLE_UINT32:
return sizeof(uint32_t);
case TABLE_PERCENT:
return sizeof(int);
default:
assert_not_reached("Uh? Unexpected cell type");
}
@@ -262,7 +275,7 @@ static bool table_data_matches(
if (k != l)
return false;
return memcmp(data, d->data, l) == 0;
return memcmp_safe(data, d->data, l) == 0;
}
static TableData *table_data_new(
@@ -376,6 +389,7 @@ int table_dup_cell(Table *t, TableCell *cell) {
}
static int table_dedup_cell(Table *t, TableCell *cell) {
_cleanup_free_ char *curl = NULL;
TableData *nd, *od;
size_t i;
@@ -394,10 +408,26 @@ static int table_dedup_cell(Table *t, TableCell *cell) {
assert(od->n_ref > 1);
nd = table_data_new(od->type, od->data, od->minimum_width, od->maximum_width, od->weight, od->align_percent, od->ellipsize_percent);
if (od->url) {
curl = strdup(od->url);
if (!curl)
return -ENOMEM;
}
nd = table_data_new(
od->type,
od->data,
od->minimum_width,
od->maximum_width,
od->weight,
od->align_percent,
od->ellipsize_percent);
if (!nd)
return -ENOMEM;
nd->color = od->color;
nd->url = TAKE_PTR(curl);
table_data_unref(od);
t->data[i] = nd;
@@ -524,6 +554,66 @@ int table_set_color(Table *t, TableCell *cell, const char *color) {
return 0;
}
int table_set_url(Table *t, TableCell *cell, const char *url) {
_cleanup_free_ char *copy = NULL;
int r;
assert(t);
assert(cell);
if (url) {
copy = strdup(url);
if (!copy)
return -ENOMEM;
}
r = table_dedup_cell(t, cell);
if (r < 0)
return r;
return free_and_replace(table_get_data(t, cell)->url, copy);
}
int table_update(Table *t, TableCell *cell, TableDataType type, const void *data) {
_cleanup_free_ char *curl = NULL;
TableData *nd, *od;
size_t i;
assert(t);
assert(cell);
i = TABLE_CELL_TO_INDEX(cell);
if (i >= t->n_cells)
return -ENXIO;
assert_se(od = t->data[i]);
if (od->url) {
curl = strdup(od->url);
if (!curl)
return -ENOMEM;
}
nd = table_data_new(
type,
data,
od->minimum_width,
od->maximum_width,
od->weight,
od->align_percent,
od->ellipsize_percent);
if (!nd)
return -ENOMEM;
nd->color = od->color;
nd->url = TAKE_PTR(curl);
table_data_unref(od);
t->data[i] = nd;
return 0;
}
int table_add_many_internal(Table *t, TableDataType first_type, ...) {
TableDataType type;
va_list ap;
@@ -542,6 +632,8 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
uint64_t size;
usec_t usec;
uint32_t uint32;
uint64_t uint64;
int percent;
bool b;
} buffer;
@@ -576,6 +668,16 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
data = &buffer.uint32;
break;
case TABLE_UINT64:
buffer.uint64 = va_arg(ap, uint64_t);
data = &buffer.uint64;
break;
case TABLE_PERCENT:
buffer.percent = va_arg(ap, int);
data = &buffer.percent;
break;
case _TABLE_DATA_TYPE_MAX:
/* Used as end marker */
va_end(ap);
@@ -699,6 +801,12 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
case TABLE_UINT32:
return CMP(a->uint32, b->uint32);
case TABLE_UINT64:
return CMP(a->uint64, b->uint64);
case TABLE_PERCENT:
return CMP(a->percent, b->percent);
default:
;
}
@@ -732,7 +840,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
r = cell_data_compare(d, *a, dd, *b);
if (r != 0)
return r;
return t->reverse_map && t->reverse_map[t->sort_map[i]] ? -r : r;
}
/* Order identical lines by the order there were originally added in */
@@ -809,6 +917,30 @@ static const char *table_data_format(TableData *d) {
break;
}
case TABLE_UINT64: {
_cleanup_free_ char *p;
p = new(char, DECIMAL_STR_WIDTH(d->uint64) + 1);
if (!p)
return NULL;
sprintf(p, "%" PRIu64, d->uint64);
d->formatted = TAKE_PTR(p);
break;
}
case TABLE_PERCENT: {
_cleanup_free_ char *p;
p = new(char, DECIMAL_STR_WIDTH(d->percent) + 2);
if (!p)
return NULL;
sprintf(p, "%i%%" , d->percent);
d->formatted = TAKE_PTR(p);
break;
}
default:
assert_not_reached("Unexpected type?");
}
@@ -838,11 +970,13 @@ static int table_data_requested_width(TableData *d, size_t *ret) {
return 0;
}
static char *align_string_mem(const char *str, size_t new_length, unsigned percent) {
size_t w = 0, space, lspace, old_length;
static char *align_string_mem(const char *str, const char *url, size_t new_length, unsigned percent) {
size_t w = 0, space, lspace, old_length, clickable_length;
_cleanup_free_ char *clickable = NULL;
const char *p;
char *ret;
size_t i;
int r;
/* As with ellipsize_mem(), 'old_length' is a byte size while 'new_length' is a width in character cells */
@@ -851,6 +985,15 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce
old_length = strlen(str);
if (url) {
r = terminal_urlify(url, str, &clickable);
if (r < 0)
return NULL;
clickable_length = strlen(clickable);
} else
clickable_length = old_length;
/* Determine current width on screen */
p = str;
while (p < str + old_length) {
@@ -867,23 +1010,23 @@ static char *align_string_mem(const char *str, size_t new_length, unsigned perce
/* Already wider than the target, if so, don't do anything */
if (w >= new_length)
return strndup(str, old_length);
return clickable ? TAKE_PTR(clickable) : strdup(str);
/* How much spaces shall we add? An how much on the left side? */
space = new_length - w;
lspace = space * percent / 100U;
ret = new(char, space + old_length + 1);
ret = new(char, space + clickable_length + 1);
if (!ret)
return NULL;
for (i = 0; i < lspace; i++)
ret[i] = ' ';
memcpy(ret + lspace, str, old_length);
for (i = lspace + old_length; i < space + old_length; i++)
memcpy(ret + lspace, clickable ?: str, clickable_length);
for (i = lspace + clickable_length; i < space + clickable_length; i++)
ret[i] = ' ';
ret[space + old_length] = 0;
ret[space + clickable_length] = 0;
return ret;
}
@@ -1136,23 +1279,41 @@ int table_print(Table *t, FILE *f) {
} else if (l < width[j]) {
/* Field is shorter than allocated space. Let's align with spaces */
buffer = align_string_mem(field, width[j], d->align_percent);
buffer = align_string_mem(field, d->url, width[j], d->align_percent);
if (!buffer)
return -ENOMEM;
field = buffer;
}
if (l >= width[j] && d->url) {
_cleanup_free_ char *clickable = NULL;
r = terminal_urlify(d->url, field, &clickable);
if (r < 0)
return r;
free_and_replace(buffer, clickable);
field = buffer;
}
if (row == t->data) /* underline header line fully, including the column separator */
fputs(ansi_underline(), f);
if (j > 0)
fputc(' ', f); /* column separator */
if (d->color)
if (d->color && colors_enabled()) {
if (row == t->data) /* first undo header underliner */
fputs(ANSI_NORMAL, f);
fputs(d->color, f);
}
fputs(field, f);
if (d->color)
fputs(ansi_normal(), f);
if (colors_enabled() && (d->color || row == t->data))
fputs(ANSI_NORMAL, f);
}
fputc('\n', f);
@@ -1199,3 +1360,57 @@ size_t table_get_columns(Table *t) {
assert(t->n_columns > 0);
return t->n_columns;
}
int table_set_reverse(Table *t, size_t column, bool b) {
assert(t);
assert(column < t->n_columns);
if (!t->reverse_map) {
if (!b)
return 0;
t->reverse_map = new0(bool, t->n_columns);
if (!t->reverse_map)
return -ENOMEM;
}
t->reverse_map[column] = b;
return 0;
}
TableCell *table_get_cell(Table *t, size_t row, size_t column) {
size_t i;
assert(t);
if (column >= t->n_columns)
return NULL;
i = row * t->n_columns + column;
if (i >= t->n_cells)
return NULL;
return TABLE_INDEX_TO_CELL(i);
}
const void *table_get(Table *t, TableCell *cell) {
TableData *d;
assert(t);
d = table_get_data(t, cell);
if (!d)
return NULL;
return d->data;
}
const void* table_get_at(Table *t, size_t row, size_t column) {
TableCell *cell;
cell = table_get_cell(t, row, column);
if (!cell)
return NULL;
return table_get(t, cell);
}
+11
View File
@@ -15,6 +15,8 @@ typedef enum TableDataType {
TABLE_TIMESPAN,
TABLE_SIZE,
TABLE_UINT32,
TABLE_UINT64,
TABLE_PERCENT,
_TABLE_DATA_TYPE_MAX,
_TABLE_DATA_TYPE_INVALID = -1,
} TableDataType;
@@ -42,6 +44,9 @@ int table_set_weight(Table *t, TableCell *cell, unsigned weight);
int table_set_align_percent(Table *t, TableCell *cell, unsigned percent);
int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent);
int table_set_color(Table *t, TableCell *cell, const char *color);
int table_set_url(Table *t, TableCell *cell, const char *color);
int table_update(Table *t, TableCell *cell, TableDataType type, const void *data);
int table_add_many_internal(Table *t, TableDataType first_type, ...);
#define table_add_many(t, ...) table_add_many_internal(t, __VA_ARGS__, _TABLE_DATA_TYPE_MAX)
@@ -50,6 +55,7 @@ void table_set_header(Table *table, bool b);
void table_set_width(Table *t, size_t width);
int table_set_display(Table *t, size_t first_column, ...);
int table_set_sort(Table *t, size_t first_column, ...);
int table_set_reverse(Table *t, size_t column, bool b);
int table_print(Table *t, FILE *f);
int table_format(Table *t, char **ret);
@@ -60,3 +66,8 @@ static inline TableCell* TABLE_HEADER_CELL(size_t i) {
size_t table_get_rows(Table *t);
size_t table_get_columns(Table *t);
TableCell *table_get_cell(Table *t, size_t row, size_t column);
const void *table_get(Table *t, TableCell *cell);
const void *table_get_at(Table *t, size_t row, size_t column);
+33 -19
View File
@@ -60,10 +60,38 @@ int terminal_urlify(const char *url, const char *text, char **ret) {
return 0;
}
int terminal_urlify_path(const char *path, const char *text, char **ret) {
int file_url_from_path(const char *path, char **ret) {
_cleanup_free_ char *absolute = NULL;
struct utsname u;
const char *url;
char *url = NULL;
int r;
if (uname(&u) < 0)
return -errno;
if (!path_is_absolute(path)) {
r = path_make_absolute_cwd(path, &absolute);
if (r < 0)
return r;
path = absolute;
}
/* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local
* hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested
* in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly
* careful with validating the strings either. */
url = strjoin("file://", u.nodename, path);
if (!url)
return -ENOMEM;
*ret = url;
return 0;
}
int terminal_urlify_path(const char *path, const char *text, char **ret) {
_cleanup_free_ char *url = NULL;
int r;
assert(path);
@@ -88,23 +116,9 @@ int terminal_urlify_path(const char *path, const char *text, char **ret) {
return 0;
}
if (uname(&u) < 0)
return -errno;
if (!path_is_absolute(path)) {
r = path_make_absolute_cwd(path, &absolute);
if (r < 0)
return r;
path = absolute;
}
/* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local
* hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested
* in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly
* careful with validating the strings either. */
url = strjoina("file://", u.nodename, path);
r = file_url_from_path(path, &url);
if (r < 0)
return r;
return terminal_urlify(url, text, ret);
}
+2
View File
@@ -3,6 +3,8 @@
void print_separator(void);
int file_url_from_path(const char *path, char **ret);
int terminal_urlify(const char *url, const char *text, char **ret);
int terminal_urlify_path(const char *path, const char *text, char **ret);
int terminal_urlify_man(const char *page, const char *section, char **ret);
-2
View File
@@ -371,8 +371,6 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
.value =
"lookup_dcookie\0"
"perf_event_open\0"
"process_vm_readv\0"
"process_vm_writev\0"
"ptrace\0"
"rtas\0"
#ifdef __NR_s390_runtime_instr
+8 -1
View File
@@ -65,7 +65,7 @@ static void test_keymaps(void) {
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
static void dump_special_glyphs(void) {
assert_cc(CROSS_MARK + 1 == _SPECIAL_GLYPH_MAX);
assert_cc(DEPRESSED_SMILEY + 1 == _SPECIAL_GLYPH_MAX);
log_info("/* %s */", __func__);
@@ -84,6 +84,13 @@ static void dump_special_glyphs(void) {
dump_glyph(MU);
dump_glyph(CHECK_MARK);
dump_glyph(CROSS_MARK);
dump_glyph(ECSTATIC_SMILEY);
dump_glyph(HAPPY_SMILEY);
dump_glyph(SLIGHTLY_HAPPY_SMILEY);
dump_glyph(NEUTRAL_SMILEY);
dump_glyph(SLIGHTLY_UNHAPPY_SMILEY);
dump_glyph(UNHAPPY_SMILEY);
dump_glyph(DEPRESSED_SMILEY);
}
int main(int argc, char *argv[]) {