[PATCH] replace tdb database by simple lockless file database

This makes the udev operation completely lockless by storing a
file for every node in /dev/.udevdb/* This solved the problem
with deadlocking concurrent udev processes waiting for each other
to release the file lock under heavy load.
This commit is contained in:
kay.sievers@vrfy.org 2004-11-06 14:28:01 +01:00 committed by Greg KH
parent 482b0ecd8f
commit 2b41e68a08
41 changed files with 326 additions and 4689 deletions

View file

@ -39,10 +39,10 @@ NOTE NOTE NOTE NOTE NOTE NOTE NOTE
the end of this file.
- make sure the /etc/udev/udev.conf file lists the udev_root as "/dev/"
and the udev_db as "/dev/.udev.tdb". It should contain the
and the udev_db as "/dev/.udevdb". It should contain the
following lines in order to work properly.
udev_root="/dev/"
udev_db="/dev/.udev.tdb"
udev_db="/dev/.udevdb"
- reboot into a 2.6 kernel and watch udev create all of the initial
device nodes in /dev

View file

@ -68,7 +68,8 @@ INSTALL_SCRIPT = ${INSTALL_PROGRAM}
EXTRAS=
# place to put our device nodes
udevdir = ${prefix}/udev
udevdir = ${prefix}/udev
udevdb = ${udevdir}/.udevdb
# Comment out this line to build with something other
# than the local version of klibc
@ -113,11 +114,6 @@ WARNINGS := -Wall
CFLAGS := -pipe
# set up the proper tdb spinlock code if we can
ifeq ($(strip $(ARCH)),i386)
CFLAGS += -DUSE_SPINLOCKS -DINTEL_SPINLOCKS
endif
ifeq ($(strip $(USE_LOG)),true)
CFLAGS += -DLOG
endif
@ -201,9 +197,6 @@ $(CRT0):
fi
$(MAKE) -C klibc SUBDIRS=klibc
TDB = tdb/tdb.o \
tdb/spinlock.o
SYSFS = $(PWD)/libsysfs/sysfs_bus.o \
$(PWD)/libsysfs/sysfs_class.o \
$(PWD)/libsysfs/sysfs_device.o \
@ -221,8 +214,7 @@ OBJS = udev_lib.o \
namedev.o \
namedev_parse.o \
dev_d.o \
$(SYSFS) \
$(TDB)
$(SYSFS)
HEADERS = udev.h \
udev_lib.h \
@ -262,8 +254,8 @@ ccdv:
udev_version.h:
@echo "Creating udev_version.h"
@echo \#define UDEV_VERSION \"$(VERSION)\" > $@
@echo \#define UDEV_ROOT \"$(udevdir)/\" >> $@
@echo \#define UDEV_DB \"$(udevdir)/.udev.tdb\" >> $@
@echo \#define UDEV_ROOT \"$(udevdir)\" >> $@
@echo \#define UDEV_DB \"$(udevdb)\" >> $@
@echo \#define UDEV_CONFIG_DIR \"$(configdir)\" >> $@
@echo \#define UDEV_CONFIG_FILE \"$(configdir)/udev.conf\" >> $@
@echo \#define UDEV_RULES_FILE \"$(configdir)/rules.d\" >> $@
@ -300,7 +292,7 @@ $(TESTER): $(LIBC) $(TESTER).o $(OBJS) $(HEADERS)
$(QUIET) $(STRIPCMD) $@
$(INFO): $(LIBC) $(INFO).o $(OBJS) $(HEADERS)
$(QUIET) $(LD) $(LDFLAGS) -o $@ $(CRT0) udevinfo.o udev_lib.o udev_config.o udevdb.o $(SYSFS) $(TDB) $(LIB_OBJS) $(ARCH_LIB_OBJS)
$(QUIET) $(LD) $(LDFLAGS) -o $@ $(CRT0) udevinfo.o udev_lib.o udev_config.o udevdb.o $(SYSFS) $(LIB_OBJS) $(ARCH_LIB_OBJS)
$(QUIET) $(STRIPCMD) $@
$(DAEMON): $(LIBC) $(DAEMON).o $(OBJS) udevd.h
@ -337,7 +329,7 @@ spotless: clean
$(MAKE) -C klibc spotless
-rm -f klibc/linux
DISTFILES = $(shell find . \( -not -name '.' \) -print | grep -v -e CVS -e "\.tar\.gz$" -e "\/\." -e releases -e BitKeeper -e SCCS -e "\.tdb$" -e test/sys | sort )
DISTFILES = $(shell find . \( -not -name '.' \) -print | grep -v -e CVS -e "\.tar\.gz" -e "\/\." -e releases -e BitKeeper -e SCCS -e test/sys | sort )
DISTDIR := $(RELEASE_NAME)
srcdir = .
release: clean
@ -429,8 +421,8 @@ install: install-initscript install-config install-man install-dev.d all
- ln -f -s $(sbindir)/$(SENDER) $(DESTDIR)$(hotplugdir)/10-udev.hotplug
- ln -f -s $(sbindir)/$(WAIT) $(DESTDIR)$(hotplugdir)/05-wait_for_sysfs.hotplug
ifndef DESTDIR
- killall udevd
- rm -f $(udevdir)/.udev.tdb
- killall $(DAEMON)
- rm -rf $(udevdb)
endif
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
@ -456,8 +448,9 @@ uninstall: uninstall-man uninstall-dev.d
- rm $(usrbindir)/$(TESTER)
- rm $(usrbindir)/$(WAIT)
- rmdir $(hotplugdir)
- rm $(udevdir)/.udev.tdb
- rm -rf $(udevdb)
- rmdir $(udevdir)
- killall $(DAEMON)
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
$(MAKE) prefix=$(prefix) LD="$(LD)" SYSFS="$(SYSFS)" \

View file

@ -47,7 +47,6 @@ static int run_program(char *name)
switch (pid) {
case 0:
/* child */
udevdb_exit(); /* close udevdb */
fd = open("/dev/null", O_RDWR);
if ( fd >= 0) {
dup2(fd, STDOUT_FILENO);

View file

@ -110,10 +110,8 @@ uses:
(1) /sys maintained by sysfs
(2) /etc/udev/udev.rules - where you can store the identifier to NAME
mapping information.
(3) The tdb (udev-021/tdb/tdb.c), trivial data base, that is held in
memory and holds the valid system configuration. It is not saved
between one boot to the next. It is constructed at boot time and
updated with configuration changes.
(3) The udevdb, that keeps track the valid system configuration.
It is constructed at boot time and updated with configuration changes.
The persistent names are kept (at least this is one way to do it) in
udev.rules (uuid and NAME), one entry per device. If you want to initially
@ -148,9 +146,9 @@ the device. The result of the program execution (the uuid) is compared
with the RESULT entry in the same udev.rules line.
- If it matches, then the NAME entered on this line is used. The uuid and
major/minor number is saved in tdb (newly recreated upon boot). That
device is created in /udev (the target directory name is configurable)
with the assigned NAME.
major/minor number is saved in the udevdb (newly recreated upon boot).
That device is created in /udev (the target directory name is configurable)
with the assigned NAME.
- If it doesn't match, the RESULT (uuid) is preserved for use on the next
udev.rules line as long as the bus type (scsi) is the same. So the
@ -159,18 +157,18 @@ with the RESULT entry in the same udev.rules line.
- If no match occurs, the device will be assigned a default name.
- Tdb is updated with the resulting name assignment.
- The udevdb is updated with the resulting name assignment.
Thus if the uuid and names are enumerated, they will be found, assigned,
and are therefore permanent.
If the device is removed from a live system, a hotplug event occurs, and it
is removed from tdb and the /udev entry disappears.
is removed from udevdb and the /udev entry disappears.
If it is re-inserted at a new location, the udev.rules file is scanned as
above. The new major/minor number goes in tdb with the uuid , the name in
udev.rules is found again, and the /udev name re-appears.
above. The rule matches again against the uuid, the name in udev.rules
is applied again and the /udev name re-appears.

View file

@ -79,8 +79,8 @@ case "$1" in
fi
# remove the database if it is there as we always want to start fresh
if [ -f $udev_root/.udev.tdb ]; then
rm -f $udev_root/.udev.tdb
if [ -f $udev_root/.udevdb ]; then
rm -rf $udev_root/.udevdb
fi
# propogate /udev from /sys - we only need this while we do not

View file

@ -6,16 +6,16 @@
# udev_root - where in the filesystem to place the device nodes
udev_root="@udevdir@/"
udev_root="@udevdir@"
# udev_db - The name and location of the udev database.
udev_db="@udevdir@/.udev.tdb"
udev_db="@udevdir@/.udevdb"
# udev_rules - The name and location of the udev rules file
udev_rules="@configdir@/rules.d/"
udev_rules="@configdir@/rules.d"
# udev_permissions - The name and location of the udev permission file
udev_permissions="@configdir@/permissions.d/"
udev_permissions="@configdir@/permissions.d"
# default_mode - set the default mode for all nodes that have no
# explicit match in the permissions file

View file

@ -184,29 +184,29 @@ static int get_format_len(char **str)
* @param name Name to check for
* @return 0 if <name> didn't exist and N otherwise.
*/
static unsigned int find_free_number (struct udevice *udev, char *name)
static int find_free_number(struct udevice *udev, const char *name)
{
char temp[NAME_SIZE];
char path[NAME_SIZE];
struct udevice dev;
int result;
char filename[NAME_SIZE];
int num = 0;
struct udevice db_udev;
/* have to sweep the database for each lookup */
result = 0;
strncpy(temp, name, sizeof (temp));
strfieldcpy(filename, name);
while (1) {
if (udevdb_get_dev_byname(temp, path, &dev) != 0)
goto found;
/* symlink might be stale if $(udevroot) isn't cleaned; check
* on major/minor to see if it's the same device
*/
if (dev.major == udev->major && dev.minor == udev->minor)
goto found;
snprintf (temp, sizeof(temp), "%s%d", name, ++result);
}
dbg("look for existing node '%s'", filename);
memset(&db_udev, 0x00, sizeof(struct udevice));
if (udevdb_get_dev_byname(&db_udev, filename) != 0) {
dbg("free num=%d", num);
return num;
}
found:
return result;
num++;
if (num > 1000) {
info("find_free_number gone crazy (num=%d), aborted", num);
return -1;
}
snprintf(filename, NAME_SIZE-1, "%s%d", name, num);
filename[NAME_SIZE-1] = '\0';
}
}
static void apply_format(struct udevice *udev, char *string, size_t maxsize,
@ -329,7 +329,7 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
case 'e':
next_free_number = find_free_number(udev, string);
if (next_free_number > 0) {
snprintf(temp2, sizeof(temp2), "%d", next_free_number);
sprintf(temp2, "%d", next_free_number);
strfieldcatmax(string, temp2, maxsize);
}
break;

View file

@ -1,32 +0,0 @@
#
# Makefile for tdb directory
#
CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1
PROGS = tdbtest tdbtool tdbtorture
TDB_OBJ = tdb.o spinlock.o
default: $(TDB_OBJ)
progs: $(PROGS)
tdbtest: tdbtest.o $(TDB_OBJ)
$(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm
tdbtool: tdbtool.o $(TDB_OBJ)
$(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ)
tdbtorture: tdbtorture.o $(TDB_OBJ)
$(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ)
tdbdump: tdbdump.o $(TDB_OBJ)
$(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ)
tdbbackup: tdbbackup.o $(TDB_OBJ)
$(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ)
clean:
rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm
spotless: clean

View file

@ -1,167 +0,0 @@
tdb - a trivial database system
tridge@linuxcare.com December 1999
==================================
This is a simple database API. It was inspired by the realisation that
in Samba we have several ad-hoc bits of code that essentially
implement small databases for sharing structures between parts of
Samba. As I was about to add another I realised that a generic
database module was called for to replace all the ad-hoc bits.
I based the interface on gdbm. I couldn't use gdbm as we need to be
able to have multiple writers to the databases at one time.
Compilation
-----------
add HAVE_MMAP=1 to use mmap instead of read/write
add TDB_DEBUG=1 for verbose debug info
add NOLOCK=1 to disable locking code
Testing
-------
Compile tdbtest.c and link with gdbm for testing. tdbtest will perform
identical operations via tdb and gdbm then make sure the result is the
same
Also included is tdbtool, which allows simple database manipulation
on the commandline.
tdbtest and tdbtool are not built as part of Samba, but are included
for completeness.
Interface
---------
The interface is very similar to gdbm except for the following:
- different open interface. The tdb_open call is more similar to a
traditional open()
- no tdbm_reorganise() function
- no tdbm_sync() function. No operations are cached in the library anyway
- added a tdb_traverse() function for traversing the whole database
A general rule for using tdb is that the caller frees any returned
TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA
return value called p. This is the same as gdbm.
here is a full list of tdb functions with brief descriptions.
----------------------------------------------------------------------
TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode)
open the database, creating it if necessary
The open_flags and mode are passed straight to the open call on the database
file. A flags value of O_WRONLY is invalid
The hash size is advisory, use zero for a default value.
return is NULL on error
possible tdb_flags are:
TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open
TDB_INTERNAL - don't use a file, instaed store the data in
memory. The filename is ignored in this case.
TDB_NOLOCK - don't do any locking
TDB_NOMMAP - don't use mmap
----------------------------------------------------------------------
char *tdb_error(TDB_CONTEXT *tdb);
return a error string for the last tdb error
----------------------------------------------------------------------
int tdb_close(TDB_CONTEXT *tdb);
close a database
----------------------------------------------------------------------
int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf);
update an entry in place - this only works if the new data size
is <= the old data size and the key exists.
on failure return -1
----------------------------------------------------------------------
TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
fetch an entry in the database given a key
if the return value has a null dptr then a error occurred
caller must free the resulting data
----------------------------------------------------------------------
int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
check if an entry in the database exists
note that 1 is returned if the key is found and 0 is returned if not found
this doesn't match the conventions in the rest of this module, but is
compatible with gdbm
----------------------------------------------------------------------
int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb,
TDB_DATA key, TDB_DATA dbuf, void *state), void *state);
traverse the entire database - calling fn(tdb, key, data, state) on each
element.
return -1 on error or the record count traversed
if fn is NULL then it is not called
a non-zero return value from fn() indicates that the traversal should stop
----------------------------------------------------------------------
TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
find the first entry in the database and return its key
the caller must free the returned data
----------------------------------------------------------------------
TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
find the next entry in the database, returning its key
the caller must free the returned data
----------------------------------------------------------------------
int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
delete an entry in the database given a key
----------------------------------------------------------------------
int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
store an element in the database, replacing any existing element
with the same key
If flag==TDB_INSERT then don't overwrite an existing entry
If flag==TDB_MODIFY then don't create a new entry
return 0 on success, -1 on failure
----------------------------------------------------------------------
int tdb_writelock(TDB_CONTEXT *tdb);
lock the database. If we already have it locked then don't do anything
----------------------------------------------------------------------
int tdb_writeunlock(TDB_CONTEXT *tdb);
unlock the database
----------------------------------------------------------------------
int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key);
lock one hash chain. This is meant to be used to reduce locking
contention - it cannot guarantee how many records will be locked
----------------------------------------------------------------------
int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key);
unlock one hash chain

