]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/posix_ipc.cc
Fix race condition when waiting for a signal
[newlib-cygwin.git] / winsup / cygwin / posix_ipc.cc
1 /* posix_ipc.cc: POSIX IPC API for Cygwin.
2
3 Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2014, 2015 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include "shared_info.h"
13 #include "thread.h"
14 #include "path.h"
15 #include "cygtls.h"
16 #include "fhandler.h"
17 #include "dtable.h"
18 #include "cygheap.h"
19 #include "sigproc.h"
20 #include "ntdll.h"
21 #include <sys/mman.h>
22 #include <sys/param.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <mqueue.h>
26 #include <semaphore.h>
27
28 /* The prefix_len is the length of the path prefix ncluding trailing "/"
29 (or "/sem." for semaphores) as well as the trailing NUL. */
30 static struct
31 {
32 const char *prefix;
33 const size_t prefix_len;
34 const char *description;
35 } ipc_names[] = {
36 { "/dev/shm", 10, "POSIX shared memory object" },
37 { "/dev/mqueue", 13, "POSIX message queue" },
38 { "/dev/shm", 14, "POSIX semaphore" }
39 };
40
41 enum ipc_type_t
42 {
43 shmem,
44 mqueue,
45 semaphore
46 };
47
48 static bool
49 check_path (char *res_name, ipc_type_t type, const char *name, size_t len)
50 {
51 /* Note that we require the existance of the appropriate /dev subdirectories
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 }
69 /* Name must not be empty, or just be a single slash, or start with more
70 than one slash. Same for backslash.
71 Apart from handling backslash like slash, the naming rules are identical
72 to Linux, including the names and requirements for subdirectories, if
73 the name contains further slashes. */
74 if (!name || (strchr ("/\\", name[0])
75 && (!name[1] || strchr ("/\\", name[1]))))
76 {
77 debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
78 set_errno (EINVAL);
79 return false;
80 }
81 /* Skip leading (back-)slash. */
82 if (strchr ("/\\", name[0]))
83 ++name;
84 if (len > PATH_MAX - ipc_names[type].prefix_len)
85 {
86 debug_printf ("%s name '%s' too long", ipc_names[type].description, name);
87 set_errno (ENAMETOOLONG);
88 return false;
89 }
90 __small_sprintf (res_name, "%s/%s%s", ipc_names[type].prefix,
91 type == semaphore ? "sem." : "",
92 name);
93 return true;
94 }
95
96 static int
97 ipc_mutex_init (HANDLE *pmtx, const char *name)
98 {
99 WCHAR buf[MAX_PATH];
100 UNICODE_STRING uname;
101 OBJECT_ATTRIBUTES attr;
102 NTSTATUS status;
103
104 __small_swprintf (buf, L"mqueue/mtx_%s", name);
105 RtlInitUnicodeString (&uname, buf);
106 InitializeObjectAttributes (&attr, &uname,
107 OBJ_INHERIT | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
108 get_shared_parent_dir (),
109 everyone_sd (CYG_MUTANT_ACCESS));
110 status = NtCreateMutant (pmtx, CYG_MUTANT_ACCESS, &attr, FALSE);
111 if (!NT_SUCCESS (status))
112 {
113 debug_printf ("NtCreateMutant: %y", status);
114 return geterrno_from_win_error (RtlNtStatusToDosError (status));
115 }
116 return 0;
117 }
118
119 static int
120 ipc_mutex_lock (HANDLE mtx, bool eintr)
121 {
122 switch (cygwait (mtx, cw_infinite, cw_cancel | cw_cancel_self
123 | (eintr ? cw_sig_eintr : cw_sig_restart)))
124 {
125 case WAIT_OBJECT_0:
126 case WAIT_ABANDONED_0:
127 return 0;
128 case WAIT_SIGNALED:
129 set_errno (EINTR);
130 return 1;
131 default:
132 break;
133 }
134 return geterrno_from_win_error ();
135 }
136
137 static inline int
138 ipc_mutex_unlock (HANDLE mtx)
139 {
140 return ReleaseMutex (mtx) ? 0 : geterrno_from_win_error ();
141 }
142
143 static inline int
144 ipc_mutex_close (HANDLE mtx)
145 {
146 return CloseHandle (mtx) ? 0 : geterrno_from_win_error ();
147 }
148
149 static int
150 ipc_cond_init (HANDLE *pevt, const char *name, char sr)
151 {
152 WCHAR buf[MAX_PATH];
153 UNICODE_STRING uname;
154 OBJECT_ATTRIBUTES attr;
155 NTSTATUS status;
156
157 __small_swprintf (buf, L"mqueue/evt_%s%c", name, sr);
158 RtlInitUnicodeString (&uname, buf);
159 InitializeObjectAttributes (&attr, &uname,
160 OBJ_INHERIT | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
161 get_shared_parent_dir (),
162 everyone_sd (CYG_EVENT_ACCESS));
163 status = NtCreateEvent (pevt, CYG_EVENT_ACCESS, &attr,
164 NotificationEvent, FALSE);
165 if (!NT_SUCCESS (status))
166 {
167 debug_printf ("NtCreateEvent: %y", status);
168 return geterrno_from_win_error (RtlNtStatusToDosError (status));
169 }
170 return 0;
171 }
172
173 static int
174 ipc_cond_timedwait (HANDLE evt, HANDLE mtx, const struct timespec *abstime)
175 {
176 HANDLE w4[4] = { evt, };
177 DWORD cnt = 2;
178 DWORD timer_idx = 0;
179 int ret = 0;
180
181 wait_signal_arrived here (w4[1]);
182 if ((w4[cnt] = pthread::get_cancel_event ()) != NULL)
183 ++cnt;
184 if (abstime)
185 {
186 if (abstime->tv_sec < 0
187 || abstime->tv_nsec < 0
188 || abstime->tv_nsec > 999999999)
189 return EINVAL;
190
191 /* If a timeout is set, we create a waitable timer to wait for.
192 This is the easiest way to handle the absolute timeout value, given
193 that NtSetTimer also takes absolute times and given the double
194 dependency on evt *and* mtx, which requires to call WFMO twice. */
195 NTSTATUS status;
196 LARGE_INTEGER duetime;
197
198 timer_idx = cnt++;
199 status = NtCreateTimer (&w4[timer_idx], TIMER_ALL_ACCESS, NULL,
200 NotificationTimer);
201 if (!NT_SUCCESS (status))
202 return geterrno_from_nt_status (status);
203 timespec_to_filetime (abstime, &duetime);
204 status = NtSetTimer (w4[timer_idx], &duetime, NULL, NULL, FALSE, 0, NULL);
205 if (!NT_SUCCESS (status))
206 {
207 NtClose (w4[timer_idx]);
208 return geterrno_from_nt_status (status);
209 }
210 }
211 ResetEvent (evt);
212 if ((ret = ipc_mutex_unlock (mtx)) != 0)
213 return ret;
214 /* Everything's set up, so now wait for the event to be signalled. */
215 restart1:
216 switch (WaitForMultipleObjects (cnt, w4, FALSE, INFINITE))
217 {
218 case WAIT_OBJECT_0:
219 break;
220 case WAIT_OBJECT_0 + 1:
221 if (_my_tls.call_signal_handler ())
222 goto restart1;
223 ret = EINTR;
224 break;
225 case WAIT_OBJECT_0 + 2:
226 if (timer_idx != 2)
227 pthread::static_cancel_self ();
228 /*FALLTHRU*/
229 case WAIT_OBJECT_0 + 3:
230 ret = ETIMEDOUT;
231 break;
232 default:
233 ret = geterrno_from_win_error ();
234 break;
235 }
236 if (ret == 0)
237 {
238 /* At this point we need to lock the mutex. The wait is practically
239 the same as before, just that we now wait on the mutex instead of the
240 event. */
241 restart2:
242 w4[0] = mtx;
243 switch (WaitForMultipleObjects (cnt, w4, FALSE, INFINITE))
244 {
245 case WAIT_OBJECT_0:
246 case WAIT_ABANDONED_0:
247 break;
248 case WAIT_OBJECT_0 + 1:
249 if (_my_tls.call_signal_handler ())
250 goto restart2;
251 ret = EINTR;
252 break;
253 case WAIT_OBJECT_0 + 2:
254 if (timer_idx != 2)
255 pthread_testcancel ();
256 /*FALLTHRU*/
257 case WAIT_OBJECT_0 + 3:
258 ret = ETIMEDOUT;
259 break;
260 default:
261 ret = geterrno_from_win_error ();
262 break;
263 }
264 }
265 if (timer_idx)
266 {
267 if (ret != ETIMEDOUT)
268 NtCancelTimer (w4[timer_idx], NULL);
269 NtClose (w4[timer_idx]);
270 }
271 return ret;
272 }
273
274 static inline void
275 ipc_cond_signal (HANDLE evt)
276 {
277 SetEvent (evt);
278 }
279
280 static inline void
281 ipc_cond_close (HANDLE evt)
282 {
283 CloseHandle (evt);
284 }
285
286 class ipc_flock
287 {
288 struct flock fl;
289
290 public:
291 ipc_flock () { memset (&fl, 0, sizeof fl); }
292
293 int lock (int fd, size_t size)
294 {
295 fl.l_type = F_WRLCK;
296 fl.l_whence = SEEK_SET;
297 fl.l_start = 0;
298 fl.l_len = size;
299 return fcntl64 (fd, F_SETLKW, &fl);
300 }
301 int unlock (int fd)
302 {
303 if (!fl.l_len)
304 return 0;
305 fl.l_type = F_UNLCK;
306 return fcntl64 (fd, F_SETLKW, &fl);
307 }
308 };
309
310 /* POSIX shared memory object implementation. */
311
312 extern "C" int
313 shm_open (const char *name, int oflag, mode_t mode)
314 {
315 size_t len = strlen (name);
316 char shmname[ipc_names[shmem].prefix_len + len];
317
318 if (!check_path (shmname, shmem, name, len))
319 return -1;
320
321 /* Check for valid flags. */
322 if (((oflag & O_ACCMODE) != O_RDONLY && (oflag & O_ACCMODE) != O_RDWR)
323 || (oflag & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC)))
324 {
325 debug_printf ("Invalid oflag 0%o", oflag);
326 set_errno (EINVAL);
327 return -1;
328 }
329
330 return open (shmname, oflag | O_CLOEXEC, mode & 0777);
331 }
332
333 extern "C" int
334 shm_unlink (const char *name)
335 {
336 size_t len = strlen (name);
337 char shmname[ipc_names[shmem].prefix_len + len];
338
339 if (!check_path (shmname, shmem, name, len))
340 return -1;
341
342 return unlink (shmname);
343 }
344
345 /* The POSIX message queue implementation is based on W. Richard STEVENS
346 implementation, just tweaked for Cygwin. The main change is
347 the usage of Windows mutexes and events instead of using the pthread
348 synchronization objects. The pathname is massaged so that the
349 files are created under /dev/mqueue. mq_timedsend and mq_timedreceive
350 are implemented additionally. */
351
352 /* The mq_attr structure is defined using long datatypes per POSIX.
353 For interoperability reasons between 32 and 64 bit processes, we have
354 to make sure to use a unified structure layout in the message queue file.
355 That's what the mq_fattr is, the in-file representation of the mq_attr
356 struct. */
357 #pragma pack (push, 4)
358 struct mq_fattr
359 {
360 uint32_t mq_flags;
361 uint32_t mq_maxmsg;
362 uint32_t mq_msgsize;
363 uint32_t mq_curmsgs;
364 };
365
366 struct mq_hdr
367 {
368 struct mq_fattr mqh_attr; /* the queue's attributes */
369 int32_t mqh_head; /* index of first message */
370 int32_t mqh_free; /* index of first free message */
371 int32_t mqh_nwait; /* #threads blocked in mq_receive() */
372 pid_t mqh_pid; /* nonzero PID if mqh_event set */
373 char mqh_uname[36]; /* unique name used to identify synchronization
374 objects connected to this queue */
375 union {
376 struct sigevent mqh_event; /* for mq_notify() */
377 /* Make sure sigevent takes the same space on 32 and 64 bit systems.
378 Other than that, it doesn't need to be compatible since only
379 one process can be notified at a time. */
380 uint64_t mqh_placeholder[8];
381 };
382 uint32_t mqh_magic; /* Expect MQI_MAGIC here, otherwise it's
383 an old-style message queue. */
384 };
385
386 struct msg_hdr
387 {
388 int32_t msg_next; /* index of next on linked list */
389 int32_t msg_len; /* actual length */
390 unsigned int msg_prio; /* priority */
391 };
392 #pragma pack (pop)
393
394 struct mq_info
395 {
396 struct mq_hdr *mqi_hdr; /* start of mmap'ed region */
397 uint32_t mqi_magic; /* magic number if open */
398 int mqi_flags; /* flags for this process */
399 HANDLE mqi_lock; /* mutex lock */
400 HANDLE mqi_waitsend; /* and condition variable for full queue */
401 HANDLE mqi_waitrecv; /* and condition variable for empty queue */
402 };
403
404 #define MQI_MAGIC 0x98765432UL
405
406 #define MSGSIZE(i) roundup((i), sizeof(long))
407
408 #define MAX_TRIES 10 /* for waiting for initialization */
409
410 struct mq_attr defattr = { 0, 10, 8192, 0 }; /* Linux defaults. */
411
412 extern "C" off_t lseek64 (int, off_t, int);
413 extern "C" void *mmap64 (void *, size_t, int, int, int, off_t);
414
415 extern "C" mqd_t
416 mq_open (const char *name, int oflag, ...)
417 {
418 int i, fd = -1, nonblock, created = 0;
419 long msgsize, index;
420 off_t filesize = 0;
421 va_list ap;
422 mode_t mode;
423 int8_t *mptr = (int8_t *) MAP_FAILED;
424 struct stat statbuff;
425 struct mq_hdr *mqhdr;
426 struct msg_hdr *msghdr;
427 struct mq_attr *attr;
428 struct mq_info *mqinfo = NULL;
429 LUID luid;
430
431 size_t len = strlen (name);
432 char mqname[ipc_names[mqueue].prefix_len + len];
433
434 if (!check_path (mqname, mqueue, name, len))
435 return (mqd_t) -1;
436
437 __try
438 {
439 oflag &= (O_CREAT | O_EXCL | O_NONBLOCK);
440 nonblock = oflag & O_NONBLOCK;
441 oflag &= ~O_NONBLOCK;
442
443 again:
444 if (oflag & O_CREAT)
445 {
446 va_start (ap, oflag); /* init ap to final named argument */
447 mode = va_arg (ap, mode_t) & ~S_IXUSR;
448 attr = va_arg (ap, struct mq_attr *);
449 va_end (ap);
450
451 /* Open and specify O_EXCL and user-execute */
452 fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC,
453 mode | S_IXUSR);
454 if (fd < 0)
455 {
456 if (errno == EEXIST && (oflag & O_EXCL) == 0)
457 goto exists; /* already exists, OK */
458 return (mqd_t) -1;
459 }
460 created = 1;
461 /* First one to create the file initializes it */
462 if (attr == NULL)
463 attr = &defattr;
464 /* Check minimum and maximum values. The max values are pretty much
465 arbitrary, taken from the linux mq_overview man page. However,
466 these max values make sure that the internal mq_fattr structure
467 can use 32 bit types. */
468 else if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768
469 || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576)
470 {
471 set_errno (EINVAL);
472 __leave;
473 }
474 /* Calculate and set the file size */
475 msgsize = MSGSIZE (attr->mq_msgsize);
476 filesize = sizeof (struct mq_hdr)
477 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
478 if (lseek64 (fd, filesize - 1, SEEK_SET) == -1)
479 __leave;
480 if (write (fd, "", 1) == -1)
481 __leave;
482
483 /* Memory map the file */
484 mptr = (int8_t *) mmap64 (NULL, (size_t) filesize,
485 PROT_READ | PROT_WRITE,
486 MAP_SHARED, fd, 0);
487 if (mptr == (int8_t *) MAP_FAILED)
488 __leave;
489
490 /* Allocate one mq_info{} for the queue */
491 if (!(mqinfo = (struct mq_info *)
492 calloc (1, sizeof (struct mq_info))))
493 __leave;
494 mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
495 mqinfo->mqi_magic = MQI_MAGIC;
496 mqinfo->mqi_flags = nonblock;
497
498 /* Initialize header at beginning of file */
499 /* Create free list with all messages on it */
500 mqhdr->mqh_attr.mq_flags = 0;
501 mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg;
502 mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize;
503 mqhdr->mqh_attr.mq_curmsgs = 0;
504 mqhdr->mqh_nwait = 0;
505 mqhdr->mqh_pid = 0;
506 NtAllocateLocallyUniqueId (&luid);
507 __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x",
508 hash_path_name (0,mqname),
509 luid.HighPart, luid.LowPart);
510 mqhdr->mqh_head = 0;
511 mqhdr->mqh_magic = MQI_MAGIC;
512 index = sizeof (struct mq_hdr);
513 mqhdr->mqh_free = index;
514 for (i = 0; i < attr->mq_maxmsg - 1; i++)
515 {
516 msghdr = (struct msg_hdr *) &mptr[index];
517 index += sizeof (struct msg_hdr) + msgsize;
518 msghdr->msg_next = index;
519 }
520 msghdr = (struct msg_hdr *) &mptr[index];
521 msghdr->msg_next = 0; /* end of free list */
522
523 /* Initialize mutex & condition variables */
524 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
525 if (i != 0)
526 {
527 set_errno (i);
528 __leave;
529 }
530 i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
531 if (i != 0)
532 {
533 set_errno (i);
534 __leave;
535 }
536 i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
537 if (i != 0)
538 {
539 set_errno (i);
540 __leave;
541 }
542 /* Initialization complete, turn off user-execute bit */
543 if (fchmod (fd, mode) == -1)
544 __leave;
545 close (fd);
546 return ((mqd_t) mqinfo);
547 }
548
549 exists:
550 /* Open the file then memory map */
551 if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0)
552 {
553 if (errno == ENOENT && (oflag & O_CREAT))
554 goto again;
555 __leave;
556 }
557 /* Make certain initialization is complete */
558 for (i = 0; i < MAX_TRIES; i++)
559 {
560 if (stat64 (mqname, &statbuff) == -1)
561 {
562 if (errno == ENOENT && (oflag & O_CREAT))
563 {
564 close (fd);
565 fd = -1;
566 goto again;
567 }
568 __leave;
569 }
570 if ((statbuff.st_mode & S_IXUSR) == 0)
571 break;
572 sleep (1);
573 }
574 if (i == MAX_TRIES)
575 {
576 set_errno (ETIMEDOUT);
577 __leave;
578 }
579
580 filesize = statbuff.st_size;
581 mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
582 MAP_SHARED, fd, 0);
583 if (mptr == (int8_t *) MAP_FAILED)
584 __leave;
585 close (fd);
586 fd = -1;
587
588 /* Allocate one mq_info{} for each open */
589 if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
590 __leave;
591 mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
592 if (mqhdr->mqh_magic != MQI_MAGIC)
593 {
594 system_printf (
595 "Old message queue \"%s\" detected!\n"
596 "This file is not usable as message queue anymore due to changes in the "
597 "internal file layout. Please remove the file and try again.", mqname);
598 set_errno (EACCES);
599 __leave;
600 }
601 mqinfo->mqi_magic = MQI_MAGIC;
602 mqinfo->mqi_flags = nonblock;
603
604 /* Initialize mutex & condition variable */
605 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
606 if (i != 0)
607 {
608 set_errno (i);
609 __leave;
610 }
611 i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
612 if (i != 0)
613 {
614 set_errno (i);
615 __leave;
616 }
617 i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
618 if (i != 0)
619 {
620 set_errno (i);
621 __leave;
622 }
623 return (mqd_t) mqinfo;
624 }
625 __except (EFAULT) {}
626 __endtry
627 /* Don't let following function calls change errno */
628 save_errno save;
629 if (created)
630 unlink (mqname);
631 if (mptr != (int8_t *) MAP_FAILED)
632 munmap((void *) mptr, (size_t) filesize);
633 if (mqinfo)
634 {
635 if (mqinfo->mqi_lock)
636 ipc_mutex_close (mqinfo->mqi_lock);
637 if (mqinfo->mqi_waitsend)
638 ipc_cond_close (mqinfo->mqi_waitsend);
639 if (mqinfo->mqi_waitrecv)
640 ipc_cond_close (mqinfo->mqi_waitrecv);
641 free (mqinfo);
642 }
643 if (fd >= 0)
644 close (fd);
645 return (mqd_t) -1;
646 }
647
648 extern "C" int
649 mq_getattr (mqd_t mqd, struct mq_attr *mqstat)
650 {
651 int n;
652 struct mq_hdr *mqhdr;
653 struct mq_fattr *attr;
654 struct mq_info *mqinfo;
655
656 __try
657 {
658 mqinfo = (struct mq_info *) mqd;
659 if (mqinfo->mqi_magic != MQI_MAGIC)
660 {
661 set_errno (EBADF);
662 __leave;
663 }
664 mqhdr = mqinfo->mqi_hdr;
665 attr = &mqhdr->mqh_attr;
666 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, false)) != 0)
667 {
668 errno = n;
669 __leave;
670 }
671 mqstat->mq_flags = mqinfo->mqi_flags; /* per-open */
672 mqstat->mq_maxmsg = attr->mq_maxmsg; /* remaining three per-queue */
673 mqstat->mq_msgsize = attr->mq_msgsize;
674 mqstat->mq_curmsgs = attr->mq_curmsgs;
675
676 ipc_mutex_unlock (mqinfo->mqi_lock);
677 return 0;
678 }
679 __except (EBADF) {}
680 __endtry
681 return -1;
682 }
683
684 extern "C" int
685 mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat)
686 {
687 int n;
688 struct mq_hdr *mqhdr;
689 struct mq_fattr *attr;
690 struct mq_info *mqinfo;
691
692 __try
693 {
694 mqinfo = (struct mq_info *) mqd;
695 if (mqinfo->mqi_magic != MQI_MAGIC)
696 {
697 set_errno (EBADF);
698 __leave;
699 }
700 mqhdr = mqinfo->mqi_hdr;
701 attr = &mqhdr->mqh_attr;
702 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, false)) != 0)
703 {
704 errno = n;
705 __leave;
706 }
707
708 if (omqstat != NULL)
709 {
710 omqstat->mq_flags = mqinfo->mqi_flags; /* previous attributes */
711 omqstat->mq_maxmsg = attr->mq_maxmsg;
712 omqstat->mq_msgsize = attr->mq_msgsize;
713 omqstat->mq_curmsgs = attr->mq_curmsgs; /* and current status */
714 }
715
716 if (mqstat->mq_flags & O_NONBLOCK)
717 mqinfo->mqi_flags |= O_NONBLOCK;
718 else
719 mqinfo->mqi_flags &= ~O_NONBLOCK;
720
721 ipc_mutex_unlock (mqinfo->mqi_lock);
722 return 0;
723 }
724 __except (EBADF) {}
725 __endtry
726 return -1;
727 }
728
729 extern "C" int
730 mq_notify (mqd_t mqd, const struct sigevent *notification)
731 {
732 int n;
733 pid_t pid;
734 struct mq_hdr *mqhdr;
735 struct mq_info *mqinfo;
736
737 __try
738 {
739 mqinfo = (struct mq_info *) mqd;
740 if (mqinfo->mqi_magic != MQI_MAGIC)
741 {
742 set_errno (EBADF);
743 __leave;
744 }
745 mqhdr = mqinfo->mqi_hdr;
746 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, false)) != 0)
747 {
748 errno = n;
749 __leave;
750 }
751
752 pid = getpid ();
753 if (!notification)
754 {
755 if (mqhdr->mqh_pid == pid)
756 mqhdr->mqh_pid = 0; /* unregister calling process */
757 }
758 else
759 {
760 if (mqhdr->mqh_pid != 0)
761 {
762 if (kill (mqhdr->mqh_pid, 0) != -1 || errno != ESRCH)
763 {
764 set_errno (EBUSY);
765 ipc_mutex_unlock (mqinfo->mqi_lock);
766 __leave;
767 }
768 }
769 mqhdr->mqh_pid = pid;
770 mqhdr->mqh_event = *notification;
771 }
772 ipc_mutex_unlock (mqinfo->mqi_lock);
773 return 0;
774 }
775 __except (EBADF) {}
776 __endtry
777 return -1;
778 }
779
780 static int
781 _mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
782 const struct timespec *abstime)
783 {
784 int n;
785 long index, freeindex;
786 int8_t *mptr;
787 struct sigevent *sigev;
788 struct mq_hdr *mqhdr;
789 struct mq_fattr *attr;
790 struct msg_hdr *msghdr, *nmsghdr, *pmsghdr;
791 struct mq_info *mqinfo = NULL;
792 bool ipc_mutex_locked = false;
793 int ret = -1;
794
795 pthread_testcancel ();
796
797 __try
798 {
799 mqinfo = (struct mq_info *) mqd;
800 if (mqinfo->mqi_magic != MQI_MAGIC)
801 {
802 set_errno (EBADF);
803 __leave;
804 }
805 if (prio > MQ_PRIO_MAX)
806 {
807 set_errno (EINVAL);
808 __leave;
809 }
810
811 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
812 mptr = (int8_t *) mqhdr; /* byte pointer */
813 attr = &mqhdr->mqh_attr;
814 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, true)) != 0)
815 {
816 errno = n;
817 __leave;
818 }
819 ipc_mutex_locked = true;
820 if (len > (size_t) attr->mq_msgsize)
821 {
822 set_errno (EMSGSIZE);
823 __leave;
824 }
825 if (attr->mq_curmsgs == 0)
826 {
827 if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0)
828 {
829 sigev = &mqhdr->mqh_event;
830 if (sigev->sigev_notify == SIGEV_SIGNAL)
831 sigqueue (mqhdr->mqh_pid, sigev->sigev_signo,
832 sigev->sigev_value);
833 mqhdr->mqh_pid = 0; /* unregister */
834 }
835 }
836 else if (attr->mq_curmsgs >= attr->mq_maxmsg)
837 {
838 /* Queue is full */
839 if (mqinfo->mqi_flags & O_NONBLOCK)
840 {
841 set_errno (EAGAIN);
842 __leave;
843 }
844 /* Wait for room for one message on the queue */
845 while (attr->mq_curmsgs >= attr->mq_maxmsg)
846 {
847 int ret = ipc_cond_timedwait (mqinfo->mqi_waitsend,
848 mqinfo->mqi_lock, abstime);
849 if (ret != 0)
850 {
851 set_errno (ret);
852 __leave;
853 }
854 }
855 }
856
857 /* nmsghdr will point to new message */
858 if ((freeindex = mqhdr->mqh_free) == 0)
859 api_fatal ("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs);
860
861 nmsghdr = (struct msg_hdr *) &mptr[freeindex];
862 nmsghdr->msg_prio = prio;
863 nmsghdr->msg_len = len;
864 memcpy (nmsghdr + 1, ptr, len); /* copy message from caller */
865 mqhdr->mqh_free = nmsghdr->msg_next; /* new freelist head */
866
867 /* Find right place for message in linked list */
868 index = mqhdr->mqh_head;
869 pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head);
870 while (index)
871 {
872 msghdr = (struct msg_hdr *) &mptr[index];
873 if (prio > msghdr->msg_prio)
874 {
875 nmsghdr->msg_next = index;
876 pmsghdr->msg_next = freeindex;
877 break;
878 }
879 index = msghdr->msg_next;
880 pmsghdr = msghdr;
881 }
882 if (index == 0)
883 {
884 /* Queue was empty or new goes at end of list */
885 pmsghdr->msg_next = freeindex;
886 nmsghdr->msg_next = 0;
887 }
888 /* Wake up anyone blocked in mq_receive waiting for a message */
889 if (attr->mq_curmsgs == 0)
890 ipc_cond_signal (mqinfo->mqi_waitrecv);
891 attr->mq_curmsgs++;
892
893 ipc_mutex_unlock (mqinfo->mqi_lock);
894 ret = 0;
895 }
896 __except (EBADF) {}
897 __endtry
898 if (ipc_mutex_locked)
899 ipc_mutex_unlock (mqinfo->mqi_lock);
900 return ret;
901 }
902
903 extern "C" int
904 mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio)
905 {
906 return _mq_send (mqd, ptr, len, prio, NULL);
907 }
908
909 extern "C" int
910 mq_timedsend (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
911 const struct timespec *abstime)
912 {
913 return _mq_send (mqd, ptr, len, prio, abstime);
914 }
915
916 static ssize_t
917 _mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
918 const struct timespec *abstime)
919 {
920 int n;
921 long index;
922 int8_t *mptr;
923 ssize_t len = -1;
924 struct mq_hdr *mqhdr;
925 struct mq_fattr *attr;
926 struct msg_hdr *msghdr;
927 struct mq_info *mqinfo = (struct mq_info *) mqd;
928 bool ipc_mutex_locked = false;
929
930 pthread_testcancel ();
931
932 __try
933 {
934 if (mqinfo->mqi_magic != MQI_MAGIC)
935 {
936 set_errno (EBADF);
937 __leave;
938 }
939 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
940 mptr = (int8_t *) mqhdr; /* byte pointer */
941 attr = &mqhdr->mqh_attr;
942 if ((n = ipc_mutex_lock (mqinfo->mqi_lock, true)) != 0)
943 {
944 errno = n;
945 __leave;
946 }
947 ipc_mutex_locked = true;
948 if (maxlen < (size_t) attr->mq_msgsize)
949 {
950 set_errno (EMSGSIZE);
951 __leave;
952 }
953 if (attr->mq_curmsgs == 0) /* queue is empty */
954 {
955 if (mqinfo->mqi_flags & O_NONBLOCK)
956 {
957 set_errno (EAGAIN);
958 __leave;
959 }
960 /* Wait for a message to be placed onto queue */
961 mqhdr->mqh_nwait++;
962 while (attr->mq_curmsgs == 0)
963 {
964 int ret = ipc_cond_timedwait (mqinfo->mqi_waitrecv,
965 mqinfo->mqi_lock, abstime);
966 if (ret != 0)
967 {
968 set_errno (ret);
969 __leave;
970 }
971 }
972 mqhdr->mqh_nwait--;
973 }
974
975 if ((index = mqhdr->mqh_head) == 0)
976 api_fatal ("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs);
977
978 msghdr = (struct msg_hdr *) &mptr[index];
979 mqhdr->mqh_head = msghdr->msg_next; /* new head of list */
980 len = msghdr->msg_len;
981 memcpy(ptr, msghdr + 1, len); /* copy the message itself */
982 if (priop != NULL)
983 *priop = msghdr->msg_prio;
984
985 /* Just-read message goes to front of free list */
986 msghdr->msg_next = mqhdr->mqh_free;
987 mqhdr->mqh_free = index;
988
989 /* Wake up anyone blocked in mq_send waiting for room */
990 if (attr->mq_curmsgs == attr->mq_maxmsg)
991 ipc_cond_signal (mqinfo->mqi_waitsend);
992 attr->mq_curmsgs--;
993
994 ipc_mutex_unlock (mqinfo->mqi_lock);
995 }
996 __except (EBADF) {}
997 __endtry
998 if (ipc_mutex_locked)
999 ipc_mutex_unlock (mqinfo->mqi_lock);
1000 return len;
1001 }
1002
1003 extern "C" ssize_t
1004 mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop)
1005 {
1006 return _mq_receive (mqd, ptr, maxlen, priop, NULL);
1007 }
1008
1009 extern "C" ssize_t
1010 mq_timedreceive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
1011 const struct timespec *abstime)
1012 {
1013 return _mq_receive (mqd, ptr, maxlen, priop, abstime);
1014 }
1015
1016 extern "C" int
1017 mq_close (mqd_t mqd)
1018 {
1019 long msgsize, filesize;
1020 struct mq_hdr *mqhdr;
1021 struct mq_fattr *attr;
1022 struct mq_info *mqinfo;
1023
1024 __try
1025 {
1026 mqinfo = (struct mq_info *) mqd;
1027 if (mqinfo->mqi_magic != MQI_MAGIC)
1028 {
1029 set_errno (EBADF);
1030 __leave;
1031 }
1032 mqhdr = mqinfo->mqi_hdr;
1033 attr = &mqhdr->mqh_attr;
1034
1035 if (mq_notify (mqd, NULL)) /* unregister calling process */
1036 __leave;
1037
1038 msgsize = MSGSIZE (attr->mq_msgsize);
1039 filesize = sizeof (struct mq_hdr)
1040 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
1041 if (munmap (mqinfo->mqi_hdr, filesize) == -1)
1042 __leave;
1043
1044 mqinfo->mqi_magic = 0; /* just in case */
1045 ipc_cond_close (mqinfo->mqi_waitsend);
1046 ipc_cond_close (mqinfo->mqi_waitrecv);
1047 ipc_mutex_close (mqinfo->mqi_lock);
1048 free (mqinfo);
1049 return 0;
1050 }
1051 __except (EBADF) {}
1052 __endtry
1053 return -1;
1054 }
1055
1056 extern "C" int
1057 mq_unlink (const char *name)
1058 {
1059 size_t len = strlen (name);
1060 char mqname[ipc_names[mqueue].prefix_len + len];
1061
1062 if (!check_path (mqname, mqueue, name, len))
1063 return -1;
1064 if (unlink (mqname) == -1)
1065 return -1;
1066 return 0;
1067 }
1068
1069 /* POSIX named semaphore implementation. Loosely based on Richard W. STEPHENS
1070 implementation as far as sem_open is concerned, but under the hood using
1071 the already existing semaphore class in thread.cc. Using a file backed
1072 solution allows to implement kernel persistent named semaphores. */
1073
1074 struct sem_finfo
1075 {
1076 unsigned int value;
1077 unsigned long long hash;
1078 LUID luid;
1079 };
1080
1081 extern "C" sem_t *
1082 sem_open (const char *name, int oflag, ...)
1083 {
1084 int i, fd = -1, created = 0;
1085 va_list ap;
1086 mode_t mode = 0;
1087 unsigned int value = 0;
1088 struct stat statbuff;
1089 sem_t *sem = SEM_FAILED;
1090 sem_finfo sf;
1091 bool wasopen = false;
1092 ipc_flock file;
1093
1094 size_t len = strlen (name);
1095 char semname[ipc_names[semaphore].prefix_len + len];
1096
1097 if (!check_path (semname, semaphore, name, len))
1098 return SEM_FAILED;
1099
1100 __try
1101 {
1102 oflag &= (O_CREAT | O_EXCL);
1103
1104 again:
1105 if (oflag & O_CREAT)
1106 {
1107 va_start (ap, oflag); /* init ap to final named argument */
1108 mode = va_arg (ap, mode_t) & ~S_IXUSR;
1109 value = va_arg (ap, unsigned int);
1110 va_end (ap);
1111
1112 /* Open and specify O_EXCL and user-execute */
1113 fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC,
1114 mode | S_IXUSR);
1115 if (fd < 0)
1116 {
1117 if (errno == EEXIST && (oflag & O_EXCL) == 0)
1118 goto exists; /* already exists, OK */
1119 return SEM_FAILED;
1120 }
1121 created = 1;
1122 /* First one to create the file initializes it. */
1123 NtAllocateLocallyUniqueId (&sf.luid);
1124 sf.value = value;
1125 sf.hash = hash_path_name (0, semname);
1126 if (write (fd, &sf, sizeof sf) != sizeof sf)
1127 __leave;
1128 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value,
1129 wasopen);
1130 if (sem == SEM_FAILED)
1131 __leave;
1132 /* Initialization complete, turn off user-execute bit */
1133 if (fchmod (fd, mode) == -1)
1134 __leave;
1135 /* Don't close (fd); */
1136 return sem;
1137 }
1138
1139 exists:
1140 /* Open the file and fetch the semaphore name. */
1141 if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0)
1142 {
1143 if (errno == ENOENT && (oflag & O_CREAT))
1144 goto again;
1145 __leave;
1146 }
1147 /* Make certain initialization is complete */
1148 for (i = 0; i < MAX_TRIES; i++)
1149 {
1150 if (stat64 (semname, &statbuff) == -1)
1151 {
1152 if (errno == ENOENT && (oflag & O_CREAT))
1153 {
1154 close (fd);
1155 fd = -1;
1156 goto again;
1157 }
1158 __leave;
1159 }
1160 if ((statbuff.st_mode & S_IXUSR) == 0)
1161 break;
1162 sleep (1);
1163 }
1164 if (i == MAX_TRIES)
1165 {
1166 set_errno (ETIMEDOUT);
1167 __leave;
1168 }
1169 if (file.lock (fd, sizeof sf))
1170 __leave;
1171 if (read (fd, &sf, sizeof sf) != sizeof sf)
1172 __leave;
1173 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value,
1174 wasopen);
1175 file.unlock (fd);
1176 if (sem == SEM_FAILED)
1177 __leave;
1178 /* If wasopen is set, the semaphore was already opened and we already have
1179 an open file descriptor pointing to the file. This means, we have to
1180 close the file descriptor created in this call. It won't be stored
1181 anywhere anyway. */
1182 if (wasopen)
1183 close (fd);
1184 return sem;
1185 }
1186 __except (EFAULT) {}
1187 __endtry
1188 /* Don't let following function calls change errno */
1189 save_errno save;
1190
1191 if (fd >= 0)
1192 file.unlock (fd);
1193 if (created)
1194 unlink (semname);
1195 if (sem != SEM_FAILED)
1196 semaphore::close (sem);
1197 if (fd >= 0)
1198 close (fd);
1199 return SEM_FAILED;
1200 }
1201
1202 int
1203 _sem_close (sem_t *sem, bool do_close)
1204 {
1205 sem_finfo sf;
1206 int fd, ret = -1;
1207 ipc_flock file;
1208
1209 if (semaphore::getinternal (sem, &fd, &sf.hash, &sf.luid, &sf.value) == -1)
1210 return -1;
1211 if (!file.lock (fd, sizeof sf)
1212 && lseek64 (fd, 0LL, SEEK_SET) != (off_t) -1
1213 && write (fd, &sf, sizeof sf) == sizeof sf)
1214 ret = do_close ? semaphore::close (sem) : 0;
1215
1216 /* Don't let following function calls change errno */
1217 save_errno save;
1218 file.unlock (fd);
1219 close (fd);
1220
1221 return ret;
1222 }
1223
1224 extern "C" int
1225 sem_close (sem_t *sem)
1226 {
1227 return _sem_close (sem, true);
1228 }
1229
1230 extern "C" int
1231 sem_unlink (const char *name)
1232 {
1233 size_t len = strlen (name);
1234 char semname[ipc_names[semaphore].prefix_len + len];
1235
1236 if (!check_path (semname, semaphore, name, len))
1237 return -1;
1238 if (unlink (semname) == -1)
1239 return -1;
1240 return 0;
1241 }
This page took 0.093736 seconds and 6 git commands to generate.