mirror of
https://github.com/systemd/systemd.git
synced 2024-12-25 01:34:28 +03:00
[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:
parent
482b0ecd8f
commit
2b41e68a08
@ -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
|
||||
|
29
Makefile
29
Makefile
@ -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)" \
|
||||
|
1
dev_d.c
1
dev_d.c
@ -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);
|
||||
|
@ -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,8 +146,8 @@ 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)
|
||||
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
|
||||
@ -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.
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
40
namedev.c
40
namedev.c
@ -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;
|
||||
|
32
tdb/Makefile
32
tdb/Makefile
@ -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
|
167
tdb/README
167
tdb/README
@ -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
|
433
tdb/spinlock.c
433
tdb/spinlock.c
@ -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
|
@ -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
|
150
tdb/tdb.h
150
tdb/tdb.h
@ -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 */
|
@ -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
|
201
tdb/tdbback.c
201
tdb/tdbback.c
@ -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;
|
||||
}
|
@ -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);
|
137
tdb/tdbbackup.c
137
tdb/tdbbackup.c
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
830
tdb/tdbutil.c
830
tdb/tdbutil.c
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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__ */
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
11
udev.c
@ -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
13
udev.h
@ -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];
|
||||
|
48
udev_add.c
48
udev_add.c
@ -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 */
|
||||
|
@ -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);
|
||||
|
45
udev_lib.c
45
udev_lib.c
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
306
udevdb.c
@ -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;
|
||||
}
|
||||
|
38
udevdb.h
38
udevdb.h
@ -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_ */
|
||||
|
@ -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"
|
||||
|
98
udevinfo.c
98
udevinfo.c
@ -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");
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user