watch_test.c 3.9 KB

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