]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/posix_ipc.cc
sys: Remove $FreeBSD$: two-line .h pattern
[newlib-cygwin.git] / winsup / cygwin / posix_ipc.cc
CommitLineData
7b487ba9
CV
1/* posix_ipc.cc: POSIX IPC API for Cygwin.
2
7b487ba9
CV
3This file is part of Cygwin.
4
5This software is a copyrighted work licensed under the terms of the
6Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7details. */
8
7b487ba9 9#include "winsup.h"
3748b3e8 10#include "shared_info.h"
8fbd574e 11#include "thread.h"
7b487ba9 12#include "path.h"
7b487ba9 13#include "cygtls.h"
e6fbf13e
CV
14#include "fhandler.h"
15#include "dtable.h"
16#include "cygheap.h"
7b487ba9 17#include "sigproc.h"
abbde487 18#include "ntdll.h"
b62450cf 19#include "tls_pbuf.h"
4fc922b2 20#include <io.h>
7b487ba9
CV
21#include <sys/mman.h>
22#include <sys/param.h>
7b487ba9 23#include <stdlib.h>
7b487ba9 24#include <unistd.h>
7b487ba9 25#include <mqueue.h>
8fbd574e 26#include <semaphore.h>
7b487ba9 27
5f621bd3 28/* The prefix_len is the length of the path prefix including trailing "/"
547ad329 29 (or "/sem." for semaphores) as well as the trailing NUL. */
cb7e1879 30static struct
7b487ba9
CV
31{
32 const char *prefix;
547ad329 33 const size_t prefix_len;
7b487ba9
CV
34 const char *description;
35} ipc_names[] = {
547ad329
CV
36 { "/dev/shm", 10, "POSIX shared memory object" },
37 { "/dev/mqueue", 13, "POSIX message queue" },
38 { "/dev/shm", 14, "POSIX semaphore" }
7b487ba9
CV
39};
40
41enum ipc_type_t
42{
43 shmem,
44 mqueue,
8fbd574e 45 semaphore
7b487ba9
CV
46};
47
48static bool
547ad329 49check_path (char *res_name, ipc_type_t type, const char *name, size_t len)
7b487ba9 50{
b32c31d1 51 /* Note that we require the existance of the appropriate /dev subdirectories
7b487ba9
CV
52 for POSIX IPC object support, similar to Linux (which supports the
53 directories, but doesn't require to mount them). We don't create
54 these directory here, that's the task of the installer. But we check
55 for existance and give ample warning. */
56 path_conv path (ipc_names[type].prefix, PC_SYM_NOFOLLOW);
57 if (path.error || !path.exists () || !path.isdir ())
58 {
59 small_printf (
60 "Warning: '%s' does not exists or is not a directory.\n\n"
61 "%ss require the existance of this directory.\n"
62 "Create the directory '%s' and set the permissions to 01777.\n"
63 "For instance on the command line: mkdir -m 01777 %s\n",
64 ipc_names[type].prefix, ipc_names[type].description,
65 ipc_names[type].prefix, ipc_names[type].prefix);
66 set_errno (EINVAL);
67 return false;
68 }
5b380b1c 69 /* Apart from handling backslash like slash, the naming rules are identical
b32c31d1
CV
70 to Linux, including the names and requirements for subdirectories, if
71 the name contains further slashes. */
5b380b1c
CV
72 /* Name must not be empty and has to start with a slash (or backslash) */
73 if (!name || !strchr ("/\\", name[0]))
7b487ba9
CV
74 {
75 debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
76 set_errno (EINVAL);
77 return false;
78 }
5b380b1c
CV
79 /* Name must not consist of just a single slash (or backslash) */
80 if (!name[1])
81 {
82 debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
83 set_errno (ENOENT);
84 return false;
85 }
86 /* Name must not contain slashes after the leading one */
87 if (strpbrk (name + 1, "/\\"))
88 {
89 debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
90 set_errno (EACCES);
91 return false;
92 }
93 /* Length must be less than or equal to NAME_MAX, or NAME_MAX - 4 in
94 case of semaphores, due to the leading "sem." prefix */
95 if (len > NAME_MAX - (type == semaphore ? strlen ("sem.") : 0))
7b487ba9
CV
96 {
97 debug_printf ("%s name '%s' too long", ipc_names[type].description, name);
98 set_errno (ENAMETOOLONG);
99 return false;
100 }
8fbd574e
CV
101 __small_sprintf (res_name, "%s/%s%s", ipc_names[type].prefix,
102 type == semaphore ? "sem." : "",
5b380b1c 103 name + 1);
7b487ba9
CV
104 return true;
105}
106
8fbd574e
CV
107class ipc_flock
108{
61522196 109 struct flock fl;
8fbd574e
CV
110
111public:
112 ipc_flock () { memset (&fl, 0, sizeof fl); }
113
114 int lock (int fd, size_t size)
115 {
116 fl.l_type = F_WRLCK;
117 fl.l_whence = SEEK_SET;
118 fl.l_start = 0;
119 fl.l_len = size;
2d9b4876 120 return fcntl (fd, F_SETLKW, &fl);
8fbd574e
CV
121 }
122 int unlock (int fd)
123 {
124 if (!fl.l_len)
125 return 0;
126 fl.l_type = F_UNLCK;
2d9b4876 127 return fcntl (fd, F_SETLKW, &fl);
8fbd574e
CV
128 }
129};
130
7b487ba9
CV
131/* POSIX shared memory object implementation. */
132
133extern "C" int
134shm_open (const char *name, int oflag, mode_t mode)
135{
547ad329
CV
136 size_t len = strlen (name);
137 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 138
547ad329 139 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
140 return -1;
141
142 /* Check for valid flags. */
143 if (((oflag & O_ACCMODE) != O_RDONLY && (oflag & O_ACCMODE) != O_RDWR)
144 || (oflag & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC)))
145 {
146 debug_printf ("Invalid oflag 0%o", oflag);
147 set_errno (EINVAL);
148 return -1;
149 }
150
e70fdfb9 151 return open (shmname, oflag | O_CLOEXEC, mode & 0777);
7b487ba9
CV
152}
153
154extern "C" int
155shm_unlink (const char *name)
156{
547ad329
CV
157 size_t len = strlen (name);
158 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 159
547ad329 160 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
161 return -1;
162
163 return unlink (shmname);
164}
165
166/* The POSIX message queue implementation is based on W. Richard STEVENS
167 implementation, just tweaked for Cygwin. The main change is
168 the usage of Windows mutexes and events instead of using the pthread
169 synchronization objects. The pathname is massaged so that the
170 files are created under /dev/mqueue. mq_timedsend and mq_timedreceive
171 are implemented additionally. */
172
7b487ba9
CV
173extern "C" mqd_t
174mq_open (const char *name, int oflag, ...)
175{
7b487ba9 176 va_list ap;
b62450cf 177 mode_t mode = 0;
723f38b1 178 fhandler_mqueue *fh = NULL;
b62450cf 179 struct mq_attr *attr = NULL;
7b487ba9 180
547ad329
CV
181 size_t len = strlen (name);
182 char mqname[ipc_names[mqueue].prefix_len + len];
183
184 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
185 return (mqd_t) -1;
186
3f3bd101
CV
187 __try
188 {
3f3bd101
CV
189 if (oflag & O_CREAT)
190 {
191 va_start (ap, oflag); /* init ap to final named argument */
192 mode = va_arg (ap, mode_t) & ~S_IXUSR;
193 attr = va_arg (ap, struct mq_attr *);
194 va_end (ap);
7b487ba9 195 }
7b487ba9 196
4fc922b2 197 /* Create file descriptor for mqueue */
b62450cf 198 cygheap_fdnew fd;
4fc922b2 199
b62450cf 200 if (fd < 0)
4fc922b2 201 __leave;
b62450cf
CV
202 fh = (fhandler_mqueue *) build_fh_name (mqname,
203 PC_OPEN | PC_POSIX
204 | PC_SYM_NOFOLLOW | PC_NULLEMPTY,
205 NULL);
4fc922b2
CV
206 if (!fh)
207 __leave;
4fc922b2 208
b62450cf
CV
209 if (fh->mq_open (oflag, mode, attr))
210 {
211 fd = fh;
212 return (mqd_t) fd;
213 }
7b487ba9 214 }
3f3bd101
CV
215 __except (EFAULT) {}
216 __endtry
bce3563e 217 if (fh)
b62450cf 218 delete fh;
7b487ba9
CV
219 return (mqd_t) -1;
220}
221
222extern "C" int
223mq_getattr (mqd_t mqd, struct mq_attr *mqstat)
224{
46f3b0ce 225 int ret = -1;
7b487ba9 226
46f3b0ce
CV
227 cygheap_fdget fd ((int) mqd, true);
228 fhandler_mqueue *fh = fd->is_mqueue ();
229 if (!fh)
230 set_errno (EBADF);
231 else
232 ret = fh->mq_getattr (mqstat);
233 return ret;
7b44665a 234}
7b487ba9
CV
235
236extern "C" int
237mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat)
238{
46f3b0ce 239 int ret = -1;
7b487ba9 240
46f3b0ce
CV
241 cygheap_fdget fd ((int) mqd, true);
242 fhandler_mqueue *fh = fd->is_mqueue ();
243 if (!fh)
244 set_errno (EBADF);
245 else
246 ret = fh->mq_setattr (mqstat, omqstat);
247 return ret;
7b487ba9
CV
248}
249
250extern "C" int
251mq_notify (mqd_t mqd, const struct sigevent *notification)
252{
46f3b0ce 253 int ret = -1;
7b44665a 254
46f3b0ce
CV
255 cygheap_fdget fd ((int) mqd, true);
256 fhandler_mqueue *fh = fd->is_mqueue ();
257 if (!fh)
258 set_errno (EBADF);
259 else
260 ret = fh->mq_notify (notification);
261 return ret;
7b44665a 262}
7b487ba9 263
46f3b0ce
CV
264extern "C" int
265mq_timedsend (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
266 const struct timespec *abstime)
7b487ba9 267{
3f3bd101 268 int ret = -1;
7b487ba9 269
46f3b0ce
CV
270 cygheap_fdget fd ((int) mqd, true);
271 fhandler_mqueue *fh = fd->is_mqueue ();
272 if (!fh)
273 set_errno (EBADF);
274 else
275 ret = fh->mq_timedsend (ptr, len, prio, abstime);
3f3bd101 276 return ret;
7b487ba9
CV
277}
278
279extern "C" int
280mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio)
281{
46f3b0ce 282 return mq_timedsend (mqd, ptr, len, prio, NULL);
7b487ba9
CV
283}
284
46f3b0ce
CV
285extern "C" ssize_t
286mq_timedreceive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
7b487ba9
CV
287 const struct timespec *abstime)
288{
46f3b0ce 289 int ret = -1;
7b487ba9 290
46f3b0ce
CV
291 cygheap_fdget fd ((int) mqd, true);
292 fhandler_mqueue *fh = fd->is_mqueue ();
293 if (!fh)
294 set_errno (EBADF);
295 else
296 ret = fh->mq_timedrecv (ptr, maxlen, priop, abstime);
297 return ret;
7b487ba9
CV
298}
299
300extern "C" ssize_t
301mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop)
302{
46f3b0ce 303 return mq_timedreceive (mqd, ptr, maxlen, priop, NULL);
7b487ba9
CV
304}
305
306extern "C" int
307mq_close (mqd_t mqd)
308{
3f3bd101 309 __try
7b487ba9 310 {
4fc922b2 311 cygheap_fdget fd ((int) mqd, true);
2cc914a3 312 if (!fd->is_mqueue ())
3f3bd101
CV
313 {
314 set_errno (EBADF);
315 __leave;
316 }
7b487ba9 317
3f3bd101
CV
318 if (mq_notify (mqd, NULL)) /* unregister calling process */
319 __leave;
7b487ba9 320
4fc922b2
CV
321 fd->isclosed (true);
322 fd->close ();
323 fd.release ();
3f3bd101
CV
324 return 0;
325 }
326 __except (EBADF) {}
327 __endtry
328 return -1;
7b487ba9
CV
329}
330
331extern "C" int
332mq_unlink (const char *name)
333{
547ad329
CV
334 size_t len = strlen (name);
335 char mqname[ipc_names[mqueue].prefix_len + len];
7b487ba9 336
547ad329 337 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
338 return -1;
339 if (unlink (mqname) == -1)
340 return -1;
341 return 0;
342}
343
8fbd574e
CV
344/* POSIX named semaphore implementation. Loosely based on Richard W. STEPHENS
345 implementation as far as sem_open is concerned, but under the hood using
346 the already existing semaphore class in thread.cc. Using a file backed
347 solution allows to implement kernel persistent named semaphores. */
348
b62450cf
CV
349#define MAX_TRIES 10 /* for waiting for initialization */
350
8fbd574e
CV
351struct sem_finfo
352{
353 unsigned int value;
354 unsigned long long hash;
355 LUID luid;
356};
357
358extern "C" sem_t *
359sem_open (const char *name, int oflag, ...)
360{
3f3bd101 361 int i, fd = -1, created = 0;
8fbd574e
CV
362 va_list ap;
363 mode_t mode = 0;
364 unsigned int value = 0;
61522196 365 struct stat statbuff;
8fbd574e
CV
366 sem_t *sem = SEM_FAILED;
367 sem_finfo sf;
8fbd574e
CV
368 bool wasopen = false;
369 ipc_flock file;
370
547ad329
CV
371 size_t len = strlen (name);
372 char semname[ipc_names[semaphore].prefix_len + len];
373
374 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
375 return SEM_FAILED;
376
3f3bd101 377 __try
8fbd574e 378 {
3f3bd101
CV
379 oflag &= (O_CREAT | O_EXCL);
380
381 again:
382 if (oflag & O_CREAT)
7b44665a 383 {
3f3bd101
CV
384 va_start (ap, oflag); /* init ap to final named argument */
385 mode = va_arg (ap, mode_t) & ~S_IXUSR;
386 value = va_arg (ap, unsigned int);
387 va_end (ap);
388
389 /* Open and specify O_EXCL and user-execute */
390 fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC,
391 mode | S_IXUSR);
392 if (fd < 0)
393 {
394 if (errno == EEXIST && (oflag & O_EXCL) == 0)
395 goto exists; /* already exists, OK */
396 return SEM_FAILED;
397 }
398 created = 1;
399 /* First one to create the file initializes it. */
400 NtAllocateLocallyUniqueId (&sf.luid);
401 sf.value = value;
402 sf.hash = hash_path_name (0, semname);
403 if (write (fd, &sf, sizeof sf) != sizeof sf)
404 __leave;
405 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value,
406 wasopen);
407 if (sem == SEM_FAILED)
408 __leave;
409 /* Initialization complete, turn off user-execute bit */
410 if (fchmod (fd, mode) == -1)
411 __leave;
412 /* Don't close (fd); */
413 return sem;
8fbd574e 414 }
8fbd574e 415
3f3bd101
CV
416 exists:
417 /* Open the file and fetch the semaphore name. */
418 if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0)
8fbd574e
CV
419 {
420 if (errno == ENOENT && (oflag & O_CREAT))
3f3bd101
CV
421 goto again;
422 __leave;
423 }
424 /* Make certain initialization is complete */
425 for (i = 0; i < MAX_TRIES; i++)
426 {
2d9b4876 427 if (stat (semname, &statbuff) == -1)
8fbd574e 428 {
3f3bd101
CV
429 if (errno == ENOENT && (oflag & O_CREAT))
430 {
431 close (fd);
432 fd = -1;
433 goto again;
434 }
435 __leave;
8fbd574e 436 }
3f3bd101
CV
437 if ((statbuff.st_mode & S_IXUSR) == 0)
438 break;
439 sleep (1);
8fbd574e 440 }
3f3bd101
CV
441 if (i == MAX_TRIES)
442 {
443 set_errno (ETIMEDOUT);
444 __leave;
445 }
446 if (file.lock (fd, sizeof sf))
447 __leave;
448 if (read (fd, &sf, sizeof sf) != sizeof sf)
449 __leave;
450 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value,
451 wasopen);
452 file.unlock (fd);
453 if (sem == SEM_FAILED)
454 __leave;
455 /* If wasopen is set, the semaphore was already opened and we already have
456 an open file descriptor pointing to the file. This means, we have to
457 close the file descriptor created in this call. It won't be stored
458 anywhere anyway. */
459 if (wasopen)
460 close (fd);
461 return sem;
8fbd574e 462 }
3f3bd101
CV
463 __except (EFAULT) {}
464 __endtry
8fbd574e
CV
465 /* Don't let following function calls change errno */
466 save_errno save;
467
3f3bd101
CV
468 if (fd >= 0)
469 file.unlock (fd);
8fbd574e
CV
470 if (created)
471 unlink (semname);
472 if (sem != SEM_FAILED)
473 semaphore::close (sem);
599d462d
CV
474 if (fd >= 0)
475 close (fd);
8fbd574e
CV
476 return SEM_FAILED;
477}
478
2d9b4876 479extern "C" off_t lseek (int, off_t, int);
b62450cf 480
8fbd574e
CV
481int
482_sem_close (sem_t *sem, bool do_close)
483{
484 sem_finfo sf;
485 int fd, ret = -1;
486 ipc_flock file;
487
488 if (semaphore::getinternal (sem, &fd, &sf.hash, &sf.luid, &sf.value) == -1)
489 return -1;
490 if (!file.lock (fd, sizeof sf)
2d9b4876 491 && lseek (fd, 0LL, SEEK_SET) != (off_t) -1
8fbd574e
CV
492 && write (fd, &sf, sizeof sf) == sizeof sf)
493 ret = do_close ? semaphore::close (sem) : 0;
494
495 /* Don't let following function calls change errno */
496 save_errno save;
497 file.unlock (fd);
498 close (fd);
499
500 return ret;
501}
502
503extern "C" int
504sem_close (sem_t *sem)
505{
506 return _sem_close (sem, true);
507}
508
509extern "C" int
510sem_unlink (const char *name)
511{
547ad329
CV
512 size_t len = strlen (name);
513 char semname[ipc_names[semaphore].prefix_len + len];
8fbd574e 514
547ad329 515 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
516 return -1;
517 if (unlink (semname) == -1)
518 return -1;
519 return 0;
520}
bf1d972d
CV
521
522extern "C" int
523sem_init (sem_t * sem, int pshared, unsigned int value)
524{
525 return semaphore::init (sem, pshared, value);
526}
527
528extern "C" int
529sem_destroy (sem_t * sem)
530{
531 return semaphore::destroy (sem);
532}
533
534extern "C" int
535sem_wait (sem_t * sem)
536{
537 return semaphore::wait (sem);
538}
539
540extern "C" int
541sem_trywait (sem_t * sem)
542{
543 return semaphore::trywait (sem);
544}
545
546extern "C" int
547sem_clockwait (sem_t * sem, clockid_t clock_id, const struct timespec *abstime)
548{
549 return semaphore::clockwait (sem, clock_id, abstime);
550}
551
552extern "C" int
553sem_timedwait (sem_t * sem, const struct timespec *abstime)
554{
555 return semaphore::clockwait (sem, CLOCK_REALTIME, abstime);
556}
557
558extern "C" int
559sem_post (sem_t *sem)
560{
561 return semaphore::post (sem);
562}
563
564extern "C" int
565sem_getvalue (sem_t * sem, int *sval)
566{
567 return semaphore::getvalue (sem, sval);
568}
This page took 0.489241 seconds and 6 git commands to generate.