-Start to convert tcpsvcs to a proper NT service. Supports starting and stopping...
[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 bShutDownFlag = FALSE;
38 BOOL bPauseFlag = 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("w"));
86 if (hLogFile == NULL)
87 {
88 TCHAR *temp = NULL;
89
90 _stprintf(temp, _T("Could not open log file: %s"), LogFilePath);
91 MessageBox(NULL, temp, NULL, MB_OK);
92 return;
93 }
94
95 LogEvent(_T("Entering ServiceMain"), 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"), 0, FALSE);
111 SetServiceStatus (hSStat, &hServStatus);
112 LogEvent(_T("Service status set to SERVICE_START_PENDING"), 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"), 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"), 0, FALSE);
127 LogEvent(_T("Leaving ServiceMain"), 0, FALSE);
128 fclose(hLogFile); /* Clean up everything, in general */
129 return;
130
131 }
132
133 VOID WINAPI
134 ServerCtrlHandler(DWORD Control)
135 {
136 switch (Control)
137 {
138 case SERVICE_CONTROL_SHUTDOWN: /* fall through */
139 case SERVICE_CONTROL_STOP:
140 bShutDownFlag = TRUE;
141 UpdateStatus(SERVICE_STOP_PENDING, -1);
142 break;
143 case SERVICE_CONTROL_PAUSE:
144 bPauseFlag = TRUE;
145 break;
146 case SERVICE_CONTROL_CONTINUE:
147 bPauseFlag = FALSE;
148 break;
149 case SERVICE_CONTROL_INTERROGATE:
150 break;
151 default:
152 if (Control > 127 && Control < 256) /* user defined */
153 break;
154 }
155 UpdateStatus(-1, -1); /* increment checkpoint */
156 return;
157 }
158
159
160 void UpdateStatus (int NewStatus, int Check)
161 /* Set a new service status and checkpoint (either specific value or increment) */
162 {
163 if (Check < 0 )
164 hServStatus.dwCheckPoint++;
165 else
166 hServStatus.dwCheckPoint = Check;
167
168 if (NewStatus >= 0)
169 hServStatus.dwCurrentState = NewStatus;
170
171 if (! SetServiceStatus (hSStat, &hServStatus))
172 LogEvent(_T("Cannot set service status"), -1, TRUE);
173
174 return;
175 }
176
177 INT
178 CreateServers()
179 {
180 DWORD dwThreadId[NUM_SERVICES];
181 HANDLE hThread[NUM_SERVICES];
182 WSADATA wsaData;
183 TCHAR temp[512]; // temp for holding LogEvent text
184 INT i;
185 DWORD RetVal;
186
187 if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
188 {
189 _stprintf(temp, _T("WSAStartup() failed : %lu\n"), RetVal);
190 LogEvent(temp, RetVal, TRUE);
191 return -1;
192 }
193
194 UpdateStatus(-1, -1); /* increment checkpoint */
195
196 LogEvent(_T("Creating server Threads\n"), 0, FALSE);
197
198 /* Create MAX_THREADS worker threads. */
199 for( i=0; i<NUM_SERVICES; i++ )
200 {
201 _stprintf(temp, _T("Starting %s server....\n"), Services[i].Name);
202 LogEvent(temp, 0, FALSE);
203
204 hThread[i] = CreateThread(
205 NULL, // default security attributes
206 0, // use default stack size
207 StartServer, // thread function
208 &Services[i], // argument to thread function
209 0, // use default creation flags
210 &dwThreadId[i]); // returns the thread identifier
211
212 /* Check the return value for success. */
213 if (hThread[i] == NULL)
214 {
215 _stprintf(temp, _T("Failed to start %s server....\n"), Services[i].Name);
216 /* don't exit process via LogEvent. We want to exit via the server
217 * which failed to start, which could mean i=0 */
218 LogEvent(temp, 0, TRUE);
219 ExitProcess(i);
220 }
221 }
222
223 LogEvent(_T("setting service status to running\n"), 0, FALSE);
224
225 UpdateStatus(SERVICE_RUNNING, 0);
226
227 /* Wait until all threads have terminated. */
228 WaitForMultipleObjects(NUM_SERVICES, hThread, TRUE, INFINITE);
229
230 /* Close all thread handles upon completion. */
231 for(i=0; i<NUM_SERVICES; i++)
232 {
233 CloseHandle(hThread[i]);
234 }
235 return 0;
236 }
237
238
239 /* LogEvent is similar to the ReportError function used elsewhere
240 For a service, however, we ReportEvent rather than write to standard
241 error. Eventually, this function should go into the utility
242 library. */
243 VOID
244 LogEvent (LPCTSTR UserMessage, DWORD ExitCode, BOOL PrintErrorMsg)
245 {
246 DWORD eMsgLen, ErrNum = GetLastError ();
247 LPTSTR lpvSysMsg;
248 TCHAR MessageBuffer[512];
249
250 if (PrintErrorMsg)
251 {
252 eMsgLen = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
253 FORMAT_MESSAGE_FROM_SYSTEM, NULL,
254 ErrNum, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
255 (LPTSTR)&lpvSysMsg, 0, NULL);
256
257 _stprintf(MessageBuffer, _T("\n%s %s ErrNum = %lu. ExitCode = %lu."),
258 UserMessage, lpvSysMsg, ErrNum, ExitCode);
259 HeapFree(GetProcessHeap (), 0, lpvSysMsg);
260 }
261 else
262 {
263 _stprintf(MessageBuffer, _T("\n%s ExitCode = %lu."),
264 UserMessage, ExitCode);
265 }
266
267 fputs (MessageBuffer, hLogFile);
268
269 if (ExitCode != 0)
270 ExitProcess(ExitCode);
271 else
272 return;
273 }