pipeTestService.py
6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# A Demo of services and named pipes.
# A multi-threaded service that simply echos back its input.
# * Install as a service using "pipeTestService.py install"
# * Use Control Panel to change the user name of the service
# to a real user name (ie, NOT the SystemAccount)
# * Start the service.
# * Run the "pipeTestServiceClient.py" program as the client pipe side.
import win32serviceutil, win32service
import pywintypes, win32con, winerror
# Use "import *" to keep this looking as much as a "normal" service
# as possible. Real code shouldn't do this.
from win32event import *
from win32file import *
from win32pipe import *
from win32api import *
from ntsecuritycon import *
# Old versions of the service framework would not let you import this
# module at the top-level. Now you can, and can check 'Debugging()' and
# 'RunningAsService()' to check your context.
import servicemanager
import traceback
import _thread
def ApplyIgnoreError(fn, args):
try:
return fn(*args)
except error: # Ignore win32api errors.
return None
class TestPipeService(win32serviceutil.ServiceFramework):
_svc_name_ = "PyPipeTestService"
_svc_display_name_ = "Python Pipe Test Service"
_svc_description_ = "Tests Python service framework by receiving and echoing messages over a named pipe"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = CreateEvent(None, 0, 0, None)
self.overlapped = pywintypes.OVERLAPPED()
self.overlapped.hEvent = CreateEvent(None,0,0,None)
self.thread_handles = []
def CreatePipeSecurityObject(self):
# Create a security object giving World read/write access,
# but only "Owner" modify access.
sa = pywintypes.SECURITY_ATTRIBUTES()
sidEveryone = pywintypes.SID()
sidEveryone.Initialize(SECURITY_WORLD_SID_AUTHORITY,1)
sidEveryone.SetSubAuthority(0, SECURITY_WORLD_RID)
sidCreator = pywintypes.SID()
sidCreator.Initialize(SECURITY_CREATOR_SID_AUTHORITY,1)
sidCreator.SetSubAuthority(0, SECURITY_CREATOR_OWNER_RID)
acl = pywintypes.ACL()
acl.AddAccessAllowedAce(FILE_GENERIC_READ|FILE_GENERIC_WRITE, sidEveryone)
acl.AddAccessAllowedAce(FILE_ALL_ACCESS, sidCreator)
sa.SetSecurityDescriptorDacl(1, acl, 0)
return sa
# The functions executed in their own thread to process a client request.
def DoProcessClient(self, pipeHandle, tid):
try:
try:
# Create a loop, reading large data. If we knew the data stream was
# was small, a simple ReadFile would do.
d = ''.encode('ascii') # ensure bytes on py2k and py3k...
hr = winerror.ERROR_MORE_DATA
while hr==winerror.ERROR_MORE_DATA:
hr, thisd = ReadFile(pipeHandle, 256)
d = d + thisd
print("Read", d)
ok = 1
except error:
# Client disconnection - do nothing
ok = 0
# A secure service would handle (and ignore!) errors writing to the
# pipe, but for the sake of this demo we dont (if only to see what errors
# we can get when our clients break at strange times :-)
if ok:
msg = ("%s (on thread %d) sent me %s" % (GetNamedPipeHandleState(pipeHandle)[4],tid, d)).encode('ascii')
WriteFile(pipeHandle, msg)
finally:
ApplyIgnoreError( DisconnectNamedPipe, (pipeHandle,) )
ApplyIgnoreError( CloseHandle, (pipeHandle,) )
def ProcessClient(self, pipeHandle):
try:
procHandle = GetCurrentProcess()
th = DuplicateHandle(procHandle, GetCurrentThread(), procHandle, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
try:
self.thread_handles.append(th)
try:
return self.DoProcessClient(pipeHandle, th)
except:
traceback.print_exc()
finally:
self.thread_handles.remove(th)
except:
traceback.print_exc()
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
SetEvent(self.hWaitStop)
def SvcDoRun(self):
# Write an event log record - in debug mode we will also
# see this message printed.
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, '')
)
num_connections = 0
while 1:
pipeHandle = CreateNamedPipe("\\\\.\\pipe\\PyPipeTest",
PIPE_ACCESS_DUPLEX| FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES, # max instances
0, 0, 6000,
self.CreatePipeSecurityObject())
try:
hr = ConnectNamedPipe(pipeHandle, self.overlapped)
except error as details:
print("Error connecting pipe!", details)
CloseHandle(pipeHandle)
break
if hr==winerror.ERROR_PIPE_CONNECTED:
# Client is already connected - signal event
SetEvent(self.overlapped.hEvent)
rc = WaitForMultipleObjects((self.hWaitStop, self.overlapped.hEvent), 0, INFINITE)
if rc==WAIT_OBJECT_0:
# Stop event
break
else:
# Pipe event - spawn thread to deal with it.
_thread.start_new_thread(self.ProcessClient, (pipeHandle,))
num_connections = num_connections + 1
# Sleep to ensure that any new threads are in the list, and then
# wait for all current threads to finish.
# What is a better way?
Sleep(500)
while self.thread_handles:
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING, 5000)
print("Waiting for %d threads to finish..." % (len(self.thread_handles)))
WaitForMultipleObjects(self.thread_handles, 1, 3000)
# Write another event log record.
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, " after processing %d connections" % (num_connections,))
)
if __name__=='__main__':
win32serviceutil.HandleCommandLine(TestPipeService)