Skip to content

Commit f5b5a16

Browse files
committed
Add sample notification program
The sample program is run like: ./samples/watch_queue/watch_test and watches "/" for mount changes and the current session keyring for key changes: # keyctl add user a a @s 1035096409 # keyctl unlink 1035096409 @s producing: # ./watch_test read() = 16 NOTIFY[000]: ty=000001 sy=02 i=00000110 KEY 2ffc2e5d change=2[linked] aux=1035096409 read() = 16 NOTIFY[000]: ty=000001 sy=02 i=00000110 KEY 2ffc2e5d change=3[unlinked] aux=1035096409 Other events may be produced, such as with a failing disk: read() = 22 NOTIFY[000]: ty=000003 sy=02 i=00000416 USB 3-7.7 dev-reset e=0 r=0 read() = 24 NOTIFY[000]: ty=000002 sy=06 i=00000418 BLOCK 00800050 e=6[critical medium] s=64000ef8 This corresponds to: blk_update_request: critical medium error, dev sdf, sector 1677725432 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 in dmesg. Signed-off-by: David Howells <[email protected]>
1 parent f7e4767 commit f5b5a16

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

samples/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,5 +190,11 @@ config SAMPLE_INTEL_MEI
190190
help
191191
Build a sample program to work with mei device.
192192

193+
config SAMPLE_WATCH_QUEUE
194+
bool "Build example /dev/watch_queue notification consumer"
195+
depends on HEADERS_INSTALL
196+
help
197+
Build example userspace program to use the new mount_notify(),
198+
sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
193199

194200
endif # SAMPLES

samples/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ obj-$(CONFIG_VIDEO_PCI_SKELETON) += v4l/
2424
obj-y += vfio-mdev/
2525
subdir-$(CONFIG_SAMPLE_VFS) += vfs
2626
obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/
27+
subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue

samples/watch_queue/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# List of programs to build
2+
hostprogs := watch_test
3+
4+
# Tell kbuild to always build the programs
5+
always-y := $(hostprogs)
6+
7+
HOSTCFLAGS_watch_test.o += -I$(objtree)/usr/include

samples/watch_queue/watch_test.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Use /dev/watch_queue to watch for notifications.
3+
*
4+
* Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
5+
* Written by David Howells ([email protected])
6+
*/
7+
8+
#define _GNU_SOURCE
9+
#include <stdbool.h>
10+
#include <stdarg.h>
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
#include <signal.h>
15+
#include <unistd.h>
16+
#include <errno.h>
17+
#include <sys/ioctl.h>
18+
#include <limits.h>
19+
#include <linux/watch_queue.h>
20+
#include <linux/unistd.h>
21+
#include <linux/keyctl.h>
22+
23+
#ifndef KEYCTL_WATCH_KEY
24+
#define KEYCTL_WATCH_KEY -1
25+
#endif
26+
#ifndef __NR_keyctl
27+
#define __NR_keyctl -1
28+
#endif
29+
30+
#define BUF_SIZE 256
31+
32+
static long keyctl_watch_key(int key, int watch_fd, int watch_id)
33+
{
34+
return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id);
35+
}
36+
37+
static const char *key_subtypes[256] = {
38+
[NOTIFY_KEY_INSTANTIATED] = "instantiated",
39+
[NOTIFY_KEY_UPDATED] = "updated",
40+
[NOTIFY_KEY_LINKED] = "linked",
41+
[NOTIFY_KEY_UNLINKED] = "unlinked",
42+
[NOTIFY_KEY_CLEARED] = "cleared",
43+
[NOTIFY_KEY_REVOKED] = "revoked",
44+
[NOTIFY_KEY_INVALIDATED] = "invalidated",
45+
[NOTIFY_KEY_SETATTR] = "setattr",
46+
};
47+
48+
static void saw_key_change(struct watch_notification *n, size_t len)
49+
{
50+
struct key_notification *k = (struct key_notification *)n;
51+
52+
if (len != sizeof(struct key_notification)) {
53+
fprintf(stderr, "Incorrect key message length\n");
54+
return;
55+
}
56+
57+
printf("KEY %08x change=%u[%s] aux=%u\n",
58+
k->key_id, n->subtype, key_subtypes[n->subtype], k->aux);
59+
}
60+
61+
/*
62+
* Consume and display events.
63+
*/
64+
static void consumer(int fd)
65+
{
66+
unsigned char buffer[4096], *p, *end;
67+
union {
68+
struct watch_notification n;
69+
unsigned char buf1[128];
70+
} n;
71+
ssize_t buf_len;
72+
73+
for (;;) {
74+
buf_len = read(fd, buffer, sizeof(buffer));
75+
if (buf_len == -1) {
76+
perror("read");
77+
exit(1);
78+
}
79+
80+
if (buf_len == 0) {
81+
printf("-- END --\n");
82+
return;
83+
}
84+
85+
if (buf_len > sizeof(buffer)) {
86+
fprintf(stderr, "Read buffer overrun: %zd\n", buf_len);
87+
return;
88+
}
89+
90+
printf("read() = %zd\n", buf_len);
91+
92+
p = buffer;
93+
end = buffer + buf_len;
94+
while (p < end) {
95+
size_t largest, len;
96+
97+
largest = end - p;
98+
if (largest > 128)
99+
largest = 128;
100+
if (largest < sizeof(struct watch_notification)) {
101+
fprintf(stderr, "Short message header: %zu\n", largest);
102+
return;
103+
}
104+
memcpy(&n, p, largest);
105+
106+
printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n",
107+
p - buffer, n.n.type, n.n.subtype, n.n.info);
108+
109+
len = n.n.info & WATCH_INFO_LENGTH;
110+
if (len < sizeof(n.n) || len > largest) {
111+
fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest);
112+
exit(1);
113+
}
114+
115+
switch (n.n.type) {
116+
case WATCH_TYPE_META:
117+
switch (n.n.subtype) {
118+
case WATCH_META_REMOVAL_NOTIFICATION:
119+
printf("REMOVAL of watchpoint %08x\n",
120+
(n.n.info & WATCH_INFO_ID) >>
121+
WATCH_INFO_ID__SHIFT);
122+
break;
123+
default:
124+
printf("other meta record\n");
125+
break;
126+
}
127+
break;
128+
case WATCH_TYPE_KEY_NOTIFY:
129+
saw_key_change(&n.n, len);
130+
break;
131+
default:
132+
printf("other type\n");
133+
break;
134+
}
135+
136+
p += len;
137+
}
138+
}
139+
}
140+
141+
static struct watch_notification_filter filter = {
142+
.nr_filters = 1,
143+
.filters = {
144+
[0] = {
145+
.type = WATCH_TYPE_KEY_NOTIFY,
146+
.subtype_filter[0] = UINT_MAX,
147+
},
148+
},
149+
};
150+
151+
int main(int argc, char **argv)
152+
{
153+
int pipefd[2], fd;
154+
155+
if (pipe2(pipefd, O_NOTIFICATION_PIPE) == -1) {
156+
perror("pipe2");
157+
exit(1);
158+
}
159+
fd = pipefd[0];
160+
161+
if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) {
162+
perror("watch_queue(size)");
163+
exit(1);
164+
}
165+
166+
if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) {
167+
perror("watch_queue(filter)");
168+
exit(1);
169+
}
170+
171+
if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) {
172+
perror("keyctl");
173+
exit(1);
174+
}
175+
176+
if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, fd, 0x02) == -1) {
177+
perror("keyctl");
178+
exit(1);
179+
}
180+
181+
consumer(fd);
182+
exit(0);
183+
}

0 commit comments

Comments
 (0)