From: Kay Sievers <kay.sievers@vrfy.org>
To: linux-hotplug@vger.kernel.org
Subject: Re: udev hangs under high loads
Date: Mon, 25 Oct 2004 02:51:14 +0000 [thread overview]
Message-ID: <20041025025114.GA29168@vrfy.org> (raw)
In-Reply-To: <20041023054119.GA11915@kroah.com>
[-- Attachment #1: Type: text/plain, Size: 3669 bytes --]
On Sun, Oct 24, 2004 at 07:08:41AM +0200, Kay Sievers wrote:
> On Sat, Oct 23, 2004 at 08:31:04AM +0200, Kay Sievers wrote:
> > On Fri, Oct 22, 2004 at 10:41:19PM -0700, Greg KH wrote:
> > > One of my coworkers (hi Nish) is trying to get 10000 disk support tested
> > > and working properly with linux and ran into a nasty udev issue
> > > yesterday. It seems that under a very high load, with a whole bunch of
> > > hotplug events happening (for when the disks get added to the system),
> > > udev hangs.
> > >
> > > It hangs in the "grabbing the database lock" portion of the code (don't
> > > have actual logs of where it hangs, will try to get that next week.)
> > > But the interesting thing is we almost handle everything properly.
> > > udev creates the node, tries to write to the database. Then the timer
> > > expires and we report this. After the timer expires, udev is done for.
> > > It just sits and spins, doing a nanosleep constantly. Have 500 of those
> > > instances all running at once, all at a nice level of -10 is a sure way
> > > to bring a box (even a relatively big one) down hard.
> > >
> > > So, while I'll agree finding the root tdb locking bug is a good idea, I
> > > wanted to point out that perhaps we should just exit udev if our timeout
> > > expires, instead of still waiting around. Or do you have a better
> > > solution?
> >
> > Maybe the time the udev process locks the db is too long for that setup
> > and the serialization of the concurrent locks will take longer than the
> > timout of the process?
> >
> > Here is a patch that tries to limit the access time to the db.
> > The current udev opens the db, reads all the rules, writes to the
> > db, executes the scripts and then closes the db. With this patch we open
> > the db after the rules and close it directly after writing to it.
> >
> > A rate limit in udevd may help here too, to keep that under control.
> >
> > > Try testing this out on your own using the scsi_debug module and adding
> > > a few hundred disks. It also helps if you have scsi generic support
> > > enabled, as that creates 2 udev events for every disk created.
> >
> > I expect that this is completely different on a SMP machine, but I will
> > try again. I once tried it with 200 disks and this was working well.
>
> Here is another idea. I've ripped out the tdb code completely. We are
> maintaining a directory of files at /dev/.udevdb/* now. Every node will
> have a corresponding file (the slashes in DEVPATH are replaced by another
> char), that carries all neccessary data. The files are human readable:
>
> [root@pim default]# ls -l /dev/.udevdb/*hda*
> -rw-r--r-- 1 root root 26 Oct 24 06:32 /dev/.udevdb/block@hda
> -rw-r--r-- 1 root root 32 Oct 24 06:32 /dev/.udevdb/block@hda@hda1
> -rw-r--r-- 1 root root 32 Oct 24 06:32 /dev/.udevdb/block@hda@hda2
> -rw-r--r-- 1 root root 32 Oct 24 06:32 /dev/.udevdb/block@hda@hda3
> -rw-r--r-- 1 root root 54 Oct 24 06:32 /dev/.udevdb/block@hdc
>
> [root@pim default]# cat /dev/.udevdb/block@hdc
> P:/block/hdc
> N:hdc
> S:cdrom dvd cdwriter dvdwriter
> A:0
>
> This way we have _no_ locking at all in userspace, every event operation
> is completely independent from other events. No need to synchronize anything,
> which is expected to be much much faster and reliable on your 10.000 disk
> system. It may also work over NFS.
>
> The patch is complete, but not well tested. It just stopped working on
> it, after the regression test was successful and my box was able to
> reboot and remove the symlinks grabbed form the files :)
New patch with a fix for a bug introduced in udevinfo.
Thanks,
Kay
[-- Attachment #2: udev-kill-tdb-02.patch --]
[-- Type: text/plain, Size: 35723 bytes --]
diff -Nru a/Makefile b/Makefile
--- a/Makefile 2004-10-25 04:48:06 +02:00
+++ b/Makefile 2004-10-25 04:48:06 +02:00
@@ -113,11 +113,6 @@
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 +196,6 @@
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 +213,7 @@
namedev.o \
namedev_parse.o \
dev_d.o \
- $(SYSFS) \
- $(TDB)
+ $(SYSFS)
HEADERS = udev.h \
udev_lib.h \
@@ -262,8 +253,8 @@
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 \"$(udevdir)/.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 +291,7 @@
$(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 +328,8 @@
$(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 )
+#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 )
DISTDIR := $(RELEASE_NAME)
srcdir = .
release: clean
@@ -430,7 +422,7 @@
- ln -f -s $(sbindir)/$(WAIT) $(DESTDIR)$(hotplugdir)/05-wait_for_sysfs.hotplug
ifndef DESTDIR
- killall udevd
- - rm -f $(udevdir)/.udev.tdb
+ - rm -f $(udevdir)/.udev/*
endif
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
@@ -456,7 +448,7 @@
- rm $(usrbindir)/$(TESTER)
- rm $(usrbindir)/$(WAIT)
- rmdir $(hotplugdir)
- - rm $(udevdir)/.udev.tdb
+ - rm -f $(udevdir)/.udev/*
- rmdir $(udevdir)
@extras="$(EXTRAS)" ; for target in $$extras ; do \
echo $$target ; \
diff -Nru a/dev_d.c b/dev_d.c
--- a/dev_d.c 2004-10-25 04:48:06 +02:00
+++ b/dev_d.c 2004-10-25 04:48:06 +02:00
@@ -47,7 +47,6 @@
switch (pid) {
case 0:
/* child */
- udevdb_exit(); /* close udevdb */
fd = open("/dev/null", O_RDWR);
if ( fd >= 0) {
dup2(fd, STDOUT_FILENO);
diff -Nru a/etc/udev/udev.conf.in b/etc/udev/udev.conf.in
--- a/etc/udev/udev.conf.in 2004-10-25 04:48:06 +02:00
+++ b/etc/udev/udev.conf.in 2004-10-25 04:48:06 +02:00
@@ -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
diff -Nru a/namedev.c b/namedev.c
--- a/namedev.c 2004-10-25 04:48:06 +02:00
+++ b/namedev.c 2004-10-25 04:48:06 +02:00
@@ -184,29 +184,29 @@
* @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;
-
- /* have to sweep the database for each lookup */
- result = 0;
- strncpy(temp, name, sizeof (temp));
+ char filename[NAME_SIZE];
+ int num = 0;
+ struct udevice db_udev;
+
+ 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;
+ }
+
+ 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';
}
-
-found:
- return result;
}
static void apply_format(struct udevice *udev, char *string, size_t maxsize,
@@ -329,7 +329,7 @@
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;
diff -Nru a/test/udev-test.pl b/test/udev-test.pl
--- a/test/udev-test.pl 2004-10-25 04:48:06 +02:00
+++ b/test/udev-test.pl 2004-10-25 04:48:06 +02:00
@@ -30,7 +30,7 @@
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";
@@ -1258,7 +1258,7 @@
}
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";
}
@@ -1302,7 +1302,7 @@
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);
diff -Nru a/udev.8.in b/udev.8.in
--- a/udev.8.in 2004-10-25 04:48:06 +02:00
+++ b/udev.8.in 2004-10-25 04:48:06 +02:00
@@ -74,7 +74,7 @@
.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 @@
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
diff -Nru a/udev.c b/udev.c
--- a/udev.c 2004-10-25 04:48:06 +02:00
+++ b/udev.c 2004-10-25 04:48:06 +02:00
@@ -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,12 +62,11 @@
{
switch (signum) {
case SIGALRM:
- gotalarm = 1;
- info("error: timeout reached, event probably not handled correctly");
- break;
+ info("we give up, timeout reached, event probably not handled correctly");
+ logging_close();
+ exit(1);
case SIGINT:
case SIGTERM:
- udevdb_exit();
exit(20 + signum);
default:
dbg("unhandled signal %d", signum);
@@ -156,10 +153,6 @@
/* 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");
@@ -198,8 +191,6 @@
/* run scripts */
dev_d_execute(&udev);
}
-
- udevdb_exit();
exit:
logging_close();
diff -Nru a/udev.h b/udev.h
--- a/udev.h 2004-10-25 04:48:06 +02:00
+++ b/udev.h 2004-10-25 04:48:06 +02:00
@@ -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_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];
diff -Nru a/udev_add.c b/udev_add.c
--- a/udev_add.c 2004-10-25 04:48:06 +02:00
+++ b/udev_add.c 2004-10-25 04:48:06 +02:00
@@ -68,37 +68,6 @@
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 @@
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 @@
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 @@
/* 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 @@
"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 */
diff -Nru a/udev_config.c b/udev_config.c
--- a/udev_config.c 2004-10-25 04:48:06 +02:00
+++ b/udev_config.c 2004-10-25 04:48:06 +02:00
@@ -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 @@
* 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 @@
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 @@
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 @@
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 @@
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);
diff -Nru a/udev_lib.c b/udev_lib.c
--- a/udev_lib.c 2004-10-25 04:48:06 +02:00
+++ b/udev_lib.c 2004-10-25 04:48:06 +02:00
@@ -120,6 +120,33 @@
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 @@
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 @@
/* 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);
diff -Nru a/udev_lib.h b/udev_lib.h
--- a/udev_lib.h 2004-10-25 04:48:06 +02:00
+++ b/udev_lib.h 2004-10-25 04:48:06 +02:00
@@ -83,11 +83,11 @@
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);
diff -Nru a/udev_remove.c b/udev_remove.c
--- a/udev_remove.c 2004-10-25 04:48:06 +02:00
+++ b/udev_remove.c 2004-10-25 04:48:06 +02:00
@@ -102,10 +102,9 @@
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 @@
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 @@
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 @@
}
/* 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 @@
filename, strerror(errno));
return retval;
}
- if (strchr(dev->symlink, '/')) {
+ if (strchr(udev->symlink, '/')) {
delete_path(filename);
}
}
@@ -168,18 +169,14 @@
*/
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 @@
}
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);
}
diff -Nru a/udevd.c b/udevd.c
--- a/udevd.c 2004-10-25 04:48:06 +02:00
+++ b/udevd.c 2004-10-25 04:48:06 +02:00
@@ -147,7 +147,7 @@
switch (pid) {
case 0:
/* child */
- execle(udev_bin, "udev", msg->subsystem, NULL, env);
+ execle(udev_bin, udev_bin, msg->subsystem, NULL, env);
dbg("exec of child failed");
_exit(1);
break;
diff -Nru a/udevdb.c b/udevdb.c
--- a/udevdb.c 2004-10-25 04:48:06 +02:00
+++ b/udevdb.c 2004-10-25 04:48:06 +02:00
@@ -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 '@'
-int udevdb_add_dev(struct udevice *udev)
+static int get_db_filename(struct udevice *udev, char *filename, int len)
{
- TDB_DATA key, data;
- char keystr[SYSFS_PATH_MAX];
-
- if (udev->test_run)
- return 0;
-
- if (udevdb == NULL)
- return -1;
+ char devpath[SYSFS_PATH_MAX];
+ char *pos;
- memset(keystr, 0x00, SYSFS_PATH_MAX);
- strfieldcpy(keystr, udev->devpath);
- key.dptr = keystr;
- key.dsize = strlen(keystr) + 1;
-
- data.dptr = (void *) udev;
- data.dsize = UDEVICE_DB_LEN;
- dbg("store key '%s' for device '%s'", keystr, udev->name);
+ /* 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 tdb_store(udevdb, key, data, TDB_REPLACE);
+ return 0;
}
-int udevdb_get_dev(const char *path, struct udevice *udev)
+int udevdb_add_dev(struct udevice *udev)
{
- TDB_DATA key, data;
+ char filename[SYSFS_PATH_MAX];
+ FILE *f;
- if (udevdb == NULL)
- return -1;
+ if (udev->test_run)
+ return 0;
- if (path == NULL)
- return -ENODEV;
+ get_db_filename(udev, filename, SYSFS_PATH_MAX);
- key.dptr = (void *)path;
- key.dsize = strlen(path) + 1;
+ create_path(filename);
- data = tdb_fetch(udevdb, key);
- if (data.dptr == NULL || data.dsize == 0)
- return -ENODEV;
+ 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(udev, 0x00, sizeof(struct udevice));
- memcpy(udev, data.dptr, UDEVICE_DB_LEN);
+ 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);
+
+ 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;
+ }
- if (path == NULL)
- return -EINVAL;
-
- memset(keystr, 0, sizeof(keystr));
- strfieldcpy(keystr, path);
+ 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;
+ }
+ }
- key.dptr = keystr;
- key.dsize = strlen(keystr) + 1;
+ if (udev->name[0] == '\0')
+ return -1;
- return tdb_delete(udevdb, key);
+ return 0;
}
-/**
- * udevdb_exit: closes database
- */
-void udevdb_exit(void)
+int udevdb_get_dev(struct udevice *udev)
{
- if (udevdb != NULL) {
- tdb_close(udevdb);
- udevdb = NULL;
- }
+ char filename[SYSFS_PATH_MAX];
+
+ get_db_filename(udev, filename, SYSFS_PATH_MAX);
+
+ return parse_db_file(udev, filename);
}
-/**
- * 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)
+int udevdb_delete_dev(struct udevice *udev)
{
- if (init_flag != UDEVDB_DEFAULT && init_flag != UDEVDB_INTERNAL)
- return -EINVAL;
+ char filename[SYSFS_PATH_MAX];
- tdb_set_lock_alarm(&gotalarm);
+ get_db_filename(udev, filename, SYSFS_PATH_MAX);
+ unlink(filename);
- 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_byname(struct udevice *udev, const char *name)
{
- 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;
+ struct dirent *ent;
+ DIR *dir;
+ char filename[NAME_SIZE];
+ struct udevice db_udev;
+
+ dir = opendir(udev_db_path);
+ if (dir == NULL) {
+ dbg("unable to udev db '%s'", udev_db_path);
+ return -1;
}
- return 0;
-}
-static int (*user_record_callback) (const char *path, struct udevice *dev);
+ while (1) {
+ ent = readdir(dir);
+ if (ent == NULL || ent->d_name[0] == '\0')
+ break;
-static int traverse_callback(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
-{
- return user_record_callback((char*) key.dptr, (struct udevice*) dbuf.dptr);
-}
+ if (ent->d_name[0] == '.')
+ continue;
-/**
- * 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;
+ snprintf(filename, NAME_SIZE-1, "%s/%s", udev_db_path, ent->d_name);
+ filename[NAME_SIZE-1] = '\0';
- if (udevdb == NULL)
- return -1;
+ memset(&db_udev, 0x00, sizeof(struct udevice));
+ if (parse_db_file(&db_udev, filename) == 0) {
+ char *pos;
+ int len;
- 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;
-}
+ if (strncmp(name, db_udev.name, NAME_SIZE) == 0) {
+ goto found;
+ }
-static struct udevice *find_dev;
-static char *find_path;
-static const char *find_name;
-static int find_found;
+ foreach_strpart(db_udev.symlink, " ", pos, len) {
+ if (strncmp(name, pos, len) != 0)
+ continue;
-static int find_device_by_name(const char *path, struct udevice *udev)
-{
- char *pos;
- int len;
+ if (len == strlen(name))
+ goto found;
+ }
- 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)
- continue;
+ }
+ }
- if (len != strlen(find_name))
- continue;
+ closedir(dir);
- memcpy(find_dev, udev, sizeof(struct udevice));
- strfieldcpymax(find_path, path, NAME_SIZE);
- find_found = 1;
- return 1;
- }
- return 0;
-}
+ return -1;
-/**
- * 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;
+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;
}
diff -Nru a/udevdb.h b/udevdb.h
--- a/udevdb.h 2004-10-25 04:48:06 +02:00
+++ b/udevdb.h 2004-10-25 04:48:06 +02:00
@@ -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_ */
diff -Nru a/udevinfo.8 b/udevinfo.8
--- a/udevinfo.8 2004-10-25 04:48:06 +02:00
+++ b/udevinfo.8 2004-10-25 04:48:06 +02:00
@@ -29,7 +29,7 @@
.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.
@@ -45,9 +45,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.
diff -Nru a/udevinfo.c b/udevinfo.c
--- a/udevinfo.c 2004-10-25 04:48:06 +02:00
+++ b/udevinfo.c 2004-10-25 04:48:06 +02:00
@@ -103,19 +103,11 @@
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 @@
NAME,
PATH,
SYMLINK,
- MODE,
- OWNER,
- GROUP,
ALL
};
@@ -213,7 +202,7 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
break;
case ALL:
- print_record(path, &dev);
+ print_record(&udev);
goto exit;
default:
@@ -397,7 +354,6 @@
printf("%s\n", result);
exit:
- udevdb_exit();
return retval;
}
@@ -427,9 +383,6 @@
" -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 @@
"\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");
next prev parent reply other threads:[~2004-10-25 2:51 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-10-23 5:41 udev hangs under high loads Greg KH
2004-10-23 6:31 ` Kay Sievers
2004-10-24 5:08 ` Kay Sievers
2004-10-24 5:53 ` Olaf Hering
2004-10-24 15:53 ` Kay Sievers
2004-10-24 15:57 ` Olaf Hering
2004-10-24 19:27 ` Kevin P. Fleming
2004-10-24 19:46 ` Kay Sievers
2004-10-24 19:51 ` Marco d'Itri
2004-10-24 19:56 ` Kay Sievers
2004-10-25 2:51 ` Kay Sievers [this message]
2004-10-25 7:01 ` Greg KH
2004-10-25 17:07 ` Kay Sievers
2004-10-25 17:12 ` Kevin P. Fleming
2004-10-25 17:27 ` Nishanth Aravamudan
2004-10-25 17:53 ` Kay Sievers
2004-10-25 20:55 ` Nishanth Aravamudan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20041025025114.GA29168@vrfy.org \
--to=kay.sievers@vrfy.org \
--cc=linux-hotplug@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).