Re: udev 145, when are events fully processed?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> On 8/7/09, Nigel Kukard <nkukard@xxxxxxxx> wrote:
>   
>>>> Trying to figure something out here, using the following I'm seeing a
>>>> delay in the creation of block devices in /dev ...
>>>>
>>>> # trigger the sorted events
>>>> echo -e '\000\000\000\000'>  /proc/sys/kernel/hotplug
>>>>
>>>> rm -fr /dev/.udev>  /dev/null 2>&1
>>>> mkdir -p /dev/.udev>  /dev/null 2>&1
>>>> /sbin/udevd
>>>>
>>>> # Not entirely sure what this is for
>>>> /sbin/udevadm control --env=STARTUP=1
>>>> /sbin/udevadm trigger
>>>>         
>>>> /sbin/udevadm settle
>>>>         
>>> settle should have waited until all events have been processed
>>>       
>> Does this mean fully processed or just received?  I asked on
>> #udev/irc.freenode.net and was told that settle only waits until udev
>> had received them.
>>     
>
> It does wait until events are "fully processed".  The problem is that
> this will only apply to the events generated directly by udevadm
> trigger.  USB devices may turn up late to the party for various
> reasons.
>
> Lurking on LKML it sounds like it _might_ be possible to fix the USB
> issue.  AFAIK no-one is working to support this for userspace though.
>
>   
>> The only reason it worked before was because of the speed improvements
>> made recently.
>>     
>
> Yup.
>
>   
>>>> # Nor sure what this does
>>>> /sbin/udevadm control --env=STARTUP=
>>>>
>>>>
>>>> I think my problem is, while all the events have been sent to udevd
>>>> there is a delay if I do a  "fsck LABEL=root" straight on say the next
>>>> line,  a "ls" shows that none of the block devices exist until a second
>>>> or two later. A sleep 5 before my "ls" works around this and the block
>>>> devices show up.
>>>>
>>>> Any ideas how I can determine once all udev events have finished
>>>> processing so I can continue boot?
>>>>         
>>> check would look like:
>>> /sbin/udevadm settle --timeout=0 || echo "Still not all udev events
>>> processed"
>>>
>>> waiting should be:
>>> /sbin/udevadm settle
>>>       
>> Same result on both.
>>
>> I went further and wrote a small C app to wait for the udev event, this
>> works 100%. I run the C app in the background before I run trigger &
>> settle, then do a "wait" until it returns.
>>
>> -N
>>     
>
> I think everybody else just loops waiting for the device node to
> appear.  So technically you may have a more advanced solution :-).
>
> Usually you also want a timeout in case something goes wrong.
> Depending on the system you can e.g. drop to an emergency shell or
> just print an error message.
>   
I have a nice C proggie I'll including in bootutils, unless you guys are
interested in including it in udev?. Attached for anyone interested,
with timeout support. It can wait for a device or labeled device to come
up  :)

-N
/*
 * 	udev-wait-for-device.c - Wait for device to appear 
 * 	Copyright (C) 2009, Nigel Kukard <nkukard@xxxxxxxx>
 *
 * 	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * 
 * --
 *
 * Portions of code from searches on the Internet:
 * - Copyright (C) 2009  Andy Walls <awa...@xxxxxxxxx>
 *
 */

#define VERSION "0.0.2"

#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <getopt.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <libudev.h>

/* Exit handling in a sane way */
static int udev_exit;

static void sig_handler(int signum)
{
	if (signum == SIGINT || signum == SIGTERM)
		udev_exit = 1;
}

/* Print out our usage */
void printUsage(char **argv) {
	printf("Usage: %s <options>\n",argv[0]);
	printf("\n");
	printf("Options:\n");
	printf("    -l,  --label=<LABEL>          Wait for device with this label\n");
	printf("    -d,  --device=<DEVICE>        Wait for device with this name\n");
	printf("    -t,  --timeout=<SECONDS>      Timeout in seconds to wait [5].\n");
	printf("    -h,  --help                   Display this page\n");
	printf("    -h                            Display this page\n\n");
	printf("Return codes: 0 - found, 1 - not found/timeout");
	printf("\n");
}


