diff --git a/builtin.h b/builtin.h
index b24fc99..4b908f0 100644
--- a/builtin.h
+++ b/builtin.h
@@ -36,6 +36,7 @@ int cmd_write_labels(int argc, const char **argv, void *ctx);
 int cmd_init_labels(int argc, const char **argv, void *ctx);
 int cmd_check_labels(int argc, const char **argv, void *ctx);
 int cmd_inject_error(int argc, const char **argv, void *ctx);
+int cmd_monitor(int argc, const char **argv, void *ctx);
 int cmd_list(int argc, const char **argv, void *ctx);
 #ifdef ENABLE_TEST
 int cmd_test(int argc, const char **argv, void *ctx);
diff --git a/ndctl/Makefile.am b/ndctl/Makefile.am
index e0db97b..e364ef9 100644
--- a/ndctl/Makefile.am
+++ b/ndctl/Makefile.am
@@ -16,7 +16,8 @@ ndctl_SOURCES = ndctl.c \
 		util/json-firmware.c \
 		inject-error.c \
 		update.c \
-		inject-smart.c
+		inject-smart.c \
+		monitor.c
 
 if ENABLE_DESTRUCTIVE
 ndctl_SOURCES += ../test/blk_namespaces.c \
@@ -41,3 +42,13 @@ ndctl_SOURCES += ../test/libndctl.c \
 		 ../test/multi-pmem.c \
 		 ../test/core.c
 endif
