コード例 #1
0
        private void StopProcess(int pid)
        {
            WriteEvent("Stopping process " + pid);
            Process proc;

            try
            {
                proc = Process.GetProcessById(pid);
            }
            catch (ArgumentException)
            {
                WriteEvent("Process " + pid + " is already stopped");
                return;
            }

            WriteEvent("Send SIGINT " + pid);
            bool successful = SigIntHelper.SendSIGINTToProcess(proc, _descriptor.StopTimeout);

            if (successful)
            {
                WriteEvent("SIGINT to" + pid + " successful");
            }
            else
            {
                try
                {
                    WriteEvent("SIGINT to " + pid + " failed - Killing as fallback");
                    proc.Kill();
                }
                catch (ArgumentException)
                {
                    // Process already exited.
                }
            }
        }
コード例 #2
0
        private void StopProcessAndChildren(int pid)
        {
            var searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);

            foreach (var mo in searcher.Get())
            {
                StopProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
            }

            var proc = Process.GetProcessById(pid);

            WriteEvent("Send SIGINT " + process.Id);
            bool successful = SigIntHelper.SendSIGINTToProcess(proc, descriptor.StopTimeout);

            if (successful)
            {
                WriteEvent("SIGINT to" + process.Id + " successful");
            }
            else
            {
                try
                {
                    WriteEvent("SIGINT to " + process.Id + " failed - Killing as fallback");
                    proc.Kill();
                }
                catch (ArgumentException)
                {
                    // Process already exited.
                }
            }
        }
