1/* Copyright (C) 1995-2020 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, August 1995.
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/sem.h>
20#include <stdarg.h>
21#include <ipc_priv.h>
22#include <sysdep.h>
23#include <shlib-compat.h>
24#include <errno.h>
25
26/* Define a `union semun' suitable for Linux here. */
27union semun
28{
29 int val; /* value for SETVAL */
30 struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
31 unsigned short int *array; /* array for GETALL & SETALL */
32 struct seminfo *__buf; /* buffer for IPC_INFO */
33};
34
35#ifndef DEFAULT_VERSION
36# ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
37# define DEFAULT_VERSION GLIBC_2_2
38# else
39# define DEFAULT_VERSION GLIBC_2_31
40# endif
41#endif
42
43static int
44semctl_syscall (int semid, int semnum, int cmd, union semun arg)
45{
46#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
47 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
48 arg.array);
49#else
50 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
51 SEMCTL_ARG_ADDRESS (arg));
52#endif
53}
54
55int
56__new_semctl (int semid, int semnum, int cmd, ...)
57{
58 /* POSIX states ipc_perm mode should have type of mode_t. */
59 _Static_assert (sizeof ((struct semid_ds){0}.sem_perm.mode)
60 == sizeof (mode_t),
61 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
62
63 union semun arg = { 0 };
64 va_list ap;
65
66 /* Get the argument only if required. */
67 switch (cmd)
68 {
69 case SETVAL: /* arg.val */
70 case GETALL: /* arg.array */
71 case SETALL:
72 case IPC_STAT: /* arg.buf */
73 case IPC_SET:
74 case SEM_STAT:
75 case IPC_INFO: /* arg.__buf */
76 case SEM_INFO:
77 va_start (ap, cmd);
78 arg = va_arg (ap, union semun);
79 va_end (ap);
80 break;
81 }
82
83#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
84 struct semid_ds tmpds;
85 if (cmd == IPC_SET)
86 {
87 tmpds = *arg.buf;
88 tmpds.sem_perm.mode *= 0x10000U;
89 arg.buf = &tmpds;
90 }
91#endif
92
93 int ret = semctl_syscall (semid, semnum, cmd, arg);
94
95#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
96 if (ret >= 0)
97 {
98 switch (cmd)
99 {
100 case IPC_STAT:
101 case SEM_STAT:
102 case SEM_STAT_ANY:
103 arg.buf->sem_perm.mode >>= 16;
104 }
105 }
106#endif
107
108 return ret;
109}
110versioned_symbol (libc, __new_semctl, semctl, DEFAULT_VERSION);
111
112#if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
113 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
114int
115attribute_compat_text_section
116__semctl_mode16 (int semid, int semnum, int cmd, ...)
117{
118 union semun arg = { 0 };
119 va_list ap;
120
121 /* Get the argument only if required. */
122 switch (cmd)
123 {
124 case SETVAL: /* arg.val */
125 case GETALL: /* arg.array */
126 case SETALL:
127 case IPC_STAT: /* arg.buf */
128 case IPC_SET:
129 case SEM_STAT:
130 case IPC_INFO: /* arg.__buf */
131 case SEM_INFO:
132 va_start (ap, cmd);
133 arg = va_arg (ap, union semun);
134 va_end (ap);
135 break;
136 }
137
138 return semctl_syscall (semid, semnum, cmd, arg);
139}
140compat_symbol (libc, __semctl_mode16, semctl, GLIBC_2_2);
141#endif
142
143#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
144/* Since semctl use a variadic argument for semid_ds there is not need to
145 define and tie the compatibility symbol to the old 'union semun'
146 definition. */
147int
148attribute_compat_text_section
149__old_semctl (int semid, int semnum, int cmd, ...)
150{
151 union semun arg = { 0 };
152 va_list ap;
153
154 /* Get the argument only if required. */
155 switch (cmd)
156 {
157 case SETVAL: /* arg.val */
158 case GETALL: /* arg.array */
159 case SETALL:
160 case IPC_STAT: /* arg.buf */
161 case IPC_SET:
162 case SEM_STAT:
163 case IPC_INFO: /* arg.__buf */
164 case SEM_INFO:
165 va_start (ap, cmd);
166 arg = va_arg (ap, union semun);
167 va_end (ap);
168 break;
169 }
170
171#if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
172 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
173 /* For architectures that have wire-up semctl but also have __IPC_64 to a
174 value different than default (0x0) it means the compat symbol used the
175 __NR_ipc syscall. */
176 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd, arg.array);
177# else
178 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd,
179 SEMCTL_ARG_ADDRESS (arg));
180# endif
181}
182compat_symbol (libc, __old_semctl, semctl, GLIBC_2_0);
183#endif
184