1 /* Basic tests for Linux SYSV message queue extensions.
2    Copyright (C) 2020-2021 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.  */
32 static char *name;
33 static int msqid;
34 
35 static void
remove_msq(void)36 remove_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, IPC_RMID, NULL);
41 }
42 
43 static void
do_prepare(int argc,char * argv[])44 do_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 
51 struct test_msginfo
52 {
53   int msgmax;
54   int msgmnb;
55   int msgmni;
56 };
57 
58 /* It tries to obtain some system-wide SysV messsage 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 
66 static int
read_proc_file(const char * file)67 read_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 (f, "%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 
86 static bool
check_msginfo(int idx,key_t key,int cmd)87 check_msginfo (int idx, key_t key, int cmd)
88 {
89   struct msqid_ds msginfo;
90   int mid = msgctl (idx, cmd, &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 
107 static int
do_test(void)108 do_test (void)
109 {
110   atexit (remove_msq);
111 
112   key_t key = ftok (name, 'G');
113   if (key == -1)
114     FAIL_EXIT1 ("ftok failed: %m");
115 
116   msqid = msgget (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 ("/proc/sys/kernel/msgmax");
122   tipcinfo.msgmnb = read_proc_file ("/proc/sys/kernel/msgmnb");
123   tipcinfo.msgmni = read_proc_file ("/proc/sys/kernel/msgmni");
124 
125   int msqidx;
126 
127   {
128     struct msginfo ipcinfo;
129     msqidx = msgctl (msqid, IPC_INFO, (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, MSG_INFO, (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 (i, key, MSG_STAT_ANY);
159 
160       if (check_msginfo (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, IPC_RMID, NULL) == -1)
172     FAIL_EXIT1 ("msgctl failed");
173 
174   return 0;
175 }
176 
177 #include <support/test-driver.c>
178