watch_test.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. // Work around glibc header silliness
  19. #undef AT_RENAME_NOREPLACE
  20. #undef AT_RENAME_EXCHANGE
  21. #undef AT_RENAME_WHITEOUT
  22. #include <linux/watch_queue.h>
  23. #include <linux/unistd.h>
  24. #include <linux/keyctl.h>
  25. #ifndef KEYCTL_WATCH_KEY
  26. #define KEYCTL_WATCH_KEY -1
  27. #endif
  28. #ifndef __NR_keyctl
  29. #define __NR_keyctl -1
  30. #endif
  31. #define BUF_SIZE 256
  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. static const char *key_subtypes[256] = {
  37. [NOTIFY_KEY_INSTANTIATED] = "instantiated",
  38. [NOTIFY_KEY_UPDATED] = "updated",
  39. [NOTIFY_KEY_LINKED] = "linked",
  40. [NOTIFY_KEY_UNLINKED] = "unlinked",
  41. [NOTIFY_KEY_CLEARED] = "cleared",
  42. [NOTIFY_KEY_REVOKED] = "revoked",
  43. [NOTIFY_KEY_INVALIDATED] = "invalidated",
  44. [NOTIFY_KEY_SETATTR] = "setattr",
  45. };
  46. static void saw_key_change(struct watch_notification *n, size_t len)
  47. {
  48. struct key_notification *k = (struct key_notification *)n;
  49. if (len != sizeof(struct key_notification)) {
  50. fprintf(stderr, "Incorrect key message length\n");
  51. return;
  52. }
  53. printf("KEY %08x change=%u[%s] aux=%u\n",
  54. k->key_id, n->subtype, key_subtypes[n->subtype], k->aux);
  55. }
  56. /*
  57. * Consume and display events.
  58. */
  59. static void consumer(int fd)
  60. {
  61. unsigned char buffer[433], *p, *end;
  62. union {
  63. struct watch_notification n;
  64. unsigned char buf1[128];
  65. } n;
  66. ssize_t buf_len;
  67. for (;;) {
  68. buf_len = read(fd, buffer, sizeof(buffer));
  69. if (buf_len == -1) {
  70. perror("read");
  71. exit(1);
  72. }
  73. if (buf_len == 0) {
  74. printf("-- END --\n");
  75. return;
  76. }
  77. if (buf_len > sizeof(buffer)) {
  78. fprintf(stderr, "Read buffer overrun: %zd\n", buf_len);
  79. return;
  80. }
  81. printf("read() = %zd\n", buf_len);
  82. p = buffer;
  83. end = buffer + buf_len;
  84. while (p < end) {
  85. size_t largest, len;
  86. largest = end - p;
  87. if (largest > 128)
  88. largest = 128;
  89. if (largest < sizeof(struct watch_notification)) {
  90. fprintf(stderr, "Short message header: %zu\n", largest);
  91. return;
  92. }
  93. memcpy(&n, p, largest);
  94. printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n",
  95. p - buffer, n.n.type, n.n.subtype, n.n.info);
  96. len = n.n.info & WATCH_INFO_LENGTH;
  97. if (len < sizeof(n.n) || len > largest) {
  98. fprintf(stderr, "Bad message length: %zu/%zu\n", len, largest);
  99. exit(1);
  100. }
  101. switch (n.n.type) {
  102. case WATCH_TYPE_META:
  103. switch (n.n.subtype) {
  104. case WATCH_META_REMOVAL_NOTIFICATION:
  105. printf("REMOVAL of watchpoint %08x\n",
  106. (n.n.info & WATCH_INFO_ID) >>
  107. WATCH_INFO_ID__SHIFT);
  108. break;
  109. case WATCH_META_LOSS_NOTIFICATION:
  110. printf("-- LOSS --\n");
  111. break;
  112. default:
  113. printf("other meta record\n");
  114. break;
  115. }
  116. break;
  117. case WATCH_TYPE_KEY_NOTIFY:
  118. saw_key_change(&n.n, len);
  119. break;
  120. default:
  121. printf("other type\n");
  122. break;
  123. }
  124. p += len;
  125. }
  126. }
  127. }
  128. static struct watch_notification_filter filter = {
  129. .nr_filters = 1,
  130. .filters = {
  131. [0] = {
  132. .type = WATCH_TYPE_KEY_NOTIFY,
  133. .subtype_filter[0] = UINT_MAX,
  134. },
  135. },
  136. };
  137. int main(int argc, char **argv)
  138. {
  139. int pipefd[2], fd;
  140. if (pipe2(pipefd, O_NOTIFICATION_PIPE) == -1) {
  141. perror("pipe2");
  142. exit(1);
  143. }
  144. fd = pipefd[0];
  145. if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) {
  146. perror("watch_queue(size)");
  147. exit(1);
  148. }
  149. if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) {
  150. perror("watch_queue(filter)");
  151. exit(1);
  152. }
  153. if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) {
  154. perror("keyctl");
  155. exit(1);
  156. }
  157. if (keyctl_watch_key(KEY_SPEC_USER_KEYRING, fd, 0x02) == -1) {
  158. perror("keyctl");
  159. exit(1);
  160. }
  161. consumer(fd);
  162. exit(0);
  163. }