]> sourceware.org Git - newlib-cygwin.git/blame_incremental - winsup/cygwin/posix_ipc.cc
* net.cc (gen_old_if_name): New function to generate short interface
[newlib-cygwin.git] / winsup / cygwin / posix_ipc.cc
... / ...
CommitLineData
1/* posix_ipc.cc: POSIX IPC API for Cygwin.
2
3 Copyright 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc.
4
5This file is part of Cygwin.
6
7This software is a copyrighted work licensed under the terms of the
8Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9details. */
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. */
30static 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
41enum ipc_type_t
42{
43 shmem,
44 mqueue,
45 semaphore
46};
47
48static bool
49check_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
96static int
97ipc_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
119static int
120ipc_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
136static inline int
137ipc_mutex_unlock (HANDLE mtx)
138{
139 return ReleaseMutex (mtx) ? 0 : geterrno_from_win_error ();
140}
141
142static inline int
143ipc_mutex_close (HANDLE mtx)
144{
145 return CloseHandle (mtx) ? 0 : geterrno_from_win_error ();
146}
147
148static int
149ipc_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
172static int
173ipc_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, (FILETIME *) &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. */
214restart1:
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
273static inline void
274ipc_cond_signal (HANDLE evt)
275{
276 SetEvent (evt);
277}
278
279static inline void
280ipc_cond_close (HANDLE evt)
281{
282 CloseHandle (evt);
283}
284
285class ipc_flock
286{
287 struct flock fl;
288
289public:
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
311extern "C" int
312shm_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
332extern "C" int
333shm_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)
357struct 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
365struct 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
385struct 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
393struct 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
409struct mq_attr defattr = { 0, 10, 8192, 0 }; /* Linux defaults. */
410
411extern "C" off_t lseek64 (int, off_t, int);
412extern "C" void *mmap64 (void *, size_t, int, int, int, off_t);
413
414extern "C" mqd_t
415mq_open (const char *name, int oflag, ...)
416{
417 int i, fd = -1, nonblock, created;
418 long msgsize, index;
419 off_t filesize = 0;
420 va_list ap;
421 mode_t mode;
422 int8_t *mptr;
423 struct stat statbuff;
424 struct mq_hdr *mqhdr;
425 struct msg_hdr *msghdr;
426 struct mq_attr *attr;
427 struct mq_info *mqinfo;
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 myfault efault;
437 if (efault.faulted (EFAULT))
438 return (mqd_t) -1;
439
440 oflag &= (O_CREAT | O_EXCL | O_NONBLOCK);
441 created = 0;
442 nonblock = oflag & O_NONBLOCK;
443 oflag &= ~O_NONBLOCK;
444 mptr = (int8_t *) MAP_FAILED;
445 mqinfo = NULL;
446
447again:
448 if (oflag & O_CREAT)
449 {
450 va_start (ap, oflag); /* init ap to final named argument */
451 mode = va_arg (ap, mode_t) & ~S_IXUSR;
452 attr = va_arg (ap, struct mq_attr *);
453 va_end (ap);
454
455 /* Open and specify O_EXCL and user-execute */
456 fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
457 if (fd < 0)
458 {
459 if (errno == EEXIST && (oflag & O_EXCL) == 0)
460 goto exists; /* already exists, OK */
461 return (mqd_t) -1;
462 }
463 created = 1;
464 /* First one to create the file initializes it */
465 if (attr == NULL)
466 attr = &defattr;
467 /* Check minimum and maximum values. The max values are pretty much
468 arbitrary, taken from the linux mq_overview man page. However,
469 these max values make sure that the internal mq_fattr structure
470 can use 32 bit types. */
471 else if (attr->mq_maxmsg <= 0 || attr->mq_maxmsg > 32768
472 || attr->mq_msgsize <= 0 || attr->mq_msgsize > 1048576)
473 {
474 set_errno (EINVAL);
475 goto err;
476 }
477 /* Calculate and set the file size */
478 msgsize = MSGSIZE (attr->mq_msgsize);
479 filesize = sizeof (struct mq_hdr)
480 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
481 if (lseek64 (fd, filesize - 1, SEEK_SET) == -1)
482 goto err;
483 if (write (fd, "", 1) == -1)
484 goto err;
485
486 /* Memory map the file */
487 mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
488 MAP_SHARED, fd, 0);
489 if (mptr == (int8_t *) MAP_FAILED)
490 goto err;
491
492 /* Allocate one mq_info{} for the queue */
493 if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
494 goto err;
495 mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
496 mqinfo->mqi_magic = MQI_MAGIC;
497 mqinfo->mqi_flags = nonblock;
498
499 /* Initialize header at beginning of file */
500 /* Create free list with all messages on it */
501 mqhdr->mqh_attr.mq_flags = 0;
502 mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg;
503 mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize;
504 mqhdr->mqh_attr.mq_curmsgs = 0;
505 mqhdr->mqh_nwait = 0;
506 mqhdr->mqh_pid = 0;
507 NtAllocateLocallyUniqueId (&luid);
508 __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x",
509 hash_path_name (0,mqname),
510 luid.HighPart, luid.LowPart);
511 mqhdr->mqh_head = 0;
512 mqhdr->mqh_magic = MQI_MAGIC;
513 index = sizeof (struct mq_hdr);
514 mqhdr->mqh_free = index;
515 for (i = 0; i < attr->mq_maxmsg - 1; i++)
516 {
517 msghdr = (struct msg_hdr *) &mptr[index];
518 index += sizeof (struct msg_hdr) + msgsize;
519 msghdr->msg_next = index;
520 }
521 msghdr = (struct msg_hdr *) &mptr[index];
522 msghdr->msg_next = 0; /* end of free list */
523
524 /* Initialize mutex & condition variables */
525 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
526 if (i != 0)
527 goto pthreaderr;
528
529 i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
530 if (i != 0)
531 goto pthreaderr;
532
533 i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
534 if (i != 0)
535 goto pthreaderr;
536
537 /* Initialization complete, turn off user-execute bit */
538 if (fchmod (fd, mode) == -1)
539 goto err;
540 close (fd);
541 return ((mqd_t) mqinfo);
542 }
543
544exists:
545 /* Open the file then memory map */
546 if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0)
547 {
548 if (errno == ENOENT && (oflag & O_CREAT))
549 goto again;
550 goto err;
551 }
552 /* Make certain initialization is complete */
553 for (i = 0; i < MAX_TRIES; i++)
554 {
555 if (stat64 (mqname, &statbuff) == -1)
556 {
557 if (errno == ENOENT && (oflag & O_CREAT))
558 {
559 close (fd);
560 fd = -1;
561 goto again;
562 }
563 goto err;
564 }
565 if ((statbuff.st_mode & S_IXUSR) == 0)
566 break;
567 sleep (1);
568 }
569 if (i == MAX_TRIES)
570 {
571 set_errno (ETIMEDOUT);
572 goto err;
573 }
574
575 filesize = statbuff.st_size;
576 mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
577 MAP_SHARED, fd, 0);
578 if (mptr == (int8_t *) MAP_FAILED)
579 goto err;
580 close (fd);
581 fd = -1;
582
583 /* Allocate one mq_info{} for each open */
584 if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
585 goto err;
586 mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
587 if (mqhdr->mqh_magic != MQI_MAGIC)
588 {
589 system_printf (
590"Old message queue \"%s\" detected!\n"
591"This file is not usable as message queue anymore due to changes in the "
592"internal file layout. Please remove the file and try again.", mqname);
593 set_errno (EACCES);
594 goto err;
595 }
596 mqinfo->mqi_magic = MQI_MAGIC;
597 mqinfo->mqi_flags = nonblock;
598
599 /* Initialize mutex & condition variable */
600 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
601 if (i != 0)
602 goto pthreaderr;
603
604 i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
605 if (i != 0)
606 goto pthreaderr;
607
608 i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
609 if (i != 0)
610 goto pthreaderr;
611
612 return (mqd_t) mqinfo;
613
614pthreaderr:
615 errno = i;
616err:
617 /* Don't let following function calls change errno */
618 save_errno save;
619
620 if (created)
621 unlink (mqname);
622 if (mptr != (int8_t *) MAP_FAILED)
623 munmap((void *) mptr, (size_t) filesize);
624 if (mqinfo)
625 {
626 if (mqinfo->mqi_lock)
627 ipc_mutex_close (mqinfo->mqi_lock);
628 if (mqinfo->mqi_waitsend)
629 ipc_cond_close (mqinfo->mqi_waitsend);
630 if (mqinfo->mqi_waitrecv)
631 ipc_cond_close (mqinfo->mqi_waitrecv);
632 free (mqinfo);
633 }
634 if (fd >= 0)
635 close (fd);
636 return (mqd_t) -1;
637}
638
639extern "C" int
640mq_getattr (mqd_t mqd, struct mq_attr *mqstat)
641{
642 int n;
643 struct mq_hdr *mqhdr;
644 struct mq_fattr *attr;
645 struct mq_info *mqinfo;
646
647 myfault efault;
648 if (efault.faulted (EBADF))
649 return -1;
650
651 mqinfo = (struct mq_info *) mqd;
652 if (mqinfo->mqi_magic != MQI_MAGIC)
653 {
654 set_errno (EBADF);
655 return -1;
656 }
657 mqhdr = mqinfo->mqi_hdr;
658 attr = &mqhdr->mqh_attr;
659 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
660 {
661 errno = n;
662 return -1;
663 }
664 mqstat->mq_flags = mqinfo->mqi_flags; /* per-open */
665 mqstat->mq_maxmsg = attr->mq_maxmsg; /* remaining three per-queue */
666 mqstat->mq_msgsize = attr->mq_msgsize;
667 mqstat->mq_curmsgs = attr->mq_curmsgs;
668
669 ipc_mutex_unlock (mqinfo->mqi_lock);
670 return 0;
671}
672
673extern "C" int
674mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat)
675{
676 int n;
677 struct mq_hdr *mqhdr;
678 struct mq_fattr *attr;
679 struct mq_info *mqinfo;
680
681 myfault efault;
682 if (efault.faulted (EBADF))
683 return -1;
684
685 mqinfo = (struct mq_info *) mqd;
686 if (mqinfo->mqi_magic != MQI_MAGIC)
687 {
688 set_errno (EBADF);
689 return -1;
690 }
691 mqhdr = mqinfo->mqi_hdr;
692 attr = &mqhdr->mqh_attr;
693 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
694 {
695 errno = n;
696 return -1;
697 }
698
699 if (omqstat != NULL)
700 {
701 omqstat->mq_flags = mqinfo->mqi_flags; /* previous attributes */
702 omqstat->mq_maxmsg = attr->mq_maxmsg;
703 omqstat->mq_msgsize = attr->mq_msgsize;
704 omqstat->mq_curmsgs = attr->mq_curmsgs; /* and current status */
705 }
706
707 if (mqstat->mq_flags & O_NONBLOCK)
708 mqinfo->mqi_flags |= O_NONBLOCK;
709 else
710 mqinfo->mqi_flags &= ~O_NONBLOCK;
711
712 ipc_mutex_unlock (mqinfo->mqi_lock);
713 return 0;
714}
715
716extern "C" int
717mq_notify (mqd_t mqd, const struct sigevent *notification)
718{
719 int n;
720 pid_t pid;
721 struct mq_hdr *mqhdr;
722 struct mq_info *mqinfo;
723
724 myfault efault;
725 if (efault.faulted (EBADF))
726 return -1;
727
728 mqinfo = (struct mq_info *) mqd;
729 if (mqinfo->mqi_magic != MQI_MAGIC)
730 {
731 set_errno (EBADF);
732 return -1;
733 }
734 mqhdr = mqinfo->mqi_hdr;
735 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
736 {
737 errno = n;
738 return -1;
739 }
740
741 pid = getpid ();
742 if (!notification)
743 {
744 if (mqhdr->mqh_pid == pid)
745 mqhdr->mqh_pid = 0; /* unregister calling process */
746 }
747 else
748 {
749 if (mqhdr->mqh_pid != 0)
750 {
751 if (kill (mqhdr->mqh_pid, 0) != -1 || errno != ESRCH)
752 {
753 set_errno (EBUSY);
754 ipc_mutex_unlock (mqinfo->mqi_lock);
755 return -1;
756 }
757 }
758 mqhdr->mqh_pid = pid;
759 mqhdr->mqh_event = *notification;
760 }
761 ipc_mutex_unlock (mqinfo->mqi_lock);
762 return 0;
763}
764
765static int
766_mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
767 const struct timespec *abstime)
768{
769 int n;
770 long index, freeindex;
771 int8_t *mptr;
772 struct sigevent *sigev;
773 struct mq_hdr *mqhdr;
774 struct mq_fattr *attr;
775 struct msg_hdr *msghdr, *nmsghdr, *pmsghdr;
776 struct mq_info *mqinfo;
777
778 pthread_testcancel ();
779
780 myfault efault;
781 if (efault.faulted (EBADF))
782 return -1;
783
784 mqinfo = (struct mq_info *) mqd;
785 if (mqinfo->mqi_magic != MQI_MAGIC)
786 {
787 set_errno (EBADF);
788 return -1;
789 }
790 if (prio > MQ_PRIO_MAX)
791 {
792 set_errno (EINVAL);
793 return -1;
794 }
795
796 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
797 mptr = (int8_t *) mqhdr; /* byte pointer */
798 attr = &mqhdr->mqh_attr;
799 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
800 {
801 errno = n;
802 return -1;
803 }
804
805 if (len > (size_t) attr->mq_msgsize)
806 {
807 set_errno (EMSGSIZE);
808 goto err;
809 }
810 if (attr->mq_curmsgs == 0)
811 {
812 if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0)
813 {
814 sigev = &mqhdr->mqh_event;
815 if (sigev->sigev_notify == SIGEV_SIGNAL)
816 sigqueue (mqhdr->mqh_pid, sigev->sigev_signo, sigev->sigev_value);
817 mqhdr->mqh_pid = 0; /* unregister */
818 }
819 }
820 else if (attr->mq_curmsgs >= attr->mq_maxmsg)
821 {
822 /* Queue is full */
823 if (mqinfo->mqi_flags & O_NONBLOCK)
824 {
825 set_errno (EAGAIN);
826 goto err;
827 }
828 /* Wait for room for one message on the queue */
829 while (attr->mq_curmsgs >= attr->mq_maxmsg)
830 {
831 int ret = ipc_cond_timedwait (mqinfo->mqi_waitsend, mqinfo->mqi_lock,
832 abstime);
833 if (ret != 0)
834 {
835 set_errno (ret);
836 return -1;
837 }
838 }
839 }
840
841 /* nmsghdr will point to new message */
842 if ((freeindex = mqhdr->mqh_free) == 0)
843 api_fatal ("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs);
844
845 nmsghdr = (struct msg_hdr *) &mptr[freeindex];
846 nmsghdr->msg_prio = prio;
847 nmsghdr->msg_len = len;
848 memcpy (nmsghdr + 1, ptr, len); /* copy message from caller */
849 mqhdr->mqh_free = nmsghdr->msg_next; /* new freelist head */
850
851 /* Find right place for message in linked list */
852 index = mqhdr->mqh_head;
853 pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head);
854 while (index)
855 {
856 msghdr = (struct msg_hdr *) &mptr[index];
857 if (prio > msghdr->msg_prio)
858 {
859 nmsghdr->msg_next = index;
860 pmsghdr->msg_next = freeindex;
861 break;
862 }
863 index = msghdr->msg_next;
864 pmsghdr = msghdr;
865 }
866 if (index == 0)
867 {
868 /* Queue was empty or new goes at end of list */
869 pmsghdr->msg_next = freeindex;
870 nmsghdr->msg_next = 0;
871 }
872 /* Wake up anyone blocked in mq_receive waiting for a message */
873 if (attr->mq_curmsgs == 0)
874 ipc_cond_signal (mqinfo->mqi_waitrecv);
875 attr->mq_curmsgs++;
876
877 ipc_mutex_unlock (mqinfo->mqi_lock);
878 return 0;
879
880err:
881 ipc_mutex_unlock (mqinfo->mqi_lock);
882 return -1;
883}
884
885extern "C" int
886mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio)
887{
888 return _mq_send (mqd, ptr, len, prio, NULL);
889}
890
891extern "C" int
892mq_timedsend (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
893 const struct timespec *abstime)
894{
895 return _mq_send (mqd, ptr, len, prio, abstime);
896}
897
898static ssize_t
899_mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
900 const struct timespec *abstime)
901{
902 int n;
903 long index;
904 int8_t *mptr;
905 ssize_t len;
906 struct mq_hdr *mqhdr;
907 struct mq_fattr *attr;
908 struct msg_hdr *msghdr;
909 struct mq_info *mqinfo;
910
911 pthread_testcancel ();
912
913 myfault efault;
914 if (efault.faulted (EBADF))
915 return -1;
916
917 mqinfo = (struct mq_info *) mqd;
918 if (mqinfo->mqi_magic != MQI_MAGIC)
919 {
920 set_errno (EBADF);
921 return -1;
922 }
923 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
924 mptr = (int8_t *) mqhdr; /* byte pointer */
925 attr = &mqhdr->mqh_attr;
926 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
927 {
928 errno = n;
929 return -1;
930 }
931
932 if (maxlen < (size_t) attr->mq_msgsize)
933 {
934 set_errno (EMSGSIZE);
935 goto err;
936 }
937 if (attr->mq_curmsgs == 0) /* queue is empty */
938 {
939 if (mqinfo->mqi_flags & O_NONBLOCK)
940 {
941 set_errno (EAGAIN);
942 goto err;
943 }
944 /* Wait for a message to be placed onto queue */
945 mqhdr->mqh_nwait++;
946 while (attr->mq_curmsgs == 0)
947 {
948 int ret = ipc_cond_timedwait (mqinfo->mqi_waitrecv, mqinfo->mqi_lock,
949 abstime);
950 if (ret != 0)
951 {
952 set_errno (ret);
953 return -1;
954 }
955 }
956 mqhdr->mqh_nwait--;
957 }
958
959 if ((index = mqhdr->mqh_head) == 0)
960 api_fatal ("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs);
961
962 msghdr = (struct msg_hdr *) &mptr[index];
963 mqhdr->mqh_head = msghdr->msg_next; /* new head of list */
964 len = msghdr->msg_len;
965 memcpy(ptr, msghdr + 1, len); /* copy the message itself */
966 if (priop != NULL)
967 *priop = msghdr->msg_prio;
968
969 /* Just-read message goes to front of free list */
970 msghdr->msg_next = mqhdr->mqh_free;
971 mqhdr->mqh_free = index;
972
973 /* Wake up anyone blocked in mq_send waiting for room */
974 if (attr->mq_curmsgs == attr->mq_maxmsg)
975 ipc_cond_signal (mqinfo->mqi_waitsend);
976 attr->mq_curmsgs--;
977
978 ipc_mutex_unlock (mqinfo->mqi_lock);
979 return len;
980
981err:
982 ipc_mutex_unlock (mqinfo->mqi_lock);
983 return -1;
984}
985
986extern "C" ssize_t
987mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop)
988{
989 return _mq_receive (mqd, ptr, maxlen, priop, NULL);
990}
991
992extern "C" ssize_t
993mq_timedreceive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
994 const struct timespec *abstime)
995{
996 return _mq_receive (mqd, ptr, maxlen, priop, abstime);
997}
998
999extern "C" int
1000mq_close (mqd_t mqd)
1001{
1002 long msgsize, filesize;
1003 struct mq_hdr *mqhdr;
1004 struct mq_fattr *attr;
1005 struct mq_info *mqinfo;
1006
1007 myfault efault;
1008 if (efault.faulted (EBADF))
1009 return -1;
1010
1011 mqinfo = (struct mq_info *) mqd;
1012 if (mqinfo->mqi_magic != MQI_MAGIC)
1013 {
1014 set_errno (EBADF);
1015 return -1;
1016 }
1017 mqhdr = mqinfo->mqi_hdr;
1018 attr = &mqhdr->mqh_attr;
1019
1020 if (mq_notify (mqd, NULL)) /* unregister calling process */
1021 return -1;
1022
1023 msgsize = MSGSIZE (attr->mq_msgsize);
1024 filesize = sizeof (struct mq_hdr)
1025 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
1026 if (munmap (mqinfo->mqi_hdr, filesize) == -1)
1027 return -1;
1028
1029 mqinfo->mqi_magic = 0; /* just in case */
1030 ipc_cond_close (mqinfo->mqi_waitsend);
1031 ipc_cond_close (mqinfo->mqi_waitrecv);
1032 ipc_mutex_close (mqinfo->mqi_lock);
1033 free (mqinfo);
1034 return 0;
1035}
1036
1037extern "C" int
1038mq_unlink (const char *name)
1039{
1040 size_t len = strlen (name);
1041 char mqname[ipc_names[mqueue].prefix_len + len];
1042
1043 if (!check_path (mqname, mqueue, name, len))
1044 return -1;
1045 if (unlink (mqname) == -1)
1046 return -1;
1047 return 0;
1048}
1049
1050/* POSIX named semaphore implementation. Loosely based on Richard W. STEPHENS
1051 implementation as far as sem_open is concerned, but under the hood using
1052 the already existing semaphore class in thread.cc. Using a file backed
1053 solution allows to implement kernel persistent named semaphores. */
1054
1055struct sem_finfo
1056{
1057 unsigned int value;
1058 unsigned long long hash;
1059 LUID luid;
1060};
1061
1062extern "C" sem_t *
1063sem_open (const char *name, int oflag, ...)
1064{
1065 int i, fd = -1, created;
1066 va_list ap;
1067 mode_t mode = 0;
1068 unsigned int value = 0;
1069 struct stat statbuff;
1070 sem_t *sem = SEM_FAILED;
1071 sem_finfo sf;
1072 bool wasopen = false;
1073 ipc_flock file;
1074
1075 size_t len = strlen (name);
1076 char semname[ipc_names[semaphore].prefix_len + len];
1077
1078 if (!check_path (semname, semaphore, name, len))
1079 return SEM_FAILED;
1080
1081 myfault efault;
1082 if (efault.faulted (EFAULT))
1083 return SEM_FAILED;
1084
1085 created = 0;
1086 oflag &= (O_CREAT | O_EXCL);
1087
1088again:
1089 if (oflag & O_CREAT)
1090 {
1091 va_start (ap, oflag); /* init ap to final named argument */
1092 mode = va_arg (ap, mode_t) & ~S_IXUSR;
1093 value = va_arg (ap, unsigned int);
1094 va_end (ap);
1095
1096 /* Open and specify O_EXCL and user-execute */
1097 fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
1098 if (fd < 0)
1099 {
1100 if (errno == EEXIST && (oflag & O_EXCL) == 0)
1101 goto exists; /* already exists, OK */
1102 return SEM_FAILED;
1103 }
1104 created = 1;
1105 /* First one to create the file initializes it. */
1106 NtAllocateLocallyUniqueId (&sf.luid);
1107 sf.value = value;
1108 sf.hash = hash_path_name (0, semname);
1109 if (write (fd, &sf, sizeof sf) != sizeof sf)
1110 goto err;
1111 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value, wasopen);
1112 if (sem == SEM_FAILED)
1113 goto err;
1114 /* Initialization complete, turn off user-execute bit */
1115 if (fchmod (fd, mode) == -1)
1116 goto err;
1117 /* Don't close (fd); */
1118 return sem;
1119 }
1120
1121exists:
1122 /* Open the file and fetch the semaphore name. */
1123 if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0)
1124 {
1125 if (errno == ENOENT && (oflag & O_CREAT))
1126 goto again;
1127 goto err;
1128 }
1129 /* Make certain initialization is complete */
1130 for (i = 0; i < MAX_TRIES; i++)
1131 {
1132 if (stat64 (semname, &statbuff) == -1)
1133 {
1134 if (errno == ENOENT && (oflag & O_CREAT))
1135 {
1136 close (fd);
1137 fd = -1;
1138 goto again;
1139 }
1140 goto err;
1141 }
1142 if ((statbuff.st_mode & S_IXUSR) == 0)
1143 break;
1144 sleep (1);
1145 }
1146 if (i == MAX_TRIES)
1147 {
1148 set_errno (ETIMEDOUT);
1149 goto err;
1150 }
1151 if (file.lock (fd, sizeof sf))
1152 goto err;
1153 if (read (fd, &sf, sizeof sf) != sizeof sf)
1154 goto err;
1155 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value, wasopen);
1156 file.unlock (fd);
1157 if (sem == SEM_FAILED)
1158 goto err;
1159 /* If wasopen is set, the semaphore was already opened and we already have
1160 an open file descriptor pointing to the file. This means, we have to
1161 close the file descriptor created in this call. It won't be stored
1162 anywhere anyway. */
1163 if (wasopen)
1164 close (fd);
1165 return sem;
1166
1167err:
1168 /* Don't let following function calls change errno */
1169 save_errno save;
1170
1171 file.unlock (fd);
1172 if (created)
1173 unlink (semname);
1174 if (sem != SEM_FAILED)
1175 semaphore::close (sem);
1176 if (fd >= 0)
1177 close (fd);
1178 return SEM_FAILED;
1179}
1180
1181int
1182_sem_close (sem_t *sem, bool do_close)
1183{
1184 sem_finfo sf;
1185 int fd, ret = -1;
1186 ipc_flock file;
1187
1188 if (semaphore::getinternal (sem, &fd, &sf.hash, &sf.luid, &sf.value) == -1)
1189 return -1;
1190 if (!file.lock (fd, sizeof sf)
1191 && lseek64 (fd, 0LL, SEEK_SET) != (off_t) -1
1192 && write (fd, &sf, sizeof sf) == sizeof sf)
1193 ret = do_close ? semaphore::close (sem) : 0;
1194
1195 /* Don't let following function calls change errno */
1196 save_errno save;
1197 file.unlock (fd);
1198 close (fd);
1199
1200 return ret;
1201}
1202
1203extern "C" int
1204sem_close (sem_t *sem)
1205{
1206 return _sem_close (sem, true);
1207}
1208
1209extern "C" int
1210sem_unlink (const char *name)
1211{
1212 size_t len = strlen (name);
1213 char semname[ipc_names[semaphore].prefix_len + len];
1214
1215 if (!check_path (semname, semaphore, name, len))
1216 return -1;
1217 if (unlink (semname) == -1)
1218 return -1;
1219 return 0;
1220}
This page took 0.032196 seconds and 5 git commands to generate.