context: /*
   * monod.cs: Mono daemon for running services based on System.ServiceProcess
   *
   * Author:
   *   Joerg Rosenkranz (joergr@voelcker.com)
   *   Miguel de Icaza (miguel@novell.com)
   *
   * (C) 2005 Voelcker Informatik AG
   * (C) 2005 Novell Inc
   */
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Reflection;
  using Mono.Unix;
  using Mono.Unix.Native;
  using System.ServiceProcess;
  using System.Threading;
  using System.Runtime.InteropServices;
  
  class MonoServiceRunner : MarshalByRefObject
  {
  	string assembly, name, logname;
  	string[] args;
  	
  	static void info (string prefix, string format, params object [] args)
  	{
  		Syscall.syslog (SyslogLevel.LOG_INFO, String.Format ("{0}: {1}", prefix, String.Format (format, args)));
  	}
  	
  	static void error (string prefix, string format, params object [] args)
  	{
  		Syscall.syslog (SyslogLevel.LOG_ERR, String.Format ("{0}: {1}", prefix, String.Format (format, args)));
  	}
  	
  	static void Usage ()
  	{
  		Console.Error.WriteLine (
  					 "Usage is:\n" +
  					 "mono-service [-d:DIRECTORY] [-l:LOCKFILE] [-n:NAME] [-m:LOGNAME] service.exe\n");
  		Environment.Exit (1);
  	}
  
  	static void call (object o, string method, object [] arg)
  	{
  		MethodInfo m = o.GetType ().GetMethod (method, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  		if (arg != null)
  			m.Invoke (o, new object [1] { arg });
  		else
  			m.Invoke (o, null);
  	}
  	
  	static int Main (string [] args)
  	{
  		string assembly = null;
  		string directory = null;
  		string lockfile = null;
  		string name = null;
  		string logname = null;
  		var assebmlyArgs = new List<string>();
  
  		foreach (string s in args){
  			if (s.Length > 3 && s [0] == '-' && s [2] == ':'){
  				string arg = s.Substring (3);
  
  				switch (Char.ToLower (s [1])){
  				case 'd': directory = arg; break;
  				case 'l': lockfile = arg; break;
  				case 'n': name = arg; break;
  				case 'm': logname = arg; break;
  				default: Usage (); break;
  				}
  			} else {
  				if (assembly != null)
  				{
  					assebmlyArgs.Add(s);
  				}
  				else
  				{
  					assembly = s;
  				}
  			}
  		}
  
  		if (logname == null)
  			logname = assembly;
  
  		if (assembly == null){
  			error (logname, "Assembly name is missing");
  			Usage ();
  		}
  		
  		if (directory != null){
  			if (Syscall.chdir (directory) != 0){
  				error (logname, "Could not change to directory {0}", directory);
  				return 1;
  			}
  		}
  		
  		// Use lockfile to allow only one instance
  		if (lockfile == null)
  			lockfile = String.Format ("/tmp/{0}.lock", Path.GetFileName (assembly));
  
  		int lfp = Syscall.open (lockfile, OpenFlags.O_RDWR|OpenFlags.O_CREAT|OpenFlags.O_EXCL, 
  			FilePermissions.S_IRUSR|FilePermissions.S_IWUSR|FilePermissions.S_IRGRP);
  
  		if (lfp<0)  {
  		        // Provide some useful info
  			if (File.Exists (lockfile))
  				error (logname, String.Format ("Lock file already exists: {0}", lockfile));
  			else 
  				error (logname, String.Format ("Cannot open/create lock file exclusively: {0}", lockfile));
  			return 1;
  		}
  	
  		if (Syscall.lockf(lfp, LockfCommand.F_TLOCK,0)<0)  {
  			info (logname, "Daemon is already running.");
  			return 0;
  		}
  		
  		try {
  			// Write pid to lock file
  			string pid = Syscall.getpid ().ToString () + Environment.NewLine;
  			IntPtr buf = Marshal.StringToCoTaskMemAnsi (pid);
  			Syscall.write (lfp, buf, (ulong)pid.Length);
  			Marshal.FreeCoTaskMem (buf);
  	
  			// Create new AppDomain to run service
  			AppDomainSetup setup = new AppDomainSetup ();
  			setup.ApplicationBase = Environment.CurrentDirectory;
  			setup.ConfigurationFile = Path.Combine (Environment.CurrentDirectory, assembly + ".config");
  			setup.ApplicationName = logname;
  			
  			AppDomain newDomain = AppDomain.CreateDomain (logname, AppDomain.CurrentDomain.Evidence, setup);
  			MonoServiceRunner rnr = newDomain.CreateInstanceAndUnwrap(
  				typeof (MonoServiceRunner).Assembly.FullName,
  				typeof (MonoServiceRunner).FullName,
  				true,
  				BindingFlags.Default,
  				null,
  				new object [] {assembly, name, logname, assebmlyArgs.ToArray()},
  				null, null, null) as MonoServiceRunner;
  				
  			if (rnr == null) {
  				error (logname, "Internal Mono Error: Could not create MonoServiceRunner.");
  				return 1;
  			}
  	
  			return rnr.StartService ();
  		} finally {
  			// Remove lock file when done
  			if (File.Exists(lockfile))
  				File.Delete (lockfile);
  		}
  	}
  	
  	public MonoServiceRunner (string assembly, string name, string logname, string[] args)
  	{
  		this.assembly = assembly;
  		this.name = name;
  		this.logname = logname;
  		this.args = args;
  	}
  	
  	public int StartService ()
  	{
  		try	{
  			// Load service assembly
  			Assembly a = null;
  			
  			try {
  				a = Assembly.LoadFrom (assembly);
  			} catch (FileNotFoundException) {
  				error (logname, "Could not find assembly {0}", assembly);
  				return 1;
  			} catch (BadImageFormatException){
  				error (logname, "File {0} is not a valid assembly", assembly);
  				return 1;
  			} catch { }
  			
  			if (a == null){
  				error (logname, "Could not load assembly {0}", assembly);
  				return 1;
  			}
  			
  			if (a.EntryPoint == null){
  				error (logname, "Entry point not defined in service");
  				return 1;
  			}
  			
  			// Hook up RunService callback
  			Type cbType = Type.GetType ("System.ServiceProcess.ServiceBase+RunServiceCallback, System.ServiceProcess");
  			if (cbType == null){
  				error (logname, "Internal Mono Error: Could not find RunServiceCallback in ServiceBase");
  				return 1;			
  			}
  			
  			FieldInfo fi = typeof (ServiceBase).GetField ("RunService", BindingFlags.Static | BindingFlags.NonPublic);
  			if (fi == null){
  				error (logname, "Internal Mono Error: Could not find RunService in ServiceBase");
  				return 1;
  			}
  			fi.SetValue (null, Delegate.CreateDelegate(cbType, this, "MainLoop"));
  			
  			// And run its Main. Our RunService handler is invoked from 
  			// ServiceBase.Run.
  			return AppDomain.CurrentDomain.ExecuteAssembly (assembly, AppDomain.CurrentDomain.Evidence, args);
  			
  		} catch ( Exception ex ) {
  			for (Exception e = ex; e != null; e = e.InnerException) {
  				error (logname, e.Message);
  			}
  			
  			return 1;
  		}
  	}
  	
  	// The main service loop
  	private void MainLoop (ServiceBase [] services)
  	{
  		try {
  			ServiceBase service;
  	
  			if (services == null || services.Length == 0){
  				error (logname, "No services were registered by this service");
  				return;
  			}
  			
  			// Start up the service.
  			service = null;
  			
  			if (name != null){
  				foreach (ServiceBase svc in services){
  					if (svc.ServiceName == name){
  						service = svc;
  						break;
  					}
  				}
  			} else {
  				service = services [0];
  			}
  
  
gt: 			if (service.ExitCode != 0) {