View file

@ -1,433 +0,0 @@
/*
Unix SMB/CIFS implementation.
Samba database functions
Copyright (C) Anton Blanchard 2001
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* udev defines */
#define STANDALONE
#define TDB_DEBUG
#define HAVE_MMAP 1
#if HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef STANDALONE
#define _KLIBC_HAS_ARCH_SIG_ATOMIC_T
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include <signal.h>
#include "tdb.h"
#include "spinlock.h"
#include "../udev.h"
#include "../logging.h"
#else
#include "includes.h"
#endif
#ifdef USE_SPINLOCKS
/*
* ARCH SPECIFIC
*/
#if defined(SPARC_SPINLOCKS)
static inline int __spin_trylock(spinlock_t *lock)
{
unsigned int result;
asm volatile("ldstub [%1], %0"
: "=r" (result)
: "r" (lock)
: "memory");
return (result == 0) ? 0 : EBUSY;
}
static inline void __spin_unlock(spinlock_t *lock)
{
asm volatile("":::"memory");
*lock = 0;
}
static inline void __spin_lock_init(spinlock_t *lock)
{
*lock = 0;
}
static inline int __spin_is_locked(spinlock_t *lock)
{
return (*lock != 0);
}
#elif defined(POWERPC_SPINLOCKS)
static inline int __spin_trylock(spinlock_t *lock)
{
unsigned int result;
__asm__ __volatile__(
"1: lwarx %0,0,%1\n\
cmpwi 0,%0,0\n\
li %0,0\n\
bne- 2f\n\
li %0,1\n\
stwcx. %0,0,%1\n\
bne- 1b\n\
isync\n\
2:" : "=&r"(result)
: "r"(lock)
: "cr0", "memory");
return (result == 1) ? 0 : EBUSY;
}
static inline void __spin_unlock(spinlock_t *lock)
{
asm volatile("eieio":::"memory");
*lock = 0;
}
static inline void __spin_lock_init(spinlock_t *lock)
{
*lock = 0;
}
static inline int __spin_is_locked(spinlock_t *lock)
{
return (*lock != 0);
}
#elif defined(INTEL_SPINLOCKS)
static inline int __spin_trylock(spinlock_t *lock)
{
int oldval;
asm volatile("xchgl %0,%1"
: "=r" (oldval), "=m" (*lock)
: "0" (0)
: "memory");
return oldval > 0 ? 0 : EBUSY;
}
static inline void __spin_unlock(spinlock_t *lock)
{
asm volatile("":::"memory");
*lock = 1;
}
static inline void __spin_lock_init(spinlock_t *lock)
{
*lock = 1;
}
static inline int __spin_is_locked(spinlock_t *lock)
{
return (*lock != 1);
}
#elif defined(MIPS_SPINLOCKS)
static inline unsigned int load_linked(unsigned long addr)
{
unsigned int res;
__asm__ __volatile__("ll\t%0,(%1)"
: "=r" (res)
: "r" (addr));
return res;
}
static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
{
unsigned int res;
__asm__ __volatile__("sc\t%0,(%2)"
: "=r" (res)
: "0" (value), "r" (addr));
return res;
}
static inline int __spin_trylock(spinlock_t *lock)
{
unsigned int mw;
do {
mw = load_linked(lock);
if (mw)
return EBUSY;
} while (!store_conditional(lock, 1));
asm volatile("":::"memory");
return 0;
}
static inline void __spin_unlock(spinlock_t *lock)
{
asm volatile("":::"memory");
*lock = 0;
}
static inline void __spin_lock_init(spinlock_t *lock)
{
*lock = 0;
}
static inline int __spin_is_locked(spinlock_t *lock)
{
return (*lock != 0);
}
#else
#error Need to implement spinlock code in spinlock.c
#endif
/*
* OS SPECIFIC
*/
static void yield_cpu(void)
{
struct timespec tm;
#ifdef USE_SCHED_YIELD
sched_yield();
#else
/* Linux will busy loop for delays < 2ms on real time tasks */
tm.tv_sec = 0;
tm.tv_nsec = 2000000L + 1;
nanosleep(&tm, NULL);
#endif
}
static int this_is_smp(void)
{
return 0;
}
/*
* GENERIC
*/
static int smp_machine = 0;
static inline void __spin_lock(spinlock_t *lock)
{
int ntries = 0;
while(__spin_trylock(lock)) {
while(__spin_is_locked(lock)) {
if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
continue;
yield_cpu();
}
}
}
static void __read_lock(tdb_rwlock_t *rwlock)
{
int ntries = 0;
while(1) {
__spin_lock(&rwlock->lock);
if (!(rwlock->count & RWLOCK_BIAS)) {
rwlock->count++;
__spin_unlock(&rwlock->lock);
return;
}
__spin_unlock(&rwlock->lock);
while(rwlock->count & RWLOCK_BIAS) {
if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
continue;
yield_cpu();
}
}
}
static void __write_lock(tdb_rwlock_t *rwlock)
{
int ntries = 0;
while(1) {
__spin_lock(&rwlock->lock);
if (rwlock->count == 0) {
rwlock->count |= RWLOCK_BIAS;
__spin_unlock(&rwlock->lock);
return;
}
__spin_unlock(&rwlock->lock);
while(rwlock->count != 0) {
if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
continue;
yield_cpu();
}
}
}
static void __write_unlock(tdb_rwlock_t *rwlock)
{
__spin_lock(&rwlock->lock);
if (!(rwlock->count & RWLOCK_BIAS))
dbg("bug: write_unlock");
rwlock->count &= ~RWLOCK_BIAS;
__spin_unlock(&rwlock->lock);
}
static void __read_unlock(tdb_rwlock_t *rwlock)
{
__spin_lock(&rwlock->lock);
if (!rwlock->count)
dbg("bug: read_unlock");
if (rwlock->count & RWLOCK_BIAS)
dbg("bug: read_unlock");
rwlock->count--;
__spin_unlock(&rwlock->lock);
}
/* TDB SPECIFIC */
/* lock a list in the database. list -1 is the alloc list */
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
{
tdb_rwlock_t *rwlocks;
if (!tdb->map_ptr) return -1;
rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
switch(rw_type) {
case F_RDLCK:
__read_lock(&rwlocks[list+1]);
break;
case F_WRLCK:
__write_lock(&rwlocks[list+1]);
break;
default:
return TDB_ERRCODE(TDB_ERR_LOCK, -1);
}
return 0;
}
/* unlock the database. */
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
{
tdb_rwlock_t *rwlocks;
if (!tdb->map_ptr) return -1;
rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
switch(rw_type) {
case F_RDLCK:
__read_unlock(&rwlocks[list+1]);
break;
case F_WRLCK:
__write_unlock(&rwlocks[list+1]);
break;
default:
return TDB_ERRCODE(TDB_ERR_LOCK, -1);
}
return 0;
}
int tdb_create_rwlocks(int fd, unsigned int hash_size)
{
unsigned size, i;
tdb_rwlock_t *rwlocks;
size = (hash_size + 1) * sizeof(tdb_rwlock_t);
rwlocks = malloc(size);
if (!rwlocks)
return -1;
for(i = 0; i < hash_size+1; i++) {
__spin_lock_init(&rwlocks[i].lock);
rwlocks[i].count = 0;
}
/* Write it out (appending to end) */
if (write(fd, rwlocks, size) != size) {
free(rwlocks);
return -1;
}
smp_machine = this_is_smp();
free(rwlocks);
return 0;
}
int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
{
tdb_rwlock_t *rwlocks;
unsigned i;
if (tdb->header.rwlocks == 0) return 0;
if (!tdb->map_ptr) return -1;
/* We're mmapped here */
rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
for(i = 0; i < tdb->header.hash_size+1; i++) {
__spin_lock_init(&rwlocks[i].lock);
rwlocks[i].count = 0;
}
return 0;
}
#else
int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
/* Non-spinlock version: remove spinlock pointer */
int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
{
tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
- (char *)&tdb->header);
tdb->header.rwlocks = 0;
if (lseek(tdb->fd, off, SEEK_SET) != off
|| write(tdb->fd, (void *)&tdb->header.rwlocks,
sizeof(tdb->header.rwlocks))
!= sizeof(tdb->header.rwlocks))
return -1;
return 0;
}
#endif

