> 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 */