[libmultipath] more path: and multipath: output prefixing
[platform/upstream/multipath-tools.git] / libmultipath / uevent.c
1 /*
2  * uevent.c - trigger upon netlink uevents from the kernel
3  *
4  *      Only kernels from version 2.6.10* on provide the uevent netlink socket.
5  *      Until the libc-kernel-headers are updated, you need to compile with:
6  *
7  *        gcc -I /lib/modules/`uname -r`/build/include -o uevent_listen uevent_listen.c
8  *
9  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
10  *
11  *      This program is free software; you can redistribute it and/or modify it
12  *      under the terms of the GNU General Public License as published by the
13  *      Free Software Foundation version 2 of the License.
14  *
15  *      This program is distributed in the hope that it will be useful, but
16  *      WITHOUT ANY WARRANTY; without even the implied warranty of
17  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *      General Public License for more details.
19  *
20  *      You should have received a copy of the GNU General Public License along
21  *      with this program; if not, write to the Free Software Foundation, Inc.,
22  *      675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <time.h>
32 #include <sys/socket.h>
33 #include <sys/user.h>
34 #include <asm/types.h>
35 #include <linux/netlink.h>
36 #include <pthread.h>
37 #include <sys/mman.h>
38
39 #include "memory.h"
40 #include "debug.h"
41 #include "uevent.h"
42
43 typedef int (uev_trigger)(struct uevent *, void * trigger_data);
44
45 pthread_t uevq_thr;
46 struct uevent *uevqhp, *uevqtp;
47 pthread_mutex_t uevq_lock, *uevq_lockp = &uevq_lock;
48 pthread_mutex_t uevc_lock, *uevc_lockp = &uevc_lock;
49 pthread_cond_t  uev_cond,  *uev_condp  = &uev_cond;
50 uev_trigger *my_uev_trigger;
51 void * my_trigger_data;
52
53 struct uevent * alloc_uevent (void)
54 {
55         return (struct uevent *)MALLOC(sizeof(struct uevent));
56 }
57
58 void
59 service_uevq(void)
60 {
61         int empty;
62         struct uevent *uev;
63
64         do {
65                 pthread_mutex_lock(uevq_lockp);
66                 empty = (uevqhp == NULL);
67                 if (!empty) {
68                         uev = uevqhp;
69                         uevqhp = uev->next;
70                         if (uevqtp == uev)
71                                 uevqtp = uev->next;
72                         pthread_mutex_unlock(uevq_lockp);
73
74                         if (my_uev_trigger && my_uev_trigger(uev,
75                                                         my_trigger_data))
76                                 condlog(0, "uevent trigger error");
77
78                         FREE(uev);
79                 }
80                 else {
81                         pthread_mutex_unlock(uevq_lockp);
82                 }
83         } while (empty == 0);
84 }
85
86 /*
87  * Service the uevent queue.
88  */
89 static void *
90 uevq_thread(void * et)
91 {
92         mlockall(MCL_CURRENT | MCL_FUTURE);
93
94         while (1) {
95                 pthread_mutex_lock(uevc_lockp);
96                 pthread_cond_wait(uev_condp, uevc_lockp);
97                 pthread_mutex_unlock(uevc_lockp);
98
99                 service_uevq();
100         }
101 }
102
103 int uevent_listen(int (*uev_trigger)(struct uevent *, void * trigger_data),
104                   void * trigger_data)
105 {
106         int sock;
107         struct sockaddr_nl snl;
108         int retval;
109         int rcvbufsz = 128*1024;
110         int rcvsz = 0;
111         int rcvszsz = sizeof(rcvsz);
112         unsigned int *prcvszsz = (unsigned int *)&rcvszsz;
113         pthread_attr_t attr;
114
115         my_uev_trigger = uev_trigger;
116         my_trigger_data = trigger_data;
117
118         /*
119          * Queue uevents for service by dedicated thread so that the uevent
120          * listening thread does not block on multipathd locks (vecs->lock)
121          * thereby not getting to empty the socket's receive buffer queue
122          * often enough.
123          */
124         uevqhp = uevqtp = NULL;
125
126         pthread_mutex_init(uevq_lockp, NULL);
127         pthread_mutex_init(uevc_lockp, NULL);
128         pthread_cond_init(uev_condp, NULL);
129
130         pthread_attr_init(&attr);
131         pthread_attr_setstacksize(&attr, 64 * 1024);
132         pthread_create(&uevq_thr, &attr, uevq_thread, NULL);
133
134         memset(&snl, 0x00, sizeof(struct sockaddr_nl));
135         snl.nl_family = AF_NETLINK;
136         snl.nl_pid = getpid();
137         snl.nl_groups = 0xffffffff;
138
139         sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
140         if (sock == -1) {
141                 condlog(0, "error getting socket, exit");
142                 return 1;
143         }
144
145         /*
146          * try to avoid dropping uevents, even so, this is not a guarantee,
147          * but it does help to change the netlink uevent socket's
148          * receive buffer threshold from the default value of 106,496 to
149          * the maximum value of 262,142.
150          */
151         retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz,
152                             sizeof(rcvbufsz));
153
154         if (retval < 0) {
155                 condlog(0, "error setting receive buffer size for socket, exit");
156                 exit(1);
157         }
158         retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz);
159
160         if (retval < 0) {
161                 condlog(0, "error setting receive buffer size for socket, exit");
162                 exit(1);
163         }
164         condlog(3, "receive buffer size for socket is %u.", rcvsz);
165
166         retval = bind(sock, (struct sockaddr *) &snl,
167                       sizeof(struct sockaddr_nl));
168         if (retval < 0) {
169                 condlog(0, "bind failed, exit");
170                 goto exit;
171         }
172
173         while (1) {
174                 static char buff[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE];
175                 int i;
176                 char *pos;
177                 size_t bufpos;
178                 ssize_t buflen;
179                 struct uevent *uev;
180                 char *buffer;
181
182                 buflen = recv(sock, &buff, sizeof(buff), 0);
183                 if (buflen <  0) {
184                         condlog(0, "error receiving message");
185                         continue;
186                 }
187
188                 if ((size_t)buflen > sizeof(buff)-1)
189                         buflen = sizeof(buff)-1;
190
191                 uev = alloc_uevent();
192
193                 if (!uev) {
194                         condlog(1, "lost uevent, oom");
195                         continue;
196                 }
197
198                 /*
199                  * Copy the shared receive buffer contents to buffer private
200                  * to this uevent so we can immediately reuse the shared buffer.
201                  */
202                 memcpy(uev->buffer, buff, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE);
203                 buffer = uev->buffer;
204                 buffer[buflen] = '\0';
205
206                 /* save start of payload */
207                 bufpos = strlen(buffer) + 1;
208
209                 /* action string */
210                 uev->action = buffer;
211                 pos = strchr(buffer, '@');
212                 if (!pos)
213                         continue;
214                 pos[0] = '\0';
215
216                 /* sysfs path */
217                 uev->devpath = &pos[1];
218
219                 /* hotplug events have the environment attached - reconstruct envp[] */
220                 for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) {
221                         int keylen;
222                         char *key;
223
224                         key = &buffer[bufpos];
225                         keylen = strlen(key);
226                         uev->envp[i] = key;
227                         bufpos += keylen + 1;
228                 }
229                 uev->envp[i] = NULL;
230
231                 condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath);
232
233                 /* print payload environment */
234                 for (i = 0; uev->envp[i] != NULL; i++)
235                         condlog(3, "%s", uev->envp[i]);
236
237                 /*
238                  * Queue uevent and poke service pthread.
239                  */
240                 pthread_mutex_lock(uevq_lockp);
241                 if (uevqtp)
242                         uevqtp->next = uev;
243                 else
244                         uevqhp = uev;
245                 uevqtp = uev;
246                 uev->next = NULL;
247                 pthread_mutex_unlock(uevq_lockp);
248
249                 pthread_mutex_lock(uevc_lockp);
250                 pthread_cond_signal(uev_condp);
251                 pthread_mutex_unlock(uevc_lockp);
252         }
253
254 exit:
255         close(sock);
256
257         pthread_mutex_lock(uevq_lockp);
258         pthread_cancel(uevq_thr);
259         pthread_mutex_unlock(uevq_lockp);
260
261         pthread_mutex_destroy(uevq_lockp);
262         pthread_mutex_destroy(uevc_lockp);
263         pthread_cond_destroy(uev_condp);
264
265         return 1;
266 }