+
+unitfiles =\
+	ndctl-monitor.service \
+	ndctl-monitor@.service
+
+unitdir = /usr/lib/systemd/system/
+
+unit_DATA = $(unitfiles)
+
+EXTRA_DIST = $(unitfiles)
diff --git a/ndctl/monitor.c b/ndctl/monitor.c
new file mode 100644
index 0000000..2164f27
--- /dev/null
+++ b/ndctl/monitor.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2018, FUJITSU LIMITED. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+#include <stdio.h>
+#include <json-c/json.h>
+#include <signal.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <util/parse-options.h>
+#include <util/log.h>
+#include <util/json.h>
+#include <util/filter.h>
+#include <ndctl/lib/private.h>
+#include <ndctl/libndctl.h>
+#include <sys/stat.h>
+#define NUM_MAX_DIMM 1024
+#define BUF_SIZE 4096
+
+struct monitor_dimm {
+	struct ndctl_dimm *dimm;
+	int health_eventfd;
+	struct list_node list;
+};
+
+struct monitor_filter_arg {
+	struct list_head mdimm;
+	int maxfd;
+	fd_set fds;
+	int num_dimm;
+	unsigned long flags;
+};
+
+struct util_filter_params param;
+
+static char *conf_dir = "/etc/sysconfig/ndctl/";
+static char *def_log_dir = "/var/log/ndctl/";
+
+static char *concat(char *str1, char *str2)
+{
+	char *result = malloc(strlen(str1) + strlen(str2) + 1);
+	strcpy(result, str1);
+	strcat(result, str2);
+	return result;
+}
+
+static bool is_dir(char *filepath)
+{
+	DIR *dir = opendir(filepath);
+	if (dir) {
+		closedir(dir);
+		return true;
+	}
+	return false;
+}
+
+static int recur_mkdir(char *filepath, mode_t mode)
+{
+	char *p;
+	char *buf = (char *)malloc(strlen(filepath) + 4);
+
+	strcpy(buf, filepath);
+	for (p = strchr(buf + 1, '/'); p; p = strchr(p + 1, '/')) {
+		*p = '\0';
+		if (!is_dir(buf)) {
+			if (mkdir(buf, mode) < 0) {
+				free(buf);
+				return -1;
+			}
+		}
+		*p = '/';
+	}
+
+	free(buf);
+	return 0;
+}
+
+static void log_file(struct ndctl_ctx *ctx, int priority, const char *file,
+	int line, const char *fn, const char *format, va_list args)
+{
+	FILE *f;
+	char *log_name, *log_dir, *buf;
+	char *errmsg = (char *)malloc(BUF_SIZE);
+	char *tail = "/";
+
+	log_dir = dirname(strdup(param.log));
+	log_name = basename(strdup(param.log));
+	if (strcmp(log_dir, ".") == 0)
+		log_dir = def_log_dir;
+	else
+		log_dir = concat(log_dir, tail);
+	if (log_dir[0] != '/')
+		log_dir = concat(def_log_dir, log_dir);
+	log_name = concat(log_dir, log_name);
+
+	if (!is_dir(log_dir)) {
+		if (recur_mkdir(log_dir, 0744) != 0) {
+			sprintf(errmsg, "cannot create dir: %s", log_dir);
+			goto out;
+		}
+	}
+
+	f = fopen(log_name, "a+");
+	if (!f) {
+		sprintf(errmsg, "open %s failed", log_name);
+		goto out;
+	}
+
+	buf = (char *)malloc(BUF_SIZE);
+	if (!buf) {
+		sprintf(errmsg, "cannot get memory for log_file");
+		goto out;
+	}
+	vsnprintf(buf, BUF_SIZE, format, args);
+	fprintf(f, "%s\n", buf);
+	free(buf);
+	fclose(f);
+	return;
+out:
+	syslog(LOG_ERR, "%s\n", errmsg);
+	if (!param.fork)
+		error("%s\n", errmsg);
+	free(errmsg);
+	exit(EXIT_FAILURE);
+}
+
+static void log_syslog(struct ndctl_ctx *ctx, int priority, const char *file,
+	int line, const char *fn, const char *format, va_list args)
+{
+	char *buf = (char *)malloc(BUF_SIZE);
+	vsnprintf(buf, BUF_SIZE, format, args);
+	syslog(priority, "%s", buf);
+	free(buf);
+}
+
+#define fail(fmt, ...) \
+do { \
+	err(ctx, "ndctl-%s:%s:%d: " fmt, \
+				VERSION, __func__, __LINE__, ##__VA_ARGS__); \
+	if (!param.fork) \
+		fprintf(stderr, "ndctl-%s:%s:%d: " fmt, \
+				VERSION, __func__, __LINE__, ##__VA_ARGS__); \
+} while (0)
+
+static int notify_json_msg(struct ndctl_ctx *ctx, struct ndctl_dimm *dimm)
+{
+	time_t c_time;
+	char date[32];
+	struct json_object *jmsg, *jdatetime, *jpid, *jdimm, *jhealth;
+
+	jmsg = json_object_new_object();
+	if (!jmsg) {
+		fail("\n");
+		return -1;
+	}
+
+	c_time = time(NULL);
+	strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&c_time));
+	jdatetime = json_object_new_string(date);
+	if (!jdatetime) {
+		fail("\n");
+		return -1;
+	}
+	json_object_object_add(jmsg, "datetime", jdatetime);
+
+	jpid = json_object_new_int((int)getpid());
+	if (!jpid) {
+		fail("\n");
+		return -1;
+	}
+	json_object_object_add(jmsg, "pid", jpid);
+
+	jdimm = util_dimm_to_json(dimm, 0);
+	if (!dimm) {
+		fail("\n");
+		return -1;
+	}
+	json_object_object_add(jmsg, "dimm", jdimm);
+
+	jhealth = util_dimm_health_to_json(dimm);
+	if (!jhealth) {
+		fail("\n");
+		return -1;
+	}
+	json_object_object_add(jdimm, "health", jhealth);
+
+	notice(ctx, "%s",
+		json_object_to_json_string_ext(jmsg, JSON_C_TO_STRING_PLAIN));
+	if (!param.fork)
+		printf("%s\n", json_object_to_json_string_ext(jmsg,
+				JSON_C_TO_STRING_PRETTY));
+	return 0;
+}
+
+static void filter_dimm(struct ndctl_dimm *dimm, struct util_filter_ctx *ctx)
+{
+	struct monitor_filter_arg *mfa = (struct monitor_filter_arg *)ctx->arg;
+	int fd;
+	char buf[BUF_SIZE];
+
+	if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_SMART_THRESHOLD))
+		return;
+
+	struct monitor_dimm *m_dimm = malloc(sizeof(*m_dimm));
+	m_dimm->dimm = dimm;
+	fd = ndctl_dimm_get_health_eventfd(dimm);
+	pread(fd, buf, sizeof(buf), 0);
+	m_dimm->health_eventfd = fd;
+	list_add_tail(&mfa->mdimm, &m_dimm->list);
+	FD_SET(fd, &mfa->fds);
+	if (fd > mfa->maxfd)
+		mfa->maxfd = fd;
+	mfa->num_dimm++;
+}
+
+static int monitor_smart_event(struct ndctl_ctx *ctx)
+{
+	struct util_filter_ctx fctx = { 0 };
+	struct monitor_filter_arg mfa = { 0 };
+	int rc;
+	char buf[BUF_SIZE];
+	char *errmsg = (char *)malloc(BUF_SIZE);
+
+	fctx.filter_dimm = filter_dimm;
+	fctx.arg = &mfa;
+	mfa.flags = 0;
+	list_head_init(&mfa.mdimm);
+	FD_ZERO(&mfa.fds);
+
+	rc = util_filter_walk(ctx, &fctx, &param);
+	if (rc)
+		goto out;
+	if (mfa.num_dimm == 0) {
+		errmsg = "no monitor dimms can be found";
+		goto out;
+	}
+
+	while(1){
+		rc = select(mfa.maxfd + 1, NULL, NULL, &mfa.fds, NULL);
+		if (rc < 1) {
+			errmsg = "select error";
+			if (rc == 0)
+				dbg(ctx, "select unexpected timeout\n");
+			else
+				dbg(ctx, "select %s\n", strerror(errno));
+			goto out;
+		}
+		struct monitor_dimm *m_dimm;
+		list_for_each(&mfa.mdimm, m_dimm, list) {
+			if (!FD_ISSET(m_dimm->health_eventfd, &mfa.fds)) {
+				FD_SET(m_dimm->health_eventfd, &mfa.fds);
+				continue;
+			}
+			if (notify_json_msg(ctx, m_dimm->dimm) != 0)
+				goto out;
+			pread(m_dimm->health_eventfd, buf, sizeof(buf), 0);
+		}
+	}
+	return 0;
+out:
+	if (errmsg) {
+		if (!param.fork)
+			error("%s\n", errmsg);
+		err(ctx, "%s\n", errmsg);
+	}
+	return 1;
+}
+
+static int create_confile(char *conf_path)
+{
+	FILE *f;
+	char *buf;
+
+	if (!is_dir(conf_dir)) {
+		if (recur_mkdir(conf_dir, 0744) != 0) {
+			error("cannot create dir: %s\n", conf_dir);
+			goto out;
+		}
+	}
+
+	f = fopen(conf_path, "w");
+	if (!f) {
+		error("open %s failed\n", conf_path);
+		goto out;
+	}
+
+	buf = (char *)malloc(BUF_SIZE);
+	if (!buf) {
+		error("cannot get memory for daemon config file\n");
+		goto out;
+	}
+	strcpy(buf, "OPTIONS=-f");
+	if (param.bus) {
+		strcat(buf, " -b ");
+		strcat(buf, param.bus);
+	}
+	if (param.dimm) {
+		strcat(buf, " -d ");
+		strcat(buf, param.dimm);
+	}
+	if (param.namespace) {
+		strcat(buf, " -n ");
+		strcat(buf, param.namespace);
+	}
+	if (param.region) {
+		strcat(buf, " -r ");
+		strcat(buf, param.region);
+	}
+	if (param.log) {
+		strcat(buf, " -l ");
+		strcat(buf, param.log);
+	}
+	fprintf(f, "%s", buf);
+	fclose(f);
+	free(buf);
+	return 0;
+out:
+	return 1;
+}
+
+static bool is_monitor_exist(void)
+{
+	char *conf_path = strdup(param.daemon);
+	conf_path = concat(conf_dir, conf_path);
+	FILE *f = fopen(conf_path, "r");
+	if (f) {
+		fclose(f);
+		return true;
+	}
+	return false;
+}
+
+int cmd_monitor(int argc, const char **argv, void *ctx)
+{
+	const struct option options[] = {
+		OPT_STRING('b', "bus", &param.bus, "bus-id", "filter by bus"),
+		OPT_STRING('r', "region", &param.region, "region-id",
+				"filter by region"),
+		OPT_STRING('d', "dimm", &param.dimm, "dimm-id",
+				"filter by dimm"),
+		OPT_STRING('n', "namespace", &param.namespace,
+				"namespace-id", "filter by namespace id"),
+		OPT_STRING('l', "log", &param.log, "log name",
+				"monitor logfile"),
+		OPT_STRING('D',"daemon", &param.daemon, "daemon-name",
+				"run ndctl monitor as a daemon"),
+		OPT_BOOLEAN_HID('f', &param.fork),
+		OPT_END(),
+	};
+	const char * const u[] = {
+		"ndctl monitor [<options>]",
+		NULL
+	};
+	argc = parse_options(argc, argv, options, u, 0);
+	for (int i = 0; i < argc; i++) {
+		error("unknown parameter \"%s\"\n", argv[i]);
+		goto out;
+	}
+	if (argc)
+		usage_with_options(u, options);
+
+	if (param.daemon) {
+		if (is_monitor_exist()) {
+			error("monitor %s is exist\n", param.daemon);
+			goto out;
+		}
+		char *conf_path = strdup(param.daemon);
+		conf_path = concat(conf_dir, conf_path);
+		if (create_confile(conf_path) != 0)
+			goto out;
+		char *sys_cmd = (char *)malloc(BUF_SIZE);
+		sprintf(sys_cmd, "systemctl start ndctl-monitor@%s.service",
+				param.daemon);
+		if (system(sys_cmd) != 0) {
+			free(sys_cmd);
+			remove(conf_path);
+			goto out;
+		}
+		free(sys_cmd);
+		return 0;
+	}
+
+	if (param.fork) {
+		if (daemon(0, 0) != 0) {
+			err((struct ndctl_ctx*)ctx, "daemon start failed\n");
+			goto out;
+		}
+	}
+
+	ndctl_set_log_priority((struct ndctl_ctx*)ctx, LOG_NOTICE);
+
+	if (!param.log || strcmp(param.log, "syslog") == 0)
+		ndctl_set_log_fn((struct ndctl_ctx*)ctx, log_syslog);
+	else
+		ndctl_set_log_fn((struct ndctl_ctx*)ctx, log_file);
+
+	if (monitor_smart_event((struct ndctl_ctx*)ctx) != 0)
+		goto out;
+
+	return 0;
+out:
+	return 1;
+}
diff --git a/ndctl/ndctl-monitor.service b/ndctl/ndctl-monitor.service
new file mode 100644
index 0000000..2f1417b
--- /dev/null
+++ b/ndctl/ndctl-monitor.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Ndctl Monitor Daemon
+
+[Service]
+Type=forking
+ExecStart=/usr/bin/ndctl monitor -f
+ExecStop=/usr/bin/kill ${MAINPID}
diff --git a/ndctl/ndctl-monitor@.service b/ndctl/ndctl-monitor@.service
new file mode 100644
index 0000000..91c85d9
--- /dev/null
+++ b/ndctl/ndctl-monitor@.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=Ndctl Monitor Daemon
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/ndctl/%i
+ExecStart=/usr/bin/ndctl monitor $OPTIONS
+ExecStop=/usr/bin/kill ${MAINPID}
+ExecStopPost=/usr/bin/rm /etc/sysconfig/ndctl/%i
diff --git a/ndctl/ndctl.c b/ndctl/ndctl.c
index d3c6db1..8938621 100644
--- a/ndctl/ndctl.c
+++ b/ndctl/ndctl.c
@@ -86,6 +86,7 @@ static struct cmd_struct commands[] = {
 	{ "inject-error", cmd_inject_error },
 	{ "update-firmware", cmd_update_firmware },
 	{ "inject-smart", cmd_inject_smart },
+	{ "monitor", cmd_monitor },
 	{ "list", cmd_list },
 	{ "help", cmd_help },
 	#ifdef ENABLE_TEST
diff --git a/util/filter.c b/util/filter.c
index b0b7fdf..fba2197 100644
--- a/util/filter.c
+++ b/util/filter.c
@@ -315,7 +315,7 @@ int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx,
 				|| !util_bus_filter_by_namespace(bus, param->namespace))
 			continue;
 
