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 | msg: "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: &msgque); |
218 | if (err) { |
219 | printf("Failed to fill queue: %d\n" , err); |
220 | goto err_destroy; |
221 | } |
222 | |
223 | err = dump_queue(msgque: &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: &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: &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: &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 | } |
256 | |