![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Сервис не ShutDownит сервер с выданными привилегиями. [D6, NT4, Win2k, WinXP]
Kacnep © (2006-08-25 07:45:00)Ув. мастера! Сервис по приходу некой команды из сети должен выполнить *.cmd файл. Вот что например
c:\adm\ss.cmd
shutdown /s /f >c:\adm\log.txt
т.е. выключить сервер, но этого не происходит.
Сам командник запускается! Файл log.txt появляется.
Привелегии необходимые вроде бы выдал - не уверен что в том месте программы.
Сервис запускается как Local System.
Может кто нить что подсказать (сервера 2000 и 2003).
Я рассматривал пример
http://www.delphimaster.ru/cgi-bin/forum.pl?id=1147525423&n=5 Вот куски кода...
....
procedure AdjTokenPrivelegs(mmName: string);
var
gler: DWord;
begin
AdjustPriviliges(mmName);
gler:=GetLastError;
if (glerERROR_SUCCESS) then begin
WriteLog(Format('%s: [FAILED] ', [mmName]));
{$IFDEF _DEV_}
ShowError(gler);
{$ENDIF}
exit;
end;
WriteLog(Format('%s: [OK] ', [mmName]));
end;
procedure Tf_Main.ServiceStart(Sender: TService; var Started: Boolean); //эта штука при старте сервера - тут и выдаю привилегии
var
sl_Tmp:TStringList;
s:string;
i:integer;
begin
Started:=FALSE;
AdjTokenPrivelegs(SE_SHUTDOWN_NAME); //выдаем ЭТУ ТОЧНО ПРИВИЛЕГИЮ!!!
AdjTokenPrivelegs(SE_DEBUG_NAME);
WriteLog('Service is starting... ');
if not StartServer then
WriteLog('Error starting server')
else WriteLog('Server started successfully!');
WriteLog('Service is started.');
Started:=TRUE;
end;
....
procedure Tf_Main.id_SrvCommandHandlers0Command(ASender: TIdCommand); //это по приходу команды из сети использую компонент инди
var
PMSI:TStartupInfo;
PMPI:TProcessInformation;
begin
WriteLog(ASender.Context.Connection.Socket.Binding.PeerIP+' -> Shutdown');
if (FIP.Count>0) and (FIP.IndexOf(ASender.Context.Connection.Socket.Binding.PeerIP)>-1) then begin
WriteLog('Execute Shutdown! '+FScript);
FillChar(PMSI,sizeof(PMSI),0);
PMSI.cb:=sizeof(PMSI);
FillChar(PMPI,sizeof(PMPI),0);
try
try //Win32Check(
if not CreateProcess(nil,PChar(FScript),nil,nil,true,0,{false,normal_priority_class,}ni l,nil,PMSI,PMPI) then RaiseLastWin32Error;
except
WriteLog('Error: '+SysErrorMessage(GetLastError));
end;
finally
CloseHandle(PMPI.hProcess);
CloseHandle(PMPI.hThread);
end;
end;//if
end;
Kacnep (2006-09-01 10:15:00) [1]Проблема до сих пор не снята. :(
Например выполняю команду в скрипте c:\windows\system32\shutdown.exe /? >c:\log.txt
Делаю логофф.
По шедулеру отрабатывает нормально. В лог.тхт хелп есть.
С помощью сервиса - длина файла лог.тхт =0 :( т.е. инфы в нем никакой нет.
Вывод - почему то не работает корректно процедура создания процесса :(
Полностью код всего проекта выложен выше.
Может кто нить сможет помочь? :(
Kacnep (2006-08-29 10:54:00) [2]Установлена :((
НИКАКОЙ щатдаун если незалогинен не работает! :(
НИКАК!
Кажеться BiN © (28.08.06 12:44) [29] прав.
На 2003 надо ставить какой то ХОТФИКС а на 2000?
Но главное выяснил - в процедуре выдачи прав - все верно. Значит приджется искать шатдаунитель нормальный %((.
umbra (2006-08-28 20:10:00) [3]
>
> Если незалогинен то шедулер запускает скрипт но ТОЛЬКО НЕ
> ШАТДАУН!!!
>
Это настройка безопасности. Запустите из командной строкиgpedit.mscи сделайтеEnabledопциюLocal computer policy\Computer Configuration\Security Settings\Local Policies\Security Options\Shutdown: Allow the system to be shut down without having to log on(Вин2003Сервер). По умолчанию запрещено гасить компьютер, если нет интерактивных сессий.
BiN (2006-08-28 12:44:00) [4]
> Если незалогинен то шедулер запускает скрипт но ТОЛЬКО НЕ
> ШАТДАУН!!!
> Все остальное отрабатывает...:(((
Ты по ссылке в BiN © (25.08.06 12:19) [14] ходил?
Kacnep (2006-08-28 12:38:00) [5]2 Ketmar
На всех серверах разные пароли и вшивать в прогу и каждый раз перекомпилить - не выход :(( В отдельном файле держать в незашифрованом виде нельзя :(.
Т.е. ЛОГОН кажеться никак применить тут нельзя... :(
2 BiN
Фантастика
Если я залогинен на сервер
то такой скрипт выполняеться
@echo time /t >c:\adm\start.log
ping 172.31.32.100 >c:\adm\ping.log
@echo time /t >c:\adm\stop.log
shutdown /r /f >c:\adm\s.log
Если незалогинен то шедулер запускает скрипт но ТОЛЬКО НЕ ШАТДАУН!!!
Все остальное отрабатывает...:(((
BiN (2006-08-28 12:18:00) [6]
> Ketmar © (28.08.06 12:05) [26]
Сусанин, блин.
-)
Ketmar (2006-08-28 12:05:00) [7]> [25] BiN © (28.08.06 12:02)
ну не мешай, а? я тут так подло толкаю в сторону дебрей логина, а ты весь кайф ломаешь... %-)
BiN (2006-08-28 12:02:00) [8]
> Kacnep © (28.08.06 11:54) [23]
Попробуй запустить скрипт шедулером. Если не вызывается, то проблема в скрипте или в ошибке системы. В этом случае нужно копать мс-овский технет.
Кстати, думаю, shutdown самостоятельно включает нужные привилегии.
Ketmar (2006-08-28 11:58:00) [9]цитата:
The LogonUser function attempts to perform a user logon operation. You specify the user with a user name and domain, and authenticate the user with a clear-text password. If the function succeeds, you receive a handle to a token that represents the logged-on user. You can then use this token handle to impersonate the specified user, or in most cases, to create a process running in the context of the specified user.
логинься себе под админом и запускай под ним всё, что угодно.
Kacnep (2006-08-28 11:54:00) [10]:((
к сожалению не хочу...
Мне ВАЖНОчтоб ЛЮБАЯпрограмма прописаная в файле "скрипта" нормально выполнялась. И ВОЗМОЖНО! потом шатдаун! Я например на критичных серваках просто остановлю и корректно завершу работу баз. Бэкапов и прочее. Это все я в скрипте пропишу! Вот для чего он нужен. А потом могу и шатдаунить спокойно. НО ЭТОТ ПРОЦЕСС!! будь он неладен :( не хочет ДАЖЕ шатдаун выполнить по всем рекомендациям Мастеров и хелпам инета. :(.
Сейчас работает примитивно - в скрипте запускает тупо утилитку psshutdown - она пробивает любую операционку (на 2000 shutdownа нет :( ).
Но я хочу добить - понятьпочему то, что по всем понятиям ДОЛЖНО работать - не работает :((.
Куча рассматриваемых вопросов в инете как зашатдаунить тачку в НТ. ВСЕ! копия моего (вернее наоборот) у них только после запуска процесса стоит дельфийская команда шатдауна :(. И видимо все работает - почему мой скрипт выполняющий стандартную команду шатдауна НА НЕЗАЛОГИНЕНОМ сервере не выполняется :(.
BiN (2006-08-28 11:41:00) [11]А без вызова shutdown.exe обойтись не хочешь? Например вызвать ExitWindowsEx или InitiateSystemShutdown(Ex)
Kacnep (2006-08-28 11:19:00) [12]ДА! Все работает! И мало того скажем если я в файле ss.cmd (запускаемый сервисом скрипт) например пишу ping 127.0.0.1 >c:\adm\log.txt -в логе нормано вижу что команда пинг отработала. Сервис работает!!! Но когда на тачке никто не залогинен команда shutdown -r не работает! :((. Уже бесит этот кусок. :(
За замечания спасибо. Просто еще в коде много лишней отладочной инфы которая пишется в лог только для проверки.
Вот что например сейчас в ЛОГе
[28.08.2006 12:09:38] Service is stoping...
[28.08.2006 12:09:38] Server stoped [OK]
[28.08.2006 12:09:38] Service stoped [OK]
[28.08.2006 12:09:38] Service uninstall [OK]
[28.08.2006 12:09:53] Service is starting...
[28.08.2006 12:09:53] Server starting...
[28.08.2006 12:09:53] Server started [OK]
[28.08.2006 12:09:53] Service started [OK]
[28.08.2006 12:10:22] 172.31.39.233 -> Shutdown
[28.08.2006 12:10:22] Execute Shutdown! C:\ADM\ss.cmd
[28.08.2006 12:10:22] Create suspend process [OK]
[28.08.2006 12:10:22] SetProcessTokenPrivilege: The operation completed successfully
[28.08.2006 12:10:22] ResumeThread: The operation completed successfully
BiN (2006-08-28 11:12:00) [13]
> Kacnep © (28.08.06 10:57) [19]
А можно узнать, что требуется ещеот сообщества? Код что ли с нуля написать?
PS
GetLastError имеет смысл проверять только при неудачном выполнении функции (это я про вызов SetProcessTokenPrivilege)
PPS
"Простой" процесс, который только и делает, что пишет о себе в лог, создавать пробовал?
Kacnep (2006-08-28 10:57:00) [14]:) Утром добрым... :)
Нет народ - это для облегчения моего гемороя :).
Если я доделаю этот сервис - то его скопировать и запустить на все серваки а их ооочень много :) не составит большого труда. А ЕСЛИ НЕТ... Придется официальный софт от производителся ставить и настраивать на каждой машине... ЭЭтого я вынести не смогу! :(((
А код - просто вставляеться в соответствующие файлы и компилиться -получаем готовый проект. Который уже можно просматривать. Вернее кажеться собака зарыта только в ф-ии создания процесса. Так сказать "код в студию".
Сейчас лазию по инету ищу примеры написания РАБОТАЮЩИХ сервисов.
Но все же основная надежда на Мастеров...
Ketmar (2006-08-28 10:10:00) [15]* Кэтмар у утреннем настроении *
нет, я, конечно, всё понимаю, но ему за это, небось, денег платят, а мы тут так, забесплатно должны кучу кода читать...
Kacnep (2006-08-28 08:23:00) [16]u_Main5.pas
unit u_Main5;
interface
uses
Windows,
Messages,
SysUtils,
Classes,
SvcMgr,
IniFiles,
IdCmdTCPServer,IdCommandHandlers, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer;
type
Tf_Main = class(TService)
id_Srv: TIdCmdTCPServer;
procedure id_SrvCommandHandlers0Command(ASender: TIdCommand);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
procedure ServiceCreate(Sender: TObject);
procedure ServiceDestroy(Sender: TObject);
private
FIP:TStringList;
FScript:string;
function StartServer: Boolean;
function StopServer: Boolean;
{ Private declarations }
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
const
c_Log='Service.log';
c_Ini='settings.ini';
var
f_Main: Tf_Main;
implementation
{$R *.DFM}
uses
ShellAPI;
var
CreateOptScan: LongWord;
Log,Ini:TFileName;
hLog: THandle;
function WriteLog(Status: string): DWord; forward;
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
f_Main.Controller(CtrlCode);
end;
function Tf_Main.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
function SetTokenPrivilege(hToken: THandle; const PrivilegeName:string; NewState: DWORD; PreState: PDWORD=nil): Boolean;
var
LUID : TLargeInteger;
Privilege, PrevPrivilege: TTokenPrivileges;
cbPrevPriv:DWORD;
begin
Result:= False;
if LookupPrivilegeValue (nil,PChar(PrivilegeName),LUID) then begin
Privilege.PrivilegeCount:=1;
Privilege.Privileges[0].Luid:=LUID;
Privilege.Privileges[0].Attributes:=NewState;
cbPrevPriv:=SizeOf(PrevPrivilege);
Result:=AdjustTokenPrivileges(hToken, False, Privilege, cbPrevPriv, Privilege, cbPrevPriv);
if (Result and Assigned(PreState)) then PreState^:=PrevPrivilege.Privileges[0].Attributes;
end;
end;
function SetProcessTokenPrivilege(hProcess:DWORD; const PrivilegeName:string;NewState: DWORD; PreState: PDWORD=nil): Boolean;
var
hToken : THandle;
begin
Result:= False;
if not OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,hToken) then begin
WriteLog('OpenProcessToken [Fail]');
Exit;
end;
Result:=SetTokenPrivilege(hToken, PrivilegeName, NewState, PreState) and (GetLastError=ERROR_SUCCESS);
CloseHandle (hToken);
end;
procedure Tf_Main.id_SrvCommandHandlers0Command(ASender: TIdCommand);
const
PrivelegStr='SeShutdownPrivilege';
var
PMSI:TStartupInfo;
PMPI:TProcessInformation;
env:array[0..100] of char;
begin
WriteLog(ASender.Context.Connection.Socket.Binding.PeerIP+' -> Shutdown');
if (FIP.Count>0) and (FIP.IndexOf(ASender.Context.Connection.Socket.Binding.PeerIP)>-1) then begin
WriteLog('Execute Shutdown! '+FScript);
ZeroMemory(@env,sizeof(env));
ZeroMemory(@PMSI,sizeof(PMSI));
ZeroMemory(@PMPI,sizeof(PMPI));
PMSI.cb:=sizeof(PMSI);
try
try
Win32Check(CreateProcess(nil,PChar(FScript),nil,nil,true,CREATE_SUSPENDED,@env,P Char('C:\'),PMSI,PMPI));
WriteLog('Create suspend process [OK]');
SetProcessTokenPrivilege(PMPI.hProcess,PChar(PrivelegStr),SE_PRIVILEGE_ENABLED);
WriteLog('SetProcessTokenPrivilege: '+SysErrorMessage(GetLastError));
if GetLastError=ERROR_SUCCESS then begin
ResumeThread(PMPI.hThread);
WriteLog('ResumeThread: '+SysErrorMessage(GetLastError));
end;
except
WriteLog('Error: '+SysErrorMessage(GetLastError));
end;
finally
CloseHandle(PMPI.hProcess);
CloseHandle(PMPI.hThread);
end;
end;//if
end;
function Tf_Main.StopServer: Boolean;
begin
id_Srv.Active:=false;
Result:=not id_Srv.Active;
if result then WriteLog('Server stoped [OK]')
else WriteLog('Server stoped [Fail]');
end;
function Tf_Main.StartServer: Boolean;
begin
id_Srv.DefaultPort:=9999;
try
WriteLog('Server starting...');
id_Srv.Active:=true;
result:=id_Srv.Active;
except
on E : Exception do begin
WriteLog('Server start [Fail]');
WriteLog(E.Message);
Result:=false;
end;
end;
end;
procedure Tf_Main.ServiceStart(Sender: TService; var Started: Boolean);
var
sl_Tmp:TStringList;
s:string;
i:integer;
begin
Started:=FALSE;
Ini:=ExtractFilePath(paramstr(0))+c_Ini;
Log:=ExtractFilePath(paramstr(0))+c_Log;
WriteLog('Service is starting... ');
FIP:=TStringList.Create;
sl_Tmp:=TStringList.Create;
try
with TIniFile.Create(Ini) do begin
FScript:=ReadString('Script','File','');
ReadSection('IPs',sl_Tmp);
for i:=0 to sl_Tmp.Count-1 do begin
s:=trim(ReadString('IPs',sl_Tmp.Strings[i],''));
if s<>'' then FIP.Add(s);
end;
Free;
end;// WIth
finally
FreeAndNil(sl_Tmp);
end;
if not StartServer then begin
WriteLog('Server start [Fail]');
Started:=False;
end
else begin
WriteLog('Server started [OK]');
WriteLog('Service started [OK]');
Started:=TRUE;
end;
end;
procedure Tf_Main.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
WriteLog('Service is stoping... ');
if not StopServer then WriteLog('Server stop [Fail]');
FreeAndNil(FIP);
WriteLog('Service stoped [OK]');
Stopped:=TRUE;
end;
procedure Tf_Main.ServiceCreate(Sender: TObject);
begin
if sysutils.Win32Platform=VER_PLATFORM_WIN32_NT then CreateOptScan:=FILE_FLAG_SEQUENTIAL_SCAN
else CreateOptScan:=0;
end;
function WriteLog(Status: string): DWord;
var
xBuf: array[0..$FF - 1] of Char;
begin
if (hLog = INVALID_HANDLE_VALUE) or (hLog = 0) then begin
if FileExists(Log) then
hLog := CreateFile(PChar(Log),
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL or CreateOptScan,
0)
else
hLog := CreateFile(PChar(Log),
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ,
nil,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL or CreateOptScan,
0);
if hLog = INVALID_HANDLE_VALUE then begin
Result := DWord(-1);
exit;
end;
FileSeek(hLog, 0, 2);
end;
FillChar(xBuf, $FF, 0);
Status := Format('[%s] %s'#13#10, [DateTimeTostr(Now), Status]);
move((Pointer(@Status[1]))^, xBuf, Length(Status));
FileWrite(hLog, xBuf, Length(Status));
FlushFileBuffers(hLog);
Result := 0;
end;
procedure Tf_Main.ServiceDestroy(Sender: TObject);
begin
WriteLog('Service uninstall [OK]');
CloseHandle(hLog);
end;
end.
Kacnep (2006-08-28 08:22:00) [17]Пока не получилось вроде применял все рекомендации.
Вот ВСЕ исходники... Может кто поможет...
Прогу компилим
запускаем сначала с параметром /install - устанавливаем как сервис {убрать соответственно параметром /uninstall}
в папке с прогой должен быть инишник
В нем первый параметр - какой скрипт и откуда запускать
Второй параметр с какого ИП разрешено принимать команды.
После СТАРТА сервиса запускаем телнет на порт 9999 и просто посылаем команду shutdown.
Клиент сразу отцепиться а на серваке {2003 или ХР есть команда SHUTDOWN} должен выполниться Ваш скрипт.
Я буду еще искать решение проблемы. Но буду благодарен любому кто укажет где в программе ошибка. Спасибо.
settings.ini
[Script]
File=C:\Adm\ss.cmd
[IPs]
IP1=172.31.34.152
IP2=127.0.0.1
ss.cmd
shutdown -r -f
u_Main5.dfm
object f_Main: Tf_Main
OldCreateOrder = False
OnCreate = ServiceCreate
OnDestroy = ServiceDestroy
AllowPause = False
DisplayName = 'Ups_Listener'
OnStart = ServiceStart
OnStop = ServiceStop
Left = 321
Top = 150
Height = 150
Width = 215
object id_Srv: TIdCmdTCPServer
Bindings = <>
DefaultPort = 9999
TerminateWaitTime = 2000
CommandHandlers = <
item
CmdDelimiter = ' '
Command = 'SHUTDOWN'
Disconnect = True
Name = 'ch_ShutDown'
NormalReply.Code = '200'
ParamDelimiter = ' '
Tag = 0
OnCommand = id_SrvCommandHandlers0Command
end>
ExceptionReply.Code = '500'
ExceptionReply.Text.Strings = (
'Unknown Internal Error')
Greeting.Code = '200'
Greeting.Text.Strings = (
'Welcome')
HelpReply.Code = '100'
HelpReply.Text.Strings = (
'Help follows')
MaxConnectionReply.Code = '300'
MaxConnectionReply.Text.Strings = (
'Too many connections. Try again later.')
ReplyTexts = <>
ReplyUnknownCommand.Code = '400'
ReplyUnknownCommand.Text.Strings = (
'Unknown Command')
Left = 28
Top = 26
end
end
Kacnep (2006-08-25 12:22:00) [18]Стоп!
А насчет Гашения базы.
В Оракле пользователькоторый может погасить "интерактивно" базу должен принадлежать группе ORA_DBA точно не помню...
Как Мной запущеный процесс поступит?
Или когда я говорю что процесс может шатдаунить машину ему выдаются привелегии АДМИНА который обладает правом ORA_DBA???
BiN (2006-08-25 12:19:00) [19]
> Kacnep © (25.08.06 12:05) [10]
>
> так попорядку от простого к сложному...
> 2 BiN © (25.08.06 11:48) [9]
> Мало того! На него никто и не заходил после перезагрузки!
> Установили сервис запустили в авто и ребутанули! Цель чтобы
> сервис уже работал (и он работает) и ожидал команды!
Ок. Просто имей ввиду http://support.microsoft.com/?id=821287
Теперь по коду.
Запусти CreateProcess с флагом CREATE_SUSPENDED, а после установки привилегии возобнови работу с пом. ResumeThread.
Мелкое замечание по
finally
// Cannot test the return value of AdjustTokenPrivileges.
if (GetLastError <> ERROR_SUCCESS) then raise Exception.Create('AdjustTokenPrivileges enable failed');
CloseHandle(hTok);
end
CloseHandle(hTok) должен стоять до возбуждения исключения.
Вот протестированная функция включения/выключения привилегий
function SetTokenPrivilege(hToken: THandle; const PrivilegeName:string;
NewState: DWORD; PreState: PDWORD=nil): Boolean;
var
LUID : TLargeInteger;
Privilege, PrevPrivilege: TTokenPrivileges;
cbPrevPriv:DWORD;
begin
Result:= False;
if LookupPrivilegeValue (nil, PChar (PrivilegeName), LUID) then
begin
Privilege.PrivilegeCount:=1;
Privilege.Privileges[0].Luid:=LUID;
Privilege.Privileges[0].Attributes:=NewState;
cbPrevPriv := SizeOf (PrevPrivilege);
Result:= AdjustTokenPrivileges(hToken, False, Privilege, cbPrevPriv, Privilege, cbPrevPriv);
if Result and Assigned(PreState)
then PreState^:=PrevPrivilege.Privileges[0].Attributes;
end;
end;
// NewState - SE_PRIVILEGE_ENABLED или 0
function SetProcessTokenPrivilege(hProcess:DWORD; const PrivilegeName:string;
NewState: DWORD; PreState: PDWORD=nil): Boolean;
var
hToken : THandle;
begin
Result:= False;
if not OpenProcessToken(hProcess,
TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,
hToken)
then Exit;
Result:=SetTokenPrivilege(hToken, PrivilegeName, NewState, PreState) and
(GetLastError()=ERROR_SUCCESS);
CloseHandle (hToken);
end;
Kacnep (2006-08-25 12:17:00) [20]Спасибо Мастера! Все советы попробую!