GNU Linux-libre 4.19.245-gnu1
[releases.git] / tools / testing / selftests / ipc / msgque.c
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                 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
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                         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;
123 err:
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
131 int 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
173 int 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
195 int main(int argc, char **argv)
196 {
197         int msg, pid, err;
198         struct msgque_data msgque;
199
200         if (getuid() != 0)
201                 return ksft_exit_skip(
202                                 "Please run the test as root - Exiting.\n");
203
204         msgque.key = ftok(argv[0], 822155650);
205         if (msgque.key == -1) {
206                 printf("Can't make key: %d\n", -errno);
207                 return ksft_exit_fail();
208         }
209
210         msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666);
211         if (msgque.msq_id == -1) {
212                 err = -errno;
213                 printf("Can't create queue: %d\n", err);
214                 goto err_out;
215         }
216
217         err = fill_msgque(&msgque);
218         if (err) {
219                 printf("Failed to fill queue: %d\n", err);
220                 goto err_destroy;
221         }
222
223         err = dump_queue(&msgque);
224         if (err) {
225                 printf("Failed to dump queue: %d\n", err);
226                 goto err_destroy;
227         }
228
229         err = check_and_destroy_queue(&msgque);
230         if (err) {
231                 printf("Failed to check and destroy queue: %d\n", err);
232                 goto err_out;
233         }
234
235         err = restore_queue(&msgque);
236         if (err) {
237                 printf("Failed to restore queue: %d\n", err);
238                 goto err_destroy;
239         }
240
241         err = check_and_destroy_queue(&msgque);
242         if (err) {
243                 printf("Failed to test queue: %d\n", err);
244                 goto err_out;
245         }
246         return ksft_exit_pass();
247
248 err_destroy:
249         if (msgctl(msgque.msq_id, IPC_RMID, NULL)) {
250                 printf("Failed to destroy queue: %d\n", -errno);
251                 return ksft_exit_fail();
252         }
253 err_out:
254         return ksft_exit_fail();
255 }