Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/share/examples/inotify/inotify.c
39530 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2025 Klara, Inc.
5
*/
6
7
/*
8
* A simple program to demonstrate inotify. Given one or more paths, it watches
9
* all events on those paths and prints them to standard output.
10
*/
11
12
#include <sys/types.h>
13
#include <sys/event.h>
14
#include <sys/inotify.h>
15
16
#include <assert.h>
17
#include <err.h>
18
#include <limits.h>
19
#include <signal.h>
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <unistd.h>
23
24
#include <libxo/xo.h>
25
26
static void
27
usage(void)
28
{
29
xo_errx(1, "usage: inotify <path1> [<path2> ...]");
30
}
31
32
static const char *
33
ev2str(uint32_t event)
34
{
35
switch (event & IN_ALL_EVENTS) {
36
case IN_ACCESS:
37
return ("IN_ACCESS");
38
case IN_ATTRIB:
39
return ("IN_ATTRIB");
40
case IN_CLOSE_WRITE:
41
return ("IN_CLOSE_WRITE");
42
case IN_CLOSE_NOWRITE:
43
return ("IN_CLOSE_NOWRITE");
44
case IN_CREATE:
45
return ("IN_CREATE");
46
case IN_DELETE:
47
return ("IN_DELETE");
48
case IN_DELETE_SELF:
49
return ("IN_DELETE_SELF");
50
case IN_MODIFY:
51
return ("IN_MODIFY");
52
case IN_MOVE_SELF:
53
return ("IN_MOVE_SELF");
54
case IN_MOVED_FROM:
55
return ("IN_MOVED_FROM");
56
case IN_MOVED_TO:
57
return ("IN_MOVED_TO");
58
case IN_OPEN:
59
return ("IN_OPEN");
60
default:
61
switch (event) {
62
case IN_IGNORED:
63
return ("IN_IGNORED");
64
case IN_Q_OVERFLOW:
65
return ("IN_Q_OVERFLOW");
66
case IN_UNMOUNT:
67
return ("IN_UNMOUNT");
68
}
69
warnx("unknown event %#x", event);
70
assert(0);
71
}
72
}
73
74
static void
75
set_handler(int kq, int sig)
76
{
77
struct kevent kev;
78
79
(void)signal(sig, SIG_IGN);
80
EV_SET(&kev, sig, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
81
if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
82
xo_err(1, "kevent");
83
}
84
85
int
86
main(int argc, char **argv)
87
{
88
struct inotify_event *iev, *iev1;
89
struct kevent kev;
90
size_t ievsz;
91
int ifd, kq;
92
93
argc = xo_parse_args(argc, argv);
94
if (argc < 2)
95
usage();
96
argc--;
97
argv++;
98
99
ifd = inotify_init1(IN_NONBLOCK);
100
if (ifd < 0)
101
xo_err(1, "inotify");
102
for (int i = 0; i < argc; i++) {
103
int wd;
104
105
wd = inotify_add_watch(ifd, argv[i], IN_ALL_EVENTS);
106
if (wd < 0)
107
xo_err(1, "inotify_add_watch(%s)", argv[i]);
108
}
109
110
xo_set_version("1");
111
xo_open_list("events");
112
113
kq = kqueue();
114
if (kq < 0)
115
xo_err(1, "kqueue");
116
117
/*
118
* Handle signals in the event loop so that we can close the xo list.
119
*/
120
set_handler(kq, SIGINT);
121
set_handler(kq, SIGTERM);
122
set_handler(kq, SIGHUP);
123
set_handler(kq, SIGQUIT);
124
125
/*
126
* Monitor the inotify descriptor for events.
127
*/
128
EV_SET(&kev, ifd, EVFILT_READ, EV_ADD, 0, 0, NULL);
129
if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
130
xo_err(1, "kevent");
131
132
ievsz = sizeof(*iev) + NAME_MAX + 1;
133
iev = malloc(ievsz);
134
if (iev == NULL)
135
err(1, "malloc");
136
137
for (;;) {
138
ssize_t n;
139
const char *ev;
140
141
if (kevent(kq, NULL, 0, &kev, 1, NULL) < 0)
142
xo_err(1, "kevent");
143
if (kev.filter == EVFILT_SIGNAL)
144
break;
145
146
n = read(ifd, iev, ievsz);
147
if (n < 0)
148
xo_err(1, "read");
149
assert(n >= (ssize_t)sizeof(*iev));
150
151
for (iev1 = iev; n > 0;) {
152
assert(n >= (ssize_t)sizeof(*iev1));
153
154
ev = ev2str(iev1->mask);
155
xo_open_instance("event");
156
xo_emit("{:wd/%3d} {:event/%16s} {:name/%s}\n",
157
iev1->wd, ev, iev1->len > 0 ? iev1->name : "");
158
xo_close_instance("event");
159
160
n -= sizeof(*iev1) + iev1->len;
161
iev1 = (struct inotify_event *)(void *)
162
((char *)iev1 + sizeof(*iev1) + iev1->len);
163
}
164
(void)xo_flush();
165
}
166
167
xo_close_list("events");
168
169
if (xo_finish() < 0)
170
xo_err(1, "stdout");
171
exit(0);
172
}
173
174