device-private.c revision 8b43440b7ef4b81c69c31de7ff820dc07a780254
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering/***
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering This file is part of systemd.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering Copyright 2014 Tom Gundersen <teg@jklm.no>
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering systemd is free software; you can redistribute it and/or modify it
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering under the terms of the GNU Lesser General Public License as published by
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering (at your option) any later version.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering systemd is distributed in the hope that it will be useful, but
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering Lesser General Public License for more details.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering You should have received a copy of the GNU Lesser General Public License
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering***/
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include <ctype.h>
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering#include <net/if.h>
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include <sys/types.h>
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "sd-device.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "device-internal.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "device-private.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "device-util.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "fd-util.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "fileio.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "fs-util.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "hashmap.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "macro.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "mkdir.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "parse-util.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "path-util.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "refcnt.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "set.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "string-table.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "string-util.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "strv.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "strxcpyx.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering#include "util.h"
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringint device_add_property(sd_device *device, const char *key, const char *value) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering int r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(key);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_add_property_aux(device, key, value, false);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (key[0] != '.') {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_add_property_aux(device, key, value, true);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering }
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return 0;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringstatic int device_add_property_internal_from_string(sd_device *device, const char *str) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering _cleanup_free_ char *key = NULL;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering char *value;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(str);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering key = strdup(str);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (!key)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return -ENOMEM;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering value = strchr(key, '=');
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (!value)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return -EINVAL;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering *value = '\0';
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (isempty(++value))
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering value = NULL;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return device_add_property_internal(device, key, value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringstatic int handle_db_line(sd_device *device, char key, const char *value) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering char *path;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering int r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering switch (key) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case 'S':
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering path = strjoina("/dev/", value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_add_devlink(device, path);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case 'L':
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = safe_atoi(value, &device->devlink_priority);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case 'E':
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_add_property_internal_from_string(device, value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case 'G':
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_add_tag(device, value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case 'W':
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = safe_atoi(value, &device->watch_handle);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case 'I':
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_set_usec_initialized(device, value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering default:
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering log_debug("device db: unknown key '%c'", key);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering }
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return 0;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringvoid device_set_devlink_priority(sd_device *device, int priority) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering device->devlink_priority = priority;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringvoid device_set_is_initialized(sd_device *device) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering device->is_initialized = true;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringint device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering char num[DECIMAL_STR_MAX(usec_t)];
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering usec_t usec_initialized;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering int r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (device_old && device_old->usec_initialized > 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering usec_initialized = device_old->usec_initialized;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering else
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering usec_initialized = now(CLOCK_MONOTONIC);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return -errno;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_set_usec_initialized(device, num);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return 0;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringstatic int device_read_db(sd_device *device) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering _cleanup_free_ char *db = NULL;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering char *path;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering const char *id, *value;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering char key;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering size_t db_len;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering unsigned i;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering int r;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering enum {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering PRE_KEY,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering KEY,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering PRE_VALUE,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering VALUE,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering INVALID_LINE,
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering } state = PRE_KEY;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (device->db_loaded || device->sealed)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return 0;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = device_get_id_filename(device, &id);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering return r;
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering path = strjoina("/run/udev/data/", id);
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = read_full_file(path, &db, &db_len);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r == -ENOENT)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return 0;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering else
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path);
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering }
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering /* devices with a database entry are initialized */
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering device_set_is_initialized(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering for (i = 0; i < db_len; i++) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering switch (state) {
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering case PRE_KEY:
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering if (!strchr(NEWLINE, db[i])) {
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering key = db[i];
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering state = KEY;
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering }
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering break;
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering case KEY:
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (db[i] != ':') {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering
dea7b6b043f0cd9e34ee719b9b612c3a4776387eLennart Poettering state = INVALID_LINE;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering } else {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering db[i] = '\0';
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering state = PRE_VALUE;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering }
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case PRE_VALUE:
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering value = &db[i];
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering state = VALUE;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case INVALID_LINE:
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (strchr(NEWLINE, db[i]))
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering state = PRE_KEY;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering case VALUE:
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (strchr(NEWLINE, db[i])) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering db[i] = '\0';
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering r = handle_db_line(device, key, value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering if (r < 0)
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering state = PRE_KEY;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering }
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering break;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering default:
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert_not_reached("invalid state when parsing db");
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering }
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering }
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering device->db_loaded = true;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return 0;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringuint64_t device_get_properties_generation(sd_device *device) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return device->properties_generation;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringuint64_t device_get_tags_generation(sd_device *device) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return device->tags_generation;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poetteringuint64_t device_get_devlinks_generation(sd_device *device) {
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering assert(device);
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering return device->devlinks_generation;
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering}
84ac7bea360cd369df26910e9685a7eed2327088Lennart Poettering
int device_get_devnode_mode(sd_device *device, mode_t *mode) {
int r;
assert(device);
assert(mode);
r = device_read_db(device);
if (r < 0)
return r;
*mode = device->devmode;
return 0;
}
int device_get_devnode_uid(sd_device *device, uid_t *uid) {
int r;
assert(device);
assert(uid);
r = device_read_db(device);
if (r < 0)
return r;
*uid = device->devuid;
return 0;
}
static int device_set_devuid(sd_device *device, const char *uid) {
unsigned u;
int r;
assert(device);
assert(uid);
r = safe_atou(uid, &u);
if (r < 0)
return r;
r = device_add_property_internal(device, "DEVUID", uid);
if (r < 0)
return r;
device->devuid = u;
return 0;
}
int device_get_devnode_gid(sd_device *device, gid_t *gid) {
int r;
assert(device);
assert(gid);
r = device_read_db(device);
if (r < 0)
return r;
*gid = device->devgid;
return 0;
}
static int device_set_devgid(sd_device *device, const char *gid) {
unsigned g;
int r;
assert(device);
assert(gid);
r = safe_atou(gid, &g);
if (r < 0)
return r;
r = device_add_property_internal(device, "DEVGID", gid);
if (r < 0)
return r;
device->devgid = g;
return 0;
}
static int device_amend(sd_device *device, const char *key, const char *value) {
int r;
assert(device);
assert(key);
assert(value);
if (streq(key, "DEVPATH")) {
char *path;
path = strjoina("/sys", value);
/* the caller must verify or trust this data (e.g., if it comes from the kernel) */
r = device_set_syspath(device, path, false);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set syspath to '%s': %m", path);
} else if (streq(key, "SUBSYSTEM")) {
r = device_set_subsystem(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set subsystem to '%s': %m", value);
} else if (streq(key, "DEVTYPE")) {
r = device_set_devtype(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set devtype to '%s': %m", value);
} else if (streq(key, "DEVNAME")) {
r = device_set_devname(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set devname to '%s': %m", value);
} else if (streq(key, "USEC_INITIALIZED")) {
r = device_set_usec_initialized(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set usec-initialized to '%s': %m", value);
} else if (streq(key, "DRIVER")) {
r = device_set_driver(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set driver to '%s': %m", value);
} else if (streq(key, "IFINDEX")) {
r = device_set_ifindex(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set ifindex to '%s': %m", value);
} else if (streq(key, "DEVMODE")) {
r = device_set_devmode(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set devmode to '%s': %m", value);
} else if (streq(key, "DEVUID")) {
r = device_set_devuid(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set devuid to '%s': %m", value);
} else if (streq(key, "DEVGID")) {
r = device_set_devgid(device, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set devgid to '%s': %m", value);
} else if (streq(key, "DEVLINKS")) {
const char *word, *state;
size_t l;
FOREACH_WORD(word, l, value, state) {
char devlink[l + 1];
strncpy(devlink, word, l);
devlink[l] = '\0';
r = device_add_devlink(device, devlink);
if (r < 0)
return log_debug_errno(r, "sd-device: could not add devlink '%s': %m", devlink);
}
} else if (streq(key, "TAGS")) {
const char *word, *state;
size_t l;
FOREACH_WORD_SEPARATOR(word, l, value, ":", state) {
char tag[l + 1];
(void)strncpy(tag, word, l);
tag[l] = '\0';
r = device_add_tag(device, tag);
if (r < 0)
return log_debug_errno(r, "sd-device: could not add tag '%s': %m", tag);
}
} else {
r = device_add_property_internal(device, key, value);
if (r < 0)
return log_debug_errno(r, "sd-device: could not add property '%s=%s': %m", key, value);
}
return 0;
}
static const char* const device_action_table[_DEVICE_ACTION_MAX] = {
[DEVICE_ACTION_ADD] = "add",
[DEVICE_ACTION_REMOVE] = "remove",
[DEVICE_ACTION_CHANGE] = "change",
[DEVICE_ACTION_MOVE] = "move",
[DEVICE_ACTION_ONLINE] = "online",
[DEVICE_ACTION_OFFLINE] = "offline",
};
DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction);
static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum,
DeviceAction *_action) {
DeviceAction action = _DEVICE_ACTION_INVALID;
uint64_t seqnum = 0;
const char *major = NULL, *minor = NULL;
char *value;
int r;
assert(device);
assert(key);
assert(_major);
assert(_minor);
assert(_seqnum);
assert(_action);
value = strchr(key, '=');
if (!value) {
log_debug("sd-device: not a key-value pair: '%s'", key);
return -EINVAL;
}
*value = '\0';
value++;
if (streq(key, "MAJOR"))
major = value;
else if (streq(key, "MINOR"))
minor = value;
else {
if (streq(key, "ACTION")) {
action = device_action_from_string(value);
if (action == _DEVICE_ACTION_INVALID)
return -EINVAL;
} else if (streq(key, "SEQNUM")) {
r = safe_atou64(value, &seqnum);
if (r < 0)
return r;
else if (seqnum == 0)
/* kernel only sends seqnum > 0 */
return -EINVAL;
}
r = device_amend(device, key, value);
if (r < 0)
return r;
}
if (major != 0)
*_major = major;
if (minor != 0)
*_minor = minor;
if (action != _DEVICE_ACTION_INVALID)
*_action = action;
if (seqnum > 0)
*_seqnum = seqnum;
return 0;
}
void device_seal(sd_device *device) {
assert(device);
device->sealed = true;
}
static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) {
assert(device);
if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) {
log_debug("sd-device: device created from strv lacks devpath, subsystem, action or seqnum");
return -EINVAL;
}
device->sealed = true;
return 0;
}
int device_new_from_strv(sd_device **ret, char **strv) {
_cleanup_device_unref_ sd_device *device = NULL;
char **key;
const char *major = NULL, *minor = NULL;
DeviceAction action = _DEVICE_ACTION_INVALID;
uint64_t seqnum;
int r;
assert(ret);
assert(strv);
r = device_new_aux(&device);
if (r < 0)
return r;
STRV_FOREACH(key, strv) {
r = device_append(device, *key, &major, &minor, &seqnum, &action);
if (r < 0)
return r;
}
if (major) {
r = device_set_devnum(device, major, minor);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor);
}
r = device_verify(device, action, seqnum);
if (r < 0)
return r;
*ret = device;
device = NULL;
return 0;
}
int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
_cleanup_device_unref_ sd_device *device = NULL;
const char *major = NULL, *minor = NULL;
DeviceAction action = _DEVICE_ACTION_INVALID;
uint64_t seqnum;
unsigned i = 0;
int r;
assert(ret);
assert(nulstr);
assert(len);
r = device_new_aux(&device);
if (r < 0)
return r;
while (i < len) {
char *key;
const char *end;
key = (char*)&nulstr[i];
end = memchr(key, '\0', len - i);
if (!end) {
log_debug("sd-device: failed to parse nulstr");
return -EINVAL;
}
i += end - key + 1;
r = device_append(device, key, &major, &minor, &seqnum, &action);
if (r < 0)
return r;
}
if (major) {
r = device_set_devnum(device, major, minor);
if (r < 0)
return log_debug_errno(r, "sd-device: could not set devnum %s:%s: %m", major, minor);
}
r = device_verify(device, action, seqnum);
if (r < 0)
return r;
*ret = device;
device = NULL;
return 0;
}
static int device_update_properties_bufs(sd_device *device) {
const char *val, *prop;
_cleanup_free_ char **buf_strv = NULL;
_cleanup_free_ uint8_t *buf_nulstr = NULL;
size_t allocated_nulstr = 0;
size_t nulstr_len = 0, num = 0, i = 0;
assert(device);
if (!device->properties_buf_outdated)
return 0;
FOREACH_DEVICE_PROPERTY(device, prop, val) {
size_t len = 0;
len = strlen(prop) + 1 + strlen(val);
buf_nulstr = GREEDY_REALLOC0(buf_nulstr, allocated_nulstr, nulstr_len + len + 2);
if (!buf_nulstr)
return -ENOMEM;
strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL);
nulstr_len += len + 1;
++num;
}
/* build buf_strv from buf_nulstr */
buf_strv = new0(char *, num + 1);
if (!buf_strv)
return -ENOMEM;
NULSTR_FOREACH(val, (char*) buf_nulstr) {
buf_strv[i] = (char *) val;
assert(i < num);
i++;
}
free(device->properties_nulstr);
device->properties_nulstr = buf_nulstr;
buf_nulstr = NULL;
device->properties_nulstr_len = nulstr_len;
free(device->properties_strv);
device->properties_strv = buf_strv;
buf_strv = NULL;
device->properties_buf_outdated = false;
return 0;
}
int device_get_properties_nulstr(sd_device *device, const uint8_t **nulstr, size_t *len) {
int r;
assert(device);
assert(nulstr);
assert(len);
r = device_update_properties_bufs(device);
if (r < 0)
return r;
*nulstr = device->properties_nulstr;
*len = device->properties_nulstr_len;
return 0;
}
int device_get_properties_strv(sd_device *device, char ***strv) {
int r;
assert(device);
assert(strv);
r = device_update_properties_bufs(device);
if (r < 0)
return r;
*strv = device->properties_strv;
return 0;
}
int device_get_devlink_priority(sd_device *device, int *priority) {
int r;
assert(device);
assert(priority);
r = device_read_db(device);
if (r < 0)
return r;
*priority = device->devlink_priority;
return 0;
}
int device_get_watch_handle(sd_device *device, int *handle) {
int r;
assert(device);
assert(handle);
r = device_read_db(device);
if (r < 0)
return r;
*handle = device->watch_handle;
return 0;
}
void device_set_watch_handle(sd_device *device, int handle) {
assert(device);
device->watch_handle = handle;
}
int device_rename(sd_device *device, const char *name) {
_cleanup_free_ char *dirname = NULL;
char *new_syspath;
const char *interface;
int r;
assert(device);
assert(name);
dirname = dirname_malloc(device->syspath);
if (!dirname)
return -ENOMEM;
new_syspath = strjoina(dirname, "/", name);
/* the user must trust that the new name is correct */
r = device_set_syspath(device, new_syspath, false);
if (r < 0)
return r;
r = sd_device_get_property_value(device, "INTERFACE", &interface);
if (r >= 0) {
r = device_add_property_internal(device, "INTERFACE", name);
if (r < 0)
return r;
/* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */
r = device_add_property_internal(device, "INTERFACE_OLD", interface);
if (r < 0)
return r;
} else if (r != -ENOENT)
return r;
return 0;
}
int device_shallow_clone(sd_device *old_device, sd_device **new_device) {
_cleanup_device_unref_ sd_device *ret = NULL;
int r;
assert(old_device);
assert(new_device);
r = device_new_aux(&ret);
if (r < 0)
return r;
r = device_set_syspath(ret, old_device->syspath, false);
if (r < 0)
return r;
r = device_set_subsystem(ret, old_device->subsystem);
if (r < 0)
return r;
ret->devnum = old_device->devnum;
*new_device = ret;
ret = NULL;
return 0;
}
int device_clone_with_db(sd_device *old_device, sd_device **new_device) {
_cleanup_device_unref_ sd_device *ret = NULL;
int r;
assert(old_device);
assert(new_device);
r = device_shallow_clone(old_device, &ret);
if (r < 0)
return r;
r = device_read_db(ret);
if (r < 0)
return r;
ret->sealed = true;
*new_device = ret;
ret = NULL;
return 0;
}
int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) {
_cleanup_device_unref_ sd_device *ret = NULL;
int r;
assert(new_device);
assert(syspath);
assert(action);
r = sd_device_new_from_syspath(&ret, syspath);
if (r < 0)
return r;
r = device_read_uevent_file(ret);
if (r < 0)
return r;
r = device_add_property_internal(ret, "ACTION", action);
if (r < 0)
return r;
*new_device = ret;
ret = NULL;
return 0;
}
int device_copy_properties(sd_device *device_dst, sd_device *device_src) {
const char *property, *value;
int r;
assert(device_dst);
assert(device_src);
FOREACH_DEVICE_PROPERTY(device_src, property, value) {
r = device_add_property(device_dst, property, value);
if (r < 0)
return r;
}
return 0;
}
void device_cleanup_tags(sd_device *device) {
assert(device);
set_free_free(device->tags);
device->tags = NULL;
device->property_tags_outdated = true;
device->tags_generation ++;
}
void device_cleanup_devlinks(sd_device *device) {
assert(device);
set_free_free(device->devlinks);
device->devlinks = NULL;
device->property_devlinks_outdated = true;
device->devlinks_generation ++;
}
void device_remove_tag(sd_device *device, const char *tag) {
assert(device);
assert(tag);
free(set_remove(device->tags, tag));
device->property_tags_outdated = true;
device->tags_generation ++;
}
static int device_tag(sd_device *device, const char *tag, bool add) {
const char *id;
char *path;
int r;
assert(device);
assert(tag);
r = device_get_id_filename(device, &id);
if (r < 0)
return r;
path = strjoina("/run/udev/tags/", tag, "/", id);
if (add) {
r = touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
if (r < 0)
return r;
} else {
r = unlink(path);
if (r < 0 && errno != ENOENT)
return -errno;
}
return 0;
}
int device_tag_index(sd_device *device, sd_device *device_old, bool add) {
const char *tag;
int r = 0, k;
if (add && device_old) {
/* delete possible left-over tags */
FOREACH_DEVICE_TAG(device_old, tag) {
if (!sd_device_has_tag(device, tag)) {
k = device_tag(device_old, tag, false);
if (r >= 0 && k < 0)
r = k;
}
}
}
FOREACH_DEVICE_TAG(device, tag) {
k = device_tag(device, tag, add);
if (r >= 0 && k < 0)
r = k;
}
return r;
}
static bool device_has_info(sd_device *device) {
assert(device);
if (!set_isempty(device->devlinks))
return true;
if (device->devlink_priority != 0)
return true;
if (!ordered_hashmap_isempty(device->properties_db))
return true;
if (!set_isempty(device->tags))
return true;
if (device->watch_handle >= 0)
return true;
return false;
}
void device_set_db_persist(sd_device *device) {
assert(device);
device->db_persist = true;
}
int device_update_db(sd_device *device) {
const char *id;
char *path;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *path_tmp = NULL;
bool has_info;
int r;
assert(device);
has_info = device_has_info(device);
r = device_get_id_filename(device, &id);
if (r < 0)
return r;
path = strjoina("/run/udev/data/", id);
/* do not store anything for otherwise empty devices */
if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) {
r = unlink(path);
if (r < 0 && errno != ENOENT)
return -errno;
return 0;
}
/* write a database file */
r = mkdir_parents(path, 0755);
if (r < 0)
return r;
r = fopen_temporary(path, &f, &path_tmp);
if (r < 0)
return r;
/*
* set 'sticky' bit to indicate that we should not clean the
* database when we transition from initramfs to the real root
*/
if (device->db_persist) {
r = fchmod(fileno(f), 01644);
if (r < 0) {
r = -errno;
goto fail;
}
} else {
r = fchmod(fileno(f), 0644);
if (r < 0) {
r = -errno;
goto fail;
}
}
if (has_info) {
const char *property, *value, *tag;
Iterator i;
if (major(device->devnum) > 0) {
const char *devlink;
FOREACH_DEVICE_DEVLINK(device, devlink)
fprintf(f, "S:%s\n", devlink + strlen("/dev/"));
if (device->devlink_priority != 0)
fprintf(f, "L:%i\n", device->devlink_priority);
if (device->watch_handle >= 0)
fprintf(f, "W:%i\n", device->watch_handle);
}
if (device->usec_initialized > 0)
fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized);
ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db, i)
fprintf(f, "E:%s=%s\n", property, value);
FOREACH_DEVICE_TAG(device, tag)
fprintf(f, "G:%s\n", tag);
}
r = fflush_and_check(f);
if (r < 0)
goto fail;
r = rename(path_tmp, path);
if (r < 0) {
r = -errno;
goto fail;
}
log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty",
path, device->devpath);
return 0;
fail:
(void) unlink(path);
(void) unlink(path_tmp);
return log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath);
}
int device_delete_db(sd_device *device) {
const char *id;
char *path;
int r;
assert(device);
r = device_get_id_filename(device, &id);
if (r < 0)
return r;
path = strjoina("/run/udev/data/", id);
r = unlink(path);
if (r < 0 && errno != ENOENT)
return -errno;
return 0;
}
int device_read_db_force(sd_device *device) {
assert(device);
return device_read_db_aux(device, true);
}