diff options
Diffstat (limited to 'extras/multipath-tools/multipathd/main.c')
-rw-r--r-- | extras/multipath-tools/multipathd/main.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/extras/multipath-tools/multipathd/main.c b/extras/multipath-tools/multipathd/main.c new file mode 100644 index 0000000000..7b5ccfca23 --- /dev/null +++ b/extras/multipath-tools/multipathd/main.c @@ -0,0 +1,674 @@ +#include <string.h> +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <libdevmapper.h> +#include <syslog.h> +#include <signal.h> +#include <wait.h> + +#include "devinfo.h" +#include "checkers.h" + +#define CHECKINT 5 +#define MAXPATHS 2048 +#define FILENAMESIZE 256 +#define MAPNAMESIZE 64 +#define TARGETTYPESIZE 16 +#define PARAMSSIZE 2048 +#define MAXMAPS 512 + +#define MULTIPATH "/sbin/multipath" +#define PIDFILE "/var/run/multipathd.pid" + +#ifndef DEBUG +#define DEBUG 1 +#endif +#define LOG(x, y, z...) if (DEBUG >= x) syslog(x, y, ##z) + +struct path +{ + int major; + int minor; + char mapname[MAPNAMESIZE]; + int (*checkfn) (char *); +}; + +struct paths +{ + pthread_mutex_t *lock; + struct path *paths_h; +}; + +struct event_thread +{ + pthread_t *thread; + pthread_mutex_t *waiter_lock; + char mapname[MAPNAMESIZE]; +}; + +struct devmap +{ + char mapname[MAPNAMESIZE]; +}; + +/* global var */ +pthread_mutex_t *event_lock; +pthread_cond_t *event; + +int makenode (char *devnode, int major, int minor) +{ + dev_t dev; + + dev = makedev (major, minor); + unlink (devnode); + + return mknod(devnode, S_IFBLK | S_IRUSR | S_IWUSR, dev); +} + +int select_checkfn(struct path *path_p) +{ + char devnode[FILENAMESIZE]; + char vendor[8]; + char product[16]; + char rev[4]; + int i, r; + + /* default checkfn */ + path_p->checkfn = &readsector0; + + sprintf (devnode, "/tmp/.select.%i.%i", path_p->major, path_p->minor); + + r = makenode (devnode, path_p->major, path_p->minor); + + if (r < 0) { + LOG(2, "[select_checkfn] can not make node %s", devnode); + return r; + } + + r = get_lun_strings(vendor, product, rev, devnode); + + if (r) { + LOG(2, "[select_checkfn] can not get strings"); + return r; + } + + r = unlink (devnode); + + if (r < 0) { + LOG(2, "[select_checkfn] can not unlink %s", devnode); + return r; + } + + static struct { + char * vendor; + char * product; + int (*checkfn) (char *); + } wlist[] = { + {"COMPAQ ", "HSV110 (C)COMPAQ", &tur}, + {"COMPAQ ", "MSA1000 ", &tur}, + {"COMPAQ ", "MSA1000 VOLUME ", &tur}, + {"DEC ", "HSG80 ", &tur}, + {"HP ", "HSV100 ", &readsector0}, + {NULL, NULL, NULL}, + }; + + for (i = 0; wlist[i].vendor; i++) { + if (strncmp(vendor, wlist[i].vendor, 8) == 0 && + strncmp(product, wlist[i].product, 16) == 0) { + path_p->checkfn = wlist[i].checkfn; + } + } + + return 0; +} + +int get_devmaps (struct devmap *devmaps) +{ + struct devmap *devmaps_p; + struct dm_task *dmt, *dmt1; + struct dm_names *names = NULL; + unsigned next = 0; + void *nexttgt; + int r = 0; + long long start, length; + char *target_type = NULL; + char *params; + + memset (devmaps, 0, MAXMAPS * sizeof (struct devmap)); + + if (!(dmt = dm_task_create(DM_DEVICE_LIST))) { + r = 1; + goto out; + } + + if (!dm_task_run(dmt)) { + r = 1; + goto out; + } + + if (!(names = dm_task_get_names(dmt))) { + r = 1; + goto out; + } + + if (!names->dev) { + LOG (1, "[get_devmaps] no devmap found"); + goto out; + } + + devmaps_p = devmaps; + + do { + /* keep only multipath maps */ + + names = (void *) names + next; + nexttgt = NULL; + LOG (3, "[get_devmaps] iterate on devmap names : %s", names->name); + + LOG (3, "[get_devmaps] dm_task_create(DM_DEVICE_STATUS)"); + if (!(dmt1 = dm_task_create(DM_DEVICE_STATUS))) + goto out1; + + LOG (3, "[get_devmaps] dm_task_set_name(dmt1, names->name)"); + if (!dm_task_set_name(dmt1, names->name)) + goto out1; + + LOG (3, "[get_devmaps] dm_task_run(dmt1)"); + if (!dm_task_run(dmt1)) + goto out1; + LOG (3, "[get_devmaps] DM_DEVICE_STATUS ioctl done"); + do { + LOG (3, "[get_devmaps] iterate on devmap's targets"); + nexttgt = dm_get_next_target(dmt1, nexttgt, + &start, + &length, + &target_type, + ¶ms); + + + LOG (3, "[get_devmaps] test target_type existence"); + if (!target_type) + goto out1; + + LOG (3, "[get_devmaps] test target_type is multipath"); + if (!strncmp (target_type, "multipath", 9)) { + strcpy (devmaps_p->mapname, names->name); + devmaps_p++; + + /* test vector overflow */ + if (devmaps_p - devmaps >= MAXMAPS * sizeof (struct devmap)) { + LOG (1, "[get_devmaps] devmaps overflow"); + dm_task_destroy(dmt1); + r = 1; + goto out; + } + } + + } while (nexttgt); + +out1: + dm_task_destroy(dmt1); + next = names->next; + + } while (next); + +out: + dm_task_destroy(dmt); + + LOG (3, "[get_devmaps] done"); + return r; +} + +int checkpath (struct path *path_p) +{ + char devnode[FILENAMESIZE]; + int r; + + LOG (2, "[checkpath] checking path %i:%i", path_p->major, path_p->minor); + sprintf (devnode, "/tmp/.checker.%i.%i", path_p->major, path_p->minor); + + if (path_p->checkfn == NULL) { + LOG (1, "[checkpath] test function not set for path %i:%i", + path_p->major, path_p->minor); + return 1; + } + + r = makenode (devnode, path_p->major, path_p->minor); + + if (r < 0) { + LOG (2, "[checkpath] can not make node for %s", devnode); + return r; + } + + r = path_p->checkfn(devnode); + unlink (devnode); + + return r; +} + +int updatepaths (struct devmap *devmaps, struct paths *failedpaths) +{ + struct path *path_p; + struct devmap *devmaps_p; + void *next; + struct dm_task *dmt; + long long start, length; + char *target_type = NULL; + char *params, *p1, *p2; + char word[6]; + int i; + + path_p = failedpaths->paths_h; + + pthread_mutex_lock (failedpaths->lock); + memset (failedpaths->paths_h, 0, MAXPATHS * sizeof (struct path)); + + /* first pass */ + /* ask DM the failed path list */ + + devmaps_p = devmaps; + + while (*devmaps_p->mapname != 0x0) { + next = NULL; + + if (!(dmt = dm_task_create(DM_DEVICE_STATUS))) + break; + + if (!dm_task_set_name(dmt, devmaps_p->mapname)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + do { + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + + /* begin ugly parser */ + p1 = params; + p2 = params; + while (*p1) { + /* p2 lags at the begining of the word p1 parses */ + while (*p1 != ' ') { + /* if the current word is a path */ + if (*p1 == ':') { + /* p1 jumps to path state */ + while (*p1 != 'A' && *p1 != 'F') + p1++; + + /* store path info */ + + path_p->checkfn = NULL; + + i = 0; + memset (&word, 'O', 6 * sizeof (char)); + while (*p2 != ':') { + word[i++] = *p2; + p2++; + } + path_p->major = atoi (word); + + p2++; + i = 0; + memset (&word, 'O', 6 * sizeof (char)); + while (*p2 != ' ') { + word[i++] = *p2; + p2++; + } + path_p->minor = atoi (word); + + strcpy (path_p->mapname, devmaps_p->mapname); + + /* + * discard active paths + * don't trust the A status flag : double check + */ + if (*p1 == 'A' && + !select_checkfn (path_p) && + checkpath (path_p)) { + LOG(2, "[updatepaths] discard %i:%i as valid path", + path_p->major, path_p->minor); + p1++; + memset (path_p, 0, sizeof(struct path)); + continue; + } + + path_p++; + + /* test vector overflow */ + if (path_p - failedpaths->paths_h >= MAXPATHS * sizeof (struct path)) { + LOG (1, "[updatepaths] path_h overflow"); + pthread_mutex_unlock (failedpaths->lock); + return 1; + } + } + p1++; + } + p2 = p1; + p1++; + } + } while (next); + +out: + dm_task_destroy(dmt); + devmaps_p++; + + } + + pthread_mutex_unlock (failedpaths->lock); + return 0; +} + +int geteventnr (char *name) +{ + struct dm_task *dmt; + struct dm_info info; + + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + return 0; + + if (!dm_task_set_name(dmt, name)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info)) + return 0; + + if (!info.exists) { + LOG(1, "Device %s does not exist", name); + return 0; + } + +out: + dm_task_destroy(dmt); + + return info.event_nr; +} + +void *waitevent (void * et) +{ + int event_nr; + struct event_thread *waiter; + + waiter = (struct event_thread *)et; + pthread_mutex_lock (waiter->waiter_lock); + + event_nr = geteventnr (waiter->mapname); + + struct dm_task *dmt; + + if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT))) + return 0; + + if (!dm_task_set_name(dmt, waiter->mapname)) + goto out; + + if (event_nr && !dm_task_set_event_nr(dmt, event_nr)) + goto out; + + dm_task_run(dmt); + +out: + dm_task_destroy(dmt); + + /* tell waiterloop we have an event */ + pthread_mutex_lock (event_lock); + pthread_cond_signal(event); + pthread_mutex_unlock (event_lock); + + /* release waiter_lock so that waiterloop knows we are gone */ + pthread_mutex_unlock (waiter->waiter_lock); + pthread_exit(waiter->thread); + + return (NULL); +} + +void *waiterloop (void *ap) +{ + struct paths *failedpaths; + struct devmap *devmaps, *devmaps_p; + struct event_thread *waiters, *waiters_p; + int r; + + /* inits */ + failedpaths = (struct paths *)ap; + devmaps = malloc (MAXMAPS * sizeof (struct devmap)); + waiters = malloc (MAXMAPS * sizeof (struct event_thread)); + memset (waiters, 0, MAXMAPS * sizeof (struct event_thread)); + + while (1) { + + /* update devmap list */ + LOG (1, "[event thread] refresh devmaps list"); + get_devmaps (devmaps); + + /* update failed paths list */ + LOG (1, "[event thread] refresh failpaths list"); + updatepaths (devmaps, failedpaths); + + /* start waiters on all devmaps */ + LOG (1, "[event thread] start up event loops"); + waiters_p = waiters; + devmaps_p = devmaps; + + while (*devmaps_p->mapname != 0x0) { + + /* find out if devmap already has a running waiter thread */ + while (*waiters_p->mapname != 0x0) { + if (!strcmp (waiters_p->mapname, devmaps_p->mapname)) + break; + waiters_p++; + } + + /* no event_thread struct : init it */ + if (*waiters_p->mapname == 0x0) { + strcpy (waiters_p->mapname, devmaps_p->mapname); + waiters_p->thread = malloc (sizeof (pthread_t)); + waiters_p->waiter_lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t)); + pthread_mutex_init (waiters_p->waiter_lock, NULL); + } + + /* event_thread struct found */ + if (*waiters_p->mapname != 0x0) { + r = pthread_mutex_trylock (waiters_p->waiter_lock); + /* thread already running : out */ + + if (r) + goto out; + + pthread_mutex_unlock (waiters_p->waiter_lock); + } + + LOG (1, "[event thread] create event thread for %s", waiters_p->mapname); + pthread_create (waiters_p->thread, NULL, waitevent, waiters_p); +out: + waiters_p = waiters; + devmaps_p++; + } + + /* wait event condition */ + pthread_mutex_lock (event_lock); + pthread_cond_wait(event, event_lock); + pthread_mutex_unlock (event_lock); + + LOG (1, "[event thread] event caught"); + } + + return (NULL); +} + +void *checkerloop (void *ap) +{ + struct paths *failedpaths; + struct path *path_p; + char *cmdargs[4] = {MULTIPATH, "-D", NULL, NULL}; + char major[5]; + char minor[5]; + int status; + + failedpaths = (struct paths *)ap; + + LOG (1, "[checker thread] path checkers start up"); + + while (1) { + path_p = failedpaths->paths_h; + pthread_mutex_lock (failedpaths->lock); + LOG (2, "[checker thread] checking paths"); + while (path_p->major != 0) { + + if (checkpath (path_p)) { + LOG (1, "[checker thread] reconfigure %s\n", path_p->mapname); + snprintf (major, 5, "%i", path_p->major); + snprintf (minor, 5, "%i", path_p->minor); + cmdargs[2] = major; + cmdargs[3] = minor; + if (fork () == 0) + execve (cmdargs[0], cmdargs, NULL); + + wait (&status); + /* MULTIPATH will ask for failedpaths refresh (SIGHUP) */ + } + + path_p++; + + /* test vector overflow */ + if (path_p - failedpaths->paths_h >= MAXPATHS * sizeof (struct path)) { + LOG (1, "[checker thread] path_h overflow"); + pthread_mutex_unlock (failedpaths->lock); + return (NULL); + } + } + pthread_mutex_unlock (failedpaths->lock); + sleep(CHECKINT); + } + + return (NULL); +} + +struct paths *initpaths (void) +{ + struct paths *failedpaths; + + failedpaths = malloc (sizeof (struct paths)); + failedpaths->paths_h = malloc (MAXPATHS * sizeof (struct path)); + failedpaths->lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t)); + pthread_mutex_init (failedpaths->lock, NULL); + event = (pthread_cond_t *) malloc (sizeof (pthread_cond_t)); + pthread_cond_init (event, NULL); + event_lock = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t)); + pthread_mutex_init (event_lock, NULL); + + return (failedpaths); +} + +void pidfile (pid_t pid) +{ + FILE *file; + struct stat *buf; + + buf = malloc (sizeof (struct stat)); + + if (!stat (PIDFILE, buf)) { + LOG(1, "[master thread] already running : out"); + free (buf); + exit (1); + } + + umask (022); + pid = setsid (); + + if (pid < -1) { + LOG(1, "[master thread] setsid() error"); + exit (1); + } + + file = fopen (PIDFILE, "w"); + fprintf (file, "%d\n", pid); + fclose (file); + free (buf); +} + +void * +signal_set(int signo, void (*func) (int)) +{ + int r; + struct sigaction sig; + struct sigaction osig; + + sig.sa_handler = func; + sigemptyset(&sig.sa_mask); + sig.sa_flags = 0; + + r = sigaction(signo, &sig, &osig); + + if (r < 0) + return (SIG_ERR); + else + return (osig.sa_handler); +} + +void sighup (int sig) +{ + LOG (1, "[master thread] SIGHUP caught : refresh devmap list"); + + /* ask for failedpaths refresh */ + pthread_mutex_lock (event_lock); + pthread_cond_signal(event); + pthread_mutex_unlock (event_lock); +} + +void sigend (int sig) +{ + LOG (1, "[master thread] unlink pidfile"); + unlink (PIDFILE); + LOG (1, "[master thread] --------shut down-------"); + exit (0); +} + +void signal_init(void) +{ + signal_set(SIGHUP, sighup); + signal_set(SIGINT, sigend); + signal_set(SIGTERM, sigend); + signal_set(SIGKILL, sigend); +} + +int main (int argc, char *argv[]) +{ + pthread_t wait, check; + struct paths *failedpaths; + pid_t pid; + + pid = fork (); + + /* can't fork */ + if (pid < 0) + exit (1); + + /* let the parent die happy */ + if (pid > 0) + exit (0); + + /* child's play */ + openlog (argv[0], 0, LOG_DAEMON); + LOG (1, "[master thread] --------start up--------"); + + pidfile (pid); + signal_init (); + + failedpaths = initpaths (); + + pthread_create (&wait, NULL, waiterloop, failedpaths); + pthread_create (&check, NULL, checkerloop, failedpaths); + pthread_join (wait, NULL); + pthread_join (check, NULL); + + return 0; +} |