]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/posix_ipc.cc
* libc/include/string.h: Include sys/cdefs.h.
[newlib-cygwin.git] / winsup / cygwin / posix_ipc.cc
CommitLineData
7b487ba9
CV
1/* posix_ipc.cc: POSIX IPC API for Cygwin.
2
e70fdfb9 3 Copyright 2007, 2008, 2009, 2010 Red Hat, Inc.
7b487ba9
CV
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
7b487ba9 11#include "winsup.h"
3748b3e8 12#include "shared_info.h"
8fbd574e 13#include "thread.h"
7b487ba9 14#include "path.h"
7b487ba9 15#include "cygtls.h"
e6fbf13e
CV
16#include "fhandler.h"
17#include "dtable.h"
18#include "cygheap.h"
7b487ba9 19#include "sigproc.h"
abbde487 20#include "ntdll.h"
7b487ba9
CV
21#include <sys/mman.h>
22#include <sys/param.h>
7b487ba9 23#include <stdlib.h>
7b487ba9 24#include <unistd.h>
7b487ba9 25#include <mqueue.h>
8fbd574e 26#include <semaphore.h>
7b487ba9 27
547ad329
CV
28/* The prefix_len is the length of the path prefix ncluding trailing "/"
29 (or "/sem." for semaphores) as well as the trailing NUL. */
cb7e1879 30static struct
7b487ba9
CV
31{
32 const char *prefix;
547ad329 33 const size_t prefix_len;
7b487ba9
CV
34 const char *description;
35} ipc_names[] = {
547ad329
CV
36 { "/dev/shm", 10, "POSIX shared memory object" },
37 { "/dev/mqueue", 13, "POSIX message queue" },
38 { "/dev/shm", 14, "POSIX semaphore" }
7b487ba9
CV
39};
40
41enum ipc_type_t
42{
43 shmem,
44 mqueue,
8fbd574e 45 semaphore
7b487ba9
CV
46};
47
48static bool
547ad329 49check_path (char *res_name, ipc_type_t type, const char *name, size_t len)
7b487ba9 50{
b32c31d1 51 /* Note that we require the existance of the appropriate /dev subdirectories
7b487ba9
CV
52 for POSIX IPC object support, similar to Linux (which supports the
53 directories, but doesn't require to mount them). We don't create
54 these directory here, that's the task of the installer. But we check
55 for existance and give ample warning. */
56 path_conv path (ipc_names[type].prefix, PC_SYM_NOFOLLOW);
57 if (path.error || !path.exists () || !path.isdir ())
58 {
59 small_printf (
60 "Warning: '%s' does not exists or is not a directory.\n\n"
61 "%ss require the existance of this directory.\n"
62 "Create the directory '%s' and set the permissions to 01777.\n"
63 "For instance on the command line: mkdir -m 01777 %s\n",
64 ipc_names[type].prefix, ipc_names[type].description,
65 ipc_names[type].prefix, ipc_names[type].prefix);
66 set_errno (EINVAL);
67 return false;
68 }
b32c31d1
CV
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]))))
7b487ba9
CV
76 {
77 debug_printf ("Invalid %s name '%s'", ipc_names[type].description, name);
78 set_errno (EINVAL);
79 return false;
80 }
b32c31d1
CV
81 /* Skip leading (back-)slash. */
82 if (strchr ("/\\", name[0]))
83 ++name;
547ad329 84 if (len > PATH_MAX - ipc_names[type].prefix_len)
7b487ba9
CV
85 {
86 debug_printf ("%s name '%s' too long", ipc_names[type].description, name);
87 set_errno (ENAMETOOLONG);
88 return false;
89 }
8fbd574e
CV
90 __small_sprintf (res_name, "%s/%s%s", ipc_names[type].prefix,
91 type == semaphore ? "sem." : "",
b32c31d1 92 name);
7b487ba9
CV
93 return true;
94}
95
96static int
97ipc_mutex_init (HANDLE *pmtx, const char *name)
98{
3748b3e8
CV
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: %p", status);
114 return geterrno_from_win_error (RtlNtStatusToDosError (status));
115 }
116 return 0;
7b487ba9
CV
117}
118
119static int
120ipc_mutex_lock (HANDLE mtx)
121{
122 HANDLE h[2] = { mtx, signal_arrived };
123
124 switch (WaitForMultipleObjects (2, h, FALSE, INFINITE))
7b44665a 125 {
7b487ba9
CV
126 case WAIT_OBJECT_0:
127 case WAIT_ABANDONED_0:
128 return 0;
129 case WAIT_OBJECT_0 + 1:
130 set_errno (EINTR);
131 return 1;
132 default:
133 break;
7b44665a 134 }
7b487ba9
CV
135 return geterrno_from_win_error ();
136}
137
138static inline int
139ipc_mutex_unlock (HANDLE mtx)
140{
141 return ReleaseMutex (mtx) ? 0 : geterrno_from_win_error ();
142}
143
144static inline int
145ipc_mutex_close (HANDLE mtx)
146{
147 return CloseHandle (mtx) ? 0 : geterrno_from_win_error ();
148}
149
150static int
3748b3e8 151ipc_cond_init (HANDLE *pevt, const char *name, char sr)
7b487ba9 152{
3748b3e8
CV
153 WCHAR buf[MAX_PATH];
154 UNICODE_STRING uname;
155 OBJECT_ATTRIBUTES attr;
156 NTSTATUS status;
157
158 __small_swprintf (buf, L"mqueue/evt_%s%c", name, sr);
159 RtlInitUnicodeString (&uname, buf);
160 InitializeObjectAttributes (&attr, &uname,
161 OBJ_INHERIT | OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
162 get_shared_parent_dir (),
163 everyone_sd (CYG_EVENT_ACCESS));
164 status = NtCreateEvent (pevt, CYG_EVENT_ACCESS, &attr,
165 NotificationEvent, FALSE);
166 if (!NT_SUCCESS (status))
167 {
168 debug_printf ("NtCreateEvent: %p", status);
169 return geterrno_from_win_error (RtlNtStatusToDosError (status));
170 }
171 return 0;
7b487ba9
CV
172}
173
174static int
175ipc_cond_timedwait (HANDLE evt, HANDLE mtx, const struct timespec *abstime)
176{
177 struct timeval tv;
178 DWORD timeout;
179 HANDLE h[2] = { mtx, evt };
180
181 if (!abstime)
182 timeout = INFINITE;
183 else if (abstime->tv_sec < 0
184 || abstime->tv_nsec < 0
185 || abstime->tv_nsec > 999999999)
186 return EINVAL;
187 else
188 {
189 gettimeofday (&tv, NULL);
190 /* Check for immediate timeout. */
191 if (tv.tv_sec > abstime->tv_sec
192 || (tv.tv_sec == abstime->tv_sec
193 && tv.tv_usec > abstime->tv_nsec / 1000))
194 return ETIMEDOUT;
195 timeout = (abstime->tv_sec - tv.tv_sec) * 1000;
196 timeout += (abstime->tv_nsec / 1000 - tv.tv_usec) / 1000;
197 }
3748b3e8 198 ResetEvent (evt);
7b487ba9
CV
199 if (ipc_mutex_unlock (mtx))
200 return -1;
201 switch (WaitForMultipleObjects (2, h, TRUE, timeout))
7b44665a 202 {
7b487ba9
CV
203 case WAIT_OBJECT_0:
204 case WAIT_ABANDONED_0:
7b487ba9
CV
205 return 0;
206 case WAIT_TIMEOUT:
207 ipc_mutex_lock (mtx);
208 return ETIMEDOUT;
209 default:
210 break;
7b44665a 211 }
7b487ba9
CV
212 return geterrno_from_win_error ();
213}
214
215static inline int
216ipc_cond_signal (HANDLE evt)
217{
218 return SetEvent (evt) ? 0 : geterrno_from_win_error ();
219}
220
221static inline int
222ipc_cond_close (HANDLE evt)
223{
224 return CloseHandle (evt) ? 0 : geterrno_from_win_error ();
225}
226
8fbd574e
CV
227class ipc_flock
228{
229 struct __flock64 fl;
230
231public:
232 ipc_flock () { memset (&fl, 0, sizeof fl); }
233
234 int lock (int fd, size_t size)
235 {
236 fl.l_type = F_WRLCK;
237 fl.l_whence = SEEK_SET;
238 fl.l_start = 0;
239 fl.l_len = size;
fabfb1a1 240 return fcntl64 (fd, F_SETLKW, &fl);
8fbd574e
CV
241 }
242 int unlock (int fd)
243 {
244 if (!fl.l_len)
245 return 0;
246 fl.l_type = F_UNLCK;
fabfb1a1 247 return fcntl64 (fd, F_SETLKW, &fl);
8fbd574e
CV
248 }
249};
250
7b487ba9
CV
251/* POSIX shared memory object implementation. */
252
253extern "C" int
254shm_open (const char *name, int oflag, mode_t mode)
255{
547ad329
CV
256 size_t len = strlen (name);
257 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 258
547ad329 259 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
260 return -1;
261
262 /* Check for valid flags. */
263 if (((oflag & O_ACCMODE) != O_RDONLY && (oflag & O_ACCMODE) != O_RDWR)
264 || (oflag & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC)))
265 {
266 debug_printf ("Invalid oflag 0%o", oflag);
267 set_errno (EINVAL);
268 return -1;
269 }
270
e70fdfb9 271 return open (shmname, oflag | O_CLOEXEC, mode & 0777);
7b487ba9
CV
272}
273
274extern "C" int
275shm_unlink (const char *name)
276{
547ad329
CV
277 size_t len = strlen (name);
278 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 279
547ad329 280 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
281 return -1;
282
283 return unlink (shmname);
284}
285
286/* The POSIX message queue implementation is based on W. Richard STEVENS
287 implementation, just tweaked for Cygwin. The main change is
288 the usage of Windows mutexes and events instead of using the pthread
289 synchronization objects. The pathname is massaged so that the
290 files are created under /dev/mqueue. mq_timedsend and mq_timedreceive
291 are implemented additionally. */
292
293struct mq_hdr
294{
93162be5
CV
295 struct mq_attr mqh_attr; /* the queue's attributes */
296 long mqh_head; /* index of first message */
297 long mqh_free; /* index of first free message */
298 long mqh_nwait; /* #threads blocked in mq_receive() */
299 pid_t mqh_pid; /* nonzero PID if mqh_event set */
8fbd574e 300 char mqh_uname[36]; /* unique name used to identify synchronization
7b44665a 301 objects connected to this queue */
93162be5 302 struct sigevent mqh_event; /* for mq_notify() */
7b487ba9
CV
303};
304
305struct msg_hdr
306{
93162be5
CV
307 long msg_next; /* index of next on linked list */
308 ssize_t msg_len; /* actual length */
309 unsigned int msg_prio; /* priority */
7b487ba9
CV
310};
311
312struct mq_info
313{
93162be5
CV
314 struct mq_hdr *mqi_hdr; /* start of mmap'ed region */
315 unsigned long mqi_magic; /* magic number if open */
316 int mqi_flags; /* flags for this process */
317 HANDLE mqi_lock; /* mutex lock */
3748b3e8
CV
318 HANDLE mqi_waitsend; /* and condition variable for full queue */
319 HANDLE mqi_waitrecv; /* and condition variable for empty queue */
7b487ba9
CV
320};
321
322#define MQI_MAGIC 0x98765432UL
323
324#define MSGSIZE(i) roundup((i), sizeof(long))
325
326#define MAX_TRIES 10 /* for waiting for initialization */
327
328struct mq_attr defattr = { 0, 10, 8192, 0 }; /* Linux defaults. */
329
330extern "C" _off64_t lseek64 (int, _off64_t, int);
331extern "C" void *mmap64 (void *, size_t, int, int, int, _off64_t);
332
333extern "C" mqd_t
334mq_open (const char *name, int oflag, ...)
335{
599d462d 336 int i, fd = -1, nonblock, created;
7b487ba9 337 long msgsize, index;
eb8a6636 338 _off64_t filesize = 0;
7b487ba9
CV
339 va_list ap;
340 mode_t mode;
341 int8_t *mptr;
342 struct __stat64 statbuff;
343 struct mq_hdr *mqhdr;
344 struct msg_hdr *msghdr;
345 struct mq_attr *attr;
346 struct mq_info *mqinfo;
8fbd574e 347 LUID luid;
7b487ba9 348
547ad329
CV
349 size_t len = strlen (name);
350 char mqname[ipc_names[mqueue].prefix_len + len];
351
352 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
353 return (mqd_t) -1;
354
355 myfault efault;
356 if (efault.faulted (EFAULT))
8fbd574e 357 return (mqd_t) -1;
7b487ba9 358
8fbd574e 359 oflag &= (O_CREAT | O_EXCL | O_NONBLOCK);
7b487ba9
CV
360 created = 0;
361 nonblock = oflag & O_NONBLOCK;
362 oflag &= ~O_NONBLOCK;
363 mptr = (int8_t *) MAP_FAILED;
364 mqinfo = NULL;
365
366again:
367 if (oflag & O_CREAT)
368 {
369 va_start (ap, oflag); /* init ap to final named argument */
370 mode = va_arg (ap, mode_t) & ~S_IXUSR;
371 attr = va_arg (ap, struct mq_attr *);
372 va_end (ap);
373
374 /* Open and specify O_EXCL and user-execute */
e70fdfb9 375 fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
7b487ba9 376 if (fd < 0)
7b44665a 377 {
7b487ba9
CV
378 if (errno == EEXIST && (oflag & O_EXCL) == 0)
379 goto exists; /* already exists, OK */
380 return (mqd_t) -1;
381 }
382 created = 1;
383 /* First one to create the file initializes it */
384 if (attr == NULL)
385 attr = &defattr;
386 else if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0)
387 {
388 set_errno (EINVAL);
389 goto err;
390 }
391 /* Calculate and set the file size */
392 msgsize = MSGSIZE (attr->mq_msgsize);
393 filesize = sizeof (struct mq_hdr)
7b44665a 394 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
7b487ba9
CV
395 if (lseek64 (fd, filesize - 1, SEEK_SET) == -1)
396 goto err;
397 if (write (fd, "", 1) == -1)
398 goto err;
399
400 /* Memory map the file */
401 mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
402 MAP_SHARED, fd, 0);
403 if (mptr == (int8_t *) MAP_FAILED)
404 goto err;
405
406 /* Allocate one mq_info{} for the queue */
3748b3e8 407 if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
7b487ba9
CV
408 goto err;
409 mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
410 mqinfo->mqi_magic = MQI_MAGIC;
411 mqinfo->mqi_flags = nonblock;
412
413 /* Initialize header at beginning of file */
414 /* Create free list with all messages on it */
415 mqhdr->mqh_attr.mq_flags = 0;
416 mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg;
417 mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize;
418 mqhdr->mqh_attr.mq_curmsgs = 0;
419 mqhdr->mqh_nwait = 0;
420 mqhdr->mqh_pid = 0;
8fbd574e 421 if (!AllocateLocallyUniqueId (&luid))
7b44665a 422 {
8fbd574e
CV
423 __seterrno ();
424 goto err;
425 }
abbde487 426 __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x",
8fbd574e
CV
427 hash_path_name (0,mqname),
428 luid.HighPart, luid.LowPart);
7b487ba9
CV
429 mqhdr->mqh_head = 0;
430 index = sizeof (struct mq_hdr);
431 mqhdr->mqh_free = index;
432 for (i = 0; i < attr->mq_maxmsg - 1; i++)
433 {
434 msghdr = (struct msg_hdr *) &mptr[index];
435 index += sizeof (struct msg_hdr) + msgsize;
436 msghdr->msg_next = index;
437 }
438 msghdr = (struct msg_hdr *) &mptr[index];
439 msghdr->msg_next = 0; /* end of free list */
440
3748b3e8 441 /* Initialize mutex & condition variables */
93162be5 442 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
7b487ba9
CV
443 if (i != 0)
444 goto pthreaderr;
445
3748b3e8
CV
446 i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
447 if (i != 0)
448 goto pthreaderr;
449
450 i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
7b487ba9
CV
451 if (i != 0)
452 goto pthreaderr;
453
454 /* Initialization complete, turn off user-execute bit */
455 if (fchmod (fd, mode) == -1)
456 goto err;
457 close (fd);
458 return ((mqd_t) mqinfo);
459 }
460
461exists:
462 /* Open the file then memory map */
e70fdfb9 463 if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0)
7b487ba9
CV
464 {
465 if (errno == ENOENT && (oflag & O_CREAT))
466 goto again;
467 goto err;
468 }
469 /* Make certain initialization is complete */
470 for (i = 0; i < MAX_TRIES; i++)
471 {
472 if (stat64 (mqname, &statbuff) == -1)
473 {
474 if (errno == ENOENT && (oflag & O_CREAT))
475 {
8fbd574e 476 close (fd);
599d462d 477 fd = -1;
7b487ba9
CV
478 goto again;
479 }
480 goto err;
481 }
482 if ((statbuff.st_mode & S_IXUSR) == 0)
483 break;
484 sleep (1);
485 }
486 if (i == MAX_TRIES)
487 {
488 set_errno (ETIMEDOUT);
489 goto err;
490 }
491
492 filesize = statbuff.st_size;
493 mptr = (int8_t *) mmap64 (NULL, (size_t) filesize, PROT_READ | PROT_WRITE,
494 MAP_SHARED, fd, 0);
495 if (mptr == (int8_t *) MAP_FAILED)
496 goto err;
497 close (fd);
599d462d 498 fd = -1;
7b487ba9
CV
499
500 /* Allocate one mq_info{} for each open */
3748b3e8 501 if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
7b487ba9 502 goto err;
eb8a6636 503 mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
7b487ba9
CV
504 mqinfo->mqi_magic = MQI_MAGIC;
505 mqinfo->mqi_flags = nonblock;
506
507 /* Initialize mutex & condition variable */
93162be5 508 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
7b487ba9
CV
509 if (i != 0)
510 goto pthreaderr;
511
3748b3e8
CV
512 i = ipc_cond_init (&mqinfo->mqi_waitsend, mqhdr->mqh_uname, 'S');
513 if (i != 0)
514 goto pthreaderr;
515
516 i = ipc_cond_init (&mqinfo->mqi_waitrecv, mqhdr->mqh_uname, 'R');
7b487ba9
CV
517 if (i != 0)
518 goto pthreaderr;
519
520 return (mqd_t) mqinfo;
521
522pthreaderr:
523 errno = i;
524err:
525 /* Don't let following function calls change errno */
526 save_errno save;
527
528 if (created)
529 unlink (mqname);
530 if (mptr != (int8_t *) MAP_FAILED)
531 munmap((void *) mptr, (size_t) filesize);
532 if (mqinfo)
3748b3e8
CV
533 {
534 if (mqinfo->mqi_lock)
535 ipc_mutex_close (mqinfo->mqi_lock);
536 if (mqinfo->mqi_waitsend)
537 ipc_cond_close (mqinfo->mqi_waitsend);
538 if (mqinfo->mqi_waitrecv)
539 ipc_cond_close (mqinfo->mqi_waitrecv);
540 free (mqinfo);
541 }
599d462d
CV
542 if (fd >= 0)
543 close (fd);
7b487ba9
CV
544 return (mqd_t) -1;
545}
546
547extern "C" int
548mq_getattr (mqd_t mqd, struct mq_attr *mqstat)
549{
550 int n;
551 struct mq_hdr *mqhdr;
552 struct mq_attr *attr;
553 struct mq_info *mqinfo;
7b44665a 554
7b487ba9
CV
555 myfault efault;
556 if (efault.faulted (EBADF))
557 return -1;
558
559 mqinfo = (struct mq_info *) mqd;
560 if (mqinfo->mqi_magic != MQI_MAGIC)
561 {
562 set_errno (EBADF);
563 return -1;
564 }
565 mqhdr = mqinfo->mqi_hdr;
566 attr = &mqhdr->mqh_attr;
567 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
568 {
569 errno = n;
570 return -1;
7b44665a 571 }
7b487ba9
CV
572 mqstat->mq_flags = mqinfo->mqi_flags; /* per-open */
573 mqstat->mq_maxmsg = attr->mq_maxmsg; /* remaining three per-queue */
574 mqstat->mq_msgsize = attr->mq_msgsize;
575 mqstat->mq_curmsgs = attr->mq_curmsgs;
576
577 ipc_mutex_unlock (mqinfo->mqi_lock);
578 return 0;
7b44665a 579}
7b487ba9
CV
580
581extern "C" int
582mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat)
583{
584 int n;
7b44665a 585 struct mq_hdr *mqhdr;
7b487ba9
CV
586 struct mq_attr *attr;
587 struct mq_info *mqinfo;
588
589 myfault efault;
590 if (efault.faulted (EBADF))
591 return -1;
592
593 mqinfo = (struct mq_info *) mqd;
594 if (mqinfo->mqi_magic != MQI_MAGIC)
595 {
596 set_errno (EBADF);
597 return -1;
598 }
599 mqhdr = mqinfo->mqi_hdr;
600 attr = &mqhdr->mqh_attr;
601 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
602 {
603 errno = n;
604 return -1;
605 }
606
607 if (omqstat != NULL)
608 {
609 omqstat->mq_flags = mqinfo->mqi_flags; /* previous attributes */
610 omqstat->mq_maxmsg = attr->mq_maxmsg;
611 omqstat->mq_msgsize = attr->mq_msgsize;
612 omqstat->mq_curmsgs = attr->mq_curmsgs; /* and current status */
613 }
614
615 if (mqstat->mq_flags & O_NONBLOCK)
616 mqinfo->mqi_flags |= O_NONBLOCK;
617 else
618 mqinfo->mqi_flags &= ~O_NONBLOCK;
619
620 ipc_mutex_unlock (mqinfo->mqi_lock);
621 return 0;
622}
623
624extern "C" int
625mq_notify (mqd_t mqd, const struct sigevent *notification)
626{
627 int n;
628 pid_t pid;
629 struct mq_hdr *mqhdr;
630 struct mq_info *mqinfo;
7b44665a 631
7b487ba9
CV
632 myfault efault;
633 if (efault.faulted (EBADF))
634 return -1;
635
636 mqinfo = (struct mq_info *) mqd;
637 if (mqinfo->mqi_magic != MQI_MAGIC)
638 {
7b44665a 639 set_errno (EBADF);
7b487ba9
CV
640 return -1;
641 }
7b44665a 642 mqhdr = mqinfo->mqi_hdr;
7b487ba9
CV
643 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
644 {
645 errno = n;
646 return -1;
647 }
7b44665a 648
7b487ba9
CV
649 pid = getpid ();
650 if (!notification)
651 {
652 if (mqhdr->mqh_pid == pid)
653 mqhdr->mqh_pid = 0; /* unregister calling process */
654 }
655 else
656 {
657 if (mqhdr->mqh_pid != 0)
658 {
659 if (kill (mqhdr->mqh_pid, 0) != -1 || errno != ESRCH)
660 {
661 set_errno (EBUSY);
662 ipc_mutex_unlock (mqinfo->mqi_lock);
663 return -1;
664 }
665 }
666 mqhdr->mqh_pid = pid;
667 mqhdr->mqh_event = *notification;
7b44665a 668 }
7b487ba9
CV
669 ipc_mutex_unlock (mqinfo->mqi_lock);
670 return 0;
7b44665a 671}
7b487ba9
CV
672
673static int
674_mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
675 const struct timespec *abstime)
676{
677 int n;
678 long index, freeindex;
679 int8_t *mptr;
680 struct sigevent *sigev;
681 struct mq_hdr *mqhdr;
682 struct mq_attr *attr;
683 struct msg_hdr *msghdr, *nmsghdr, *pmsghdr;
684 struct mq_info *mqinfo;
685
686 myfault efault;
687 if (efault.faulted (EBADF))
688 return -1;
689
690 mqinfo = (struct mq_info *) mqd;
691 if (mqinfo->mqi_magic != MQI_MAGIC)
692 {
693 set_errno (EBADF);
694 return -1;
695 }
696 if (prio > MQ_PRIO_MAX)
697 {
698 set_errno (EINVAL);
699 return -1;
700 }
701
702 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
703 mptr = (int8_t *) mqhdr; /* byte pointer */
704 attr = &mqhdr->mqh_attr;
705 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
706 {
707 errno = n;
708 return -1;
709 }
710
711 if (len > (size_t) attr->mq_msgsize)
712 {
713 set_errno (EMSGSIZE);
714 goto err;
715 }
716 if (attr->mq_curmsgs == 0)
717 {
718 if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0)
719 {
720 sigev = &mqhdr->mqh_event;
721 if (sigev->sigev_notify == SIGEV_SIGNAL)
722 sigqueue (mqhdr->mqh_pid, sigev->sigev_signo, sigev->sigev_value);
723 mqhdr->mqh_pid = 0; /* unregister */
724 }
725 }
726 else if (attr->mq_curmsgs >= attr->mq_maxmsg)
727 {
728 /* Queue is full */
729 if (mqinfo->mqi_flags & O_NONBLOCK)
730 {
731 set_errno (EAGAIN);
732 goto err;
733 }
734 /* Wait for room for one message on the queue */
735 while (attr->mq_curmsgs >= attr->mq_maxmsg)
3748b3e8 736 ipc_cond_timedwait (mqinfo->mqi_waitsend, mqinfo->mqi_lock, abstime);
7b487ba9
CV
737 }
738
739 /* nmsghdr will point to new message */
740 if ((freeindex = mqhdr->mqh_free) == 0)
741 api_fatal ("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs);
742
743 nmsghdr = (struct msg_hdr *) &mptr[freeindex];
744 nmsghdr->msg_prio = prio;
745 nmsghdr->msg_len = len;
746 memcpy (nmsghdr + 1, ptr, len); /* copy message from caller */
747 mqhdr->mqh_free = nmsghdr->msg_next; /* new freelist head */
748
749 /* Find right place for message in linked list */
750 index = mqhdr->mqh_head;
751 pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head);
752 while (index)
753 {
754 msghdr = (struct msg_hdr *) &mptr[index];
755 if (prio > msghdr->msg_prio)
756 {
757 nmsghdr->msg_next = index;
758 pmsghdr->msg_next = freeindex;
759 break;
760 }
761 index = msghdr->msg_next;
762 pmsghdr = msghdr;
763 }
764 if (index == 0)
765 {
766 /* Queue was empty or new goes at end of list */
767 pmsghdr->msg_next = freeindex;
768 nmsghdr->msg_next = 0;
769 }
770 /* Wake up anyone blocked in mq_receive waiting for a message */
771 if (attr->mq_curmsgs == 0)
3748b3e8 772 ipc_cond_signal (mqinfo->mqi_waitrecv);
7b487ba9
CV
773 attr->mq_curmsgs++;
774
775 ipc_mutex_unlock (mqinfo->mqi_lock);
776 return 0;
777
778err:
779 ipc_mutex_unlock (mqinfo->mqi_lock);
780 return -1;
781}
782
783extern "C" int
784mq_send (mqd_t mqd, const char *ptr, size_t len, unsigned int prio)
785{
786 return _mq_send (mqd, ptr, len, prio, NULL);
787}
788
789extern "C" int
790mq_timedsend (mqd_t mqd, const char *ptr, size_t len, unsigned int prio,
791 const struct timespec *abstime)
792{
793 return _mq_send (mqd, ptr, len, prio, abstime);
794}
795
796static ssize_t
797_mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
798 const struct timespec *abstime)
799{
800 int n;
801 long index;
802 int8_t *mptr;
803 ssize_t len;
7b44665a 804 struct mq_hdr *mqhdr;
7b487ba9
CV
805 struct mq_attr *attr;
806 struct msg_hdr *msghdr;
807 struct mq_info *mqinfo;
808
809 myfault efault;
810 if (efault.faulted (EBADF))
811 return -1;
812
813 mqinfo = (struct mq_info *) mqd;
814 if (mqinfo->mqi_magic != MQI_MAGIC)
815 {
816 set_errno (EBADF);
817 return -1;
818 }
819 mqhdr = mqinfo->mqi_hdr; /* struct pointer */
820 mptr = (int8_t *) mqhdr; /* byte pointer */
821 attr = &mqhdr->mqh_attr;
822 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
823 {
824 errno = n;
825 return -1;
826 }
827
828 if (maxlen < (size_t) attr->mq_msgsize)
829 {
830 set_errno (EMSGSIZE);
831 goto err;
832 }
833 if (attr->mq_curmsgs == 0) /* queue is empty */
834 {
835 if (mqinfo->mqi_flags & O_NONBLOCK)
836 {
837 set_errno (EAGAIN);
838 goto err;
839 }
840 /* Wait for a message to be placed onto queue */
841 mqhdr->mqh_nwait++;
842 while (attr->mq_curmsgs == 0)
3748b3e8 843 ipc_cond_timedwait (mqinfo->mqi_waitrecv, mqinfo->mqi_lock, abstime);
7b487ba9
CV
844 mqhdr->mqh_nwait--;
845 }
846
847 if ((index = mqhdr->mqh_head) == 0)
848 api_fatal ("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs);
849
850 msghdr = (struct msg_hdr *) &mptr[index];
851 mqhdr->mqh_head = msghdr->msg_next; /* new head of list */
852 len = msghdr->msg_len;
853 memcpy(ptr, msghdr + 1, len); /* copy the message itself */
854 if (priop != NULL)
855 *priop = msghdr->msg_prio;
856
857 /* Just-read message goes to front of free list */
858 msghdr->msg_next = mqhdr->mqh_free;
859 mqhdr->mqh_free = index;
860
861 /* Wake up anyone blocked in mq_send waiting for room */
862 if (attr->mq_curmsgs == attr->mq_maxmsg)
3748b3e8 863 ipc_cond_signal (mqinfo->mqi_waitsend);
7b487ba9
CV
864 attr->mq_curmsgs--;
865
866 ipc_mutex_unlock (mqinfo->mqi_lock);
867 return len;
868
869err:
870 ipc_mutex_unlock (mqinfo->mqi_lock);
871 return -1;
872}
873
874extern "C" ssize_t
875mq_receive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop)
876{
877 return _mq_receive (mqd, ptr, maxlen, priop, NULL);
878}
879
880extern "C" ssize_t
881mq_timedreceive (mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop,
882 const struct timespec *abstime)
883{
884 return _mq_receive (mqd, ptr, maxlen, priop, abstime);
885}
886
887extern "C" int
888mq_close (mqd_t mqd)
889{
890 long msgsize, filesize;
891 struct mq_hdr *mqhdr;
892 struct mq_attr *attr;
893 struct mq_info *mqinfo;
894
895 myfault efault;
896 if (efault.faulted (EBADF))
897 return -1;
898
899 mqinfo = (struct mq_info *) mqd;
900 if (mqinfo->mqi_magic != MQI_MAGIC)
901 {
902 set_errno (EBADF);
903 return -1;
904 }
905 mqhdr = mqinfo->mqi_hdr;
906 attr = &mqhdr->mqh_attr;
907
908 if (mq_notify (mqd, NULL)) /* unregister calling process */
909 return -1;
910
911 msgsize = MSGSIZE (attr->mq_msgsize);
912 filesize = sizeof (struct mq_hdr)
913 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
914 if (munmap (mqinfo->mqi_hdr, filesize) == -1)
915 return -1;
916
917 mqinfo->mqi_magic = 0; /* just in case */
3748b3e8
CV
918 ipc_cond_close (mqinfo->mqi_waitsend);
919 ipc_cond_close (mqinfo->mqi_waitrecv);
7b487ba9
CV
920 ipc_mutex_close (mqinfo->mqi_lock);
921 free (mqinfo);
922 return 0;
923}
924
925extern "C" int
926mq_unlink (const char *name)
927{
547ad329
CV
928 size_t len = strlen (name);
929 char mqname[ipc_names[mqueue].prefix_len + len];
7b487ba9 930
547ad329 931 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
932 return -1;
933 if (unlink (mqname) == -1)
934 return -1;
935 return 0;
936}
937
8fbd574e
CV
938/* POSIX named semaphore implementation. Loosely based on Richard W. STEPHENS
939 implementation as far as sem_open is concerned, but under the hood using
940 the already existing semaphore class in thread.cc. Using a file backed
941 solution allows to implement kernel persistent named semaphores. */
942
943struct sem_finfo
944{
945 unsigned int value;
946 unsigned long long hash;
947 LUID luid;
948};
949
950extern "C" sem_t *
951sem_open (const char *name, int oflag, ...)
952{
599d462d 953 int i, fd = -1, created;
8fbd574e
CV
954 va_list ap;
955 mode_t mode = 0;
956 unsigned int value = 0;
957 struct __stat64 statbuff;
958 sem_t *sem = SEM_FAILED;
959 sem_finfo sf;
8fbd574e
CV
960 bool wasopen = false;
961 ipc_flock file;
962
547ad329
CV
963 size_t len = strlen (name);
964 char semname[ipc_names[semaphore].prefix_len + len];
965
966 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
967 return SEM_FAILED;
968
969 myfault efault;
970 if (efault.faulted (EFAULT))
971 return SEM_FAILED;
972
973 created = 0;
974 oflag &= (O_CREAT | O_EXCL);
975
976again:
977 if (oflag & O_CREAT)
978 {
979 va_start (ap, oflag); /* init ap to final named argument */
980 mode = va_arg (ap, mode_t) & ~S_IXUSR;
981 value = va_arg (ap, unsigned int);
982 va_end (ap);
983
984 /* Open and specify O_EXCL and user-execute */
e70fdfb9 985 fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
8fbd574e 986 if (fd < 0)
7b44665a 987 {
8fbd574e
CV
988 if (errno == EEXIST && (oflag & O_EXCL) == 0)
989 goto exists; /* already exists, OK */
990 return SEM_FAILED;
991 }
992 created = 1;
993 /* First one to create the file initializes it. */
994 if (!AllocateLocallyUniqueId (&sf.luid))
7b44665a 995 {
8fbd574e
CV
996 __seterrno ();
997 goto err;
998 }
999 sf.value = value;
1000 sf.hash = hash_path_name (0, semname);
1001 if (write (fd, &sf, sizeof sf) != sizeof sf)
7b44665a 1002 goto err;
8fbd574e
CV
1003 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value, wasopen);
1004 if (sem == SEM_FAILED)
7b44665a 1005 goto err;
8fbd574e
CV
1006 /* Initialization complete, turn off user-execute bit */
1007 if (fchmod (fd, mode) == -1)
1008 goto err;
1009 /* Don't close (fd); */
1010 return sem;
1011 }
1012
1013exists:
1014 /* Open the file and fetch the semaphore name. */
e70fdfb9 1015 if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0)
8fbd574e
CV
1016 {
1017 if (errno == ENOENT && (oflag & O_CREAT))
1018 goto again;
1019 goto err;
1020 }
1021 /* Make certain initialization is complete */
1022 for (i = 0; i < MAX_TRIES; i++)
1023 {
1024 if (stat64 (semname, &statbuff) == -1)
1025 {
1026 if (errno == ENOENT && (oflag & O_CREAT))
1027 {
1028 close (fd);
599d462d 1029 fd = -1;
8fbd574e
CV
1030 goto again;
1031 }
1032 goto err;
1033 }
1034 if ((statbuff.st_mode & S_IXUSR) == 0)
1035 break;
1036 sleep (1);
1037 }
1038 if (i == MAX_TRIES)
1039 {
1040 set_errno (ETIMEDOUT);
1041 goto err;
1042 }
1043 if (file.lock (fd, sizeof sf))
1044 goto err;
1045 if (read (fd, &sf, sizeof sf) != sizeof sf)
1046 goto err;
1047 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, sf.value, wasopen);
1048 file.unlock (fd);
1049 if (sem == SEM_FAILED)
1050 goto err;
1051 /* If wasopen is set, the semaphore was already opened and we already have
1052 an open file descriptor pointing to the file. This means, we have to
1053 close the file descriptor created in this call. It won't be stored
1054 anywhere anyway. */
1055 if (wasopen)
1056 close (fd);
1057 return sem;
1058
1059err:
1060 /* Don't let following function calls change errno */
1061 save_errno save;
1062
1063 file.unlock (fd);
1064 if (created)
1065 unlink (semname);
1066 if (sem != SEM_FAILED)
1067 semaphore::close (sem);
599d462d
CV
1068 if (fd >= 0)
1069 close (fd);
8fbd574e
CV
1070 return SEM_FAILED;
1071}
1072
1073int
1074_sem_close (sem_t *sem, bool do_close)
1075{
1076 sem_finfo sf;
1077 int fd, ret = -1;
1078 ipc_flock file;
1079
1080 if (semaphore::getinternal (sem, &fd, &sf.hash, &sf.luid, &sf.value) == -1)
1081 return -1;
1082 if (!file.lock (fd, sizeof sf)
1083 && lseek64 (fd, 0LL, SEEK_SET) != (_off64_t) -1
1084 && write (fd, &sf, sizeof sf) == sizeof sf)
1085 ret = do_close ? semaphore::close (sem) : 0;
1086
1087 /* Don't let following function calls change errno */
1088 save_errno save;
1089 file.unlock (fd);
1090 close (fd);
1091
1092 return ret;
1093}
1094
1095extern "C" int
1096sem_close (sem_t *sem)
1097{
1098 return _sem_close (sem, true);
1099}
1100
1101extern "C" int
1102sem_unlink (const char *name)
1103{
547ad329
CV
1104 size_t len = strlen (name);
1105 char semname[ipc_names[semaphore].prefix_len + len];
8fbd574e 1106
547ad329 1107 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
1108 return -1;
1109 if (unlink (semname) == -1)
1110 return -1;
1111 return 0;
1112}
This page took 0.249776 seconds and 5 git commands to generate.