The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=1d8664d6904149e0be5dbfa0ee35268de2a83f1e

commit 1d8664d6904149e0be5dbfa0ee35268de2a83f1e
Author:     Mark Johnston <ma...@freebsd.org>
AuthorDate: 2025-07-03 20:00:55 +0000
Commit:     Mark Johnston <ma...@freebsd.org>
CommitDate: 2025-07-04 14:42:34 +0000

    examples: Add a demo program for inotify
    
    MFC after:      3 months
    Sponsored by:   Klara, Inc.
---
 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 97b555e50dc1..ffdd82ae9911 100644
--- a/etc/mtree/BSD.usr.dist
+++ b/etc/mtree/BSD.usr.dist
@@ -304,6 +304,8 @@
             ..
             indent
             ..
+            inotify
+            ..
             ipfilter
             ..
             ipfw
diff --git a/share/examples/Makefile b/share/examples/Makefile
index 560fdae6de5b..f0c050a36306 100644
--- a/share/examples/Makefile
+++ b/share/examples/Makefile
@@ -15,6 +15,7 @@ LDIRS=        BSD_daemon \
        find_interface \
        flua \
        indent \
+       inotify \
        ipfw \
        jails \
        kld \
@@ -97,6 +98,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);
+}

Reply via email to