View file

@ -1,55 +0,0 @@
#ifndef __SPINLOCK_H__
#define __SPINLOCK_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "tdb.h"
#ifdef USE_SPINLOCKS
#define RWLOCK_BIAS 0x1000UL
/* OS SPECIFIC */
#define MAX_BUSY_LOOPS 1000
#undef USE_SCHED_YIELD
/* ARCH SPECIFIC */
/* We should make sure these are padded to a cache line */
#if defined(SPARC_SPINLOCKS)
typedef volatile char spinlock_t;
#elif defined(POWERPC_SPINLOCKS)
typedef volatile unsigned long spinlock_t;
#elif defined(INTEL_SPINLOCKS)
typedef volatile int spinlock_t;
#elif defined(MIPS_SPINLOCKS)
typedef volatile unsigned long spinlock_t;
#else
#error Need to implement spinlock code in spinlock.h
#endif
typedef struct {
spinlock_t lock;
volatile int count;
} tdb_rwlock_t;
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
int tdb_create_rwlocks(int fd, unsigned int hash_size);
int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
#else /* !USE_SPINLOCKS */
#if 0
#define tdb_create_rwlocks(fd, hash_size) 0
#define tdb_spinlock(tdb, list, rw_type) (-1)
#define tdb_spinunlock(tdb, list, rw_type) (-1)
#else
int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type);
int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type);
int tdb_create_rwlocks(int fd, unsigned int hash_size);
#endif
int tdb_clear_spinlocks(TDB_CONTEXT *tdb);
#endif
#endif

2099
tdb/tdb.c

File diff suppressed because it is too large Load diff

150
tdb/tdb.h
View file

@ -1,150 +0,0 @@
#ifndef __TDB_H__
#define __TDB_H__
/*
Unix SMB/CIFS implementation.
Samba database functions
Copyright (C) Andrew Tridgell 1999
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define _KLIBC_HAS_ARCH_SIG_ATOMIC_T
#include <signal.h>
/* flags to tdb_store() */
#define TDB_REPLACE 1
#define TDB_INSERT 2
#define TDB_MODIFY 3
/* flags for tdb_open() */
#define TDB_DEFAULT 0 /* just a readability place holder */
#define TDB_CLEAR_IF_FIRST 1
#define TDB_INTERNAL 2 /* don't store on disk */
#define TDB_NOLOCK 4 /* don't do any locking */
#define TDB_NOMMAP 8 /* don't use mmap */
#define TDB_CONVERT 16 /* convert endian (internal use) */
#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */
#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret)
/* error codes */
enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK,
TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOEXIST, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT };
#ifndef u32
#define u32 unsigned
#endif
typedef struct {
char *dptr;
size_t dsize;
} TDB_DATA;
typedef u32 tdb_len;
typedef u32 tdb_off;
/* this is stored at the front of every database */
struct tdb_header {
char magic_food[32]; /* for /etc/magic */
u32 version; /* version of the code */
u32 hash_size; /* number of hash entries */
tdb_off rwlocks;
tdb_off reserved[31];
};
struct tdb_lock_type {
u32 count;
u32 ltype;
};
struct tdb_traverse_lock {
struct tdb_traverse_lock *next;
u32 off;
u32 hash;
};
/* this is the context structure that is returned from a db open */
typedef struct tdb_context {
char *name; /* the name of the database */
void *map_ptr; /* where it is currently mapped */
int fd; /* open file descriptor for the database */
tdb_len map_size; /* how much space has been mapped */
int read_only; /* opened read-only */
struct tdb_lock_type *locked; /* array of chain locks */
enum TDB_ERROR ecode; /* error code for last tdb error */
struct tdb_header header; /* a cached copy of the header */
u32 flags; /* the flags passed to tdb_open */
u32 *lockedkeys; /* array of locked keys: first is #keys */
struct tdb_traverse_lock travlocks; /* current traversal locks */
struct tdb_context *next; /* all tdbs to avoid multiple opens */
dev_t device; /* uniquely identifies this tdb */
ino_t inode; /* uniquely identifies this tdb */
void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...); /* logging function */
int open_flags; /* flags used in the open - needed by reopen */
} TDB_CONTEXT;
typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *);
typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...);
TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode);
TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode,
tdb_log_func log_fn);
int tdb_reopen(TDB_CONTEXT *tdb);
int tdb_reopen_all(void);
void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func);
enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb);
const char *tdb_errorstr(TDB_CONTEXT *tdb);
TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag);
int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf);
int tdb_close(TDB_CONTEXT *tdb);
TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb);
TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state);
int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]);
void tdb_unlockkeys(TDB_CONTEXT *tdb);
int tdb_lockall(TDB_CONTEXT *tdb);
void tdb_unlockall(TDB_CONTEXT *tdb);
/* Low level locking functions: use with care */
void tdb_set_lock_alarm(sig_atomic_t *palarm);
int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key);
/* Debug functions. Not used in production. */
void tdb_dump_all(TDB_CONTEXT *tdb);
int tdb_printfreelist(TDB_CONTEXT *tdb);
/* used only in tdbutil.c */
int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key);
int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key);
extern TDB_DATA tdb_null;
#ifdef __cplusplus
}
#endif
#endif /* tdb.h */

View file

@ -1,10 +0,0 @@
# Magic file(1) information about tdb files.
#
# Install this into /etc/magic or the corresponding location for your
# system, or pass as a -m argument to file(1).
# You may use and redistribute this file without restriction.
0 string TDB\ file TDB database
>32 lelong =0x2601196D version 6, little-endian
>>36 lelong x hash size %d bytes

View file

@ -1,201 +0,0 @@
/*
Unix SMB/CIFS implementation.
low level tdb backup and restore utility
Copyright (C) Andrew Tridgell 2002
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <ctype.h>
#include <signal.h>
#include "tdb.h"
static int failed;
char *add_suffix(const char *name, const char *suffix)
{
char *ret;
int len = strlen(name) + strlen(suffix) + 1;
ret = malloc(len);
if (!ret) {
fprintf(stderr,"Out of memory!\n");
exit(1);
}
strncpy(ret, name, len);
strncat(ret, suffix, len);
return ret;
}
static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
fprintf(stderr,"Failed to insert into %s\n", tdb_new->name);
failed = 1;
return 1;
}
return 0;
}
static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
return 0;
}
/*
carefully backup a tdb, validating the contents and
only doing the backup if its OK
this function is also used for restore
*/
int backup_tdb(const char *old_name, const char *new_name)
{
TDB_CONTEXT *tdb;
TDB_CONTEXT *tdb_new;
char *tmp_name;
struct stat st;
int count1, count2;
tmp_name = add_suffix(new_name, ".tmp");
/* stat the old tdb to find its permissions */
if (stat(old_name, &st) != 0) {
perror(old_name);
return 1;
}
/* open the old tdb */
tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
if (!tdb) {
printf("Failed to open %s\n", old_name);
return 1;
}
/* create the new tdb */
unlink(tmp_name);
tdb_new = tdb_open(tmp_name, tdb->header.hash_size,
TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL,
st.st_mode & 0777);
if (!tdb_new) {
perror(tmp_name);
free(tmp_name);
return 1;
}
/* lock the old tdb */
if (tdb_lockall(tdb) != 0) {
fprintf(stderr,"Failed to lock %s\n", old_name);
tdb_close(tdb);
tdb_close(tdb_new);
unlink(tmp_name);
free(tmp_name);
return 1;
}
failed = 0;
/* traverse and copy */
count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
if (count1 < 0 || failed) {
fprintf(stderr,"failed to copy %s\n", old_name);
tdb_close(tdb);
tdb_close(tdb_new);
unlink(tmp_name);
free(tmp_name);
return 1;
}
/* close the old tdb */
tdb_close(tdb);
/* close the new tdb and re-open read-only */
tdb_close(tdb_new);
tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
if (!tdb_new) {
fprintf(stderr,"failed to reopen %s\n", tmp_name);
unlink(tmp_name);
perror(tmp_name);
free(tmp_name);
return 1;
}
/* traverse the new tdb to confirm */
count2 = tdb_traverse(tdb_new, test_fn, 0);
if (count2 != count1) {
fprintf(stderr,"failed to copy %s\n", old_name);
tdb_close(tdb_new);
unlink(tmp_name);
free(tmp_name);
return 1;
}
/* make sure the new tdb has reached stable storage */
fsync(tdb_new->fd);
/* close the new tdb and rename it to .bak */
tdb_close(tdb_new);
unlink(new_name);
if (rename(tmp_name, new_name) != 0) {
perror(new_name);
free(tmp_name);
return 1;
}
free(tmp_name);
return 0;
}
/*
verify a tdb and if it is corrupt then restore from *.bak
*/
int verify_tdb(const char *fname, const char *bak_name)
{
TDB_CONTEXT *tdb;
int count = -1;
/* open the tdb */
tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
/* traverse the tdb, then close it */
if (tdb) {
count = tdb_traverse(tdb, test_fn, NULL);
tdb_close(tdb);
}
/* count is < 0 means an error */
if (count < 0) {
printf("restoring %s\n", fname);
return backup_tdb(bak_name, fname);
}
printf("%s : %d records\n", fname, count);
return 0;
}

View file

