Exemple #1
0
        private static string ReadPatchRegistryAttribute(
            XPathNodeIterator iterator,
            string patchName,
            string attributeName,
            bool throwIfError)
        {
            // Read registry attribute
            string         attributeValue = null;
            XPathNavigator tempIterator   = iterator.Current.SelectSingleNode(attributeName);

            if (tempIterator != null)
            {
                attributeValue = tempIterator.Value.Trim();
            }

            if (throwIfError && string.IsNullOrEmpty(attributeValue))
            {
                LogUtils.WriteTrace(DateTime.UtcNow, string.Format(
                                        CultureInfo.InvariantCulture,
                                        "{0} attribute not found or is empty in element Registry of Patch name {1}",
                                        attributeName,
                                        patchName));

                throw new InvalidOperationException(
                          string.Format(
                              CultureInfo.InvariantCulture,
                              "{0} attribute not found or is empty in element Registry of Patch name {1}",
                              attributeName,
                              patchName));
            }

            return(attributeValue);
        }
Exemple #2
0
        /// <summary>
        /// Install patches
        /// </summary>
        public void InstallPatches()
        {
            string patchConfigFilePath = Path.Combine(Path.GetPathRoot(Assembly.GetExecutingAssembly().Location), "plugins", "patchplugin", PatchConfigurationFileName);

            PatchConfiguration patchConfiguration = new PatchConfiguration();

            patchConfiguration.Load(patchConfigFilePath);

            if (patchConfiguration.PatchMap == null || patchConfiguration.PatchMap.Count == 0)
            {
                LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: no patches to install.");
                return;
            }

            // First check if all patches have been installed
            IList <PatchConfiguration.Patch> uninstalledPatches = GetUninstalledPatches(patchConfiguration);

            if (uninstalledPatches.Count == 0)
            {
                LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: all patches have been installed.");
                return;
            }

            try
            {
                InstallPatches(uninstalledPatches, patchConfiguration.OverallTimeoutSeconds * 1000);

                // Wait timeout and return
                DateTime startTime = DateTime.UtcNow;
                while (DateTime.UtcNow < startTime + TimeSpan.FromSeconds(patchConfiguration.OverallTimeoutSeconds))
                {
                    uninstalledPatches = GetUninstalledPatches(patchConfiguration);
                    if (uninstalledPatches.Count == 0)
                    {
                        LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: all patches have been installed. Exit and reboot without waiting until timeout.");
                        return;
                    }

                    LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: sleep 1 minutes before checking patch installation again.");
                    Thread.Sleep(TimeSpan.FromMinutes(1));
                }
            }
            catch (Exception e)
            {
                // Log the exception
                LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: Patch installation encountered an Exception: " + e);

                if (!_rebootRequired)
                {
                    // We haven't start install patches, so throw and the process will be restarted (without reboot)
                    throw;
                }

                // Eat the exception and the VM is going to be rebooted.
                LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: At least one patch is installed, waiting to be rebooted.");
            }
        }
Exemple #3
0
        private void InstallPatches(IList <PatchConfiguration.Patch> patchesToInstall, int timeoutMs)
        {
            string patchInstallFolder = GetPatchInstallationFolder();

            LogUtils.WriteTrace(DateTime.UtcNow,
                                string.Format("PatchUtil: set patch installation folder: '{0}'", patchInstallFolder));

            foreach (PatchConfiguration.Patch patch in patchesToInstall)
            {
                StringBuilder outString = new StringBuilder();
                StringBuilder errString = new StringBuilder();

                LogUtils.WriteTrace(DateTime.UtcNow, string.Format("PatchUtil: install patch '{0}'", patch.Name));
                string command = patch.CommandLine.TrimStart();
                LogUtils.WriteTrace(DateTime.UtcNow, string.Format("PatchUtil: run commandline '{0}'", command));

                // Prepare the log folder
                if (!string.IsNullOrEmpty(patch.LogFolderPath))
                {
                    string expandedPath = Environment.ExpandEnvironmentVariables(patch.LogFolderPath);
                    LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: patch log folder is at: " + expandedPath);

                    if (!Directory.Exists(expandedPath))
                    {
                        Directory.CreateDirectory(expandedPath);
                        LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: patch log folder is created.");
                    }
                    else
                    {
                        LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: patch log folder exist.");
                    }
                }

                // Start to run the command to install patch. Set _rebootRequired to be true
                _rebootRequired = true;

                int exitCode = ScriptingHelper.Execute(
                    command,
                    new StringWriter(outString, CultureInfo.InvariantCulture),
                    new StringWriter(errString, CultureInfo.InvariantCulture),
                    timeoutMs,
                    patchInstallFolder);

                LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: run commandline exit with code: " + exitCode);
                if (!string.IsNullOrEmpty(outString.ToString()))
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: run commandline output: " + outString);
                }

                if (!string.IsNullOrEmpty(errString.ToString()))
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, "PatchUtil: run commandline error: " + errString);
                }

                // Do not check the exit code because we don't know what is expected
            }
        }
