255 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3#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 3213 14struct 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 122 23#define ANOTHER_TEST_STRING "Yet another test sysv5 msg"24#define ANOTHER_MSG_TYPE 2653825 26struct 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 35int 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 printf("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 printf("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 printf("Failed to create queue\n");56 return -errno;57 }58 59 if (id != msgque->msq_id) {60 printf("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 printf("msgsnd failed (%m)\n");70 ret = -errno;71 goto destroy;72 }73 }74 return 0;75 76destroy:77 if (msgctl(id, IPC_RMID, NULL))78 printf("Failed to destroy queue: %d\n", -errno);79 return ret;80}81 82int 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 printf("Failed to read IPC message: %m\n");94 ret = -errno;95 goto err;96 }97 if (ret != msgque->messages[cnt].msize) {98 printf("Wrong message size: %d (expected %d)\n", ret,99 msgque->messages[cnt].msize);100 ret = -EINVAL;101 goto err;102 }103 if (message.mtype != msgque->messages[cnt].mtype) {104 printf("Wrong message type\n");105 ret = -EINVAL;106 goto err;107 }108 if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) {109 printf("Wrong message content\n");110 ret = -EINVAL;111 goto err;112 }113 cnt++;114 }115 116 if (cnt != msgque->qnum) {117 printf("Wrong message number\n");118 ret = -EINVAL;119 goto err;120 }121 122 ret = 0;123err:124 if (msgctl(msgque->msq_id, IPC_RMID, NULL)) {125 printf("Failed to destroy queue: %d\n", -errno);126 return -errno;127 }128 return ret;129}130 131int dump_queue(struct msgque_data *msgque)132{133 struct msqid_ds ds;134 int kern_id;135 int i, ret;136 137 for (kern_id = 0; kern_id < 256; kern_id++) {138 ret = msgctl(kern_id, MSG_STAT, &ds);139 if (ret < 0) {140 if (errno == EINVAL)141 continue;142 printf("Failed to get stats for IPC queue with id %d\n",143 kern_id);144 return -errno;145 }146 147 if (ret == msgque->msq_id)148 break;149 }150 151 msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum);152 if (msgque->messages == NULL) {153 printf("Failed to get stats for IPC queue\n");154 return -ENOMEM;155 }156 157 msgque->qnum = ds.msg_qnum;158 msgque->mode = ds.msg_perm.mode;159 msgque->qbytes = ds.msg_qbytes;160 161 for (i = 0; i < msgque->qnum; i++) {162 ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype,163 MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY);164 if (ret < 0) {165 printf("Failed to copy IPC message: %m (%d)\n", errno);166 return -errno;167 }168 msgque->messages[i].msize = ret;169 }170 return 0;171}172 173int fill_msgque(struct msgque_data *msgque)174{175 struct msg1 msgbuf;176 177 msgbuf.mtype = MSG_TYPE;178 memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING));179 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING),180 IPC_NOWAIT) != 0) {181 printf("First message send failed (%m)\n");182 return -errno;183 }184 185 msgbuf.mtype = ANOTHER_MSG_TYPE;186 memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING));187 if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING),188 IPC_NOWAIT) != 0) {189 printf("Second message send failed (%m)\n");190 return -errno;191 }192 return 0;193}194 195int main(int argc, char **argv)196{197 int msg, pid, err;198 struct msgque_data msgque;199 200 if (getuid() != 0)201 ksft_exit_skip("Please run the test as root - Exiting.\n");202 203 msgque.key = ftok(argv[0], 822155650);204 if (msgque.key == -1) {205 printf("Can't make key: %d\n", -errno);206 ksft_exit_fail();207 }208 209 msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666);210 if (msgque.msq_id == -1) {211 err = -errno;212 printf("Can't create queue: %d\n", err);213 goto err_out;214 }215 216 err = fill_msgque(&msgque);217 if (err) {218 printf("Failed to fill queue: %d\n", err);219 goto err_destroy;220 }221 222 err = dump_queue(&msgque);223 if (err) {224 printf("Failed to dump queue: %d\n", err);225 goto err_destroy;226 }227 228 err = check_and_destroy_queue(&msgque);229 if (err) {230 printf("Failed to check and destroy queue: %d\n", err);231 goto err_out;232 }233 234 err = restore_queue(&msgque);235 if (err) {236 printf("Failed to restore queue: %d\n", err);237 goto err_destroy;238 }239 240 err = check_and_destroy_queue(&msgque);241 if (err) {242 printf("Failed to test queue: %d\n", err);243 goto err_out;244 }245 ksft_exit_pass();246 247err_destroy:248 if (msgctl(msgque.msq_id, IPC_RMID, NULL)) {249 printf("Failed to destroy queue: %d\n", -errno);250 ksft_exit_fail();251 }252err_out:253 ksft_exit_fail();254}255