1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <errno.h> 7 #include <sys/msg.h> 8 #include <fcntl.h> 9 10 #include "../kselftest.h" 11 12 #define MAX_MSG_SIZE 32 13 14 struct msg1 { 15 int msize; 16 long mtype; 17 char mtext[MAX_MSG_SIZE]; 18 }; 19 20 #define TEST_STRING "Test sysv5 msg" 21 #define MSG_TYPE 1 22 23 #define ANOTHER_TEST_STRING "Yet another test sysv5 msg" 24 #define ANOTHER_MSG_TYPE 26538 25 26 struct msgque_data { 27 key_t key; 28 int msq_id; 29 int qbytes; 30 int qnum; 31 int mode; 32 struct msg1 *messages; 33 }; 34 35 int restore_queue(struct msgque_data *msgque) 36 { 37 int fd, ret, id, i; 38 char buf[32]; 39 40 fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); 41 if (fd == -1) { 42 ksft_test_result_fail("Failed to open /proc/sys/kernel/msg_next_id\n"); 43 return -errno; 44 } 45 sprintf(buf, "%d", msgque->msq_id); 46 47 ret = write(fd, buf, strlen(buf)); 48 if (ret != strlen(buf)) { 49 ksft_test_result_fail("Failed to write to /proc/sys/kernel/msg_next_id\n"); 50 return -errno; 51 } 52 53 id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); 54 if (id == -1) { 55 ksft_test_result_fail("Failed to create queue\n"); 56 return -errno; 57 } 58 59 if (id != msgque->msq_id) { 60 ksft_test_result_fail("Restored queue has wrong id (%d instead of %d)\n" 61 , id, msgque->msq_id); 62 ret = -EFAULT; 63 goto destroy; 64 } 65 66 for (i = 0; i < msgque->qnum; i++) { 67 if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, 68 msgque->messages[i].msize, IPC_NOWAIT) != 0) { 69 ksft_test_result_fail("msgsnd failed (%m)\n"); 70 ret = -errno; 71 goto destroy; 72 } 73 } 74 return 0; 75 76 destroy: 77 if (msgctl(id, IPC_RMID, NULL)) 78 printf("Failed to destroy queue: %d\n", -errno); 79 return ret; 80 } 81 82 int check_and_destroy_queue(struct msgque_data *msgque) 83 { 84 struct msg1 message; 85 int cnt = 0, ret; 86 87 while (1) { 88 ret = msgrcv(msgque->msq_id, &message.mtype, MAX_MSG_SIZE, 89 0, IPC_NOWAIT); 90 if (ret < 0) { 91 if (errno == ENOMSG) 92 break; 93 ksft_test_result_fail("Failed to read IPC message: %m\n"); 94 ret = -errno; 95 goto err; 96 } 97 if (ret != msgque->messages[cnt].msize) { 98 ksft_test_result_fail("Wrong message size: %d (expected %d)\n", ret, msgque->messages[cnt].msize); 99 ret = -EINVAL; 100 goto err; 101 } 102 if (message.mtype != msgque->messages[cnt].mtype) { 103 ksft_test_result_fail("Wrong message type\n"); 104 ret = -EINVAL; 105 goto err; 106 } 107 if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { 108 ksft_test_result_fail("Wrong message content\n"); 109 ret = -EINVAL; 110 goto err; 111 } 112 cnt++; 113 } 114 115 if (cnt != msgque->qnum) { 116 ksft_test_result_fail("Wrong message number\n"); 117 ret = -EINVAL; 118 goto err; 119 } 120 121 ret = 0; 122 err: 123 if (msgctl(msgque->msq_id, IPC_RMID, NULL)) { 124 printf("Failed to destroy queue: %d\n", -errno); 125 return -errno; 126 } 127 return ret; 128 } 129 130 int dump_queue(struct msgque_data *msgque) 131 { 132 struct msqid_ds ds; 133 int kern_id; 134 int i, ret; 135 136 for (kern_id = 0; kern_id < 256; kern_id++) { 137 ret = msgctl(kern_id, MSG_STAT, &ds); 138 if (ret < 0) { 139 if (errno == EINVAL) 140 continue; 141 ksft_test_result_fail("Failed to get stats for IPC queue with id %d\n", 142 kern_id); 143 return -errno; 144 } 145 146 if (ret == msgque->msq_id) 147 break; 148 } 149 150 msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); 151 if (msgque->messages == NULL) { 152 ksft_test_result_fail("Failed to get stats for IPC queue\n"); 153 return -ENOMEM; 154 } 155 156 msgque->qnum = ds.msg_qnum; 157 msgque->mode = ds.msg_perm.mode; 158 msgque->qbytes = ds.msg_qbytes; 159 160 for (i = 0; i < msgque->qnum; i++) { 161 ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, 162 MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); 163 if (ret < 0) { 164 ksft_test_result_fail("Failed to copy IPC message: %m (%d)\n", errno); 165 return -errno; 166 } 167 msgque->messages[i].msize = ret; 168 } 169 return 0; 170 } 171 172 int fill_msgque(struct msgque_data *msgque) 173 { 174 struct msg1 msgbuf; 175 176 msgbuf.mtype = MSG_TYPE; 177 memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); 178 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), 179 IPC_NOWAIT) != 0) { 180 ksft_test_result_fail("First message send failed (%m)\n"); 181 return -errno; 182 } 183 184 msgbuf.mtype = ANOTHER_MSG_TYPE; 185 memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); 186 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), 187 IPC_NOWAIT) != 0) { 188 ksft_test_result_fail("Second message send failed (%m)\n"); 189 return -errno; 190 } 191 return 0; 192 } 193 194 int main(int argc, char **argv) 195 { 196 int err; 197 struct msgque_data msgque; 198 199 if (getuid() != 0) 200 ksft_exit_skip("Please run the test as root - Exiting.\n"); 201 202 msgque.key = ftok(argv[0], 822155650); 203 if (msgque.key == -1) { 204 ksft_test_result_fail("Can't make key: %d\n", -errno); 205 ksft_exit_fail(); 206 } 207 208 msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); 209 if (msgque.msq_id == -1) { 210 err = -errno; 211 ksft_test_result_fail("Can't create queue: %d\n", err); 212 goto err_out; 213 } 214 215 err = fill_msgque(&msgque); 216 if (err) { 217 ksft_test_result_fail("Failed to fill queue: %d\n", err); 218 goto err_destroy; 219 } 220 221 err = dump_queue(&msgque); 222 if (err) { 223 ksft_test_result_fail("Failed to dump queue: %d\n", err); 224 goto err_destroy; 225 } 226 227 err = check_and_destroy_queue(&msgque); 228 if (err) { 229 ksft_test_result_fail("Failed to check and destroy queue: %d\n", err); 230 goto err_out; 231 } 232 233 err = restore_queue(&msgque); 234 if (err) { 235 ksft_test_result_fail("Failed to restore queue: %d\n", err); 236 goto err_destroy; 237 } 238 239 err = check_and_destroy_queue(&msgque); 240 if (err) { 241 ksft_test_result_fail("Failed to test queue: %d\n", err); 242 goto err_out; 243 } 244 ksft_exit_pass(); 245 246 err_destroy: 247 if (msgctl(msgque.msq_id, IPC_RMID, NULL)) { 248 printf("Failed to destroy queue: %d\n", -errno); 249 ksft_exit_fail(); 250 } 251 err_out: 252 ksft_exit_fail(); 253 } 254