sd-device.c revision 24eb4a30982ed18d4716bd59c454a72f161d5982
/***
This file is part of systemd.
Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
Copyright 2014 Tom Gundersen <teg@jklm.no>
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <ctype.h>
#include "util.h"
#include "macro.h"
#include "path-util.h"
#include "strxcpyx.h"
#include "fileio.h"
#include "hashmap.h"
#include "set.h"
#include "strv.h"
#include "sd-device.h"
#include "device-util.h"
#include "device-private.h"
#include "device-internal.h"
if (!device)
return -ENOMEM;
return 0;
}
if (device)
return device;
}
}
return NULL;
}
if (db)
else
if (_value) {
int r;
if (r < 0)
return r;
if (!key)
return -ENOMEM;
if (!value)
return -ENOMEM;
if (r < 0)
return r;
} else {
}
if (!db) {
device->properties_buf_outdated = true;
}
return 0;
}
}
const char *devpath;
int r;
/* must be a subdirectory of /sys */
return -EINVAL;
}
if (verify) {
if (r == -EINVAL) {
/* not a symlink */
if (!syspath) {
return -errno;
}
/* ignore errors due to the link not being a symlink */
} else if (r < 0 && r != -EINVAL) {
return r;
}
char *path;
/* all 'devices' require an 'uevent' file */
if (r < 0) {
return -errno;
}
} else {
/* everything else just just needs to be a directory */
return -EINVAL;
}
}
} else {
if (!syspath)
return -ENOMEM;
}
if (r < 0)
return r;
return 0;
}
int r;
r = device_new_aux(&device);
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
char *syspath;
}
_public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) {
char *syspath;
char *driver;
if (driver) {
driver[0] = '\0';
driver++;
} else
return -EINVAL;
} else {
}
return -ENOENT;
}
int r;
if (!devtype)
return -ENOMEM;
if (r < 0)
return r;
return 0;
}
int ifindex, r;
if (r < 0)
return r;
if (ifindex <= 0)
return -EINVAL;
if (r < 0)
return r;
return 0;
}
int r;
if (_devname[0] != '/') {
if (r < 0)
return -ENOMEM;
} else {
if (!devname)
return -ENOMEM;
}
if (r < 0)
return r;
return 0;
}
unsigned devmode;
int r;
if (r < 0)
return r;
if (devmode > 07777)
return -EINVAL;
if (r < 0)
return r;
return 0;
}
int r;
if (r < 0)
return r;
if (!maj)
return 0;
if (minor) {
if (r < 0)
return r;
}
if (r < 0)
return r;
if (minor) {
if (r < 0)
return r;
}
return 0;
}
static int handle_uevent_line(sd_device *device, const char *key, const char *value, const char **major, const char **minor) {
int r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
else {
if (r < 0)
return r;
}
return 0;
}
char *path;
unsigned i;
int r;
enum {
KEY,
return 0;
if (r < 0)
return r;
if (r < 0) {
return r;
}
for (i = 0; i < uevent_len; i++) {
switch (state) {
case PRE_KEY:
}
break;
case KEY:
if (uevent[i] == '=') {
uevent[i] = '\0';
uevent[i] = '\0';
}
break;
case PRE_VALUE:
break;
case VALUE:
uevent[i] = '\0';
if (r < 0)
}
break;
default:
assert_not_reached("invalid state when parsing uevent file");
}
}
if (major) {
if (r < 0)
log_debug("sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %s", major, minor, path, strerror(-r));
}
device->uevent_loaded = true;
return 0;
}
int r;
if (r < 0)
return r;
return 0;
}
int r;
switch (id[0]) {
case 'b':
case 'c':
{
char type;
if (r != 3)
return -EINVAL;
}
case 'n':
{
int ifindex;
if (r < 0)
return r;
else if (ifr.ifr_ifindex <= 0)
return -EINVAL;
if (sk < 0)
return -errno;
if (r < 0)
return -errno;
if (r < 0)
return r;
if (r < 0)
return r;
/* this si racey, so we might end up with the wrong device */
return -ENODEV;
return 0;
}
case '+':
{
char *sysname;
if (!sysname)
return -EINVAL;
sysname[0] = '\0';
sysname ++;
}
default:
return -EINVAL;
}
}
return 0;
}
int r;
if (r < 0)
return r;
if (!path)
return -ENOMEM;
for (;;) {
char *pos;
break;
*pos = '\0';
if (r < 0)
continue;
return 0;
}
return -ENOENT;
}
if (!child->parent_set) {
child->parent_set = true;
}
return -ENOENT;
return 0;
}
int r;
if (!subsystem)
return -ENOMEM;
if (r < 0)
return r;
device->subsystem_set = true;
return 0;
}
if (!device->subsystem_set) {
const char *syspath;
char *path;
int r;
/* read 'subsystem' link */
if (r < 0)
return r;
if (r >= 0)
/* use implicit names */
if (r < 0)
return r;
device->subsystem_set = true;
}
return 0;
}
int r;
if (r < 0)
return r;
return 0;
}
_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
int r;
while (r >= 0) {
const char *parent_subsystem = NULL;
const char *parent_devtype = NULL;
if (!devtype)
break;
break;
}
}
if (r < 0)
return r;
return 0;
}
int r;
if (r < 0)
return r;
return 0;
}
int r;
if (!driver)
return -ENOMEM;
if (r < 0)
return r;
device->driver_set = true;
return 0;
}
if (!device->driver_set) {
const char *syspath;
char *path;
int r;
if (r < 0)
return r;
if (r >= 0) {
if (r < 0)
return r;
}
}
return 0;
}
return 0;
}
int r;
if (r < 0)
return r;
return -ENOENT;
return 0;
}
const char *pos;
if (!pos)
return -EINVAL;
pos ++;
/* devpath is not a root directory */
return -EINVAL;
if (!sysname)
return -ENOMEM;
/* some devices have '!' in their name, change that to '/' */
len ++;
}
/* trailing number */
if (len == 0)
device->sysname_set = true;
return 0;
}
int r;
if (!device->sysname_set) {
r = device_set_sysname(device);
if (r < 0)
return r;
}
return 0;
}
int r;
if (!device->sysname_set) {
r = device_set_sysname(device);
if (r < 0)
return r;
}
return 0;
}
static bool is_valid_tag(const char *tag) {
}
int r;
if (!is_valid_tag(tag))
return -EINVAL;
if (r < 0)
return r;
if (r < 0)
return r;
device->tags_generation ++;
device->property_tags_outdated = true;
return 0;
}
int r;
if (r < 0)
return r;
if (r < 0)
return r;
device->property_devlinks_outdated = true;
return 0;
}
char *value;
if (!key)
return -ENOMEM;
if (!value)
return -EINVAL;
*value = '\0';
}
int r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
char *path;
int r;
switch (key) {
case 'G':
if (r < 0)
return r;
break;
case 'S':
if (r < 0)
return r;
break;
case 'E':
if (r < 0)
return r;
break;
case 'I':
if (r < 0)
return r;
break;
case 'L':
if (r < 0)
return r;
break;
case 'W':
if (r < 0)
return r;
break;
default:
}
return 0;
}
if (!device->id_filename) {
const char *subsystem;
int ifindex, r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
/* use dev_t -- b259:131072, c254:0 */
if (r < 0)
return -errno;
} else if (ifindex > 0) {
/* use netdev ifindex -- n3 */
if (r < 0)
return -errno;
} else {
/* use $subsys:$sysname -- pci:0000:00:1f.2
* sysname() has '!' translated, get it from devpath
*/
const char *sysname;
if (!sysname)
return -EINVAL;
if (r < 0)
return -errno;
}
}
return 0;
}
char *path;
char key;
unsigned i;
int r;
enum {
KEY,
return 0;
if (r < 0)
return r;
if (r < 0) {
if (r == -ENOENT)
return 0;
else {
return r;
}
}
/* devices with a database entry are initialized */
device->is_initialized = true;;
for (i = 0; i < db_len; i++) {
switch (state) {
case PRE_KEY:
}
break;
case KEY:
if (db[i] != ':') {
} else {
db[i] = '\0';
}
break;
case PRE_VALUE:
break;
case INVALID_LINE:
break;
case VALUE:
db[i] = '\0';
if (r < 0)
}
break;
default:
assert_not_reached("invalid state when parsing db");
}
}
return 0;
}
int r;
r = device_read_db(device);
if (r < 0)
return r;
return 0;
}
int r;
r = device_read_db(device);
if (r < 0)
return r;
if (!device->is_initialized)
return -EBUSY;
if (!device->usec_initialized)
return -ENODATA;
return -EIO;
return 0;
}
(void) device_read_db(device);
}
(void) device_read_db(device);
return NULL;
}
(void) device_read_db(device);
}
(void) device_read_db(device);
return NULL;
}
int r;
if (r < 0)
return r;
r = device_read_db(device);
if (r < 0)
return r;
if (device->property_devlinks_outdated) {
const char *devlink;
if (devlink)
if (r < 0)
return r;
device->property_devlinks_outdated = false;
}
if (device->property_tags_outdated) {
const char *tag;
if (tag)
if (r < 0)
return r;
device->property_tags_outdated = false;
}
return 0;
}
const char *key;
const char *value;
int r;
if (r < 0)
return NULL;
value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
if (_value)
return key;
}
const char *key;
const char *value;
int r;
if (r < 0)
return NULL;
return NULL;
value = ordered_hashmap_iterate(device->properties, &device->properties_iterator, (const void**)&key);
if (_value)
return key;
}
const char *syspath;
int r;
if (device->sysattrs_read)
return 0;
if (r < 0)
return r;
if (!dir)
return -errno;
if (r < 0)
return r;
char *path;
/* only handle symlinks and regular files */
continue;
continue;
continue;
if (r < 0)
return r;
}
device->sysattrs_read = true;
return 0;
}
int r;
if (!device->sysattrs_read) {
if (r < 0) {
errno = -r;
return NULL;
}
}
}
if (!device->sysattrs_read)
return NULL;
}
(void) device_read_db(device);
}
_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) {
char *value;
int r;
if (r < 0)
return r;
if (!value)
return -ENOENT;
return 0;
}
/* replaces the value if it already exists */
int r;
if (r < 0)
return r;
if (!key) {
if (!key)
return -ENOMEM;
}
if (r < 0)
return r;
return 0;
}
if (!key)
return -ENOENT;
if (_value)
return 0;
}
/* We cache all sysattr lookups. If an attribute does not exist, it is stored
* with a NULL value in the cache, otherwise the returned string is stored */
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
char *path;
int r;
/* look for possibly already cached result */
if (r != -ENOENT) {
if (r < 0)
return r;
if (!cached_value)
/* we looked up the sysattr before and it did not exist */
return -ENOENT;
if (_value)
*_value = cached_value;
return 0;
}
if (r < 0)
return r;
if (r < 0) {
/* remember that we could not access the sysattr */
if (r < 0)
return r;
return -ENOENT;
/* Some core links return only the last element of the target path,
* these are just values, the paths should not be exposed. */
if (r < 0)
return r;
} else
return -EINVAL;
/* skip directories */
return -EINVAL;
/* skip non-readable files */
return -EPERM;
} else {
/* read attribute value */
if (r < 0)
return r;
/* drop trailing newlines */
}
if (r < 0)
return r;
return 0;
}
return;
}
/* set the attribute and save it in the cache. If a NULL value is passed the
* attribute is cleared from the cache */
const char *syspath;
char *path;
int r;
if (!_value) {
return 0;
}
if (r < 0)
return r;
if (r < 0) {
if (!value)
return -ENOMEM;
if (r < 0)
return r;
return -ENXIO;
}
return -EINVAL;
/* skip directories */
return -EISDIR;
/* skip non-readable files */
return -EACCES;
/* drop trailing newlines */
/* value length is limited to 4k */
if (value_len > 4096)
return -EINVAL;
if (fd < 0)
return -errno;
if (!value)
return -ENOMEM;
if (size < 0)
return -errno;
return -EIO;
if (r < 0)
return r;
return 0;
}