diff --git a/src/Console/Program.cs b/src/Console/Program.cs index 90c8af5f..808f0f56 100644 --- a/src/Console/Program.cs +++ b/src/Console/Program.cs @@ -1,4 +1,5 @@ using Bit.Core.Models; +using Bit.Core.Services; using Bit.Core.Utilities; using System; using System.Collections.Generic; @@ -55,7 +56,7 @@ namespace Bit.Console Con.WriteLine("4. Configure sync"); Con.WriteLine("5. Simulate directory sync"); Con.WriteLine("6. Sync directory"); - Con.WriteLine("7. Start/stop background service"); + Con.WriteLine("7. Control background service"); Con.WriteLine("8. Exit"); Con.WriteLine(); Con.Write("What would you like to do? "); @@ -95,9 +96,10 @@ namespace Bit.Console case "sync": await SyncAsync(); break; + case "7": case "svc": case "service": - + await ServiceAsync(); break; case "exit": case "quit": @@ -697,6 +699,80 @@ namespace Bit.Console } } + private static Task ServiceAsync() + { + try + { + Con.WriteLine("Service current status: {0}", ControllerService.Instance.StatusString); + } + catch + { + Con.WriteLine("Service unavailable."); + return Task.FromResult(0); + } + + var start = false; + var stop = false; + var status = false; + if(_usingArgs) + { + var parameters = ParseParameters(); + if(parameters.ContainsKey("start")) + { + start = true; + } + else if(parameters.ContainsKey("stop")) + { + stop = true; + } + } + else + { + Con.WriteLine("1. Start service"); + Con.WriteLine("2. Stop service"); + Con.WriteLine("3. Check service status"); + Con.WriteLine("4. Nothing, go back"); + Con.WriteLine(); + Con.Write("Option: "); + var selection = Con.ReadLine(); + + switch(selection) + { + case "1": + case "start": + start = true; + break; + case "2": + case "stop": + stop = true; + break; + case "3": + case "status": + status = true; + break; + default: + break; + } + } + + if(start) + { + Con.WriteLine("Starting service..."); + ControllerService.Instance.Start(); + } + else if(stop) + { + Con.WriteLine("Stopping service..."); + ControllerService.Instance.Stop(); + } + else if(status) + { + Con.WriteLine("Status: {0}", ControllerService.Instance.StatusString); + } + + return Task.FromResult(0); + } + private static string ReadSecureLine() { var input = string.Empty; diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index fa40fdbc..4428a52b 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -53,6 +53,7 @@ + @@ -83,6 +84,7 @@ + diff --git a/src/Core/Services/ControllerService.cs b/src/Core/Services/ControllerService.cs new file mode 100644 index 00000000..426a20cd --- /dev/null +++ b/src/Core/Services/ControllerService.cs @@ -0,0 +1,70 @@ +using Bit.Core.Enums; +using Bit.Core.Models; +using Bit.Core.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace Bit.Core.Services +{ + public class ControllerService + { + private static ControllerService _instance; + + private ControllerService() + { + Controller = new ServiceController("bitwarden Directory Connector"); + } + + public static ControllerService Instance + { + get + { + if(_instance == null) + { + _instance = new ControllerService(); + } + + return _instance; + } + } + + public ServiceController Controller { get; private set; } + public ServiceControllerStatus Status => Controller.Status; + public string StatusString => Controller == null ? "Unavailable" : Status.ToString(); + public bool Running => Status == ServiceControllerStatus.Running; + public bool Paused => Status == ServiceControllerStatus.Paused; + public bool Stopped => Status == ServiceControllerStatus.Stopped; + public bool Pending => + Status == ServiceControllerStatus.ContinuePending || + Status == ServiceControllerStatus.PausePending || + Status == ServiceControllerStatus.StartPending || + Status == ServiceControllerStatus.StopPending; + + public bool Start() + { + if(Controller == null || !Stopped) + { + return false; + } + + Controller.Start(); + return true; + } + + public bool Stop() + { + if(Controller == null || !Controller.CanStop) + { + return false; + } + + Controller.Stop(); + return true; + } + } +} diff --git a/src/Service/Service.cs b/src/Service/Service.cs index 38c9043a..d2dd2c76 100644 --- a/src/Service/Service.cs +++ b/src/Service/Service.cs @@ -1,4 +1,6 @@ -using System; +using Bit.Core.Services; +using Bit.Core.Utilities; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -6,7 +8,7 @@ using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; -using System.Threading.Tasks; +using System.Threading; namespace Service { @@ -15,6 +17,7 @@ namespace Service { private IContainer _components; private EventLog _eventLog; + private Timer _timer; public Service() { @@ -44,6 +47,9 @@ namespace Service _components?.Dispose(); _components = null; + + _timer?.Dispose(); + _timer = null; } base.Dispose(disposing); @@ -52,11 +58,53 @@ namespace Service protected override void OnStart(string[] args) { _eventLog.WriteEntry("Service started!", EventLogEntryType.Information); + + if(SettingsService.Instance.Server == null) + { + _eventLog.WriteEntry("Server not configured.", EventLogEntryType.Error); + return; + } + + if(SettingsService.Instance.Sync == null) + { + _eventLog.WriteEntry("Sync not configured.", EventLogEntryType.Error); + return; + } + + if(!AuthService.Instance.Authenticated || !AuthService.Instance.OrganizationSet) + { + _eventLog.WriteEntry("Not authenticated with proper organization set.", EventLogEntryType.Error); + return; + } + + var timerDelegate = new TimerCallback(Callback); + _timer = new Timer(timerDelegate, null, 1000, 60 * 1000); } protected override void OnStop() { _eventLog.WriteEntry("Service stopped!", EventLogEntryType.Information); } + + private void Callback(object stateInfo) + { + try + { + var result = Sync.SyncAllAsync(false, true).GetAwaiter().GetResult(); + if(result.Success) + { + _eventLog.WriteEntry($"Synced {result.Groups.Count} groups, {result.Users.Count} users.", + EventLogEntryType.SuccessAudit); + } + else + { + _eventLog.WriteEntry($"Sync failed: {result.ErrorMessage}", EventLogEntryType.FailureAudit); + } + } + catch(ApplicationException e) + { + _eventLog.WriteEntry($"Sync exception: {e.Message}", EventLogEntryType.Error); + } + } } }