| .\" |
| .\" Copyright (C) 2019 Red Hat, Inc. All Rights Reserved. |
| .\" Written by David Howells (dhowells@redhat.com) |
| .\" |
| .\" This program is free software; you can redistribute it and/or |
| .\" modify it under the terms of the GNU General Public Licence |
| .\" as published by the Free Software Foundation; either version |
| .\" 2 of the Licence, or (at your option) any later version. |
| .\" |
| .TH WATCH_QUEUE 7 "07 Aug 2019" Linux "Linux Programmer's Manual" |
| .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" |
| .SH NAME |
| /dev/watch_queue \- General kernel notification queue |
| .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" |
| .SH SYNOPSIS |
| #include <linux/watch_queue.h> |
| .EX |
| |
| pipe2(fds, O_NOTIFICATION_QUEUE); |
| ioctl(fds[0], IOC_WATCH_QUEUE_SET_SIZE, max_message_count); |
| ioctl(fds[0], IOC_WATCH_QUEUE_SET_FILTER, &filter); |
| .EE |
| .SH OVERVIEW |
| .PP |
| The general kernel notification queue is a general purpose transport for kernel |
| notification messages to userspace. Notification messages are marked with type |
| information so that events from multiple sources can be distinguished. |
| Messages are also of variable length to accommodate different information for |
| each type. |
| .PP |
| Queues are implemented on top of standard pipes and multiple independent queues |
| can be created. After creation, queues are configured with the size and |
| filtering, event sources are attached and then the pipe is read or polled to |
| wait for messages. |
| .PP |
| Multiple messages may be read out of the queue at a time if the buffer is large |
| enough, but messages will not get split amongst multiple reads. If the buffer |
| isn't large enough for a message, |
| .B ENOBUFS |
| will be returned. |
| .PP |
| In the case of message loss, |
| .BR read (2) |
| will fabricate a loss message and write that into the buffer immediately after |
| the point at which the loss occurred. |
| .PP |
| A notification pipe a certain amount of locked kernel memory (so that the |
| kernel can write a notification into it from contexts where swapping cannot be |
| performed), and so is subject to pipe resource limit restrictions. |
| .PP |
| Sources must be attached to a queue manually; there's no single global event |
| source, but rather a variety of sources, each of which can be attached to by |
| multiple queues. Attachments can be set up by: |
| .TP |
| .BR keyctl_watch_key (3) |
| Monitor a key or keyring for changes. |
| .TP |
| .BR device_notify (2) |
| Monitor a global source of device events from USB and block devices, such as |
| device detection, device removal and I/O errors. |
| .PP |
| Because a source can produce a lot of different events, not all of which may be |
| of interest to the watcher, a filter can be set on a queue to determine whether |
| a particular event will get inserted in a queue at the point of posting inside |
| the kernel. |
| |
| .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" |
| .SH MESSAGE STRUCTURE |
| .PP |
| The output from reading the pipe is divided into variable length messages. |
| Read will never split a message across two separate read calls. Each message |
| begins with a header of the form: |
| .PP |
| .in +4n |
| .EX |
| struct watch_notification { |
| __u32 type:24; |
| __u32 subtype:8; |
| __u32 info; |
| }; |
| .EE |
| .in |
| .PP |
| Where |
| .I type |
| indicates the general class of notification, |
| .I subtype |
| indicates the specific type of notification within that class and |
| .I info |
| includes the message length (in bytes), the watcher's ID and some type-specific |
| information. |
| .PP |
| In the event that the ring is full when the kernel needs to insert a |
| notification into the pipe and a message is lost, the reader will insert a |
| .BR WATCH_META_LOSS_NOTIFICATION -subtype |
| message after the message that was last in the pipe at the time the loss |
| occurred is read. |
| .PP |
| If a notification source goes away whilst it is being watched, a |
| .BR WATCH_META_REMOVAL_NOTIFICATION -subtype |
| message is posted to the queue. This comes in two lengths: a short variant |
| that carries just the header and a long variant that includes a 64-bit |
| identifier as well that identifies the source more precisely (the identifier is |
| source dependent). |
| |
| .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" |
| .SH IOCTL COMMANDS |
| The device has the following |
| .IR ioctl () |
| commands: |
| .TP |
| .B IOC_WATCH_QUEUE_SET_SIZE |
| The ioctl argument is indicates the maximum number of messages that can be |
| inserted into the pipe. This must be a power of two. This command also |
| pre-allocates memory to hold messages. |
| .IP |
| This may only be done once and the queue cannot be used until this command has |
| been done. |
| .TP |
| .B IOC_WATCH_QUEUE_SET_FILTER |
| This is used to set filters on the notifications that get written into the |
| buffer. The ioctl argument points to a structure of the following form: |
| .IP |
| .in +4n |
| .EX |
| struct watch_notification_filter { |
| __u32 nr_filters; |
| __u32 __reserved; |
| struct watch_notification_type_filter filters[]; |
| }; |
| .EE |
| .in |
| .IP |
| Where |
| .I nr_filters |
| indicates the number of elements in the |
| .IR filters [] |
| array. Each element in the filters array specifies a filter and is of the |
| following form: |
| .IP |
| .in +4n |
| .EX |
| struct watch_notification_type_filter { |
| __u32 type; |
| __u32 info_filter; |
| __u32 info_mask; |
| __u32 subtype_filter[8]; |
| }; |
| .EE |
| .in |
| .IP |
| Where |
| .I type |
| refer to the type field in a notification record header, info_filter and |
| info_mask refer to the info field and subtype_filter is a bit-mask of subtypes. |
| .IP |
| If no filters are installed, all notifications are allowed by default and if |
| one or more filters are installed, notifications are disallowed by default. |
| .IP |
| A notifications matches a filter if, for notification N and filter F: |
| .IP |
| .in +4n |
| .EX |
| N->type == F->type && |
| (F->subtype_filter[N->subtype >> 5] & |
| (1U << (N->subtype & 31))) && |
| (N->info & F->info_mask) == F->info_filter) |
| .EE |
| .in |
| .IP |
| |
| |
| .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" |
| .SH EXAMPLE |
| To use the notification mechanism, first of all the device has to be opened, |
| the size must be set: |
| .PP |
| .in +4n |
| .EX |
| int fds[2]; |
| pipe2(fd[0], O_NOTIFICATION_QUEUE); |
| int wfd = fd[0]; |
| |
| ioctl(wfd, IOC_WATCH_QUEUE_SET_SIZE, 1); |
| .EE |
| .in |
| .PP |
| From this point, the buffer is open for business. Filters can be set to |
| restrict the notifications that get inserted into the buffer from the sources |
| that are watched. For example: |
| .PP |
| .in +4n |
| .EX |
| static struct watch_notification_filter filter = { |
| .nr_filters = 2, |
| .__reserved = 0, |
| .filters = { |
| [0] = { |
| .type = WATCH_TYPE_KEY_NOTIFY, |
| .subtype_filter[0] = 1 << NOTIFY_KEY_LINKED, |
| .info_filter = 1 << WATCH_INFO_FLAG_2, |
| .info_mask = 1 << WATCH_INFO_FLAG_2, |
| }, |
| [1] = { |
| .type = WATCH_TYPE_USB_NOTIFY, |
| .subtype_filter[0] = 1 << NOTIFY_USB_DEVICE_ADD, |
| }, |
| }, |
| }; |
| |
| ioctl(wfd, IOC_WATCH_QUEUE_SET_FILTER, &filter); |
| .EE |
| .in |
| .PP |
| will only allow key-change notifications that indicate a key is linked into a |
| keyring and then only if type-specific flag WATCH_INFO_FLAG_2 is set on the |
| notification and will only allow USB device-add notifications, blocking other |
| USB notifications and all block device notifications. |
| .PP |
| Sources can then be watched, for example: |
| .PP |
| .in +4n |
| .EX |
| keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, wfd, 0x33); |
| watch_devices(wfd, 0x55, 0); |
| .EE |
| .in |
| .PP |
| The first places a watch on the process's session keyring, directing the |
| notifications to the buffer we just created and specifying that they should be |
| tagged with 0x33 in the info ID field. The second places a watch on the global |
| device notifications queue, specifying that notifications from that should be |
| tagged with info ID 0x55. |
| .PP |
| When it is determined that there is something in the buffer, messages can be |
| read out of the ring with something like the following: |
| .PP |
| .in +4n |
| .EX |
| for (;;) { |
| unsigned char buf[128]; |
| read(fd, buf, sizeof(buf)); |
| struct watch_notification *n = (struct watch_notification *)buf; |
| switch (n->type) { |
| case WATCH_TYPE_META: |
| switch (n->subtype) { |
| case WATCH_META_REMOVAL_NOTIFICATION: |
| saw_removal_notification(n); |
| break; |
| case WATCH_META_LOSS_NOTIFICATION: |
| printf("-- LOSS --\n"); |
| break; |
| } |
| break; |
| case WATCH_TYPE_KEY_NOTIFY: |
| saw_key_change(n); |
| break; |
| case WATCH_TYPE_BLOCK_NOTIFY: |
| saw_block_change(n); |
| break; |
| case WATCH_TYPE_USB_NOTIFY: |
| saw_usb_event(n); |
| break; |
| } |
| } |
| .EE |
| .in |
| .PP |
| |
| .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" |
| .SH VERSIONS |
| The notification queue driver first appeared in v??? of the Linux kernel. |
| .SH SEE ALSO |
| .ad l |
| .nh |
| .BR keyctl (1), |
| .BR ioctl (2), |
| .BR read (2), |
| .BR keyctl_watch_key (3) |