git: 03f565116b7d - stable/14 - examples: Add a demo program for inotify

Mark Johnston <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a7353d1.45055.20b12063__11181.8830062234$1785943157$gmane$org@gitrepo.freebsd.org>
The branch stable/14 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=03f565116b7d015a479a5c37257cc9e6a38685c7

commit 03f565116b7d015a479a5c37257cc9e6a38685c7
Author:     Mark Johnston <[email protected]>
AuthorDate: 2025-07-03 20:00:55 +0000
Commit:     Mark Johnston <[email protected]>
CommitDate: 2026-08-05 15:15:15 +0000

    examples: Add a demo program for inotify
    
    MFC after:      3 months
    Sponsored by:   Klara, Inc.
    
    (cherry picked from commit 1d8664d6904149e0be5dbfa0ee35268de2a83f1e)
---
 etc/mtree/BSD.usr.dist           |   2 +
 share/examples/Makefile          |   5 ++
 share/examples/inotify/Makefile  |   6 ++
 share/examples/inotify/inotify.c | 172 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 185 insertions(+)

diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist
index fddbbdd94a6c..654a9408430c 100644
--- a/etc/mtree/BSD.usr.dist
+++ b/etc/mtree/BSD.usr.dist
@@ -288,6 +288,8 @@
             ..
             indent
             ..
+            inotify
+            ..
             ipfilter
             ..
             ipfw
diff --git a/share/examples/Makefile b/share/examples/Makefile
index 9029cbad5900..0cb3b97913cb 100644
--- a/share/examples/Makefile
+++ b/share/examples/Makefile
@@ -15,6 +15,7 @@ LDIRS=	BSD_daemon \
 	find_interface \
 	flua \
 	indent \
+	inotify \
 	ipfw \
 	jails \
 	kld \
@@ -91,6 +92,10 @@ SE_FLUA=	libjail.lua
 SE_DIRS+=	indent
 SE_INDENT=	indent.pro
 
+SE_DIRS+=	inotify
+SE_INOTIFY=	inotify.c \
+		Makefile
+
 .if ${MK_IPFILTER} != "no"
 SUBDIR+=	ipfilter
 .endif
diff --git a/share/examples/inotify/Makefile b/share/examples/inotify/Makefile
new file mode 100644
index 000000000000..c54c629c58d7
--- /dev/null
+++ b/share/examples/inotify/Makefile
@@ -0,0 +1,6 @@
+PROG= inotify
+MAN=
+
+LIBADD=	xo
+
+.include <bsd.prog.mk>
diff --git a/share/examples/inotify/inotify.c b/share/examples/inotify/inotify.c
new file mode 100644
index 000000000000..b8e300bc992c
--- /dev/null
+++ b/share/examples/inotify/inotify.c
@@ -0,0 +1,172 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Klara, Inc.
+ */
+
+/*
+ * A simple program to demonstrate inotify.  Given one or more paths, it watches
+ * all events on those paths and prints them to standard output.
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/inotify.h>
+
+#include <assert.h>
+#include <err.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libxo/xo.h>
+
+static void
+usage(void)
+{
+	xo_errx(1, "usage: inotify <path1> [<path2> ...]");
+}
+
+static const char *
+ev2str(uint32_t event)
+{
+	switch (event & IN_ALL_EVENTS) {
+	case IN_ACCESS:
+		return ("IN_ACCESS");
+	case IN_ATTRIB:
+		return ("IN_ATTRIB");
+	case IN_CLOSE_WRITE:
+		return ("IN_CLOSE_WRITE");
+	case IN_CLOSE_NOWRITE:
+		return ("IN_CLOSE_NOWRITE");
+	case IN_CREATE:
+		return ("IN_CREATE");
+	case IN_DELETE:
+		return ("IN_DELETE");
+	case IN_DELETE_SELF:
+		return ("IN_DELETE_SELF");
+	case IN_MODIFY:
+		return ("IN_MODIFY");
+	case IN_MOVE_SELF:
+		return ("IN_MOVE_SELF");
+	case IN_MOVED_FROM:
+		return ("IN_MOVED_FROM");
+	case IN_MOVED_TO:
+		return ("IN_MOVED_TO");
+	case IN_OPEN:
+		return ("IN_OPEN");
+	default:
+		switch (event) {
+		case IN_IGNORED:
+			return ("IN_IGNORED");
+		case IN_Q_OVERFLOW:
+			return ("IN_Q_OVERFLOW");
+		case IN_UNMOUNT:
+			return ("IN_UNMOUNT");
+		}
+		warnx("unknown event %#x", event);
+		assert(0);
+	}
+}
+
+static void
+set_handler(int kq, int sig)
+{
+	struct kevent kev;
+
+	(void)signal(sig, SIG_IGN);
+	EV_SET(&kev, sig, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+	if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
+		xo_err(1, "kevent");
+}
+
+int
+main(int argc, char **argv)
+{
+	struct inotify_event *iev, *iev1;
+	struct kevent kev;
+	size_t ievsz;
+	int ifd, kq;
+
+	argc = xo_parse_args(argc, argv);
+	if (argc < 2)
+		usage();
+	argc--;
+	argv++;
+
+	ifd = inotify_init1(IN_NONBLOCK);
+	if (ifd < 0)
+		xo_err(1, "inotify");
+	for (int i = 0; i < argc; i++) {
+		int wd;
+
+		wd = inotify_add_watch(ifd, argv[i], IN_ALL_EVENTS);
+		if (wd < 0)
+			xo_err(1, "inotify_add_watch(%s)", argv[i]);
+	}
+
+	xo_set_version("1");
+	xo_open_list("events");
+
+	kq = kqueue();
+	if (kq < 0)
+		xo_err(1, "kqueue");
+
+	/*
+	 * Handle signals in the event loop so that we can close the xo list.
+	 */
+	set_handler(kq, SIGINT);
+	set_handler(kq, SIGTERM);
+	set_handler(kq, SIGHUP);
+	set_handler(kq, SIGQUIT);
+
+	/*
+	 * Monitor the inotify descriptor for events.
+	 */
+	EV_SET(&kev, ifd, EVFILT_READ, EV_ADD, 0, 0, NULL);
+	if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
+		xo_err(1, "kevent");
+
+	ievsz = sizeof(*iev) + NAME_MAX + 1;
+	iev = malloc(ievsz);
+	if (iev == NULL)
+		err(1, "malloc");
+
+	for (;;) {
+		ssize_t n;
+		const char *ev;
+
+		if (kevent(kq, NULL, 0, &kev, 1, NULL) < 0)
+			xo_err(1, "kevent");
+		if (kev.filter == EVFILT_SIGNAL)
+			break;
+
+		n = read(ifd, iev, ievsz);
+		if (n < 0)
+			xo_err(1, "read");
+		assert(n >= (ssize_t)sizeof(*iev));
+
+		for (iev1 = iev; n > 0;) {
+			assert(n >= (ssize_t)sizeof(*iev1));
+
+			ev = ev2str(iev1->mask);
+			xo_open_instance("event");
+			xo_emit("{:wd/%3d} {:event/%16s} {:name/%s}\n",
+			    iev1->wd, ev, iev1->name);
+			xo_close_instance("event");
+
+			n -= sizeof(*iev1) + iev1->len;
+			iev1 = (struct inotify_event *)(void *)
+			    ((char *)iev1 + sizeof(*iev1) + iev1->len);
+		}
+		(void)xo_flush();
+	}
+
+	xo_close_list("events");
+
+	if (xo_finish() < 0)
+		xo_err(1, "stdout");
+	exit(0);
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.