@ -1,23 +0,0 @@
/*
Unix SMB/CIFS implementation.
low level tdb backup and restore utility
Copyright (C) Andrew Tridgell 2002
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
char *add_suffix(const char *name, const char *suffix);
int backup_tdb(const char *old_name, const char *new_name);
int verify_tdb(const char *fname, const char *bak_name);

View file

@ -1,137 +0,0 @@
/*
Unix SMB/CIFS implementation.
low level tdb backup and restore utility
Copyright (C) Andrew Tridgell 2002
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
This program is meant for backup/restore of tdb databases. Typical usage would be:
tdbbackup *.tdb
when Samba shuts down cleanly, which will make a backup of all the local databases
to *.bak files. Then on Samba startup you would use:
tdbbackup -v *.tdb
and this will check the databases for corruption and if corruption is detected then
the backup will be restored.
You may also like to do a backup on a regular basis while Samba is
running, perhaps using cron.
The reason this program is needed is to cope with power failures
while Samba is running. A power failure could lead to database
corruption and Samba will then not start correctly.
Note that many of the databases in Samba are transient and thus
don't need to be backed up, so you can optimise the above a little
by only running the backup on the critical databases.
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <ctype.h>
#include <signal.h>
#include "tdb.h"
#include "tdbback.h"
/*
see if one file is newer than another
*/
static int file_newer(const char *fname1, const char *fname2)
{
struct stat st1, st2;
if (stat(fname1, &st1) != 0) {
return 0;
}
if (stat(fname2, &st2) != 0) {
return 1;
}
return (st1.st_mtime > st2.st_mtime);
}
static void usage(void)
{
printf("Usage: tdbbackup [options] <fname...>\n\n");
printf(" -h this help message\n");
printf(" -s suffix set the backup suffix\n");
printf(" -v verify mode (restore if corrupt)\n");
}
int main(int argc, char *argv[])
{
int i;
int ret = 0;
int c;
int verify = 0;
const char *suffix = ".bak";
extern int optind;
extern char *optarg;
while ((c = getopt(argc, argv, "vhs:")) != -1) {
switch (c) {
case 'h':
usage();
exit(0);
case 'v':
verify = 1;
break;
case 's':
suffix = optarg;
break;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage();
exit(1);
}
for (i=0; i<argc; i++) {
const char *fname = argv[i];
char *bak_name;
bak_name = add_suffix(fname, suffix);
if (verify) {
if (verify_tdb(fname, bak_name) != 0) {
ret = 1;
}
} else {
if (file_newer(fname, bak_name) &&
backup_tdb(fname, bak_name) != 0) {
ret = 1;
}
}
free(bak_name);
}
return ret;
}

View file

@ -1,89 +0,0 @@
/*
Unix SMB/CIFS implementation.
simple tdb dump util
Copyright (C) Andrew Tridgell 2001
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <ctype.h>
#include <signal.h>
#include "tdb.h"
static void print_data(TDB_DATA d)
{
unsigned char *p = d.dptr;
int len = d.dsize;
while (len--) {
if (isprint(*p) && !strchr("\"\\", *p)) {
fputc(*p, stdout);
} else {
printf("\\%02X", *p);
}
p++;
}
}
static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
{
printf("{\n");
printf("key = \"");
print_data(key);
printf("\"\n");
printf("data = \"");
print_data(dbuf);
printf("\"\n");
printf("}\n");
return 0;
}
static int dump_tdb(const char *fname)
{
TDB_CONTEXT *tdb;
tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
if (!tdb) {
printf("Failed to open %s\n", fname);
return 1;
}
tdb_traverse(tdb, traverse_fn, NULL);
return 0;
}
int main(int argc, char *argv[])
{
char *fname;
if (argc < 2) {
printf("Usage: tdbdump <fname>\n");
exit(1);
}
fname = argv[1];
return dump_tdb(fname);
}

View file

@ -1,830 +0,0 @@
/*
Unix SMB/CIFS implementation.
tdb utility functions
Copyright (C) Andrew Tridgell 1992-1998
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include <fnmatch.h>
/* these are little tdb utility functions that are meant to make
dealing with a tdb database a little less cumbersome in Samba */
static SIG_ATOMIC_T gotalarm;
/***************************************************************
Signal function to tell us we timed out.
****************************************************************/
static void gotalarm_sig(void)
{
gotalarm = 1;
}
/***************************************************************
Make a TDB_DATA and keep the const warning in one place
****************************************************************/
static TDB_DATA make_tdb_data(const char *dptr, size_t dsize)
{
TDB_DATA ret;
ret.dptr = dptr;
ret.dsize = dsize;
return ret;
}
/****************************************************************************
Lock a chain with timeout (in seconds).
****************************************************************************/
static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
{
/* Allow tdb_chainlock to be interrupted by an alarm. */
int ret;
gotalarm = 0;
tdb_set_lock_alarm(&gotalarm);
if (timeout) {
CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
alarm(timeout);
}
if (rw_type == F_RDLCK)
ret = tdb_chainlock_read(tdb, key);
else
ret = tdb_chainlock(tdb, key);
if (timeout) {
alarm(0);
CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
if (gotalarm) {
DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
timeout, key.dptr, tdb->name ));
/* TODO: If we time out waiting for a lock, it might
* be nice to use F_GETLK to get the pid of the
* process currently holding the lock and print that
* as part of the debugging message. -- mbp */
return -1;
}
}
return ret;
}
/****************************************************************************
Write lock a chain. Return -1 if timeout or lock failed.
****************************************************************************/
int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
{
return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
}
/****************************************************************************
Lock a chain by string. Return -1 if timeout or lock failed.
****************************************************************************/
int tdb_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
{
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
}
/****************************************************************************
Unlock a chain by string.
****************************************************************************/
void tdb_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
{
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
tdb_chainunlock(tdb, key);
}
/****************************************************************************
Read lock a chain by string. Return -1 if timeout or lock failed.
****************************************************************************/
int tdb_read_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
{
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
}
/****************************************************************************
Read unlock a chain by string.
****************************************************************************/
void tdb_read_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval)
{
TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1);
tdb_chainunlock_read(tdb, key);
}
/****************************************************************************
Fetch a int32 value by a arbitrary blob key, return -1 if not found.
Output is int32 in native byte order.
****************************************************************************/
int32 tdb_fetch_int32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len)
{
TDB_DATA key = make_tdb_data(keyval, len);
TDB_DATA data;
int32 ret;
data = tdb_fetch(tdb, key);
if (!data.dptr || data.dsize != sizeof(int32)) {
SAFE_FREE(data.dptr);
return -1;
}
ret = IVAL(data.dptr,0);
SAFE_FREE(data.dptr);
return ret;
}
/****************************************************************************
Fetch a int32 value by string key, return -1 if not found.
Output is int32 in native byte order.
****************************************************************************/
int32 tdb_fetch_int32(TDB_CONTEXT *tdb, const char *keystr)
{
return tdb_fetch_int32_byblob(tdb, keystr, strlen(keystr) + 1);
}
/****************************************************************************
Store a int32 value by an arbitary blob key, return 0 on success, -1 on failure.
Input is int32 in native byte order. Output in tdb is in little-endian.
****************************************************************************/
int tdb_store_int32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, int32 v)
{
TDB_DATA key = make_tdb_data(keystr, len);
TDB_DATA data;
int32 v_store;
SIVAL(&v_store,0,v);
data.dptr = (void *)&v_store;
data.dsize = sizeof(int32);
return tdb_store(tdb, key, data, TDB_REPLACE);
}
/****************************************************************************
Store a int32 value by string key, return 0 on success, -1 on failure.
Input is int32 in native byte order. Output in tdb is in little-endian.
****************************************************************************/
int tdb_store_int32(TDB_CONTEXT *tdb, const char *keystr, int32 v)
{
return tdb_store_int32_byblob(tdb, keystr, strlen(keystr) + 1, v);
}
/****************************************************************************
Fetch a uint32 value by a arbitrary blob key, return -1 if not found.
Output is uint32 in native byte order.
****************************************************************************/
BOOL tdb_fetch_uint32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len, uint32 *value)
{
TDB_DATA key = make_tdb_data(keyval, len);
TDB_DATA data;
data = tdb_fetch(tdb, key);
if (!data.dptr || data.dsize != sizeof(uint32)) {
SAFE_FREE(data.dptr);
return False;
}
*value = IVAL(data.dptr,0);
SAFE_FREE(data.dptr);
return True;
}
/****************************************************************************
Fetch a uint32 value by string key, return -1 if not found.
Output is uint32 in native byte order.
****************************************************************************/
BOOL tdb_fetch_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 *value)
{
return tdb_fetch_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
}
/****************************************************************************
Store a uint32 value by an arbitary blob key, return 0 on success, -1 on failure.
Input is uint32 in native byte order. Output in tdb is in little-endian.
****************************************************************************/
BOOL tdb_store_uint32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, uint32 value)
{
TDB_DATA key = make_tdb_data(keystr, len);
TDB_DATA data;
uint32 v_store;
BOOL ret = True;
SIVAL(&v_store, 0, value);
data.dptr = (void *)&v_store;
data.dsize = sizeof(uint32);
if (tdb_store(tdb, key, data, TDB_REPLACE) == -1)
ret = False;
return ret;
}
/****************************************************************************
Store a uint32 value by string key, return 0 on success, -1 on failure.
Input is uint32 in native byte order. Output in tdb is in little-endian.
****************************************************************************/
BOOL tdb_store_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 value)
{
return tdb_store_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value);
}
/****************************************************************************
Store a buffer by a null terminated string key. Return 0 on success, -1
on failure.
****************************************************************************/
int tdb_store_bystring(TDB_CONTEXT *tdb, const char *keystr, TDB_DATA data, int flags)
{
TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
return tdb_store(tdb, key, data, flags);
}
/****************************************************************************
Fetch a buffer using a null terminated string key. Don't forget to call
free() on the result dptr.
****************************************************************************/
TDB_DATA tdb_fetch_bystring(TDB_CONTEXT *tdb, const char *keystr)
{
TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
return tdb_fetch(tdb, key);
}
/****************************************************************************
Delete an entry using a null terminated string key.
****************************************************************************/
int tdb_delete_bystring(TDB_CONTEXT *tdb, const char *keystr)
{
TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1);
return tdb_delete(tdb, key);
}
/****************************************************************************
Atomic integer change. Returns old value. To create, set initial value in *oldval.
****************************************************************************/
int32 tdb_change_int32_atomic(TDB_CONTEXT *tdb, const char *keystr, int32 *oldval, int32 change_val)
{
int32 val;
int32 ret = -1;
if (tdb_lock_bystring(tdb, keystr,0) == -1)
return -1;
if ((val = tdb_fetch_int32(tdb, keystr)) == -1) {
/* The lookup failed */
if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
/* but not because it didn't exist */
goto err_out;
}
/* Start with 'old' value */
val = *oldval;
} else {
/* It worked, set return value (oldval) to tdb data */
*oldval = val;
}
/* Increment value for storage and return next time */
val += change_val;
if (tdb_store_int32(tdb, keystr, val) == -1)
goto err_out;
ret = 0;
err_out:
tdb_unlock_bystring(tdb, keystr);
return ret;
}
/****************************************************************************
Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval.
****************************************************************************/
BOOL tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val)
{
uint32 val;
BOOL ret = False;
if (tdb_lock_bystring(tdb, keystr,0) == -1)
return False;
if (!tdb_fetch_uint32(tdb, keystr, &val)) {
/* It failed */
if (tdb_error(tdb) != TDB_ERR_NOEXIST) {
/* and not because it didn't exist */
goto err_out;
}
/* Start with 'old' value */
val = *oldval;
} else {
/* it worked, set return value (oldval) to tdb data */
*oldval = val;
}
/* get a new value to store */
val += change_val;
if (!tdb_store_uint32(tdb, keystr, val))
goto err_out;
ret = True;
err_out:
tdb_unlock_bystring(tdb, keystr);
return ret;
}
/****************************************************************************
Useful pair of routines for packing/unpacking data consisting of
integers and strings.
****************************************************************************/
size_t tdb_pack(char *buf, int bufsize, const char *fmt, ...)
{
va_list ap;
uint8 bt;
uint16 w;
uint32 d;
int i;
void *p;
int len;
char *s;
char c;
char *buf0 = buf;
const char *fmt0 = fmt;
int bufsize0 = bufsize;
va_start(ap, fmt);
while (*fmt) {
switch ((c = *fmt++)) {
case 'b': /* unsigned 8-bit integer */
len = 1;
bt = (uint8)va_arg(ap, int);
if (bufsize && bufsize >= len)
SSVAL(buf, 0, bt);
break;
case 'w': /* unsigned 16-bit integer */
len = 2;
w = (uint16)va_arg(ap, int);
if (bufsize && bufsize >= len)
SSVAL(buf, 0, w);
break;
case 'd': /* signed 32-bit integer (standard int in most systems) */
len = 4;
d = va_arg(ap, uint32);
if (bufsize && bufsize >= len)
SIVAL(buf, 0, d);
break;
case 'p': /* pointer */
len = 4;
p = va_arg(ap, void *);
d = p?1:0;
if (bufsize && bufsize >= len)
SIVAL(buf, 0, d);
break;
case 'P': /* null-terminated string */
s = va_arg(ap,char *);
w = strlen(s);
len = w + 1;
if (bufsize && bufsize >= len)
memcpy(buf, s, len);
break;
case 'f': /* null-terminated string */
s = va_arg(ap,char *);
w = strlen(s);
len = w + 1;
if (bufsize && bufsize >= len)
memcpy(buf, s, len);
break;
case 'B': /* fixed-length string */
i = va_arg(ap, int);
s = va_arg(ap, char *);
len = 4+i;
if (bufsize && bufsize >= len) {
SIVAL(buf, 0, i);
memcpy(buf+4, s, i);
}
break;
default:
DEBUG(0,("Unknown tdb_pack format %c in %s\n",
c, fmt));
len = 0;
break;
}
buf += len;
if (bufsize)
bufsize -= len;
if (bufsize < 0)
bufsize = 0;
}
va_end(ap);
DEBUG(18,("tdb_pack(%s, %d) -> %d\n",
fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
return PTR_DIFF(buf, buf0);
}
/****************************************************************************
Useful pair of routines for packing/unpacking data consisting of
integers and strings.
****************************************************************************/
int tdb_unpack(char *buf, int bufsize, const char *fmt, ...)
{
va_list ap;
uint8 *bt;
uint16 *w;
uint32 *d;
int len;
int *i;
void **p;
char *s, **b;
char c;
char *buf0 = buf;
const char *fmt0 = fmt;
int bufsize0 = bufsize;
va_start(ap, fmt);
while (*fmt) {
switch ((c=*fmt++)) {
case 'b':
len = 1;
bt = va_arg(ap, uint8 *);
if (bufsize < len)
goto no_space;
*bt = SVAL(buf, 0);
break;
case 'w':
len = 2;
w = va_arg(ap, uint16 *);
if (bufsize < len)
goto no_space;
*w = SVAL(buf, 0);
break;
case 'd':
len = 4;
d = va_arg(ap, uint32 *);
if (bufsize < len)
goto no_space;
*d = IVAL(buf, 0);
break;
case 'p':
len = 4;
p = va_arg(ap, void **);
if (bufsize < len)
goto no_space;
*p = (void *)IVAL(buf, 0);
break;
case 'P':
s = va_arg(ap,char *);
len = strlen(buf) + 1;
if (bufsize < len || len > sizeof(pstring))
goto no_space;
memcpy(s, buf, len);
break;
case 'f':
s = va_arg(ap,char *);
len = strlen(buf) + 1;
if (bufsize < len || len > sizeof(fstring))
goto no_space;
memcpy(s, buf, len);
break;
case 'B':
i = va_arg(ap, int *);
b = va_arg(ap, char **);
len = 4;
if (bufsize < len)
goto no_space;
*i = IVAL(buf, 0);
if (! *i) {
*b = NULL;
break;
}
len += *i;
if (bufsize < len)
goto no_space;
*b = (char *)malloc(*i);
if (! *b)
goto no_space;
memcpy(*b, buf+4, *i);
break;
default:
DEBUG(0,("Unknown tdb_unpack format %c in %s\n",
c, fmt));
len = 0;
break;
}
buf += len;
bufsize -= len;
}
va_end(ap);
DEBUG(18,("tdb_unpack(%s, %d) -> %d\n",
fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
return PTR_DIFF(buf, buf0);
no_space:
return -1;
}
/**
* Pack SID passed by pointer
*
* @param pack_buf pointer to buffer which is to be filled with packed data
* @param bufsize size of packing buffer
* @param sid pointer to sid to be packed
*
* @return length of the packed representation of the whole structure
**/
size_t tdb_sid_pack(char* pack_buf, int bufsize, DOM_SID* sid)
{
int idx;
size_t len = 0;
if (!sid || !pack_buf) return -1;
len += tdb_pack(pack_buf + len, bufsize - len, "bb", sid->sid_rev_num,
sid->num_auths);
for (idx = 0; idx < 6; idx++) {
len += tdb_pack(pack_buf + len, bufsize - len, "b", sid->id_auth[idx]);
}
for (idx = 0; idx < MAXSUBAUTHS; idx++) {
len += tdb_pack(pack_buf + len, bufsize - len, "d", sid->sub_auths[idx]);
}
return len;
}
/**
* Unpack SID into a pointer
*
* @param pack_buf pointer to buffer with packed representation
* @param bufsize size of the buffer
* @param sid pointer to sid structure to be filled with unpacked data
*
* @return size of structure unpacked from buffer
**/
size_t tdb_sid_unpack(char* pack_buf, int bufsize, DOM_SID* sid)
{
int idx, len = 0;
if (!sid || !pack_buf) return -1;
len += tdb_unpack(pack_buf + len, bufsize - len, "bb",
&sid->sid_rev_num, &sid->num_auths);
for (idx = 0; idx < 6; idx++) {
len += tdb_unpack(pack_buf + len, bufsize - len, "b", &sid->id_auth[idx]);
}
for (idx = 0; idx < MAXSUBAUTHS; idx++) {
len += tdb_unpack(pack_buf + len, bufsize - len, "d", &sid->sub_auths[idx]);
}
return len;
}
/**
* Pack TRUSTED_DOM_PASS passed by pointer
*
* @param pack_buf pointer to buffer which is to be filled with packed data
* @param bufsize size of the buffer
* @param pass pointer to trusted domain password to be packed
*
* @return length of the packed representation of the whole structure
**/
size_t tdb_trusted_dom_pass_pack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
{
int idx, len = 0;
if (!pack_buf || !pass) return -1;
/* packing unicode domain name and password */
len += tdb_pack(pack_buf + len, bufsize - len, "d", pass->uni_name_len);
for (idx = 0; idx < 32; idx++)
len += tdb_pack(pack_buf + len, bufsize - len, "w", pass->uni_name[idx]);
len += tdb_pack(pack_buf + len, bufsize - len, "dPd", pass->pass_len,
pass->pass, pass->mod_time);
/* packing SID structure */
len += tdb_sid_pack(pack_buf + len, bufsize - len, &pass->domain_sid);
return len;
}
/**
* Unpack TRUSTED_DOM_PASS passed by pointer
*
* @param pack_buf pointer to buffer with packed representation
* @param bufsize size of the buffer
* @param pass pointer to trusted domain password to be filled with unpacked data
*
* @return size of structure unpacked from buffer
**/
size_t tdb_trusted_dom_pass_unpack(char* pack_buf, int bufsize, TRUSTED_DOM_PASS* pass)
{
int idx, len = 0;
if (!pack_buf || !pass) return -1;
/* unpack unicode domain name and plaintext password */
len += tdb_unpack(pack_buf, bufsize - len, "d", &pass->uni_name_len);
for (idx = 0; idx < 32; idx++)
len += tdb_unpack(pack_buf + len, bufsize - len, "w", &pass->uni_name[idx]);
len += tdb_unpack(pack_buf + len, bufsize - len, "dPd", &pass->pass_len, &pass->pass,
&pass->mod_time);
/* unpack domain sid */
len += tdb_sid_unpack(pack_buf + len, bufsize - len, &pass->domain_sid);
return len;
}
/****************************************************************************
Log tdb messages via DEBUG().
****************************************************************************/
static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...)
{
va_list ap;
char *ptr = NULL;
va_start(ap, format);
vasprintf(&ptr, format, ap);
va_end(ap);
if (!ptr || !*ptr)
return;
DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr));
SAFE_FREE(ptr);
}
/****************************************************************************
Like tdb_open() but also setup a logging function that redirects to
the samba DEBUG() system.
****************************************************************************/
TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode)
{
TDB_CONTEXT *tdb;
if (!lp_use_mmap())
tdb_flags |= TDB_NOMMAP;
tdb = tdb_open_ex(name, hash_size, tdb_flags,
open_flags, mode, tdb_log);
if (!tdb)
return NULL;
return tdb;
}
/****************************************************************************
Allow tdb_delete to be used as a tdb_traversal_fn.
****************************************************************************/
int tdb_traverse_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
void *state)
{
return tdb_delete(the_tdb, key);
}
/**
* Search across the whole tdb for keys that match the given pattern
* return the result as a list of keys
*
* @param tdb pointer to opened tdb file context
* @param pattern searching pattern used by fnmatch(3) functions
*
* @return list of keys found by looking up with given pattern
**/
TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern)
{
TDB_DATA key, next;
TDB_LIST_NODE *list = NULL;
TDB_LIST_NODE *rec = NULL;
TDB_LIST_NODE *tmp = NULL;
for (key = tdb_firstkey(tdb); key.dptr; key = next) {
/* duplicate key string to ensure null-termination */
char *key_str = (char*) strndup(key.dptr, key.dsize);
if (!key_str) {
DEBUG(0, ("tdb_search_keys: strndup() failed!\n"));
smb_panic("strndup failed!\n");
}
DEBUG(18, ("checking %s for match to pattern %s\n", key_str, pattern));
next = tdb_nextkey(tdb, key);
/* do the pattern checking */
if (fnmatch(pattern, key_str, 0) == 0) {
rec = (TDB_LIST_NODE*) malloc(sizeof(*rec));
ZERO_STRUCTP(rec);
rec->node_key = key;
DLIST_ADD_END(list, rec, tmp);
DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern));
} else {
free(key.dptr);
}
/* free duplicated key string */
free(key_str);
}
return list;
};
/**
* Free the list returned by tdb_search_keys
*
* @param node list of results found by tdb_search_keys
**/
void tdb_search_list_free(TDB_LIST_NODE* node)
{
TDB_LIST_NODE *next_node;
while (node) {
next_node = node->next;
SAFE_FREE(node->node_key.dptr);
SAFE_FREE(node);
node = next_node;
};
};

