summaryrefslogtreecommitdiff
path: root/multipathd/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'multipathd/main.c')
-rw-r--r--multipathd/main.c1059
1 files changed, 1059 insertions, 0 deletions
diff --git a/multipathd/main.c b/multipathd/main.c
new file mode 100644
index 0000000..0f36b77
--- /dev/null
+++ b/multipathd/main.c
@@ -0,0 +1,1059 @@
+#include <string.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <linux/unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libdevmapper.h>
+#include <signal.h>
+#include <wait.h>
+#include <sched.h>
+#include <errno.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+
+/*
+ * libsysfs
+ */
+#include <sysfs/libsysfs.h>
+#include <sysfs/dlist.h>
+
+/*
+ * libcheckers
+ */
+#include <checkers.h>
+#include <path_state.h>
+
+/*
+ * libmultipath
+ */
+#include <parser.h>
+#include <vector.h>
+#include <memory.h>
+#include <config.h>
+#include <callout.h>
+#include <util.h>
+#include <blacklist.h>
+#include <hwtable.h>
+#include <defaults.h>
+#include <structs.h>
+#include <dmparser.h>
+#include <devmapper.h>
+#include <dict.h>
+#include <discovery.h>
+#include <debug.h>
+#include <propsel.h>
+#include <uevent.h>
+
+#include "main.h"
+#include "copy.h"
+#include "clone_platform.h"
+#include "pidfile.h"
+
+#define FILE_NAME_SIZE 256
+#define CMDSIZE 160
+
+#define CALLOUT_DIR "/var/cache/multipathd"
+
+#define LOG_MSG(a,b) \
+ if (strlen(a)) { \
+ log_safe(LOG_WARNING, "%s: %s", b, a); \
+ memset(a, 0, MAX_CHECKER_MSG_SIZE); \
+ }
+
+#ifdef LCKDBG
+#define lock(a) \
+ fprintf(stderr, "%s:%s(%i) lock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
+ pthread_mutex_lock(a)
+#define unlock(a) \
+ fprintf(stderr, "%s:%s(%i) unlock %p\n", __FILE__, __FUNCTION__, __LINE__, a); \
+ pthread_mutex_unlock(a)
+#else
+#define lock(a) pthread_mutex_lock(a)
+#define unlock(a) pthread_mutex_unlock(a)
+#endif
+
+/*
+ * global vars
+ */
+int pending_event = 0;
+pthread_mutex_t *event_lock;
+pthread_cond_t *event;
+
+/*
+ * structs
+ */
+struct paths {
+ pthread_mutex_t *lock;
+ vector pathvec;
+};
+
+struct event_thread {
+ pthread_t *thread;
+ pthread_mutex_t *waiter_lock;
+ int lease;
+ int event_nr;
+ char mapname[WWID_SIZE];
+ struct paths *allpaths;
+};
+
+int
+uev_trigger (struct uevent * uev, void * trigger_data)
+{
+ int r = 0;
+ int i;
+ char devname[32];
+ struct path * pp;
+ struct paths * allpaths;
+
+ allpaths = (struct paths *)trigger_data;
+
+ if (strncmp(uev->devpath, "/block", 6))
+ goto out;
+
+ basename(uev->devpath, devname);
+ lock(allpaths->lock);
+ pp = find_path_by_dev(allpaths->pathvec, devname);
+
+ r = 1;
+
+ if (pp && !strncmp(uev->action, "remove", 6)) {
+ condlog(2, "remove %s path checker", devname);
+ i = find_slot(allpaths->pathvec, (void *)pp);
+ vector_del_slot(allpaths->pathvec, i);
+ free_path(pp);
+ }
+ if (!pp && !strncmp(uev->action, "add", 3)) {
+ condlog(2, "add %s path checker", devname);
+ store_pathinfo(allpaths->pathvec, conf->hwtable, devname);
+ }
+ unlock(allpaths->lock);
+
+ r = 0;
+out:
+ FREE(uev);
+ return r;
+}
+
+static void *
+ueventloop (void * ap)
+{
+ uevent_listen(&uev_trigger, ap);
+
+ return NULL;
+}
+
+static void
+strvec_free (vector vec)
+{
+ int i;
+ char * str;
+
+ vector_foreach_slot (vec, str, i)
+ if (str)
+ FREE(str);
+
+ vector_free(vec);
+}
+
+static int
+exit_daemon (int status)
+{
+ if (status != 0)
+ fprintf(stderr, "bad exit status. see daemon.log\n");
+
+ log_safe(LOG_INFO, "umount ramfs");
+ umount(CALLOUT_DIR);
+
+ log_safe(LOG_INFO, "unlink pidfile");
+ unlink(DEFAULT_PIDFILE);
+
+ log_safe(LOG_NOTICE, "--------shut down-------");
+ log_thread_stop();
+ exit(status);
+}
+
+static void
+set_paths_owner (struct paths * allpaths, struct multipath * mpp)
+{
+ int i;
+ struct path * pp;
+
+ lock(allpaths->lock);
+
+ vector_foreach_slot (allpaths->pathvec, pp, i)
+ if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE))
+ pp->mpp = mpp;
+
+ unlock(allpaths->lock);
+}
+
+static int
+get_dm_mpvec (vector mpvec, struct paths * allpaths)
+{
+ int i;
+ struct multipath * mpp;
+ char * wwid;
+
+ if (dm_get_maps(mpvec, "multipath"))
+ return 1;
+
+ vector_foreach_slot (mpvec, mpp, i) {
+ wwid = get_mpe_wwid(mpp->alias);
+
+ if (wwid) {
+ strncpy(mpp->wwid, wwid, WWID_SIZE);
+ wwid = NULL;
+ } else
+ strncpy(mpp->wwid, mpp->alias, WWID_SIZE);
+
+ set_paths_owner(allpaths, mpp);
+ }
+ return 0;
+}
+
+static int
+path_discovery_locked (struct paths *allpaths, char *sysfs_path)
+{
+ lock(allpaths->lock);
+ path_discovery(allpaths->pathvec, conf, 0);
+ unlock(allpaths->lock);
+
+ return 0;
+}
+
+static int
+mark_failed_path (struct paths *allpaths, char *mapname)
+{
+ struct multipath *mpp;
+ struct pathgroup *pgp;
+ struct path *pp;
+ struct path *app;
+ int i, j;
+ int r = 1;
+
+ if (!dm_map_present(mapname))
+ return 0;
+
+ mpp = alloc_multipath();
+
+ if (!mpp)
+ return 1;
+
+ if (dm_get_map(mapname, &mpp->size, (char *)mpp->params))
+ goto out;
+
+ if (dm_get_status(mapname, mpp->status))
+ goto out;
+
+ lock(allpaths->lock);
+ r = disassemble_map(allpaths->pathvec, mpp->params, mpp);
+ unlock(allpaths->lock);
+
+ if (r)
+ goto out;
+
+ r = disassemble_status(mpp->status, mpp);
+
+ if (r)
+ goto out;
+
+ r = 0; /* can't fail from here on */
+ lock(allpaths->lock);
+
+ vector_foreach_slot (mpp->pg, pgp, i) {
+ vector_foreach_slot (pgp->paths, pp, j) {
+ if (pp->dmstate != PSTATE_FAILED)
+ continue;
+
+ app = find_path_by_devt(allpaths->pathvec, pp->dev_t);
+
+ if (app && app->state != PATH_DOWN) {
+ log_safe(LOG_NOTICE, "mark %s as failed",
+ pp->dev_t);
+ app->state = PATH_DOWN;
+ }
+ }
+ }
+ unlock(allpaths->lock);
+out:
+ free_multipath(mpp, KEEP_PATHS);
+
+ return r;
+}
+
+static void *
+waiteventloop (struct event_thread * waiter, char * cmd)
+{
+ struct dm_task *dmt;
+ int event_nr;
+
+ if (!waiter->event_nr)
+ waiter->event_nr = dm_geteventnr(waiter->mapname);
+
+ if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
+ return 0;
+
+ if (!dm_task_set_name(dmt, waiter->mapname))
+ goto out;
+
+ if (waiter->event_nr && !dm_task_set_event_nr(dmt, waiter->event_nr))
+ goto out;
+
+ dm_task_no_open_count(dmt);
+
+ dm_task_run(dmt);
+
+ waiter->event_nr++;
+
+ /*
+ * upon event ...
+ */
+ while (1) {
+ log_safe(LOG_DEBUG, "%s", cmd);
+ log_safe(LOG_NOTICE, "devmap event (%i) on %s",
+ waiter->event_nr, waiter->mapname);
+
+ mark_failed_path(waiter->allpaths, waiter->mapname);
+
+ event_nr = dm_geteventnr(waiter->mapname);
+
+ if (waiter->event_nr == event_nr)
+ break;
+
+ waiter->event_nr = event_nr;
+ }
+
+out:
+ dm_task_destroy(dmt);
+
+ /*
+ * tell waiterloop we have an event
+ */
+ lock(event_lock);
+ pending_event++;
+ pthread_cond_signal(event);
+ unlock(event_lock);
+
+ return NULL;
+}
+
+static void *
+waitevent (void * et)
+{
+ struct event_thread *waiter;
+ char cmd[CMDSIZE];
+
+ mlockall(MCL_CURRENT | MCL_FUTURE);
+
+ waiter = (struct event_thread *)et;
+ lock(waiter->waiter_lock);
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ if (safe_snprintf(cmd, CMDSIZE, "%s %s",
+ conf->multipath, waiter->mapname)) {
+ log_safe(LOG_ERR, "command too long, abord reconfigure");
+ goto out;
+ }
+ while (1)
+ waiteventloop(waiter, cmd);
+
+out:
+ /*
+ * release waiter_lock so that waiterloop knows we are gone
+ */
+ unlock(waiter->waiter_lock);
+ pthread_exit(waiter->thread);
+
+ return NULL;
+}
+
+static void *
+alloc_waiter (void)
+{
+
+ struct event_thread * wp;
+
+ wp = MALLOC(sizeof(struct event_thread));
+
+ if (!wp)
+ return NULL;
+
+ wp->thread = MALLOC(sizeof(pthread_t));
+
+ if (!wp->thread)
+ goto out;
+
+ wp->waiter_lock = (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t));
+
+ if (!wp->waiter_lock)
+ goto out1;
+
+ pthread_mutex_init(wp->waiter_lock, NULL);
+ return wp;
+
+out1:
+ free(wp->thread);
+out:
+ free(wp);
+ return NULL;
+}
+
+static void
+free_waiter (struct event_thread * wp)
+{
+ pthread_mutex_destroy(wp->waiter_lock);
+ free(wp->thread);
+ free(wp);
+}
+
+static void
+fail_path (struct path * pp)
+{
+ if (!pp->mpp)
+ return;
+
+ log_safe(LOG_NOTICE, "checker failed path %s in map %s",
+ pp->dev_t, pp->mpp->alias);
+
+ dm_fail_path(pp->mpp->alias, pp->dev_t);
+}
+
+static void *
+waiterloop (void *ap)
+{
+ struct paths *allpaths;
+ struct multipath *mpp;
+ vector mpvec = NULL;
+ vector waiters;
+ struct event_thread *wp;
+ pthread_attr_t attr;
+ int r;
+ char buff[1];
+ int i, j;
+
+ mlockall(MCL_CURRENT | MCL_FUTURE);
+ log_safe(LOG_NOTICE, "start DM events thread");
+
+ if (sysfs_get_mnt_path(sysfs_path, FILE_NAME_SIZE)) {
+ log_safe(LOG_ERR, "can not find sysfs mount point");
+ return NULL;
+ }
+
+ /*
+ * inits
+ */
+ allpaths = (struct paths *)ap;
+ waiters = vector_alloc();
+
+ if (!waiters)
+ return NULL;
+
+ if (pthread_attr_init(&attr))
+ return NULL;
+
+ pthread_attr_setstacksize(&attr, 32 * 1024);
+
+ /*
+ * update paths list
+ */
+ log_safe(LOG_INFO, "fetch paths list");
+
+ while(path_discovery_locked(allpaths, sysfs_path)) {
+ log_safe(LOG_ERR, "can't update path list ... retry");
+ sleep(5);
+ }
+
+ log_safe(LOG_NOTICE, "initial reconfigure multipath maps");
+// execute_program(conf->multipath, buff, 1);
+
+ while (1) {
+ /*
+ * revoke the leases
+ */
+ vector_foreach_slot(waiters, wp, i)
+ wp->lease = 0;
+
+ /*
+ * update multipaths list
+ */
+ log_safe(LOG_INFO, "refresh multipaths list");
+
+ if (mpvec)
+ free_multipathvec(mpvec, KEEP_PATHS);
+
+ while (1) {
+ /*
+ * we're not allowed to fail here
+ */
+ mpvec = vector_alloc();
+
+ if (mpvec && !get_dm_mpvec(mpvec, allpaths))
+ break;
+
+ log_safe(LOG_ERR, "can't get mpvec ... retry");
+ sleep(5);
+ }
+
+ /*
+ * start waiters on all mpvec
+ */
+ log_safe(LOG_INFO, "start up event loops");
+
+ vector_foreach_slot (mpvec, mpp, i) {
+ /*
+ * find out if devmap already has
+ * a running waiter thread
+ */
+ vector_foreach_slot (waiters, wp, j)
+ if (!strcmp(wp->mapname, mpp->alias))
+ break;
+
+ /*
+ * no event_thread struct : init it
+ */
+ if (j == VECTOR_SIZE(waiters)) {
+ wp = alloc_waiter();
+
+ if (!wp)
+ continue;
+
+ strncpy(wp->mapname, mpp->alias, WWID_SIZE);
+ wp->allpaths = allpaths;
+
+ if (!vector_alloc_slot(waiters)) {
+ free_waiter(wp);
+ continue;
+ }
+ vector_set_slot(waiters, wp);
+ }
+
+ /*
+ * event_thread struct found
+ */
+ else if (j < VECTOR_SIZE(waiters)) {
+ r = pthread_mutex_trylock(wp->waiter_lock);
+
+ /*
+ * thread already running : next devmap
+ */
+ if (r) {
+ log_safe(LOG_DEBUG,
+ "event checker running : %s",
+ wp->mapname);
+
+ /*
+ * renew the lease
+ */
+ wp->lease = 1;
+ continue;
+ }
+ pthread_mutex_unlock(wp->waiter_lock);
+ }
+
+ if (pthread_create(wp->thread, &attr, waitevent, wp)) {
+ log_safe(LOG_ERR,
+ "cannot create event checker : %s",
+ wp->mapname);
+ free_waiter(wp);
+ vector_del_slot(waiters, j);
+ continue;
+ }
+ wp->lease = 1;
+ log_safe(LOG_NOTICE, "event checker started : %s",
+ wp->mapname);
+ }
+ vector_foreach_slot (waiters, wp, i) {
+ if (wp->lease == 0) {
+ log_safe(LOG_NOTICE, "reap event checker : %s",
+ wp->mapname);
+
+ pthread_cancel(*wp->thread);
+ free_waiter(wp);
+ vector_del_slot(waiters, i);
+ i--;
+ }
+ }
+ /*
+ * wait event condition
+ */
+ lock(event_lock);
+
+ if (pending_event > 0)
+ pending_event--;
+
+ log_safe(LOG_INFO, "%i pending event(s)", pending_event);
+ if(pending_event == 0)
+ pthread_cond_wait(event, event_lock);
+
+ unlock(event_lock);
+ }
+ return NULL;
+}
+
+static void *
+checkerloop (void *ap)
+{
+ struct paths *allpaths;
+ struct path *pp;
+ int i;
+ int newstate;
+ char buff[1];
+ char cmd[CMDSIZE];
+ char checker_msg[MAX_CHECKER_MSG_SIZE];
+
+ mlockall(MCL_CURRENT | MCL_FUTURE);
+
+ memset(checker_msg, 0, MAX_CHECKER_MSG_SIZE);
+ allpaths = (struct paths *)ap;
+
+ log_safe(LOG_NOTICE, "path checkers start up");
+
+ while (1) {
+ lock(allpaths->lock);
+ log_safe(LOG_DEBUG, "checking paths");
+
+ vector_foreach_slot (allpaths->pathvec, pp, i) {
+ if (!pp->checkfn) {
+ pathinfo(pp, conf->hwtable, DI_SYSFS);
+ select_checkfn(pp);
+ }
+
+ if (!pp->checkfn) {
+ log_safe(LOG_ERR, "%s: checkfn is void",
+ pp->dev);
+ continue;
+ }
+ newstate = pp->checkfn(pp->fd, checker_msg,
+ &pp->checker_context);
+
+ if (newstate != pp->state) {
+ pp->state = newstate;
+ LOG_MSG(checker_msg, pp->dev_t);
+
+ /*
+ * proactively fail path in the DM
+ */
+ if (newstate == PATH_DOWN ||
+ newstate == PATH_SHAKY) {
+ fail_path(pp);
+ continue;
+ }
+
+ /*
+ * reconfigure map now
+ */
+ if (safe_snprintf(cmd, CMDSIZE, "%s %s",
+ conf->multipath, pp->dev_t)) {
+ log_safe(LOG_ERR, "command too long,"
+ " abord reconfigure");
+ } else {
+ log_safe(LOG_DEBUG, "%s", cmd);
+ log_safe(LOG_INFO,
+ "reconfigure %s multipath",
+ pp->dev_t);
+ execute_program(cmd, buff, 1);
+ }
+
+ /*
+ * tell waiterloop we have an event
+ */
+ lock (event_lock);
+ pending_event++;
+ pthread_cond_signal(event);
+ unlock (event_lock);
+ }
+ pp->state = newstate;
+ }
+ unlock(allpaths->lock);
+ sleep(conf->checkint);
+ }
+ return NULL;
+}
+
+static struct paths *
+init_paths (void)
+{
+ struct paths *allpaths;
+
+ allpaths = MALLOC(sizeof(struct paths));
+
+ if (!allpaths)
+ return NULL;
+
+ allpaths->lock =
+ (pthread_mutex_t *)MALLOC(sizeof(pthread_mutex_t));
+
+ if (!allpaths->lock)
+ goto out;
+
+ allpaths->pathvec = vector_alloc();
+
+ if (!allpaths->pathvec)
+ goto out1;
+
+ pthread_mutex_init (allpaths->lock, NULL);
+
+ return (allpaths);
+out1:
+ FREE(allpaths->lock);
+out:
+ FREE(allpaths);
+ return NULL;
+}
+
+static int
+init_event (void)
+{
+ event = (pthread_cond_t *)MALLOC(sizeof (pthread_cond_t));
+
+ if (!event)
+ return 1;
+
+ pthread_cond_init (event, NULL);
+ event_lock = (pthread_mutex_t *) MALLOC (sizeof (pthread_mutex_t));
+
+ if (!event_lock)
+ goto out;
+
+ pthread_mutex_init (event_lock, NULL);
+
+ return 0;
+out:
+ FREE(event);
+ return 1;
+}
+/*
+ * this logic is all about keeping callouts working in case of
+ * system disk outage (think system over SAN)
+ * this needs the clone syscall, so don't bother if not present
+ * (Debian Woody)
+ */
+#ifdef CLONE_NEWNS
+static int
+prepare_namespace(void)
+{
+ mode_t mode = S_IRWXU;
+ struct stat *buf;
+ char ramfs_args[64];
+ int i;
+ int fd;
+ char * bin;
+ size_t size = 10;
+ struct stat statbuf;
+
+ buf = MALLOC(sizeof(struct stat));
+
+ /*
+ * create a temp mount point for ramfs
+ */
+ if (stat(CALLOUT_DIR, buf) < 0) {
+ if (mkdir(CALLOUT_DIR, mode) < 0) {
+ log_safe(LOG_ERR, "cannot create " CALLOUT_DIR);
+ return -1;
+ }
+ log_safe(LOG_DEBUG, "created " CALLOUT_DIR);
+ }
+
+ /*
+ * compute the optimal ramdisk size
+ */
+ vector_foreach_slot (conf->binvec, bin,i) {
+ if ((fd = open(bin, O_RDONLY)) < 0) {
+ log_safe(LOG_ERR, "cannot open %s", bin);
+ return -1;
+ }
+ if (fstat(fd, &statbuf) < 0) {
+ log_safe(LOG_ERR, "cannot stat %s", bin);
+ return -1;
+ }
+ size += statbuf.st_size;
+ close(fd);
+ }
+ log_safe(LOG_INFO, "ramfs maxsize is %u", (unsigned int) size);
+
+ /*
+ * mount the ramfs
+ */
+ if (safe_sprintf(ramfs_args, "maxsize=%u", (unsigned int) size)) {
+ fprintf(stderr, "ramfs_args too small\n");
+ return -1;
+ }
+ if (mount(NULL, CALLOUT_DIR, "ramfs", MS_SYNCHRONOUS, ramfs_args) < 0) {
+ log_safe(LOG_ERR, "cannot mount ramfs on " CALLOUT_DIR);
+ return -1;
+ }
+ log_safe(LOG_DEBUG, "mount ramfs on " CALLOUT_DIR);
+
+ /*
+ * populate the ramfs with callout binaries
+ */
+ vector_foreach_slot (conf->binvec, bin,i) {
+ if (copytodir(bin, CALLOUT_DIR) < 0) {
+ log_safe(LOG_ERR, "cannot copy %s in ramfs", bin);
+ exit_daemon(1);
+ }
+ log_safe(LOG_DEBUG, "cp %s in ramfs", bin);
+ }
+ strvec_free(conf->binvec);
+
+ /*
+ * bind the ramfs to :
+ * /sbin : default home of multipath ...
+ * /bin : default home of scsi_id ...
+ * /tmp : home of scsi_id temp files
+ */
+ if (mount(CALLOUT_DIR, "/sbin", NULL, MS_BIND, NULL) < 0) {
+ log_safe(LOG_ERR, "cannot bind ramfs on /sbin");
+ return -1;
+ }
+ log_safe(LOG_DEBUG, "bind ramfs on /sbin");
+ if (mount(CALLOUT_DIR, "/bin", NULL, MS_BIND, NULL) < 0) {
+ log_safe(LOG_ERR, "cannot bind ramfs on /bin");
+ return -1;
+ }
+ log_safe(LOG_DEBUG, "bind ramfs on /bin");
+ if (mount(CALLOUT_DIR, "/tmp", NULL, MS_BIND, NULL) < 0) {
+ log_safe(LOG_ERR, "cannot bind ramfs on /tmp");
+ return -1;
+ }
+ log_safe(LOG_DEBUG, "bind ramfs on /tmp");
+
+ return 0;
+}
+#endif
+
+static 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);
+}
+
+static void
+sighup (int sig)
+{
+ log_safe(LOG_NOTICE, "SIGHUP received");
+
+#ifdef _DEBUG_
+ dbg_free_final(NULL);
+#endif
+}
+
+static void
+sigend (int sig)
+{
+ exit_daemon(0);
+}
+
+static void
+signal_init(void)
+{
+ signal_set(SIGHUP, sighup);
+ signal_set(SIGINT, sigend);
+ signal_set(SIGTERM, sigend);
+ signal_set(SIGKILL, sigend);
+}
+
+static void
+setscheduler (void)
+{
+ int res;
+ static struct sched_param sched_param = {
+ sched_priority: 99
+ };
+
+ res = sched_setscheduler (0, SCHED_RR, &sched_param);
+
+ if (res == -1)
+ log_safe(LOG_WARNING, "Could not set SCHED_RR at priority 99");
+ return;
+}
+
+static void
+set_oom_adj (int val)
+{
+ FILE *fp;
+
+ fp = fopen("/proc/self/oom_adj", "w");
+
+ if (!fp)
+ return;
+
+ fprintf(fp, "%i", val);
+ fclose(fp);
+}
+
+static int
+child (void * param)
+{
+ pthread_t wait_thr, check_thr, uevent_thr;
+ pthread_attr_t attr;
+ struct paths * allpaths;
+
+ mlockall(MCL_CURRENT | MCL_FUTURE);
+
+ log_thread_start();
+ log_safe(LOG_NOTICE, "--------start up--------");
+
+ if (pidfile_create(DEFAULT_PIDFILE, getpid())) {
+ log_thread_stop();
+ exit(1);
+ }
+ signal_init();
+ setscheduler();
+ set_oom_adj(-17);
+ allpaths = init_paths();
+
+ if (!allpaths || init_event())
+ exit(1);
+
+ conf->checkint = CHECKINT;
+
+ setlogmask(LOG_UPTO(conf->verbosity + 3));
+
+ condlog(2, "read " DEFAULT_CONFIGFILE);
+ init_data(DEFAULT_CONFIGFILE, init_keywords);
+
+ /*
+ * fill the voids left in the config file
+ */
+ if (conf->binvec == NULL) {
+ conf->binvec = vector_alloc();
+ push_callout("/sbin/scsi_id");
+ }
+ if (conf->multipath == NULL) {
+ conf->multipath = MULTIPATH;
+ push_callout(conf->multipath);
+ }
+ if (conf->hwtable == NULL) {
+ conf->hwtable = vector_alloc();
+ setup_default_hwtable(conf->hwtable);
+ }
+ if (conf->blist == NULL) {
+ conf->blist = vector_alloc();
+ setup_default_blist(conf->blist);
+ }
+ if (conf->default_selector == NULL)
+ conf->default_selector = set_default(DEFAULT_SELECTOR);
+
+ if (conf->udev_dir == NULL)
+ conf->udev_dir = set_default(DEFAULT_UDEVDIR);
+
+ if (conf->default_getuid == NULL)
+ conf->default_getuid = set_default(DEFAULT_GETUID);
+
+ if (conf->default_features == NULL)
+ conf->default_features = set_default(DEFAULT_FEATURES);
+
+ if (conf->default_hwhandler == NULL)
+ conf->default_hwhandler = set_default(DEFAULT_HWHANDLER);
+
+
+#ifdef CLONE_NEWNS
+ if (prepare_namespace() < 0) {
+ log_safe(LOG_ERR, "cannot prepare namespace");
+ exit_daemon(1);
+ }
+#endif
+
+ /*
+ * start threads
+ */
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 64 * 1024);
+
+ pthread_create(&wait_thr, &attr, waiterloop, allpaths);
+ pthread_create(&check_thr, &attr, checkerloop, allpaths);
+ pthread_create(&uevent_thr, &attr, ueventloop, allpaths);
+ pthread_join(wait_thr, NULL);
+ pthread_join(check_thr, NULL);
+ pthread_join(uevent_thr, NULL);
+
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ int arg;
+ int err;
+ void * child_stack;
+
+ if (getuid() != 0) {
+ fprintf(stderr, "need to be root, exit");
+ exit(1);
+ }
+
+ /* make sure we don't lock any path */
+ chdir("/");
+ umask(umask(077) | 022);
+
+ child_stack = (void *)malloc(CHILD_STACK_SIZE);
+
+ if (!child_stack)
+ exit(1);
+
+ conf = alloc_config();
+
+ if (!conf)
+ exit(1);
+
+ conf->verbosity = 2;
+
+ while ((arg = getopt(argc, argv, ":qdlFSi:v:p:")) != EOF ) {
+ switch(arg) {
+ case 'v':
+ if (sizeof(optarg) > sizeof(char *) ||
+ !isdigit(optarg[0]))
+ exit(1);
+
+ conf->verbosity = atoi(optarg);
+ break;
+ default:
+ ;
+ }
+ }
+
+#ifdef CLONE_NEWNS /* recent systems have clone() */
+
+# if defined(__hppa__) || defined(__powerpc64__)
+ err = clone(child, child_stack, CLONE_NEWNS, NULL);
+# elif defined(__ia64__)
+ err = clone2(child, child_stack,
+ CHILD_STACK_SIZE, CLONE_NEWNS, NULL,
+ NULL, NULL, NULL);
+# else
+ err = clone(child, child_stack + CHILD_STACK_SIZE, CLONE_NEWNS, NULL);
+# endif
+ if (err < 0)
+ exit (1);
+
+ exit(0);
+#else /* older system fallback to fork() */
+ err = fork();
+
+ if (err < 0)
+ exit (1);
+
+ return (child(child_stack));
+#endif
+
+}