diff --git a/udsService/rpc/Operation.cs b/udsService/rpc/Operation.cs index fa75cdb3..b2e758d5 100644 --- a/udsService/rpc/Operation.cs +++ b/udsService/rpc/Operation.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using log4net; using System.Runtime.InteropServices; +using System.Threading; namespace uds { @@ -22,6 +23,20 @@ namespace uds [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GetCurrentProcess(); + const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; + const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; + const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; + const uint FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; + const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; + const uint FORMAT_MESSAGE_FROM_STRING = 0x00000400; + + [DllImport("Kernel32.dll", SetLastError = true)] + static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, ref IntPtr lpBuffer, + uint nSize, IntPtr pArguments); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr LocalFree(IntPtr hMem); + [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); @@ -59,7 +74,7 @@ namespace uds NETSETUP_DEFER_SPN_SET = 0x10000000 } - [DllImport("netapi32.dll", CharSet = CharSet.Unicode)] + [DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)] static extern uint NetJoinDomain(string lpServer, string lpDomain, string lpAccountOU, string lpAccount, string lpPassword, JoinOptions NameType); enum COMPUTER_NAME_FORMAT @@ -73,7 +88,7 @@ namespace uds ComputerNamePhysicalDnsDomain, ComputerNamePhysicalDnsFullyQualified, } - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError=true)] static extern bool SetComputerNameEx(COMPUTER_NAME_FORMAT NameType, string lpBuffer); internal const int SE_PRIVILEGE_ENABLED = 0x00000002; @@ -92,7 +107,6 @@ namespace uds public static bool Reboot(int flg = EWX_FORCEIFHUNG|EWX_REBOOT) { - logger.Debug("Rebooting computer"); bool ok; TokPriv1Luid tp; IntPtr hproc = GetCurrentProcess(); @@ -105,12 +119,30 @@ namespace uds ok = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); ok = ExitWindowsEx(flg, 0); logger.Debug("Result: " + ok.ToString()); + if (ok) + logger.Info("Rebooting computer"); + else + logger.Error("Could not reboot machine. (Error " + ok.ToString() + ")"); + return ok; } + private static bool waitAfterError(string op, bool useGetLastError, ManualResetEvent waitEvent, TimeSpan retryDelay) + { + if (useGetLastError) + logger.Error("Error at " + op + ": " + GetLastErrorStr() + ". Retrying in " + retryDelay.Seconds.ToString() + " secs"); + else + logger.Error("Error at " + op + ". Retrying in " + retryDelay.Seconds.ToString() + " secs"); + + if (waitEvent.WaitOne(retryDelay)) + return false; + + return true; + } + public static bool RenameComputer(string newName) { - logger.Debug("Renaming computer to \"" + newName + "\""); + logger.Info("Renaming computer to \"" + newName + "\""); try { return SetComputerNameEx(COMPUTER_NAME_FORMAT.ComputerNamePhysicalDnsHostname, newName); @@ -121,6 +153,16 @@ namespace uds } } + public static bool RenameComputer(string newName, ManualResetEvent waitEvent, TimeSpan retryDelay) + { + while (RenameComputer(newName) == false) + { + if (waitAfterError("Rename", true, waitEvent, retryDelay) == false) + return false; + } + return true; + } + public static bool JoinDomain(string domain, string ou, string account, string password, bool oneStep = false) { if (account.Contains('@') == false && account.Contains('\\') == false) @@ -130,7 +172,7 @@ namespace uds else account = domain + "\\" + account; } - logger.Debug("Joining domain: \"" + domain + "\", \"" + ou + "\", \"" + account + "\", \"" + password + "\"" + ", oneStep = " + oneStep.ToString()); + logger.Info("Joining domain: \"" + domain + "\", \"" + ou + "\", \"" + account + "\", \"" + "*****" + "\"" + ", oneStep = " + oneStep.ToString()); // Flag NETSETUP_JOIN_WITH_NEW_NAME not supported on win xp/2000 JoinOptions flags = JoinOptions.NETSETUP_ACCT_CREATE | JoinOptions.NETSETUP_DOMAIN_JOIN_IF_JOINED | JoinOptions.NETSETUP_JOIN_DOMAIN; @@ -142,6 +184,18 @@ namespace uds try { uint res = NetJoinDomain(null, domain, ou, account, password, flags); + if (res == 2224) + { + flags = JoinOptions.NETSETUP_DOMAIN_JOIN_IF_JOINED | JoinOptions.NETSETUP_JOIN_DOMAIN; + logger.Info("Existing account for machine found, reusing it"); + res = NetJoinDomain(null, domain, null, account, password, flags); + } + if (res != 0) + { + logger.Error("Error joining domain:" + GetLastErrorStr((int)res)); + } + else + logger.Info("Successfully joined domain"); logger.Debug("Result of join: " + res); return res == 0; } @@ -153,6 +207,16 @@ namespace uds } + public static bool JoinDomain(string domain, string ou, string account, string password, bool oneStep, ManualResetEvent waitEvent, TimeSpan retryDelay) + { + while (JoinDomain(domain, ou, account, password, oneStep) == false) + { + if (waitAfterError("Join domain", true, waitEvent, retryDelay) == false) + return false; + } + return true; + } + public static bool ChangeUserPassword(string user, string oldPass, string newPass) { try { @@ -175,5 +239,33 @@ namespace uds return false; } } + + private static string GetLastErrorStr(int nLastError=-1) + { + if(nLastError == -1) + nLastError = Marshal.GetLastWin32Error(); + + IntPtr lpMsgBuf = IntPtr.Zero; + + uint dwChars = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + IntPtr.Zero, + (uint)nLastError, + 0, // Default language + ref lpMsgBuf, + 0, + IntPtr.Zero); + if (dwChars == 0) + { + return "(unknown)"; + } + + string sRet = Marshal.PtrToStringAnsi(lpMsgBuf); + + // Free the buffer. + lpMsgBuf = LocalFree(lpMsgBuf); + return sRet; + + } } } diff --git a/udsService/rpc/rpc.cs b/udsService/rpc/rpc.cs index 701a996e..75ca1f4e 100644 --- a/udsService/rpc/rpc.cs +++ b/udsService/rpc/rpc.cs @@ -161,7 +161,7 @@ namespace uds bool ok = false; if (rpc.Manager != null) { - logger.Debug("Informing broker of ready state"); + logger.Info("Machine is Ready"); try { List interfaces = Info.Computer.GetInterfacesInfo(); @@ -205,9 +205,21 @@ namespace uds } } + public static void FlushLoggers() + { + log4net.Repository.ILoggerRepository rep = LogManager.GetRepository(); + foreach (log4net.Appender.IAppender appender in rep.GetAppenders()) + { + var buffered = appender as log4net.Appender.BufferingAppenderSkeleton; + if (buffered != null) + buffered.Flush(); + } + } + public static void ResetId() { logger.Debug("Reseting ID of rpc"); + FlushLoggers(); if (rpc.Manager != null) rpc.Manager._id = null; } @@ -215,6 +227,7 @@ namespace uds public static void ResetManager() { logger.Debug("Disabling rpc"); + FlushLoggers(); rpc._manager = null; } } diff --git a/udsService/udsService/RPCAppender.cs b/udsService/udsService/RPCAppender.cs new file mode 100644 index 00000000..65669500 --- /dev/null +++ b/udsService/udsService/RPCAppender.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using log4net; + +namespace uds +{ + + public class RPCAppender : log4net.Appender.AppenderSkeleton + { + /// + /// Sends the logging event to UDS + /// + override protected void Append(log4net.Core.LoggingEvent lEvent) + { + string message = RenderLoggingEvent(lEvent); + // Filters out messages that are FATAL or DEBUG + if (lEvent.Level == log4net.Core.Level.Critical || lEvent.Level == log4net.Core.Level.Fatal || lEvent.Level == log4net.Core.Level.Debug) + return; + + rpc.Log(message, lEvent.Level.Name); + } + + /// + /// This appender requires a to be set. + /// + override protected bool RequiresLayout + { + get { return true; } + } + } +} diff --git a/udsService/udsService/Service.cs b/udsService/udsService/Service.cs index f2dc3e43..479ce5da 100644 --- a/udsService/udsService/Service.cs +++ b/udsService/udsService/Service.cs @@ -13,10 +13,12 @@ namespace uds.Services { private static ILog logger = LogManager.GetLogger(typeof(Service)); const int secsDelay = 5; + const int retrySecsDelay = 60; private Thread _thread; private ManualResetEvent _stopEvent; private TimeSpan _delay; + private TimeSpan _retryDelay; private bool _reboot; private static void SensLogon_Logon(string userName) @@ -71,6 +73,7 @@ namespace uds.Services _thread = null; _stopEvent = null; _delay = new TimeSpan(0, 0, 0, secsDelay, 0); + _retryDelay = new TimeSpan(0, 0, 0, retrySecsDelay, 0); _reboot = false; } @@ -113,129 +116,137 @@ namespace uds.Services { logger.Debug("Initiated Service main"); - // We have to wait till we have ip - List interfaces = null; - while (interfaces == null) + Dictionary knownIps = new Dictionary(); + + try { - logger.Debug("Trying to get network info.."); + // We have to wait till we have ip + List interfaces = null; + while (interfaces == null) + { + logger.Debug("Trying to get network info.."); + try + { + interfaces = Info.Computer.GetInterfacesInfo(); + } + catch (Exception e) + { + logger.Error("Exception!!!", e); + } + if (interfaces == null) + { + bool exit = _stopEvent.WaitOne(_delay); + if (exit) + { + logger.Debug("Exit requested waiting for interfaces"); + return; + } + } + } + // We have now interfaces info, intialize the connection and try to connect + // In fact, we do not use the interfaces received except for logging, initialize gets their own data from there + rpc.Initialize(config.broker, config.ssl); + logger.Info("Interfaces: " + string.Join(",", interfaces.ConvertAll(i => i.mac + "=" + i.ip).ToArray())); + string action = null; + while (action == null) + { + logger.Debug("Trying to contact server to get action"); + rpc.ResetId(); // So we get interfaces info every time we try to contact broker + action = rpc.GetInfo(); // Get action to execute + if (action == null) + { + bool exit = _stopEvent.WaitOne(_delay); + if (exit) + { + logger.Debug("Exit requested waiting for broker info"); + return; + } + } + } + + if (action == "") + { + logger.Debug("Unmanaged machine, exiting..."); + // Reset rpc so next calls are simply ignored... + rpc.ResetManager(); + return; + } + + // Important note: + // Remove ":" as separator, due to the posibility that ":" can be used as part of a password + // Old ":" is now '\r' + // In order to keep compatibility, getInfo will invoke rcp "information", so old actors "versions" + // will keep invoking "info" and return the old ":" separator way + + // Message is in the form "action\rparams", where we can identify: + // rename\rcomputername --- > Just rename + // rename\rcomputername\tuser\toldPass\tnewPass --> Rename with user password changing + // domain:computername\tdomain\tou\tuserToAuth\tpassToAuth --> Rename and add machine to domain + string[] data = action.Split('\r'); + if (data.Length != 2) + { + logger.Error("Unrecognized instruction: \"" + action + "\""); + rpc.ResetManager(); // Invalidates manager, cause we don't recognized it + return; + } + + string[] parms = data[1].Split('\t'); + + switch (data[0]) + { + case "rename": + if (parms.Length == 1) + // Do not have to change user password + Rename(parms[0], null, null, null); + else if (parms.Length == 4) + // Rename, and also change user password + Rename(parms[0], parms[1], parms[2], parms[3]); + else + { + logger.Error("Unrecognized parameters: " + data[1]); + rpc.ResetManager(); + return; + } + + break; + case "domain": + { + if (parms.Length != 5) + { + logger.Error("Unrecognized parameters: " + data[1]); + rpc.ResetManager(); // Invalidates manager, cause we don't recognized it + return; + } + JoinDomain(parms[0], parms[1], parms[2], parms[3], parms[4]); + } + break; + default: + logger.Error("Unrecognized action: \"" + data[0] + "\""); + rpc.ResetManager(); // Invalidates manager, cause we don't recognized it + return; + } + // Reboot process or no process at all, exit + if (_reboot || rpc.Manager == null) + { + logger.Debug("Returning, reboot = '" + _reboot.ToString() + "' + rcp.Manager = '" + rpc.Manager.ToString() + "'"); + return; + } + logger.Debug("Main loop waiting for ip change"); + // Now, every secs delay, get if the interfaces ips changes and notify service try { - interfaces = Info.Computer.GetInterfacesInfo(); + foreach (Info.Computer.InterfaceInfo i in Info.Computer.GetInterfacesInfo()) + knownIps.Add(i.mac, i.ip); } catch (Exception e) { - logger.Error("Exception!!!",e); - } - if (interfaces == null) - { - bool exit = _stopEvent.WaitOne(_delay); - if (exit) - { - logger.Debug("Exit requested waiting for interfaces"); - return; - } - } - } - // We have now interfaces info, intialize the connection and try to connect - // In fact, we do not use the interfaces received except for logging, initialize gets their own data from there - rpc.Initialize(config.broker, config.ssl); - logger.Info("Interfaces: " + string.Join(",", interfaces.ConvertAll(i => i.mac + "=" + i.ip).ToArray())); - string action = null; - while (action == null) - { - logger.Debug("Trying to contact server to get action"); - rpc.ResetId(); // So we get interfaces info every time we try to contact broker - action = rpc.GetInfo(); // Get action to execute - if (action == null) - { - bool exit = _stopEvent.WaitOne(_delay); - if (exit) - { - logger.Debug("Exit requested waiting for broker info"); - return; - } - } - } - - if (action == "") - { - logger.Debug("Unmanaged machine, exiting..."); - // Reset rpc so next calls are simply ignored... - rpc.ResetManager(); - return; - } - - // Important note: - // Remove ":" as separator, due to the posibility that ":" can be used as part of a password - // Old ":" is now '\r' - // In order to keep compatibility, getInfo will invoke rcp "information", so old actors "versions" - // will keep invoking "info" and return the old ":" separator way - - // Message is in the form "action\rparams", where we can identify: - // rename\rcomputername --- > Just rename - // rename\rcomputername\tuser\toldPass\tnewPass --> Rename with user password changing - // domain:computername\tdomain\tou\tuserToAuth\tpassToAuth --> Rename and add machine to domain - string[] data = action.Split('\r'); - if (data.Length != 2) - { - logger.Error("Unrecognized instruction: \"" + action + "\""); - rpc.ResetManager(); // Invalidates manager, cause we don't recognized it - return; - } - - string[] parms = data[1].Split('\t'); - - switch (data[0]) - { - case "rename": - if (parms.Length == 1 ) - // Do not have to change user password - Rename(parms[0], null, null, null); - else if (parms.Length == 4) - // Rename, and also change user password - Rename(parms[0], parms[1], parms[2], parms[3]); - else - { - logger.Error("Unrecognized parameters: " + data[1]); - rpc.ResetManager(); - return; - } - - break; - case "domain": - { - if (parms.Length != 5) - { - logger.Error("Unrecognized parameters: " + data[1]); - rpc.ResetManager(); // Invalidates manager, cause we don't recognized it - return; - } - JoinDomain(parms[0], parms[1], parms[2], parms[3], parms[4]); - } - break; - default: - logger.Error("Unrecognized action: \"" + data[0] + "\""); - rpc.ResetManager(); // Invalidates manager, cause we don't recognized it + logger.Error("Could not accesss ip adresses!!", e); return; - } - // Reboot process or no process at all, exit - if (_reboot || rpc.Manager == null) - { - logger.Debug("Returning, reboot = '" + _reboot.ToString() + "' + rcp.Manager = '" + rpc.Manager.ToString() + "'"); - return; - } - logger.Debug("Main loop waiting for ip change"); - // Now, every secs delay, get if the interfaces ips changes and notify service - Dictionary knownIps = new Dictionary(); - try - { - foreach (Info.Computer.InterfaceInfo i in Info.Computer.GetInterfacesInfo()) - knownIps.Add(i.mac, i.ip); + } } catch (Exception e) { - logger.Error("Could not accesss ip adresses!!", e); - return; + logger.Error(e); } while (true) @@ -273,13 +284,12 @@ namespace uds.Services private void Rename(string name, string user, string oldPass, string newPass) { - logger.Info("Requested renaming of computer to \"" + name + "\""); // name and newName can be different case, but still same Info.DomainInfo info = Info.Computer.GetDomainInfo(); if ( string.Equals(info.ComputerName, name, StringComparison.CurrentCultureIgnoreCase)) { - logger.Info("Computer do not needs to be renamed"); + logger.Info("Computer name is " + info.ComputerName); rpc.SetReady(); return; } @@ -296,7 +306,7 @@ namespace uds.Services } } - if (Operation.RenameComputer(name) == false) + if (Operation.RenameComputer(name, _stopEvent, _retryDelay) == false) { logger.Error("Could not rename machine to \"" + name + "\""); rpc.ResetManager(); @@ -310,7 +320,6 @@ namespace uds.Services private void OneStepJoin(string name, string domain, string ou, string account, string pass) { - logger.Info("Requested one step join of computer to \"" + domain + "\" with name \"" + name + "\" under ou \"" + ou + "\"" ); // name and newName can be different case, but still same Info.DomainInfo info = Info.Computer.GetDomainInfo(); if (string.Equals(info.ComputerName, name, StringComparison.CurrentCultureIgnoreCase)) @@ -318,7 +327,7 @@ namespace uds.Services // We should be already in the domain, if not, will try second step of "multiStepJoin" if(info.Status == Info.DomainInfo.NetJoinStatus.NetSetupDomainName ) // Already in domain { - logger.Debug("Machine already in the domain"); + logger.Info("Machine " + name + " in domain " + domain); rpc.SetReady(); return; } @@ -327,14 +336,14 @@ namespace uds.Services return; } // Needs to rename + join - if (Operation.RenameComputer(name) == false) + if (Operation.RenameComputer(name, _stopEvent, _retryDelay) == false) { logger.Error("Could not rename machine to \"" + name + "\""); rpc.ResetManager(); return; } // Now try to join domain - if (Operation.JoinDomain(domain, ou, account, pass, true) == false) + if (Operation.JoinDomain(domain, ou, account, pass, true, _stopEvent, _retryDelay) == false) { logger.Error("Could not join domain \"" + domain + "\", ou \"" + ou + "\""); rpc.ResetManager(); @@ -346,19 +355,18 @@ namespace uds.Services private void MultiStepJoin(string name, string domain, string ou, string account, string pass) { - logger.Info("Requested two step join of computer to \"" + domain + "\" with name \"" + name + "\" under ou \"" + ou + "\""); Info.DomainInfo info = Info.Computer.GetDomainInfo(); if (string.Equals(info.ComputerName, name, StringComparison.CurrentCultureIgnoreCase)) { // Name already, now see if already in domain if (info.Status == Info.DomainInfo.NetJoinStatus.NetSetupDomainName) // Already in domain { - logger.Debug("Machine already in the domain"); + logger.Info("Machine " + name + " in domain " + domain); rpc.SetReady(); return; } // Now try to join domain - if (Operation.JoinDomain(domain, ou, account, pass, true) == false) + if (Operation.JoinDomain(domain, ou, account, pass, false, _stopEvent, _retryDelay) == false) { logger.Error("Could not join domain \"" + domain + "\", ou \"" + ou + "\""); rpc.ResetManager(); @@ -368,7 +376,7 @@ namespace uds.Services else { // Try to rename machine - if (Operation.RenameComputer(name) == false) + if (Operation.RenameComputer(name, _stopEvent, _retryDelay) == false) { logger.Error("Could not rename machine to \"" + name + "\""); rpc.ResetManager();