Mail Archives: cygwin/2005/02/28/09:53:20
Hello list.
Here is an example of a very simple Windows service.
This code works fine if
a) You start/stop from the Windows Service Control Manager. Always a
telnet from another machine to port 1984 on the app-machine suceeds and
writes back data.
b) You reboot the machine and _don't_ log in. If you simply leave the
machine at the login prompt, this particular Windows Service, will
always behave as expected.
However, if you do start the login process and poll programatically/by
hand the service to return data, you should notice as I have that
roughly 1-2 seconds before the full login, to the Windows desktop, that
the process stops accepting connections, but, is still listed as a
running process.
At this point a "netstat -an" will show BugTester in TIME_WAIT for port
1984, however, if left long enough, it will disappear (from the netstat
-an list, not from the process list).
I've included the strace output of an instance of BugTester which
mysteriously disappeared from netstat.
In fact I've just run two straces on this test process, the first strace
is before the BugTester application disappears from a netstat -an and
quite simply the process seems to be doing nothing, the second strace is
attached in part in this email, and shows what the process is doing
_after_ it disappears from netstat -an.
ie, it looks as if
a: BugTester simply _isn't_ doing it's select once the full windows
login has occured, despite being listed in netstat
b: As soon as BugTester disappears from the netstat listing that the
select stuff is running, but, is not picking up incoming connections.
c: However, critically, if a login to a Windows desktop doesn't occur
(at least on my machine), BugTester seems to _always_ work.
The host operating system is Windows XP SP1.
Cygwin dll version : 1005.12.0.0
gcc 3.3.3 (cygwin special)
I'm not subscribed to the list, so if anybody can crack this/answers,
I'd appreciate a cc.
Thanks
--
Bryan
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <windows.h>
#include <winsvc.h>
#include <assert.h>
#define REG_PATH "Software\\Bugtestpath\\BugTest\\MyPath"
#define PATH_KEY "PathVal"
sem_t aSem;
//// Global /////////////////////////////////////////////////////////
FILE* pLog;
char* ServiceName = "BugTest"; // Name of the service
HANDLE terminateEvent = NULL; // Event used to hold
ServerMain from completing
// Handle used to
communicate status info with
// the SCM. Created by
RegisterService3Handler
HANDLE threadHandle = 0; // Thread for the actual work
BOOL pauseService = FALSE; // Flags holding current
state of service
BOOL runningService = FALSE; //
SERVICE_STATUS_HANDLE serviceStatusHandle; //
DWORD WINAPI ServiceThread( LPDWORD lParam);
BOOL SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode,
DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
void StopService();
void ServiceCtrlHandler(DWORD controlCode);
void ServiceMain(DWORD argc, LPTSTR *argv);
void ErrorHandler(char *s, int err);
void GetStatus(SC_HANDLE service);
void ShowUsage();
// service config program tasks
bool InstallService();
bool UninstallService();
bool GetConfiguration();
bool ChangeConfig();
// service control program tasks
bool ServiceRun();
bool ServiceControl(char* CONTROL);
static void mainThread(void*arg);
void startTestHttpThread();
void stopTestHttpThread();
int main(int argc, char *argv[])
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ ServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{ NULL, NULL}
};
BOOL success;
if(argc == 2)
{
if (stricmp("-i", argv[1]) == 0)
InstallService();
else if (stricmp("-u", argv[1]) == 0)
UninstallService();
else if (stricmp("-r", argv[1]) == 0)
ServiceRun();
else
ShowUsage();
}
else
{
//register with SCM
success = StartServiceCtrlDispatcher(serviceTable);
if (!success)
ErrorHandler("StartServiceCtrlDispatcher",GetLastError());
}
return 0;
}
/********* Thread stuff *********/
/* Variables */
pthread_t ptHandle;
sem_t semSignal;
/* Functions */
static void mainThread(void*arg){
//printf("%s started id %lu\n",__FUNCTION__, pthread_self());
//printf("Main thread started\n");
int sock_handle=-1;
struct sockaddr_in sin;
sock_handle=socket(AF_INET,SOCK_STREAM,0);
if(sock_handle<0)
goto done;
sin.sin_addr.s_addr=INADDR_ANY;
sin.sin_port=htons(1984);
sin.sin_family=AF_INET;
if(bind(sock_handle,(struct sockaddr*)&sin,sizeof(sin))<0){
fprintf(stderr,"Extreme error, couldn't bind 1984!\n");
goto done;
}
listen(sock_handle,5);
fd_set fd1,fd2;
char naff_buff[128];
struct timeval tm;
while(1){
FD_ZERO(&fd1);
FD_SET(sock_handle,&fd1);
fd2=fd1;
tm.tv_sec=0;
tm.tv_usec=1000; //Check often
int val=select(FD_SETSIZE,&fd2,NULL,NULL,&tm);
if(FD_ISSET(sock_handle,&fd2)){
//deal with request
//printf("Have information pending on socket\n");
struct sockaddr_in sin_in;
int sin_size=sizeof(sin_in);
int sess_sock=accept(sock_handle,(struct
sockaddr*)&sin_in,&sin_size);
int nbytes=read(sess_sock,naff_buff,128);
if(nbytes<=0){
close(sess_sock);
break;
}
char * noddy_string="HTTP 1/0 200 OK\r\nServer:
BugTester \r\nContent-Type: text/html\r\nConnection:
close\r\n\r\n<HTML><HEAD><TITLE>BugTester
working</TITLE></HEAD><BODY>BUGTESTER WORKING !!</BODY></HTML>";
write(sess_sock,noddy_string,strlen(noddy_string));
close(sess_sock);
}
//else check state of semaphore
if(sem_trywait(&semSignal)==0){
//printf("Locked semaphore shutdown condition\n");
goto done;
}
}
done:
if(sock_handle!=-1)
close(sock_handle);
pthread_exit(0);
}
void startTestHttpThread(void){
sem_init(&semSignal,0,0);
if(pthread_create(&ptHandle,NULL,(void*(*)(void*))mainThread,NULL)<0){
ErrorHandler("Unable to start main thread",GetLastError());
}
return;
}
void stopTestHttpThread(void){
sem_post(&semSignal);
pthread_join(ptHandle,NULL);
return;
}
void ServiceMain(DWORD argc, LPTSTR *argv)
{
BOOL success;
//immediately call registration function
serviceStatusHandle = RegisterServiceCtrlHandler(ServiceName,
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
if (!serviceStatusHandle)
{
exit(-1);
return;
}
//notify SCM
success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 1,
5000);
if (!success)
{
exit(-1);
return;
}
//create termination event
terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
if (!terminateEvent)
{
exit(-1);
return;
}
//notify SCM
success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 2,
1000);
if (!success)
{
exit(-1);
return;
}
//start service
DWORD dwType, dwLen;
HKEY hKey;
DWORD disposition, size, type;
char *stringData=0;
// Open our registry key
RegCreateKeyEx(HKEY_LOCAL_MACHINE,REG_PATH, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKey, &disposition);
// Check if the value exists, and how long it is
long result = RegQueryValueEx(hKey, PATH_KEY, 0,&type, NULL, &size);
if (result != ERROR_SUCCESS)
MessageBox(NULL, "The value named 'PathVal'"
" was not found in the registry",
"Problem", MB_OK);
else
if (type == REG_SZ)
{
stringData = new char[size];
RegQueryValueEx(hKey, PATH_KEY,
0, &type, (BYTE*)stringData, &size);
}
else // If the value exists,
// but isn't a REG_SZ value
MessageBox(NULL,
"The value named 'Message' does not "
"contain string data", "Problem", MB_OK);
// Close the key
RegCloseKey(hKey);
chdir(stringData); /* hack */
if(stringData)
delete[] stringData;
sem_init(&aSem,0,0);
startTestHttpThread();
//notify SCM service is runnning
success = SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);
if (!success)
{
exit(-1);
return;
}
//wait for stop signal and then terminate
sem_wait(&aSem);
exit(0);
}
//stops service by allowing ServiceMain to complete
void StopService()
{
runningService = FALSE;
//set the event that is holding ServiceMain
stopTestHttpThread();
sem_post(&aSem);
}
//this function consolidates the activities of updating
//the service status with SetServiceStatus
BOOL SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD
dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
{
BOOL success;
SERVICE_STATUS serviceStatus;
//fill in all of the SERVICE_STATUS fields
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = dwCurrentState;
//if in the process of something, then accept
//no control events, else accept anything
if (dwCurrentState == SERVICE_START_PENDING)
serviceStatus.dwControlsAccepted = 0;
else
serviceStatus.dwControlsAccepted =
SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_SHUTDOWN;
//if a specific exit code is defines, set up the win32 exit code
properly
if (dwServiceSpecificExitCode == 0)
serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
else
serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
serviceStatus.dwCheckPoint = dwCheckPoint;
serviceStatus.dwWaitHint = dwWaitHint;
success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
if (!success)
StopService();
return success;
}
void ServiceCtrlHandler(DWORD controlCode)
{
DWORD currentState = 0;
BOOL success;
switch(controlCode)
{
// START = ServiceMain()
// STOP
case SERVICE_CONTROL_STOP:
currentState = SERVICE_STOP_PENDING;
//notify SCM
success = SendStatusToSCM(
SERVICE_STOP_PENDING,
NO_ERROR,
0,
1,
5000);
//stop service
StopService();
return;
case SERVICE_CONTROL_SHUTDOWN:
//do nothing
return;
default:
break;
}
//notify SCM current state
SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}
void ErrorHandler(char *s, int err)
{
pLog = fopen("server.log","a");
fprintf(pLog, "%s failed, error code = %d\n",s , err);
fclose(pLog);
exit(-err);
}
void ShowUsage()
{
printf("USAGE:\n");
printf("server -i\tInstall service\n");
printf("server -u\tUninstall service\n");
printf("server status\tCurrent status\n");
}
bool SaveRegistrySettings(){
char zBuf[1024];
DWORD dwLen=1024;
GetCurrentDirectory(dwLen,zBuf);
HKEY hKey;
DWORD disposition;
// Open our registry key
RegCreateKeyEx(HKEY_LOCAL_MACHINE,
REG_PATH, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&hKey, &disposition);
// Set a value
DWORD data = 42;
RegSetValueEx(hKey, PATH_KEY, 0,
REG_SZ, (BYTE *)zBuf, strlen(zBuf));
RegCloseKey(hKey);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Purpose :Install service into SCM.
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool InstallService()
{
SC_HANDLE newService;
SC_HANDLE scm;
char szBuffer[255];
char szPath[MAX_PATH];
//get file path
GetModuleFileName( GetModuleHandle(NULL), szPath, MAX_PATH );
strcpy( szBuffer, "\"" );
strcat( szBuffer, szPath );
strcat( szBuffer, "\"" );
//open connection to SCM
scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (!scm)
ErrorHandler("OpenSCManager", GetLastError());
//install service
newService = CreateService(
scm, //scm database
ServiceName, //service name
ServiceName, //display name
SERVICE_ALL_ACCESS, //access rights to the service
SERVICE_WIN32_OWN_PROCESS, //service type
SERVICE_AUTO_START, //service start type
SERVICE_ERROR_NORMAL, //error control type
szBuffer, //service path
NULL, //no load ordering group
NULL, //no tag identifier
"RPCSS\0\LanmanWorkstation\0", //no
dependencies
NULL, //LocalSystem account
NULL); //no password
if(!newService)
{
ErrorHandler("CreateService", GetLastError());
return false;
}
else
{
//Save chdir path to Registry
if(SaveRegistrySettings()){
printf("Service Installed\n");
ServiceRun();
}
}
//clean up
CloseServiceHandle(newService);
CloseServiceHandle(scm);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Purpose :Uninstall service from SCM.
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool UninstallService()
{
SC_HANDLE service;
SC_HANDLE scm;
BOOL SUCCESS;
SERVICE_STATUS status;
//Open connection to SCM
scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (!scm)
ErrorHandler("OpenSCManager", GetLastError());
//Get service's handle
service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS | DELETE);
if (!service)
ErrorHandler("OpenService", GetLastError());
//Get service status
SUCCESS = QueryServiceStatus(service, &status);
if (!SUCCESS)
ErrorHandler("QueryServiceStatus", GetLastError());
//Stop service if necessary
if (status.dwCurrentState != SERVICE_STOPPED)
{
printf( "Stopping service...\n");
SUCCESS = ControlService(service, SERVICE_CONTROL_STOP, &status);
if (!SUCCESS)
ErrorHandler("ControlService", GetLastError());
Sleep(500);
}
//Delete service
SUCCESS = DeleteService(service);
if (SUCCESS)
printf("Service Uninstalled\n");
else
ErrorHandler("DeleteService", GetLastError());
//Clean up
CloseServiceHandle(service);
CloseServiceHandle(scm);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Purpose :Run service
// Parameter:N/A
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
bool ServiceRun()
{
SC_HANDLE scm, Service;
SERVICE_STATUS ssStatus;
DWORD dwOldCheckPoint;
DWORD dwStartTickCount;
DWORD dwWaitTime;
DWORD dwStatus;
//open connection to SCM
scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!scm)
ErrorHandler("OpenSCManager", GetLastError());
//open service
Service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
if(!Service)
{
ErrorHandler("OpenService", GetLastError());
return false;
}
else
{
//start service
StartService(Service, 0, NULL);
GetStatus(Service);
// Check the status until the service is no longer start pending.
if (!QueryServiceStatus( Service, &ssStatus) )
ErrorHandler("QueryServiceStatus", GetLastError());
// Save the tick count and initial checkpoint.
dwStartTickCount = GetTickCount();
dwOldCheckPoint = ssStatus.dwCheckPoint;
while (ssStatus.dwCurrentState == SERVICE_START_PENDING)
{
// Do not wait longer than the wait hint. A good interval is
// one tenth the wait hint, but no less than 1 second and no
// more than 10 seconds.
dwWaitTime = ssStatus.dwWaitHint / 10;
if( dwWaitTime < 1000 )
dwWaitTime = 1000;
else if ( dwWaitTime > 10000 )
dwWaitTime = 10000;
Sleep( dwWaitTime );
// Check the status again.
if (!QueryServiceStatus(Service, &ssStatus) )
break;
if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
{
// The service is making progress.
dwStartTickCount = GetTickCount();
dwOldCheckPoint = ssStatus.dwCheckPoint;
}
else
{
if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
{
// No progress made within the wait hint
break;
}
}
}
if (ssStatus.dwCurrentState == SERVICE_RUNNING)
{
GetStatus(Service);
dwStatus = NO_ERROR;
}
else
{
printf("Service not started.\n");
printf("Current State: %lu\n",ssStatus.dwCurrentState);
printf("Exit Code: %lu\n",ssStatus.dwWin32ExitCode);
printf("Service Specific Exit Code:
%lu\n",ssStatus.dwServiceSpecificExitCode);
printf("Check Point: %lu\n",ssStatus.dwCheckPoint);
printf("Wait Hint: %lu\n",ssStatus.dwWaitHint);
dwStatus = GetLastError();
}
}
CloseServiceHandle(scm);
CloseServiceHandle(Service);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// Purpose :Get the current status of the service
// Parameter:service handle.
// Returns :N/A
////////////////////////////////////////////////////////////////////////////////
void GetStatus(SC_HANDLE service)
{
BOOL SUCCESS;
SERVICE_STATUS status;
DWORD CurrentState;
SUCCESS = QueryServiceStatus(service, &status);
switch(status.dwCurrentState)
{
case SERVICE_RUNNING:
CurrentState = SERVICE_RUNNING;
printf("Service RUNNING.\n");
break;
case SERVICE_STOPPED:
CurrentState = SERVICE_STOPPED;
printf("Service STOPPED.\n");
break;
case SERVICE_START_PENDING:
CurrentState = SERVICE_START_PENDING;
printf( "Service is starting...\n");
break;
case SERVICE_STOP_PENDING:
CurrentState = SERVICE_STOP_PENDING;
printf( "Service is stopping...\n");
break;
default:
break;
}
SendStatusToSCM(CurrentState, NO_ERROR, 0, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////////////
// Logging functions
6191 1284598550 [unknown (0x4F4)] BugTester 1244
select_stuff::~select_stuff: deleting select records
6264 1284604814 [unknown (0x4F4)] BugTester 1244 cygwin_select: 64,
0xCDEEE8, 0x0, 0x0, 0xCDEEF0
6002 1284610816 [unknown (0x4F4)] BugTester 1244 dtable::select_read:
fd 0
6114 1284616930 [unknown (0x4F4)] BugTester 1244 cygwin_select:
to->tv_sec 0, to->tv_usec 1000, ms 1
6019 1284622949 [unknown (0x4F4)] BugTester 1244 cygwin_select:
sel.always_ready 0
6212 1284629161 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
Handle 0x138
5954 1284635115 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
Added to readfds
6108 1284641223 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
exitsock 0x140
5948 1284647171 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
stuff_start 0xCDEE54
6624 1284653795 [unknown (0x4F4)] BugTester 1244 select_stuff::wait: m
2, ms 1
5560 1284659355 [select_socket] BugTester 1244 thread_socket:
stuff_start 0xA05540C
6234 1284665589 [unknown (0x4F4)] BugTester 1244 select_stuff::wait:
timed out
6379 1284671968 [unknown (0x4F4)] BugTester 1244 select_stuff::wait:
returning 1
5693 1284677661 [unknown (0x4F4)] BugTester 1244
select_stuff::cleanup: calling cleanup routines
6261 1284683922 [unknown (0x4F4)] BugTester 1244 socket_cleanup: si
0xA0523F8 si->thread 0x610F4870
5986 1284689908 [unknown (0x4F4)] BugTester 1244 socket_cleanup: sent
a byte to exitsock 0x140, res 1
6163 1284696071 [select_socket] BugTester 1244 thread_socket: Win32
select returned 1
5909 1284701980 [select_socket] BugTester 1244 thread_socket: s
0xA0523C8, testing fd 0 ()
6178 1284708158 [select_socket] BugTester 1244 thread_socket: saw
exitsock read
6014 1284714172 [unknown (0x4F4)] BugTester 1244 socket_cleanup:
reading a byte from exitsock 0x140
6184 1284720356 [unknown (0x4F4)] BugTester 1244 socket_cleanup: recv
returned 1
5986 1284726342 [unknown (0x4F4)] BugTester 1244 socket_cleanup: returning
6169 1284732511 [unknown (0x4F4)] BugTester 1244 peek_socket:
considering handle 0x138
5915 1284738426 [unknown (0x4F4)] BugTester 1244 peek_socket: adding
read fd_set , fd 0
6179 1284744605 [unknown (0x4F4)] BugTester 1244 peek_socket:
WINSOCK_SELECT returned 0
6120 1284750725 [unknown (0x4F4)] BugTester 1244 select_stuff::poll:
returning 0
6044 1284756769 [unknown (0x4F4)] BugTester 1244
select_stuff::cleanup: calling cleanup routines
6188 1284762957 [unknown (0x4F4)] BugTester 1244
select_stuff::~select_stuff: deleting select records
6062 1284769019 [unknown (0x4F4)] BugTester 1244 cygwin_select: 64,
0xCDEEE8, 0x0, 0x0, 0xCDEEF0
6172 1284775191 [unknown (0x4F4)] BugTester 1244 dtable::select_read:
fd 0
5995 1284781186 [unknown (0x4F4)] BugTester 1244 cygwin_select:
to->tv_sec 0, to->tv_usec 1000, ms 1
6092 1284787278 [unknown (0x4F4)] BugTester 1244 cygwin_select:
sel.always_ready 0
6059 1284793337 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
Handle 0x138
6088 1284799425 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
Added to readfds
5937 1284805362 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
exitsock 0x140
6193 1284811555 [unknown (0x4F4)] BugTester 1244 start_thread_socket:
stuff_start 0xCDEE54
6154 1284817709 [unknown (0x4F4)] BugTester 1244 select_stuff::wait: m
2, ms 1
6095 1284823804 [select_socket] BugTester 1244 thread_socket:
stuff_start 0xA05540C
20378 1284844182 [unknown (0x4F4)] BugTester 1244 select_stuff::wait:
timed out
3345 1284847527 [unknown (0x4F4)] BugTester 1244 select_stuff::wait:
returning 1
6216 1284853743 [unknown (0x4F4)] BugTester 1244
select_stuff::cleanup: calling cleanup routines
5982 1284859725 [unknown (0x4F4)] BugTester 1244 socket_cleanup: si
0xA0523F8 si->thread 0x610F4870
6391 1284866116 [unknown (0x4F4)] BugTester 1244 socket_cleanup: sent
a byte to exitsock 0x140, res 1
^C
--
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple
Problem reports: http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ: http://cygwin.com/faq/
- Raw text -