1/* Basic tests for Linux SYSV message queue extensions.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <sys/ipc.h>
20#include <sys/msg.h>
21#include <errno.h>
22#include <stdlib.h>
23#include <stdbool.h>
24#include <stdio.h>
25
26#include <support/check.h>
27#include <support/temp_file.h>
28
29#define MSGQ_MODE 0644
30
31/* These are for the temporary file we generate. */
32static char *name;
33static int msqid;
34
35static void
36remove_msq (void)
37{
38 /* Enforce message queue removal in case of early test failure.
39 Ignore error since the msg may already have being removed. */
40 msgctl (msqid: msqid, IPC_RMID, NULL);
41}
42
43static void
44do_prepare (int argc, char *argv[])
45{
46 TEST_VERIFY_EXIT (create_temp_file ("tst-sysvmsg.", &name) != -1);
47}
48
49#define PREPARE do_prepare
50
51struct test_msginfo
52{
53 int msgmax;
54 int msgmnb;
55 int msgmni;
56};
57
58/* It tries to obtain some system-wide SysV message queue information from
59 /proc to check against IPC_INFO/MSG_INFO. The /proc only returns the
60 tunables value of MSGMAX, MSGMNB, and MSGMNI.
61
62 The kernel also returns constant value for MSGSSZ, MSGSEG and also MSGMAP,
63 MSGPOOL, and MSGTQL (for IPC_INFO). The issue to check them is they might
64 change over kernel releases. */
65
66static int
67read_proc_file (const char *file)
68{
69 FILE *f = fopen (file, "r");
70 if (f == NULL)
71 FAIL_UNSUPPORTED ("/proc is not mounted or %s is not available", file);
72
73 int v;
74 int r = fscanf (stream: f, format: "%d", & v);
75 TEST_VERIFY_EXIT (r == 1);
76
77 fclose (f);
78 return v;
79}
80
81
82/* Check if the message queue with IDX (index into the kernel's internal
83 array) matches the one with KEY. The CMD is either MSG_STAT or
84 MSG_STAT_ANY. */
85
86static bool
87check_msginfo (int idx, key_t key, int cmd)
88{
89 struct msqid_ds msginfo;
90 int mid = msgctl (msqid: idx, cmd: cmd, buf: &msginfo);
91 /* Ignore unused array slot returned by the kernel or information from
92 unknown message queue. */
93 if ((mid == -1 && errno == EINVAL) || mid != msqid)
94 return false;
95
96 if (mid == -1)
97 FAIL_EXIT1 ("msgctl with %s failed: %m",
98 cmd == MSG_STAT ? "MSG_STAT" : "MSG_STAT_ANY");
99
100 TEST_COMPARE (msginfo.msg_perm.__key, key);
101 TEST_COMPARE (msginfo.msg_perm.mode, MSGQ_MODE);
102 TEST_COMPARE (msginfo.msg_qnum, 0);
103
104 return true;
105}
106
107static int
108do_test (void)
109{
110 atexit (func: remove_msq);
111
112 key_t key = ftok (pathname: name, proj_id: 'G');
113 if (key == -1)
114 FAIL_EXIT1 ("ftok failed: %m");
115
116 msqid = msgget (key: key, MSGQ_MODE | IPC_CREAT);
117 if (msqid == -1)
118 FAIL_EXIT1 ("msgget failed: %m");
119
120 struct test_msginfo tipcinfo;
121 tipcinfo.msgmax = read_proc_file (file: "/proc/sys/kernel/msgmax");
122 tipcinfo.msgmnb = read_proc_file (file: "/proc/sys/kernel/msgmnb");
123 tipcinfo.msgmni = read_proc_file (file: "/proc/sys/kernel/msgmni");
124
125 int msqidx;
126
127 {
128 struct msginfo ipcinfo;
129 msqidx = msgctl (msqid: msqid, IPC_INFO, buf: (struct msqid_ds *) &ipcinfo);
130 if (msqidx == -1)
131 FAIL_EXIT1 ("msgctl with IPC_INFO failed: %m");
132
133 TEST_COMPARE (ipcinfo.msgmax, tipcinfo.msgmax);
134 TEST_COMPARE (ipcinfo.msgmnb, tipcinfo.msgmnb);
135 TEST_COMPARE (ipcinfo.msgmni, tipcinfo.msgmni);
136 }
137
138 /* Same as before but with MSG_INFO. */
139 {
140 struct msginfo ipcinfo;
141 msqidx = msgctl (msqid: msqid, MSG_INFO, buf: (struct msqid_ds *) &ipcinfo);
142 if (msqidx == -1)
143 FAIL_EXIT1 ("msgctl with IPC_INFO failed: %m");
144
145 TEST_COMPARE (ipcinfo.msgmax, tipcinfo.msgmax);
146 TEST_COMPARE (ipcinfo.msgmnb, tipcinfo.msgmnb);
147 TEST_COMPARE (ipcinfo.msgmni, tipcinfo.msgmni);
148 }
149
150 /* We check if the created message queue shows in global list. */
151 bool found = false;
152 for (int i = 0; i <= msqidx; i++)
153 {
154 /* We can't tell apart if MSG_STAT_ANY is not supported (kernel older
155 than 4.17) or if the index used is invalid. So it just check if the
156 value returned from a valid call matches the created message
157 queue. */
158 check_msginfo (idx: i, key, MSG_STAT_ANY);
159
160 if (check_msginfo (idx: i, key, MSG_STAT))
161 {
162 found = true;
163 break;
164 }
165 }
166
167 if (!found)
168 FAIL_EXIT1 ("msgctl with MSG_STAT/MSG_STAT_ANY could not find the "
169 "created message queue");
170
171 if (msgctl (msqid: msqid, IPC_RMID, NULL) == -1)
172 FAIL_EXIT1 ("msgctl failed");
173
174 return 0;
175}
176
177#include <support/test-driver.c>
178

source code of glibc/sysdeps/unix/sysv/linux/tst-sysvmsg-linux.c