0

I want to restart Kestrel (asp.net core 3.1) via an authorized http request. Kestrel is contained in a Windows service configured to restart itself automatically upon failure. As far as i know the simplest way is to return an exit code different from 0, windows will do all the rest.

In light of that, the code I wrote is actually simple:

        public MaintenanceController(IHostApplicationLifetime hostLifetime)
        {
            this.HostLifetime = hostLifetime ?? throw new ArgumentNullException(nameof(hostLifetime));
        }

        [HttpPost]
        [Route("service/restart")]
        [Authorize("AdminOnly")]
        public ActionResult RestartService()
        {
                Program.IsRestart = true; //see below
                this.HostLifetime.StopApplication();

                //this does not work!
                if (HostLifetime is WindowsServiceLifetime lifetime)
                {
                    lifetime.ExitCode = 1;
                }
                //neither this!
                Environment.ExitCode = 1;

                return Ok();
        } 

The only way to make windows restarts the service is just actually call

Environment.Exit(1);

without HostLifetime.StopApplication();

But the issue with Environment.Exit called alone is it causes a non graceful shutdown, something I want absolutely to avoid.

Another approach I tried, to force an exit code different from 0, which did not work, was to put in Startup.cs:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
        {
            [...]

            applicationLifetime.ApplicationStopped.Register(() =>
            {
                //this can be set in the controller
                if (Program.IsRestart)
                {
                    Environment.Exit(1); 
                }
            });
        }

but, when called after ApplicationStopped, Environment.Exit(1) does apparently nothing, in fact even inside event viewer there is no trace of the services' shutdown with error, so windows does nothing.

UPDATE

Going back to classic, I changed the Main entry point to return an int and returned 1. Still windows does not restart the service nor a failure entry is written to event viewer. It looks like is always stopped gracefully

user1624411
  • 398
  • 4
  • 6

2 Answers2

0

Everything is working like it should. You may want to consider writing a wrapper batch file or watcher process.

Your watcher would wait for the process to exit, and if a flag file (e.g. .staydown) is present, it would exit gracefully. If the file doesn't exist, it would then restart the process.

In your RestartService method, use Environment.Exit(0) which would be a graceful shutdown, but since the .staydown file doesn't exist, your watcher would then restart the server. Your watcher would only stop running if the .staydown file exists and then the server is stopped, or the Windows Service itself is stopped.

When your app starts up, be sure to delete .staydown if present.

If you have node installed, you might be able to use a utility like forever in place of the watcher batch file.

Jason Armstrong
  • 1,058
  • 9
  • 17
  • Environment.Exit(0) will cause the environment stop immediately, not a graceful shutdown. In fact, the return Ok(); is not executed and the httprequest terminated abnormally. The error code 0 will just tell windows it was graceful when actually it wasn't. But apart from that I'm looking for an ideal solution without extra process – user1624411 May 29 '20 at 18:46
0

You need to check the 'Enable actions for stops with errors' on the service, or run 'sc failureflag "My Service" 1'

More info here: https://blog.stephencleary.com/2020/06/servicebase-gotcha-recovery-actions.html

(this is a very strange behaviour on Windows part I think, honouring exit codes should be default!)