]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/posix_ipc.cc
Cygwin: POSIX msg queues: implement open/mq_open entirely in fhandler
[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
7b487ba9 107static int
c6d1382a 108ipc_mutex_lock (HANDLE mtx, bool eintr)
7b487ba9 109{
c6d1382a
CV
110 switch (cygwait (mtx, cw_infinite, cw_cancel | cw_cancel_self
111 | (eintr ? cw_sig_eintr : cw_sig_restart)))
7b44665a 112 {
7b487ba9
CV
113 case WAIT_OBJECT_0:
114 case WAIT_ABANDONED_0:
115 return 0;
962f9a2c 116 case WAIT_SIGNALED:
7b487ba9
CV
117 set_errno (EINTR);
118 return 1;
119 default:
120 break;
7b44665a 121 }
7b487ba9
CV
122 return geterrno_from_win_error ();
123}
124
125static inline int
126ipc_mutex_unlock (HANDLE mtx)
127{
128 return ReleaseMutex (mtx) ? 0 : geterrno_from_win_error ();
129}
130
7b487ba9
CV
131static int
132ipc_cond_timedwait (HANDLE evt, HANDLE mtx, const struct timespec *abstime)
133{
962f9a2c 134 HANDLE w4[4] = { evt, };
86bf572e 135 DWORD cnt = 2;
206a6ee9 136 DWORD timer_idx = 0;
86bf572e
CV
137 int ret = 0;
138
c43e9340 139 wait_signal_arrived here (w4[1]);
a91ac4dc
CV
140 if ((w4[cnt] = pthread::get_cancel_event ()) != NULL)
141 ++cnt;
86bf572e
CV
142 if (abstime)
143 {
397526de 144 if (!valid_timespec (*abstime))
86bf572e
CV
145 return EINVAL;
146
147 /* If a timeout is set, we create a waitable timer to wait for.
148 This is the easiest way to handle the absolute timeout value, given
149 that NtSetTimer also takes absolute times and given the double
150 dependency on evt *and* mtx, which requires to call WFMO twice. */
151 NTSTATUS status;
152 LARGE_INTEGER duetime;
153
206a6ee9
CV
154 timer_idx = cnt++;
155 status = NtCreateTimer (&w4[timer_idx], TIMER_ALL_ACCESS, NULL,
86bf572e
CV
156 NotificationTimer);
157 if (!NT_SUCCESS (status))
158 return geterrno_from_nt_status (status);
0ff057d5 159 timespec_to_filetime (abstime, &duetime);
206a6ee9 160 status = NtSetTimer (w4[timer_idx], &duetime, NULL, NULL, FALSE, 0, NULL);
86bf572e
CV
161 if (!NT_SUCCESS (status))
162 {
206a6ee9 163 NtClose (w4[timer_idx]);
86bf572e
CV
164 return geterrno_from_nt_status (status);
165 }
7b487ba9 166 }
3748b3e8 167 ResetEvent (evt);
86bf572e
CV
168 if ((ret = ipc_mutex_unlock (mtx)) != 0)
169 return ret;
170 /* Everything's set up, so now wait for the event to be signalled. */
171restart1:
172 switch (WaitForMultipleObjects (cnt, w4, FALSE, INFINITE))
7b44665a 173 {
7b487ba9 174 case WAIT_OBJECT_0:
86bf572e
CV
175 break;
176 case WAIT_OBJECT_0 + 1:
177 if (_my_tls.call_signal_handler ())
178 goto restart1;
179 ret = EINTR;
180 break;
181 case WAIT_OBJECT_0 + 2:
206a6ee9 182 if (timer_idx != 2)
b86f999a 183 pthread::static_cancel_self ();
50ad1980 184 fallthrough;
206a6ee9 185 case WAIT_OBJECT_0 + 3:
86bf572e
CV
186 ret = ETIMEDOUT;
187 break;
7b487ba9 188 default:
86bf572e 189 ret = geterrno_from_win_error ();
7b487ba9 190 break;
7b44665a 191 }
86bf572e
CV
192 if (ret == 0)
193 {
194 /* At this point we need to lock the mutex. The wait is practically
195 the same as before, just that we now wait on the mutex instead of the
196 event. */
197 restart2:
198 w4[0] = mtx;
199 switch (WaitForMultipleObjects (cnt, w4, FALSE, INFINITE))
200 {
201 case WAIT_OBJECT_0:
202 case WAIT_ABANDONED_0:
203 break;
204 case WAIT_OBJECT_0 + 1:
205 if (_my_tls.call_signal_handler ())
206 goto restart2;
207 ret = EINTR;
208 break;
209 case WAIT_OBJECT_0 + 2:
206a6ee9
CV
210 if (timer_idx != 2)
211 pthread_testcancel ();
50ad1980 212 fallthrough;
206a6ee9 213 case WAIT_OBJECT_0 + 3:
86bf572e
CV
214 ret = ETIMEDOUT;
215 break;
216 default:
217 ret = geterrno_from_win_error ();
218 break;
219 }
220 }
206a6ee9 221 if (timer_idx)
86bf572e
CV
222 {
223 if (ret != ETIMEDOUT)
206a6ee9
CV
224 NtCancelTimer (w4[timer_idx], NULL);
225 NtClose (w4[timer_idx]);
86bf572e
CV
226 }
227 return ret;
7b487ba9
CV
228}
229
86bf572e 230static inline void
7b487ba9
CV
231ipc_cond_signal (HANDLE evt)
232{
86bf572e 233 SetEvent (evt);
7b487ba9
CV
234}
235
8fbd574e
CV
236class ipc_flock
237{
61522196 238 struct flock fl;
8fbd574e
CV
239
240public:
241 ipc_flock () { memset (&fl, 0, sizeof fl); }
242
243 int lock (int fd, size_t size)
244 {
245 fl.l_type = F_WRLCK;
246 fl.l_whence = SEEK_SET;
247 fl.l_start = 0;
248 fl.l_len = size;
fabfb1a1 249 return fcntl64 (fd, F_SETLKW, &fl);
8fbd574e
CV
250 }
251 int unlock (int fd)
252 {
253 if (!fl.l_len)
254 return 0;
255 fl.l_type = F_UNLCK;
fabfb1a1 256 return fcntl64 (fd, F_SETLKW, &fl);
8fbd574e
CV
257 }
258};
259
7b487ba9
CV
260/* POSIX shared memory object implementation. */
261
262extern "C" int
263shm_open (const char *name, int oflag, mode_t mode)
264{
547ad329
CV
265 size_t len = strlen (name);
266 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 267
547ad329 268 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
269 return -1;
270
271 /* Check for valid flags. */
272 if (((oflag & O_ACCMODE) != O_RDONLY && (oflag & O_ACCMODE) != O_RDWR)
273 || (oflag & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC)))
274 {
275 debug_printf ("Invalid oflag 0%o", oflag);
276 set_errno (EINVAL);
277 return -1;
278 }
279
e70fdfb9 280 return open (shmname, oflag | O_CLOEXEC, mode & 0777);
7b487ba9
CV
281}
282
283extern "C" int
284shm_unlink (const char *name)
285{
547ad329
CV
286 size_t len = strlen (name);
287 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 288
547ad329 289 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
290 return -1;
291
292 return unlink (shmname);
293}
294
295/* The POSIX message queue implementation is based on W. Richard STEVENS
296 implementation, just tweaked for Cygwin. The main change is
297 the usage of Windows mutexes and events instead of using the pthread
298 synchronization objects. The pathname is massaged so that the
299 files are created under /dev/mqueue. mq_timedsend and mq_timedreceive
300 are implemented additionally. */
301
7b487ba9
CV
302extern "C" mqd_t
303mq_open (const char *name, int oflag, ...)
304{
7b487ba9 305 va_list ap;
b62450cf 306 mode_t mode = 0;
723f38b1 307 fhandler_mqueue *fh = NULL;
b62450cf 308 struct mq_attr *attr = NULL;
7b487ba9 309
547ad329
CV
310 size_t len = strlen (name);
311 char mqname[ipc_names[mqueue].prefix_len + len];
312
313 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
314 return (mqd_t) -1;
315
3f3bd101
CV
316 __try
317 {
3f3bd101
CV
318 if (oflag & O_CREAT)
319 {
320 va_start (ap, oflag); /* init ap to final named argument */
321 mode = va_arg (ap, mode_t) & ~S_IXUSR;
322 attr = va_arg (ap, struct mq_attr *);
323 va_end (ap);
7b487ba9 324 }
7b487ba9 325
4fc922b2 326 /* Create file descriptor for mqueue */
b62450cf 327 cygheap_fdnew fd;
4fc922b2 328
b62450cf 329 if (fd < 0)
4fc922b2 330 __leave;
b62450cf
CV
331 fh = (fhandler_mqueue *) build_fh_name (mqname,
332 PC_OPEN | PC_POSIX
333 | PC_SYM_NOFOLLOW | PC_NULLEMPTY,
334 NULL);
4fc922b2
CV
335 if (!fh)
336 __leave;
4fc922b2 337
b62450cf
CV
338 if (fh->mq_open (oflag, mode, attr))
339 {
340 fd = fh;
341 return (mqd_t) fd;
342 }
7b487ba9 343 }
3f3bd101
CV
344 __except (EFAULT) {}
345 __endtry
bce3563e 346 if (fh)
b62450cf 347 delete fh;
7b487ba9
CV
348 return (mqd_t) -1;
349}
350
4fc922b2
CV
351static struct mq_info *
352get_mqinfo (cygheap_fdget &fd)
353{
354 if (fd >= 0)
355 {
356 fhandler_mqueue *fh = fd->is_mqueue ();
a4e07467 357 if (fh)
4fc922b2 358 return fh->mqinfo ();
a4e07467 359 set_errno (EINVAL);
4fc922b2
CV
360 }
361 return NULL;
362}
363
7b487ba9
CV
364extern "C" int
365mq_getattr (mqd_t mqd, struct mq_attr *mqstat)
366{
367 int n;
368 struct mq_hdr *mqhdr;
61522196 369 struct mq_fattr *attr;
7b487ba9 370 struct mq_info *mqinfo;
7b44665a 371
3f3bd101 372 __try
7b487ba9 373 {
4fc922b2
CV
374 cygheap_fdget fd ((int) mqd, true);
375 mqinfo = get_mqinfo (fd);
3f3bd101
CV
376 if (mqinfo->mqi_magic != MQI_MAGIC)
377 {
378 set_errno (EBADF);
379 __leave;
380 }
381 mqhdr = mqinfo->mqi_hdr;
382 attr = &mqhdr->mqh_attr;
c6d1382a 383 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, false)) != 0)
3f3bd101
CV
384 {
385 errno = n;
386 __leave;
387 }
388 mqstat->mq_flags = mqinfo->mqi_flags; /* per-open */
389 mqstat->mq_maxmsg = attr->mq_maxmsg; /* remaining three per-queue */
390 mqstat->mq_msgsize = attr->mq_msgsize;
391 mqstat->mq_curmsgs = attr->mq_curmsgs;
7b487ba9 392
3f3bd101
CV
393 ipc_mutex_unlock (mqinfo->mqi_lock);
394 return 0;
395 }
396 __except (EBADF) {}
397 __endtry
398 return -1;
7b44665a 399}
7b487ba9
CV
400
401extern "C" int
402mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat)
403{
404 int n;
7b44665a 405 struct mq_hdr *mqhdr;
61522196 406 struct mq_fattr *attr;
7b487ba9
CV
407 struct mq_info *mqinfo;
408
3f3bd101 409 __try
7b487ba9 410 {
4fc922b2
CV
411 cygheap_fdget fd ((int) mqd, true);
412 mqinfo = get_mqinfo (fd);
3f3bd101
CV
413 if (mqinfo->mqi_magic != MQI_MAGIC)
414 {
415 set_errno (EBADF);
416 __leave;
417 }
418 mqhdr = mqinfo->mqi_hdr;
419 attr = &mqhdr->mqh_attr;
c6d1382a 420 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, false)) != 0)
3f3bd101
CV
421 {
422 errno = n;
423 __leave;
424 }
7b487ba9 425
3f3bd101
CV
426 if (omqstat != NULL)
427 {
428 omqstat->mq_flags = mqinfo->mqi_flags; /* previous attributes */
429 omqstat->mq_maxmsg = attr->mq_maxmsg;
430 omqstat->mq_msgsize = attr->mq_msgsize;
431 omqstat->mq_curmsgs = attr->mq_curmsgs; /* and current status */
432 }
7b487ba9 433
3f3bd101
CV
434 if (mqstat->mq_flags & O_NONBLOCK)
435 mqinfo->mqi_flags |= O_NONBLOCK;
436 else
437 mqinfo->mqi_flags &= ~O_NONBLOCK;
7b487ba9 438
3f3bd101
CV
439 ipc_mutex_unlock (mqinfo->mqi_lock);
440 return 0;
441 }
442 __except (EBADF) {}
443 __endtry
444 return -1;
7b487ba9
CV
445}
446
447extern "C" int
448mq_notify (mqd_t mqd, const struct sigevent *notification)
449{
450 int n;
451 pid_t pid;
452 struct mq_hdr *mqhdr;
453 struct mq_info *mqinfo;
7b44665a 454
3f3bd101 455 __try
7b487ba9 456 {
4fc922b2
CV
457 cygheap_fdget fd ((int) mqd, true);
458 mqinfo = get_mqinfo (fd);
3f3bd101
CV
459 if (mqinfo->mqi_magic != MQI_MAGIC)
460 {
461 set_errno (EBADF);
462 __leave;
463 }
464 mqhdr = mqinfo->mqi_hdr;
c6d1382a 465 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, false)) != 0)
3f3bd101
CV
466 {
467 errno = n;
468 __leave;
469 }
7b44665a 470
3f3bd101
CV
471 pid = getpid ();
472 if (!notification)
7b487ba9 473 {
3f3bd101
CV
474 if (mqhdr->mqh_pid == pid)
475 mqhdr->mqh_pid = 0; /* unregister calling process */
476 }
477 else
478 {
479 if (mqhdr->mqh_pid != 0)
7b487ba9 480 {
3f3bd101
CV
481 if (kill (mqhdr->mqh_pid, 0) != -1 || errno != ESRCH)
482 {
483 set_errno (EBUSY);
484 ipc_mutex_unlock (mqinfo->mqi_lock);
485 __leave;
486 }
7b487ba9 487 }
3f3bd101
CV
488 mqhdr->mqh_pid = pid;
489 mqhdr->mqh_event = *notification;
7b487ba9 490 }
3f3bd101
CV
491 ipc_mutex_unlock (mqinfo->mqi_lock);
492 return 0;
7b44665a 493 }
3f3bd101
CV
494 __except (EBADF) {}
495 __endtry
496 return -1;
7b44665a 497}
7b487ba9
CV
498
499static int
500_mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
501 const struct timespec *abstime)
502{
503 int n;
504 long index, freeindex;
505 int8_t *mptr;
506 struct sigevent *sigev;
507 struct mq_hdr *mqhdr;
61522196 508 struct mq_fattr *attr;
7b487ba9 509 struct msg_hdr *msghdr, *nmsghdr, *pmsghdr;
3f3bd101
CV
510 struct mq_info *mqinfo = NULL;
511 bool ipc_mutex_locked = false;
512 int ret = -1;
7b487ba9 513
74f9ac5b
CV
514 pthread_testcancel ();
515
3f3bd101 516 __try
7b487ba9 517 {
4fc922b2
CV
518 cygheap_fdget fd ((int) mqd);
519 mqinfo = get_mqinfo (fd);
3f3bd101
CV
520 if (mqinfo->mqi_magic != MQI_MAGIC)
521 {
522 set_errno (EBADF);
523 __leave;
524 }
4f89f24f 525 if (prio >= MQ_PRIO_MAX)
3f3bd101
CV
526 {
527 set_errno (EINVAL);
528 __leave;
529 }
7b487ba9 530
3f3bd101
CV
531 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
532 mptr = (int8_t *) mqhdr; /* byte pointer */
533 attr = &mqhdr->mqh_attr;
c6d1382a 534 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, true)) != 0)
7b487ba9 535 {
3f3bd101
CV
536 errno = n;
537 __leave;
7b487ba9 538 }
3f3bd101
CV
539 ipc_mutex_locked = true;
540 if (len > (size_t) attr->mq_msgsize)
7b487ba9 541 {
3f3bd101
CV
542 set_errno (EMSGSIZE);
543 __leave;
7b487ba9 544 }
3f3bd101 545 if (attr->mq_curmsgs == 0)
b994b837 546 {
3f3bd101 547 if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0)
b994b837 548 {
3f3bd101
CV
549 sigev = &mqhdr->mqh_event;
550 if (sigev->sigev_notify == SIGEV_SIGNAL)
551 sigqueue (mqhdr->mqh_pid, sigev->sigev_signo,
552 sigev->sigev_value);
553 mqhdr->mqh_pid = 0; /* unregister */
554 }
555 }
556 else if (attr->mq_curmsgs >= attr->mq_maxmsg)
557 {
558 /* Queue is full */
559 if (mqinfo->mqi_flags & O_NONBLOCK)
560 {
561 set_errno (EAGAIN);
562 __leave;
563 }
564 /* Wait for room for one message on the queue */
565 while (attr->mq_curmsgs >= attr->mq_maxmsg)
566 {
567 int ret = ipc_cond_timedwait (mqinfo->mqi_waitsend,
568 mqinfo->mqi_lock, abstime);
569 if (ret != 0)
570 {
571 set_errno (ret);
572 __leave;
573 }
b994b837
CV
574 }
575 }
7b487ba9 576
3f3bd101
CV
577 /* nmsghdr will point to new message */
578 if ((freeindex = mqhdr->mqh_free) == 0)
579 api_fatal ("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs);
7b487ba9 580
3f3bd101
CV
581 nmsghdr = (struct msg_hdr *) &mptr[freeindex];
582 nmsghdr->msg_prio = prio;
583 nmsghdr->msg_len = len;
584 memcpy (nmsghdr + 1, ptr, len); /* copy message from caller */
585 mqhdr->mqh_free = nmsghdr->msg_next; /* new freelist head */
7b487ba9 586
3f3bd101
CV
587 /* Find right place for message in linked list */
588 index = mqhdr->mqh_head;
589 pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head);
590 while (index)
7b487ba9 591 {
3f3bd101
CV
592 msghdr = (struct msg_hdr *) &mptr[index];
593 if (prio > msghdr->msg_prio)
594 {
595 nmsghdr->msg_next = index;
596 pmsghdr->msg_next = freeindex;
597 break;
598 }
599 index = msghdr->msg_next;
600 pmsghdr = msghdr;
601 }
602 if (index == 0)
603 {
604 /* Queue was empty or new goes at end of list */
7b487ba9 605 pmsghdr->msg_next = freeindex;
3f3bd101 606 nmsghdr->msg_next = 0;
7b487ba9 607 }
3f3bd101
CV
608 /* Wake up anyone blocked in mq_receive waiting for a message */
609 if (attr->mq_curmsgs == 0)
610 ipc_cond_signal (mqinfo->mqi_waitrecv);
611 attr->mq_curmsgs++;
7b487ba9 612
3f3bd101
CV
613 ret = 0;
614 }
615 __except (EBADF) {}
616 __endtry
617 if (ipc_mutex_locked)
618 ipc_mutex_unlock (mqinfo->mqi_lock);
619 return ret;
7b487ba9
CV
620}
621
622extern "C" int
623mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio)
624{
625 return _mq_send (mqd, ptr, len, prio, NULL);
626}
627
628extern "C" int
629mq_timedsend (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
630 const struct timespec *abstime)
631{
632 return _mq_send (mqd, ptr, len, prio, abstime);
633}
634
635static ssize_t
636_mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
637 const struct timespec *abstime)
638{
639 int n;
640 long index;
641 int8_t *mptr;
3f3bd101 642 ssize_t len = -1;
7b44665a 643 struct mq_hdr *mqhdr;
61522196 644 struct mq_fattr *attr;
7b487ba9 645 struct msg_hdr *msghdr;
4fc922b2 646 struct mq_info *mqinfo;
3f3bd101 647 bool ipc_mutex_locked = false;
7b487ba9 648
74f9ac5b
CV
649 pthread_testcancel ();
650
3f3bd101 651 __try
7b487ba9 652 {
4fc922b2
CV
653 cygheap_fdget fd ((int) mqd);
654 mqinfo = get_mqinfo (fd);
3f3bd101
CV
655 if (mqinfo->mqi_magic != MQI_MAGIC)
656 {
657 set_errno (EBADF);
658 __leave;
659 }
660 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
661 mptr = (int8_t *) mqhdr; /* byte pointer */
662 attr = &mqhdr->mqh_attr;
c6d1382a 663 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, true)) != 0)
3f3bd101
CV
664 {
665 errno = n;
666 __leave;
667 }
668 ipc_mutex_locked = true;
669 if (maxlen < (size_t) attr->mq_msgsize)
7b487ba9 670 {
3f3bd101
CV
671 set_errno (EMSGSIZE);
672 __leave;
7b487ba9 673 }
3f3bd101 674 if (attr->mq_curmsgs == 0) /* queue is empty */
b994b837 675 {
3f3bd101 676 if (mqinfo->mqi_flags & O_NONBLOCK)
b994b837 677 {
3f3bd101
CV
678 set_errno (EAGAIN);
679 __leave;
b994b837 680 }
3f3bd101
CV
681 /* Wait for a message to be placed onto queue */
682 mqhdr->mqh_nwait++;
683 while (attr->mq_curmsgs == 0)
684 {
685 int ret = ipc_cond_timedwait (mqinfo->mqi_waitrecv,
686 mqinfo->mqi_lock, abstime);
687 if (ret != 0)
688 {
689 set_errno (ret);
690 __leave;
691 }
692 }
693 mqhdr->mqh_nwait--;
b994b837 694 }
7b487ba9 695
3f3bd101
CV
696 if ((index = mqhdr->mqh_head) == 0)
697 api_fatal ("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs);
7b487ba9 698
3f3bd101
CV
699 msghdr = (struct msg_hdr *) &mptr[index];
700 mqhdr->mqh_head = msghdr->msg_next; /* new head of list */
701 len = msghdr->msg_len;
702 memcpy(ptr, msghdr + 1, len); /* copy the message itself */
703 if (priop != NULL)
704 *priop = msghdr->msg_prio;
705
706 /* Just-read message goes to front of free list */
707 msghdr->msg_next = mqhdr->mqh_free;
708 mqhdr->mqh_free = index;
7b487ba9 709
3f3bd101
CV
710 /* Wake up anyone blocked in mq_send waiting for room */
711 if (attr->mq_curmsgs == attr->mq_maxmsg)
712 ipc_cond_signal (mqinfo->mqi_waitsend);
713 attr->mq_curmsgs--;
3f3bd101
CV
714 }
715 __except (EBADF) {}
716 __endtry
717 if (ipc_mutex_locked)
718 ipc_mutex_unlock (mqinfo->mqi_lock);
7b487ba9 719 return len;
7b487ba9
CV
720}
721
722extern "C" ssize_t
723mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop)
724{
725 return _mq_receive (mqd, ptr, maxlen, priop, NULL);
726}
727
728extern "C" ssize_t
729mq_timedreceive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
730 const struct timespec *abstime)
731{
732 return _mq_receive (mqd, ptr, maxlen, priop, abstime);
733}
734
735extern "C" int
736mq_close (mqd_t mqd)
737{
3f3bd101 738 __try
7b487ba9 739 {
4fc922b2 740 cygheap_fdget fd ((int) mqd, true);
2cc914a3 741 if (!fd->is_mqueue ())
3f3bd101
CV
742 {
743 set_errno (EBADF);
744 __leave;
745 }
7b487ba9 746
3f3bd101
CV
747 if (mq_notify (mqd, NULL)) /* unregister calling process */
748 __leave;
7b487ba9 749
4fc922b2
CV
750 fd->isclosed (true);
751 fd->close ();
752 fd.release ();
3f3bd101
CV
753 return 0;
754 }
755 __except (EBADF) {}
756 __endtry
757 return -1;
7b487ba9
CV
758}
759
760extern "C" int
761mq_unlink (const char *name)
762{
547ad329
CV
763 size_t len = strlen (name);
764 char mqname[ipc_names[mqueue].prefix_len + len];
7b487ba9 765
547ad329 766 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
767 return -1;
768 if (unlink (mqname) == -1)
769 return -1;
770 return 0;
771}
772
8fbd574e
CV
773/* POSIX named semaphore implementation. Loosely based on Richard W. STEPHENS
774 implementation as far as sem_open is concerned, but under the hood using
775 the already existing semaphore class in thread.cc. Using a file backed
776 solution allows to implement kernel persistent named semaphores. */
777
b62450cf
CV
778#define MAX_TRIES 10 /* for waiting for initialization */
779
8fbd574e
CV
780struct sem_finfo
781{
782 unsigned int value;
783 unsigned long long hash;
784 LUID luid;
785};
786
787extern "C" sem_t *
788sem_open (const char *name, int oflag, ...)
789{
3f3bd101 790 int i, fd = -1, created = 0;
8fbd574e
CV
791 va_list ap;
792 mode_t mode = 0;
793 unsigned int value = 0;
61522196 794 struct stat statbuff;
8fbd574e
CV
795 sem_t *sem = SEM_FAILED;
796 sem_finfo sf;
8fbd574e
CV
797 bool wasopen = false;
798 ipc_flock file;
799
547ad329
CV
800 size_t len = strlen (name);
801 char semname[ipc_names[semaphore].prefix_len + len];
802
803 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
804 return SEM_FAILED;
805
3f3bd101 806 __try
8fbd574e 807 {
3f3bd101
CV
808 oflag &= (O_CREAT | O_EXCL);
809
810 again:
811 if (oflag & O_CREAT)
7b44665a 812 {
3f3bd101
CV
813 va_start (ap, oflag); /* init ap to final named argument */
814 mode = va_arg (ap, mode_t) & ~S_IXUSR;
815 value = va_arg (ap, unsigned int);
816 va_end (ap);
817
818 /* Open and specify O_EXCL and user-execute */
819 fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC,
820 mode | S_IXUSR);
821 if (fd < 0)
822 {
823 if (errno == EEXIST && (oflag & O_EXCL) == 0)
824 goto exists; /* already exists, OK */
825 return SEM_FAILED;
826 }
827 created = 1;
828 /* First one to create the file initializes it. */
829 NtAllocateLocallyUniqueId (&sf.luid);
830 sf.value = value;
831 sf.hash = hash_path_name (0, semname);
832 if (write (fd, &sf, sizeof sf) != sizeof sf)
833 __leave;
834 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value,
835 wasopen);
836 if (sem == SEM_FAILED)
837 __leave;
838 /* Initialization complete, turn off user-execute bit */
839 if (fchmod (fd, mode) == -1)
840 __leave;
841 /* Don't close (fd); */
842 return sem;
8fbd574e 843 }
8fbd574e 844
3f3bd101
CV
845 exists:
846 /* Open the file and fetch the semaphore name. */
847 if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0)
8fbd574e
CV
848 {
849 if (errno == ENOENT && (oflag & O_CREAT))
3f3bd101
CV
850 goto again;
851 __leave;
852 }
853 /* Make certain initialization is complete */
854 for (i = 0; i < MAX_TRIES; i++)
855 {
856 if (stat64 (semname, &statbuff) == -1)
8fbd574e 857 {
3f3bd101
CV
858 if (errno == ENOENT && (oflag & O_CREAT))
859 {
860 close (fd);
861 fd = -1;
862 goto again;
863 }
864 __leave;
8fbd574e 865 }
3f3bd101
CV
866 if ((statbuff.st_mode & S_IXUSR) == 0)
867 break;
868 sleep (1);
8fbd574e 869 }
3f3bd101
CV
870 if (i == MAX_TRIES)
871 {
872 set_errno (ETIMEDOUT);
873 __leave;
874 }
875 if (file.lock (fd, sizeof sf))
876 __leave;
877 if (read (fd, &sf, sizeof sf) != sizeof sf)
878 __leave;
879 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value,
880 wasopen);
881 file.unlock (fd);
882 if (sem == SEM_FAILED)
883 __leave;
884 /* If wasopen is set, the semaphore was already opened and we already have
885 an open file descriptor pointing to the file. This means, we have to
886 close the file descriptor created in this call. It won't be stored
887 anywhere anyway. */
888 if (wasopen)
889 close (fd);
890 return sem;
8fbd574e 891 }
3f3bd101
CV
892 __except (EFAULT) {}
893 __endtry
8fbd574e
CV
894 /* Don't let following function calls change errno */
895 save_errno save;
896
3f3bd101
CV
897 if (fd >= 0)
898 file.unlock (fd);
8fbd574e
CV
899 if (created)
900 unlink (semname);
901 if (sem != SEM_FAILED)
902 semaphore::close (sem);
599d462d
CV
903 if (fd >= 0)
904 close (fd);
8fbd574e
CV
905 return SEM_FAILED;
906}
907
b62450cf
CV
908extern "C" off_t lseek64 (int, off_t, int);
909
8fbd574e
CV
910int
911_sem_close (sem_t *sem, bool do_close)
912{
913 sem_finfo sf;
914 int fd, ret = -1;
915 ipc_flock file;
916
917 if (semaphore::getinternal (sem, &fd, &sf.hash, &sf.luid, &sf.value) == -1)
918 return -1;
919 if (!file.lock (fd, sizeof sf)
61522196 920 && lseek64 (fd, 0LL, SEEK_SET) != (off_t) -1
8fbd574e
CV
921 && write (fd, &sf, sizeof sf) == sizeof sf)
922 ret = do_close ? semaphore::close (sem) : 0;
923
924 /* Don't let following function calls change errno */
925 save_errno save;
926 file.unlock (fd);
927 close (fd);
928
929 return ret;
930}
931
932extern "C" int
933sem_close (sem_t *sem)
934{
935 return _sem_close (sem, true);
936}
937
938extern "C" int
939sem_unlink (const char *name)
940{
547ad329
CV
941 size_t len = strlen (name);
942 char semname[ipc_names[semaphore].prefix_len + len];
8fbd574e 943
547ad329 944 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
945 return -1;
946 if (unlink (semname) == -1)
947 return -1;
948 return 0;
949}
This page took 0.408637 seconds and 5 git commands to generate.