Exemple #4
0
 private static void Halt()
 {
     while (true)
     {
         Thread.Sleep(TimeSpan.FromMinutes(1));
         LogUtils.WriteTrace(DateTime.UtcNow, "Waiting for reboot to occur...");
     }
     // ReSharper disable FunctionNeverReturns
 }
Exemple #5
0
        public static int Execute(string binaryPath, string parameters, TextWriter outStream, TextWriter errStream, StreamReader inputStream, int timeoutInMS, string workingDirectory)
        {
            ScriptExecution se = new ScriptExecution(outStream, errStream);

            int returnCode = -1;

            LogUtils.WriteTrace(DateTime.UtcNow, string.Format("Binary path : {0}", binaryPath));
            LogUtils.WriteTrace(DateTime.UtcNow, string.Format("Binary parameters : {0}", parameters));
            LogUtils.WriteTrace(DateTime.UtcNow, string.Format("Working directory : {0}", workingDirectory));

            using (Process proc = CreateProcess(binaryPath, parameters, outStream, errStream, inputStream, workingDirectory))
            {
                if (-1 != timeoutInMS)
                {
                    if (!proc.WaitForExit(timeoutInMS))
                    {
                        proc.ErrorDataReceived  -= se.ErrorDataHandler;
                        proc.OutputDataReceived -= se.OutputDataHandler;

                        // The ScriptingHelper before throwing TimeoutException exception should
                        // disable the output/input handlers of the ScriptExecution class as there
                        // can be already scheduled output write events by the Process object in
                        // the ThreadPool that will be executed asynchronously after the exception
                        // is thrown.
                        // As throwing the exception usually causes the output/input streams in
                        // upper layer to be disposed the scheduled output write events by the
                        // Process in the ThreadPool that are executed after the disposing would
                        // throw in the background thread the ObjectDisposed exception that would cause the process to crash.
                        // Therefore disabling here the handlers explicitly before throwing.
                        se.DisableDataHandlers = true;
                        LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "process {0} {1} timed out!", binaryPath, parameters));
                        throw new TimeoutException(string.Format(CultureInfo.InvariantCulture, "process {0} {1} timed out!", binaryPath, parameters));
                    }

                    // Call to WaitForExit(Int32) returned >>>TRUE<<<, so we've exited without a timeout. But according to documentation, we're not guaranteed to
                    // have completed processing of asynchronous events when redirecting stdout.
                    //
                    // Workaround is to call WaitForExit(void) a second time which won't return until async events finish processing. Waiting is important
                    // so we can safely call StringBuilder.ToString() which is not thread safe, and we don't want event handlers to call StringBuilder.AppendLine
                    // from parallel threads.
                    // COMMENTED OUT: This blocks on opened handles if the child process starts another child process and then exists.
                    // proc.WaitForExit();
                    proc.ErrorDataReceived  -= se.ErrorDataHandler;
                    proc.OutputDataReceived -= se.OutputDataHandler;
                    se.DisableDataHandlers   = true;
                }
                else
                {
                    proc.WaitForExit();
                }

                returnCode = proc.ExitCode;
            }

            return(returnCode);
        }
Exemple #6
0
        /// <summary>
        /// This function reboots the local machine using the Win32_OperatingSystem
        /// WMI object from the local WMI instance
        /// </summary>
        internal static void Reboot()
        {
            try
            {
                string computerName = Environment.MachineName; // computer name or IP address

                ConnectionOptions options = new ConnectionOptions();
                options.EnablePrivileges = true;
                // To connect to the remote computer using a different account, specify these values:
                // options.Username = "******";

                // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Comment with example")]
                // options.Password = "******";
                // options.Authority = "ntlmdomain:DOMAIN";

                ManagementScope scope = new ManagementScope("\\\\" + computerName + "\\root\\CIMV2", options);
                scope.Connect();

                SelectQuery query = new SelectQuery("Win32_OperatingSystem");
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

                LogUtils.WriteTrace(DateTime.UtcNow, "Requesting machine reboot");

                // DEV NOTE : We are missing adding UploadData to Xstore here as I did not want that to interrupt this work
                // UploadData();

                foreach (ManagementObject os in searcher.Get())
                {
                    // Obtain in-parameters for the method
                    ManagementBaseObject inParams = os.GetMethodParameters("Win32Shutdown");

                    // Add the input parameters for Forced Reboot
                    inParams["Flags"] = 6;

                    // Execute the method and obtain the return values.
                    os.InvokeMethod("Win32Shutdown", inParams, null);
                }

                // Halt execution permanently; machine should reboot
                Halt();
            }
            catch (ManagementException err)
            {
                LogUtils.WriteTrace(DateTime.UtcNow, string.Format("An error occurred while trying to execute the WMI method: {0} ", err));
                throw;
            }
            catch (UnauthorizedAccessException unauthorizedErr)
            {
                LogUtils.WriteTrace(DateTime.UtcNow, string.Format("Connection error (user name or password might be incorrect): {0} ", unauthorizedErr));
                throw;
            }
        }
