1219019Sgabor// SPDX-License-Identifier: GPL-2.0 2219019Sgabor/* Use watch_queue API to watch for notifications. 3219019Sgabor * 4219019Sgabor * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. 5219019Sgabor * Written by David Howells (dhowells@redhat.com) 6219019Sgabor */ 7219019Sgabor 8219019Sgabor#define _GNU_SOURCE 9219019Sgabor#include <stdbool.h> 10219019Sgabor#include <stdarg.h> 11219019Sgabor#include <stdio.h> 12219019Sgabor#include <stdlib.h> 13219019Sgabor#include <string.h> 14219019Sgabor#include <signal.h> 15219019Sgabor#include <unistd.h> 16219019Sgabor#include <errno.h> 17219019Sgabor#include <sys/ioctl.h> 18219019Sgabor#include <limits.h> 19219019Sgabor#include <linux/watch_queue.h> 20219019Sgabor#include <linux/unistd.h> 21219019Sgabor#include <linux/keyctl.h> 22219019Sgabor 23219019Sgabor#ifndef KEYCTL_WATCH_KEY 24219019Sgabor#define KEYCTL_WATCH_KEY -1 25219019Sgabor#endif 26219019Sgabor#ifndef __NR_keyctl 27219019Sgabor#define __NR_keyctl -1 28219019Sgabor#endif 29219019Sgabor 30219019Sgabor#define BUF_SIZE 256 31219019Sgabor 32219019Sgaborstatic long keyctl_watch_key(int key, int watch_fd, int watch_id) 33219019Sgabor{ 34219019Sgabor return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id); 35219019Sgabor} 36219019Sgabor 37219019Sgaborstatic const char *key_subtypes[256] = { 38219019Sgabor [NOTIFY_KEY_INSTANTIATED] = "instantiated", 39219019Sgabor [NOTIFY_KEY_UPDATED] = "updated", 40219019Sgabor [NOTIFY_KEY_LINKED] = "linked", 41219019Sgabor [NOTIFY_KEY_UNLINKED] = "unlinked", 42219019Sgabor [NOTIFY_KEY_CLEARED] = "cleared", 43219019Sgabor [NOTIFY_KEY_REVOKED] = "revoked", 44219019Sgabor [NOTIFY_KEY_INVALIDATED] = "invalidated", 45219019Sgabor [NOTIFY_KEY_SETATTR] = "setattr", 46219019Sgabor}; 47219019Sgabor 48219019Sgaborstatic void saw_key_change(struct watch_notification *n, size_t len) 49219019Sgabor{ 50219019Sgabor struct key_notification *k = (struct key_notification *)n; 51219019Sgabor 52219019Sgabor if (len != sizeof(struct key_notification)) { 53219019Sgabor fprintf(stderr, "Incorrect key message length\n"); 54219019Sgabor return; 55219019Sgabor } 56219019Sgabor 57219019Sgabor printf("KEY %08x change=%u[%s] aux=%u\n", 58219019Sgabor k->key_id, n->subtype, key_subtypes[n->subtype], k->aux); 59219019Sgabor} 60219019Sgabor 61219019Sgabor/* 62219019Sgabor * Consume and display events. 63219019Sgabor */ 64219019Sgaborstatic void consumer(int fd) 65219019Sgabor{ 66219019Sgabor unsigned char buffer[433], *p, *end; 67219019Sgabor union { 68219019Sgabor struct watch_notification n; 69219019Sgabor unsigned char buf1[128]; 70219019Sgabor } n; 71219019Sgabor ssize_t buf_len; 72282275Stijl 73219019Sgabor for (;;) { 74219019Sgabor buf_len = read(fd, buffer, sizeof(buffer)); 75219019Sgabor if (buf_len == -1) { 76219019Sgabor perror("read"); 77219019Sgabor exit(1); 78219019Sgabor } 79219019Sgabor 80219019Sgabor if (buf_len == 0) { 81219019Sgabor printf("-- END --\n"); 82219019Sgabor return; 83219019Sgabor } 84219019Sgabor 85219019Sgabor if (buf_len > sizeof(buffer)) { 86219019Sgabor fprintf(stderr, "Read buffer overrun: %zd\n", buf_len); 87219019Sgabor return; 88219019Sgabor } 89219019Sgabor 90219019Sgabor printf("read() = %zd\n", buf_len); 91219019Sgabor 92219019Sgabor p = buffer; 93219019Sgabor end = buffer + buf_len; 94219019Sgabor while (p < end) { 95219019Sgabor size_t largest, len; 96219019Sgabor 97219019Sgabor largest = end - p; 98219019Sgabor if (largest > 128) 99219019Sgabor largest = 128; 100219019Sgabor if (largest < sizeof(struct watch_notification)) { 101219019Sgabor fprintf(stderr, "Short message header: %zu\n", largest); 102219019Sgabor return; 103219019Sgabor } 104219019Sgabor memcpy(&n, p, largest); 105219019Sgabor 106219019Sgabor printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n", 107219019Sgabor p - buffer, n.n.type, n.n.subtype, n.n.info); 108219019Sgabor 109219019Sgabor len = n.n.info & WATCH_INFO_LENGTH; 110219019Sgabor if (len < sizeof(n.n) || len > largest) { 111219019Sgabor fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest); 112219019Sgabor exit(1); 113219019Sgabor } 114219019Sgabor 115219019Sgabor switch (n.n.type) { 116219019Sgabor case WATCH_TYPE_META: 117219019Sgabor switch (n.n.subtype) { 118219019Sgabor case WATCH_META_REMOVAL_NOTIFICATION: 119219019Sgabor printf("REMOVAL of watchpoint %08x\n", 120219019Sgabor (n.n.info & WATCH_INFO_ID) >> 121219019Sgabor WATCH_INFO_ID__SHIFT); 122219019Sgabor break; 123219019Sgabor case WATCH_META_LOSS_NOTIFICATION: 124219019Sgabor printf("-- LOSS --\n"); 125 break; 126 default: 127 printf("other meta record\n"); 128 break; 129 } 130 break; 131 case WATCH_TYPE_KEY_NOTIFY: 132 saw_key_change(&n.n, len); 133 break; 134 default: 135 printf("other type\n"); 136 break; 137 } 138 139 p += len; 140 } 141 } 142} 143 144static struct watch_notification_filter filter = { 145 .nr_filters = 1, 146 .filters = { 147 [0] = { 148 .type = WATCH_TYPE_KEY_NOTIFY, 149 .subtype_filter[0] = UINT_MAX, 150 }, 151 }, 152}; 153 154int main(int argc, char **argv) 155{ 156 int pipefd[2], fd; 157 158 if (pipe2(pipefd, O_NOTIFICATION_PIPE) == -1) { 159 perror("pipe2"); 160 exit(1); 161 } 162 fd = pipefd[0]; 163 164 if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) { 165 perror("watch_queue(size)"); 166 exit(1); 167 } 168 169 if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) { 170 perror("watch_queue(filter)"); 171 exit(1); 172 } 173 174 if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) { 175 perror("keyctl"); 176 exit(1); 177 } 178 179 if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, fd, 0x02) == -1) { 180 perror("keyctl"); 181 exit(1); 182 } 183 184 consumer(fd); 185 exit(0); 186} 187