1
/****************************************************************************
3
| Copyright (c) 2007 Novell, Inc.
6
| This program is free software; you can redistribute it and/or
7
| modify it under the terms of version 2 of the GNU General Public License as
8
| published by the Free Software Foundation.
10
| This program is distributed in the hope that it will be useful,
11
| but WITHOUT ANY WARRANTY; without even the implied warranty of
12
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
| GNU General Public License for more details.
15
| You should have received a copy of the GNU General Public License
16
| along with this program; if not, contact Novell, Inc.
18
| To contact Novell about this file by physical or electronic mail,
19
| you may find current contact information at www.novell.com
21
| Author: Calvin Gaisford <cgaisford@novell.com>
22
|***************************************************************************/
26
using System.Collections;
27
using System.ComponentModel;
30
using System.Net.Sockets;
32
using System.Threading;
34
using System.Web.SessionState;
38
using Simias.Client.Event;
45
/// Summary description for Global.
47
public class Global : HttpApplication
52
/// Environment variables used by apache.
54
private static string EnvSimiasRunAsClient = "SimiasRunAsClient";
55
private static string EnvSimiasDataDir = "SimiasDataDir";
56
private static string EnvSimiasVerbose = "SimiasVerbose";
59
/// Object used to manage the simias services.
61
static private Simias.Service.Manager serviceManager;
64
/// A thread to keep the application alive long enough to close Simias.
66
private Thread keepAliveThread;
69
/// Quit the application flag.
74
/// Specifies whether to run as a client or server.
76
private static bool runAsServer = true;
79
/// Prints extra data for debugging purposes.
81
private static bool verbose = false;
84
/// Path to the simias data area.
86
private static string simiasDataPath = null;
89
/// Port used as an IPC between application domains.
91
private static int ipcPort;
94
/// Port used to talk local service. This will be (-1) if running
95
/// in an enterprise configuration.
97
private static int localServicePort = -1;
104
/// Static constructor that starts only one shutdown thread.
108
// The presedence for settings is
111
// 3. defaults.conf (in etc)
113
ReadDefaultsConfig();
114
ParseEnvironmentVariables();
115
ParseConfigurationParameters( Environment.GetCommandLineArgs() );
118
// Make sure that there is a data path specified.
119
if ( simiasDataPath == null )
121
ApplicationException apEx = new ApplicationException( "The Simias data path was not specified." );
122
Console.Error.WriteLine( apEx.Message );
128
Console.Error.WriteLine("Simias Application Path: {0}", Environment.CurrentDirectory);
129
Console.Error.WriteLine("Simias Data Path: {0}", simiasDataPath);
130
Console.Error.WriteLine("Run in {0} configuration", runAsServer ? "server" : "client" );
131
Console.Error.WriteLine("Local service port = {0}", localServicePort );
134
// Check the current datadir, if it's not setup then copy the bootstrap files there
135
// only do this if we are starting up as a server.
141
// Initialize the store.
142
Store.Initialize( simiasDataPath, runAsServer, localServicePort );
147
#region Private Methods
150
/// Parses the command line parameters to get the configuration for Simias.
152
/// <param name="args">Command line parameters.</param>
153
private static void ParseConfigurationParameters( string[] args )
155
for ( int i = 0; i < args.Length; ++i )
157
switch ( args[ i ].ToLower() )
159
case "--runasclient":
167
if ( ( i + 1 ) < args.Length )
169
simiasDataPath = args[ ++i ];
173
ApplicationException apEx = new ApplicationException( "Error: The Simias data dir was not specified." );
174
Console.Error.WriteLine( apEx.Message );
183
if ( ( i + 1 ) < args.Length )
185
localServicePort = Convert.ToInt32( args[ ++i ] );
189
ApplicationException apEx = new ApplicationException( "Error: The local service port was not specified." );
190
Console.Error.WriteLine( apEx.Message );
199
if ( ( i + 1 ) < args.Length )
201
ipcPort = Convert.ToInt32( args[ ++i ] );
205
ApplicationException apEx = new ApplicationException( "Error: The IPC port was not specified." );
206
Console.Error.WriteLine( apEx.Message );
224
/// Gets the Simias environment variables set by mod-mono-server in the apache process.
226
private static void ParseEnvironmentVariables()
229
if( Environment.GetEnvironmentVariable( EnvSimiasRunAsClient ) != null )
234
tmpPath = Environment.GetEnvironmentVariable( EnvSimiasDataDir );
235
if( tmpPath != null )
237
simiasDataPath = tmpPath.Trim( new char [] { '\"' } );
240
if( Environment.GetEnvironmentVariable( EnvSimiasVerbose ) != null )
248
/// Gets the default settings from the /etc/defaults.config file
250
private static void ReadDefaultsConfig()
252
runAsServer = !Simias.Defaults.RunsAsClient;
253
simiasDataPath = Simias.Defaults.SimiasDataDir;
254
//verbose = Simias.Defaults.Verbose;
259
/// Sends the specified message via the IPC to the listening process.
261
/// <param name="message">Message to send.</param>
262
private static void SendIpcMessage( string message )
264
// Put the message into a length preceeded buffer.
265
UTF8Encoding utf8 = new UTF8Encoding();
266
int msgLength = utf8.GetByteCount( message );
267
byte[] msgHeader = BitConverter.GetBytes( msgLength );
268
byte[] buffer = new byte[ msgHeader.Length + msgLength ];
270
// Copy the message length and the message into the buffer.
271
msgHeader.CopyTo( buffer, 0 );
272
utf8.GetBytes( message, 0, message.Length, buffer, 4 );
274
// Allocate a socket to send the shutdown message on.
275
Socket socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
278
// Connect to the listening client on the specifed ipc port.
279
socket.Connect( new IPEndPoint( IPAddress.Loopback, ipcPort ) );
280
socket.Send( buffer );
281
socket.Shutdown( SocketShutdown.Send );
290
#region Protected Methods
293
/// Application_Start
295
/// <param name="sender"></param>
296
/// <param name="e"></param>
297
protected void Application_Start(Object sender, EventArgs e)
300
// update the prefix of the installed directory
301
// but only if we are on windows
302
SimiasSetup.prefix = Path.Combine(Server.MapPath(null), "..");
304
Environment.CurrentDirectory = SimiasSetup.webbindir;
308
Console.Error.WriteLine("Simias Process Starting");
311
serviceManager = Simias.Service.Manager.GetManager();
312
serviceManager.StartServices();
313
serviceManager.WaitForServicesStarted();
315
// Send the simias up event.
316
EventPublisher eventPub = new EventPublisher();
317
eventPub.RaiseEvent( new NotifyEventArgs("Simias-Up", "The simias service is running", DateTime.Now) );
321
Console.Error.WriteLine("Simias Process Running");
325
// NOTE: We have seen a FLAIM corruption because the database was not given
326
// the opportunity to close on shutdown. The solution is to work with
327
// Dispose() methods with the native calls and to control our own life cycle
328
// (which in a web application is difficult). It would mean separating the
329
// database application from the web application, which is not very practical
330
// today. We can bend the rules in the web application by using a foreground
331
// thread. This is a brittle solution, but it seems to work today.
332
keepAliveThread = new Thread(new ThreadStart(this.KeepAlive));
334
keepAliveThread.Start();
341
protected static void Setup_Datadir()
343
if(simiasDataPath == null)
346
Console.Error.WriteLine("SimiasDataPath was null, ignoring bootstrap process");
350
// if we don't have a bootstrap, nothing to do!
351
if(!Directory.Exists(SimiasSetup.bootstrapdir))
354
Console.Error.WriteLine("Ignoring bootstrap process because file not found at : " + SimiasSetup.bootstrapdir);
358
string[] fileEntries =
359
Directory.GetFileSystemEntries(simiasDataPath);
361
// if we find entries in our current
362
// data path, bail out
363
if(fileEntries.Length > 1)
366
Console.Error.WriteLine("Files found in exising simias data area... Ignoring bootstrap process");
370
// move all of the files
371
fileEntries = Directory.GetFiles(SimiasSetup.bootstrapdir);
372
foreach(string fileName in fileEntries)
376
File.Copy(fileName, Path.Combine(simiasDataPath,
377
Path.GetFileName(fileName)) );
381
Console.Error.WriteLine("Error bootstraping file: " + fileName);
382
Console.Error.WriteLine(e.Message);
386
// move all of the dirs
387
fileEntries = Directory.GetDirectories(SimiasSetup.bootstrapdir);
388
foreach(string fileName in fileEntries)
392
CopyDirectory(fileName, simiasDataPath);
396
Console.Error.WriteLine("Error bootstraping diretory: " + fileName);
397
Console.Error.WriteLine(e.Message);
402
protected static void CopyDirectory(string src, string targetdir)
404
string dirName = Path.GetFileName(src);
405
string targetName = Path.Combine(targetdir, dirName);
407
DirectoryInfo di = new DirectoryInfo(targetdir);
408
di.CreateSubdirectory(dirName);
410
// move all of the files
411
string[] fileEntries = Directory.GetFiles(src);
412
foreach(string fileName in fileEntries)
414
File.Copy(fileName, Path.Combine(targetName,
415
Path.GetFileName(fileName)) );
418
// move all of the dirs
419
fileEntries = Directory.GetDirectories(src);
420
foreach(string fileName in fileEntries)
422
CopyDirectory(fileName, targetdir);
428
/// Keep Alive Thread
430
protected void KeepAlive()
432
TimeSpan span = TimeSpan.FromSeconds(1);
443
/// <param name="sender"></param>
444
/// <param name="e"></param>
445
protected void Session_Start(Object sender, EventArgs e)
450
/// Application_BeginRequest
452
/// <param name="sender"></param>
453
/// <param name="e"></param>
454
protected void Application_BeginRequest(Object sender, EventArgs e)
459
/// Application_EndRequest
461
/// <param name="sender"></param>
462
/// <param name="e"></param>
463
protected void Application_EndRequest(Object sender, EventArgs e)
468
/// Application_AuthenticateRequest
470
/// <param name="sender"></param>
471
/// <param name="e"></param>
472
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
477
/// Application_Error
479
/// <param name="sender"></param>
480
/// <param name="e"></param>
481
protected void Application_Error(Object sender, EventArgs e)
488
/// <param name="sender"></param>
489
/// <param name="e"></param>
490
protected void Session_End(Object sender, EventArgs e)
497
/// <param name="sender"></param>
498
/// <param name="e"></param>
499
protected void Application_End(Object sender, EventArgs e)
503
Console.Error.WriteLine("Simias Process Starting Shutdown");
506
if ( serviceManager != null )
508
// Send the simias down event and wait for 1/2 second for the message to be routed.
509
EventPublisher eventPub = new EventPublisher();
510
eventPub.RaiseEvent( new NotifyEventArgs("Simias-Down", "The simias service is terminating", DateTime.Now) );
513
serviceManager.StopServices();
514
serviceManager.WaitForServicesStopped();
515
serviceManager = null;
520
Console.Error.WriteLine("Simias Process Shutdown");
524
// NOTE: an interrupt or abort here is currently causing a hang on Mono
530
#region Public Methods
533
/// Causes the controlling server process to shut down the web services.
535
public static void SimiasProcessExit()
537
SendIpcMessage( "stop_server" );