From 5e09e37d42bd8896a687df830a95f5cb56e4d47c Mon Sep 17 00:00:00 2001 From: Mike McDonald Date: Fri, 18 Apr 2014 13:16:42 -0400 Subject: [PATCH] Modified named pipes to support multiple instances (i.e., multiple calls to CreateNamedPipe with the same pipe name). --- winpr/libwinpr/handle/handle.c | 31 +++++-- winpr/libwinpr/pipe/pipe.c | 147 ++++++++++++++++++++++++--------- winpr/libwinpr/pipe/pipe.h | 4 + 3 files changed, 135 insertions(+), 47 deletions(-) diff --git a/winpr/libwinpr/handle/handle.c b/winpr/libwinpr/handle/handle.c index 929ee0399..150019087 100644 --- a/winpr/libwinpr/handle/handle.c +++ b/winpr/libwinpr/handle/handle.c @@ -22,6 +22,7 @@ #endif #include +#include #ifndef _WIN32 @@ -36,6 +37,8 @@ #include "../handle/handle.h" +extern wArrayList* WinPR_NamedPipeBaseInstances; + BOOL CloseHandle(HANDLE hObject) { ULONG Type; @@ -167,16 +170,28 @@ BOOL CloseHandle(HANDLE hObject) pipe = (WINPR_NAMED_PIPE*) Object; - if (pipe->clientfd != -1) - close(pipe->clientfd); + if (--pipe->dwRefCount == 0) + { + ArrayList_Lock(WinPR_NamedPipeBaseInstances); + ArrayList_Remove(WinPR_NamedPipeBaseInstances, pipe); + ArrayList_Unlock(WinPR_NamedPipeBaseInstances); - if (pipe->serverfd != -1) - close(pipe->serverfd); + if (pipe->pBaseNamedPipe) + { + CloseHandle((HANDLE) pipe->pBaseNamedPipe); + } - free((char *)pipe->lpFileName); - free((char *)pipe->lpFilePath); - free((char *)pipe->name); - free(pipe); + if (pipe->clientfd != -1) + close(pipe->clientfd); + + if (pipe->serverfd != -1) + close(pipe->serverfd); + + free((char *)pipe->lpFileName); + free((char *)pipe->lpFilePath); + free((char *)pipe->name); + free(pipe); + } return TRUE; } diff --git a/winpr/libwinpr/pipe/pipe.c b/winpr/libwinpr/pipe/pipe.c index 36ab3be6c..3384caaee 100644 --- a/winpr/libwinpr/pipe/pipe.c +++ b/winpr/libwinpr/pipe/pipe.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -44,6 +45,34 @@ #include "pipe.h" +/* + * Since the WINPR implementation of named pipes makes use of UNIX domain + * sockets, it is not possible to bind the same name more than once (i.e., + * SO_REUSEADDR does not work with UNIX domain sockets). As a result, the + * first call to CreateNamedPipe must create the UNIX domain socket and + * subsequent calls to CreateNamedPipe will reference the first named pipe + * handle and duplicate the socket descriptor. + * + * The following array keeps track of the named pipe handles for the first + * instance. If multiple instances are created, subsequent instances store + * a pointer to the first instance and a reference count is maintained. When + * the last instance is closed, the named pipe handle is removed from the list. + */ + +wArrayList* WinPR_NamedPipeBaseInstances; + +static void InitWinPRPipeModule() +{ + static BOOL bInitialized = FALSE; + + if (bInitialized) return; + + WinPR_NamedPipeBaseInstances = ArrayList_New(TRUE); + + bInitialized = TRUE; +} + + /* * Unnamed pipe */ @@ -96,15 +125,34 @@ BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpP HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes) { + int index; int status; HANDLE hNamedPipe; char* lpPipePath; struct sockaddr_un s; WINPR_NAMED_PIPE* pNamedPipe; + WINPR_NAMED_PIPE* pBaseNamedPipe; if (!lpName) return INVALID_HANDLE_VALUE; + InitWinPRPipeModule(); + + /* Find the base named pipe instance (i.e., the first instance). */ + pBaseNamedPipe = NULL; + + ArrayList_Lock(WinPR_NamedPipeBaseInstances); + for (index = 0; index < ArrayList_Count(WinPR_NamedPipeBaseInstances); index++) + { + WINPR_NAMED_PIPE* p = (WINPR_NAMED_PIPE*) ArrayList_GetItem(WinPR_NamedPipeBaseInstances, index); + if (strcmp(p->name, lpName) == 0) + { + pBaseNamedPipe = p; + break; + } + } + ArrayList_Unlock(WinPR_NamedPipeBaseInstances); + pNamedPipe = (WINPR_NAMED_PIPE*) malloc(sizeof(WINPR_NAMED_PIPE)); hNamedPipe = (HANDLE) pNamedPipe; @@ -122,54 +170,75 @@ HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpName); pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpName); - lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA(); - - if (!PathFileExistsA(lpPipePath)) - { - CreateDirectoryA(lpPipePath, 0); - UnixChangeFileMode(lpPipePath, 0xFFFF); - } - - free(lpPipePath); - - if (PathFileExistsA(pNamedPipe->lpFilePath)) - { - DeleteFileA(pNamedPipe->lpFilePath); - } - pNamedPipe->clientfd = -1; pNamedPipe->ServerMode = TRUE; - pNamedPipe->serverfd = socket(AF_UNIX, SOCK_STREAM, 0); + pNamedPipe->pBaseNamedPipe = pBaseNamedPipe; + pNamedPipe->dwRefCount = 1; - if (pNamedPipe->serverfd == -1) + /* If this is the first instance of the named pipe... */ + if (pBaseNamedPipe == NULL) { - fprintf(stderr, "CreateNamedPipeA: socket error, %s\n", strerror(errno)); - return INVALID_HANDLE_VALUE; + /* Create the UNIX domain socket and start listening. */ + lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA(); + + if (!PathFileExistsA(lpPipePath)) + { + CreateDirectoryA(lpPipePath, 0); + UnixChangeFileMode(lpPipePath, 0xFFFF); + } + + free(lpPipePath); + + if (PathFileExistsA(pNamedPipe->lpFilePath)) + { + DeleteFileA(pNamedPipe->lpFilePath); + } + + pNamedPipe->serverfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (pNamedPipe->serverfd == -1) + { + fprintf(stderr, "CreateNamedPipeA: socket error, %s\n", strerror(errno)); + return INVALID_HANDLE_VALUE; + } + + ZeroMemory(&s, sizeof(struct sockaddr_un)); + s.sun_family = AF_UNIX; + strcpy(s.sun_path, pNamedPipe->lpFilePath); + + status = bind(pNamedPipe->serverfd, (struct sockaddr*) &s, sizeof(struct sockaddr_un)); + + if (status != 0) + { + fprintf(stderr, "CreateNamedPipeA: bind error, %s\n", strerror(errno)); + return INVALID_HANDLE_VALUE; + } + + status = listen(pNamedPipe->serverfd, 2); + + if (status != 0) + { + fprintf(stderr, "CreateNamedPipeA: listen error, %s\n", strerror(errno)); + return INVALID_HANDLE_VALUE; + } + + UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF); + + /* Add the named pipe to the list of base named pipe instances. */ + ArrayList_Lock(WinPR_NamedPipeBaseInstances); + ArrayList_Add(WinPR_NamedPipeBaseInstances, pNamedPipe); + ArrayList_Unlock(WinPR_NamedPipeBaseInstances); } - - ZeroMemory(&s, sizeof(struct sockaddr_un)); - s.sun_family = AF_UNIX; - strcpy(s.sun_path, pNamedPipe->lpFilePath); - - status = bind(pNamedPipe->serverfd, (struct sockaddr*) &s, sizeof(struct sockaddr_un)); - - if (status != 0) + else { - fprintf(stderr, "CreateNamedPipeA: bind error, %s\n", strerror(errno)); - return INVALID_HANDLE_VALUE; + /* Duplicate the file handle for the UNIX domain socket in the first instance. */ + pNamedPipe->serverfd = dup(pBaseNamedPipe->serverfd); + + /* Update the reference count in the base named pipe instance. */ + pBaseNamedPipe->dwRefCount++; } - status = listen(pNamedPipe->serverfd, 2); - - if (status != 0) - { - fprintf(stderr, "CreateNamedPipeA: listen error, %s\n", strerror(errno)); - return INVALID_HANDLE_VALUE; - } - - UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF); - if (dwOpenMode & FILE_FLAG_OVERLAPPED) { #if 0 diff --git a/winpr/libwinpr/pipe/pipe.h b/winpr/libwinpr/pipe/pipe.h index 51b423dfa..042918b94 100644 --- a/winpr/libwinpr/pipe/pipe.h +++ b/winpr/libwinpr/pipe/pipe.h @@ -38,6 +38,10 @@ struct winpr_named_pipe { WINPR_HANDLE_DEF(); + struct winpr_named_pipe* pBaseNamedPipe; + + DWORD dwRefCount; + int clientfd; int serverfd;