/*
* win9x.c -- Windows 95/98/ME support for binkd
*
* win9x.c is a part of binkd project
*
* Copyright (C) 2002 Alexander Reznikov, homebrewer@yandex.ru (Fido 2:4600/220)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. See COPYING.
*/
/*
* $Id: win9x.c,v 2.4.2.6 2003/09/12 09:57:40 hbrew Exp $
*
* Revision history:
* $Log: win9x.c,v $
* Revision 2.4.2.6 2003/09/12 09:57:40 hbrew
* Close process and thread handles after CreateProcess()
*
* Revision 2.4.2.5 2003/09/12 09:44:57 hbrew
* Fix old noncritical bug in binkd9x (STD_OUTPUT_HANDLE --> STD_INPUT_HANDLE)
*
* Revision 2.4.2.4 2003/08/28 06:29:51 hbrew
* Update binkd9x for compability with binkd 0.9.6 commandline; put binkd9x.txt from current
*
* Revision 2.4.2.3 2003/07/07 01:01:13 hbrew
* Remove unused parameter 'type' from win9x_service_control()
*
* Revision 2.4.2.2 2003/06/17 15:48:01 stas
* Prevent service operations on incompatible OS (NT and 9x)
*
* Revision 2.4.2.1 2003/06/14 00:47:36 hbrew
* Add NULL to new argv array.
* Fix binkd9x -t(--all) and -u(--all) crashes
*
* Revision 2.4 2003/05/10 00:30:37 hbrew
* binkd9x: -u option now support '--all' service name (uninstall all services).
* Unneeded spaces cleanup.
*
* Revision 2.3 2003/02/28 20:39:08 gul
* Code cleanup:
* change "()" to "(void)" in function declarations;
* change C++-style comments to C-style
*
* Revision 2.2 2002/11/13 07:58:19 gul
* Add CVS macros
*
*
*/
#ifdef BINKDW9X
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#include <process.h>
#include <io.h>
#include "../Config.h"
#include "../tools.h"
#include "win9x.h"
#if !defined(ENDSESSION_LOGOFF)
#define ENDSESSION_LOGOFF 0x80000000
#endif
PHANDLER_ROUTINE phandler;
HWND mainHWND = NULL;
WNDCLASS wc;
int s_console = 0;
FILE stdout_old, stderr_old;
enum serviceflags{
w32_noservice=0,
w32_installservice=1,
w32_uninstallservice=-1,
w32_startservice=2,
w32_stopservice=-2,
w32_restartservice=3,
w32_queryservice=4,
w32_run_as_service=-4
};
enum serviceflags w9x_service = w32_noservice;
int w9x_service_reg = 0;
int s_quiet = 0;
char *srvname = NULL;
DWORD SigType = -1;
char *srvparm = NULL;
const char *Win9xWindowClassName = "binkdWin9xHandler";
const char *Win9xRegServ = "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices";
const char *Win9xServPrefix = "binkd9x-service";
const char *Win9xStartService = "--service";
#define WM_BINKD9XCOMMAND WM_USER+50
extern int checkcfg_flag;
extern int quiet_flag;
int binkd_main(int argc, char **argv, char **envp);
int win9x_service_cmdline(int argc, char **argv, char **envp);
void win9x_service_un_install(int argc, char **argv, char **envp);
void win9x_service_control(void);
int win9x_check_name_all(void);
int W32_CheckOS(unsigned long PlatformId); /* see TCPErr.c */
/* win9x service support :) :( */
typedef DWORD (WINAPI* RSPType)(DWORD, DWORD);
RSPType RegisterServiceProcess;
#if 0
DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, /* process identifier */
DWORD dwServiceType); /* type of service */
#endif
#if !defined(RSP_SIMPLE_SERVICE)
#define RSP_SIMPLE_SERVICE 0x00000001
/* Registers the process as a simple service process. */
#endif
#if !defined(RSP_UNREGISTER_SERVICE)
#define RSP_UNREGISTER_SERVICE 0x00000000
/* Unregisters the process as a service process. */
#endif
typedef struct _binkd_win9x_srvlst binkd_win9x_srvlst;
struct _binkd_win9x_srvlst
{
int count;
char **names;
};
int win9xExec(char *cmdline)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
BOOL rc;
memset(&si, 0, sizeof(si));
si.cb=sizeof(si);
if ((rc = CreateProcess(NULL, cmdline, NULL, NULL, 0, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &si, &pi)))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return rc;
}
void win9xAtExit(void)
{
if (srvname)
{
free(srvname);
srvname = NULL;
}
FreeTempConsole();
if (checkcfg_flag==2)
win9xExec(GetCommandLine());
}
void win9x_extend_service_name(void)
{
if (w9x_service != w32_noservice || srvname)
{
if (!srvname)
srvname = strdup(Win9xServPrefix);
else if (!win9x_check_name_all())
{
char *tmp;
int len_sn = strlen(srvname);
int len_pr = strlen(Win9xServPrefix);
if ((strncmp(srvname, Win9xServPrefix, len_pr)==0) &&
((len_sn == len_pr)||(len_sn>(len_pr+1) && srvname[len_pr] == '-')))
return;
tmp = (char *)malloc(len_sn+len_pr+2);
memcpy(tmp, Win9xServPrefix, len_pr);
tmp[len_pr] = '-';
memcpy(tmp+len_pr+1, srvname, len_sn);
tmp[len_pr+len_sn+1] = 0;
free(srvname);
srvname = tmp;
}
}
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
int r;
char *sp, *tmp = NULL;
HINSTANCE hl;
atexit(win9xAtExit);
r = win9x_service_cmdline(__argc, __argv, environ);
if (w9x_service != w32_installservice && srvparm)
{
free(srvparm); srvparm = NULL;
}
if (!r)
return 1;
if (w9x_service == w32_noservice)
return binkd_main(__argc, __argv, environ);
else
win9x_extend_service_name();
if( W32_CheckOS(VER_PLATFORM_WIN32_WINDOWS) )
{
if (!s_quiet) AllocTempConsole();
Log((s_quiet?0:-1), "Can't operate witn Windows 9x services: incompatible OS type%s", s_quiet?"":"\n");
return 1;
}
if ((w9x_service == w32_installservice)||(w9x_service == w32_uninstallservice))
{
win9x_service_un_install(__argc, __argv, environ);
if (srvparm)
{
free(srvparm); srvparm = NULL;
}
return 0;
}
if (w9x_service == w32_startservice ||
w9x_service == w32_stopservice ||
w9x_service == w32_restartservice ||
w9x_service == w32_queryservice)
{
win9x_service_control();
return 0;
}
/* Running as Win9x service (r == 1) */
for (sp = __argv[0]+strlen(__argv[0])-1;sp>__argv[0];sp--)
if ((sp[0] == '\\')||(sp[0] == '/'))
{
sp--;
if (sp>__argv[0])
{
tmp = (char *)malloc(sp-__argv[0]+2);
memcpy(tmp, __argv[0], sp-__argv[0]+1);
tmp[sp-__argv[0]+1] = 0;
}
break;
}
if (tmp)
{
SetCurrentDirectory(tmp);
free(tmp);
}
hl = LoadLibrary("KERNEL32.DLL");
if (hl != NULL)
{
RegisterServiceProcess = (RSPType)GetProcAddress(hl, "RegisterServiceProcess");
if (RegisterServiceProcess!=NULL)
if (RegisterServiceProcess(0, RSP_SIMPLE_SERVICE))
w9x_service_reg = 1;
FreeLibrary(hl);
}
quiet_flag = 1;
return binkd_main(__argc, __argv, environ);
}
LRESULT CALLBACK MainWin9xWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CLOSE:
SigType = CTRL_CLOSE_EVENT;
DestroyWindow(hWnd);
break;
case WM_ENDSESSION:
if (wParam)
{
if (lParam & ENDSESSION_LOGOFF)
{
if (!(w9x_service && w9x_service_reg))
{
SigType = CTRL_LOGOFF_EVENT;
DestroyWindow(hWnd);
}
}
else
{
SigType = CTRL_SHUTDOWN_EVENT;
DestroyWindow(hWnd);
}
}
break;
case WM_BINKD9XCOMMAND:
SigType = wParam;
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
static void Win9xWindowThread(void *p)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
MSG msg;
phandler = (PHANDLER_ROUTINE)p;
memset(&wc, 0, sizeof(WNDCLASS));
wc.lpfnWndProc = (WNDPROC)MainWin9xWndProc;
wc.hInstance = hInstance;
wc.lpszClassName = (w9x_service==0)?Win9xWindowClassName:srvname;
RegisterClass(&wc);
mainHWND= CreateWindow(wc.lpszClassName, wc.lpszClassName, 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
mainHWND = NULL;
win9xAtExit();
if (SigType!=-1)
phandler(SigType);
}
void CreateWin9xThread(PHANDLER_ROUTINE phandler)
{
_beginthread(Win9xWindowThread, 0, phandler);
}
/*
void DestroyWin9xThread()
{
if (mainHWND)
SendMessage(mainHWND, WM_DESTROY, 0, 0);
win9xAtExit();
}
*/
/* Win9x service */
int win9x_checkservice(char *srvname)
{
HKEY hk=0;
int r=0;
if (RegOpenKey(HKEY_LOCAL_MACHINE, Win9xRegServ, &hk)!=ERROR_SUCCESS)
return 0;
if (RegQueryValueEx(hk, srvname, NULL, NULL, NULL, NULL)==ERROR_SUCCESS)
r=1;
RegCloseKey(hk);
return r;
}
int win9x_service_cmdline(int argc, char **argv, char **envp)
{
int i, j, skip, r, len, opt;
char *p, *s, *t;
if (argc == 1)
return 1;
len = 1;
for(i=1; i<argc; i++)
len += strlen(argv[i])+1;
srvparm = t = (char *)malloc(len);
for(i=1; i<argc; i++)
{
j = i;
p = argv[i];
opt = 0;
if (p[0] == '-')
{
if (!strcmp(p, Win9xStartService))
{
w9x_service = w32_run_as_service;
continue;
}
skip = 0;
for(++p;*p && !skip;p++)
{
r = 0;
s = NULL;
switch(*p)
{
case 'q':
s_quiet = 1;
break;
case 'i':
w9x_service = w32_installservice;
r = 1;
break;
case 'u':
w9x_service = w32_uninstallservice;
r = 1;
break;
case 'S':
if (*(p+1))
s = p+1;
else
{
if (++i < argc)
s = argv[i];
else
{
Log(0, "Parameter required after '-S' option (service name)\n");
return 0;
}
}
if (srvname)
free(srvname);
srvname = strdup(s);
skip = 1;
break;
case 't':
if (*(p+1))
s = p+1;
else
{
if (++i < argc)
s = argv[i];
else
{
Log(0, "Parameter required after '-t' option (service command)\n");
return 0;
}
}
if (!strcmp(s, "start")) { w9x_service = w32_startservice; }
else
if (!strcmp(s, "stop")) { w9x_service = w32_stopservice; }
else
if (!strcmp(s, "restart")) { w9x_service = w32_restartservice; }
else
if (!strcmp(s, "status")) { w9x_service = w32_queryservice; }
else
{
Log(0, "Unknown command '-t %s'\n", s);
return 0;
}
skip = 1;
r = 1;
break;
}
if (!r)
{
if (!opt)
{
opt = 1;
*t++ = '-';
}
*t++ = *p;
if (s)
{
if (i != j) { *t++ = '\0'; }
for (; *s; s++) { *t++ = *s; }
}
}
}
if (opt)
*t++ = '\0';
}
else
{
for (; *p; p++) { *t++ = *p; }
*t++ = '\0';
}
}
*t = '\0';
return 1;
}
int win9x_service_start(char *name)
{
HKEY hk;
int k;
char *cmdline;
DWORD size, reg_type;
if (RegOpenKey(HKEY_LOCAL_MACHINE, Win9xRegServ, &hk)!=ERROR_SUCCESS)
return 0;
size = 0;
k = RegQueryValueEx(hk, name, NULL, ®_type, NULL, &size) == ERROR_SUCCESS;
k = k&&(reg_type == REG_SZ);
if(k)
{
cmdline = (char *)malloc(size);
k = RegQueryValueEx(hk, name, NULL, ®_type, cmdline, &size) == ERROR_SUCCESS;
if(k) k = win9xExec(cmdline);
free(cmdline);
}
RegCloseKey(hk);
return k!=0;
}
int win9x_check_name_all(void)
{
if (!srvname) return 0;
return stricmp(srvname, "all")? 0: 1;
}
HWND win9x_service_find(char *name)
{
return FindWindow(name, NULL);
}
void win9x_service_control_exec(char *tmp, enum serviceflags cmd)
{
HWND hwnd;
hwnd = win9x_service_find(tmp);
switch (cmd)
{
case w32_queryservice:
if (!s_quiet) Log(-1, "\'%s\': %s\n", tmp, hwnd?"started":"stopped");
break;
case w32_startservice:
if (!hwnd)
{
if (win9x_service_start(tmp))
{
if (!s_quiet) Log(-1, "\'%s\': started\n", tmp);
}
else
{
if (!s_quiet) Log(-1, "\'%s\': starting failed!\n", tmp);
}
}
else
{
if (!s_quiet) Log(-1, "\'%s\': already started\n", tmp);
}
break;
case w32_stopservice:
if (hwnd) SendMessage(hwnd, WM_BINKD9XCOMMAND, 254, 0);
if (!s_quiet) Log(-1, "\'%s\': %s\n", tmp, hwnd?"stopped":"already stopped");
break;
case w32_restartservice:
if (hwnd)
{
SendMessage(hwnd, WM_BINKD9XCOMMAND, 255, 0);
if (win9x_service_start(tmp))
{
if (!s_quiet) Log(-1, "\'%s\': restarted\n", tmp);
}
else
{
if (!s_quiet) Log(-1, "\'%s\': restarting failed!\n", tmp);
}
}
else
if (!s_quiet) Log(-1, "\'%s\': already stopped\n", tmp);
break;
default: /* Avoid gcc warnings about non-handled enumeration values */
break;
}
}
static int win9x_compare_strings(const void *a, const void *b)
{
return strcmp(*(char **)a, *(char **)b);
}
binkd_win9x_srvlst *win9x_get_services_list(int sort)
{
binkd_win9x_srvlst *srvlst;
HKEY hk;
char *tmp;
int i, len, prefixlen;
DWORD size, reg_type;
srvlst = (binkd_win9x_srvlst *)malloc(sizeof(binkd_win9x_srvlst));
srvlst->count = 0;
srvlst->names = NULL;
if (RegOpenKey(HKEY_LOCAL_MACHINE, Win9xRegServ, &hk)==ERROR_SUCCESS)
{
tmp = (char *)malloc(256);
prefixlen = strlen(Win9xServPrefix);
for (i=0, size = 256; RegEnumValue(hk, i, tmp, &size, NULL, ®_type, NULL, NULL)==ERROR_SUCCESS; i++, size = 256)
{
if (reg_type != REG_SZ) continue;
len = strlen(tmp);
if ((len<prefixlen)||(strncmp(tmp, Win9xServPrefix, prefixlen)!=0)||((len>prefixlen)&&(tmp[prefixlen]!='-')))
continue;
srvlst->names = (char **)realloc(srvlst->names, (srvlst->count+1)*sizeof(char *));
srvlst->names[srvlst->count] = strdup(tmp);
srvlst->count++;
}
free(tmp);
RegCloseKey(hk);
if (sort && srvlst->count)
qsort(srvlst->names, srvlst->count, sizeof(char **), win9x_compare_strings);
}
return srvlst;
}
void win9x_free_services_list(binkd_win9x_srvlst *srvlst)
{
int i;
if (srvlst == NULL)
return;
for(i=0; i<srvlst->count; i++)
if (srvlst->names[i])
free(srvlst->names[i]);
if (srvlst->names)
free(srvlst->names);
free(srvlst);
}
void win9x_service_control(void)
{
int i;
char *msg;
binkd_win9x_srvlst *srvlst;
if (!s_quiet) AllocTempConsole();
switch (w9x_service)
{
case w32_queryservice:
msg = "Status of binkd9x service(s):\n";
break;
case w32_startservice:
msg = "Starting service(s):\n";
break;
case w32_stopservice:
msg = "Stopping service(s):\n";
break;
case w32_restartservice:
msg = "Restarting service(s):\n";
break;
default:
msg = NULL;
break;
}
if (!quiet_flag && msg) Log(-1, msg);
if (win9x_check_name_all())
{
srvlst = win9x_get_services_list(1);
for(i=0; i<srvlst->count; i++)
win9x_service_control_exec(srvlst->names[i], w9x_service);
win9x_free_services_list(srvlst);
}
else
win9x_service_control_exec(srvname, w9x_service);
}
void win9x_service_do_uninstall(char *srvname)
{
HKEY hk;
if (RegOpenKey(HKEY_LOCAL_MACHINE, Win9xRegServ, &hk)==ERROR_SUCCESS)
{
RegDeleteValue(hk, srvname);
RegCloseKey(hk);
}
if (!s_quiet) Log(-1, "\'%s\' uninstalled...\n", srvname);
win9x_service_control_exec(srvname, w32_stopservice);
}
void win9x_service_un_install(int argc, char **argv, char **envp)
{
int i, j, k, all, q, q1, tmplen1, tmplen2, len = 0;
char *sp, *tmp, *path, *p;
HKEY hk=0;
binkd_win9x_srvlst *srvlst;
if (!s_quiet) AllocTempConsole();
all = win9x_check_name_all();
if (all&&(w9x_service != w32_uninstallservice))
{
Log((s_quiet?0:-1), "Invalid service name!%s", s_quiet?"":"\n");
return;
}
if (!all)
{
j = win9x_checkservice(srvname);
if (w9x_service == w32_uninstallservice)
j = !j;
if (j)
{
if (!s_quiet) Log(-1, "Service already %sinstalled...\n", w9x_service==w32_installservice?"":"UN");
return;
}
}
if (w9x_service == w32_uninstallservice)
{
if (all)
{
srvlst = win9x_get_services_list(1);
if (!srvlst->count)
Log(-1, "No installed services.\n");
else
for(i=0; i<srvlst->count; i++)
win9x_service_do_uninstall(srvlst->names[i]);
win9x_free_services_list(srvlst);
}
else
win9x_service_do_uninstall(srvname);
return;
}
/* service_flag == w32_installservice */
k = 1;
if (RegOpenKey(HKEY_LOCAL_MACHINE, Win9xRegServ, &hk)!=ERROR_SUCCESS)
if (RegCreateKey(HKEY_LOCAL_MACHINE, Win9xRegServ, &hk)!=ERROR_SUCCESS)
k = 0;
if (k)
{
j = GetCurrentDirectory(0, NULL);
p = (char *)malloc(j);
GetCurrentDirectory(j, p);
sp = argv[0]+strlen(argv[0])-1;
for (;sp>argv[0];sp--)
if ((sp[0] == '\\')||(sp[0] == '/'))
{
sp++;
break;
}
q = 0;
if (strchr(p, ' ')) { q = 1; }
else
if (strchr(sp, ' ')) { q = 1; }
tmplen1 = strlen(sp);
tmplen2 = strlen(Win9xStartService);
len = j+tmplen1+tmplen2+2; /* {path(without '\0' (j-1))}+'\\'+{filename{argv[0]}}+' '+{--service}+...+'\0' */
if (srvparm && *srvparm)
{
for (tmp = srvparm; *tmp; tmp++)
{
len++;
for(q1 = 0; *tmp; tmp++)
{
len++;
if (!q1 && *tmp == ' ') { q1 = 1; len += 2; }
}
}
}
path = tmp = (char *)malloc(len);
if (q) { *tmp++ = '"'; }
memcpy(tmp, p, j-1); tmp += j-1;
free(p);
*tmp++ = '\\';
memcpy(tmp, sp, tmplen1); tmp += tmplen1;
if (q) { *tmp++ = '"'; }
*tmp++ = ' ';
memcpy(tmp, Win9xStartService, tmplen2); tmp += tmplen2;
if (srvparm && *srvparm)
{
for(sp = srvparm; *sp; sp++)
{
*tmp++ = ' ';
if (strchr(sp, ' ')) { *tmp++ = '"'; q = 1; } else { q = 0; }
for(;*sp;sp++) { *tmp++ = *sp; }
if (q) { *tmp++ = '"'; }
}
}
*tmp = '\0';
k = RegSetValueEx(hk, srvname, 0, REG_SZ, path, strlen(path)) == ERROR_SUCCESS;
free(path);
RegCloseKey(hk);
}
if (!k)
Log((s_quiet?0:-1), "Unable to store data in registry...%s", s_quiet?"":"\n");
else
{
if (win9x_service_start(srvname))
{
if (!s_quiet) Log(-1, "\'%s\' installed and started...\n", srvname);
}
else
{
if (!s_quiet)
{
Log(-1, "\'%s\' installed...\n", srvname);
Log(-1, "Unable to start service!\n");
}
}
}
return;
}
/* TempConsole */
void AllocTempConsole(void)
{
if (s_console)
return;
if (AllocConsole())
{
HANDLE ha = GetStdHandle(STD_OUTPUT_HANDLE);
int hCrt = _open_osfhandle((long) ha, 0x4000);
FILE *hf = _fdopen( hCrt, "w" );
stdout_old = *stdout;
*stdout = *hf;
ha = GetStdHandle(STD_ERROR_HANDLE);
hCrt = _open_osfhandle((long) ha, 0x4000);
hf = _fdopen( hCrt, "w" );
stderr_old = *stderr;
*stderr = *hf;
setvbuf( stdout, NULL, _IONBF, 0 );
setvbuf( stderr, NULL, _IONBF, 0 );
s_console = 1;
}
}
void FreeTempConsole(void)
{
HANDLE in;
int hCrt;
FILE stdin_old, *hf;
if (!s_console)
return;
printf("Press any key...\n");
in = GetStdHandle(STD_INPUT_HANDLE);
FlushConsoleInputBuffer(in);
hCrt = _open_osfhandle((long) in, 0x4000);
hf = _fdopen( hCrt, "r" );
stdin_old = *stdin;
*stdin = *hf;
setvbuf(stdin, NULL, _IONBF, 0 );
getch();
FlushConsoleInputBuffer(in);
*stdin = stdin_old;
*stdout = stdout_old;
*stderr = stderr_old;
FreeConsole();
s_console = 0;
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1