Search code examples
c#linuxasp.net-coresystemdpid

How to create PID file from ASP.NET Core application under Linux


I wrote an ASP.NET Core application (.NET 5 actually) and run it under Linux as a systemd service (following this Microsoft guide). Then I wanted to monitor this application with monit application. This application requires services to create a PID file so they are easily discoverable. I decided I'll write a Q&A-style question to help others achieve the same faster.


Solution

  • Note: the solution described here is a part of my NuGet package with open source available on github in case you prefer to achieve your goal the package way.

    IHostedService

    There are a few ways in .NET Core to run a background task. IHostedService is one of them that contains StartAsync method which, after a service is registered, is called on the startup. The interface also defines StopAsync which is called on a graceful shutdown. Visit this Microsoft page for more details on IHostedService.

    Knowing that we can create our own IHostedService that will manage the PID file of our application:

    public class PidFileHostedService : IHostedService
    {
        private readonly ILogger<PidFileHostedService> logger;
        private readonly IConfiguration configuration;
        private readonly IWebHostEnvironment env;
    
        private bool isPidFileCreated = false;
        private string pidFile = string.Empty;
    
        public PidFileHostedService(ILogger<PidFileHostedService> logger,
            IConfiguration configuration,
            IWebHostEnvironment env)
        {
            this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
            this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
            this.env = env ?? throw new ArgumentNullException(nameof(env));
        }
    
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            try
            {
                if (env.IsDevelopment())
                    return;
    
                pidFile = configuration.GetValue<string>("PidFile");
    
                if (string.IsNullOrWhiteSpace(pidFile))
                    return;
    
                await WritePidFile();
                
                isPidFileCreated = true;
            }
            catch (Exception ex)
            {
                logger.LogError(ex, $"Unexpected error when starting {nameof(PidFileHostedService)}", null);
            }
        }
    
        private async Task WritePidFile()
        {
            var processId = Environment.ProcessId.ToString();
            await File.WriteAllTextAsync(pidFile, processId);
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            try
            {
                if (isPidFileCreated)
                    File.Delete(pidFile);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Unexpected error when deleting PID file", null);
            }
            
            return Task.CompletedTask;
        }
    }
    

    This service assumes there is the PidFile property in the appsettings.json file of an application. Do not forget to register the service in the Startup.cs:

    services.AddHostedService<PidFileHostedService>();
    

    Service file changes

    As I wrote in the question I used Microsoft guide on how to make my app run as a systemd service. Now, after the changes, the service has to have necessary permissions to the directory in which it will create the PID file. Creation of this directory with necessary permission can be delegated to systemd. It is enough to add this line to the service file under [Service] section:

    RuntimeDirectory=helloapp
    

    Thanks to this line systemd will create helloapp subdirectory in /var/run on every service start with necessary permissions. It is also a good idea to add the following line to the same section which will tell systemd to double check the PID file is deleted after the app is stopped:

    PIDFile=/var/run/helloapp/helloapp.pid