-		if (!fctx->filter_bus(bus, fctx))
+		if (fctx->filter_bus && !fctx->filter_bus(bus, fctx))
 			continue;
 
 		ndctl_dimm_foreach(bus, dimm) {
@@ -345,7 +345,8 @@ int util_filter_walk(struct ndctl_ctx *ctx, struct util_filter_ctx *fctx,
 			if (type && ndctl_region_get_type(region) != type)
 				continue;
 
-			if (!fctx->filter_region(region, fctx))
+			if (fctx->filter_region &&
+					!fctx->filter_region(region, fctx))
 				continue;
 
 			ndctl_namespace_foreach(region, ndns) {
diff --git a/util/filter.h b/util/filter.h
index aea5a71..82f3b0d 100644
--- a/util/filter.h
+++ b/util/filter.h
@@ -77,6 +77,9 @@ struct util_filter_params {
 	const char *dimm;
 	const char *mode;
 	const char *namespace;
+	const char *log;
+	const char *daemon;
+	bool fork;
 };
 
 struct ndctl_ctx;
diff --git a/util/parse-options.h b/util/parse-options.h
index 6fd6b24..2262c42 100644
--- a/util/parse-options.h
+++ b/util/parse-options.h
@@ -123,6 +123,7 @@ struct option {
 #define OPT_GROUP(h)                { .type = OPTION_GROUP, .help = (h) }
 #define OPT_BIT(s, l, v, h, b)      { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
 #define OPT_BOOLEAN(s, l, v, h)     { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
+#define OPT_BOOLEAN_HID(s, v)       { .type = OPTION_BOOLEAN, .short_name = (s), .value = check_vtype(v, bool *), .flags = PARSE_OPT_HIDDEN}
 #define OPT_BOOLEAN_SET(s, l, v, os, h) \
 	{ .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \
 	.value = check_vtype(v, bool *), .help = (h), \
