about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/tst-sysvmsg-linux.c
blob: f18464e3f5ee6238e6ece512f02075ff4e26f7fa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* Basic tests for Linux SYSV message queue extensions.
   Copyright (C) 2020-2023 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>

#include <support/check.h>
#include <support/temp_file.h>

#define MSGQ_MODE 0644

/* These are for the temporary file we generate.  */
static char *name;
static int msqid;

static void
remove_msq (void)
{
  /* Enforce message queue removal in case of early test failure.
     Ignore error since the msg may already have being removed.  */
  msgctl (msqid, IPC_RMID, NULL);
}

static void
do_prepare (int argc, char *argv[])
{
  TEST_VERIFY_EXIT (create_temp_file ("tst-sysvmsg.", &name) != -1);
}

#define PREPARE do_prepare

struct test_msginfo
{
  int msgmax;
  int msgmnb;
  int msgmni;
};

/* It tries to obtain some system-wide SysV message queue information from
   /proc to check against IPC_INFO/MSG_INFO.  The /proc only returns the
   tunables value of MSGMAX, MSGMNB, and MSGMNI.

   The kernel also returns constant value for MSGSSZ, MSGSEG and also MSGMAP,
   MSGPOOL, and MSGTQL (for IPC_INFO).  The issue to check them is they might
   change over kernel releases.  */

static int
read_proc_file (const char *file)
{
  FILE *f = fopen (file, "r");
  if (f == NULL)
    FAIL_UNSUPPORTED ("/proc is not mounted or %s is not available", file);

  int v;
  int r = fscanf (f, "%d", & v);
  TEST_VERIFY_EXIT (r == 1);

  fclose (f);
  return v;
}


/* Check if the message queue with IDX (index into the kernel's internal
   array) matches the one with KEY.  The CMD is either MSG_STAT or
   MSG_STAT_ANY.  */

static bool
check_msginfo (int idx, key_t key, int cmd)
{
  struct msqid_ds msginfo;
  int mid = msgctl (idx, cmd, &msginfo);
  /* Ignore unused array slot returned by the kernel or information from
     unknown message queue.  */
  if ((mid == -1 && errno == EINVAL) || mid != msqid)
    return false;

  if (mid == -1)
    FAIL_EXIT1 ("msgctl with %s failed: %m",
		cmd == MSG_STAT ? "MSG_STAT" : "MSG_STAT_ANY");

  TEST_COMPARE (msginfo.msg_perm.__key, key);
  TEST_COMPARE (msginfo.msg_perm.mode, MSGQ_MODE);
  TEST_COMPARE (msginfo.msg_qnum, 0);

  return true;
}

static int
do_test (void)
{
  atexit (remove_msq);

  key_t key = ftok (name, 'G');
  if (key == -1)
    FAIL_EXIT1 ("ftok failed: %m");

  msqid = msgget (key, MSGQ_MODE | IPC_CREAT);
  if (msqid == -1)
    FAIL_EXIT1 ("msgget failed: %m");

  struct test_msginfo tipcinfo;
  tipcinfo.msgmax = read_proc_file ("/proc/sys/kernel/msgmax");
  tipcinfo.msgmnb = read_proc_file ("/proc/sys/kernel/msgmnb");
  tipcinfo.msgmni = read_proc_file ("/proc/sys/kernel/msgmni");

  int msqidx;

  {
    struct msginfo ipcinfo;
    msqidx = msgctl (msqid, IPC_INFO, (struct msqid_ds *) &ipcinfo);
    if (msqidx == -1)
      FAIL_EXIT1 ("msgctl with IPC_INFO failed: %m");

    TEST_COMPARE (ipcinfo.msgmax, tipcinfo.msgmax);
    TEST_COMPARE (ipcinfo.msgmnb, tipcinfo.msgmnb);
    TEST_COMPARE (ipcinfo.msgmni, tipcinfo.msgmni);
  }

  /* Same as before but with MSG_INFO.  */
  {
    struct msginfo ipcinfo;
    msqidx = msgctl (msqid, MSG_INFO, (struct msqid_ds *) &ipcinfo);
    if (msqidx == -1)
      FAIL_EXIT1 ("msgctl with IPC_INFO failed: %m");

    TEST_COMPARE (ipcinfo.msgmax, tipcinfo.msgmax);
    TEST_COMPARE (ipcinfo.msgmnb, tipcinfo.msgmnb);
    TEST_COMPARE (ipcinfo.msgmni, tipcinfo.msgmni);
  }

  /* We check if the created message queue shows in global list.  */
  bool found = false;
  for (int i = 0; i <= msqidx; i++)
    {
      /* We can't tell apart if MSG_STAT_ANY is not supported (kernel older
	 than 4.17) or if the index used is invalid.  So it just check if the
	 value returned from a valid call matches the created message
	 queue.  */
      check_msginfo (i, key, MSG_STAT_ANY);

      if (check_msginfo (i, key, MSG_STAT))
	{
	  found = true;
	  break;
	}
    }

  if (!found)
    FAIL_EXIT1 ("msgctl with MSG_STAT/MSG_STAT_ANY could not find the "
		"created message queue");

  if (msgctl (msqid, IPC_RMID, NULL) == -1)
    FAIL_EXIT1 ("msgctl failed");

  return 0;
}

#include <support/test-driver.c>