chiark / gitweb /
Docs: udev.xml: Remove unnecessary repitition
[elogind.git] / udev / udevadm-settle.c
1 /*
2  * Copyright (C) 2006-2009 Kay Sievers <kay@vrfy.org>
3  * Copyright (C) 2009 Canonical Ltd.
4  * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
5  *
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.
10  *
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/>.
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>
29 #include <getopt.h>
30 #include <signal.h>
31 #include <time.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #include "udev.h"
36
37 #define DEFAULT_TIMEOUT                 180
38 #define LOOP_PER_SECOND                 20
39
40 static volatile sig_atomic_t is_timeout;
41
42 static void sig_handler(int signum)
43 {
44         switch (signum) {
45                 case SIGALRM:
46                         is_timeout = 1;
47                 case SIGUSR1:
48                         ;
49         }
50 }
51
52 int udevadm_settle(struct udev *udev, int argc, char *argv[])
53 {
54         static const struct option options[] = {
55                 { "seq-start", required_argument, NULL, 's' },
56                 { "seq-end", required_argument, NULL, 'e' },
57                 { "timeout", required_argument, NULL, 't' },
58                 { "exit-if-exists", required_argument, NULL, 'E' },
59                 { "quiet", no_argument, NULL, 'q' },
60                 { "help", no_argument, NULL, 'h' },
61                 {}
62         };
63         unsigned long long start = 0;
64         unsigned long long end = 0;
65         int quiet = 0;
66         const char *exists = NULL;
67         int timeout = DEFAULT_TIMEOUT;
68         struct sigaction act;
69         sigset_t mask;
70         struct udev_queue *udev_queue = NULL;
71         int rc = 1;
72
73         dbg(udev, "version %s\n", VERSION);
74
75         /* set signal handlers */
76         memset(&act, 0x00, sizeof(act));
77         act.sa_handler = sig_handler;
78         sigemptyset (&act.sa_mask);
79         act.sa_flags = 0;
80         sigaction(SIGALRM, &act, NULL);
81         sigaction(SIGUSR1, &act, NULL);
82         sigemptyset(&mask);
83         sigaddset(&mask, SIGUSR1);
84         sigaddset(&mask, SIGALRM);
85         sigprocmask(SIG_UNBLOCK, &mask, NULL);
86
87         for (;;) {
88                 int option;
89                 int seconds;
90
91                 option = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL);
92                 if (option == -1)
93                         break;
94
95                 switch (option) {
96                 case 's':
97                         start = strtoull(optarg, NULL, 0);
98                         break;
99                 case 'e':
100                         end = strtoull(optarg, NULL, 0);
101                         break;
102                 case 't':
103                         seconds = atoi(optarg);
104                         if (seconds >= 0)
105                                 timeout = seconds;
106                         else
107                                 fprintf(stderr, "invalid timeout value\n");
108                         dbg(udev, "timeout=%i\n", timeout);
109                         break;
110                 case 'q':
111                         quiet = 1;
112                         break;
113                 case 'E':
114                         exists = optarg;
115                         break;
116                 case 'h':
117                         printf("Usage: udevadm settle OPTIONS\n"
118                                "  --timeout=<seconds>     maximum time to wait for events\n"
119                                "  --seq-start=<seqnum>    first seqnum to wait for\n"
120                                "  --seq-end=<seqnum>      last seqnum to wait for\n"
121                                "  --exit-if-exists=<file> stop waiting if file exists\n"
122                                "  --quiet                 do not print list after timeout\n"
123                                "  --help\n\n");
124                         exit(0);
125                 }
126         }
127
128         if (timeout > 0)
129                 alarm(timeout);
130         else
131                 is_timeout = 1;
132
133         udev_queue = udev_queue_new(udev);
134         if (udev_queue == NULL)
135                 exit(2);
136
137         if (start > 0) {
138                 unsigned long long kernel_seq;
139
140                 kernel_seq = udev_queue_get_kernel_seqnum(udev_queue);
141
142                 /* unless specified, the last event is the current kernel seqnum */
143                 if (end == 0)
144                         end = udev_queue_get_kernel_seqnum(udev_queue);
145
146                 if (start > end) {
147                         err(udev, "seq-start larger than seq-end, ignoring\n");
148                         start = 0;
149                         end = 0;
150                 }
151
152                 if (start > kernel_seq || end > kernel_seq) {
153                         err(udev, "seq-start or seq-end larger than current kernel value, ignoring\n");
154                         start = 0;
155                         end = 0;
156                 }
157                 info(udev, "start=%llu end=%llu current=%llu\n", start, end, kernel_seq);
158         } else {
159                 if (end > 0) {
160                         err(udev, "seq-end needs seq-start parameter, ignoring\n");
161                         end = 0;
162                 }
163         }
164
165         /* guarantee that the udev daemon isn't pre-processing */
166         if (getuid() == 0) {
167                 struct udev_ctrl *uctrl;
168
169                 uctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
170                 if (uctrl != NULL) {
171                         sigset_t oldmask;
172
173                         sigemptyset(&mask);
174                         sigaddset(&mask, SIGUSR1);
175                         sigaddset(&mask, SIGALRM);
176                         sigprocmask(SIG_BLOCK, &mask, &oldmask);
177                         if (udev_ctrl_send_settle(uctrl) > 0)
178                                 sigsuspend(&oldmask);
179                         sigprocmask(SIG_SETMASK, &oldmask, NULL);
180                         udev_ctrl_unref(uctrl);
181                 }
182         }
183
184         for (;;) {
185                 struct stat statbuf;
186                 const struct timespec duration = { 0 , 1000 * 1000 * 1000 / LOOP_PER_SECOND };
187
188                 if (exists != NULL && stat(exists, &statbuf) == 0) {
189                         rc = 0;
190                         break;
191                 }
192
193                 if (start > 0) {
194                         /* if asked for, wait for a specific sequence of events */
195                         if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) {
196                                 rc = 0;
197                                 break;
198                         }
199                 } else {
200                         /* exit if queue is empty */
201                         if (udev_queue_get_queue_is_empty(udev_queue)) {
202                                 rc = 0;
203                                 break;
204                         }
205                 }
206
207                 if (is_timeout)
208                         break;
209
210                 nanosleep(&duration, NULL);
211         }
212
213         /* if we reached the timeout, print the list of remaining events */
214         if (is_timeout) {
215                 struct udev_list_entry *list_entry;
216
217                 if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) {
218                         info(udev, "timeout waiting for udev queue\n");
219                         printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout);
220                         udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue))
221                                 printf("  %s (%s)\n",
222                                        udev_list_entry_get_name(list_entry),
223                                        udev_list_entry_get_value(list_entry));
224                 }
225         }
226
227         udev_queue_unref(udev_queue);
228         return rc;
229 }