From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kay Sievers Date: Sun, 24 Oct 2004 05:08:41 +0000 Subject: Re: udev hangs under high loads Message-Id: <20041024050841.GA26445@vrfy.org> MIME-Version: 1 Content-Type: multipart/mixed; boundary="sdtB3X0nJg68CQEu" List-Id: References: <20041023054119.GA11915@kroah.com> In-Reply-To: <20041023054119.GA11915@kroah.com> To: linux-hotplug@vger.kernel.org --sdtB3X0nJg68CQEu Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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 :) Thanks, Kay --sdtB3X0nJg68CQEu Content-Type: text/plain; charset=us-ascii Content-Disposition: inline; filename="udev-kill-tdb-01.patch" diff -Nru a/Makefile b/Makefile --- a/Makefile 2004-10-24 06:45:27 +02:00 +++ b/Makefile 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/dev_d.c 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/etc/udev/udev.conf.in 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/namedev.c 2004-10-24 06:45:27 +02:00 @@ -184,29 +184,29 @@ * @param name Name to check for * @return 0 if 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-24 06:45:27 +02:00 +++ b/test/udev-test.pl 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev.8.in 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev.c 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev.h 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev_add.c 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev_config.c 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev_lib.c 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev_lib.h 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udev_remove.c 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udevd.c 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udevdb.c 2004-10-24 06:45:27 +02:00 @@ -1,10 +1,10 @@ /* - * udevdb.c - udev database library + * udevdb.c * * Userspace devfs * * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2004 Kay Sievers * * 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 #include #include #include #include #include -#include #include -#include +#include #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-24 06:45:27 +02:00 +++ b/udevdb.h 2004-10-24 06:45:27 +02:00 @@ -1,22 +1,34 @@ /* - * udevdb header file + * udevdb.h + * + * Userspace devfs + * + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2004 Kay Sievers + * + * 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-24 06:45:27 +02:00 +++ b/udevinfo.8 2004-10-24 06:45:27 +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-24 06:45:27 +02:00 +++ b/udevinfo.c 2004-10-24 06:45:27 +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,15 @@ 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'; + } + strfieldcat(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 +344,7 @@ break; case ALL: - print_record(path, &dev); + print_record(&udev); goto exit; default: @@ -397,7 +353,6 @@ printf("%s\n", result); exit: - udevdb_exit(); return retval; } @@ -427,9 +382,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 +390,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"); --sdtB3X0nJg68CQEu-- ------------------------------------------------------- This SF.net email is sponsored by: IT Product Guide on ITManagersJournal Use IT products in your business? Tell us what you think of them. Give us Your Opinions, Get Free ThinkGeek Gift Certificates! Click to find out more http://productguide.itmanagersjournal.com/guidepromo.tmpl _______________________________________________ Linux-hotplug-devel mailing list http://linux-hotplug.sourceforge.net Linux-hotplug-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel