188fbfa5a68d27c5f73fcc54a88c4a3dd34c928d
[reactos.git] / reactos / services / tcpsvcs / tcpsvcs.c
1 /*
2 * ReactOS Services
3 * Copyright (C) 2005 ReactOS Team
4 *
5 * LICENCE: GPL - See COPYING in the top level directory
6 * PROJECT: ReactOS simple TCP/IP services
7 * FILE: apps/utils/net/tcpsvcs/tcpsvcs.c
8 * PURPOSE: Provide CharGen, Daytime, Discard, Echo, and Qotd services
9 * PROGRAMMERS: Ged Murphy (gedmurphy@gmail.com)
10 * REVISIONS:
11 * GM 04/10/05 Created
12 *
13 */
14 /*
15 * TODO:
16 * - Start tcpsvcs as a service.
17 * - write debugging function and print all dbg info via that.
18 * - change 'temp' to something meaningfull
19 */
20
21
22 #include "tcpsvcs.h"
23
24 //#define NDEBUG
25 //#include <debug.h>
26
27
28 /*
29 * globals
30 */
31 VOID WINAPI ServiceMain(DWORD argc, LPTSTR argv[]);
32
33 static SERVICE_STATUS hServStatus;
34 static SERVICE_STATUS_HANDLE hSStat;
35
36 FILE *hLogFile;
37 BOOL bShutDown = FALSE;
38 BOOL bPause = FALSE;
39
40 LPCTSTR LogFileName = "\\tcpsvcs_log.log";
41 LPTSTR ServiceName = _T("Simp Tcp");
42 //LPTSTR DisplayName = _T("Simple TCP/IP Services");
43
44 static SERVICES
45 Services[NUM_SERVICES] =
46 {
47 {ECHO_PORT, _T("Echo"), EchoHandler},
48 {DISCARD_PORT, _T("Discard"), DiscardHandler},
49 {DAYTIME_PORT, _T("Daytime"), DaytimeHandler},
50 {QOTD_PORT, _T("QOTD"), QotdHandler},
51 {CHARGEN_PORT, _T("Chargen"), ChargenHandler}
52 };
53
54
55 int
56 main(int argc, char *argv[])
57 {
58 SERVICE_TABLE_ENTRY ServiceTable[] =
59 {
60 {ServiceName, ServiceMain},
61 {NULL, NULL}
62 };
63
64 //DPRINT("Starting tcpsvcs service. See \system32%s for logs\n", LogFileName);
65
66 if (! StartServiceCtrlDispatcher(ServiceTable))
67 LogEvent(_T("failed to start the service control dispatcher\n"), -1, TRUE);
68
69 //DPRINT("Shutdown tcpsvcs service\n");
70
71 return 0;
72 }
73
74
75 VOID WINAPI
76 ServiceMain(DWORD argc, LPTSTR argv[])
77 {
78 TCHAR LogFilePath[MAX_PATH];
79
80 if(! GetSystemDirectory(LogFilePath, MAX_PATH))
81 return;
82
83 _tcscat(LogFilePath, LogFileName);
84
85 hLogFile = fopen(LogFilePath, _T("a+"));
86 if (hLogFile == NULL)
87 {
88 TCHAR buf[50];
89
90 _stprintf(buf, _T("Could not open log file: %s\n"), LogFilePath);
91 MessageBox(NULL, buf, NULL, MB_OK);
92 return;
93 }
94
95
96 LogEvent(_T("Entering ServiceMain\n"), 0, FALSE);
97
98 hServStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
99 hServStatus.dwCurrentState = SERVICE_START_PENDING;
100 hServStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
101 SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
102 hServStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
103 hServStatus.dwServiceSpecificExitCode = NO_ERROR;
104 hServStatus.dwCheckPoint = 0;
105 hServStatus.dwWaitHint = 2*CS_TIMEOUT;
106
107 hSStat = RegisterServiceCtrlHandler(ServiceName, ServerCtrlHandler);
108 if (hSStat == 0)
109 LogEvent(_T("Failed to register service\n"), -1, TRUE);
110
111 LogEvent(_T("Control handler registered successfully\n"), 0, FALSE);
112 SetServiceStatus (hSStat, &hServStatus);
113 LogEvent(_T("Service status set to SERVICE_START_PENDING\n"), 0, FALSE);
114
115 if (CreateServers() != 0)
116 {
117 hServStatus.dwCurrentState = SERVICE_STOPPED;
118 hServStatus.dwServiceSpecificExitCode = 1;
119 SetServiceStatus(hSStat, &hServStatus);
120 return;
121 }
122
123 LogEvent(_T("Service threads shut down. Set SERVICE_STOPPED status\n"), 0, FALSE);
124 /* We will only return here when the ServiceSpecific function
125 completes, indicating system shutdown. */
126 UpdateStatus (SERVICE_STOPPED, 0);
127 LogEvent(_T("Service status set to SERVICE_STOPPED\n"), 0, FALSE);
128 LogEvent(_T("Leaving ServiceMain\n"), 0, FALSE);
129
130 fclose(hLogFile);
131
132 return;
133
134 }
135
136 VOID WINAPI
137 ServerCtrlHandler(DWORD Control)
138 {
139 TCHAR buf[256];
140
141 switch (Control)
142 {
143 case SERVICE_CONTROL_SHUTDOWN: /* fall through */
144 case SERVICE_CONTROL_STOP:
145 LogEvent(_T("stopping service\n"), 0, FALSE);
146 InterlockedExchange((LONG *)&bShutDown, TRUE);
147 UpdateStatus(SERVICE_STOP_PENDING, -1);
148 break;
149 case SERVICE_CONTROL_PAUSE: /* not yet implemented */
150 LogEvent(_T("pausing service\n"), 0, FALSE);
151 InterlockedExchange((LONG *)&bPause, TRUE);
152 break;
153 case SERVICE_CONTROL_CONTINUE:
154 LogEvent(_T("continuing service\n"), 0, FALSE);
155 InterlockedExchange((LONG *)&bPause, FALSE);
156 break;
157 case SERVICE_CONTROL_INTERROGATE:
158 break;
159 default:
160 if (Control > 127 && Control < 256) /* user defined */
161 break;
162 }
163 UpdateStatus(-1, -1); /* increment checkpoint */
164 return;
165 }
166
167
168 void UpdateStatus (int NewStatus, int Check)
169 /* Set a new service status and checkpoint (either specific value or increment) */
170 {
171 if (Check < 0 )
172 hServStatus.dwCheckPoint++;
173 else
174 hServStatus.dwCheckPoint = Check;
175
176 if (NewStatus >= 0)
177 hServStatus.dwCurrentState = NewStatus;
178
179 if (! SetServiceStatus (hSStat, &hServStatus))
180 LogEvent(_T("Cannot set service status\n"), -1, TRUE);
181
182 return;
183 }
184
185 INT
186 CreateServers()
187 {
188 DWORD dwThreadId[NUM_SERVICES];
189 HANDLE hThread[NUM_SERVICES];
190 WSADATA wsaData;
191 TCHAR buf[256];
192 INT i;
193 DWORD RetVal;
194
195 if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
196 {
197 _stprintf(buf, _T("WSAStartup() failed : %lu\n"), RetVal);
198 LogEvent(buf, RetVal, TRUE);
199 return -1;
200 }
201
202 UpdateStatus(-1, -1); /* increment checkpoint */
203
204 LogEvent(_T("Creating server Threads\n"), 0, FALSE);
205
206 /* Create MAX_THREADS worker threads. */
207 for( i=0; i<NUM_SERVICES; i++ )
208 {
209 _stprintf(buf, _T("Starting %s server....\n"), Services[i].Name);
210 LogEvent(buf, 0, FALSE);
211
212 hThread[i] = CreateThread(
213 NULL, // default security attributes
214 0, // use default stack size
215 StartServer, // thread function
216 &Services[i], // argument to thread function
217 0, // use default creation flags
218 &dwThreadId[i]); // returns the thread identifier
219
220 /* Check the return value for success. */
221 if (hThread[i] == NULL)
222 {
223 _stprintf(buf, _T("Failed to start %s server....\n"), Services[i].Name);
224 /* don't exit process via LogEvent. We want to exit via the server
225 * which failed to start, which could mean i=0 */
226 LogEvent(buf, 0, TRUE);
227 ExitProcess(i);
228 }
229 }
230
231 LogEvent(_T("setting service status to running\n"), 0, FALSE);
232
233 UpdateStatus(SERVICE_RUNNING, 0);
234
235 /* Wait until all threads have terminated. */
236 WaitForMultipleObjects(NUM_SERVICES, hThread, TRUE, INFINITE);
237
238 /* Close all thread handles upon completion. */
239 for(i=0; i<NUM_SERVICES; i++)
240 {
241 CloseHandle(hThread[i]);
242 }
243
244 LogEvent(_T("Detaching Winsock2...\n"), 0, FALSE);
245 WSACleanup();
246
247 return 0;
248 }
249
250
251 /* LogEvent is similar to the ReportError function used elsewhere
252 For a service, however, we ReportEvent rather than write to standard
253 error. Eventually, this function should go into the utility
254 library. */
255 VOID
256 LogEvent (LPCTSTR UserMessage, DWORD ExitCode, BOOL PrintErrorMsg)
257 {
258 DWORD eMsgLen, ErrNum = GetLastError ();
259 LPTSTR lpvSysMsg;
260 TCHAR MessageBuffer[512];
261
262
263
264 if (PrintErrorMsg)
265 {
266 eMsgLen = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
267 FORMAT_MESSAGE_FROM_SYSTEM, NULL,
268 ErrNum, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
269 (LPTSTR)&lpvSysMsg, 0, NULL);
270
271 _stprintf(MessageBuffer, _T("%s %s ErrNum = %lu. ExitCode = %lu."),
272 UserMessage, lpvSysMsg, ErrNum, ExitCode);
273 HeapFree(GetProcessHeap (), 0, lpvSysMsg);
274 }
275 else
276 {
277 _stprintf(MessageBuffer, _T("%s"), UserMessage);
278 }
279
280 fputs (MessageBuffer, hLogFile);
281
282 if (ExitCode != 0)
283 ExitProcess(ExitCode);
284 else
285 return;
286 }