]> sourceware.org Git - newlib-cygwin.git/blame - 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
7b487ba9
CV
1/* posix_ipc.cc: POSIX IPC API for Cygwin.
2
bc837d22 3 Copyright 2007, 2008, 2009, 2010, 2011, 2012 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 {
61522196 113 debug_printf ("NtCreateMutant: %y", status);
3748b3e8
CV
114 return geterrno_from_win_error (RtlNtStatusToDosError (status));
115 }
116 return 0;
7b487ba9
CV
117}
118
119static int
120ipc_mutex_lock (HANDLE mtx)
121{
806e732c 122 switch (cygwait (mtx, cw_infinite, cw_sig_eintr | cw_cancel | cw_cancel_self))
7b44665a 123 {
7b487ba9
CV
124 case WAIT_OBJECT_0:
125 case WAIT_ABANDONED_0:
126 return 0;
962f9a2c 127 case WAIT_SIGNALED:
7b487ba9
CV
128 set_errno (EINTR);
129 return 1;
130 default:
131 break;
7b44665a 132 }
7b487ba9
CV
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
3748b3e8 149ipc_cond_init (HANDLE *pevt, const char *name, char sr)
7b487ba9 150{
3748b3e8
CV
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 {
61522196 166 debug_printf ("NtCreateEvent: %y", status);
3748b3e8
CV
167 return geterrno_from_win_error (RtlNtStatusToDosError (status));
168 }
169 return 0;
7b487ba9
CV
170}
171
172static int
173ipc_cond_timedwait (HANDLE evt, HANDLE mtx, const struct timespec *abstime)
174{
962f9a2c 175 HANDLE w4[4] = { evt, };
86bf572e 176 DWORD cnt = 2;
206a6ee9 177 DWORD timer_idx = 0;
86bf572e
CV
178 int ret = 0;
179
44aa2292 180 set_signal_arrived here (w4[1]);
a91ac4dc
CV
181 if ((w4[cnt] = pthread::get_cancel_event ()) != NULL)
182 ++cnt;
86bf572e
CV
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
206a6ee9
CV
197 timer_idx = cnt++;
198 status = NtCreateTimer (&w4[timer_idx], TIMER_ALL_ACCESS, NULL,
86bf572e
CV
199 NotificationTimer);
200 if (!NT_SUCCESS (status))
201 return geterrno_from_nt_status (status);
202 timespec_to_filetime (abstime, (FILETIME *) &duetime);
206a6ee9 203 status = NtSetTimer (w4[timer_idx], &duetime, NULL, NULL, FALSE, 0, NULL);
86bf572e
CV
204 if (!NT_SUCCESS (status))
205 {
206a6ee9 206 NtClose (w4[timer_idx]);
86bf572e
CV
207 return geterrno_from_nt_status (status);
208 }
7b487ba9 209 }
3748b3e8 210 ResetEvent (evt);
86bf572e
CV
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))
7b44665a 216 {
7b487ba9 217 case WAIT_OBJECT_0:
86bf572e
CV
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:
206a6ee9 225 if (timer_idx != 2)
b86f999a 226 pthread::static_cancel_self ();
206a6ee9
CV
227 /*FALLTHRU*/
228 case WAIT_OBJECT_0 + 3:
86bf572e
CV
229 ret = ETIMEDOUT;
230 break;
7b487ba9 231 default:
86bf572e 232 ret = geterrno_from_win_error ();
7b487ba9 233 break;
7b44665a 234 }
86bf572e
CV
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:
206a6ee9
CV
253 if (timer_idx != 2)
254 pthread_testcancel ();
255 /*FALLTHRU*/
256 case WAIT_OBJECT_0 + 3:
86bf572e
CV
257 ret = ETIMEDOUT;
258 break;
259 default:
260 ret = geterrno_from_win_error ();
261 break;
262 }
263 }
206a6ee9 264 if (timer_idx)
86bf572e
CV
265 {
266 if (ret != ETIMEDOUT)
206a6ee9
CV
267 NtCancelTimer (w4[timer_idx], NULL);
268 NtClose (w4[timer_idx]);
86bf572e
CV
269 }
270 return ret;
7b487ba9
CV
271}
272
86bf572e 273static inline void
7b487ba9
CV
274ipc_cond_signal (HANDLE evt)
275{
86bf572e 276 SetEvent (evt);
7b487ba9
CV
277}
278
86bf572e 279static inline void
7b487ba9
CV
280ipc_cond_close (HANDLE evt)
281{
86bf572e 282 CloseHandle (evt);
7b487ba9
CV
283}
284
8fbd574e
CV
285class ipc_flock
286{
61522196 287 struct flock fl;
8fbd574e
CV
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;
fabfb1a1 298 return fcntl64 (fd, F_SETLKW, &fl);
8fbd574e
CV
299 }
300 int unlock (int fd)
301 {
302 if (!fl.l_len)
303 return 0;
304 fl.l_type = F_UNLCK;
fabfb1a1 305 return fcntl64 (fd, F_SETLKW, &fl);
8fbd574e
CV
306 }
307};
308
7b487ba9
CV
309/* POSIX shared memory object implementation. */
310
311extern "C" int
312shm_open (const char *name, int oflag, mode_t mode)
313{
547ad329
CV
314 size_t len = strlen (name);
315 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 316
547ad329 317 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
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
e70fdfb9 329 return open (shmname, oflag | O_CLOEXEC, mode & 0777);
7b487ba9
CV
330}
331
332extern "C" int
333shm_unlink (const char *name)
334{
547ad329
CV
335 size_t len = strlen (name);
336 char shmname[ipc_names[shmem].prefix_len + len];
7b487ba9 337
547ad329 338 if (!check_path (shmname, shmem, name, len))
7b487ba9
CV
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
61522196
CV
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
7b487ba9
CV
365struct mq_hdr
366{
61522196
CV
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() */
93162be5 371 pid_t mqh_pid; /* nonzero PID if mqh_event set */
8fbd574e 372 char mqh_uname[36]; /* unique name used to identify synchronization
7b44665a 373 objects connected to this queue */
61522196
CV
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. */
7b487ba9
CV
383};
384
385struct msg_hdr
386{
61522196
CV
387 int32_t msg_next; /* index of next on linked list */
388 int32_t msg_len; /* actual length */
93162be5 389 unsigned int msg_prio; /* priority */
7b487ba9 390};
61522196 391#pragma pack (pop)
7b487ba9
CV
392
393struct mq_info
394{
93162be5 395 struct mq_hdr *mqi_hdr; /* start of mmap'ed region */
61522196 396 uint32_t mqi_magic; /* magic number if open */
93162be5
CV
397 int mqi_flags; /* flags for this process */
398 HANDLE mqi_lock; /* mutex lock */
3748b3e8
CV
399 HANDLE mqi_waitsend; /* and condition variable for full queue */
400 HANDLE mqi_waitrecv; /* and condition variable for empty queue */
7b487ba9
CV
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
61522196
CV
411extern "C" off_t lseek64 (int, off_t, int);
412extern "C" void *mmap64 (void *, size_t, int, int, int, off_t);
7b487ba9
CV
413
414extern "C" mqd_t
415mq_open (const char *name, int oflag, ...)
416{
599d462d 417 int i, fd = -1, nonblock, created;
7b487ba9 418 long msgsize, index;
61522196 419 off_t filesize = 0;
7b487ba9
CV
420 va_list ap;
421 mode_t mode;
422 int8_t *mptr;
61522196 423 struct stat statbuff;
7b487ba9
CV
424 struct mq_hdr *mqhdr;
425 struct msg_hdr *msghdr;
426 struct mq_attr *attr;
427 struct mq_info *mqinfo;
8fbd574e 428 LUID luid;
7b487ba9 429
547ad329
CV
430 size_t len = strlen (name);
431 char mqname[ipc_names[mqueue].prefix_len + len];
432
433 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
434 return (mqd_t) -1;
435
436 myfault efault;
437 if (efault.faulted (EFAULT))
8fbd574e 438 return (mqd_t) -1;
7b487ba9 439
8fbd574e 440 oflag &= (O_CREAT | O_EXCL | O_NONBLOCK);
7b487ba9
CV
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 */
e70fdfb9 456 fd = open (mqname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
7b487ba9 457 if (fd < 0)
7b44665a 458 {
7b487ba9
CV
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;
61522196
CV
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)
7b487ba9
CV
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)
7b44665a 480 + (attr->mq_maxmsg * (sizeof (struct msg_hdr) + msgsize));
7b487ba9
CV
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 */
3748b3e8 493 if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
7b487ba9
CV
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;
41e9c145 507 NtAllocateLocallyUniqueId (&luid);
abbde487 508 __small_sprintf (mqhdr->mqh_uname, "%016X%08x%08x",
8fbd574e
CV
509 hash_path_name (0,mqname),
510 luid.HighPart, luid.LowPart);
7b487ba9 511 mqhdr->mqh_head = 0;
61522196 512 mqhdr->mqh_magic = MQI_MAGIC;
7b487ba9
CV
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
3748b3e8 524 /* Initialize mutex & condition variables */
93162be5 525 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
7b487ba9
CV
526 if (i != 0)
527 goto pthreaderr;
528
3748b3e8
CV
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');
7b487ba9
CV
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 */
e70fdfb9 546 if ((fd = open (mqname, O_RDWR | O_CLOEXEC)) < 0)
7b487ba9
CV
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 {
8fbd574e 559 close (fd);
599d462d 560 fd = -1;
7b487ba9
CV
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);
599d462d 581 fd = -1;
7b487ba9
CV
582
583 /* Allocate one mq_info{} for each open */
3748b3e8 584 if (!(mqinfo = (struct mq_info *) calloc (1, sizeof (struct mq_info))))
7b487ba9 585 goto err;
eb8a6636 586 mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
61522196
CV
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 }
7b487ba9
CV
596 mqinfo->mqi_magic = MQI_MAGIC;
597 mqinfo->mqi_flags = nonblock;
598
599 /* Initialize mutex & condition variable */
93162be5 600 i = ipc_mutex_init (&mqinfo->mqi_lock, mqhdr->mqh_uname);
7b487ba9
CV
601 if (i != 0)
602 goto pthreaderr;
603
3748b3e8
CV
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');
7b487ba9
CV
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)
3748b3e8
CV
625 {
626 if (mqinfo->mqi_lock)
b86f999a 627 ipc_mutex_close (mqinfo->mqi_lock);
3748b3e8
CV
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 }
599d462d
CV
634 if (fd >= 0)
635 close (fd);
7b487ba9
CV
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;
61522196 644 struct mq_fattr *attr;
7b487ba9 645 struct mq_info *mqinfo;
7b44665a 646
7b487ba9
CV
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;
7b44665a 663 }
7b487ba9
CV
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;
7b44665a 671}
7b487ba9
CV
672
673extern "C" int
674mq_setattr (mqd_t mqd, const struct mq_attr *mqstat, struct mq_attr *omqstat)
675{
676 int n;
7b44665a 677 struct mq_hdr *mqhdr;
61522196 678 struct mq_fattr *attr;
7b487ba9
CV
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;
7b44665a 723
7b487ba9
CV
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 {
7b44665a 731 set_errno (EBADF);
7b487ba9
CV
732 return -1;
733 }
7b44665a 734 mqhdr = mqinfo->mqi_hdr;
7b487ba9
CV
735 if ((n = ipc_mutex_lock (mqinfo->mqi_lock)) != 0)
736 {
737 errno = n;
738 return -1;
739 }
7b44665a 740
7b487ba9
CV
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;
7b44665a 760 }
7b487ba9
CV
761 ipc_mutex_unlock (mqinfo->mqi_lock);
762 return 0;
7b44665a 763}
7b487ba9
CV
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;
61522196 774 struct mq_fattr *attr;
7b487ba9
CV
775 struct msg_hdr *msghdr, *nmsghdr, *pmsghdr;
776 struct mq_info *mqinfo;
777
74f9ac5b
CV
778 pthread_testcancel ();
779
7b487ba9
CV
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)
b994b837
CV
830 {
831 int ret = ipc_cond_timedwait (mqinfo->mqi_waitsend, mqinfo->mqi_lock,
832 abstime);
833 if (ret != 0)
834 {
835 set_errno (ret);
86bf572e 836 return -1;
b994b837
CV
837 }
838 }
7b487ba9
CV
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)
3748b3e8 874 ipc_cond_signal (mqinfo->mqi_waitrecv);
7b487ba9
CV
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;
7b44665a 906 struct mq_hdr *mqhdr;
61522196 907 struct mq_fattr *attr;
7b487ba9
CV
908 struct msg_hdr *msghdr;
909 struct mq_info *mqinfo;
910
74f9ac5b
CV
911 pthread_testcancel ();
912
7b487ba9
CV
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)
b994b837
CV
947 {
948 int ret = ipc_cond_timedwait (mqinfo->mqi_waitrecv, mqinfo->mqi_lock,
949 abstime);
950 if (ret != 0)
951 {
952 set_errno (ret);
86bf572e 953 return -1;
b994b837
CV
954 }
955 }
7b487ba9
CV
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)
3748b3e8 975 ipc_cond_signal (mqinfo->mqi_waitsend);
7b487ba9
CV
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;
61522196 1004 struct mq_fattr *attr;
7b487ba9
CV
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 */
3748b3e8
CV
1030 ipc_cond_close (mqinfo->mqi_waitsend);
1031 ipc_cond_close (mqinfo->mqi_waitrecv);
7b487ba9
CV
1032 ipc_mutex_close (mqinfo->mqi_lock);
1033 free (mqinfo);
1034 return 0;
1035}
1036
1037extern "C" int
1038mq_unlink (const char *name)
1039{
547ad329
CV
1040 size_t len = strlen (name);
1041 char mqname[ipc_names[mqueue].prefix_len + len];
7b487ba9 1042
547ad329 1043 if (!check_path (mqname, mqueue, name, len))
7b487ba9
CV
1044 return -1;
1045 if (unlink (mqname) == -1)
1046 return -1;
1047 return 0;
1048}
1049
8fbd574e
CV
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{
599d462d 1065 int i, fd = -1, created;
8fbd574e
CV
1066 va_list ap;
1067 mode_t mode = 0;
1068 unsigned int value = 0;
61522196 1069 struct stat statbuff;
8fbd574e
CV
1070 sem_t *sem = SEM_FAILED;
1071 sem_finfo sf;
8fbd574e
CV
1072 bool wasopen = false;
1073 ipc_flock file;
1074
547ad329
CV
1075 size_t len = strlen (name);
1076 char semname[ipc_names[semaphore].prefix_len + len];
1077
1078 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
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 */
e70fdfb9 1097 fd = open (semname, oflag | O_EXCL | O_RDWR | O_CLOEXEC, mode | S_IXUSR);
8fbd574e 1098 if (fd < 0)
7b44665a 1099 {
8fbd574e
CV
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. */
41e9c145 1106 NtAllocateLocallyUniqueId (&sf.luid);
8fbd574e
CV
1107 sf.value = value;
1108 sf.hash = hash_path_name (0, semname);
1109 if (write (fd, &sf, sizeof sf) != sizeof sf)
7b44665a 1110 goto err;
8fbd574e
CV
1111 sem = semaphore::open (sf.hash, sf.luid, fd, oflag, mode, value, wasopen);
1112 if (sem == SEM_FAILED)
7b44665a 1113 goto err;
8fbd574e
CV
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. */
e70fdfb9 1123 if ((fd = open (semname, O_RDWR | O_CLOEXEC)) < 0)
8fbd574e
CV
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);
599d462d 1137 fd = -1;
8fbd574e
CV
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);
599d462d
CV
1176 if (fd >= 0)
1177 close (fd);
8fbd574e
CV
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)
61522196 1191 && lseek64 (fd, 0LL, SEEK_SET) != (off_t) -1
8fbd574e
CV
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{
547ad329
CV
1212 size_t len = strlen (name);
1213 char semname[ipc_names[semaphore].prefix_len + len];
8fbd574e 1214
547ad329 1215 if (!check_path (semname, semaphore, name, len))
8fbd574e
CV
1216 return -1;
1217 if (unlink (semname) == -1)
1218 return -1;
1219 return 0;
1220}
This page took 0.337691 seconds and 5 git commands to generate.