18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Use watch_queue API to watch for notifications. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define _GNU_SOURCE 98c2ecf20Sopenharmony_ci#include <stdbool.h> 108c2ecf20Sopenharmony_ci#include <stdarg.h> 118c2ecf20Sopenharmony_ci#include <stdio.h> 128c2ecf20Sopenharmony_ci#include <stdlib.h> 138c2ecf20Sopenharmony_ci#include <string.h> 148c2ecf20Sopenharmony_ci#include <signal.h> 158c2ecf20Sopenharmony_ci#include <unistd.h> 168c2ecf20Sopenharmony_ci#include <errno.h> 178c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 188c2ecf20Sopenharmony_ci#include <limits.h> 198c2ecf20Sopenharmony_ci#include <linux/watch_queue.h> 208c2ecf20Sopenharmony_ci#include <linux/unistd.h> 218c2ecf20Sopenharmony_ci#include <linux/keyctl.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#ifndef KEYCTL_WATCH_KEY 248c2ecf20Sopenharmony_ci#define KEYCTL_WATCH_KEY -1 258c2ecf20Sopenharmony_ci#endif 268c2ecf20Sopenharmony_ci#ifndef __NR_keyctl 278c2ecf20Sopenharmony_ci#define __NR_keyctl -1 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define BUF_SIZE 256 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic long keyctl_watch_key(int key, int watch_fd, int watch_id) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const char *key_subtypes[256] = { 388c2ecf20Sopenharmony_ci [NOTIFY_KEY_INSTANTIATED] = "instantiated", 398c2ecf20Sopenharmony_ci [NOTIFY_KEY_UPDATED] = "updated", 408c2ecf20Sopenharmony_ci [NOTIFY_KEY_LINKED] = "linked", 418c2ecf20Sopenharmony_ci [NOTIFY_KEY_UNLINKED] = "unlinked", 428c2ecf20Sopenharmony_ci [NOTIFY_KEY_CLEARED] = "cleared", 438c2ecf20Sopenharmony_ci [NOTIFY_KEY_REVOKED] = "revoked", 448c2ecf20Sopenharmony_ci [NOTIFY_KEY_INVALIDATED] = "invalidated", 458c2ecf20Sopenharmony_ci [NOTIFY_KEY_SETATTR] = "setattr", 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void saw_key_change(struct watch_notification *n, size_t len) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct key_notification *k = (struct key_notification *)n; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (len != sizeof(struct key_notification)) { 538c2ecf20Sopenharmony_ci fprintf(stderr, "Incorrect key message length\n"); 548c2ecf20Sopenharmony_ci return; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci printf("KEY %08x change=%u[%s] aux=%u\n", 588c2ecf20Sopenharmony_ci k->key_id, n->subtype, key_subtypes[n->subtype], k->aux); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * Consume and display events. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic void consumer(int fd) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci unsigned char buffer[433], *p, *end; 678c2ecf20Sopenharmony_ci union { 688c2ecf20Sopenharmony_ci struct watch_notification n; 698c2ecf20Sopenharmony_ci unsigned char buf1[128]; 708c2ecf20Sopenharmony_ci } n; 718c2ecf20Sopenharmony_ci ssize_t buf_len; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci for (;;) { 748c2ecf20Sopenharmony_ci buf_len = read(fd, buffer, sizeof(buffer)); 758c2ecf20Sopenharmony_ci if (buf_len == -1) { 768c2ecf20Sopenharmony_ci perror("read"); 778c2ecf20Sopenharmony_ci exit(1); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (buf_len == 0) { 818c2ecf20Sopenharmony_ci printf("-- END --\n"); 828c2ecf20Sopenharmony_ci return; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (buf_len > sizeof(buffer)) { 868c2ecf20Sopenharmony_ci fprintf(stderr, "Read buffer overrun: %zd\n", buf_len); 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci printf("read() = %zd\n", buf_len); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci p = buffer; 938c2ecf20Sopenharmony_ci end = buffer + buf_len; 948c2ecf20Sopenharmony_ci while (p < end) { 958c2ecf20Sopenharmony_ci size_t largest, len; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci largest = end - p; 988c2ecf20Sopenharmony_ci if (largest > 128) 998c2ecf20Sopenharmony_ci largest = 128; 1008c2ecf20Sopenharmony_ci if (largest < sizeof(struct watch_notification)) { 1018c2ecf20Sopenharmony_ci fprintf(stderr, "Short message header: %zu\n", largest); 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci memcpy(&n, p, largest); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n", 1078c2ecf20Sopenharmony_ci p - buffer, n.n.type, n.n.subtype, n.n.info); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci len = n.n.info & WATCH_INFO_LENGTH; 1108c2ecf20Sopenharmony_ci if (len < sizeof(n.n) || len > largest) { 1118c2ecf20Sopenharmony_ci fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest); 1128c2ecf20Sopenharmony_ci exit(1); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci switch (n.n.type) { 1168c2ecf20Sopenharmony_ci case WATCH_TYPE_META: 1178c2ecf20Sopenharmony_ci switch (n.n.subtype) { 1188c2ecf20Sopenharmony_ci case WATCH_META_REMOVAL_NOTIFICATION: 1198c2ecf20Sopenharmony_ci printf("REMOVAL of watchpoint %08x\n", 1208c2ecf20Sopenharmony_ci (n.n.info & WATCH_INFO_ID) >> 1218c2ecf20Sopenharmony_ci WATCH_INFO_ID__SHIFT); 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case WATCH_META_LOSS_NOTIFICATION: 1248c2ecf20Sopenharmony_ci printf("-- LOSS --\n"); 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci printf("other meta record\n"); 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci case WATCH_TYPE_KEY_NOTIFY: 1328c2ecf20Sopenharmony_ci saw_key_change(&n.n, len); 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci default: 1358c2ecf20Sopenharmony_ci printf("other type\n"); 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci p += len; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct watch_notification_filter filter = { 1458c2ecf20Sopenharmony_ci .nr_filters = 1, 1468c2ecf20Sopenharmony_ci .filters = { 1478c2ecf20Sopenharmony_ci [0] = { 1488c2ecf20Sopenharmony_ci .type = WATCH_TYPE_KEY_NOTIFY, 1498c2ecf20Sopenharmony_ci .subtype_filter[0] = UINT_MAX, 1508c2ecf20Sopenharmony_ci }, 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int pipefd[2], fd; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (pipe2(pipefd, O_NOTIFICATION_PIPE) == -1) { 1598c2ecf20Sopenharmony_ci perror("pipe2"); 1608c2ecf20Sopenharmony_ci exit(1); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci fd = pipefd[0]; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) { 1658c2ecf20Sopenharmony_ci perror("watch_queue(size)"); 1668c2ecf20Sopenharmony_ci exit(1); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) { 1708c2ecf20Sopenharmony_ci perror("watch_queue(filter)"); 1718c2ecf20Sopenharmony_ci exit(1); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) { 1758c2ecf20Sopenharmony_ci perror("keyctl"); 1768c2ecf20Sopenharmony_ci exit(1); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, fd, 0x02) == -1) { 1808c2ecf20Sopenharmony_ci perror("keyctl"); 1818c2ecf20Sopenharmony_ci exit(1); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci consumer(fd); 1858c2ecf20Sopenharmony_ci exit(0); 1868c2ecf20Sopenharmony_ci} 187