1/*
2 * Copyright (C) 2006 Kay Sievers <kay@vrfy.org>
3 *
4 *	This program is free software; you can redistribute it and/or modify it
5 *	under the terms of the GNU General Public License as published by the
6 *	Free Software Foundation version 2 of the License.
7 *
8 *	This program is distributed in the hope that it will be useful, but
9 *	WITHOUT ANY WARRANTY; without even the implied warranty of
10 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 *	General Public License for more details.
12 *
13 *	You should have received a copy of the GNU General Public License along
14 *	with this program; if not, write to the Free Software Foundation, Inc.,
15 *	51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16 *
17 */
18
19#include <stdlib.h>
20#include <stddef.h>
21#include <string.h>
22#include <stdio.h>
23#include <unistd.h>
24#include <errno.h>
25#include <dirent.h>
26#include <fcntl.h>
27#include <syslog.h>
28#include <getopt.h>
29#include <sys/stat.h>
30#include <sys/types.h>
31
32#include "udev.h"
33#include "udevd.h"
34
35#define DEFAULT_TIMEOUT			180
36#define LOOP_PER_SECOND			20
37
38
39#ifdef USE_LOG
40void log_message(int priority, const char *format, ...)
41{
42	va_list args;
43
44	if (priority > udev_log_priority)
45		return;
46
47	va_start(args, format);
48	vsyslog(priority, format, args);
49	va_end(args);
50}
51#endif
52
53int main(int argc, char *argv[], char *envp[])
54{
55	char queuename[PATH_SIZE];
56	char filename[PATH_SIZE];
57	unsigned long long seq_kernel;
58	unsigned long long seq_udev;
59	char seqnum[32];
60	int fd;
61	ssize_t len;
62	int timeout = DEFAULT_TIMEOUT;
63	int loop;
64	static const struct option options[] = {
65		{ "timeout", 1, NULL, 't' },
66		{ "help", 0, NULL, 'h' },
67		{}
68	};
69	int option;
70	int rc = 1;
71	int seconds;
72
73	logging_init("udevsettle");
74	udev_config_init();
75	dbg("version %s", UDEV_VERSION);
76	sysfs_init();
77
78	while (1) {
79		option = getopt_long(argc, argv, "t:h", options, NULL);
80		if (option == -1)
81			break;
82
83		switch (option) {
84		case 't':
85			seconds = atoi(optarg);
86			if (seconds > 0)
87				timeout = seconds;
88			else
89				fprintf(stderr, "invalid timeout value\n");
90			dbg("timeout=%i", timeout);
91			break;
92		case 'h':
93			printf("Usage: udevsettle [--help] [--timeout=<seconds>]\n\n");
94			goto exit;
95		}
96	}
97
98	strlcpy(queuename, udev_root, sizeof(queuename));
99	strlcat(queuename, "/" EVENT_QUEUE_DIR, sizeof(queuename));
100
101	loop = timeout * LOOP_PER_SECOND;
102	while (loop--) {
103		/* wait for events in queue to finish */
104		while (loop--) {
105			struct stat statbuf;
106
107			if (stat(queuename, &statbuf) < 0) {
108				info("queue is empty");
109				break;
110			}
111			usleep(1000 * 1000 / LOOP_PER_SECOND);
112		}
113		if (loop <= 0) {
114			info("timeout waiting for queue");
115			goto exit;
116		}
117
118		/* read current udev seqnum */
119		strlcpy(filename, udev_root, sizeof(filename));
120		strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename));
121		fd = open(filename, O_RDONLY);
122		if (fd < 0)
123			goto exit;
124		len = read(fd, seqnum, sizeof(seqnum)-1);
125		close(fd);
126		if (len <= 0)
127			goto exit;
128		seqnum[len] = '\0';
129		seq_udev = strtoull(seqnum, NULL, 10);
130		info("udev seqnum = %llu", seq_udev);
131
132		/* read current kernel seqnum */
133		strlcpy(filename, sysfs_path, sizeof(filename));
134		strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename));
135		fd = open(filename, O_RDONLY);
136		if (fd < 0)
137			goto exit;
138		len = read(fd, seqnum, sizeof(seqnum)-1);
139		close(fd);
140		if (len <= 0)
141			goto exit;
142		seqnum[len] = '\0';
143		seq_kernel = strtoull(seqnum, NULL, 10);
144		info("kernel seqnum = %llu", seq_kernel);
145
146		/* make sure all kernel events have arrived in the queue */
147		if (seq_udev >= seq_kernel) {
148			info("queue is empty and no pending events left");
149			rc = 0;
150			goto exit;
151		}
152		usleep(1000 * 1000 / LOOP_PER_SECOND);
153		info("queue is empty, but events still pending");
154	}
155
156exit:
157	sysfs_cleanup();
158	logging_close();
159	return rc;
160}
161