Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
RemoveModuleCommand.cs383 linesDownload Raw Back to Modules
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Diagnostics.CodeAnalysis;7using System.Linq;8using System.Management.Automation;9using System.Management.Automation.Internal;10using System.Management.Automation.Runspaces;11using System.Reflection;12 13using Dbg = System.Management.Automation.Diagnostics;14 15//16// Now define the set of commands for manipulating modules.17//18 19namespace Microsoft.PowerShell.Commands20{21    #region Remove-Module22    /// <summary>23    /// Implements a cmdlet that gets the list of loaded modules...24    /// </summary>25    [Cmdlet(VerbsCommon.Remove, "Module", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096802")]26    public sealed class RemoveModuleCommand : ModuleCmdletBase27    {28        /// <summary>29        /// This parameter specifies the current pipeline object.30        /// </summary>31        [Parameter(Mandatory = true, ParameterSetName = "name", ValueFromPipeline = true, Position = 0)]32        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]33        public string[] Name34        {35            get { return _name; }36 37            set { _name = value; }38        }39 40        private string[] _name = Array.Empty<string>();41 42        /// <summary>43        /// This parameter specifies the current pipeline object.44        /// </summary>45        [Parameter(Mandatory = true, ParameterSetName = "FullyQualifiedName", ValueFromPipeline = true, Position = 0)]46        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]47        public ModuleSpecification[] FullyQualifiedName { get; set; }48 49        /// <summary>50        /// This parameter specifies the current pipeline object.51        /// </summary>52        [Parameter(Mandatory = true, ParameterSetName = "ModuleInfo", ValueFromPipeline = true, Position = 0)]53        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")]54        public PSModuleInfo[] ModuleInfo55        {56            get { return _moduleInfo; }57 58            set { _moduleInfo = value; }59        }60 61        private PSModuleInfo[] _moduleInfo = Array.Empty<PSModuleInfo>();62 63        /// <summary>64        /// If provided, this parameter will allow readonly modules to be removed.65        /// </summary>66        [Parameter]67        public SwitchParameter Force68        {69            get { return BaseForce; }70 71            set { BaseForce = value; }72        }73 74        private int _numberRemoved = 0;  // Maintains a count of the number of modules removed...75 76        /// <summary>77        /// Remove the specified modules. Modules can be specified either through a ModuleInfo or a name.78        /// </summary>79        protected override void ProcessRecord()80        {81            // This dictionary has the list of modules to be removed.82            // Key - Module specified as a parameter to Remove-Module83            // Values - List of all modules that need to be removed for this key (includes all nested modules of this module)84            Dictionary<PSModuleInfo, List<PSModuleInfo>> modulesToRemove = new Dictionary<PSModuleInfo, List<PSModuleInfo>>();85 86            foreach (var m in Context.Modules.GetModules(_name, false))87            {88                modulesToRemove.Add(m, new List<PSModuleInfo> { m });89            }90 91            if (FullyQualifiedName != null)92            {93                // TODO:94                // Paths in the module name may fail here because95                // they the wrong directory separator or are relative.96                // Fix with the code below:97                // FullyQualifiedName = FullyQualifiedName.Select(ms => ms.WithNormalizedName(Context, SessionState.Path.CurrentLocation.Path)).ToArray();98                foreach (var m in Context.Modules.GetModules(FullyQualifiedName, false))99                {100                    modulesToRemove.Add(m, new List<PSModuleInfo> { m });101                }102            }103 104            foreach (var m in _moduleInfo)105            {106                modulesToRemove.Add(m, new List<PSModuleInfo> { m });107            }108 109            // Add any of the child modules of a manifests to the list of modules to remove...110            Dictionary<PSModuleInfo, List<PSModuleInfo>> nestedModules = new Dictionary<PSModuleInfo, List<PSModuleInfo>>();111            foreach (var entry in modulesToRemove)112            {113                var module = entry.Key;114                if (module.NestedModules != null && module.NestedModules.Count > 0)115                {116                    List<PSModuleInfo> nestedModulesWithNoCircularReference = new List<PSModuleInfo>();117                    GetAllNestedModules(module, ref nestedModulesWithNoCircularReference);118                    nestedModules.Add(module, nestedModulesWithNoCircularReference);119                }120            }121 122            // dont add duplicates to our original modulesToRemove list..so that the123            // evaluation loop below will not duplicate in case of WriteError and WriteWarning.124            // A global list of modules to be removed is maintained for this purpose125            HashSet<PSModuleInfo> globalListOfModules = new HashSet<PSModuleInfo>(new PSModuleInfoComparer());126 127            if (nestedModules.Count > 0)128            {129                foreach (var entry in nestedModules)130                {131                    List<PSModuleInfo> values = null;132                    if (modulesToRemove.TryGetValue(entry.Key, out values))133                    {134                        foreach (var module in entry.Value)135                        {136                            if (!globalListOfModules.Contains(module))137                            {138                                values.Add(module);139                                globalListOfModules.Add(module);140                            }141                        }142                    }143                }144            }145 146            // Check the list of modules to remove and exclude those that cannot or should not be removed147            Dictionary<PSModuleInfo, List<PSModuleInfo>> actualModulesToRemove = new Dictionary<PSModuleInfo, List<PSModuleInfo>>();148 149            // We want to remove the modules starting from the nested modules150            // If we start from the parent module, the nested modules do not get removed and are left orphaned in the parent modules's sessionstate.151            foreach (var entry in modulesToRemove)152            {153                List<PSModuleInfo> moduleList = new List<PSModuleInfo>();154                for (int i = entry.Value.Count - 1; i >= 0; i--)155                {156                    PSModuleInfo module = entry.Value[i];157                    // See if the module is constant...158                    if (module.AccessMode == ModuleAccessMode.Constant)159                    {160                        string message = StringUtil.Format(Modules.ModuleIsConstant, module.Name);161                        InvalidOperationException moduleNotRemoved = new InvalidOperationException(message);162                        ErrorRecord er = new ErrorRecord(moduleNotRemoved, "Modules_ModuleIsConstant",163                                                         ErrorCategory.PermissionDenied, module);164                        WriteError(er);165                        continue;166                    }167 168                    // See if the module is readonly...169                    if (module.AccessMode == ModuleAccessMode.ReadOnly && !BaseForce)170                    {171                        string message = StringUtil.Format(Modules.ModuleIsReadOnly, module.Name);172 173                        if (InitialSessionState.IsConstantEngineModule(module.Name))174                        {175                            WriteWarning(message);176                        }177                        else178                        {179                            InvalidOperationException moduleNotRemoved = new InvalidOperationException(message);180                            ErrorRecord er = new ErrorRecord(moduleNotRemoved, "Modules_ModuleIsReadOnly",181                                                             ErrorCategory.PermissionDenied, module);182                            WriteError(er);183                        }184 185                        continue;186                    }187 188                    if (!ShouldProcess(StringUtil.Format(Modules.ConfirmRemoveModule, module.Name, module.Path)))189                    {190                        continue;191                    }192 193                    // If this module provides the current session drive, then we cannot remove it.194                    // Abort this command since we don't want to do a partial removal of a module manifest.195                    if (ModuleProvidesCurrentSessionDrive(module))196                    {197                        if (InitialSessionState.IsEngineModule(module.Name))198                        {199                            if (!BaseForce)200                            {201                                string message = StringUtil.Format(Modules.CoreModuleCannotBeRemoved, module.Name);202                                this.WriteWarning(message);203                            }204 205                            continue;206                        }207                        // Specify the overall module name if there is only one.208                        // Otherwise specify the particular module name.209                        string moduleName = (_name.Length == 1) ? _name[0] : module.Name;210 211                        PSInvalidOperationException invalidOperation =212                            PSTraceSource.NewInvalidOperationException(213                                Modules.ModuleDriveInUse,214                                moduleName);215 216                        throw (invalidOperation);217                    }218 219                    // Add module to remove list.220                    moduleList.Add(module);221                }222 223                actualModulesToRemove[entry.Key] = moduleList;224            }225 226            // Now remove the modules, first checking the RequiredModules dependencies227            Dictionary<PSModuleInfo, List<PSModuleInfo>> requiredDependencies = GetRequiredDependencies();228 229            foreach (var entry in actualModulesToRemove)230            {231                foreach (var module in entry.Value)232                {233                    if (!BaseForce)234                    {235                        List<PSModuleInfo> requiredBy = null;236 237                        if (requiredDependencies.TryGetValue(module, out requiredBy))238                        {239                            for (int i = requiredBy.Count - 1; i >= 0; i--)240                            {241                                if (actualModulesToRemove.ContainsKey(requiredBy[i]))242                                {243                                    requiredBy.RemoveAt(i);244                                }245                            }246 247                            if (requiredBy.Count > 0)248                            {249                                string message = StringUtil.Format(Modules.ModuleIsRequired, module.Name, requiredBy[0].Name);250                                InvalidOperationException moduleNotRemoved = new InvalidOperationException(message);251                                ErrorRecord er = new ErrorRecord(moduleNotRemoved, "Modules_ModuleIsRequired",252                                                                 ErrorCategory.PermissionDenied, module);253                                WriteError(er);254                                continue;255                            }256                        }257                    }258 259                    _numberRemoved++;260 261                    this.RemoveModule(module, entry.Key.Name);262                }263            }264        }265 266        private bool ModuleProvidesCurrentSessionDrive(PSModuleInfo module)267        {268            if (module.ModuleType == ModuleType.Binary)269            {270                Dictionary<string, List<ProviderInfo>> providers = Context.TopLevelSessionState.Providers;271                foreach (KeyValuePair<string, List<ProviderInfo>> pList in providers)272                {273                    Dbg.Assert(pList.Value != null, "There should never be a null list of entries in the provider table");274                    foreach (ProviderInfo pInfo in pList.Value)275                    {276                        string implTypeAssemblyLocation = pInfo.ImplementingType.Assembly.Location;277                        if (implTypeAssemblyLocation.Equals(module.Path, StringComparison.OrdinalIgnoreCase))278                        {279                            foreach (PSDriveInfo dInfo in Context.TopLevelSessionState.GetDrivesForProvider(pInfo.FullName))280                            {281                                if (dInfo == SessionState.Drive.Current)282                                {283                                    return true;284                                }285                            }286                        }287                    }288                }289            }290 291            return false;292        }293 294        private static void GetAllNestedModules(PSModuleInfo module, ref List<PSModuleInfo> nestedModulesWithNoCircularReference)295        {296            List<PSModuleInfo> nestedModules = new List<PSModuleInfo>();297            if (module.NestedModules != null && module.NestedModules.Count > 0)298            {299                foreach (var nestedModule in module.NestedModules)300                {301                    if (!nestedModulesWithNoCircularReference.Contains(nestedModule))302                    {303                        nestedModulesWithNoCircularReference.Add(nestedModule);304                        nestedModules.Add(nestedModule);305                    }306                }307 308                foreach (PSModuleInfo child in nestedModules)309                {310                    GetAllNestedModules(child, ref nestedModulesWithNoCircularReference);311                }312            }313        }314 315        /// <summary>316        /// Returns a map from a module to the list of modules that require it.317        /// </summary>318        private Dictionary<PSModuleInfo, List<PSModuleInfo>> GetRequiredDependencies()319        {320            Dictionary<PSModuleInfo, List<PSModuleInfo>> requiredDependencies = new Dictionary<PSModuleInfo, List<PSModuleInfo>>();321 322            foreach (PSModuleInfo module in Context.Modules.GetModules(new string[] { "*" }, false))323            {324                foreach (PSModuleInfo requiredModule in module.RequiredModules)325                {326                    List<PSModuleInfo> requiredByList = null;327 328                    if (!requiredDependencies.TryGetValue(requiredModule, out requiredByList))329                    {330                        requiredDependencies.Add(requiredModule, requiredByList = new List<PSModuleInfo>());331                    }332 333                    requiredByList.Add(module);334                }335            }336 337            return requiredDependencies;338        }339 340        /// <summary>341        /// Reports an error if no modules were removed...342        /// </summary>343        protected override void EndProcessing()344        {345            // Write an error record if specific modules were to be removed.346            // By specific, we mean either a name sting with no wildcards or347            // or a PSModuleInfo object. If the removal request only includes patterns348            // then we won't write the error.349 350            if (_numberRemoved == 0 && !MyInvocation.BoundParameters.ContainsKey("WhatIf"))351            {352                bool hasWildcards = true;353                bool isEngineModule = true;354                foreach (string n in _name)355                {356                    if (!InitialSessionState.IsEngineModule(n))357                    {358                        isEngineModule = false;359                    }360 361                    if (!WildcardPattern.ContainsWildcardCharacters(n))362                        hasWildcards = false;363                }364 365                if (FullyQualifiedName != null && (FullyQualifiedName.Any(static moduleSpec => !InitialSessionState.IsEngineModule(moduleSpec.Name))))366                {367                    isEngineModule = false;368                }369 370                if (!isEngineModule && (!hasWildcards || _moduleInfo.Length != 0 || (FullyQualifiedName != null && FullyQualifiedName.Length != 0)))371                {372                    string message = StringUtil.Format(Modules.NoModulesRemoved);373                    InvalidOperationException invalidOp = new InvalidOperationException(message);374                    ErrorRecord er = new ErrorRecord(invalidOp, "Modules_NoModulesRemoved",375                        ErrorCategory.ResourceUnavailable, null);376                    WriteError(er);377                }378            }379        }380    }381    #endregion Remove-Module382}383