Exemple #7
0
        public void Load(string configFilePath)
        {
            if (string.IsNullOrEmpty(configFilePath))
            {
                LogUtils.WriteTrace(DateTime.UtcNow, "configFilePath cannot be null or empty");
                throw new ArgumentException("Patch configuration file path cannot be null or empty.", "configFilePath");
            }

            if (!File.Exists(configFilePath))
            {
                LogUtils.WriteTrace(DateTime.UtcNow, string.Format("Cannot find patch configuration file: {0}", configFilePath));
                throw new InvalidOperationException("Cannot find patch configuration file: " + configFilePath);
            }

            LoadConfiguration(configFilePath);
        }
Exemple #8
0
        private static int Main(string[] args)
        {
            bool success = false;

            LogUtils.WriteTrace(DateTime.UtcNow, "patch plugin started");
            // Run patches
            try
            {
                success = RunTasks();
            }
            catch (Exception ex)
            {
                LogUtils.WriteTrace(DateTime.UtcNow, "Sterling patch operation ran into Exception: " + ex);
            }

            return(success ? 0 : 1);
        }
Exemple #9
0
        private void LoadConfiguration(string configFilePath)
        {
            XPathNavigator navigator            = GetNavigator(configFilePath);
            string         overallTimeoutString = navigator.SelectSingleNode(OverallTimeoutValueXPath).Value;

            if (string.IsNullOrEmpty(overallTimeoutString) || (!int.TryParse(overallTimeoutString, out _overallTimeoutSeconds)))
            {
                LogUtils.WriteTrace(DateTime.UtcNow, "OverallTimeout value is not defined or is not a valid integer value. OverallTimeout = " +
                                    (string.IsNullOrEmpty(overallTimeoutString) ? "null" : overallTimeoutString));

                throw new InvalidOperationException(
                          "OverallTimeout value is not defined or is not a valid integer value. OverallTimeout = " +
                          (string.IsNullOrEmpty(overallTimeoutString) ? "null" : overallTimeoutString));
            }

            if (_overallTimeoutSeconds < 0)
            {
                LogUtils.WriteTrace(DateTime.UtcNow, "OverallTimeout value cannot be less than 0. OverallTimeout = " +
                                    overallTimeoutString);

                throw new InvalidOperationException(
                          "OverallTimeout value cannot be less than 0. OverallTimeout = " +
                          overallTimeoutString);
            }

            // Loop through all patches
            XPathNodeIterator nodeIterator = navigator.Select(PatchXPath);

            while (nodeIterator.MoveNext())
            {
                Patch patch = new Patch();

                // Read name
                if (nodeIterator.Current.SelectSingleNode("@name") == null)
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, "name attribute needed for Patch element");

                    throw new InvalidOperationException("name attribute needed for Patch element");
                }

                patch.Name = nodeIterator.Current.SelectSingleNode("@name").Value.Trim();
                if (string.IsNullOrEmpty(patch.Name))
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, "name attribute can't be empty for Patch element");

                    throw new InvalidOperationException("name attribute can't be empty for Patch element");
                }

                if (_patchMap.ContainsKey(patch.Name))
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "Patch '{0}' is defined in multiple places.", patch.Name));

                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Patch '{0}' is defined in multiple places.", patch.Name));
                }

                // Read OS versions the patch applies to and check whether it is applicable to current OS version
                if (nodeIterator.Current.SelectSingleNode("@appliesToOsVersion") != null)
                {
                    string osVersionAppliesTo = string.Empty;

                    try
                    {
                        bool invalidVersionFormat = false;
                        osVersionAppliesTo = nodeIterator.Current.SelectSingleNode("@appliesToOsVersion").Value.Trim();

                        Version currentOsVersion = Environment.OSVersion.Version;

                        char[]   versionDelimiter           = new char[] { '.' };
                        string[] versionComponents          = osVersionAppliesTo.Split(versionDelimiter);
                        string[] currentOsVersionComponents = currentOsVersion.ToString().Split(versionDelimiter);

                        // OS version the patch applies to should have four components: major.minor.build.revision
                        if (versionComponents.Length != 4)
                        {
                            invalidVersionFormat = true;
                        }
                        else
                        {
                            // Each version component should be an integer or a wildcard character '*'
                            foreach (string t in versionComponents)
                            {
                                int versionComponent;
                                if (t.Equals("*", StringComparison.OrdinalIgnoreCase) || int.TryParse(t, out versionComponent))
                                {
                                    continue;
                                }
                                invalidVersionFormat = true;
                                break;
                            }
                        }

                        if (invalidVersionFormat)
                        {
                            LogUtils.WriteTrace(DateTime.UtcNow, string.Format(
                                                    @"PathUtil: Invalid version format for OS version patch {0} applies to : {1}; the patch will be installed",
                                                    patch.Name,
                                                    osVersionAppliesTo));
                        }
                        else
                        {
                            bool skipPatch = false;
                            for (int i = 0; i < Math.Min(versionComponents.Length, currentOsVersionComponents.Length); i++)
                            {
                                if (!versionComponents[i].Equals("*", StringComparison.OrdinalIgnoreCase) &&
                                    !versionComponents[i].Equals(currentOsVersionComponents[i], StringComparison.OrdinalIgnoreCase))
                                {
                                    skipPatch = true;
                                }
                            }

                            // If the patch is not applicable to current OS version, skip it
                            if (skipPatch)
                            {
                                LogUtils.WriteTrace(DateTime.UtcNow, string.Format(
                                                        @"PatchUtil: Patch {0} will be skipped since it is only applicable to OS version {1} while current OS version is {2}",
                                                        patch.Name,
                                                        osVersionAppliesTo,
                                                        currentOsVersion));

                                continue;
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        LogUtils.WriteTrace(DateTime.UtcNow, string.Format(
                                                @"PathUtil: Exception occurred when checking whether patch {0} (applicable to OS version {1}) is applicable to current OS version, skip version checking and continue installing : {2}",
                                                patch.Name,
                                                osVersionAppliesTo,
                                                e));
                    }
                }

                // Read command line.
                if (nodeIterator.Current.SelectSingleNode(CommandLineXPath) == null)
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "Command value attribute needed for Patch '{0}'.", patch.Name));

                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Command value attribute needed for Patch '{0}'.", patch.Name));
                }

                patch.CommandLine = nodeIterator.Current.SelectSingleNode(CommandLineXPath).Value.Trim();

                if (string.IsNullOrEmpty(patch.CommandLine))
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "Command value can't be empty for Patch '{0}'.", patch.Name));

                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Command value can't be empty for Patch '{0}'.", patch.Name));
                }

                // Read log path (optional element).
                patch.LogFolderPath = null;
                if (nodeIterator.Current.SelectSingleNode(LogFolderPathXPath) != null)
                {
                    string logFolderPath = nodeIterator.Current.SelectSingleNode(LogFolderPathXPath).Value.Trim();

                    if (!string.IsNullOrEmpty(logFolderPath))
                    {
                        patch.LogFolderPath = logFolderPath;
                    }
                }

                // Read registry settings
                List <PatchRegistry> registrySettings = new List <PatchRegistry>();
                XPathNodeIterator    iterator         = nodeIterator.Current.Select(PatchRegistryXPath);
                while (iterator.MoveNext())
                {
                    string keyName       = ReadPatchRegistryAttribute(iterator, patch.Name, RegistryKeyNameAttribute, true);
                    string valueName     = ReadPatchRegistryAttribute(iterator, patch.Name, RegistryValueNameAttribute, true);
                    string expectedValue = ReadPatchRegistryAttribute(iterator, patch.Name, RegistryExpectedValueAttribute, false);

                    PatchRegistry registrySetting = new PatchRegistry
                    {
                        RegistryKeyName   = keyName,
                        RegistryValueName = valueName,
                        ExpectedValue     = expectedValue,
                    };
                    registrySettings.Add(registrySetting);
                }

                // We need at least one registry setting to confirm the patch is installed.
                if (!registrySettings.Any())
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, string.Format(CultureInfo.InvariantCulture, "There is no registry setting found for Patch '{0}'", patch.Name));

                    throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "There is no registry setting found for Patch '{0}'", patch.Name));
                }

                patch.RegistrySettings = registrySettings;
                _patchMap.Add(patch.Name, patch);
            }
        }
