From b533e4255a0ab0e267d456a2577082c9ec1d8b39 Mon Sep 17 00:00:00 2001 From: Michael Haberler Date: Wed, 26 Sep 2012 13:50:10 +0200 Subject: [PATCH] demo program for using ZMQ_IGNERR patch also contains a named pipe reading example Signed-off-by: Michael Haberler --- demo/Makefile | 23 +++++++++++ demo/README | 58 +++++++++++++++++++++++++++ demo/sysfsexample.c | 91 ++++++++++++++++++++++++++++++++++++++++++ demo/zloop-namedpipe.c | 82 +++++++++++++++++++++++++++++++++++++ demo/zloop-sysfs.c | 83 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 demo/Makefile create mode 100644 demo/README create mode 100644 demo/sysfsexample.c create mode 100644 demo/zloop-namedpipe.c create mode 100644 demo/zloop-sysfs.c diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 000000000..f8d45568a --- /dev/null +++ b/demo/Makefile @@ -0,0 +1,23 @@ +CZMQ_FLAGS := $(shell pkg-config libczmq libzmq --cflags --libs) + +all: zloop-sysfs zloop-namedpipe sysfsexample.ko + + +zloop-namedpipe: zloop-namedpipe.c + gcc -g $(CZMQ_FLAGS) -o $@ $^ + + + +zloop-sysfs: zloop-sysfs.c + gcc -g $(CZMQ_FLAGS) -o $@ $^ + +obj-m := sysfsexample.o + +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +sysfsexample.ko: sysfsexample.c + make M=$(PWD) -C $(KDIR) + +clean: + rm -f zloop-sysfs zloop-namedpipe sysfsexample.ko *.o modules.order Module.symvers diff --git a/demo/README b/demo/README new file mode 100644 index 000000000..67a987fcd --- /dev/null +++ b/demo/README @@ -0,0 +1,58 @@ +This directory contains two zloop examples: + + +zloop-sysfs: +------------ +Example monitoring a sysfs device through a file descriptor zmq_pollitem. + + +sysfsexample kernel module: +--------------------------- +this creates two sysfs entries: +- /sys/syfs_example/notify +- /sys/syfs_example/trigger + +to activate: + +$ insmod ./sysfsexample.ko + +Verify with dmesg that it has been properly loaded. + +An int value written to /sys/syfs_example/trigger will be returned by +reading /sys/syfs_example/notify; also a sysfs_notify() is executed on +/sys/syfs_example/notify to signal to a user process that new content is available. + +then start zloop-sysfs like so: + +$ zloop-sysfs /sys/sysfs_example/notify + +then try a few times +# echo 123 > /sys/syfs_example/trigger + +this will cause a sysfs_notify() on /sys/sysfs_example/notify; note that only +the first echo will cause zloop-sysfs to react since the handler is disabled +by czmq as a reaction to the POLLERR delivered by the sysfs event. + +To demonstrate the ZMQ_IGNERR patch, try like so: + + +$ zloop-sysfs /sys/sysfs_example/notify 1 + +# NB: the extra argument will activate the ZMQ_IGNERR option + +now try again a few times, which should work fine: +# echo 123 > /sys/syfs_example/trigger + +The handler will not be disabled any more. + + +zloop-namedpipe: +------------ +Example monitoring a named pipe device through a file descriptor zmq_pollitem. +This has nothing to do with the ZMQ_IGNERR patch per se - it just demonstrates handling +reading, and dealing with a closed named pipe which is a bit tricky. + +To try: + +$ mknod /tmp/pipe p +$ zloop-namedpip /tmp/pipe diff --git a/demo/sysfsexample.c b/demo/sysfsexample.c new file mode 100644 index 000000000..206fe282d --- /dev/null +++ b/demo/sysfsexample.c @@ -0,0 +1,91 @@ +//http://godandme.wordpress.com/2011/04/05/how-to-make-a-sysfs-entry/ +#include +#include +#include +#include +#include + +struct my_attr { + struct attribute attr; + int value; +}; + +static struct my_attr notify = { + .attr.name="notify", + .attr.mode = 0644, + .value = 0, +}; + +static struct my_attr trigger = { + .attr.name="trigger", + .attr.mode = 0644, + .value = 0, +}; + +static struct attribute * myattr[] = { + ¬ify.attr, + &trigger.attr, + NULL +}; + +static ssize_t default_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct my_attr *a = container_of(attr, struct my_attr, attr); + return scnprintf(buf, PAGE_SIZE, "%d\n", a->value); +} +static struct kobject *mykobj; + +static ssize_t default_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct my_attr *a = container_of(attr, struct my_attr, attr); + + sscanf(buf, "%d", &a->value); + notify.value = a->value; + printk("sysfs_notify store %s = %d\n", a->attr.name, a->value); + sysfs_notify(mykobj, NULL, "notify"); + return sizeof(int); +} + +static struct sysfs_ops myops = { + .show = default_show, + .store = default_store, +}; + +static struct kobj_type mytype = { + .sysfs_ops = &myops, + .default_attrs = myattr, +}; + +static struct kobject *mykobj; +static int __init sysfsexample_module_init(void) +{ + int err = -1; + printk("sysfs_notify init\n"); + mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL); + if (mykobj) { + kobject_init(mykobj, &mytype); + if (kobject_add(mykobj, NULL, "%s", "sysfs_sample")) { + err = -1; + printk("sysfs_notify: kobject_add() failed\n"); + kobject_put(mykobj); + mykobj = NULL; + } + err = 0; + } + return err; +} + +static void __exit sysfsexample_module_exit(void) +{ + if (mykobj) { + kobject_put(mykobj); + kfree(mykobj); + } + printk("sysfs_notify exit\n"); +} + +module_init(sysfsexample_module_init); +module_exit(sysfsexample_module_exit); +MODULE_LICENSE("GPL"); diff --git a/demo/zloop-namedpipe.c b/demo/zloop-namedpipe.c new file mode 100644 index 000000000..8a7b48c94 --- /dev/null +++ b/demo/zloop-namedpipe.c @@ -0,0 +1,82 @@ +#include "czmq.h" + +static char *pipe_name; +static int pipe_fd; +static void *ctx; +static zloop_t *loop; + + +int s_handle_pipe(zloop_t *loop, zmq_pollitem_t *poller, void *arg) +{ + char buffer[20], *endptr; + unsigned eventmask; + int retval; + + printf("s_handle_pipe revents=0x%x\n", poller->revents); + + // if 'echo 123 >pipe' is executed, this handler is called twice: + // - first time when there's actually something to read + // - a second time when the pipe write side is closed + + // depending on how libzmq is configured, revents may be subtly different + // on pipe write side close: + // if configured with --with-poller=poll|epoll, a ZMQ_POLLERR is signaled + // if configured with --with-poller=select, a ZMQ_POLLIN is signaled + + if (poller->revents & (ZMQ_POLLIN|ZMQ_POLLERR)) { + // check if pipe readable + retval = read(poller->fd, buffer, sizeof(buffer)); + if (retval > 0) { + printf("s_handle_pipe read(%d): '%.*s'\n", retval, retval, buffer); + } + if ((retval == 0) || (poller->revents & ZMQ_POLLERR)) { + printf("s_handle_pipe: pipe closed - re-registering poller\n"); + + // the write side was closed. + zloop_poller_end(loop, poller); + // close & reopen pipe, + close(poller->fd); + poller->fd = pipe_fd = open(pipe_name, O_RDONLY|O_NONBLOCK); + if (poller->fd < 0) { + // real bad - pipe removed? + printf("ERROR: reopening pipe %s : %s - disabling pipe notifications\n", + pipe_name, + strerror (errno)); + return 0; + } + // reestablish zloop poller. + zloop_poller(loop, poller, s_handle_pipe, NULL); + + } else if (retval < 0) { + // 'should not happen' + printf("ERROR: read(%s): %s - disabling pipe notifications\n", + pipe_name, + strerror (errno)); + } + } + return 0; +} + + +int main (int argc, char *argv[]) +{ + int i, retval; + + pipe_name = argv[1]; + + ctx = zctx_new (); + assert(ctx != NULL); + + pipe_fd = open(pipe_name, O_RDONLY|O_NONBLOCK); + assert(pipe_fd >= 0); + + loop = zloop_new(); + assert (loop); + zloop_set_verbose (loop, 1); + + zmq_pollitem_t pipe_poller = { 0, pipe_fd, ZMQ_POLLIN }; + + zloop_poller (loop, &pipe_poller, s_handle_pipe, 0); + zloop_start(loop); + exit(0); +} diff --git a/demo/zloop-sysfs.c b/demo/zloop-sysfs.c new file mode 100644 index 000000000..4d8b93f13 --- /dev/null +++ b/demo/zloop-sysfs.c @@ -0,0 +1,83 @@ +// example of monitoring a sysfs entry with a zloop reactor +// +// sysfs has a way of notifying a user process that the content of a sysfs +// file has changed - the kernel function sysfs_notify() makes a +// sysfs entry pollable. +// +// sysfs_notify() will make a poll(2) return with the POLLPRI and POLLERR +// event bits set. However, by default czmq will interpret POLLERR +// on a file descriptor as fatal and disable the zloop callback, resulting +// in a once-only notification on a sysfs entry. +// +// the new ZMQ_IGNERR flag can be set in zmq_pollitem_t.events to suppress +// this behaviour. +// +// see also: http://lwn.net/Articles/174660/ for background on sysfs_notify() +// + + + +#include "czmq.h" + + +static char *sysfs_name; +static int sysfs_fd; +static void *ctx; +static zloop_t *loop; + +int s_handle_sysfs(zloop_t *loop, zmq_pollitem_t *poller, void *arg) +{ + char buffer[256], *endptr; + unsigned eventmask; + int retval; + + printf("s_handle_sysfs revents=0x%x\n", poller->revents); + + // with a sysfs device, poll returns POLLPRI|POLLERR which is mapped + // to ZMQ_POLLERR by libzmq; same for select() version of libzmq. + + if (poller->revents & ZMQ_POLLERR) { + lseek(poller->fd, 0, SEEK_SET); + retval = read(poller->fd, buffer, sizeof(buffer)); + if (retval > 0) { + printf("s_handle_sysfs read(%d): '%.*s'\n", retval, retval, buffer); + } else { + // pretty bad, but not much to do about it + printf("reading sysfs entry %s - %s : disabling sysfs notifications\n", + sysfs_name, strerror(retval)); + zloop_poller_end (loop, poller); + } + } + return 0; +} + +int main (int argc, char *argv[]) +{ + int i, retval; + + assert(argc > 1); + sysfs_name = argv[1]; + + ctx = zctx_new (); + assert(ctx != NULL); + + sysfs_fd = open(sysfs_name, O_RDONLY); + assert(sysfs_fd >= 0); + + loop = zloop_new(); + assert (loop); + zloop_set_verbose (loop, 1); + + zmq_pollitem_t sysfs_poller = { 0, sysfs_fd, ZMQ_POLLERR }; + if (argc > 2) { +#ifdef ZMQ_IGNERR + sysfs_poller.events |= ZMQ_IGNERR; + printf("ZMQ_IGNERR set on sysfs_poller\n"); +#else + printf("ZMQ_IGNERR not available in this czmq version.\n"); +#endif + } + zloop_poller (loop, &sysfs_poller, s_handle_sysfs, 0); + zloop_start(loop); + exit(0); +}