View file

@ -1,37 +0,0 @@
/*
Unix SMB/CIFS implementation.
tdb utility functions
Copyright (C) Andrew Tridgell 1999
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __TDBUTIL_H__
#define __TDBUTIL_H__
/* single node of a list returned by tdb_search_keys */
typedef struct keys_node
{
struct keys_node *prev, *next;
TDB_DATA node_key;
} TDB_LIST_NODE;
TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT*, const char*);
void tdb_search_list_free(TDB_LIST_NODE*);
#endif /* __TDBUTIL_H__ */

View file

@ -14,7 +14,7 @@ EOF
cat > $CONFIG << EOF
udev_root="$PWD/udev/"
udev_db="$PWD/udev/.udev.tdb"
udev_db="$PWD/udev/.udevdb"
udev_rules="$PWD/$RULES"
udev_permissions="$PWD/udev.permissions"
EOF

View file

@ -13,7 +13,7 @@ EOF
cat > $CONFIG << EOF
udev_root="$PWD/udev/"
udev_db="$PWD/udev/.udev.tdb"
udev_db="$PWD/udev/.udevdb"
udev_rules="$PWD/$RULES"
udev_permissions="$PWD/udev.permissions"
udev_log="true"

View file

@ -14,7 +14,7 @@ EOF
cat > $CONFIG << EOF
udev_root="$PWD/udev/"
udev_db="$PWD/udev/.udev.tdb"
udev_db="$PWD/udev/.udevdb"
udev_rules="$PWD/$RULES"
udev_permissions="$PWD/udev.permissions"
EOF

