Author: joergr
Date: 2005-04-04 16:20:24 -0400 (Mon, 04 Apr 2005)
New Revision: 42538
Added:
trunk/mono/monod/
trunk/mono/monod/ChangeLog
trunk/mono/monod/Makefile.am
trunk/mono/monod/monod.c
Modified:
trunk/mono/Makefile.am
trunk/mono/configure.in
Log:
2005-04-04 Joerg Rosenkranz <[EMAIL PROTECTED]>
* monod.c: First commit: Daemon for running services built
with System.ServiceProcess under Linux.
Modified: trunk/mono/Makefile.am
===================================================================
--- trunk/mono/Makefile.am 2005-04-04 20:02:20 UTC (rev 42537)
+++ trunk/mono/Makefile.am 2005-04-04 20:20:24 UTC (rev 42538)
@@ -1,11 +1,11 @@
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I .
-SUBDIRS = $(libgc_dir) mono $(ikvm_native_dir) support docs data runtime
scripts man samples web
+SUBDIRS = $(libgc_dir) mono $(ikvm_native_dir) support docs data runtime
scripts man samples web monod
# Keep in sync with SUBDIRS
## 'tools' is not normally built
-DIST_SUBDIRS = libgc mono ikvm-native support docs data runtime scripts man
samples web tools
+DIST_SUBDIRS = libgc mono ikvm-native support docs data runtime scripts man
samples web tools monod
EXTRA_DIST= mono.pc.in mono.spec.in mint.pc.in
Modified: trunk/mono/configure.in
===================================================================
--- trunk/mono/configure.in 2005-04-04 20:02:20 UTC (rev 42537)
+++ trunk/mono/configure.in 2005-04-04 20:20:24 UTC (rev 42538)
@@ -1665,6 +1665,7 @@
tools/Makefile
tools/locale-builder/Makefile
runtime/Makefile
+monod/Makefile
])
(
Added: trunk/mono/monod/ChangeLog
===================================================================
--- trunk/mono/monod/ChangeLog 2005-04-04 20:02:20 UTC (rev 42537)
+++ trunk/mono/monod/ChangeLog 2005-04-04 20:20:24 UTC (rev 42538)
@@ -0,0 +1,4 @@
+2005-04-04 Joerg Rosenkranz <[EMAIL PROTECTED]>
+
+ * monod.c: First commit: Daemon for running services built
+ with System.ServiceProcess under Linux.
Added: trunk/mono/monod/Makefile.am
===================================================================
--- trunk/mono/monod/Makefile.am 2005-04-04 20:02:20 UTC (rev 42537)
+++ trunk/mono/monod/Makefile.am 2005-04-04 20:20:24 UTC (rev 42538)
@@ -0,0 +1,26 @@
+if PLATFORM_WIN32
+bin_PROGRAMS =
+else
+bin_PROGRAMS = monod
+endif
+
+if JIT_SUPPORTED
+runtime_lib=../mono/mini/libmono.la
+else
+runtime_lib=../mono/interpreter/libmint.la
+endif
+
+monod_SOURCES = monod.c
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(GMODULE_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+monod_LDADD = \
+ $(runtime_lib) \
+ $(GLIB_LIBS) \
+ $(GMODULE_LIBS) \
+ -lm
+
+EXTRA_DIST = ChangeLog
Added: trunk/mono/monod/monod.c
===================================================================
--- trunk/mono/monod/monod.c 2005-04-04 20:02:20 UTC (rev 42537)
+++ trunk/mono/monod/monod.c 2005-04-04 20:20:24 UTC (rev 42538)
@@ -0,0 +1,430 @@
+/*
+ * monod.c: Mono daemon for running services based
+ * on System.ServiceProcess.
+ *
+ * Author:
+ * Joerg Rosenkranz ([EMAIL PROTECTED])
+ *
+ * (C) 2005 Voelcker Informatik AG
+ */
+
+
+#include <mono/jit/jit.h>
+#include <mono/metadata/environment.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/debug-helpers.h>
+
+#include <mono/io-layer/uglify.h>
+#include <mono/io-layer/wait.h>
+#include <mono/io-layer/events.h>
+
+#include <locale.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <syslog.h>
+
+/* #define DEBUG_OUTPUT */
+
+#define DEFAULT_LOCK_PREFIX "/tmp/"
+#define DEFAULT_LOCK_EXTENSION ".lock"
+
+/* Exit codes */
+#define EXIT_SUCCESS 0
+#define EXIT_USAGE 1
+#define EXIT_COULDNOTLOADASSEMBLY 2
+#define EXIT_COULDNOTGETCLASS 3
+#define EXIT_COULDNOTCREATEOBJECT 4
+#define EXIT_COULDNOTCREATEMETHODDESC 5
+#define EXIT_COULDNOTGETMETHOD 6
+#define EXIT_FORKERROR 7
+#define EXIT_CANNOTOPENLOCK 8
+#define EXIT_NOSVCOBJECT 9
+
+#define EXIT_NOTIMPLEMENTED 1024
+
+typedef struct
+{
+ MonoDomain *domain;
+ const char *file;
+ const char *servicename;
+} MainThreadArgs;
+
+/*
+ * Global variables.
+ */
+gpointer svc_sig_event;
+int svc_sig;
+
+MonoObject* get_svc_object(const char* name)
+{
+ MonoImage *image;
+ MonoArray *svc_array;
+ MonoClass *svcbase_class;
+ MonoClassField *field;
+
+ /*
+ * Get array of registered services from static member of ServiceBase.
+ * This property is only set when ServiceBase.Run was called in the
+ * service's Main.
+ *
+ * System.ServiceProcess should already be loaded.
+ */
+ image = mono_image_loaded ("System.ServiceProcess");
+ if (!image) {
+ syslog (LOG_ERR, "Could not get image
System.ServiceProcess.\n");
+ return NULL;
+ }
+
+ svcbase_class = mono_class_from_name (image, "System.ServiceProcess",
"ServiceBase");
+ if (!svcbase_class) {
+ syslog (LOG_ERR, "Could not get class
System.ServiceProcess.ServiceBase.\n");
+ return NULL;
+ }
+
+ field = mono_class_get_field_from_name (svcbase_class,
"RegisteredServices");
+ if (!field) {
+ syslog (LOG_ERR, "Could not get field
ServiceBase.RegisteredServices.\n");
+ return NULL;
+ }
+
+ svc_array = (MonoArray*) mono_field_get_value_object (mono_domain_get
(), field, NULL);
+ if (!svc_array || mono_array_length (svc_array) == 0) {
+ syslog (LOG_ERR, "No service object registered by Main.\n");
+ return NULL;
+ }
+
+ if (name && *name) {
+ /*
+ * There was a name provided
+ * -> Search for the right service object
+ */
+
+ syslog (LOG_ERR, "Searching service object by name not yet
implemented.");
+ exit (EXIT_NOTIMPLEMENTED);
+ }
+
+ /* Default: Return the first service */
+ return mono_array_get (svc_array, MonoObject*, 0);
+}
+
+void invoke_service_method (MonoClass *svc_class, MonoObject *svc_object,
const char *name)
+{
+ void * params [] = {NULL};
+ MonoMethodDesc *method_desc;
+ MonoMethod *method;
+
+ if (svc_class && svc_object) {
+
+ /* Get method from class */
+ method_desc = mono_method_desc_new (name, FALSE);
+ if (! method_desc) {
+ syslog (LOG_ERR, "Could not create method description
for %s.\n", name);
+ exit (EXIT_COULDNOTCREATEMETHODDESC);
+ }
+
+ method = mono_method_desc_search_in_class (method_desc,
svc_class);
+ if (!method) {
+ syslog (LOG_ERR, "Could not get method %s.\n", name);
+ exit (EXIT_COULDNOTGETMETHOD);
+ }
+
+ mono_method_desc_free (method_desc);
+
+ /* Run method. */
+ mono_runtime_invoke (method, svc_object, params, NULL);
+ }
+}
+
+MonoObject* get_service_property (MonoClass *svc_class, MonoObject
*svc_object, const char *name)
+{
+ MonoProperty *prop;
+
+ if (!svc_class || !svc_object) {
+ return NULL;
+ }
+
+ prop = mono_class_get_property_from_name (svc_class, name);
+ if (!prop) {
+ return NULL;
+ }
+
+ return mono_property_get_value (prop, svc_object, NULL, NULL);
+}
+
+static void service_main (gpointer user_data)
+{
+ MainThreadArgs *main_args = (MainThreadArgs *) user_data;
+ MonoAssembly *assembly;
+ MonoClass *svc_class;
+ MonoObject *svc_object;
+ gboolean bEnd;
+ const char* servicedisplay = main_args->file;
+ MonoString *s;
+ char** args;
+
+ /*
+ * Get service assembly.
+ */
+ assembly = mono_domain_assembly_open (main_args->domain,
+ main_args->file);
+ if (!assembly) {
+ syslog (LOG_ERR, "Could not load assembly %s.\n",
main_args->file);
+ exit (EXIT_COULDNOTLOADASSEMBLY);
+ }
+
+ /*
+ * Run Main to get list of services.
+ */
+ args = (char**) malloc (2 * sizeof(char*));
+ args[0] = (char*) main_args->file;
+ args[1] = NULL;
+ mono_jit_exec (main_args->domain, assembly, 1, args);
+
+ /*
+ * Get service object
+ */
+ svc_object = get_svc_object (main_args->servicename);
+ if (!svc_object) {
+ syslog (LOG_ERR, "Could not get service object. Maybe not
registered in Main.");
+ exit (EXIT_NOSVCOBJECT);
+ }
+
+ svc_class = mono_object_get_class (svc_object);
+ if (!svc_class) {
+ syslog (LOG_ERR, "Could not get class from object.\n");
+ exit (EXIT_COULDNOTGETCLASS);
+ }
+
+ s = (MonoString*) get_service_property (svc_class, svc_object,
"ServiceName");
+ if (s) {
+ servicedisplay = mono_string_to_utf8 (s);
+ }
+
+ syslog (LOG_INFO, "Starting service %s...\n", servicedisplay);
+ invoke_service_method (svc_class, svc_object, "ServiceBase:OnStart");
+
+ bEnd = FALSE;
+ while (!bEnd) {
+ if (WaitForSingleObject (svc_sig_event, INFINITE) ==
WAIT_FAILED) {
+ syslog (LOG_ERR, "Waiting for handle failed.\n");
+ bEnd = TRUE;
+ } else {
+ if (svc_sig != 0) {
+ switch (svc_sig) {
+ case SIGTERM:
+ syslog (LOG_INFO, "Stopping
service %s...\n", servicedisplay);
+ invoke_service_method
(svc_class, svc_object, "ServiceBase:OnStop");
+ bEnd = TRUE;
+ break;
+
+ case SIGUSR1:
+ syslog (LOG_INFO, "Pausing
service %s...\n", servicedisplay);
+ invoke_service_method
(svc_class, svc_object, "ServiceBase:OnPause");
+ break;
+
+ case SIGUSR2:
+ syslog (LOG_INFO, "Continuing
service %s...\n", servicedisplay);
+ invoke_service_method
(svc_class, svc_object, "ServiceBase:OnContinue");
+ break;
+ }
+
+ svc_sig = 0;
+ }
+ }
+ }
+
+ CloseHandle (svc_sig_event);
+}
+
+void signal_handler (int sig)
+{
+ svc_sig = sig;
+
+ /* Signal the wait event of our main loop */
+ SetEvent (svc_sig_event);
+}
+
+void run_service (const char *file, const char *svcname)
+{
+ MonoDomain *domain;
+ MainThreadArgs main_args;
+
+ svc_sig = 0;
+
+ /* Create AutoResetEvent to wait for signals in main loop */
+ svc_sig_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+ /* Install signal handler control events */
+ signal(SIGTERM, signal_handler);
+ signal(SIGUSR1, signal_handler);
+ signal(SIGUSR2, signal_handler);
+
+ /*
+ * mono_jit_init() creates a domain: each assembly is
+ * loaded and run in a MonoDomain.
+ */
+ domain = mono_jit_init (file);
+
+ /* Parse default config files */
+ mono_config_parse (NULL);
+
+ main_args.domain = domain;
+ main_args.file = file;
+ main_args.servicename = svcname;
+
+ mono_runtime_exec_managed_code (domain, service_main, &main_args);
+
+ mono_jit_cleanup (domain);
+}
+
+void daemonize (char * lockfile, char * rundirectory, char * logname)
+{
+ int i, lfp;
+ char str [10];
+
+ /* fork to create daemon process */
+ i = fork ();
+
+ if (i<0)
+ exit (EXIT_FORKERROR); /* fork error */
+
+ if (i>0)
+ exit (EXIT_SUCCESS); /* parent exits */
+
+ /* child (daemon) continues */
+ setsid (); /* obtain a new process group */
+
+#ifndef DEBUG_OUTPUT
+ /* suppress stdout and stderr */
+ for (i = getdtablesize(); i >= 0 ; --i)
+ close (i); /* close all descriptors */
+
+ i = open ("/dev/null", O_RDWR);
+ dup (i);
+ dup (i); /* handle standard I/O */
+#endif
+
+ /* open syslog handle */
+ openlog (logname, LOG_PID, LOG_DAEMON);
+
+ if (rundirectory && *rundirectory) {
+ syslog (LOG_INFO, "Running in directory %s", rundirectory);
+ chdir (rundirectory); /* change running directory */
+ }
+
+ /* create and lock lock file */
+ lfp = open (lockfile, O_RDWR|O_CREAT, 0640);
+
+ if (lfp<0) {
+ syslog (LOG_ERR, "Cannot open lock file.\n");
+ exit (EXIT_CANNOTOPENLOCK); /* can not open */
+ }
+
+ if (lockf(lfp, F_TLOCK,0)<0) {
+ syslog (LOG_ERR, "Daemon is already running.\n");
+ exit (EXIT_SUCCESS); /* can not lock */
+ }
+
+ /* first instance continues */
+ sprintf (str,"%d\n", getpid());
+ write(lfp, str, strlen (str)); /* record pid to lockfile */
+
+ signal (SIGCHLD, SIG_IGN); /* ignore child */
+ signal (SIGTSTP, SIG_IGN); /* ignore tty signals */
+ signal (SIGTTOU, SIG_IGN);
+ signal (SIGTTIN, SIG_IGN);
+}
+
+int main (int argc, char* argv[])
+{
+ char * lockfile;
+ char * directory;
+ char * name;
+ char * assembly;
+ char * logname;
+ gboolean bShowUsage;
+ int i;
+ char * param;
+
+ setlocale (LC_ALL, "");
+ g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
+ g_log_set_fatal_mask (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR);
+
+ /* set default values */
+ lockfile = NULL;
+ directory = NULL;
+ name = NULL;
+ logname = NULL;
+ assembly = NULL;
+ bShowUsage = FALSE;
+
+ /* parse command line arguments */
+ for (i = 1; !bShowUsage && i < argc - 1; i++) {
+ param = argv [i];
+
+ if (strlen (param) < 3 || param [0] != '-' || param [2] != ':')
{
+ bShowUsage = TRUE;
+ }
+ else {
+ switch (param [1]) {
+ case 'd':
+ case 'D':
+ directory = ¶m [3];
+ break;
+
+ case 'l':
+ case 'L':
+ lockfile = ¶m [3];
+ break;
+
+ case 'n':
+ case 'N':
+ name = ¶m [3];
+ break;
+
+ case 'm':
+ case 'M':
+ logname = ¶m [3];
+ break;
+
+ default:
+ bShowUsage = TRUE;
+ }
+ }
+ }
+
+ if (argc > 1 && *argv [argc - 1] != '-') {
+ assembly = argv [argc - 1];
+ } else {
+ fprintf (stderr, "Assembly name is missing.\n");
+ bShowUsage = TRUE;
+ }
+
+ if (bShowUsage) {
+ fprintf (stderr, "\nUsage: %s [ -d:<runtime directory> ] [
-l:<lock file> ] [ -m:<syslog name> ] [ -n:<service name> ] <service
assembly>\n\n", argv [0]);
+ return EXIT_USAGE;
+ }
+
+ if ( !lockfile ) {
+ /* Build default lock file name */
+ lockfile = (char*) malloc (strlen (DEFAULT_LOCK_PREFIX) +
strlen (assembly) + strlen (DEFAULT_LOCK_EXTENSION));
+ strcpy (lockfile, DEFAULT_LOCK_PREFIX);
+ strcat (lockfile, assembly);
+ strcat (lockfile, DEFAULT_LOCK_EXTENSION);
+ }
+
+ if ( !logname ) {
+ logname = assembly;
+ }
+
+ /* Run as daemon */
+ daemonize (lockfile, directory, logname);
+
+ /* Start the service */
+ run_service (assembly, name);
+
+ return EXIT_SUCCESS;
+}
+
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches