1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com> 4 */ 5 6#ifndef WQUEUE_COMMON_H__ 7#define WQUEUE_COMMON_H__ 8 9#include <unistd.h> 10#include "tst_test.h" 11#include "lapi/watch_queue.h" 12#include "lapi/keyctl.h" 13 14static struct watch_notification_filter wqueue_filter = { 15 .nr_filters = 2, 16 .filters = { 17 [0] = { 18 .type = WATCH_TYPE_META, 19 .subtype_filter[0] = UINT_MAX, 20 }, 21 [1] = { 22 .type = WATCH_TYPE_KEY_NOTIFY, 23 .subtype_filter[0] = UINT_MAX, 24 }, 25 }, 26}; 27 28static inline int wqueue_key_event(struct watch_notification *n, size_t len, 29 unsigned int wtype, int type) 30{ 31 struct key_notification *k; 32 const char *msg; 33 34 if (wtype != WATCH_TYPE_KEY_NOTIFY) 35 return 0; 36 37 if (len != sizeof(struct key_notification)) 38 tst_brk(TBROK, "Incorrect key message length"); 39 40 switch (n->subtype) { 41 case NOTIFY_KEY_INSTANTIATED: 42 msg = "instantiated"; 43 break; 44 case NOTIFY_KEY_UPDATED: 45 msg = "updated"; 46 break; 47 case NOTIFY_KEY_LINKED: 48 msg = "linked"; 49 break; 50 case NOTIFY_KEY_UNLINKED: 51 msg = "unlinked"; 52 break; 53 case NOTIFY_KEY_CLEARED: 54 msg = "cleared"; 55 break; 56 case NOTIFY_KEY_REVOKED: 57 msg = "revoked"; 58 break; 59 case NOTIFY_KEY_INVALIDATED: 60 msg = "invalidated"; 61 break; 62 case NOTIFY_KEY_SETATTR: 63 msg = "setattr"; 64 break; 65 default: 66 msg = "Invalid notification"; 67 break; 68 }; 69 70 k = (struct key_notification *)n; 71 tst_res(TINFO, "KEY %08x change=%u[%s] aux=%u", k->key_id, n->subtype, msg, 72 k->aux); 73 74 if (n->subtype == type) 75 return 1; 76 77 return 0; 78} 79 80static inline key_serial_t wqueue_add_key(int fd) 81{ 82 key_serial_t key; 83 84 key = add_key("user", "ltptestkey", "a", 1, KEY_SPEC_SESSION_KEYRING); 85 if (key == -1) 86 tst_brk(TBROK, "add_key error: %s", tst_strerrno(errno)); 87 88 keyctl(KEYCTL_WATCH_KEY, key, fd, 0x01); 89 keyctl(KEYCTL_WATCH_KEY, KEY_SPEC_SESSION_KEYRING, fd, 0x02); 90 91 return key; 92} 93 94static inline int wqueue_watch(int buf_size, 95 struct watch_notification_filter *filter) 96{ 97 int pipefd[2]; 98 int fd; 99 100 TEST(pipe2(pipefd, O_NOTIFICATION_PIPE)); 101 if (TST_RET) { 102 switch (TST_ERR) { 103 case ENOPKG: 104 tst_brk(TCONF | TTERRNO, "CONFIG_WATCH_QUEUE is not set"); 105 break; 106 case EINVAL: 107 tst_brk(TCONF | TTERRNO, "O_NOTIFICATION_PIPE is not supported"); 108 break; 109 default: 110 tst_brk(TBROK | TTERRNO, "pipe2() returned %ld", TST_RET); 111 } 112 } 113 114 fd = pipefd[0]; 115 116 SAFE_IOCTL(fd, IOC_WATCH_QUEUE_SET_SIZE, buf_size); 117 SAFE_IOCTL(fd, IOC_WATCH_QUEUE_SET_FILTER, filter); 118 119 return fd; 120} 121 122typedef void (*wqueue_callback)(struct watch_notification *n, size_t len, 123 unsigned int wtype); 124 125static void wqueue_consumer(int fd, wqueue_callback cb) 126{ 127 unsigned char buffer[433], *p, *end; 128 union { 129 struct watch_notification n; 130 unsigned char buf1[128]; 131 } n; 132 ssize_t buf_len; 133 134 tst_res(TINFO, "Reading watch queue events"); 135 136 buf_len = SAFE_READ(0, fd, buffer, sizeof(buffer)); 137 138 p = buffer; 139 end = buffer + buf_len; 140 while (p < end) { 141 size_t largest, len; 142 143 largest = end - p; 144 if (largest > 128) 145 largest = 128; 146 147 if (largest < sizeof(struct watch_notification)) 148 tst_brk(TBROK, "Short message header: %zu", largest); 149 150 memcpy(&n, p, largest); 151 152 tst_res(TINFO, "NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x", p - buffer, 153 n.n.type, n.n.subtype, n.n.info); 154 155 len = n.n.info & WATCH_INFO_LENGTH; 156 if (len < sizeof(n.n) || len > largest) 157 tst_brk(TBROK, "Bad message length: %zu/%zu", len, largest); 158 159 cb(&n.n, len, n.n.type); 160 161 p += len; 162 } 163} 164 165#endif 166