Exemple #10
0
        /// <summary>
        /// Get uninstalled patches list
        /// </summary>
        /// <param name="patchConfiguration"></param>
        /// <returns></returns>
        private static IList <PatchConfiguration.Patch> GetUninstalledPatches(PatchConfiguration patchConfiguration)
        {
            List <PatchConfiguration.Patch> uninstalledPatches = new List <PatchConfiguration.Patch>();

            foreach (PatchConfiguration.Patch patch in patchConfiguration.PatchMap.Values)
            {
                LogUtils.WriteTrace(DateTime.UtcNow,
                                    string.Format("PatchUtil: check registry setting for patch '{0}'", patch.Name));

                if (patch.RegistrySettings == null)
                {
                    continue;
                }

                foreach (PatchConfiguration.PatchRegistry reg in patch.RegistrySettings)
                {
                    LogUtils.WriteTrace(DateTime.UtcNow, string.Format(@"PatchUtil: read registry value '{0}\{1}' for patch '{2}'",
                                                                       reg.RegistryKeyName, reg.RegistryValueName, patch.Name));

                    object value = Registry.GetValue(reg.RegistryKeyName, reg.RegistryValueName, null);

                    if (value == null)
                    {
                        LogUtils.WriteTrace(DateTime.UtcNow,
                                            string.Format(
                                                @"PatchUtil: registry value '{0}\{1}' does not exist or has null value. Need to install patch '{2}'",
                                                reg.RegistryKeyName, reg.RegistryValueName, patch.Name));

                        // PatchConfiguration doesn't allow the same patch be defined multiple times. No need to check it here again.
                        uninstalledPatches.Add(patch);

                        // Go to next patch
                        break;
                    }

                    // Compare registry value. We only support REG_DWORD and REG_SZ for now
                    Type valueType = value.GetType();

                    if (valueType == typeof(string))
                    {
                        string stringValue = value as string;

                        LogUtils.WriteTrace(DateTime.UtcNow,
                                            string.Format(
                                                @"PatchUtil: registry value '{0}\{1}' has string value: '{2}'",
                                                reg.RegistryKeyName, reg.RegistryValueName, stringValue));

                        // If reg.ExpectedValue is null, we only validate there is a reg value there.
                        if (reg.ExpectedValue == null)
                        {
                            LogUtils.WriteTrace(DateTime.UtcNow, @"PatchUtil: skip validation because the ExpectedValue is null");
                        }
                        else if (!string.Equals(stringValue, reg.ExpectedValue, StringComparison.OrdinalIgnoreCase))
                        {
                            LogUtils.WriteTrace(DateTime.UtcNow, string.Format(
                                                    @"PatchUtil: registry value '{0}\{1}' does not match the expected value: '{2}'. Need to install patch '{3}'",
                                                    reg.RegistryKeyName,
                                                    reg.RegistryValueName,
                                                    reg.ExpectedValue,
                                                    patch.Name));

                            // Add to the list
                            uninstalledPatches.Add(patch);

                            // Go to next patch
                            break;
                        }
                    }
                    else if (valueType == typeof(int))
                    {
                        int intValue = (int)value;

                        LogUtils.WriteTrace(DateTime.UtcNow, string.Format(@"PatchUtil: registry value '{0}\{1}' has integer value: '{2}'",
                                                                           reg.RegistryKeyName, reg.RegistryValueName, intValue));

                        int expectedValue;
                        if (reg.ExpectedValue == null)
                        {
                            LogUtils.WriteTrace(DateTime.UtcNow, @"PatchUtil: skip validation because the ExpectedValue is null");
                        }
                        else if ((!int.TryParse(reg.ExpectedValue, out expectedValue)) || (expectedValue != intValue))
                        {
                            LogUtils.WriteTrace(DateTime.UtcNow, string.Format(
                                                    @"PatchUtil: registry value '{0}\{1}' does not match the expected value: '{2}'. Need to install patch '{3}'",
                                                    reg.RegistryKeyName,
                                                    reg.RegistryValueName,
                                                    reg.ExpectedValue,
                                                    patch.Name));

                            // Add to the list
                            uninstalledPatches.Add(patch);

                            // Go to next patch
                            break;
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "PatchUtil: Registry value type '{0}' is not supported.", valueType));
                    }
                }
            }

            return(uninstalledPatches);
        }