View file

@ -13,7 +13,7 @@ EOF
cat > $CONFIG << EOF
udev_root="$PWD/udev/"
udev_db="$PWD/udev/.udev.tdb"
udev_db="$PWD/udev/.udevdb"
udev_rules="$PWD/$RULES"
udev_permissions="$PWD/udev.permissions"
EOF

View file

@ -13,7 +13,7 @@ EOF
cat > $CONFIG << EOF
udev_root="$PWD/udev/"
udev_db="$PWD/udev/.udev.tdb"
udev_db="$PWD/udev/.udevdb"
udev_rules="$PWD/$RULES"
udev_permissions="$PWD/udev.permissions"
EOF

View file

@ -13,7 +13,7 @@ EOF
cat > $CONFIG << EOF
udev_root="$PWD/udev/"
udev_db="$PWD/udev/.udev.tdb"
udev_db="$PWD/udev/.udevdb"
udev_rules="$PWD/$RULES"
udev_permissions="$PWD/udev.permissions"
EOF

View file

@ -13,7 +13,7 @@ EOF
cat > $CONFIG << EOF
udev_root="$PWD/udev/"
udev_db="$PWD/udev/.udev.tdb"
udev_db="$PWD/udev/.udevdb"
udev_rules="$PWD/$RULES"
udev_permissions="$PWD/udev.permissions"
EOF

View file

