]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/unix/sysv/linux/semctl.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / sysdeps / unix / sysv / linux / semctl.c
1 /* Copyright (C) 1995-2021 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 <bits/types/struct_semid64_ds.h> /* For __semid64_ds. */
25 #include <linux/posix_types.h> /* For __kernel_mode_t. */
26
27 /* The struct used to issue the syscall. For architectures that assume
28 64-bit time as default (!__ASSUME_TIME64_SYSCALLS) the syscall will
29 split the resulting 64-bit sem_{o,c}time in two fields (sem_{o,c}time
30 and __sem_{o,c}time_high). */
31 union semun
32 {
33 int val; /* value for SETVAL */
34 struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
35 unsigned short int *array; /* array for GETALL & SETALL */
36 struct seminfo *__buf; /* buffer for IPC_INFO */
37 };
38
39 #if __IPC_TIME64 == 0
40 # define semun64 semun
41 typedef union semun semctl_arg_t;
42 #else
43 # include <struct_kernel_semid64_ds.h>
44
45 union ksemun64
46 {
47 int val;
48 struct kernel_semid64_ds *buf;
49 unsigned short int *array;
50 struct seminfo *__buf;
51 };
52
53 # if __TIMESIZE == 64
54 # define semun64 semun
55 # else
56 /* The struct used when __semctl64 is called. */
57 union semun64
58 {
59 int val;
60 struct __semid64_ds *buf;
61 unsigned short int *array;
62 struct seminfo *__buf;
63 };
64 # endif
65
66 static void
67 semid64_to_ksemid64 (const struct __semid64_ds *semid64,
68 struct kernel_semid64_ds *ksemid)
69 {
70 ksemid->sem_perm = semid64->sem_perm;
71 ksemid->sem_otime = semid64->sem_otime;
72 ksemid->sem_otime_high = semid64->sem_otime >> 32;
73 ksemid->sem_ctime = semid64->sem_ctime;
74 ksemid->sem_ctime_high = semid64->sem_ctime >> 32;
75 ksemid->sem_nsems = semid64->sem_nsems;
76 }
77
78 static void
79 ksemid64_to_semid64 (const struct kernel_semid64_ds *ksemid,
80 struct __semid64_ds *semid64)
81 {
82 semid64->sem_perm = ksemid->sem_perm;
83 semid64->sem_otime = ksemid->sem_otime
84 | ((__time64_t) ksemid->sem_otime_high << 32);
85 semid64->sem_ctime = ksemid->sem_ctime
86 | ((__time64_t) ksemid->sem_ctime_high << 32);
87 semid64->sem_nsems = ksemid->sem_nsems;
88 }
89
90 static union ksemun64
91 semun64_to_ksemun64 (int cmd, union semun64 semun64,
92 struct kernel_semid64_ds *buf)
93 {
94 union ksemun64 r = { 0 };
95 switch (cmd)
96 {
97 case SETVAL:
98 r.val = semun64.val;
99 break;
100 case GETALL:
101 case SETALL:
102 r.array = semun64.array;
103 break;
104 case SEM_STAT:
105 case SEM_STAT_ANY:
106 case IPC_STAT:
107 case IPC_SET:
108 r.buf = buf;
109 semid64_to_ksemid64 (semun64.buf, r.buf);
110 break;
111 case IPC_INFO:
112 case SEM_INFO:
113 r.__buf = semun64.__buf;
114 break;
115 }
116 return r;
117 }
118
119 typedef union ksemun64 semctl_arg_t;
120 #endif
121
122 static int
123 semctl_syscall (int semid, int semnum, int cmd, semctl_arg_t arg)
124 {
125 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
126 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
127 arg.array);
128 #else
129 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
130 SEMCTL_ARG_ADDRESS (arg));
131 #endif
132 }
133
134 /* POSIX states ipc_perm mode should have type of mode_t. */
135 _Static_assert (sizeof ((struct semid_ds){0}.sem_perm.mode)
136 == sizeof (mode_t),
137 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
138
139 int
140 __semctl64 (int semid, int semnum, int cmd, ...)
141 {
142 union semun64 arg64 = { 0 };
143 va_list ap;
144
145 /* Get the argument only if required. */
146 switch (cmd)
147 {
148 case SETVAL: /* arg.val */
149 case GETALL: /* arg.array */
150 case SETALL:
151 case IPC_STAT: /* arg.buf */
152 case IPC_SET:
153 case SEM_STAT:
154 case SEM_STAT_ANY:
155 case IPC_INFO: /* arg.__buf */
156 case SEM_INFO:
157 va_start (ap, cmd);
158 arg64 = va_arg (ap, union semun64);
159 va_end (ap);
160 break;
161 case IPC_RMID: /* arg ignored. */
162 case GETNCNT:
163 case GETPID:
164 case GETVAL:
165 case GETZCNT:
166 break;
167 default:
168 __set_errno (EINVAL);
169 return -1;
170 }
171
172 #if __IPC_TIME64
173 struct kernel_semid64_ds ksemid;
174 union ksemun64 ksemun = semun64_to_ksemun64 (cmd, arg64, &ksemid);
175 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
176 if (cmd == IPC_SET)
177 ksemid.sem_perm.mode *= 0x10000U;
178 # endif
179 union ksemun64 arg = ksemun;
180 #else
181 union semun arg = arg64;
182 #endif
183
184 int ret = semctl_syscall (semid, semnum, cmd, arg);
185 if (ret < 0)
186 return ret;
187
188 switch (cmd)
189 {
190 case IPC_STAT:
191 case SEM_STAT:
192 case SEM_STAT_ANY:
193 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
194 arg.buf->sem_perm.mode >>= 16;
195 #else
196 /* Old Linux kernel versions might not clear the mode padding. */
197 if (sizeof ((struct semid_ds){0}.sem_perm.mode)
198 != sizeof (__kernel_mode_t))
199 arg.buf->sem_perm.mode &= 0xFFFF;
200 #endif
201
202 #if __IPC_TIME64
203 ksemid64_to_semid64 (arg.buf, arg64.buf);
204 #endif
205 }
206
207 return ret;
208 }
209 #if __TIMESIZE != 64
210 libc_hidden_def (__semctl64)
211
212
213 /* The 64-bit time_t semid_ds version might have a different layout and
214 internal field alignment. */
215
216 static void
217 semid_to_semid64 (struct __semid64_ds *ds64, const struct semid_ds *ds)
218 {
219 ds64->sem_perm = ds->sem_perm;
220 ds64->sem_otime = ds->sem_otime
221 | ((__time64_t) ds->__sem_otime_high << 32);
222 ds64->sem_ctime = ds->sem_ctime
223 | ((__time64_t) ds->__sem_ctime_high << 32);
224 ds64->sem_nsems = ds->sem_nsems;
225 }
226
227 static void
228 semid64_to_semid (struct semid_ds *ds, const struct __semid64_ds *ds64)
229 {
230 ds->sem_perm = ds64->sem_perm;
231 ds->sem_otime = ds64->sem_otime;
232 ds->__sem_otime_high = 0;
233 ds->sem_ctime = ds64->sem_ctime;
234 ds->__sem_ctime_high = 0;
235 ds->sem_nsems = ds64->sem_nsems;
236 }
237
238 static union semun64
239 semun_to_semun64 (int cmd, union semun semun, struct __semid64_ds *semid64)
240 {
241 union semun64 r = { 0 };
242 switch (cmd)
243 {
244 case SETVAL:
245 r.val = semun.val;
246 break;
247 case GETALL:
248 case SETALL:
249 r.array = semun.array;
250 break;
251 case SEM_STAT:
252 case SEM_STAT_ANY:
253 case IPC_STAT:
254 case IPC_SET:
255 r.buf = semid64;
256 semid_to_semid64 (r.buf, semun.buf);
257 break;
258 case IPC_INFO:
259 case SEM_INFO:
260 r.__buf = semun.__buf;
261 break;
262 }
263 return r;
264 }
265
266 int
267 __semctl (int semid, int semnum, int cmd, ...)
268 {
269 union semun arg = { 0 };
270
271 va_list ap;
272
273 /* Get the argument only if required. */
274 switch (cmd)
275 {
276 case SETVAL: /* arg.val */
277 case GETALL: /* arg.array */
278 case SETALL:
279 case IPC_STAT: /* arg.buf */
280 case IPC_SET:
281 case SEM_STAT:
282 case SEM_STAT_ANY:
283 case IPC_INFO: /* arg.__buf */
284 case SEM_INFO:
285 va_start (ap, cmd);
286 arg = va_arg (ap, union semun);
287 va_end (ap);
288 break;
289 /* __semctl64 handles non-supported commands. */
290 }
291
292 struct __semid64_ds semid64;
293 union semun64 arg64 = semun_to_semun64 (cmd, arg, &semid64);
294
295 int ret = __semctl64 (semid, semnum, cmd, arg64);
296 if (ret < 0)
297 return ret;
298
299 switch (cmd)
300 {
301 case IPC_STAT:
302 case SEM_STAT:
303 case SEM_STAT_ANY:
304 semid64_to_semid (arg.buf, arg64.buf);
305 }
306
307 return ret;
308 }
309 #endif
310
311 #ifndef DEFAULT_VERSION
312 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
313 # define DEFAULT_VERSION GLIBC_2_2
314 # else
315 # define DEFAULT_VERSION GLIBC_2_31
316 # endif
317 #endif
318 versioned_symbol (libc, __semctl, semctl, DEFAULT_VERSION);
319
320 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
321 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
322 int
323 attribute_compat_text_section
324 __semctl_mode16 (int semid, int semnum, int cmd, ...)
325 {
326 semctl_arg_t arg = { 0 };
327 va_list ap;
328
329 /* Get the argument only if required. */
330 switch (cmd)
331 {
332 case SETVAL: /* arg.val */
333 case GETALL: /* arg.array */
334 case SETALL:
335 case IPC_STAT: /* arg.buf */
336 case IPC_SET:
337 case SEM_STAT:
338 case SEM_STAT_ANY:
339 case IPC_INFO: /* arg.__buf */
340 case SEM_INFO:
341 va_start (ap, cmd);
342 arg = va_arg (ap, semctl_arg_t);
343 va_end (ap);
344 break;
345 }
346
347 return semctl_syscall (semid, semnum, cmd, arg);
348 }
349 compat_symbol (libc, __semctl_mode16, semctl, GLIBC_2_2);
350 #endif
351
352 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
353 /* Since semctl use a variadic argument for semid_ds there is not need to
354 define and tie the compatibility symbol to the old 'union semun'
355 definition. */
356 int
357 attribute_compat_text_section
358 __old_semctl (int semid, int semnum, int cmd, ...)
359 {
360 union semun arg = { 0 };
361 va_list ap;
362
363 /* Get the argument only if required. */
364 switch (cmd)
365 {
366 case SETVAL: /* arg.val */
367 case GETALL: /* arg.array */
368 case SETALL:
369 case IPC_STAT: /* arg.buf */
370 case IPC_SET:
371 case SEM_STAT:
372 case SEM_STAT_ANY:
373 case IPC_INFO: /* arg.__buf */
374 case SEM_INFO:
375 va_start (ap, cmd);
376 arg = va_arg (ap, union semun);
377 va_end (ap);
378 break;
379 }
380
381 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
382 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
383 /* For architectures that have wire-up semctl but also have __IPC_64 to a
384 value different than default (0x0) it means the compat symbol used the
385 __NR_ipc syscall. */
386 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd, arg.array);
387 # else
388 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd,
389 SEMCTL_ARG_ADDRESS (arg));
390 # endif
391 }
392 compat_symbol (libc, __old_semctl, semctl, GLIBC_2_0);
393 #endif