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