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