malloc/Makefile: Split and sort tests
[glibc.git] / sysdeps / unix / sysv / linux / semctl.c
blobc57fceab6bb1310180bf2c94725e02804edcd2c8
1 /* Copyright (C) 1995-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
18 #include <sys/sem.h>
19 #include <stdarg.h>
20 #include <ipc_priv.h>
21 #include <sysdep.h>
22 #include <shlib-compat.h>
23 #include <linux/posix_types.h> /* For __kernel_mode_t. */
25 /* The struct used to issue the syscall. For architectures that assume
26 64-bit time as default (!__ASSUME_TIME64_SYSCALLS) the syscall will
27 split the resulting 64-bit sem_{o,c}time in two fields (sem_{o,c}time
28 and __sem_{o,c}time_high). */
29 union semun
31 int val; /* value for SETVAL */
32 struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
33 unsigned short int *array; /* array for GETALL & SETALL */
34 struct seminfo *__buf; /* buffer for IPC_INFO */
37 #if __IPC_TIME64 == 0
38 # define semun64 semun
39 typedef union semun semctl_arg_t;
40 #else
41 # include <struct_kernel_semid64_ds.h>
43 union ksemun64
45 int val;
46 struct kernel_semid64_ds *buf;
47 unsigned short int *array;
48 struct seminfo *__buf;
51 # if __TIMESIZE == 64
52 # define semun64 semun
53 # else
54 /* The struct used when __semctl64 is called. */
55 union semun64
57 int val;
58 struct __semid64_ds *buf;
59 unsigned short int *array;
60 struct seminfo *__buf;
62 # endif
64 static void
65 semid64_to_ksemid64 (const struct __semid64_ds *semid64,
66 struct kernel_semid64_ds *ksemid)
68 ksemid->sem_perm = semid64->sem_perm;
69 ksemid->sem_otime = semid64->sem_otime;
70 ksemid->sem_otime_high = semid64->sem_otime >> 32;
71 ksemid->sem_ctime = semid64->sem_ctime;
72 ksemid->sem_ctime_high = semid64->sem_ctime >> 32;
73 ksemid->sem_nsems = semid64->sem_nsems;
76 static void
77 ksemid64_to_semid64 (const struct kernel_semid64_ds *ksemid,
78 struct __semid64_ds *semid64)
80 semid64->sem_perm = ksemid->sem_perm;
81 semid64->sem_otime = ksemid->sem_otime
82 | ((__time64_t) ksemid->sem_otime_high << 32);
83 semid64->sem_ctime = ksemid->sem_ctime
84 | ((__time64_t) ksemid->sem_ctime_high << 32);
85 semid64->sem_nsems = ksemid->sem_nsems;
88 static union ksemun64
89 semun64_to_ksemun64 (int cmd, union semun64 semun64,
90 struct kernel_semid64_ds *buf)
92 union ksemun64 r = { 0 };
93 switch (cmd)
95 case SETVAL:
96 r.val = semun64.val;
97 break;
98 case GETALL:
99 case SETALL:
100 r.array = semun64.array;
101 break;
102 case SEM_STAT:
103 case SEM_STAT_ANY:
104 case IPC_STAT:
105 case IPC_SET:
106 r.buf = buf;
107 semid64_to_ksemid64 (semun64.buf, r.buf);
108 break;
109 case IPC_INFO:
110 case SEM_INFO:
111 r.__buf = semun64.__buf;
112 break;
114 return r;
117 typedef union ksemun64 semctl_arg_t;
118 #endif
120 static int
121 semctl_syscall (int semid, int semnum, int cmd, semctl_arg_t arg)
123 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
124 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
125 arg.array);
126 #else
127 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
128 SEMCTL_ARG_ADDRESS (arg));
129 #endif
132 /* POSIX states ipc_perm mode should have type of mode_t. */
133 _Static_assert (sizeof ((struct semid_ds){0}.sem_perm.mode)
134 == sizeof (mode_t),
135 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
138 __semctl64 (int semid, int semnum, int cmd, ...)
140 union semun64 arg64 = { 0 };
141 va_list ap;
143 /* Some applications pass the __IPC_64 flag in cmd, to invoke
144 previously unsupported commands back when there was no EINVAL
145 error checking in glibc. Mask the flag for the switch statements
146 below. semctl_syscall adds back the __IPC_64 flag for the actual
147 system call. */
148 cmd &= ~__IPC_64;
150 /* Get the argument only if required. */
151 switch (cmd)
153 case SETVAL: /* arg.val */
154 case GETALL: /* arg.array */
155 case SETALL:
156 case IPC_STAT: /* arg.buf */
157 case IPC_SET:
158 case SEM_STAT:
159 case SEM_STAT_ANY:
160 case IPC_INFO: /* arg.__buf */
161 case SEM_INFO:
162 va_start (ap, cmd);
163 arg64 = va_arg (ap, union semun64);
164 va_end (ap);
165 break;
166 case IPC_RMID: /* arg ignored. */
167 case GETNCNT:
168 case GETPID:
169 case GETVAL:
170 case GETZCNT:
171 break;
172 default:
173 __set_errno (EINVAL);
174 return -1;
177 #if __IPC_TIME64
178 struct kernel_semid64_ds ksemid;
179 union ksemun64 ksemun = semun64_to_ksemun64 (cmd, arg64, &ksemid);
180 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
181 if (cmd == IPC_SET)
182 ksemid.sem_perm.mode *= 0x10000U;
183 # endif
184 union ksemun64 arg = ksemun;
185 #else
186 union semun arg = arg64;
187 #endif
189 int ret = semctl_syscall (semid, semnum, cmd, arg);
190 if (ret < 0)
191 return ret;
193 switch (cmd)
195 case IPC_STAT:
196 case SEM_STAT:
197 case SEM_STAT_ANY:
198 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
199 arg.buf->sem_perm.mode >>= 16;
200 #else
201 /* Old Linux kernel versions might not clear the mode padding. */
202 if (sizeof ((struct semid_ds){0}.sem_perm.mode)
203 != sizeof (__kernel_mode_t))
204 arg.buf->sem_perm.mode &= 0xFFFF;
205 #endif
207 #if __IPC_TIME64
208 ksemid64_to_semid64 (arg.buf, arg64.buf);
209 #endif
212 return ret;
214 #if __TIMESIZE != 64
215 libc_hidden_def (__semctl64)
218 /* The 64-bit time_t semid_ds version might have a different layout and
219 internal field alignment. */
221 static void
222 semid_to_semid64 (struct __semid64_ds *ds64, const struct semid_ds *ds)
224 ds64->sem_perm = ds->sem_perm;
225 ds64->sem_otime = ds->sem_otime
226 | ((__time64_t) ds->__sem_otime_high << 32);
227 ds64->sem_ctime = ds->sem_ctime
228 | ((__time64_t) ds->__sem_ctime_high << 32);
229 ds64->sem_nsems = ds->sem_nsems;
232 static void
233 semid64_to_semid (struct semid_ds *ds, const struct __semid64_ds *ds64)
235 ds->sem_perm = ds64->sem_perm;
236 ds->sem_otime = ds64->sem_otime;
237 ds->__sem_otime_high = 0;
238 ds->sem_ctime = ds64->sem_ctime;
239 ds->__sem_ctime_high = 0;
240 ds->sem_nsems = ds64->sem_nsems;
243 static union semun64
244 semun_to_semun64 (int cmd, union semun semun, struct __semid64_ds *semid64)
246 union semun64 r = { 0 };
247 switch (cmd)
249 case SETVAL:
250 r.val = semun.val;
251 break;
252 case GETALL:
253 case SETALL:
254 r.array = semun.array;
255 break;
256 case SEM_STAT:
257 case SEM_STAT_ANY:
258 case IPC_STAT:
259 case IPC_SET:
260 r.buf = semid64;
261 semid_to_semid64 (r.buf, semun.buf);
262 break;
263 case IPC_INFO:
264 case SEM_INFO:
265 r.__buf = semun.__buf;
266 break;
268 return r;
272 __semctl (int semid, int semnum, int cmd, ...)
274 union semun arg = { 0 };
276 va_list ap;
278 /* Get the argument only if required. */
279 switch (cmd)
281 case SETVAL: /* arg.val */
282 case GETALL: /* arg.array */
283 case SETALL:
284 case IPC_STAT: /* arg.buf */
285 case IPC_SET:
286 case SEM_STAT:
287 case SEM_STAT_ANY:
288 case IPC_INFO: /* arg.__buf */
289 case SEM_INFO:
290 va_start (ap, cmd);
291 arg = va_arg (ap, union semun);
292 va_end (ap);
293 break;
294 /* __semctl64 handles non-supported commands. */
297 struct __semid64_ds semid64;
298 union semun64 arg64 = semun_to_semun64 (cmd, arg, &semid64);
300 int ret = __semctl64 (semid, semnum, cmd, arg64);
301 if (ret < 0)
302 return ret;
304 switch (cmd)
306 case IPC_STAT:
307 case SEM_STAT:
308 case SEM_STAT_ANY:
309 semid64_to_semid (arg.buf, arg64.buf);
312 return ret;
314 #endif
316 #ifndef DEFAULT_VERSION
317 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
318 # define DEFAULT_VERSION GLIBC_2_2
319 # else
320 # define DEFAULT_VERSION GLIBC_2_31
321 # endif
322 #endif
323 versioned_symbol (libc, __semctl, semctl, DEFAULT_VERSION);
325 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
326 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
328 attribute_compat_text_section
329 __semctl_mode16 (int semid, int semnum, int cmd, ...)
331 semctl_arg_t arg = { 0 };
332 va_list ap;
334 /* Get the argument only if required. */
335 switch (cmd)
337 case SETVAL: /* arg.val */
338 case GETALL: /* arg.array */
339 case SETALL:
340 case IPC_STAT: /* arg.buf */
341 case IPC_SET:
342 case SEM_STAT:
343 case SEM_STAT_ANY:
344 case IPC_INFO: /* arg.__buf */
345 case SEM_INFO:
346 va_start (ap, cmd);
347 arg = va_arg (ap, semctl_arg_t);
348 va_end (ap);
349 break;
352 return semctl_syscall (semid, semnum, cmd, arg);
354 compat_symbol (libc, __semctl_mode16, semctl, GLIBC_2_2);
355 #endif
357 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
358 /* Since semctl use a variadic argument for semid_ds there is not need to
359 define and tie the compatibility symbol to the old 'union semun'
360 definition. */
362 attribute_compat_text_section
363 __old_semctl (int semid, int semnum, int cmd, ...)
365 union semun arg = { 0 };
366 va_list ap;
368 /* Get the argument only if required. */
369 switch (cmd)
371 case SETVAL: /* arg.val */
372 case GETALL: /* arg.array */
373 case SETALL:
374 case IPC_STAT: /* arg.buf */
375 case IPC_SET:
376 case SEM_STAT:
377 case SEM_STAT_ANY:
378 case IPC_INFO: /* arg.__buf */
379 case SEM_INFO:
380 va_start (ap, cmd);
381 arg = va_arg (ap, union semun);
382 va_end (ap);
383 break;
386 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
387 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
388 /* For architectures that have wire-up semctl but also have __IPC_64 to a
389 value different than default (0x0) it means the compat symbol used the
390 __NR_ipc syscall. */
391 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd, arg.array);
392 # else
393 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd,
394 SEMCTL_ARG_ADDRESS (arg));
395 # endif
397 compat_symbol (libc, __old_semctl, semctl, GLIBC_2_0);
398 #endif