chiark / gitweb /
f884337779b929df8aa9cc331b79007158be66da
[elogind.git] / udev / udevadm-monitor.c
1 /*
2  * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@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 <unistd.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <signal.h>
27 #include <getopt.h>
28 #include <sys/time.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/select.h>
32 #include <linux/types.h>
33 #include <linux/netlink.h>
34
35 #include "udev.h"
36
37 static int uevent_netlink_sock = -1;
38 static int udev_exit;
39
40 static int init_uevent_netlink_sock(void)
41 {
42         struct sockaddr_nl snl;
43         int err;
44
45         memset(&snl, 0x00, sizeof(struct sockaddr_nl));
46         snl.nl_family = AF_NETLINK;
47         snl.nl_pid = getpid();
48         snl.nl_groups = 1;
49
50         uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
51         if (uevent_netlink_sock == -1) {
52                 fprintf(stderr, "error getting socket: %s\n", strerror(errno));
53                 return -1;
54         }
55
56         err = bind(uevent_netlink_sock, (struct sockaddr *) &snl,
57                       sizeof(struct sockaddr_nl));
58         if (err < 0) {
59                 fprintf(stderr, "bind failed: %s\n", strerror(errno));
60                 close(uevent_netlink_sock);
61                 uevent_netlink_sock = -1;
62                 return -1;
63         }
64
65         return 0;
66 }
67
68 static void asmlinkage sig_handler(int signum)
69 {
70         if (signum == SIGINT || signum == SIGTERM)
71                 udev_exit = 1;
72 }
73
74 static const char *search_key(const char *searchkey, const char *buf, size_t buflen)
75 {
76         size_t bufpos = 0;
77         size_t searchkeylen = strlen(searchkey);
78
79         while (bufpos < buflen) {
80                 const char *key;
81                 int keylen;
82
83                 key = &buf[bufpos];
84                 keylen = strlen(key);
85                 if (keylen == 0)
86                         break;
87                  if ((strncmp(searchkey, key, searchkeylen) == 0) && key[searchkeylen] == '=')
88                         return &key[searchkeylen + 1];
89                 bufpos += keylen + 1;
90         }
91         return NULL;
92 }
93
94 int udevadm_monitor(struct udev *udev, int argc, char *argv[])
95 {
96         struct sigaction act;
97         int option;
98         int env = 0;
99         int print_kernel = 0;
100         int print_udev = 0;
101         struct udev_monitor *udev_monitor = NULL;
102         fd_set readfds;
103         int rc = 0;
104
105         static const struct option options[] = {
106                 { "environment", 0, NULL, 'e' },
107                 { "kernel", 0, NULL, 'k' },
108                 { "udev", 0, NULL, 'u' },
109                 { "help", 0, NULL, 'h' },
110                 {}
111         };
112
113         while (1) {
114                 option = getopt_long(argc, argv, "ekuh", options, NULL);
115                 if (option == -1)
116                         break;
117
118                 switch (option) {
119                 case 'e':
120                         env = 1;
121                         break;
122                 case 'k':
123                         print_kernel = 1;
124                         break;
125                 case 'u':
126                         print_udev = 1;
127                         break;
128                 case 'h':
129                         printf("Usage: udevadm monitor [--environment] [--kernel] [--udev] [--help]\n"
130                                "  --env    print the whole event environment\n"
131                                "  --kernel print kernel uevents\n"
132                                "  --udev   print udev events\n"
133                                "  --help   print this help text\n\n");
134                 default:
135                         goto out;
136                 }
137         }
138
139         if (!print_kernel && !print_udev) {
140                 print_kernel = 1;
141                 print_udev =1;
142         }
143
144         if (getuid() != 0 && print_kernel) {
145                 fprintf(stderr, "root privileges needed to subscribe to kernel events\n");
146                 goto out;
147         }
148
149         /* set signal handlers */
150         memset(&act, 0x00, sizeof(struct sigaction));
151         act.sa_handler = (void (*)(int)) sig_handler;
152         sigemptyset(&act.sa_mask);
153         act.sa_flags = SA_RESTART;
154         sigaction(SIGINT, &act, NULL);
155         sigaction(SIGTERM, &act, NULL);
156
157         printf("monitor will print the received events for:\n");
158         if (print_udev) {
159                 udev_monitor = udev_monitor_new_from_socket(udev, "@/org/kernel/udev/monitor");
160                 if (udev_monitor == NULL) {
161                         rc = 1;
162                         goto out;
163                 }
164                 if (udev_monitor_enable_receiving(udev_monitor) < 0) {
165                         rc = 2;
166                         goto out;
167                 }
168                 printf("UDEV the event which udev sends out after rule processing\n");
169         }
170         if (print_kernel) {
171                 if (init_uevent_netlink_sock() < 0) {
172                         rc = 3;
173                         goto out;
174                 }
175                 printf("UEVENT the kernel uevent\n");
176         }
177         printf("\n");
178
179         while (!udev_exit) {
180                 char buf[UEVENT_BUFFER_SIZE*2];
181                 ssize_t buflen;
182                 ssize_t bufpos;
183                 ssize_t keys;
184                 int fdcount;
185                 struct timeval tv;
186                 struct timezone tz;
187                 char timestr[64];
188                 const char *source = NULL;
189                 const char *devpath, *action, *subsys;
190
191                 buflen = 0;
192                 FD_ZERO(&readfds);
193                 if (uevent_netlink_sock >= 0)
194                         FD_SET(uevent_netlink_sock, &readfds);
195                 if (udev_monitor != NULL)
196                         FD_SET(udev_monitor_get_fd(udev_monitor), &readfds);
197
198                 fdcount = select(UDEV_MAX(uevent_netlink_sock, udev_monitor_get_fd(udev_monitor))+1,
199                                  &readfds, NULL, NULL, NULL);
200                 if (fdcount < 0) {
201                         if (errno != EINTR)
202                                 fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno));
203                         continue;
204                 }
205
206                 if (gettimeofday(&tv, &tz) == 0) {
207                         snprintf(timestr, sizeof(timestr), "%llu.%06u",
208                                  (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec);
209                 } else
210                         timestr[0] = '\0';
211
212                 if ((uevent_netlink_sock >= 0) && FD_ISSET(uevent_netlink_sock, &readfds)) {
213                         buflen = recv(uevent_netlink_sock, &buf, sizeof(buf), 0);
214                         if (buflen <= 0) {
215                                 fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno));
216                                 continue;
217                         }
218                         source = "UEVENT";
219                 }
220
221                 if ((udev_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(udev_monitor), &readfds)) {
222                         buflen = recv(udev_monitor_get_fd(udev_monitor), &buf, sizeof(buf), 0);
223                         if (buflen <= 0) {
224                                 fprintf(stderr, "error receiving udev message: %s\n", strerror(errno));
225                                 continue;
226                         }
227                         source = "UDEV  ";
228                 }
229
230                 if (buflen == 0)
231                         continue;
232
233                 keys = strlen(buf) + 1; /* start of payload */
234                 devpath = search_key("DEVPATH", &buf[keys], buflen);
235                 action = search_key("ACTION", &buf[keys], buflen);
236                 subsys = search_key("SUBSYSTEM", &buf[keys], buflen);
237                 printf("%s[%s] %-8s %s (%s)\n", source, timestr, action, devpath, subsys);
238
239                 /* print environment */
240                 bufpos = keys;
241                 if (env) {
242                         while (bufpos < buflen) {
243                                 int keylen;
244                                 char *key;
245
246                                 key = &buf[bufpos];
247                                 keylen = strlen(key);
248                                 if (keylen == 0)
249                                         break;
250                                 printf("%s\n", key);
251                                 bufpos += keylen + 1;
252                         }
253                         printf("\n");
254                 }
255         }
256
257 out:
258         udev_monitor_unref(udev_monitor);
259         if (uevent_netlink_sock >= 0)
260                 close(uevent_netlink_sock);
261
262         return rc;
263 }