chiark / gitweb /
udevinitsend: handle replay messages correctly
[elogind.git] / udevinitsend.c
1 /*
2  * udevinitsend.c
3  *
4  * Userspace devfs
5  *
6  * Copyright (C) 2004, 2005 Hannes Reinecke <hare@suse.de>
7  *
8  *      This program is free software; you can redistribute it and/or modify it
9  *      under the terms of the GNU General Public License as published by the
10  *      Free Software Foundation version 2 of the License.
11  * 
12  *      This program is distributed in the hope that it will be useful, but
13  *      WITHOUT ANY WARRANTY; without even the implied warranty of
14  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *      General Public License for more details.
16  * 
17  *      You should have received a copy of the GNU General Public License along
18  *      with this program; if not, write to the Free Software Foundation, Inc.,
19  *      675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/mman.h>
33 #include <sys/socket.h>
34 #include <sys/wait.h>
35 #include <sys/un.h>
36 #include <dirent.h>
37
38 #include "udev.h"
39 #include "udev_version.h"
40 #include "udevd.h"
41 #include "logging.h"
42
43
44 #ifdef USE_LOG
45 void log_message (int level, const char *format, ...)
46 {
47         va_list args;
48
49         va_start(args, format);
50         vsyslog(level, format, args);
51         va_end(args);
52 }
53 #endif
54
55 /*
56  * udevsend
57  *
58  * Scan a file, write all variables into the msgbuf and
59  * fires the message to udevd.
60  */
61 static int udevsend(char *filename, int sock, int disable_loop_detection)
62 {
63         static struct udevd_msg usend_msg;
64         int usend_msg_len;
65         int bufpos = 0;
66         struct stat statbuf;
67         int fd;
68         char *fdmap, *ls, *le, *ch;
69         struct sockaddr_un saddr;
70         socklen_t addrlen;
71         int retval = 0;
72
73         if (stat(filename,&statbuf) < 0) {
74                 dbg("cannot stat %s: %s\n", filename, strerror(errno));
75                 return 1;
76         }
77         fd = open(filename,O_RDONLY);
78         if (fd < 0)
79                 return 1;
80
81         fdmap = mmap(0, statbuf.st_size,
82                      PROT_READ, MAP_PRIVATE, fd, 0);
83         close(fd);
84         if (fdmap == MAP_FAILED) {
85                 dbg("mmap failed, errno %d\n", errno);
86                 return 1;
87         }
88
89         memset(&saddr, 0x00, sizeof(struct sockaddr_un));
90         saddr.sun_family = AF_LOCAL;
91         /* use abstract namespace for socket path */
92         strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH);
93         addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
94
95         memset(&usend_msg, 0x00, sizeof(struct udevd_msg));
96         strcpy(usend_msg.magic, UDEV_MAGIC);
97         usend_msg.type = UDEVD_INITSEND;
98
99         ls = fdmap;
100         ch = le = ls;
101         while (*ch && ch < fdmap + statbuf.st_size) {
102                 le = strchr(ch, '\n');
103                 if (!le)
104                         break;
105                 ch = strchr(ch, '=');
106                 if (!ch)
107                         break;
108
109                 /* prevent loops in the scripts we execute */
110                 if (strncmp(ls, "UDEVD_EVENT=", 12) == 0) {
111                         if (!disable_loop_detection) {
112                                 dbg("event already handled by udev\n");
113                                 retval = -1;
114                                 break;
115                         } 
116                         goto loop_end;
117                 }
118
119                 /* omit shell-generated keys */
120                 if (ls[0] == '_' && ls[1] == '=') {
121                         goto loop_end;
122                 }
123
124                 if (ch < le) {
125
126                         strncpy(&usend_msg.envbuf[bufpos],ls,(ch - ls) + 1);
127                         bufpos += (ch - ls) + 1;
128                         if (ch[1] == '\'' && le[-1] == '\'') {
129                                 strncpy(&usend_msg.envbuf[bufpos],ch + 2, (le - ch) -3);
130                                 bufpos += (le - ch) - 3;
131                         } else {
132                                 strncpy(&usend_msg.envbuf[bufpos],ch, (le - ch));
133                                 bufpos += (le - ch);
134                         }
135                         bufpos++;
136                 }
137 loop_end:
138                 ch = le + 1;
139                 ls = ch;
140         }
141         munmap(fdmap, statbuf.st_size);
142
143         usend_msg_len = offsetof(struct udevd_msg, envbuf) + bufpos;
144         dbg("usend_msg_len=%i", usend_msg_len);
145
146         if (!retval) {
147                 retval = sendto(sock, &usend_msg, usend_msg_len, 0, (struct sockaddr *)&saddr, addrlen);
148                 if (retval < 0) {
149                         dbg("error sending message (%s)", strerror(errno));
150                 }
151         }
152                 
153         return retval;
154 }
155
156 int main(int argc, char *argv[], char *envp[])
157 {
158         static const char short_options[] = "d:f:lVh";
159         int option;
160         char *event_dir = NULL;
161         char *event_file = NULL;
162         DIR *dirstream;
163         struct dirent *direntry;
164         int retval = 1;
165         int disable_loop_detection = 0;
166         int sock;
167
168         logging_init("udevinitsend");
169         dbg("version %s", UDEV_VERSION);
170
171         /* get command line options */
172         while (1) {
173                 option = getopt(argc, argv, short_options);
174                 if (option == -1)
175                         break;
176
177                 dbg("option '%c': ", option);
178                 switch (option) {
179                 case 'd':
180                         dbg("scan directory %s\n", optarg);
181                         event_dir = optarg;
182                         break;
183
184                 case 'f':
185                         dbg("use event file %s\n", optarg);
186                         event_file = optarg;
187                         break;
188
189                 case 'l':
190                         dbg("disable loop detection, ignore UDEVD_EVENT\n");
191                         disable_loop_detection = 1;
192                         break;
193
194                 case 'h':
195                         retval = 0;
196                 }
197         }
198
199         sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
200         if (sock == -1) {
201                 dbg("error getting socket");
202                 return 1;
203         }
204
205         if (event_dir) {
206                 dirstream = opendir(event_dir);
207                 if (!dirstream) {
208                         info("error opening directory %s: %s\n",
209                              event_dir, strerror(errno));
210                         return 1;
211                 }
212                 chdir(event_dir);
213                 while ((direntry = readdir(dirstream)) != NULL) {
214                         if (!strcmp(direntry->d_name,".") ||
215                             !strcmp(direntry->d_name,".."))
216                                 continue;
217                         retval = udevsend(direntry->d_name, sock, disable_loop_detection);
218                 }
219                 closedir(dirstream);
220         } else if (event_file) {
221                 retval = udevsend(event_file, sock, disable_loop_detection);
222         }
223
224         if (sock != -1)
225                 close(sock);
226
227         return retval;
228 }