(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
tst-sysvmsg-linux.c
       1  /* Basic tests for Linux SYSV message queue extensions.
       2     Copyright (C) 2020-2023 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
      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
      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 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  
      66  static int
      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
      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
     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>