]>
Commit | Line | Data |
---|---|---|
7baada47 | 1 | /* |
fc206fbe | 2 | * Copyright (C) 2006-2009 Kay Sievers <kay@vrfy.org> |
bb38678e SJR |
3 | * Copyright (C) 2009 Canonical Ltd. |
4 | * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> | |
7baada47 | 5 | * |
55e9959b KS |
6 | * This program is free software: you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 2 of the License, or | |
9 | * (at your option) any later version. | |
7baada47 | 10 | * |
55e9959b KS |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
7baada47 KS |
18 | */ |
19 | ||
20 | #include <stdlib.h> | |
21 | #include <stddef.h> | |
22 | #include <string.h> | |
23 | #include <stdio.h> | |
24 | #include <unistd.h> | |
25 | #include <errno.h> | |
26 | #include <dirent.h> | |
27 | #include <fcntl.h> | |
28 | #include <syslog.h> | |
c2666405 | 29 | #include <getopt.h> |
c2c24d4d | 30 | #include <signal.h> |
959e8b5d | 31 | #include <time.h> |
a3eca08b KS |
32 | #include <sys/inotify.h> |
33 | #include <sys/poll.h> | |
7baada47 KS |
34 | #include <sys/stat.h> |
35 | #include <sys/types.h> | |
36 | ||
37 | #include "udev.h" | |
7baada47 | 38 | |
1985c76e | 39 | static int adm_settle(struct udev *udev, int argc, char *argv[]) |
7baada47 | 40 | { |
c2666405 | 41 | static const struct option options[] = { |
98d3d517 KS |
42 | { "seq-start", required_argument, NULL, 's' }, |
43 | { "seq-end", required_argument, NULL, 'e' }, | |
033e9f8c | 44 | { "timeout", required_argument, NULL, 't' }, |
97f48a8c | 45 | { "exit-if-exists", required_argument, NULL, 'E' }, |
b76ad2e5 | 46 | { "quiet", no_argument, NULL, 'q' }, |
033e9f8c | 47 | { "help", no_argument, NULL, 'h' }, |
c2666405 KS |
48 | {} |
49 | }; | |
ead7c62a | 50 | unsigned long long start_usec = now_usec(); |
98d3d517 KS |
51 | unsigned long long start = 0; |
52 | unsigned long long end = 0; | |
b76ad2e5 | 53 | int quiet = 0; |
97f48a8c | 54 | const char *exists = NULL; |
d2f4a346 | 55 | unsigned int timeout = 120; |
a3eca08b | 56 | struct pollfd pfd[1]; |
f6bb9e98 | 57 | struct udev_queue *udev_queue = NULL; |
ead7c62a | 58 | int rc = EXIT_FAILURE; |
7baada47 | 59 | |
7d563a17 | 60 | dbg(udev, "version %s\n", VERSION); |
7baada47 | 61 | |
88cbfb09 | 62 | for (;;) { |
8249e04e KS |
63 | int option; |
64 | int seconds; | |
65 | ||
97f48a8c | 66 | option = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL); |
c2666405 KS |
67 | if (option == -1) |
68 | break; | |
7baada47 | 69 | |
c2666405 | 70 | switch (option) { |
98d3d517 KS |
71 | case 's': |
72 | start = strtoull(optarg, NULL, 0); | |
73 | break; | |
74 | case 'e': | |
75 | end = strtoull(optarg, NULL, 0); | |
76 | break; | |
c2666405 KS |
77 | case 't': |
78 | seconds = atoi(optarg); | |
b76ad2e5 | 79 | if (seconds >= 0) |
e3396a2d KS |
80 | timeout = seconds; |
81 | else | |
82 | fprintf(stderr, "invalid timeout value\n"); | |
7d563a17 | 83 | dbg(udev, "timeout=%i\n", timeout); |
c2666405 | 84 | break; |
b76ad2e5 KS |
85 | case 'q': |
86 | quiet = 1; | |
87 | break; | |
97f48a8c KS |
88 | case 'E': |
89 | exists = optarg; | |
90 | break; | |
c2666405 | 91 | case 'h': |
f1e7e360 | 92 | printf("Usage: udevadm settle OPTIONS\n" |
97f48a8c KS |
93 | " --timeout=<seconds> maximum time to wait for events\n" |
94 | " --seq-start=<seqnum> first seqnum to wait for\n" | |
95 | " --seq-end=<seqnum> last seqnum to wait for\n" | |
96 | " --exit-if-exists=<file> stop waiting if file exists\n" | |
97 | " --quiet do not print list after timeout\n" | |
f1e7e360 | 98 | " --help\n\n"); |
d800e868 PU |
99 | exit(EXIT_SUCCESS); |
100 | default: | |
101 | exit(EXIT_FAILURE); | |
7baada47 KS |
102 | } |
103 | } | |
104 | ||
8249e04e KS |
105 | udev_queue = udev_queue_new(udev); |
106 | if (udev_queue == NULL) | |
9f894a33 | 107 | exit(2); |
98d3d517 KS |
108 | |
109 | if (start > 0) { | |
110 | unsigned long long kernel_seq; | |
111 | ||
112 | kernel_seq = udev_queue_get_kernel_seqnum(udev_queue); | |
113 | ||
114 | /* unless specified, the last event is the current kernel seqnum */ | |
115 | if (end == 0) | |
116 | end = udev_queue_get_kernel_seqnum(udev_queue); | |
117 | ||
118 | if (start > end) { | |
119 | err(udev, "seq-start larger than seq-end, ignoring\n"); | |
98d3d517 KS |
120 | start = 0; |
121 | end = 0; | |
122 | } | |
123 | ||
124 | if (start > kernel_seq || end > kernel_seq) { | |
125 | err(udev, "seq-start or seq-end larger than current kernel value, ignoring\n"); | |
98d3d517 KS |
126 | start = 0; |
127 | end = 0; | |
128 | } | |
129 | info(udev, "start=%llu end=%llu current=%llu\n", start, end, kernel_seq); | |
130 | } else { | |
131 | if (end > 0) { | |
132 | err(udev, "seq-end needs seq-start parameter, ignoring\n"); | |
98d3d517 KS |
133 | end = 0; |
134 | } | |
135 | } | |
136 | ||
bb38678e SJR |
137 | /* guarantee that the udev daemon isn't pre-processing */ |
138 | if (getuid() == 0) { | |
139 | struct udev_ctrl *uctrl; | |
140 | ||
5cc4112e | 141 | uctrl = udev_ctrl_new(udev); |
bb38678e | 142 | if (uctrl != NULL) { |
ff2c503d KS |
143 | if (udev_ctrl_send_ping(uctrl, timeout) < 0) { |
144 | info(udev, "no connection to daemon\n"); | |
145 | udev_ctrl_unref(uctrl); | |
ead7c62a | 146 | rc = EXIT_SUCCESS; |
ff2c503d KS |
147 | goto out; |
148 | } | |
bb38678e SJR |
149 | udev_ctrl_unref(uctrl); |
150 | } | |
151 | } | |
152 | ||
a3eca08b KS |
153 | pfd[0].events = POLLIN; |
154 | pfd[0].fd = inotify_init1(IN_CLOEXEC); | |
155 | if (pfd[0].fd < 0) { | |
156 | err(udev, "inotify_init failed: %m\n"); | |
157 | } else { | |
4b718be8 | 158 | if (inotify_add_watch(pfd[0].fd, udev_get_run_path(udev), IN_MOVED_TO) < 0) { |
a3eca08b KS |
159 | err(udev, "watching '%s' failed\n", udev_get_run_path(udev)); |
160 | close(pfd[0].fd); | |
161 | pfd[0].fd = -1; | |
162 | } | |
163 | } | |
164 | ||
88cbfb09 | 165 | for (;;) { |
97f48a8c KS |
166 | struct stat statbuf; |
167 | ||
168 | if (exists != NULL && stat(exists, &statbuf) == 0) { | |
ead7c62a | 169 | rc = EXIT_SUCCESS; |
97f48a8c KS |
170 | break; |
171 | } | |
172 | ||
98d3d517 | 173 | if (start > 0) { |
f503f6b2 | 174 | /* if asked for, wait for a specific sequence of events */ |
9f894a33 | 175 | if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) { |
ead7c62a | 176 | rc = EXIT_SUCCESS; |
f503f6b2 | 177 | break; |
9f894a33 | 178 | } |
f503f6b2 AJ |
179 | } else { |
180 | /* exit if queue is empty */ | |
9f894a33 | 181 | if (udev_queue_get_queue_is_empty(udev_queue)) { |
ead7c62a | 182 | rc = EXIT_SUCCESS; |
98d3d517 | 183 | break; |
9f894a33 | 184 | } |
98d3d517 | 185 | } |
f503f6b2 | 186 | |
a3eca08b | 187 | if (pfd[0].fd >= 0) { |
bd8e0e38 KS |
188 | int delay; |
189 | ||
bd8e0e38 KS |
190 | if (exists != NULL || start > 0) |
191 | delay = 100; | |
192 | else | |
193 | delay = 1000; | |
2661ff21 | 194 | /* wake up after delay, or immediately after the queue is rebuilt */ |
bd8e0e38 | 195 | if (poll(pfd, 1, delay) > 0 && pfd[0].revents & POLLIN) { |
a3eca08b KS |
196 | char buf[sizeof(struct inotify_event) + PATH_MAX]; |
197 | ||
198 | read(pfd[0].fd, buf, sizeof(buf)); | |
199 | } | |
200 | } else { | |
ead7c62a | 201 | sleep(1); |
a3eca08b | 202 | } |
a402404f | 203 | |
ead7c62a KS |
204 | if (timeout > 0) { |
205 | unsigned long long age_usec; | |
206 | ||
207 | age_usec = now_usec() - start_usec; | |
208 | if (age_usec / (1000 * 1000) >= timeout) { | |
209 | struct udev_list_entry *list_entry; | |
210 | ||
211 | if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) { | |
212 | info(udev, "timeout waiting for udev queue\n"); | |
213 | printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout); | |
214 | udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) | |
215 | printf(" %s (%s)\n", | |
216 | udev_list_entry_get_name(list_entry), | |
217 | udev_list_entry_get_value(list_entry)); | |
218 | } | |
219 | ||
220 | break; | |
221 | } | |
b76ad2e5 | 222 | } |
8249e04e | 223 | } |
ff2c503d | 224 | out: |
a3eca08b KS |
225 | if (pfd[0].fd >= 0) |
226 | close(pfd[0].fd); | |
8249e04e | 227 | udev_queue_unref(udev_queue); |
7baada47 KS |
228 | return rc; |
229 | } | |
1985c76e KS |
230 | |
231 | const struct udevadm_cmd udevadm_settle = { | |
232 | .name = "settle", | |
233 | .cmd = adm_settle, | |
234 | .help = "wait for the event queue to finish", | |
235 | }; |