Home | History | Annotate | Download | only in ipc
      1 // SPDX-License-Identifier: GPL-2.0
      2 #include <stdlib.h>
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include <errno.h>
      6 #include <linux/msg.h>
      7 #include <fcntl.h>
      8 
      9 #include "../kselftest.h"
     10 
     11 #define MAX_MSG_SIZE		32
     12 
     13 struct msg1 {
     14 	int msize;
     15 	long mtype;
     16 	char mtext[MAX_MSG_SIZE];
     17 };
     18 
     19 #define TEST_STRING "Test sysv5 msg"
     20 #define MSG_TYPE 1
     21 
     22 #define ANOTHER_TEST_STRING "Yet another test sysv5 msg"
     23 #define ANOTHER_MSG_TYPE 26538
     24 
     25 struct msgque_data {
     26 	key_t key;
     27 	int msq_id;
     28 	int qbytes;
     29 	int qnum;
     30 	int mode;
     31 	struct msg1 *messages;
     32 };
     33 
     34 int restore_queue(struct msgque_data *msgque)
     35 {
     36 	int fd, ret, id, i;
     37 	char buf[32];
     38 
     39 	fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY);
     40 	if (fd == -1) {
     41 		printf("Failed to open /proc/sys/kernel/msg_next_id\n");
     42 		return -errno;
     43 	}
     44 	sprintf(buf, "%d", msgque->msq_id);
     45 
     46 	ret = write(fd, buf, strlen(buf));
     47 	if (ret != strlen(buf)) {
     48 		printf("Failed to write to /proc/sys/kernel/msg_next_id\n");
     49 		return -errno;
     50 	}
     51 
     52 	id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL);
     53 	if (id == -1) {
     54 		printf("Failed to create queue\n");
     55 		return -errno;
     56 	}
     57 
     58 	if (id != msgque->msq_id) {
     59 		printf("Restored queue has wrong id (%d instead of %d)\n",
     60 							id, msgque->msq_id);
     61 		ret = -EFAULT;
     62 		goto destroy;
     63 	}
     64 
     65 	for (i = 0; i < msgque->qnum; i++) {
     66 		if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype,
     67 			   msgque->messages[i].msize, IPC_NOWAIT) != 0) {
     68 			printf("msgsnd failed (%m)\n");
     69 			ret = -errno;
     70 			goto destroy;
     71 		};
     72 	}
     73 	return 0;
     74 
     75 destroy:
     76 	if (msgctl(id, IPC_RMID, 0))
     77 		printf("Failed to destroy queue: %d\n", -errno);
     78 	return ret;
     79 }
     80 
     81 int check_and_destroy_queue(struct msgque_data *msgque)
     82 {
     83 	struct msg1 message;
     84 	int cnt = 0, ret;
     85 
     86 	while (1) {
     87 		ret = msgrcv(msgque->msq_id, &message.mtype, MAX_MSG_SIZE,
     88 				0, IPC_NOWAIT);
     89 		if (ret < 0) {
     90 			if (errno == ENOMSG)
     91 				break;
     92 			printf("Failed to read IPC message: %m\n");
     93 			ret = -errno;
     94 			goto err;
     95 		}
     96 		if (ret != msgque->messages[cnt].msize) {
     97 			printf("Wrong message size: %d (expected %d)\n", ret,
     98 						msgque->messages[cnt].msize);
     99 			ret = -EINVAL;
    100 			goto err;
    101 		}
    102 		if (message.mtype != msgque->messages[cnt].mtype) {
    103 			printf("Wrong message type\n");
    104 			ret = -EINVAL;
    105 			goto err;
    106 		}
    107 		if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) {
    108 			printf("Wrong message content\n");
    109 			ret = -EINVAL;
    110 			goto err;
    111 		}
    112 		cnt++;
    113 	}
    114 
    115 	if (cnt != msgque->qnum) {
    116 		printf("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, 0)) {
    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 msqid64_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 			printf("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 		printf("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 			printf("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 		printf("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 		printf("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 msg, pid, err;
    197 	struct msgque_data msgque;
    198 
    199 	if (getuid() != 0)
    200 		return ksft_exit_skip(
    201 				"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 		return 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 	return ksft_exit_pass();
    246 
    247 err_destroy:
    248 	if (msgctl(msgque.msq_id, IPC_RMID, 0)) {
    249 		printf("Failed to destroy queue: %d\n", -errno);
    250 		return ksft_exit_fail();
    251 	}
    252 err_out:
    253 	return ksft_exit_fail();
    254 }
    255