/* Main function */
int main(int argc, char *argv[])
{
        struct udev *udev;
        const char *s;
        struct udev_list_entry *udev_list_entry;

	int rc = 127;
	
	struct sigaction act;
	struct udev_monitor *udev_monitor = NULL;
	fd_set readfds;
	int prop = 1;

	char *deviceName;
	char *deviceLabel;
	int timeout = 5;


	/* Our long options */
	struct option long_options[] = {
		{"label",1,0,'l'},
		{"device",1,0,'d'},
		{"timeout",1,0,'t'},
		{"help",0,0,'h'},
		{0,0,0,0}
	};

//	printf("UDEV-WAIT-FOR v%s - Copyright (c) 2009, Nigel Kukard\n\n",VERSION);

	/* Go straight to help if no params provided */
	if (argc == 1) {
		printUsage(argv);
		exit(2);
	}

	/* Loop with options */
	while (1) {
		int option_index = 0;
		char c;

		/* Process */
		c = getopt_long(argc,argv,"l:d:",long_options,&option_index);
		if (c == -1)
			break;

		/* Check... */
		switch (c) {
			case 'l':
				deviceLabel = optarg;
				break;
			case 'd':
				deviceName = optarg;
				break;
			case 't':
				timeout = atoi(optarg);
				break;
			case 'h':
				printUsage(argv);
				return 0;
			default:
				printUsage(argv);
				return 1;
		}
	}

	/* We shouldn't have anything left over */
	if (optind < argc) {
		while (optind < argc)
			fprintf(stderr,"%s: Invalid option -- %s\n",argv[0],argv[optind++]);
		exit(2);
	}


	/* Initialize udev */
        udev = udev_new();
        if (udev == NULL) {
		fprintf(stderr, "ERROR: udev_new() failed\n");
                exit(3);
        }

	/* set signal handlers */
	memset(&act, 0x00, sizeof(struct sigaction));
	act.sa_handler = sig_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_RESTART;
	sigaction(SIGINT, &act, NULL);
	sigaction(SIGTERM, &act, NULL);

	/* Monitor from UDEV netlink */
	udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
	if (udev_monitor == NULL) {
		fprintf(stderr, "ERROR: Unable to create netlink socket\n");
		rc = 3;
		goto out;
	}

	/* Filter block devices */
	if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) < 0)
	{
		fprintf(stderr, "ERROR: Unable to apply filter\n");
		rc = 3;
		goto out;
	}

	/* Enable receiving of events */
	if (udev_monitor_enable_receiving(udev_monitor) < 0) {
		fprintf(stderr, "ERROR: Unable to subscribe to udev events\n");
		rc = 3;
		goto out;
	}

	/* Loop */
	while (!udev_exit) {
		int fdcount;
		struct timeval tv;

		/* Setup timeouts */
		tv.tv_sec = timeout;
		tv.tv_usec = 0;

		/* Setup select fds */
		FD_ZERO(&readfds);
		FD_SET(udev_monitor_get_fd(udev_monitor), &readfds);

		/* Fire up select */
		fdcount = select(udev_monitor_get_fd(udev_monitor)+1, &readfds, NULL, NULL, &tv);

		/* Look for timeout */
		if (fdcount == 0) {
			udev_exit = 1;
			rc = 1;
			continue;
		/* Look for interrupt */
		} else if (fdcount < 0) {
			if (errno != EINTR)
				fprintf(stderr, "ERROR Receiving uevent message: %m\n");
			continue;
		}

		/* Check for set FD */
		if (FD_ISSET(udev_monitor_get_fd(udev_monitor), &readfds)) {
			struct udev_device *device;
			struct udev_list_entry *list_entry;

			/* Grab device */
			device = udev_monitor_receive_device(udev_monitor);
			if (device == NULL)
				continue;

			/* Loop with properties */
			udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) {
				const char *propname = udev_list_entry_get_name(list_entry);
				const char *propval = udev_list_entry_get_value(list_entry);
				int found = 0;
	

				if (deviceLabel)
					if (!strcmp(propname,"ID_FS_LABEL") && !strcmp(propval,deviceLabel)) 
						found = 1;
	
				if (deviceName)
					if (!strcmp(propname,"DEVNAME") && !strcmp(propval,deviceName))
						found = 1;
	
				if (found) {
					rc = 0;
					udev_exit = 1;
					break;
				}
			}

			udev_device_unref(device);
		}
	}

out:
	udev_monitor_unref(udev_monitor);
        udev_unref(udev);
        exit(rc);
}

/* vim: ts=4 */

[Index of Archives]     [Linux Kernel]     [Linux DVB]     [Asterisk Internet PBX]     [DCCP]     [Netdev]     [X.org]     [Util Linux NG]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux