Compare commits

...

2 Commits

Author SHA1 Message Date
ZIHCO 43fca10bc8
Merge 695d64bf2b into a035eaa227 2025-04-17 21:57:11 +08:00
ZIHCO 695d64bf2b machine-info: implement the etc/machine-info field for ANSI color 2025-04-15 16:58:35 +01:00
1 changed files with 186 additions and 4 deletions

View File

@ -15,6 +15,7 @@
#include "bus-get-properties.h" #include "bus-get-properties.h"
#include "bus-log-control-api.h" #include "bus-log-control-api.h"
#include "bus-polkit.h" #include "bus-polkit.h"
#include "color-util.h"
#include "constants.h" #include "constants.h"
#include "daemon-util.h" #include "daemon-util.h"
#include "device-private.h" #include "device-private.h"
@ -62,6 +63,7 @@ typedef enum {
PROP_LOCATION, PROP_LOCATION,
PROP_HARDWARE_VENDOR, PROP_HARDWARE_VENDOR,
PROP_HARDWARE_MODEL, PROP_HARDWARE_MODEL,
PROP_ANSI_COLOR,
/* Read from /etc/os-release (or /usr/lib/os-release) */ /* Read from /etc/os-release (or /usr/lib/os-release) */
PROP_OS_PRETTY_NAME, PROP_OS_PRETTY_NAME,
@ -166,7 +168,8 @@ static void context_read_machine_info(Context *c) {
(UINT64_C(1) << PROP_DEPLOYMENT) | (UINT64_C(1) << PROP_DEPLOYMENT) |
(UINT64_C(1) << PROP_LOCATION) | (UINT64_C(1) << PROP_LOCATION) |
(UINT64_C(1) << PROP_HARDWARE_VENDOR) | (UINT64_C(1) << PROP_HARDWARE_VENDOR) |
(UINT64_C(1) << PROP_HARDWARE_MODEL)); (UINT64_C(1) << PROP_HARDWARE_MODEL) |
(UINT64_C(1) << PROP_ANSI_COLOR));
r = parse_env_file(NULL, "/etc/machine-info", r = parse_env_file(NULL, "/etc/machine-info",
"PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME], "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
@ -175,7 +178,8 @@ static void context_read_machine_info(Context *c) {
"DEPLOYMENT", &c->data[PROP_DEPLOYMENT], "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
"LOCATION", &c->data[PROP_LOCATION], "LOCATION", &c->data[PROP_LOCATION],
"HARDWARE_VENDOR", &c->data[PROP_HARDWARE_VENDOR], "HARDWARE_VENDOR", &c->data[PROP_HARDWARE_VENDOR],
"HARDWARE_MODEL", &c->data[PROP_HARDWARE_MODEL]); "HARDWARE_MODEL", &c->data[PROP_HARDWARE_MODEL],
"ANSI_COLOR", &c->data[PROP_ANSI_COLOR]);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m"); log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
@ -488,6 +492,35 @@ static bool valid_deployment(const char *deployment) {
return in_charset(deployment, VALID_DEPLOYMENT_CHARS); return in_charset(deployment, VALID_DEPLOYMENT_CHARS);
} }
static const char* fallback_ansi_color(Context *c) {
uint64_t hash;
const uint8_t key[16] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
double h, s = 0.7, v = 0.9;
uint8_t r, g, b;
const char* hostname;
_cleanup_free_ char *color = NULL;
int k;
struct siphash state;
assert(c);
hostname = isempty(c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS]) ? \
"localhost" : c->data[PROP_STATIC_HOSTNAME_SUBSTITUTED_WILDCARDS];
siphash24_init(&state, key);
string_hash_func(hostname, &state);
hash = siphash24_finalize(&state);
h = (double)(hash % 360);
hsv_to_rgb(h, s, v, &r, &g, &b);
k = asprintf(&color, "38;2;%u;%u;%u", r, g, b);
if (k < 0) {
log_oom();
return NULL;
}
return color;
}
static const char* fallback_chassis_by_virtualization(void) { static const char* fallback_chassis_by_virtualization(void) {
Virtualization v = detect_virtualization(); Virtualization v = detect_virtualization();
if (v < 0) { if (v < 0) {
@ -772,6 +805,7 @@ static int context_write_data_machine_info(Context *c) {
[PROP_CHASSIS] = "CHASSIS", [PROP_CHASSIS] = "CHASSIS",
[PROP_DEPLOYMENT] = "DEPLOYMENT", [PROP_DEPLOYMENT] = "DEPLOYMENT",
[PROP_LOCATION] = "LOCATION", [PROP_LOCATION] = "LOCATION",
[PROP_ANSI_COLOR] = "ANSI_COLOR",
}; };
_cleanup_strv_free_ char **l = NULL; _cleanup_strv_free_ char **l = NULL;
int r; int r;
@ -786,7 +820,7 @@ static int context_write_data_machine_info(Context *c) {
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return r; return r;
for (int p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) { for (int p = PROP_PRETTY_HOSTNAME; p <= PROP_ANSI_COLOR; p++) {
assert(name[p]); assert(name[p]);
r = strv_env_assign(&l, name[p], empty_to_null(c->data[p])); r = strv_env_assign(&l, name[p], empty_to_null(c->data[p]));
@ -810,6 +844,30 @@ static int context_write_data_machine_info(Context *c) {
return 0; return 0;
} }
static int property_get_ansi_color(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Context *c = ASSERT_PTR(userdata);
_cleanup_free_ char *fallback = NULL;
const char *color;
context_read_etc_hostname(c);
context_read_machine_info(c);
if (isempty(c->data[PROP_ANSI_COLOR]))
color = fallback_ansi_color(c);
else
color = c->data[PROP_ANSI_COLOR];
return sd_bus_message_append(reply, "s", color);
}
static int property_get_hardware_property( static int property_get_hardware_property(
sd_bus_message *reply, sd_bus_message *reply,
Context *c, Context *c,
@ -1241,6 +1299,122 @@ static int validate_and_substitute_hostname(const char *name, char **ret_substit
return 1; return 1;
} }
static bool validate_ansi_color(const char *color) {
if (isempty(color))
return true;
if (string_has_cc(color, NULL))
return false;
_cleanup_strv_free_ char **parts = NULL;
int r = strv_split_full(&parts, color, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return false;
if (strv_length(parts) < 2)
return false;
unsigned code;
r = safe_atou(parts[0], &code);
if (r < 0)
return false;
if (code == 0) {
if (strv_length(parts) != 2)
return false;
r = safe_atou(parts[1], &code);
if (r < 0 || code > 255)
return false;
return true;
}
if (code == 38 || code == 48) {
r = safe_atou(parts[1], &code);
if (r < 0 || (code != 2 && code != 5))
return false;
if (code == 2) {
if (strv_length(parts) != 5)
return false;
for (int i = 2; i < 5; i++) {
r = safe_atou(parts[i], &code);
if (r < 0 || code > 255)
return false;
}
return true;
}
if (code == 5) {
if (strv_length(parts) != 3)
return false;
r = safe_atou(parts[2], &code);
if (r < 0 || code > 255)
return false;
return true;
}
}
return false;
}
static int method_set_ansi_color(sd_bus_message *m,void *userdata, sd_bus_error *error) {
Context *c = ASSERT_PTR(userdata);
const char *color;
int interactive, r;
assert(m);
r = sd_bus_message_read(m, "sb", &color, &interactive);
if (r < 0)
return r;
color = empty_to_null(color);
context_read_machine_info(c);
if (streq_ptr(color, c->data[PROP_ANSI_COLOR]))
return sd_bus_reply_method_return(m, NULL);
if (!isempty(color) && !validate_ansi_color(color))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid ANSI color '%s'", color);
r = bus_verify_polkit_async_full(
m,
"org.freedesktop.hostname1.set-machine-info",
NULL,
UID_INVALID,
interactive ? POLKIT_ALLOW_INTERACTIVE : 0,
&c->polkit_registry,
error);
if (r < 0)
return r;
if (r == 0)
return 1;
r = free_and_strdup_warn(&c->data[PROP_ANSI_COLOR], color);
if (r < 0)
return r;
r = context_write_data_machine_info(c);
if (r < 0) {
log_error_errno(r, "Failed to write machine info: %m");
if (ERRNO_IS_PRIVILEGE(r))
return sd_bus_error_set(error, BUS_ERROR_FILE_IS_PROTECTED, "Not allowed to update /etc/machine-info.");
if (r == -EROFS)
return sd_bus_error_set(error, BUS_ERROR_READ_ONLY_FILESYSTEM, "/etc/machine-info is in a read-only filesystem.");
return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %m");
}
log_info("Changed ANSI color to '%s'", strna(c->data[PROP_ANSI_COLOR]));
(void) sd_bus_emit_properties_changed(
sd_bus_message_get_bus(m),
"/org/freedesktop/hostname1",
"org.freedesktop.hostname1",
"AnsiColor",
NULL);
return sd_bus_reply_method_return(m, NULL);
}
static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) { static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = ASSERT_PTR(userdata); Context *c = ASSERT_PTR(userdata);
const char *name; const char *name;
@ -1535,7 +1709,7 @@ static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_
static int build_describe_response(Context *c, bool privileged, sd_json_variant **ret) { static int build_describe_response(Context *c, bool privileged, sd_json_variant **ret) {
_cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL,
*chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL, *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL, *firmware_version = NULL,
*firmware_vendor = NULL, *chassis_asset_tag = NULL; *firmware_vendor = NULL, *chassis_asset_tag = NULL, *ansi_color = NULL;
_cleanup_strv_free_ char **os_release_pairs = NULL, **machine_info_pairs = NULL; _cleanup_strv_free_ char **os_release_pairs = NULL, **machine_info_pairs = NULL;
usec_t firmware_date = USEC_INFINITY, eol = USEC_INFINITY; usec_t firmware_date = USEC_INFINITY, eol = USEC_INFINITY;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
@ -1626,6 +1800,7 @@ static int build_describe_response(Context *c, bool privileged, sd_json_variant
SD_JSON_BUILD_PAIR_STRING("HardwareSerial", serial), SD_JSON_BUILD_PAIR_STRING("HardwareSerial", serial),
SD_JSON_BUILD_PAIR_STRING("FirmwareVersion", firmware_version), SD_JSON_BUILD_PAIR_STRING("FirmwareVersion", firmware_version),
SD_JSON_BUILD_PAIR_STRING("FirmwareVendor", firmware_vendor), SD_JSON_BUILD_PAIR_STRING("FirmwareVendor", firmware_vendor),
SD_JSON_BUILD_PAIR_STRING("AnsiColor", ansi_color ?: c->data[PROP_ANSI_COLOR]),
JSON_BUILD_PAIR_FINITE_USEC("FirmwareDate", firmware_date), JSON_BUILD_PAIR_FINITE_USEC("FirmwareDate", firmware_date),
SD_JSON_BUILD_PAIR_ID128("MachineID", machine_id), SD_JSON_BUILD_PAIR_ID128("MachineID", machine_id),
SD_JSON_BUILD_PAIR_ID128("BootID", boot_id), SD_JSON_BUILD_PAIR_ID128("BootID", boot_id),
@ -1702,6 +1877,7 @@ static const sd_bus_vtable hostname_vtable[] = {
SD_BUS_PROPERTY("BootID", "ay", property_get_boot_id, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BootID", "ay", property_get_boot_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("VSockCID", "u", property_get_vsock_cid, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("VSockCID", "u", property_get_vsock_cid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ChassisAssetTag", "s", property_get_chassis_asset_tag, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ChassisAssetTag", "s", property_get_chassis_asset_tag, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AnsiColor", "s", property_get_ansi_color, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD_WITH_ARGS("SetHostname", SD_BUS_METHOD_WITH_ARGS("SetHostname",
SD_BUS_ARGS("s", hostname, "b", interactive), SD_BUS_ARGS("s", hostname, "b", interactive),
@ -1754,6 +1930,12 @@ static const sd_bus_vtable hostname_vtable[] = {
method_describe, method_describe,
SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("SetAniColor",
SD_BUS_ARGS("s", color, "b", interactive),
SD_BUS_NO_RESULT,
method_set_ansi_color,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END, SD_BUS_VTABLE_END,
}; };