1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2014-2020 Fujitsu Ltd.
4 * Author: Xiaoguang Wang <[email protected]>
5 * Author: Yang Xu <[email protected]>
6 *
7 * Basic test for msgrcv(2) using MSG_EXCEPT, MSG_NOERROR, MSG_COPY and
8 * different msg_typ(zero,positive,negative).
9 *
10 * * With MSG_EXCEPT flag any message type but the one passed to the function
11 * is received.
12 *
13 * * With MSG_NOERROR and buffer size less than message size only part of the
14 * buffer is received.
15 *
16 * * With MSG_COPY and IPC_NOWAIT flag read the msg but don't destroy it in
17 * msg queue.
18 *
19 * * With msgtyp is 0, then the first message in the queue is read.
20 *
21 * * With msgtyp is greater than 0, then the first message in the queue of type
22 * msgtyp is read.
23 *
24 * * With msgtyp is less than 0, then the first message in the queue with the
25 * lowest type less than or equal to absolute value of msgtyp is received.
26 */
27
28 #define _GNU_SOURCE
29 #include <sys/wait.h>
30 #include "tst_test.h"
31 #include "tst_safe_sysv_ipc.h"
32 #include "libnewipc.h"
33 #include "lapi/msg.h"
34
35 #define MSGTYPE1 1
36 #define MSGTYPE2 2
37 #define MSG1 "messagetype1"
38 #define MSG2 "messagetype2"
39
40 static key_t msgkey;
41 static int queue_id = -1, msg_copy_sup;
42 static struct buf {
43 long type;
44 char mtext[MSGSIZE];
45 } rcv_buf, snd_buf[2] = {
46 {MSGTYPE1, MSG1},
47 {MSGTYPE2, MSG2}
48 };
49
prepare_queue(void)50 static void prepare_queue(void)
51 {
52 queue_id = SAFE_MSGGET(msgkey, IPC_CREAT | IPC_EXCL | MSG_RW);
53 SAFE_MSGSND(queue_id, &snd_buf[0], MSGSIZE, 0);
54 SAFE_MSGSND(queue_id, &snd_buf[1], MSGSIZE, 0);
55 memset(&rcv_buf, 0, sizeof(rcv_buf));
56 }
57
test_msg_except(void)58 static void test_msg_except(void)
59 {
60 prepare_queue();
61
62 TEST(msgrcv(queue_id, &rcv_buf, MSGSIZE, MSGTYPE2, MSG_EXCEPT));
63 if (TST_RET == -1) {
64 tst_res(TFAIL | TTERRNO, "msgrcv(MSG_EXCEPT) failed");
65 goto exit;
66 }
67
68 tst_res(TPASS, "msgrcv(MSG_EXCEPT) succeeded");
69
70 if (strcmp(rcv_buf.mtext, MSG1) == 0 && rcv_buf.type == MSGTYPE1)
71 tst_res(TPASS, "MSG_EXCEPT excepted MSGTYPE2 and got MSGTYPE1");
72 else
73 tst_res(TFAIL, "MSG_EXCEPT didn't get MSGTYPE1 message");
74
75 exit:
76 SAFE_MSGCTL(queue_id, IPC_RMID, NULL);
77 queue_id = -1;
78 }
79
test_msg_noerror(void)80 static void test_msg_noerror(void)
81 {
82 int msg_len;
83
84 queue_id = SAFE_MSGGET(msgkey, IPC_CREAT | IPC_EXCL | MSG_RW);
85 SAFE_MSGSND(queue_id, &snd_buf[0], MSGSIZE, 0);
86 msg_len = sizeof(MSG1) / 2;
87 memset(&rcv_buf, 0, sizeof(rcv_buf));
88
89 TEST(msgrcv(queue_id, &rcv_buf, msg_len, MSGTYPE1, MSG_NOERROR));
90 if (TST_RET == -1) {
91 tst_res(TFAIL | TTERRNO, "msgrcv(MSG_NOERROR) failed");
92 goto exit;
93 }
94
95 tst_res(TPASS, "msgrcv(MSG_NOERROR) succeeded");
96
97 if (strncmp(rcv_buf.mtext, MSG1, msg_len) == 0 && rcv_buf.type == MSGTYPE1)
98 tst_res(TPASS, "MSG_NOERROR truncated message correctly");
99 else
100 tst_res(TFAIL, "MSG_NOERROR truncated message incorrectly");
101
102 exit:
103 SAFE_MSGCTL(queue_id, IPC_RMID, NULL);
104 queue_id = -1;
105 }
106
test_msg_copy(void)107 static void test_msg_copy(void)
108 {
109 struct msqid_ds buf = {0};
110
111 if (!msg_copy_sup) {
112 tst_res(TCONF, "MSG_COPY not supported");
113 return;
114 }
115
116 prepare_queue();
117
118 /*
119 * If MSG_COPY flag was specified, then mtype is interpreted as number
120 * of the message to copy.
121 */
122 TEST(msgrcv(queue_id, &rcv_buf, MSGSIZE, 0, MSG_COPY | IPC_NOWAIT));
123 if (TST_RET == -1) {
124 if (TST_ERR == ENOSYS) {
125 tst_res(TCONF,
126 "MSG_COPY needs CONFIG_CHECKPORINT_RESTORE");
127 msg_copy_sup = 0;
128 } else {
129 tst_res(TFAIL | TTERRNO, "msgrcv(0, MSG_COPY) failed");
130 }
131 goto exit;
132 }
133
134 tst_res(TPASS, "msgrcv(0, MSG_COPY) succeeded");
135
136 if (strcmp(rcv_buf.mtext, MSG1) == 0 && rcv_buf.type == MSGTYPE1)
137 tst_res(TPASS, "MSG_COPY got MSGTYPE1 data correctly");
138 else
139 tst_res(TFAIL, "MSG_COPY got MSGTYPE1 data incorrectly");
140
141 memset(&rcv_buf, 0, sizeof(rcv_buf));
142 TEST(msgrcv(queue_id, &rcv_buf, MSGSIZE, 1, MSG_COPY | IPC_NOWAIT));
143 if (TST_RET == -1) {
144 tst_res(TFAIL | TTERRNO, "msgrcv(1, MSG_COPY) failed");
145 goto exit;
146 }
147
148 tst_res(TPASS, "msgrcv(1, MSG_COPY) succeeded");
149
150 if (strcmp(rcv_buf.mtext, MSG2) == 0 && rcv_buf.type == MSGTYPE2)
151 tst_res(TPASS, "MSG_COPY got MSGTYPE2 data correctly");
152 else
153 tst_res(TFAIL, "MSG_COPY got MSGTYPE2 data incorrectly");
154
155 SAFE_MSGCTL(queue_id, IPC_STAT, &buf);
156 if (buf.msg_qnum == 2) {
157 tst_res(TPASS, "Two messages still in queue");
158 } else {
159 tst_res(TFAIL, "Expected 2 msgs in queue got %d",
160 (int)buf.msg_qnum);
161 }
162
163 exit:
164 SAFE_MSGCTL(queue_id, IPC_RMID, NULL);
165 queue_id = -1;
166 }
167
test_zero_msgtyp(void)168 static void test_zero_msgtyp(void)
169 {
170 prepare_queue();
171
172 TEST(msgrcv(queue_id, &rcv_buf, MSGSIZE, 0, 0));
173 if (TST_RET == -1) {
174 tst_res(TFAIL | TTERRNO, "msgrcv(zero_msgtyp) failed");
175 goto exit;
176 }
177
178 tst_res(TPASS, "msgrcv(zero_msgtyp) succeeded");
179
180 if (strcmp(rcv_buf.mtext, MSG1) == 0 && rcv_buf.type == MSGTYPE1)
181 tst_res(TPASS, "zero_msgtyp got the first message");
182 else
183 tst_res(TFAIL, "zero_msgtyp didn't get the first message");
184
185 exit:
186 SAFE_MSGCTL(queue_id, IPC_RMID, NULL);
187 queue_id = -1;
188 }
189
test_positive_msgtyp(void)190 static void test_positive_msgtyp(void)
191 {
192 prepare_queue();
193
194 TEST(msgrcv(queue_id, &rcv_buf, MSGSIZE, MSGTYPE2, 0));
195 if (TST_RET == -1) {
196 tst_res(TFAIL | TTERRNO, "msgrcv(positive_msgtyp) failed");
197 goto exit;
198 }
199
200 tst_res(TPASS, "msgrcv(positive_msgtyp) succeeded");
201
202 if (strcmp(rcv_buf.mtext, MSG2) == 0 && rcv_buf.type == MSGTYPE2) {
203 tst_res(TPASS,
204 "msgtyp got the first message in the queue of type msgtyp");
205 } else {
206 tst_res(TFAIL,
207 "msgtyp didn't get the first message in the queue of type msgtyp");
208 }
209
210 exit:
211 SAFE_MSGCTL(queue_id, IPC_RMID, NULL);
212 queue_id = -1;
213 }
214
test_negative_msgtyp(void)215 static void test_negative_msgtyp(void)
216 {
217 prepare_queue();
218
219 TEST(msgrcv(queue_id, &rcv_buf, MSGSIZE, -MSGTYPE2, 0));
220 if (TST_RET == -1) {
221 tst_res(TFAIL | TTERRNO, "msgrcv(negative_msgtyp) failed");
222 goto exit;
223 }
224
225 tst_res(TPASS, "msgrcv(negative_msgtyp) succeeded");
226
227 if (strcmp(rcv_buf.mtext, MSG1) == 0 && rcv_buf.type == MSGTYPE1) {
228 tst_res(TPASS,
229 "-msgtyp got the first message in the queue with the lowest type");
230 } else {
231 tst_res(TFAIL,
232 "-msgtyp didn't get the first message in the queue with the lowest type");
233 }
234
235 exit:
236 SAFE_MSGCTL(queue_id, IPC_RMID, NULL);
237 queue_id = -1;
238 }
239
cleanup(void)240 static void cleanup(void)
241 {
242 if (queue_id != -1)
243 SAFE_MSGCTL(queue_id, IPC_RMID, NULL);
244 }
245
setup(void)246 static void setup(void)
247 {
248 msgkey = GETIPCKEY();
249
250 msg_copy_sup = 1;
251 }
252
253 static void (*testfunc[])(void) = {test_msg_except, test_msg_noerror,
254 test_msg_copy, test_zero_msgtyp,
255 test_positive_msgtyp, test_negative_msgtyp};
256
verify_msgcrv(unsigned int n)257 static void verify_msgcrv(unsigned int n)
258 {
259 (*testfunc[n])();
260 }
261
262 static struct tst_test test = {
263 .needs_tmpdir = 1,
264 .setup = setup,
265 .cleanup = cleanup,
266 .test = verify_msgcrv,
267 .tcnt = ARRAY_SIZE(testfunc),
268 };
269