@ -30,7 +30,7 @@ my $PWD = $ENV{PWD};
my $sysfs = "sys/";
my $udev_bin = "../udev";
my $udev_root = "udev-root/"; # !!! directory will be removed !!!
my $udev_db = ".udev.tdb";
my $udev_db = ".udevdb";
my $perm = "udev.permissions";
my $main_conf = "udev-test.conf";
my $conf_tmp = "udev-test.rules";
@ -1268,7 +1268,7 @@ sub run_test {
}
if (defined($config->{option}) && $config->{option} eq "clear") {
unlink($udev_db);
system("rm -rf $udev_db");
system("rm -rf $udev_root");
mkdir($udev_root) || die "unable to create udev_root: $udev_root\n";
}
@ -1319,7 +1319,7 @@ if ($ARGV[0]) {
print "$error errors occured\n\n";
# cleanup
unlink($udev_db);
system("rm -rf $udev_db");
system("rm -rf $udev_root");
unlink($conf_tmp);
unlink($main_conf);

View file

@ -74,7 +74,7 @@ value is
.TP
.B udev_db
The name and location of the udev database. The default value is
.IR @udevdir@/.udev.tdb .
.IR @udevdir@/.udevdb .
.TP
.B udev_rules
The name of the udev rules file or directory to look for files with the suffix
@ -117,7 +117,7 @@ file. The default value is
udev_root="/udev"
# udev_db - The name and location of the udev database
udev_db="/udev/.udev.tdb"
udev_db="/udev/.udevdb"
# udev_rules - The name of the udev rules file or directory to look
for files with the suffix .rules

11
udev.c
View file

@ -38,8 +38,6 @@
#include "namedev.h"
#include "udevdb.h"
/* timeout flag for udevdb */
extern sig_atomic_t gotalarm;
/* global variables */
char **main_argv;
@ -64,8 +62,7 @@ static void asmlinkage sig_handler(int signum)
{
switch (signum) {
case SIGALRM:
gotalarm = 1;
break;
exit(1);
case SIGINT:
case SIGTERM:
exit(20 + signum);
@ -153,10 +150,6 @@ int main(int argc, char *argv[], char *envp[])
/* trigger timout to interrupt blocking syscalls */
alarm(ALARM_TIMEOUT);
/* initialize udev database */
if (udevdb_init(UDEVDB_DEFAULT) != 0)
info("error: unable to initialize database, continuing without database");
switch(act_type) {
case UDEVSTART:
dbg("udevstart");
@ -196,8 +189,6 @@ int main(int argc, char *argv[], char *envp[])
dev_d_execute(&udev);
}
udevdb_exit();
exit:
logging_close();
return retval;

13
udev.h
View file

@ -41,26 +41,21 @@
#define LINE_SIZE 256
/* length of public data to store in udevdb */
#define UDEVICE_DB_LEN (offsetof(struct udevice, devpath))
struct udevice {
char devpath[DEVPATH_SIZE];
char subsystem[SUBSYSTEM_SIZE];
char name[NAME_SIZE];
char owner[OWNER_SIZE];
char group[GROUP_SIZE];
char type;
int major;
int minor;
unsigned int mode; /* not mode_t due to conflicting definitions in different libcs */
mode_t mode;
char symlink[NAME_SIZE];
int partitions;
int config_line;
char config_file[NAME_SIZE];
long config_uptime;
/* private data, not stored in udevdb */
char devpath[DEVPATH_SIZE];
char subsystem[SUBSYSTEM_SIZE];
char bus_id[SYSFS_NAME_LEN];
char bus[SYSFS_NAME_LEN];
char program_result[NAME_SIZE];
@ -81,7 +76,7 @@ extern char **main_argv;
extern char **main_envp;
extern char sysfs_path[SYSFS_PATH_MAX];
extern char udev_root[PATH_MAX];
extern char udev_db_filename[PATH_MAX+NAME_MAX];
extern char udev_db_path[PATH_MAX+NAME_MAX];
extern char udev_permissions_filename[PATH_MAX+NAME_MAX];
extern char udev_config_filename[PATH_MAX+NAME_MAX];
extern char udev_rules_filename[PATH_MAX+NAME_MAX];

View file

@ -68,37 +68,6 @@ error:
return -1;
}
static int create_path(char *file)
{
char p[NAME_SIZE];
char *pos;
int retval;
struct stat stats;
strfieldcpy(p, file);
pos = strchr(p+1, '/');
while (1) {
pos = strchr(pos+1, '/');
if (pos == NULL)
break;
*pos = 0x00;
if (stat(p, &stats)) {
selinux_setfscreatecon(p, S_IFDIR);
retval = mkdir(p, 0755);
if (retval != 0) {
dbg("mkdir(%s) failed with error '%s'",
p, strerror(errno));
return retval;
}
dbg("created '%s'", p);
} else {
selinux_setfilecon(p, S_IFDIR);
}
*pos = '/';
}
return 0;
}
static int make_node(char *file, int major, int minor, unsigned int mode, uid_t uid, gid_t gid)
{
struct stat stats;
@ -152,8 +121,6 @@ exit:
static int create_node(struct udevice *udev)
{
char filename[NAME_SIZE];
char linkname[NAME_SIZE];
char linktarget[NAME_SIZE];
char partitionname[NAME_SIZE];
uid_t uid = 0;
gid_t gid = 0;
@ -162,8 +129,8 @@ static int create_node(struct udevice *udev)
char *pos;
int len;
strfieldcpy(filename, udev_root);
strfieldcat(filename, udev->name);
snprintf(filename, NAME_SIZE-1, "%s/%s", udev_root, udev->name);
filename[NAME_SIZE-1] = '\0';
switch (udev->type) {
case 'b':
@ -239,9 +206,13 @@ static int create_node(struct udevice *udev)
/* create symlink(s) if requested */
foreach_strpart(udev->symlink, " ", pos, len) {
char linkname[NAME_SIZE];
char linktarget[NAME_SIZE];
strfieldcpymax(linkname, pos, len+1);
strfieldcpy(filename, udev_root);
strfieldcat(filename, linkname);
snprintf(filename, NAME_SIZE-1, "%s/%s", udev_root, linkname);
filename[NAME_SIZE-1] = '\0';
dbg("symlink '%s' to node '%s' requested", filename, udev->name);
if (!udev->test_run)
if (strrchr(linkname, '/'))
@ -337,7 +308,8 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
"remove might not work for custom names");
/* use full path to the environment */
snprintf(udev->devname, NAME_SIZE-1, "%s%s", udev_root, udev->name);
snprintf(udev->devname, NAME_SIZE-1, "%s/%s", udev_root, udev->name);
udev->devname[NAME_SIZE-1] = '\0';
} else if (udev->type == 'n') {
/* look if we want to change the name of the netif */

View file

@ -42,7 +42,7 @@
/* global variables */
char sysfs_path[SYSFS_PATH_MAX];
char udev_root[PATH_MAX];
char udev_db_filename[PATH_MAX+NAME_MAX];
char udev_db_path[PATH_MAX+NAME_MAX];
char udev_permissions_filename[PATH_MAX+NAME_MAX];
char udev_rules_filename[PATH_MAX+NAME_MAX];
char udev_config_filename[PATH_MAX+NAME_MAX];
@ -72,7 +72,7 @@ static void init_variables(void)
* If any config values are specified, they will
* override these values. */
strfieldcpy(udev_root, UDEV_ROOT);
strfieldcpy(udev_db_filename, UDEV_DB);
strfieldcpy(udev_db_path, UDEV_DB);
strfieldcpy(udev_config_filename, UDEV_CONFIG_FILE);
strfieldcpy(udev_rules_filename, UDEV_RULES_FILE);
strfieldcpy(udev_permissions_filename, UDEV_PERMISSION_FILE);
@ -181,24 +181,25 @@ static int parse_config_file(void)
if (strcasecmp(variable, "udev_root") == 0) {
strfieldcpy(udev_root, value);
leading_slash(udev_root);
no_trailing_slash(udev_root);
continue;
}
if (strcasecmp(variable, "udev_db") == 0) {
strfieldcpy(udev_db_filename, value);
strfieldcpy(udev_db_path, value);
no_trailing_slash(udev_db_path);
continue;
}
if (strcasecmp(variable, "udev_rules") == 0) {
strfieldcpy(udev_rules_filename, value);
no_leading_slash(udev_rules_filename);
no_trailing_slash(udev_rules_filename);
continue;
}
if (strcasecmp(variable, "udev_permissions") == 0) {
strfieldcpy(udev_permissions_filename, value);
no_leading_slash(udev_permissions_filename);
no_trailing_slash(udev_permissions_filename);
continue;
}
@ -244,7 +245,7 @@ static void get_dirs(void)
temp = getenv("SYSFS_PATH");
if (temp != NULL) {
strfieldcpy(sysfs_path, temp);
no_leading_slash(sysfs_path);
no_trailing_slash(sysfs_path);
}
temp = getenv("UDEV_CONFIG_FILE");
@ -255,7 +256,7 @@ static void get_dirs(void)
dbg("sysfs_path='%s'", sysfs_path);
dbg_parse("udev_root = %s", udev_root);
dbg_parse("udev_config_filename = %s", udev_config_filename);
dbg_parse("udev_db_filename = %s", udev_db_filename);
dbg_parse("udev_db_path = %s", udev_db_path);
dbg_parse("udev_rules_filename = %s", udev_rules_filename);
dbg_parse("udev_permissions_filename = %s", udev_permissions_filename);
dbg_parse("udev_log = %d", udev_log);
@ -264,7 +265,7 @@ static void get_dirs(void)
dbg("udev_root = %s", udev_root);
dbg("udev_config_filename = %s", udev_config_filename);
dbg("udev_db_filename = %s", udev_db_filename);
dbg("udev_db_path = %s", udev_db_path);
dbg("udev_rules_filename = %s", udev_rules_filename);
dbg("udev_permissions_filename = %s", udev_permissions_filename);
dbg("udev_log_str = %d", udev_log);

View file

@ -120,6 +120,33 @@ void udev_set_values(struct udevice *udev, const char* devpath, const char *subs
udev->type = get_device_type(devpath, subsystem);
}
int create_path(const char *path)
{
char p[NAME_SIZE];
char *pos;
struct stat stats;
strcpy (p, path);
pos = strrchr(p, '/');
if (pos == p || pos == NULL)
return 0;
while (pos[-1] == '/')
pos--;
pos[0] = '\0';
dbg("stat '%s'\n", p);
if (stat (p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR)
return 0;
if (create_path (p) != 0)
return -1;
dbg("mkdir '%s'\n", p);
return mkdir(p, 0755);
}
int file_map(const char *filename, char **buf, size_t *bufsize)
{
struct stat stats;
@ -161,18 +188,7 @@ size_t buf_get_line(char *buf, size_t buflen, size_t cur)
return count - cur;
}
void leading_slash(char *path)
{
int len;
len = strlen(path);
if (len > 0 && path[len-1] != '/') {
path[len] = '/';
path[len+1] = '\0';
}
}
void no_leading_slash(char *path)
void no_trailing_slash(char *path)
{
int len;
@ -249,9 +265,8 @@ int call_foreach_file(int fnct(char *f) , char *dirname, char *suffix)
/* call function for every file in the list */
list_for_each_entry_safe(loop_file, tmp_file, &file_list, list) {
strfieldcpy(file, dirname);
strfieldcat(file, "/");
strfieldcat(file, loop_file->name);
snprintf(file, NAME_SIZE-1, "%s/%s", dirname, loop_file->name);
file[NAME_SIZE-1] = '\0';
fnct(file);

View file

@ -83,11 +83,11 @@ extern char *get_seqnum(void);
extern char *get_subsystem(char *subsystem);
extern char get_device_type(const char *path, const char *subsystem);
extern void udev_set_values(struct udevice *udev, const char* devpath, const char *subsystem);
extern int create_path(const char *path);
extern int file_map(const char *filename, char **buf, size_t *bufsize);
extern void file_unmap(char *buf, size_t bufsize);
extern size_t buf_get_line(char *buf, size_t buflen, size_t cur);
extern void leading_slash(char *path);
extern void no_leading_slash(char *path);
extern void no_trailing_slash(char *path);
extern int call_foreach_file(int fnct(char *f) , char *filename, char *extension);
extern int set_cloexec_flag (int desc, int value);

View file

@ -102,10 +102,9 @@ static int secure_unlink(const char *filename)
return retval;
}
static int delete_node(struct udevice *dev)
static int delete_node(struct udevice *udev)
{
char filename[NAME_SIZE];
char linkname[NAME_SIZE];
char partitionname[NAME_SIZE];
int retval;
int i;
@ -113,8 +112,8 @@ static int delete_node(struct udevice *dev)
int len;
int num;
strfieldcpy(filename, udev_root);
strfieldcat(filename, dev->name);
snprintf(filename, NAME_SIZE-1, "%s/%s", udev_root, udev->name);
filename[NAME_SIZE-1] = '\0';
info("removing device node '%s'", filename);
retval = secure_unlink(filename);
@ -122,7 +121,7 @@ static int delete_node(struct udevice *dev)
return retval;
/* remove all_partitions nodes */
num = dev->partitions;
num = udev->partitions;
if (num > 0) {
info("removing all_partitions '%s[1-%i]'", filename, num);
if (num > PARTITIONS_COUNT) {
@ -137,13 +136,15 @@ static int delete_node(struct udevice *dev)
}
/* remove subdirectories */
if (strchr(dev->name, '/'))
if (strchr(udev->name, '/'))
delete_path(filename);
foreach_strpart(dev->symlink, " ", pos, len) {
foreach_strpart(udev->symlink, " ", pos, len) {
char linkname[NAME_SIZE];
strfieldcpymax(linkname, pos, len+1);
strfieldcpy(filename, udev_root);
strfieldcat(filename, linkname);
snprintf(filename, NAME_SIZE-1, "%s/%s", udev_root, linkname);
filename[NAME_SIZE-1] = '\0';
dbg("unlinking symlink '%s'", filename);
retval = unlink(filename);
@ -154,7 +155,7 @@ static int delete_node(struct udevice *dev)
filename, strerror(errno));
return retval;
}
if (strchr(dev->symlink, '/')) {
if (strchr(udev->symlink, '/')) {
delete_path(filename);
}
}
@ -168,18 +169,14 @@ static int delete_node(struct udevice *dev)
*/
int udev_remove_device(struct udevice *udev)
{
struct udevice db_dev;
const char *temp;
int retval;
if (udev->type != 'b' && udev->type != 'c')
return 0;
retval = udevdb_get_dev(udev->devpath, &db_dev);
if (retval == 0) {
/* copy over the stored values to our device */
memcpy(udev, &db_dev, UDEVICE_DB_LEN);
} else {
retval = udevdb_get_dev(udev);
if (retval) {
/* fall back to kernel name */
temp = strrchr(udev->devpath, '/');
if (temp == NULL)
@ -189,10 +186,10 @@ int udev_remove_device(struct udevice *udev)
}
dbg("remove name='%s'", udev->name);
udevdb_delete_dev(udev->devpath);
udevdb_delete_dev(udev);
/* use full path to the environment */
snprintf(udev->devname, NAME_SIZE-1, "%s%s", udev_root, udev->name);
snprintf(udev->devname, NAME_SIZE-1, "%s/%s", udev_root, udev->name);
return delete_node(udev);
}

306
udevdb.c
View file

@ -1,10 +1,10 @@
/*
* udevdb.c - udev database library
* udevdb.c
*
* Userspace devfs
*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003 IBM Corp.
* Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -21,216 +21,194 @@
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <dirent.h>
#include "libsysfs/sysfs/libsysfs.h"
#include "udev.h"
#include "udev_lib.h"
#include "udev_version.h"
#include "logging.h"
#include "namedev.h"
#include "udevdb.h"
#include "tdb/tdb.h"
static TDB_CONTEXT *udevdb;
sig_atomic_t gotalarm;
#define PATH_TO_NAME_CHAR '@'
static int get_db_filename(struct udevice *udev, char *filename, int len)
{
char devpath[SYSFS_PATH_MAX];
char *pos;
/* replace '/' to transform path into a filename */
strfieldcpy(devpath, udev->devpath);
pos = strchr(&devpath[1], '/');
while (pos) {
pos[0] = PATH_TO_NAME_CHAR;
pos = strchr(&pos[1], '/');
}
snprintf(filename, len-1, "%s%s", udev_db_path, devpath);
filename[len-1] = '\0';
return 0;
}
int udevdb_add_dev(struct udevice *udev)
{
TDB_DATA key, data;
char keystr[SYSFS_PATH_MAX];
char filename[SYSFS_PATH_MAX];
FILE *f;
if (udev->test_run)
return 0;
if (udevdb == NULL)
get_db_filename(udev, filename, SYSFS_PATH_MAX);
create_path(filename);
f = fopen(filename, "w");
if (f == NULL) {
dbg("unable to create db file '%s'", filename);
return -1;
}
dbg("storing data for device '%s' in '%s'", udev->devpath, filename);
memset(keystr, 0x00, SYSFS_PATH_MAX);
strfieldcpy(keystr, udev->devpath);
key.dptr = keystr;
key.dsize = strlen(keystr) + 1;
fprintf(f, "P:%s\n", udev->devpath);
fprintf(f, "N:%s\n", udev->name);
fprintf(f, "S:%s\n", udev->symlink);
fprintf(f, "A:%d\n", udev->partitions);
data.dptr = (void *) udev;
data.dsize = UDEVICE_DB_LEN;
dbg("store key '%s' for device '%s'", keystr, udev->name);
return tdb_store(udevdb, key, data, TDB_REPLACE);
}
int udevdb_get_dev(const char *path, struct udevice *udev)
{
TDB_DATA key, data;
if (udevdb == NULL)
return -1;
if (path == NULL)
return -ENODEV;
key.dptr = (void *)path;
key.dsize = strlen(path) + 1;
data = tdb_fetch(udevdb, key);
if (data.dptr == NULL || data.dsize == 0)
return -ENODEV;
memset(udev, 0x00, sizeof(struct udevice));
memcpy(udev, data.dptr, UDEVICE_DB_LEN);
fclose(f);
return 0;
}
int udevdb_delete_dev(const char *path)
static int parse_db_file(struct udevice *udev, const char *filename)
{
TDB_DATA key;
char keystr[SYSFS_PATH_MAX];
char line[NAME_SIZE];
char *bufline;
char *buf;
size_t bufsize;
size_t cur;
size_t count;
if (udevdb == NULL)
if (file_map(filename, &buf, &bufsize) != 0) {
dbg("unable to read db file '%s'", filename);
return -1;
}
cur = 0;
while (cur < bufsize) {
count = buf_get_line(buf, bufsize, cur);
bufline = &buf[cur];
cur += count+1;
switch(bufline[0]) {
case 'P':
if (count > DEVPATH_SIZE)
count = DEVPATH_SIZE-1;
strncpy(udev->devpath, &bufline[2], count-2);
break;
case 'N':
if (count > NAME_SIZE)
count = NAME_SIZE-1;
strncpy(udev->name, &bufline[2], count-2);
break;
case 'S':
if (count > NAME_SIZE)
count = NAME_SIZE-1;
strncpy(udev->symlink, &bufline[2], count-2);
break;
case 'A':
strfieldcpy(line, &bufline[2]);
udev->partitions = atoi(line);
break;
}
}
if (udev->name[0] == '\0')
return -1;
if (path == NULL)
return -EINVAL;
memset(keystr, 0, sizeof(keystr));
strfieldcpy(keystr, path);
key.dptr = keystr;
key.dsize = strlen(keystr) + 1;
return tdb_delete(udevdb, key);
}
/**
* udevdb_exit: closes database
*/
void udevdb_exit(void)
{
if (udevdb != NULL) {
tdb_close(udevdb);
udevdb = NULL;
}
}
/**
* udevdb_init: initializes database
* @init_flag: UDEVDB_INTERNAL - database stays in memory
* UDEVDB_DEFAULT - database is written to a file
*/
int udevdb_init(int init_flag)
{
if (init_flag != UDEVDB_DEFAULT && init_flag != UDEVDB_INTERNAL)
return -EINVAL;
tdb_set_lock_alarm(&gotalarm);
udevdb = tdb_open(udev_db_filename, 0, init_flag, O_RDWR | O_CREAT, 0644);
if (udevdb == NULL) {
if (init_flag == UDEVDB_INTERNAL)
dbg("unable to initialize in-memory database");
else
dbg("unable to initialize database at '%s'", udev_db_filename);
return -EACCES;
}
return 0;
}
/**
* udevdb_open_ro: open database for reading
*/
int udevdb_open_ro(void)
int udevdb_get_dev(struct udevice *udev)
{
udevdb = tdb_open(udev_db_filename, 0, 0, O_RDONLY, 0);
if (udevdb == NULL) {
dbg("unable to open database at '%s'", udev_db_filename);
return -EACCES;
}
char filename[SYSFS_PATH_MAX];
get_db_filename(udev, filename, SYSFS_PATH_MAX);
return parse_db_file(udev, filename);
}
int udevdb_delete_dev(struct udevice *udev)
{
char filename[SYSFS_PATH_MAX];
get_db_filename(udev, filename, SYSFS_PATH_MAX);
unlink(filename);
return 0;
}
static int (*user_record_callback) (const char *path, struct udevice *dev);
static int traverse_callback(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
int udevdb_get_dev_byname(struct udevice *udev, const char *name)
{
return user_record_callback((char*) key.dptr, (struct udevice*) dbuf.dptr);
}
struct dirent *ent;
DIR *dir;
char filename[NAME_SIZE];
struct udevice db_udev;
/**
* udevdb_call_foreach: dumps whole database by passing record data to user function
* @user_record_handler: user function called for every record in the database
*/
int udevdb_call_foreach(int (*user_record_handler) (const char *path, struct udevice *dev))
{
int retval = 0;
if (udevdb == NULL)
dir = opendir(udev_db_path);
if (dir == NULL) {
dbg("unable to udev db '%s'", udev_db_path);
return -1;
if (user_record_handler == NULL) {
dbg("invalid user record handling function");
return -EINVAL;
}
user_record_callback = user_record_handler;
retval = tdb_traverse(udevdb, traverse_callback, NULL);
if (retval < 0)
return -ENODEV;
else
return 0;
}
static struct udevice *find_dev;
static char *find_path;
static const char *find_name;
static int find_found;
while (1) {
ent = readdir(dir);
if (ent == NULL || ent->d_name[0] == '\0')
break;
static int find_device_by_name(const char *path, struct udevice *udev)
{
char *pos;
int len;
if (strncmp(udev->name, find_name, sizeof(udev->name)) == 0) {
memcpy(find_dev, udev, sizeof(struct udevice));
strfieldcpymax(find_path, path, NAME_SIZE);
find_found = 1;
/* stop search */
return 1;
}
/* look for matching symlink*/
foreach_strpart(udev->symlink, " ", pos, len) {
if (strncmp(pos, find_name, len) != 0)
if (ent->d_name[0] == '.')
continue;
if (len != strlen(find_name))
continue;
snprintf(filename, NAME_SIZE-1, "%s/%s", udev_db_path, ent->d_name);
filename[NAME_SIZE-1] = '\0';
memcpy(find_dev, udev, sizeof(struct udevice));
strfieldcpymax(find_path, path, NAME_SIZE);
find_found = 1;
return 1;
memset(&db_udev, 0x00, sizeof(struct udevice));
if (parse_db_file(&db_udev, filename) == 0) {
char *pos;
int len;
if (strncmp(name, db_udev.name, NAME_SIZE) == 0) {
goto found;
}
foreach_strpart(db_udev.symlink, " ", pos, len) {
if (strncmp(name, pos, len) != 0)
continue;
if (len == strlen(name))
goto found;
}
}
}
closedir(dir);
return -1;
found:
closedir(dir);
strfieldcpy(udev->devpath, db_udev.devpath);
strfieldcpy(udev->name, db_udev.name);
strfieldcpy(udev->symlink, db_udev.symlink);
udev->partitions = db_udev.partitions;
return 0;
}
/**
* udevdb_get_dev_byname: search device with given name by traversing the whole database
*/
int udevdb_get_dev_byname(const char *name, char *path, struct udevice *dev)
{
find_found = 0;
find_path = path;
find_dev = dev;
find_name = name;
udevdb_call_foreach(find_device_by_name);
if (find_found == 1)
return 0;
else
return -1;
}

View file

@ -1,22 +1,34 @@
/*
* udevdb header file
* udevdb.h
*
* Userspace devfs
*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 of the License.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef _UDEVDB_H_
#define _UDEVDB_H_
/* Udevdb initialization flags */
#define UDEVDB_DEFAULT 0 /* defaults database to use file */
#define UDEVDB_INTERNAL 1 /* don't store db on disk, use in memory */
/* function prototypes */
extern void udevdb_exit(void);
extern int udevdb_init(int init_flag);
extern int udevdb_open_ro(void);
extern int udevdb_call_foreach(int (*user_record_handler) (const char *path, struct udevice *dev));
extern int udevdb_add_dev(struct udevice *dev);
extern int udevdb_get_dev(const char *path, struct udevice *dev);
extern int udevdb_delete_dev(const char *path);
extern int udevdb_get_dev_byname(const char *name, char *path, struct udevice *dev);
extern int udevdb_get_dev(struct udevice *dev);
extern int udevdb_delete_dev(struct udevice *dev);
extern int udevdb_get_dev_byname(struct udevice *udev, const char *name);
#endif /* _UDEVDB_H_ */

View file

@ -29,7 +29,7 @@ interface.
.RB Needs " \-p " or " \-n " specified.
.br
Valid types are:
.BR name ", " symlink ", " mode " ," owner " , " group " , " path " or " all.
.BR name ", " symlink ", " path " or " all.
.TP
.BI \-p " sysfs_path"
Specify the sysfs path of the device to query.
@ -46,9 +46,6 @@ attributes along the device chain. Useful for finding
unique attributes to compose a rule.
.RB Needs " \-p " specified.
.TP
.B \-d
Dump the whole database.
.TP
.B \-h
Print help text.
.SH "FILES"

View file

@ -103,19 +103,11 @@ exit:
return retval;
}
/* callback for database dump */
static int print_record(const char *path, struct udevice *dev)
static int print_record(struct udevice *udev)
{
printf("P: %s\n", path);
printf("N: %s\n", dev->name);
printf("T: %c\n", dev->type);
printf("M: %#o\n", dev->mode);
printf("S: %s\n", dev->symlink);
printf("O: %s\n", dev->owner);
printf("G: %s\n", dev->group);
printf("F: %s\n", dev->config_file);
printf("L: %i\n", dev->config_line);
printf("U: %li\n", dev->config_uptime);
printf("P: %s\n", udev->devpath);
printf("N: %s\n", udev->name);
printf("S: %s\n", udev->symlink);
printf("\n");
return 0;
}
@ -125,9 +117,6 @@ enum query_type {
NAME,
PATH,
SYMLINK,
MODE,
OWNER,
GROUP,
ALL
};
@ -213,7 +202,7 @@ static int process_options(void)
static const char short_options[] = "adn:p:q:rVh";
int option;
int retval = 1;
struct udevice dev;
struct udevice udev;
int root = 0;
int attributes = 0;
enum query_type query = NONE;
@ -254,21 +243,6 @@ static int process_options(void)
break;
}
if (strcmp(optarg, "mode") == 0) {
query = MODE;
break;
}
if (strcmp(optarg, "owner") == 0) {
query = OWNER;
break;
}
if (strcmp(optarg, "group") == 0) {
query = GROUP;
break;
}
if (strcmp(optarg, "path") == 0) {
query = PATH;
break;
@ -290,16 +264,6 @@ static int process_options(void)
attributes = 1;
break;
case 'd':
retval = udevdb_open_ro();
if (retval != 0) {
printf("unable to open udev database\n");
exit(2);
}
udevdb_call_foreach(print_record);
udevdb_exit();
exit(0);
case 'V':
printf("udevinfo, version %s\n", UDEV_VERSION);
exit(0);
@ -314,12 +278,6 @@ static int process_options(void)
/* process options */
if (query != NONE) {
retval = udevdb_open_ro();
if (retval != 0) {
printf("unable to open udev database\n");
return -EACCES;
}
if (path[0] != '\0') {
/* remove sysfs_path if given */
if (strncmp(path, sysfs_path, strlen(sysfs_path)) == 0) {
@ -334,7 +292,9 @@ static int process_options(void)
pos = path;
}
}
retval = udevdb_get_dev(pos, &dev);
memset(&udev, 0x00, sizeof(struct udevice));
strfieldcpy(udev.devpath, pos);
retval = udevdb_get_dev(&udev);
if (retval != 0) {
printf("device not found in database\n");
goto exit;
@ -344,15 +304,21 @@ static int process_options(void)
if (name[0] != '\0') {
/* remove udev_root if given */
if (strncmp(name, udev_root, strlen(udev_root)) == 0) {
pos = name + strlen(udev_root);
int len = strlen(udev_root);
if (strncmp(name, udev_root, len) == 0) {
pos = &name[len+1];
} else
pos = name;
retval = udevdb_get_dev_byname(pos, path, &dev);
memset(&udev, 0x00, sizeof(struct udevice));
strfieldcpy(udev.name, pos);
retval = udevdb_get_dev_byname(&udev, pos);
if (retval != 0) {
printf("device not found in database\n");
goto exit;
}
goto print;
}
@ -362,25 +328,16 @@ static int process_options(void)
print:
switch(query) {
case NAME:
if (root)
strfieldcpy(result, udev_root);
strfieldcat(result, dev.name);
if (root) {
snprintf(result, NAME_SIZE-1, "%s/%s", udev_root, udev.name);
result[NAME_SIZE-1] = '\0';
} else {
strfieldcpy(result, udev.name);
}
break;
case SYMLINK:
strfieldcpy(result, dev.symlink);
break;
case MODE:
sprintf(result, "%#o", dev.mode);
break;
case GROUP:
strfieldcpy(result, dev.group);
break;
case OWNER:
strfieldcpy(result, dev.owner);
strfieldcpy(result, udev.symlink);
break;
case PATH:
@ -388,7 +345,7 @@ print:
break;
case ALL:
print_record(path, &dev);
print_record(&udev);
goto exit;
default:
@ -397,7 +354,6 @@ print:
printf("%s\n", result);
exit:
udevdb_exit();
return retval;
}
@ -427,9 +383,6 @@ help:
" -q TYPE query database for the specified value:\n"
" 'name' name of device node\n"
" 'symlink' pointing to node\n"
" 'mode' permissions of node\n"
" 'owner' of node\n"
" 'group' of node\n"
" 'path' sysfs device path\n"
" 'all' all values\n"
"\n"
@ -438,7 +391,6 @@ help:
"\n"
" -r print udev root\n"
" -a print all SYSFS_attributes along the device chain\n"
" -d dump whole database\n"
" -V print udev version\n"
" -h print this help text\n"
"\n");

View file

@ -95,7 +95,7 @@ static void run_udev(const char *subsystem)
switch (pid) {
case 0:
/* child */
execl(UDEV_BIN, UDEV_BIN, subsystem, NULL);
execl(UDEV_BIN, "udev", subsystem, NULL);
dbg("exec of child failed");
_exit(1);
break;