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);
+ }
+ }
}
}