Are mailslots implemented? [inter-domain mailslots]

Mike McCormack mike at codeweavers.com
Tue Mar 15 03:11:51 CST 2005


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

-------------- next part --------------
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 */
+ at REQ(create_mailslot)
+    unsigned int   max_msgsize;
+    unsigned int   read_timeout;
+    int            inherit;
+    VARARG(name,unicode_str); /* mailslot name */
+ at REPLY
+    obj_handle_t   handle;    /* handle to the mailslot */
+ at END
+
+
+/* Open an existing mailslot */
+ at REQ(open_mailslot)
+    unsigned int   access;
+    int            inherit;      /* inherit flag */
+    int            sharing;      /* sharing mode */
+    VARARG(name,unicode_str);    /* mailslot name */
+ at REPLY
+    obj_handle_t   handle;       /* handle to the mailslot */
+ at END
+
+
+#define MAILSLOT_SET_INFO  1
+ at REQ(mailslot_info)
+    obj_handle_t   handle;    /* handle to the mailslot */
+    int            flags;
+    unsigned int   read_timeout;
+ at REPLY
+    unsigned int   max_msgsize;
+    unsigned int   read_timeout;
+    unsigned int   msg_count;
+    unsigned int   next_msgsize;
+ at 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 );
+    }
+}


More information about the wine-devel mailing list