コード例 #3
0
ファイル: Main.cs プロジェクト: yst726/winsw
        // ReSharper disable once InconsistentNaming
        /// <summary>
        /// Runs the wrapper.
        /// </summary>
        /// <param name="_args">Arguments.</param>
        /// <param name="descriptor">Service descriptor. If null, it will be initialized within the method.
        ///                          In such case configs will be loaded from the XML Configuration File.</param>
        /// <exception cref="Exception">Any unhandled exception</exception>
        public static void Run(string[] _args, ServiceDescriptor?descriptor = null)
        {
            bool inCliMode = _args.Length > 0;

            // If descriptor is not specified, initialize the new one (and load configs from there)
            descriptor ??= new ServiceDescriptor();

            // Configure the wrapper-internal logging.
            // STDIN and STDOUT of the child process will be handled independently.
            InitLoggers(descriptor, inCliMode);

            if (!inCliMode)
            {
                Log.Debug("Starting WinSW in the service mode");
                Run(new WrapperService(descriptor));
                return;
            }

            Log.Debug("Starting WinSW in the CLI mode");

            if (_args.Length == 0)
            {
                printHelp();
                return;
            }

            // Get service info for the future use
            Win32Services svc = new WmiRoot().GetCollection <Win32Services>();
            Win32Service? s   = svc.Select(descriptor.Id);

            var args = new List <string>(Array.AsReadOnly(_args));

            if (args[0] == "/redirect")
            {
                // Redirect output
                // One might ask why we support this when the caller
                // can redirect the output easily. The answer is for supporting UAC.
                // On UAC-enabled Windows such as Vista, SCM operation requires
                // elevated privileges, thus winsw.exe needs to be launched
                // accordingly. This in turn limits what the caller can do,
                // and among other things it makes it difficult for the caller
                // to read stdout/stderr. Thus redirection becomes handy.
                var f = new FileStream(args[1], FileMode.Create);
                var w = new StreamWriter(f)
                {
                    AutoFlush = true
                };
                Console.SetOut(w);
                Console.SetError(w);

                var handle = f.SafeFileHandle;
                Kernel32.SetStdHandle(-11, handle); // set stdout
                Kernel32.SetStdHandle(-12, handle); // set stder

                args = args.GetRange(2, args.Count - 2);
            }

            bool elevated;

            if (args[0] == "/elevated")
            {
                elevated = true;

                _ = SigIntHelper.FreeConsole();
                _ = SigIntHelper.AttachConsole(SigIntHelper.ATTACH_PARENT_PROCESS);

                args = args.GetRange(1, args.Count - 1);
            }
            else if (Environment.OSVersion.Version.Major == 5)
            {
                // Windows XP
                elevated = true;
            }
            else
            {
                elevated = IsProcessElevated();
            }

            switch (args[0].ToLower())
            {
            case "install":
                Install();
                return;

            case "uninstall":
                Uninstall();
                return;

            case "start":
                Start();
                return;

            case "stop":
                Stop();
                return;

            case "restart":
                Restart();
                return;

            case "restart!":
                RestartSelf();
                return;

            case "status":
                Status();
                return;

            case "test":
                Test();
                return;

            case "testwait":
                TestWait();
                return;

            case "help":
            case "--help":
            case "-h":
            case "-?":
            case "/?":
                printHelp();
                return;

            case "version":
                printVersion();
                return;

            default:
                Console.WriteLine("Unknown command: " + args[0]);
                printAvailableCommandsInfo();
                throw new Exception("Unknown command: " + args[0]);
            }

            void Install()
            {
                if (!elevated)
                {
                    Elevate();
                    return;
                }

                Log.Info("Installing the service with id '" + descriptor.Id + "'");

                // Check if the service exists
                if (s != null)
                {
                    Console.WriteLine("Service with id '" + descriptor.Id + "' already exists");
                    Console.WriteLine("To install the service, delete the existing one or change service Id in the configuration file");
                    throw new Exception("Installation failure: Service with id '" + descriptor.Id + "' already exists");
                }

                string?username = null;
                string?password = null;
                bool   setallowlogonasaserviceright = false; // This variable is very readable.

                if (args.Count > 1 && args[1] == "/p")
                {
                    // we expected username/password on stdin
                    Console.Write("Username: "******"Password: "******"Set Account rights to allow log on as a service (y/n)?: ");
                    var keypressed = Console.ReadKey();
                    Console.WriteLine();
                    if (keypressed.Key == ConsoleKey.Y)
                    {
                        setallowlogonasaserviceright = true;
                    }
                }
                else
                {
                    if (descriptor.HasServiceAccount())
                    {
                        username = descriptor.ServiceAccountUser;
                        password = descriptor.ServiceAccountPassword;
                        setallowlogonasaserviceright = descriptor.AllowServiceAcountLogonRight;
                    }
                }

                if (setallowlogonasaserviceright)
                {
                    LogonAsAService.AddLogonAsAServiceRight(username !);
                }

                svc.Create(
                    descriptor.Id,
                    descriptor.Caption,
                    "\"" + descriptor.ExecutablePath + "\"",
                    ServiceType.OwnProcess,
                    ErrorControl.UserNotified,
                    descriptor.StartMode.ToString(),
                    descriptor.Interactive,
                    username,
                    password,
                    descriptor.ServiceDependencies);

                using ServiceManager scm = new ServiceManager();
                using Service sc         = scm.Open(descriptor.Id);

                sc.SetDescription(descriptor.Description);

                var actions = descriptor.FailureActions;

                if (actions.Count > 0)
                {
                    sc.SetFailureActions(descriptor.ResetFailureAfter, actions);
                }

                var isDelayedAutoStart = descriptor.StartMode == StartMode.Automatic && descriptor.DelayedAutoStart;

                if (isDelayedAutoStart)
                {
                    sc.SetDelayedAutoStart(true);
                }

                if (descriptor.SecurityDescriptor != null)
                {
                    // throws ArgumentException
                    RawSecurityDescriptor rawSecurityDescriptor = new RawSecurityDescriptor(descriptor.SecurityDescriptor);
                    byte[] securityDescriptorBytes = new byte[rawSecurityDescriptor.BinaryLength];
                    rawSecurityDescriptor.GetBinaryForm(securityDescriptorBytes, 0);
                    _ = Advapi32.SetServiceObjectSecurity(sc.Handle, SecurityInfos.DiscretionaryAcl, securityDescriptorBytes);
                }

                string eventLogSource = descriptor.Id;

                if (!EventLog.SourceExists(eventLogSource))
                {
                    EventLog.CreateEventSource(eventLogSource, "Application");
                }
            }

            void Uninstall()
            {
                if (!elevated)
                {
                    Elevate();
                    return;
                }

                Log.Info("Uninstalling the service with id '" + descriptor.Id + "'");
                if (s is null)
                {
                    Log.Warn("The service with id '" + descriptor.Id + "' does not exist. Nothing to uninstall");
                    return; // there's no such service, so consider it already uninstalled
                }

                if (s.Started)
                {
                    // We could fail the opeartion here, but it would be an incompatible change.
                    // So it is just a warning
                    Log.Warn("The service with id '" + descriptor.Id + "' is running. It may be impossible to uninstall it");
                }

                try
                {
                    s.Delete();
                }
                catch (WmiException e)
                {
                    if (e.ErrorCode == ReturnValue.ServiceMarkedForDeletion)
                    {
                        Log.Error("Failed to uninstall the service with id '" + descriptor.Id + "'"
                                  + ". It has been marked for deletion.");

                        // TODO: change the default behavior to Error?
                        return; // it's already uninstalled, so consider it a success
                    }
                    else
                    {
                        Log.Fatal("Failed to uninstall the service with id '" + descriptor.Id + "'. WMI Error code is '" + e.ErrorCode + "'");
                    }

                    throw e;
                }
            }

            void Start()
            {
                if (!elevated)
                {
                    Elevate();
                    return;
                }

                Log.Info("Starting the service with id '" + descriptor.Id + "'");
                if (s is null)
                {
                    ThrowNoSuchService();
                }

                try
                {
                    s.StartService();
                }
                catch (WmiException e)
                {
                    if (e.ErrorCode == ReturnValue.ServiceAlreadyRunning)
                    {
                        Log.Info($"The service with ID '{descriptor.Id}' has already been started");
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            void Stop()
            {
                if (!elevated)
                {
                    Elevate();
                    return;
                }

                Log.Info("Stopping the service with id '" + descriptor.Id + "'");
                if (s is null)
                {
                    ThrowNoSuchService();
                }

                try
                {
                    s.StopService();
                }
                catch (WmiException e)
                {
                    if (e.ErrorCode == ReturnValue.ServiceCannotAcceptControl)
                    {
                        Log.Info($"The service with ID '{descriptor.Id}' is not running");
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            void Restart()
            {
                if (!elevated)
                {
                    Elevate();
                    return;
                }

                Log.Info("Restarting the service with id '" + descriptor.Id + "'");
                if (s is null)
                {
                    ThrowNoSuchService();
                }

                if (s.Started)
                {
                    s.StopService();
                }

                while (s.Started)
                {
                    Thread.Sleep(1000);
                    s = svc.Select(descriptor.Id) !;
                }

                s.StartService();
            }

            void RestartSelf()
            {
                if (!elevated)
                {
                    throw new UnauthorizedAccessException("Access is denied.");
                }

                Log.Info("Restarting the service with id '" + descriptor.Id + "'");

                // run restart from another process group. see README.md for why this is useful.

                bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, Kernel32.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);