[Mono-list] Running mono-service in linux as non-root

alexc mono-forum at czutro.de
Mon Dec 1 17:21:47 UTC 2014


I need to run a .net C# application (developed on a Windows system) as a
service/daemon on an embedded system with a minimal Ubuntu installation (no
X, no servers except ssh, only relevant software).  I created an
`/etc/init.d` script containing the line

    mono-service my-.net-app.exe service

and this worked well.  There is also an option to start the application
interactively (for debugging purposes) with 

    mono my-.net-app.exe interactive

The last argument is an argument for the .NET application telling it if it's
running as service.  This was implemented roughly this way:

    private static void Main(string[] args){
      if(args.Any() && args[0] != null && args[0] == "service"){
        ServiceBase.Run(new[] {(ServiceBase) new MyService()});
      }else{
        try{
          Console.Write("starting app");
          if(StartWork()){
            Console.Write("press any key to exit");
            Console.ReadKey();
          }else{
            Console.WriteLine("starting app failed");
          }
        } // end try
        finally{
          StopWork();
          Console.WriteLine("finished app");
        }
      } // end else
    ...
    } // end Main

    public class MyService : ServiceBase{
      static private Thread _worker;

      protected override void OnStart(string[] args){
        _worker = new Thread(() => Program.StartWork(asService: true)); //
this asService tells StartWork to not produce console output
        _worker.Start();
      }

      protected override void OnStop(){
        Program.StopWork();
        _worker.Join(1000);
      }
    }

The purpose of this implementation was to allow the application to die
gracefully (i.e. to execute `StopWork()`) upon sending `SIGTERM` on the
linux machine.

For security reasons, I need to be able to run the service as non-root.  I
created a new user and made it owner of the directories where the
application writes its log files and added it to various groups to give it
access to required device files.  Then, root would start the application as

    sudo -u newuser mono-service my-.net-app.exe service

or

    sudo -u newuser mono my-.net-app.exe interactive

The second option with `mono` works well, but the first one with
`mono-service` doesn't (see error message below).  Since it works with
`mono`, I'm confident that the user `newuser` has appropriate rights to
access all relevant files and devices.  I wonder whether `mono-service` has
been conceived as a root-only application.

I could also live with using the `mono` option and suppressing the console
output, like this:

    private static void Main(string[] args){
      try{
        Console.Write("starting app");
        if(StartWork(consoleoutput)){ // true or false depending on whether
the service argument was given
          Console.Write("press any key to exit");
          Console.ReadKey();
        }else{
          Console.WriteLine("starting app failed");
        }
      } // end try
      finally{
        StopWork();
        Console.WriteLine("finished app");
      }
    ...
    } // end Main

but then, when I kill the service (i.e. send SIGTERM to the `mono` process),
it stops the .net application immediately without allowing it to execute the
`finally` block.

**Finally, my question** is whether someone has an idea why `mono-service`
is failing when not started as root.  The error message is the following
and, as I mentioned before, it doesn't exist when I use `mono` instead of
`mono-service`.

    ERROR Program [4] [15:03:06.795 01/12/14] Error in Main!
    FluentNHibernate.Cfg.FluentConfigurationException: An invalid or
incomplete configuration was used while creating a SessionFactory. Check
PotentialReasons collection, and InnerException for more detail.

    ---> NHibernate.HibernateException: Could not create the driver from
SAFEmine.DataStore.Database.MonoSqliteDriver, SAFEmine.DataStore,
Version=1.3.0.6, Culture=neutral, PublicKeyToken=null. --->
System.Reflection.TargetInvocationException: Exception has been thrown by
the target of an invocation. ---> NHibernate.HibernateException: The
IDbCommand and IDbConnection implementation in the assembly Mono.Data.Sqlite
could not be found. Ensure that the assembly Mono.Data.Sqlite is located in
the application directory or in the Global Assembly Cache. If the assembly
is in the GAC, use <qualifyAssembly/> element in the application
configuration file to specify the full name of the assembly.
    at NHibernate.Driver.ReflectionBasedDriver..ctor (System.String
providerInvariantName, System.String driverAssemblyName, System.String
connectionTypeName, System.String commandTypeName) [0x00000] in <filename
unknown>:0 
    at NHibernate.Driver.ReflectionBasedDriver..ctor (System.String
driverAssemblyName, System.String connectionTypeName, System.String
commandTypeName) [0x00000] in <filename unknown>:0 
    at SAFEmine.DataStore.Database.MonoSqliteDriver..ctor () [0x00000] in
<filename unknown>:0 
    at (wrapper managed-to-native)
System.Reflection.MonoCMethod:InternalInvoke
(System.Reflection.MonoCMethod,object,object[],System.Exception&)
    at System.Reflection.MonoCMethod.InternalInvoke (System.Object obj,
System.Object[] parameters) [0x00000] in <filename unknown>:0 

Alternatively, if I settle for `mono` instead of `mono-service`, is there a
way to catch a `SIGTERM` from within the .net application and to die
gracefully?  I tried this:
https://github.com/ServiceStack/ServiceStack/wiki/Run-ServiceStack-as-a-daemon-on-Linux
, but the code wouldn't compile on Visual Studio saying that the `using
Mono.Unix;` and `using Mono.Unix.Native` lines were invalid.  I also
installed Mono on Windows and tried to use the Mono compiler, but it
complained about the same thing.



--
View this message in context: http://mono.1490590.n4.nabble.com/Running-mono-service-in-linux-as-non-root-tp4664858.html
Sent from the Mono - General mailing list archive at Nabble.com.


More information about the Mono-list mailing list