chiark / gitweb /
7b5ccfca2332a6a6dc5655e9da4383fa7232885e
[elogind.git] / extras / multipath-tools / multipathd / main.c
1 #include <string.h>
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <libdevmapper.h>
10 #include <syslog.h>
11 #include <signal.h>
12 #include <wait.h>
13
14 #include "devinfo.h"
15 #include "checkers.h"
16
17 #define CHECKINT 5
18 #define MAXPATHS 2048
19 #define FILENAMESIZE 256
20 #define MAPNAMESIZE 64
21 #define TARGETTYPESIZE 16
22 #define PARAMSSIZE 2048
23 #define MAXMAPS 512
24
25 #define MULTIPATH "/sbin/multipath"
26 #define PIDFILE "/var/run/multipathd.pid"
27
28 #ifndef DEBUG
29 #define DEBUG 1
30 #endif
31 #define LOG(x, y, z...) if (DEBUG >= x) syslog(x, y, ##z)
32
33 struct path
34 {
35         int major;
36         int minor;
37         char mapname[MAPNAMESIZE];
38         int (*checkfn) (char *);
39 };
40
41 struct paths
42 {
43         pthread_mutex_t *lock;
44         struct path *paths_h;
45 };
46
47 struct event_thread
48 {
49         pthread_t *thread;
50         pthread_mutex_t *waiter_lock;
51         char mapname[MAPNAMESIZE];
52 };
53
54 struct devmap
55 {
56         char mapname[MAPNAMESIZE];
57 };
58
59 /* global var */
60 pthread_mutex_t *event_lock;
61 pthread_cond_t *event;
62
63 int makenode (char *devnode, int major, int minor)
64 {
65         dev_t dev;
66         
67         dev = makedev (major, minor);
68         unlink (devnode);
69         
70         return mknod(devnode, S_IFBLK | S_IRUSR | S_IWUSR, dev);
71 }
72
73 int select_checkfn(struct path *path_p)
74 {
75         char devnode[FILENAMESIZE];
76         char vendor[8];
77         char product[16];
78         char rev[4];
79         int i, r;
80
81         /* default checkfn */
82         path_p->checkfn = &readsector0;
83         
84         sprintf (devnode, "/tmp/.select.%i.%i", path_p->major, path_p->minor);
85
86         r = makenode (devnode, path_p->major, path_p->minor);
87         
88         if (r < 0) {
89                 LOG(2, "[select_checkfn] can not make node %s", devnode);
90                 return r;
91         }
92
93         r = get_lun_strings(vendor, product, rev, devnode);
94
95         if (r) {
96                 LOG(2, "[select_checkfn] can not get strings");
97                 return r;
98         }
99
100         r = unlink (devnode);
101
102         if (r < 0) {
103                 LOG(2, "[select_checkfn] can not unlink %s", devnode);
104                 return r;
105         }
106
107         static struct {
108                 char * vendor;
109                 char * product;
110                 int (*checkfn) (char *);
111         } wlist[] = {
112                 {"COMPAQ  ", "HSV110 (C)COMPAQ", &tur},
113                 {"COMPAQ  ", "MSA1000         ", &tur},
114                 {"COMPAQ  ", "MSA1000 VOLUME  ", &tur},
115                 {"DEC     ", "HSG80           ", &tur},
116                 {"HP      ", "HSV100          ", &readsector0},
117                 {NULL, NULL, NULL},
118         };
119         
120         for (i = 0; wlist[i].vendor; i++) {
121                 if (strncmp(vendor, wlist[i].vendor, 8) == 0 &&
122                     strncmp(product, wlist[i].product, 16) == 0) {
123                         path_p->checkfn = wlist[i].checkfn;
124                 }
125         }
126
127         return 0;
128 }
129
130 int get_devmaps (struct devmap *devmaps)
131 {
132         struct devmap *devmaps_p;
133         struct dm_task *dmt, *dmt1;
134         struct dm_names *names = NULL;
135         unsigned next = 0;
136         void *nexttgt;
137         int r = 0;
138         long long start, length;
139         char *target_type = NULL;
140         char *params;
141
142         memset (devmaps, 0, MAXMAPS * sizeof (struct devmap));
143
144         if (!(dmt = dm_task_create(DM_DEVICE_LIST))) {
145                 r = 1;
146                 goto out;
147         }
148
149         if (!dm_task_run(dmt)) {
150                 r = 1;
151                 goto out;
152         }
153
154         if (!(names = dm_task_get_names(dmt))) {
155                 r = 1;
156                 goto out;
157         }
158
159         if (!names->dev) {
160                 LOG (1, "[get_devmaps] no devmap found");
161                 goto out;
162         }
163
164         devmaps_p = devmaps;
165
166         do {
167                 /* keep only multipath maps */
168
169                 names = (void *) names + next;
170                 nexttgt = NULL;
171                 LOG (3, "[get_devmaps] iterate on devmap names : %s", names->name);
172
173                 LOG (3, "[get_devmaps]  dm_task_create(DM_DEVICE_STATUS)");
174                 if (!(dmt1 = dm_task_create(DM_DEVICE_STATUS)))
175                         goto out1;
176                 
177                 LOG (3, "[get_devmaps]  dm_task_set_name(dmt1, names->name)");
178                 if (!dm_task_set_name(dmt1, names->name))
179                         goto out1;
180                 
181                 LOG (3, "[get_devmaps]  dm_task_run(dmt1)");
182                 if (!dm_task_run(dmt1))
183                         goto out1;
184                 LOG (3, "[get_devmaps]  DM_DEVICE_STATUS ioctl done");
185                 do {
186                         LOG (3, "[get_devmaps]   iterate on devmap's targets");
187                         nexttgt = dm_get_next_target(dmt1, nexttgt,
188                                                    &start,
189                                                    &length,
190                                                    &target_type,
191                                                    &params);
192
193
194                         LOG (3, "[get_devmaps]   test target_type existence");
195                         if (!target_type)
196                                 goto out1;
197                         
198                         LOG (3, "[get_devmaps]   test target_type is multipath");
199                         if (!strncmp (target_type, "multipath", 9)) {
200                                 strcpy (devmaps_p->mapname, names->name);
201                                 devmaps_p++;
202                                 
203                                 /* test vector overflow */
204                                 if (devmaps_p - devmaps >= MAXMAPS * sizeof (struct devmap)) {
205                                         LOG (1, "[get_devmaps] devmaps overflow");
206                                         dm_task_destroy(dmt1);
207                                         r = 1;
208                                         goto out;
209                                 }
210                         }
211
212                 } while (nexttgt);
213
214 out1:
215                 dm_task_destroy(dmt1);
216                 next = names->next;
217
218         } while (next);
219
220 out:
221         dm_task_destroy(dmt);
222
223         LOG (3, "[get_devmaps] done");
224         return r;
225 }
226
227 int checkpath (struct path *path_p)
228 {
229         char devnode[FILENAMESIZE];
230         int r;
231         
232         LOG (2, "[checkpath] checking path %i:%i", path_p->major, path_p->minor);
233         sprintf (devnode, "/tmp/.checker.%i.%i", path_p->major, path_p->minor);
234         
235         if (path_p->checkfn == NULL) {
236                 LOG (1, "[checkpath] test function not set for path %i:%i",
237                      path_p->major, path_p->minor);
238                 return 1;
239         }
240
241         r = makenode (devnode, path_p->major, path_p->minor);
242
243         if (r < 0) {
244                 LOG (2, "[checkpath] can not make node for %s", devnode);
245                 return r;
246         }
247
248         r = path_p->checkfn(devnode);
249         unlink (devnode);
250                                 
251         return r;
252 }
253                 
254 int updatepaths (struct devmap *devmaps, struct paths *failedpaths)
255 {
256         struct path *path_p;
257         struct devmap *devmaps_p;
258         void *next;
259         struct dm_task *dmt;
260         long long start, length;
261         char *target_type = NULL;
262         char *params, *p1, *p2;
263         char word[6];
264         int i;
265         
266         path_p = failedpaths->paths_h;
267         
268         pthread_mutex_lock (failedpaths->lock);
269         memset (failedpaths->paths_h, 0, MAXPATHS * sizeof (struct path));
270
271         /* first pass */
272         /* ask DM the failed path list */
273
274         devmaps_p = devmaps;
275
276         while (*devmaps_p->mapname != 0x0) {
277                 next = NULL;
278                 
279                 if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
280                         break;
281                 
282                 if (!dm_task_set_name(dmt, devmaps_p->mapname))
283                         goto out;
284                 
285                 if (!dm_task_run(dmt))
286                         goto out;
287
288                 do {
289                         next = dm_get_next_target(dmt, next, &start, &length,
290                                                    &target_type, &params);
291
292                         /* begin ugly parser */
293                         p1 = params;
294                         p2 = params;
295                         while (*p1) {
296                                 /* p2 lags at the begining of the word p1 parses */
297                                 while (*p1 != ' ') {
298                                         /* if the current word is a path */
299                                         if (*p1 == ':') {
300                                                 /* p1 jumps to path state */
301                                                 while (*p1 != 'A' && *p1 != 'F')
302                                                         p1++; 
303                                                 
304                                                 /* store path info */
305
306                                                 path_p->checkfn = NULL;
307
308                                                 i = 0;
309                                                 memset (&word, 'O', 6 * sizeof (char));
310                                                 while (*p2 != ':') {
311                                                         word[i++] = *p2;
312                                                         p2++;
313                                                 }
314                                                 path_p->major = atoi (word);
315                                                 
316                                                 p2++;
317                                                 i = 0;
318                                                 memset (&word, 'O', 6 * sizeof (char));
319                                                 while (*p2 != ' ') {
320                                                         word[i++] = *p2;
321                                                         p2++;
322                                                 }
323                                                 path_p->minor = atoi (word);
324
325                                                 strcpy (path_p->mapname, devmaps_p->mapname);
326
327                                                 /* 
328                                                  * discard active paths
329                                                  * don't trust the A status flag : double check
330                                                  */
331                                                 if (*p1 == 'A' &&
332                                                     !select_checkfn (path_p) &&
333                                                     checkpath (path_p)) {
334                                                         LOG(2, "[updatepaths] discard %i:%i as valid path",
335                                                             path_p->major, path_p->minor);
336                                                         p1++;
337                                                         memset (path_p, 0, sizeof(struct path));
338                                                         continue;
339                                                 }
340                                                 
341                                                 path_p++;
342
343                                                 /* test vector overflow */
344                                                 if (path_p - failedpaths->paths_h >= MAXPATHS * sizeof (struct path)) {
345                                                         LOG (1, "[updatepaths] path_h overflow");
346                                                         pthread_mutex_unlock (failedpaths->lock);
347                                                         return 1;
348                                                 }
349                                         }
350                                         p1++;
351                                 }
352                                 p2 = p1;
353                                 p1++;
354                         }
355                 } while (next);
356                         
357 out:
358                 dm_task_destroy(dmt);
359                 devmaps_p++;
360                 
361         }
362
363         pthread_mutex_unlock (failedpaths->lock);
364         return 0;
365 }
366
367 int geteventnr (char *name)
368 {
369         struct dm_task *dmt;
370         struct dm_info info;
371         
372         if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
373                 return 0;
374
375         if (!dm_task_set_name(dmt, name))
376                 goto out;
377
378         if (!dm_task_run(dmt))
379                 goto out;
380
381         if (!dm_task_get_info(dmt, &info))
382                 return 0;
383
384         if (!info.exists) {
385                 LOG(1, "Device %s does not exist", name);
386                 return 0;
387         }
388
389 out:
390         dm_task_destroy(dmt);
391
392         return info.event_nr;
393 }
394
395 void *waitevent (void * et)
396 {
397         int event_nr;
398         struct event_thread *waiter;
399
400         waiter = (struct event_thread *)et;
401         pthread_mutex_lock (waiter->waiter_lock);
402
403         event_nr = geteventnr (waiter->mapname);
404
405         struct dm_task *dmt;
406
407         if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
408                 return 0;
409
410         if (!dm_task_set_name(dmt, waiter->mapname))
411                 goto out;
412
413         if (event_nr && !dm_task_set_event_nr(dmt, event_nr))
414                 goto out;
415
416         dm_task_run(dmt);
417
418 out:
419         dm_task_destroy(dmt);
420
421         /* tell waiterloop we have an event */
422         pthread_mutex_lock (event_lock);
423         pthread_cond_signal(event);
424         pthread_mutex_unlock (event_lock);
425         
426         /* release waiter_lock so that waiterloop knows we are gone */
427         pthread_mutex_unlock (waiter->waiter_lock);
428         pthread_exit(waiter->thread);
429
430         return (NULL);
431 }
432
433 void *waiterloop (void *ap)
434 {
435         struct paths *failedpaths;
436         struct devmap *devmaps, *devmaps_p;
437         struct event_thread *waiters, *waiters_p;
438         int r;
439
440         /* inits */
441         failedpaths = (struct paths *)ap;
442         devmaps = malloc (MAXMAPS * sizeof (struct devmap));
443         waiters = malloc (MAXMAPS * sizeof (struct event_thread));
444         memset (waiters, 0, MAXMAPS * sizeof (struct event_thread));
445
446         while (1) {
447                 
448                 /* update devmap list */
449                 LOG (1, "[event thread] refresh devmaps list");
450                 get_devmaps (devmaps);
451
452                 /* update failed paths list */
453                 LOG (1, "[event thread] refresh failpaths list");
454                 updatepaths (devmaps, failedpaths);
455                 
456                 /* start waiters on all devmaps */
457                 LOG (1, "[event thread] start up event loops");
458                 waiters_p = waiters;
459                 devmaps_p = devmaps;
460
461                 while (*devmaps_p->mapname != 0x0) {
462                         
463                         /* find out if devmap already has a running waiter thread */
464                         while (*waiters_p->mapname != 0x0) {
465                                 if (!strcmp (waiters_p->mapname, devmaps_p->mapname))
466                                         break;
467                                 waiters_p++;
468                         }
469                                         
470                         /* no event_thread struct : init it */
471                         if (*waiters_p->mapname == 0x0) {
472                                 strcpy (waiters_p->mapname, devmaps_p->mapname);
473                                 waiters_p->thread = malloc (sizeof (pthread_t));
474                                 waiters_p->waiter_lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
475                                 pthread_mutex_init (waiters_p->waiter_lock, NULL);
476                         }
477                         
478                         /* event_thread struct found */
479                         if (*waiters_p->mapname != 0x0) {
480                                 r = pthread_mutex_trylock (waiters_p->waiter_lock);
481                                 /* thread already running : out */
482
483                                 if (r)
484                                         goto out;
485                                 
486                                 pthread_mutex_unlock (waiters_p->waiter_lock);
487                         }
488                         
489                         LOG (1, "[event thread] create event thread for %s", waiters_p->mapname);
490                         pthread_create (waiters_p->thread, NULL, waitevent, waiters_p);
491 out:
492                         waiters_p = waiters;
493                         devmaps_p++;
494                 }
495
496                 /* wait event condition */
497                 pthread_mutex_lock (event_lock);
498                 pthread_cond_wait(event, event_lock);
499                 pthread_mutex_unlock (event_lock);
500
501                 LOG (1, "[event thread] event caught");
502         }
503
504         return (NULL);
505 }
506
507 void *checkerloop (void *ap)
508 {
509         struct paths *failedpaths;
510         struct path *path_p;
511         char *cmdargs[4] = {MULTIPATH, "-D", NULL, NULL};
512         char major[5];
513         char minor[5];
514         int status;
515
516         failedpaths = (struct paths *)ap;
517
518         LOG (1, "[checker thread] path checkers start up");
519
520         while (1) {
521                 path_p = failedpaths->paths_h;
522                 pthread_mutex_lock (failedpaths->lock);
523                 LOG (2, "[checker thread] checking paths");
524                 while (path_p->major != 0) {
525                         
526                         if (checkpath (path_p)) {
527                                 LOG (1, "[checker thread] reconfigure %s\n", path_p->mapname);
528                                 snprintf (major, 5, "%i", path_p->major);
529                                 snprintf (minor, 5, "%i", path_p->minor);
530                                 cmdargs[2] = major;
531                                 cmdargs[3] = minor;
532                                 if (fork () == 0)
533                                         execve (cmdargs[0], cmdargs, NULL);
534
535                                 wait (&status);
536                                 /* MULTIPATH will ask for failedpaths refresh (SIGHUP) */
537                         }
538                         
539                         path_p++;
540                         
541                         /* test vector overflow */
542                         if (path_p - failedpaths->paths_h >= MAXPATHS * sizeof (struct path)) {
543                                 LOG (1, "[checker thread] path_h overflow");
544                                 pthread_mutex_unlock (failedpaths->lock);
545                                 return (NULL);
546                         }
547                 }
548                 pthread_mutex_unlock (failedpaths->lock);
549                 sleep(CHECKINT);
550         }
551
552         return (NULL);
553 }
554
555 struct paths *initpaths (void)
556 {
557         struct paths *failedpaths;
558
559         failedpaths = malloc (sizeof (struct paths));
560         failedpaths->paths_h = malloc (MAXPATHS * sizeof (struct path));
561         failedpaths->lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
562         pthread_mutex_init (failedpaths->lock, NULL);
563         event = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
564         pthread_cond_init (event, NULL);
565         event_lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
566         pthread_mutex_init (event_lock, NULL);
567         
568         return (failedpaths);
569 }
570
571 void pidfile (pid_t pid)
572 {
573         FILE *file;
574         struct stat *buf;
575
576         buf = malloc (sizeof (struct stat));
577
578         if (!stat (PIDFILE, buf)) {
579                 LOG(1, "[master thread] already running : out");
580                 free (buf);
581                 exit (1);
582         }
583                 
584         umask (022);
585         pid = setsid ();
586
587         if (pid < -1) {
588                 LOG(1, "[master thread] setsid() error");
589                 exit (1);
590         }
591         
592         file = fopen (PIDFILE, "w");
593         fprintf (file, "%d\n", pid);
594         fclose (file);
595         free (buf);
596 }
597
598 void *
599 signal_set(int signo, void (*func) (int))
600 {
601         int r;
602         struct sigaction sig;
603         struct sigaction osig;
604
605         sig.sa_handler = func;
606         sigemptyset(&sig.sa_mask);
607         sig.sa_flags = 0;
608
609         r = sigaction(signo, &sig, &osig);
610
611         if (r < 0)
612                 return (SIG_ERR);
613         else
614                 return (osig.sa_handler);
615 }
616
617 void sighup (int sig)
618 {
619         LOG (1, "[master thread] SIGHUP caught : refresh devmap list");
620
621         /* ask for failedpaths refresh */
622         pthread_mutex_lock (event_lock);
623         pthread_cond_signal(event);
624         pthread_mutex_unlock (event_lock);
625 }
626
627 void sigend (int sig)
628 {
629         LOG (1, "[master thread] unlink pidfile");
630         unlink (PIDFILE);
631         LOG (1, "[master thread] --------shut down-------");
632         exit (0);
633 }
634
635 void signal_init(void)
636 {
637         signal_set(SIGHUP, sighup);
638         signal_set(SIGINT, sigend);
639         signal_set(SIGTERM, sigend);
640         signal_set(SIGKILL, sigend);
641 }
642
643 int main (int argc, char *argv[])
644 {
645         pthread_t wait, check;
646         struct paths *failedpaths;
647         pid_t pid;
648
649         pid = fork ();
650
651         /* can't fork */
652         if (pid < 0)
653                 exit (1);
654
655         /* let the parent die happy */
656         if (pid > 0)
657                 exit (0);
658         
659         /* child's play */
660         openlog (argv[0], 0, LOG_DAEMON);
661         LOG (1, "[master thread] --------start up--------");
662
663         pidfile (pid);
664         signal_init ();
665
666         failedpaths = initpaths ();
667         
668         pthread_create (&wait, NULL, waiterloop, failedpaths);
669         pthread_create (&check, NULL, checkerloop, failedpaths);
670         pthread_join (wait, NULL);
671         pthread_join (check, NULL);
672
673         return 0;
674 }