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