device-private.c revision ccc1002a1c510b7d4631833eaf60225f028f2280
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier/***
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier This file is part of systemd.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier Copyright 2014 Tom Gundersen <teg@jklm.no>
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier systemd is free software; you can redistribute it and/or modify it
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier under the terms of the GNU Lesser General Public License as published by
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier the Free Software Foundation; either version 2.1 of the License, or
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier (at your option) any later version.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier systemd is distributed in the hope that it will be useful, but
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier WITHOUT ANY WARRANTY; without even the implied warranty of
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier Lesser General Public License for more details.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier You should have received a copy of the GNU Lesser General Public License
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier along with systemd; If not, see <http://www.gnu.org/licenses/>.
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier***/
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include <ctype.h>
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include <sys/types.h>
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include <net/if.h>
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
0d39fa9c69b97a2ceb156053deef69c0866c2b97Lennart Poettering#include "util.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "macro.h"
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include "refcnt.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "path-util.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "strxcpyx.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "fileio.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "hashmap.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "set.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "strv.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "mkdir.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "sd-device.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "device-util.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "device-internal.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier#include "device-private.h"
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalierint device_add_property(sd_device *device, const char *key, const char *value) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier int r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(device);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(key);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier r = device_add_property_aux(device, key, value, false);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (r < 0)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (key[0] != '.') {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier r = device_add_property_aux(device, key, value, true);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (r < 0)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier }
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return 0;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier}
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalierstatic int device_add_property_internal_from_string(sd_device *device, const char *str) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier _cleanup_free_ char *key = NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier char *value;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(device);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(str);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier key = strdup(str);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!key)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return -ENOMEM;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier value = strchr(key, '=');
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (!value)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return -EINVAL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier *value = '\0';
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (isempty(++value))
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier value = NULL;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return device_add_property_internal(device, key, value);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier}
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalierstatic int handle_db_line(sd_device *device, char key, const char *value) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier char *path;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier int r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(device);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier assert(value);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier switch (key) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier case 'S':
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier path = strjoina("/dev/", value);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier r = device_add_devlink(device, path);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (r < 0)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier break;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier case 'L':
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier r = safe_atoi(value, &device->devlink_priority);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (r < 0)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier break;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier case 'E':
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier r = device_add_property_internal_from_string(device, value);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier if (r < 0)
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier return r;
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier break;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier case 'G':
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier r = device_add_tag(device, value);
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier if (r < 0)
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier return r;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier break;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier case 'W':
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier r = safe_atoi(value, &device->watch_handle);
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier if (r < 0)
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier return r;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier break;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier case 'I':
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier r = device_set_usec_initialized(device, value);
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier if (r < 0)
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier return r;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier break;
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier default:
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier log_debug("device db: unknown key '%c'", key);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier }
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier return 0;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier}
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevaliervoid device_set_devlink_priority(sd_device *device, int priority) {
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier assert(device);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier device->devlink_priority = priority;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier}
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevaliervoid device_set_is_initialized(sd_device *device) {
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier assert(device);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier device->is_initialized = true;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier}
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalierint device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier char num[DECIMAL_STR_MAX(usec_t)];
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier usec_t usec_initialized;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier int r;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier assert(device);
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (device_old && device_old->usec_initialized > 0)
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier usec_initialized = device_old->usec_initialized;
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier else
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier usec_initialized = now(CLOCK_MONOTONIC);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (r < 0)
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return -errno;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier r = device_set_usec_initialized(device, num);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (r < 0)
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return r;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return 0;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier}
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalierstatic int device_read_db(sd_device *device) {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier _cleanup_free_ char *db = NULL;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier char *path;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier const char *id, *value;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier char key;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier size_t db_len;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier unsigned i;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier int r;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier enum {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier PRE_KEY,
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier KEY,
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier PRE_VALUE,
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier VALUE,
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier INVALID_LINE,
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier } state = PRE_KEY;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier assert(device);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (device->db_loaded || device->sealed)
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return 0;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier r = device_get_id_filename(device, &id);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (r < 0)
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return r;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier path = strjoina("/run/udev/data/", id);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier r = read_full_file(path, &db, &db_len);
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (r < 0) {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier if (r == -ENOENT)
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return 0;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier else {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier log_debug("sd-device: failed to read db '%s': %s", path, strerror(-r));
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier return r;
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier }
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier }
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier /* devices with a database entry are initialized */
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier device_set_is_initialized(device);
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier for (i = 0; i < db_len; i++) {
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier switch (state) {
2de61bbebfe6a1a97709b3277b150cacc30a79cdRonny Chevalier case PRE_KEY:
d7aeffea144c2c6bfee4e63131bb2b6c460de678Ronny Chevalier if (!strchr(NEWLINE, db[i])) {
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier key = db[i];
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier
0805e9a9c87845be9f801efacc0a358da6991190Ronny Chevalier state = KEY;
4630bbb766d564eafee3bfcd36342fbae8534c15Ronny Chevalier }
4630bbb766d564eafee3bfcd36342fbae8534c15Ronny Chevalier
106ecd769d57116ad81efdea2c27557fba062138Ronny Chevalier break;
case KEY:
if (db[i] != ':') {
log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
state = INVALID_LINE;
} else {
db[i] = '\0';
state = PRE_VALUE;
}
break;
case PRE_VALUE:
value = &db[i];
state = VALUE;
break;
case INVALID_LINE:
if (strchr(NEWLINE, db[i]))
state = PRE_KEY;
break;
case VALUE:
if (strchr(NEWLINE, db[i])) {
db[i] = '\0';
r = handle_db_line(device, key, value);
if (r < 0)
log_debug("sd-device: failed to handle db entry '%c:%s': %s", key, value, strerror(-r));
state = PRE_KEY;
}
break;
default:
assert_not_reached("invalid state when parsing db");
}
}
device->db_loaded = true;
return 0;
}
uint64_t device_get_properties_generation(sd_device *device) {
assert(device);
return device->properties_generation;
}
uint64_t device_get_tags_generation(sd_device *device) {
assert(device);
return device->tags_generation;
}
uint64_t device_get_devlinks_generation(sd_device *device) {
assert(device);
return device->devlinks_generation;
}
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:
log_error_errno(r, "failed to create %s file '%s' for '%s'", has_info ? "db" : "empty",
path, device->devpath);
unlink(path);
unlink(path_tmp);
return r;
}
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);
}