Fiz no trabalho uma aplicação em ASP.NET (com C# no server side) que consulta o status de um Windows Service que eu havia criado, podendo pará-lo ou iniciá-lo.
Como nós temos balanceamento de carga em produção, o serviço será instalado em 4 servidores mas em apenas um deles ele será iniciado para ficar em execução. A página que criei permite escolher em qual servidor iniciar/parar o serviço, bem como ver o status em tempo real e não deixar iniciar em mais de um servidor ao mesmo tempo.
Criei os métodos abaixo pra controlar o serviço (tirei o tratamento de erro para ficar mais claro):
[code lang=”C#”]using System.ServiceProcess;
public void IniciarServico(string nomeServico, string nomeServidor)
{
ServiceController servico = new ServiceController(nomeServico, nomeServidor);
if (servico.Status == ServiceControllerStatus.Stopped)
{
servico.Start();
servico.WaitForStatus(ServiceControllerStatus.Running);
}
}
public void PararServico(string nomeServico, string nomeServidor)
{
ServiceController servico = new ServiceController(nomeServico, nomeServidor);
if (servico.CanStop)
servico.Stop();
}
public string StatusServico(string nomeServico, string nomeServidor)
{
return new ServiceController(nomeServico, nomeServidor).Status.ToString();
}[/code]
Tudo funcionou muito bem nos testes unitários e no ambiente de desenvolvimento, mas no ambiente de TI (Testes Integrados), começou a retornar exceção System.InvalidOperationException, com a seguinte mensagem: Não é possível abrir o serviço RoboMovimentacaoServico no computador ‘‘xxxxxxxxxx’ sendo que a InnerException foi Access is denied.
Pesquisando na Internet achei um problema muito semelhante ao meu, onde um tal de Charlie dá uma resposta sugerindo modificar o security descriptor (DACL) para incluir permissões de SERVICE_START, SERVICE_STOP e, opcionalmente, GENERIC_READ.
Segundo ele existem duas formas de fazer isso:
- através de linha de comando utilizando o SC do Windows
- criando um programa em C++ e utilizando a API SetNamedSecurityInfo
Resolvi procurar por uma solução em C# e achei no stackenbloggen… mas além de não funcionar por falta de permissão, excluiu todos os acessos ao serviço! Não conseguia iniciar, parar, ver as propriedades, desinstalar, excluir… sempre dava erro “5 – Access Denied“. Na hora pensei: f*deu!
Felizmente outro cara já havia passado por isso e deu as dicas de como redefinir uma DACL vazia em um descritor de segurança de um serviço em um computador Windows Server 2003:
- abrir um prompt de comando no modo interativo, para ficar logado com o usuário NT AUTHORITY\SYSTEM (conta de sistema). O comando pra fazer isso é: “at 18:05 cmd.exe /interactive” (sem as aspas, onde 18:05 é a hora daqui a 1 minuto)
- utilizar o comando “sc sdset service_name D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCDCLCSWRPWPDTLCRSDRCWDWO;;;SO)(A;;CCLCSWRPWPDTLOCRRC;;;SY)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;WD)” (sem as aspas, onde service_name é o nome do serviço)
Obs.: pro prompt abrir, tive de deixar aberta a tela de tarefas agendadas (Start -> Programs -> Accessories -> System Tools -> Scheduled Tasks).
O comando SC é meio cavernoso, então vou partir para o SubInAcl, que descobri nessa página do Craig Box.
Ele teve um problema idêntico ao meu e chegou à conclusão de que no teste unitário o serviço é controlado interativamente e pelo site o processo do IIS utiliza uma conta de usuário, que não tem acesso ao serviço. Pelo SubInAcl (assim como pelo SC) é possível incluir essa autorização, sendo que pelo SubInAcl é muuuuito mais fácil. A única desvantagem do SubInAcl é que ele não vem instalado no Windows, ao contrário do SC…
Amanhã brinco um pouco com o SC, o SubInAcl e o código em C# pra configurar esse serviço…
Update: consegui conceder o acesso utilizando tanto o SC quanto o SubInAcl.
1 Comentários.