Hi, Is the one-way mailslot IPC implemented in wine? I'm getting fixme:sync:CreateMailslotW(...): stub messages. Is this a simple thing to implement, or would it require additions to the server protocol? Thanks, Kuba Ober
Kuba Ober wrote:
Is the one-way mailslot IPC implemented in wine?
No.
I'm getting fixme:sync:CreateMailslotW(...): stub messages. Is this a simple thing to implement, or would it require additions to the server protocol?
Yes, it needs to be implemented in the server. I started writing it some time ago, but never got round to completing it. I submitted a test case...
Which application uses mailslots? I only ever found one real application that used them, and that was "Declan's Korean Dictionary" from [1].
If you're interested, I can send you what I wrote. It's probably very out of date by now though.
Mike
On Tue, Mar 15, 2005 at 01:44:19AM +0900, Mike McCormack wrote about 'Re: Are mailslots implemented?':
Kuba Ober wrote:
I'm getting fixme:sync:CreateMailslotW(...): stub messages. Is this a simple thing to implement, or would it require additions to the server protocol?
Yes, it needs to be implemented in the server. I started writing it some time ago, but never got round to completing it. I submitted a test case...
Which application uses mailslots? I only ever found one real application that used them, and that was "Declan's Korean Dictionary" from [1].
The browse (Network Neighborhood) service uses them. Other then that, I have not seen any uses of it.
Cheers,
Jelmer
Answering myself, partially:
After a quick read: libsmbclient.h doesn't expose any datagram stuff. That means we'd need to import samba internal headers in order to use the relevant functions which I hope are in libsmbclient.so. Or maybe just make a header with a couple definitions for just the stuff that we need. What are the policies on such stuff?
readelf'ing it showed a couple nice ones like cli_send_mailslot. That's what we need, I hope :)
Jelmer: what is the stability of mailslot-related function API in libsmbclient?
I would do an dlopen to bind it at runtime, only if available, so that wine wouldn't get a samba dependency. Otherwise I plan on issuing a warning and reverting to the current-session-only mailslots. Is that a good plan?
Cheers, Kuba
Kuba Ober wrote:
Answering myself, partially:
After a quick read: libsmbclient.h doesn't expose any datagram stuff. That
Hi Guys,
Here's the mailslot implementatation that I wrote a while back. I've updated it to compile on the current CVS tip, but it's totally untested... I will try to test it, fix it and submit it, but incase I don't get round to doing that, here's a starting point for somebody.
Note that this patch does not allow mailslots to work over the network, only program to program inside Wine. Mailslots and named pipes over the network are hard to implement correctly in Wine. We can't use libsmbclient, and it probably needs to be done in the kernel to work correctly. Perhaps fuse (http://fuse.sourceforge.net/) will help when it is integrated into the Linux kernel.
The test program I wrote to test mailslots a while back is available at:
http://mandoo.dyndns.org/winetests/mailslot.zip
It's pretty much the same as the regression test in dlls/kernel/tests/mailslot.c.
Apply the patch by running the following commands on an up to date CVS copy of Wine:
cd wine patch -p0 --dry-run < mailslot.diff (check there's no errors) patch -p0 < mailslot.diff tools/make_requests config.status server/Makefile make depend make
The patch and the test program are both LGPL licensed. The test program should run and terminate with no output or errors. (Running the test program now causes it to hang...)
Mike
Index: dlls/kernel/sync.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/sync.c,v retrieving revision 1.71 diff -u -p -r1.71 sync.c --- dlls/kernel/sync.c 4 Mar 2005 12:38:37 -0000 1.71 +++ dlls/kernel/sync.c 15 Mar 2005 08:50:49 -0000 @@ -1574,10 +1574,31 @@ HANDLE WINAPI CreateMailslotA( LPCSTR lp HANDLE WINAPI CreateMailslotW( LPCWSTR lpName, DWORD nMaxMessageSize, DWORD lReadTimeout, LPSECURITY_ATTRIBUTES sa ) { - FIXME("(%s,%ld,%ld,%p): stub\n", debugstr_w(lpName), + DWORD len = lpName ? strlenW(lpName) : 0; + HANDLE ret; + + TRACE("%s %ld %ld %p\n", debugstr_w(lpName), nMaxMessageSize, lReadTimeout, sa); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return INVALID_HANDLE_VALUE; + + if( !lpName ) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + + SERVER_START_REQ( create_mailslot ) + { + req->max_msgsize = nMaxMessageSize; + req->read_timeout = lReadTimeout; + req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle); + wine_server_add_data( req, lpName, len * sizeof(WCHAR) ); + SetLastError(0); + if (!wine_server_call_err( req )) ret = reply->handle; + else ret = INVALID_HANDLE_VALUE; + } + SERVER_END_REQ; + + return ret; }
@@ -1601,9 +1622,32 @@ BOOL WINAPI GetMailslotInfo( HANDLE hMai LPDWORD lpNextSize, LPDWORD lpMessageCount, LPDWORD lpReadTimeout ) { - FIXME("(%p): stub\n",hMailslot); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + BOOL r; + + TRACE("%p %p %p %p %p\n",hMailslot, + lpMaxMessageSize,lpNextSize,lpMessageCount,lpReadTimeout); + + SERVER_START_REQ( mailslot_info ) + { + req->handle = hMailslot; + req->flags = 0; + SetLastError(0); + r = !wine_server_call_err( req ); + if( r ) + { + if( lpMaxMessageSize ) + *lpMaxMessageSize = reply->max_msgsize; + if( lpNextSize ) + *lpNextSize = reply->next_msgsize; + if( lpMessageCount ) + *lpMessageCount = reply->msg_count; + if( lpReadTimeout ) + *lpReadTimeout = reply->read_timeout; + } + } + SERVER_END_REQ; + + return r; }
@@ -1622,9 +1666,21 @@ BOOL WINAPI GetMailslotInfo( HANDLE hMai */ BOOL WINAPI SetMailslotInfo( HANDLE hMailslot, DWORD dwReadTimeout) { - FIXME("%p %ld: stub\n", hMailslot, dwReadTimeout); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + BOOL r; + + TRACE("%p %ld\n", hMailslot, dwReadTimeout); + + SERVER_START_REQ( mailslot_info ) + { + req->handle = hMailslot; + req->flags = MAILSLOT_SET_INFO; + req->read_timeout = dwReadTimeout; + SetLastError(0); + r = !wine_server_call_err( req ); + } + SERVER_END_REQ; + + return r; }
Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.120 diff -u -p -r1.120 protocol.def --- server/protocol.def 10 Mar 2005 11:52:25 -0000 1.120 +++ server/protocol.def 15 Mar 2005 08:50:50 -0000 @@ -2260,3 +2260,37 @@ enum message_type @REPLY obj_handle_t new_handle; /* duplicated handle */ @END + +/* create a mailslot */ +@REQ(create_mailslot) + unsigned int max_msgsize; + unsigned int read_timeout; + int inherit; + VARARG(name,unicode_str); /* mailslot name */ +@REPLY + obj_handle_t handle; /* handle to the mailslot */ +@END + + +/* Open an existing mailslot */ +@REQ(open_mailslot) + unsigned int access; + int inherit; /* inherit flag */ + int sharing; /* sharing mode */ + VARARG(name,unicode_str); /* mailslot name */ +@REPLY + obj_handle_t handle; /* handle to the mailslot */ +@END + + +#define MAILSLOT_SET_INFO 1 +@REQ(mailslot_info) + obj_handle_t handle; /* handle to the mailslot */ + int flags; + unsigned int read_timeout; +@REPLY + unsigned int max_msgsize; + unsigned int read_timeout; + unsigned int msg_count; + unsigned int next_msgsize; +@END Index: server/Makefile.in =================================================================== RCS file: /home/wine/wine/server/Makefile.in,v retrieving revision 1.53 diff -u -p -r1.53 Makefile.in --- server/Makefile.in 14 Jan 2005 19:54:39 -0000 1.53 +++ server/Makefile.in 15 Mar 2005 08:50:50 -0000 @@ -21,6 +21,7 @@ C_SRCS = \ file.c \ handle.c \ hook.c \ + mailslot.c \ main.c \ mapping.c \ mutex.c \ --- /dev/null 2005-02-25 01:31:53.000000000 +0900 +++ server/mailslot.c 2005-03-15 17:33:20.000000000 +0900 @@ -0,0 +1,454 @@ +/* + * Server-side mailslot management + * + * Copyright (C) 1998 Alexandre Julliard + * Copyright (C) 2003 Mike McCormack + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "wine/port.h" +#include "wine/unicode.h" + +#include <assert.h> +#include <fcntl.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "windef.h" +#include "winbase.h" + +#include "file.h" +#include "handle.h" +#include "thread.h" +#include "request.h" + +struct mail_writer; + +struct mailslot +{ + struct object obj; + struct fd *fd; + struct fd *write_fd; + unsigned int max_msgsize; + unsigned int read_timeout; + struct mail_writer *first_writer, *last_writer; + struct list read_q; +}; + +/* mailslot functions */ +static void mailslot_dump( struct object *obj, int verbose ); +static struct fd *mailslot_get_fd( struct object *obj ); +static void mailslot_destroy( struct object *obj); + +static const struct object_ops mailslot_ops = +{ + sizeof(struct mailslot), /* size */ + mailslot_dump, /* dump */ + default_fd_add_queue, /* add_queue */ + default_fd_remove_queue, /* remove_queue */ + default_fd_signaled, /* signaled */ + no_satisfied, /* satisfied */ + mailslot_get_fd, /* get_fd */ + mailslot_destroy /* destroy */ +}; + +static int mailslot_get_poll_events( struct fd *fd ); +static void mailslot_poll_event( struct fd *fd, int event ); +static int mailslot_get_info( struct fd *fd ); +static void mailslot_queue_async( struct fd *fd, void *apc, void *user, void *iosb, int type, int count ); +static void mailslot_cancel_async( struct fd *fd ); + +static const struct fd_ops mailslot_fd_ops = +{ + mailslot_get_poll_events, /* get_poll_events */ + mailslot_poll_event, /* poll_event */ + no_flush, /* flush */ + mailslot_get_info, /* get_file_info */ + mailslot_queue_async, /* queue_async */ + mailslot_cancel_async /* cancel_async */ +}; + +struct mail_writer +{ + struct object obj; + struct mailslot *mailslot; + struct mail_writer *next, *prev; + int access; + int sharing; +}; + +static void mail_writer_dump( struct object *obj, int verbose ); +static struct fd *mail_writer_get_fd( struct object *obj ); +static void mail_writer_destroy( struct object *obj); + +static const struct object_ops mail_writer_ops = +{ + sizeof(struct mail_writer), /* size */ + mail_writer_dump, /* dump */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + mail_writer_get_fd, /* get_fd */ + mail_writer_destroy /* destroy */ +}; + +static int mail_writer_get_info( struct fd *fd ); + +static const struct fd_ops mail_writer_fd_ops = +{ + NULL, /* get_poll_events */ + NULL, /* poll_event */ + no_flush, /* flush */ + mail_writer_get_info, /* get_file_info */ + no_queue_async, /* queue_async */ + NULL /* cancel_async */ +}; + +static void mailslot_destroy( struct object *obj) +{ + struct mailslot *mailslot = (struct mailslot *) obj; + + assert( mailslot->fd ); + assert( mailslot->write_fd ); + + async_terminate_queue( &mailslot->read_q, STATUS_CANCELLED ); + + release_object( mailslot->fd ); + release_object( mailslot->write_fd ); +} + +static void mailslot_dump( struct object *obj, int verbose ) +{ + struct mailslot *mailslot = (struct mailslot *) obj; + + assert( obj->ops == &mailslot_ops ); + fprintf( stderr, "mailslot max_msgsize = %p read_timeout = %d %d\n", + mailslot, mailslot->max_msgsize, mailslot->read_timeout ); +} + +static int mailslot_get_info( struct fd *fd ) +{ + struct mailslot *mailslot = get_fd_user( fd ); + assert( mailslot->obj.ops == &mailslot_ops ); + return FD_FLAG_TIMEOUT; +} + +static struct fd *mailslot_get_fd( struct object *obj ) +{ + struct mailslot *mailslot = (struct mailslot *) obj; + + return (struct fd *)grab_object( mailslot->fd ); +} + +static int mailslot_get_poll_events( struct fd *fd ) +{ + struct mailslot *mailslot = get_fd_user( fd ); + int events = 0; + assert( mailslot->obj.ops == &mailslot_ops ); + + if( !list_empty( &mailslot->read_q )) + events |= POLLIN; + + return events; +} + +static void mailslot_poll_event( struct fd *fd, int event ) +{ + struct mailslot *mailslot = get_fd_user( fd ); + + fprintf(stderr,"Poll event %02x\n",event); + + if( !list_empty( &mailslot->read_q ) && (POLLIN & event) ) + async_terminate_head( &mailslot->read_q, STATUS_ALERTED ); + + set_fd_events( fd, mailslot_get_poll_events(fd) ); +} + +static void mailslot_queue_async( struct fd *fd, void *apc, void *user, void *iosb, int type, int count ) +{ + struct mailslot *mailslot = get_fd_user( fd ); + int events; + + assert(mailslot->obj.ops == &mailslot_ops); + + if( type != ASYNC_TYPE_READ ) + { + set_error(STATUS_INVALID_PARAMETER); + return; + } + + if (!create_async( fd, current, 0, &mailslot->read_q, apc, user, iosb )) + return; + + /* Check if the new pending request can be served immediately */ + events = check_fd_events( fd, mailslot_get_poll_events( fd ) ); + if (events) + { + mailslot_poll_event( fd, events ); + return; + } + + set_fd_events( fd, mailslot_get_poll_events( fd )); +} + +static void mailslot_cancel_async( struct fd *fd ) +{ + struct mailslot *mailslot = get_fd_user( fd ); + + assert(mailslot->obj.ops == &mailslot_ops); + async_terminate_queue( &mailslot->read_q, STATUS_CANCELLED ); +} + +struct mailslot *create_mailslot( const WCHAR *name, int len, + int max_msgsize, int read_timeout ) +{ + struct mailslot *mailslot; + int fds[2]; + WCHAR slot[] = {'\','\','.','\','m','a','i','l','s','l','o','t','\',0}; + + if( ( len <= strlenW( slot ) ) || strncmpiW( slot, name, strlenW( slot ) ) ) + { + set_error( STATUS_OBJECT_NAME_INVALID ); + return NULL; + } + + mailslot = create_named_object( sync_namespace, &mailslot_ops, name, len ); + if( !mailslot ) + return NULL; + + /* it already exists - there can only be one mailslot to read from */ + if( get_error() == STATUS_OBJECT_NAME_COLLISION ) + { + release_object( mailslot ); + return NULL; + } + + list_init( &mailslot->read_q ); + mailslot->fd = NULL; + mailslot->write_fd = NULL; + mailslot->max_msgsize = max_msgsize; + mailslot->read_timeout = read_timeout; + mailslot->first_writer = NULL; + mailslot->last_writer = NULL; + + if( !socketpair( PF_UNIX, SOCK_STREAM, 0, fds ) ) + { + fcntl( fds[0], F_SETFL, O_NONBLOCK ); + fcntl( fds[1], F_SETFL, O_NONBLOCK ); + mailslot->fd = create_anonymous_fd( &mailslot_fd_ops, + fds[1], &mailslot->obj ); + mailslot->write_fd = create_anonymous_fd( &mail_writer_fd_ops, + fds[0], &mailslot->obj ); + } + else + file_set_error(); + if( mailslot->fd && mailslot->write_fd ) + return mailslot; + release_object( mailslot ); + return NULL; +} + +static struct mailslot *open_mailslot( const WCHAR *name, size_t len ) +{ + struct object *obj; + + if ((obj = find_object( sync_namespace, name, len ))) + { + if (obj->ops == &mailslot_ops) return (struct mailslot *)obj; + release_object( obj ); + set_error( STATUS_OBJECT_TYPE_MISMATCH ); + } + else set_error( STATUS_OBJECT_NAME_NOT_FOUND ); + + return NULL; +} + +static void mail_writer_dump( struct object *obj, int verbose ) +{ + struct mail_writer *writer = (struct mail_writer *) obj; + + assert( obj->ops == &mail_writer_ops ); + fprintf( stderr, "mail_writer %p\n", writer ); +} + +static void mail_writer_destroy( struct object *obj) +{ + struct mail_writer *writer = (struct mail_writer *) obj; + + if( writer->prev ) + writer->prev->next = writer->next; + else + writer->mailslot->first_writer = writer->next; + if( writer->next ) + writer->next->prev = writer->prev; + else + writer->mailslot->last_writer = writer->prev; + + release_object( writer->mailslot ); +} + +static int mail_writer_get_info( struct fd *fd ) +{ + return 0; +} + +static struct fd *mail_writer_get_fd( struct object *obj ) +{ + struct mail_writer *writer = (struct mail_writer *) obj; + + return (struct fd *)grab_object( writer->mailslot->write_fd ); +} + +/* + * If there's more than one writer, all writers must open with FILE_SHARE_WRITE + * FIXME: We're just assuming everybody's a writer for now + */ +static struct mail_writer * create_mail_writer( + struct mailslot *mailslot, int access, int sharing ) +{ + struct mail_writer *writer; + + if( mailslot->first_writer ) + { + if ( ! ( ( mailslot->first_writer->sharing & FILE_SHARE_WRITE ) + && ( sharing & FILE_SHARE_WRITE ) ) ) + { + set_error( STATUS_SHARING_VIOLATION ); + return NULL; + } + } + + writer = alloc_object( &mail_writer_ops ); + if( !writer ) + return NULL; + + grab_object( mailslot ); + writer->mailslot = mailslot; + writer->access = access; + writer->sharing = sharing; + + writer->prev = mailslot->last_writer; + writer->next = NULL; + if( mailslot->last_writer ) + mailslot->last_writer->next = writer; + else + mailslot->first_writer = writer; + mailslot->last_writer = writer; + + return writer; +} + +static struct mailslot *get_mailslot_obj( struct process *process, + obj_handle_t handle, unsigned int access ) +{ + struct object *obj; + obj = get_handle_obj( process, handle, access, &mailslot_ops ); + return (struct mailslot *) obj; +} + +DECL_HANDLER(create_mailslot) +{ + struct mailslot *mailslot; + + reply->handle = 0; + mailslot = create_mailslot( get_req_data(), get_req_data_size(), + req->max_msgsize, req->read_timeout ); + if( mailslot ) + { + reply->handle = alloc_handle( current->process, mailslot, + GENERIC_READ, req->inherit ); + release_object( mailslot ); + } +} + +DECL_HANDLER(open_mailslot) +{ + struct mailslot *mailslot; + + reply->handle = 0; + + if( ! ( req->sharing & FILE_SHARE_READ ) ) + { + set_error( STATUS_SHARING_VIOLATION ); + return; + } + + mailslot = open_mailslot( get_req_data(), get_req_data_size() ); + if( mailslot ) + { + struct mail_writer *writer; + int access = req->access; + + writer = create_mail_writer( mailslot, access, req->sharing ); + if( writer ) + { + access = access&GENERIC_WRITE; + reply->handle = alloc_handle( current->process, writer, + access, req->inherit ); + release_object( writer ); + } + release_object( mailslot ); + } + else + set_error( STATUS_NO_SUCH_FILE ); +} + +DECL_HANDLER(mailslot_info) +{ + struct mailslot *mailslot; + + mailslot = get_mailslot_obj( current->process, req->handle, 0 ); + if( mailslot ) + { + struct pollfd pfd; + int r; + + if( req->flags & MAILSLOT_SET_INFO ) + mailslot->read_timeout = req->read_timeout; + reply->max_msgsize = mailslot->max_msgsize; + reply->read_timeout = mailslot->read_timeout; + + /* poll the socket to see if there's any messages */ + pfd.fd = get_unix_fd( mailslot->fd ); + pfd.events = POLLIN; + pfd.revents = 0; + if( 1 == poll( &pfd, 1, 0 ) ) + reply->msg_count = 1; + + /* get the size of the next message */ + r = recv( pfd.fd, NULL, 0, MSG_PEEK | MSG_TRUNC ); + if( r < 0 ) + reply->next_msgsize = MAILSLOT_NO_MESSAGE; + else + reply->next_msgsize = r; + + release_object( mailslot ); + } +}
Hi,
On Tue, Mar 15, 2005 at 01:44:19AM +0900, Mike McCormack wrote:
Which application uses mailslots? I only ever found one real application that used them, and that was "Declan's Korean Dictionary" from [1].
Matro's RealPopup, a very good winpopup replacement.
We might want to add a note about this list of applications to these stubs...
Andreas Mohr