Thumbnail file hits. Based on a patch from D Bera
[beagle.git] / beagled / BeagleDaemon.cs
blob954467277d53c310731603892b7dfda59178b732
1 //
2 // BeagleDaemon.cs
3 //
4 // Copyright (C) 2004 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
27 using System;
28 using System.Collections;
29 using System.IO;
30 using System.Reflection;
31 using System.Threading;
33 using Gtk;
35 using Beagle.Util;
37 #if ENABLE_WEBSERVICES
38 using Beagle.WebService;
39 #endif
41 namespace Beagle.Daemon {
42 class BeagleDaemon {
44 public static Thread MainLoopThread = null;
46 private static Server server = null;
48 private static bool arg_replace = false;
49 private static bool arg_disable_scheduler = false;
51 public static bool StartServer ()
53 Logger.Log.Debug ("Starting messaging server");
54 try {
55 server = new Server ("socket");
56 server.Start ();
57 } catch (InvalidOperationException) {
58 return false;
61 return true;
64 public static void ReplaceExisting ()
66 Logger.Log.Info ("Attempting to replace another beagled.");
68 do {
69 ShutdownRequest request = new ShutdownRequest ();
70 Logger.Log.Info ("Sending Shutdown");
71 request.Send ();
72 // Give it a second to shut down the messaging server
73 Thread.Sleep (1000);
74 } while (! StartServer ());
77 private static void LogMemoryUsage ()
79 while (! Shutdown.ShutdownRequested) {
80 int vm_size = SystemInformation.VmSize;
81 int vm_rss = SystemInformation.VmRss;
83 Logger.Log.Debug ("Memory usage: VmSize={0:.0} MB, VmRSS={1:.0} MB, GC.GetTotalMemory={2}",
84 vm_size/1024.0, vm_rss/1024.0, GC.GetTotalMemory (false));
86 if (vm_size > 300 * 1024) {
87 Logger.Log.Debug ("VmSize too large --- shutting down");
88 Shutdown.BeginShutdown ();
91 Thread.Sleep (5000);
95 private static void PrintUsage ()
97 string usage =
98 "beagled: The daemon to the Beagle search system.\n" +
99 "Web page: http://www.gnome.org/projects/beagle\n" +
100 "Copyright (C) 2004-2005 Novell, Inc.\n\n";
102 usage +=
103 "Usage: beagled [OPTIONS]\n\n" +
104 "Options:\n" +
105 " --foreground, --fg\tRun the daemon in the foreground.\n" +
106 " --background, --bg\tRun the daemon in the background.\n" +
107 " --replace\t\tReplace a running daemon with a new instance.\n" +
108 " --debug\t\tWrite out debugging information.\n" +
109 " --debug-memory\tWrite out debugging information about memory use.\n" +
110 " --deny-backend\tDeny a specific backend.\n" +
111 " --allow-backend\tAllow a specific backend.\n" +
112 " --list-backends\tList all the available backends.\n" +
113 " --add-static-backend\tAdd a static backend by path.\n" +
114 " --disable-scheduler\tDisable the use of the scheduler.\n" +
115 " --help\t\tPrint this usage message.\n";
117 #if ENABLE_WEBSERVICES
118 usage += "\n" +
119 " --web-global\t\tAllow global access to the Web & WebService interfaces.\n" +
120 " --web-port\t\tPort to use for the internal web server.\n" +
121 " --web-root\t\tRoot directory to use for the internal web server.\n" +
122 " --web-disable\t\tDisable Web & WebServices functionality.\n";
123 #endif
125 Console.WriteLine (usage);
128 public static bool StartupProcess ()
130 // Profile our initialization
131 Stopwatch stopwatch = new Stopwatch ();
132 stopwatch.Start ();
134 SetupSignalHandlers ();
136 // Fire up our server
137 if (! StartServer ()) {
138 if (arg_replace)
139 ReplaceExisting ();
140 else {
141 Logger.Log.Fatal ("Could not set up the listener for beagle requests. "
142 + "There is probably another beagled instance running. "
143 + "Use --replace to replace the running service");
144 Environment.Exit (1);
148 // Set up out-of-process indexing
149 if (Environment.GetEnvironmentVariable ("BEAGLE_ENABLE_IN_PROCESS_INDEXING") == null)
150 LuceneQueryable.IndexerHook = new LuceneQueryable.IndexerCreator (RemoteIndexer.NewRemoteIndexer);
152 // Initialize syncronization to keep the indexes local if PathFinder.HomeDir
153 // is on a non-block device, or if BEAGLE_SYNCHRONIZE_LOCALLY is set
154 if ((! SystemInformation.IsPathOnBlockDevice (PathFinder.HomeDir) && Conf.Daemon.IndexSynchronization) ||
155 Environment.GetEnvironmentVariable ("BEAGLE_SYNCHRONIZE_LOCALLY") != null)
156 IndexSynchronization.Initialize ();
158 // Start the query driver.
159 Logger.Log.Debug ("Starting QueryDriver");
160 QueryDriver.Start ();
162 // Start the Global Scheduler thread
163 if (! arg_disable_scheduler) {
164 Logger.Log.Debug ("Starting Scheduler thread");
165 Scheduler.Global.Start ();
168 // Start our Inotify threads
169 Inotify.Start ();
171 // Test if the FileAdvise stuff is working: This will print a
172 // warning if not. The actual advice calls will fail silently.
173 FileAdvise.TestAdvise ();
175 #if ENABLE_WEBSERVICES
176 //Beagle Web, WebService access initialization code:
177 WebServiceBackEnd.Start();
178 #endif
179 Shutdown.ShutdownEvent += OnShutdown;
181 Conf.WatchForUpdates ();
183 stopwatch.Stop ();
185 Logger.Log.Debug ("Daemon initialization finished after {0}", stopwatch);
187 return false;
190 public static int Main (string[] args)
192 // Process the command-line arguments
193 bool arg_debug = false;
194 bool arg_debug_memory = false;
195 bool arg_fg = false;
197 int i = 0;
198 while (i < args.Length) {
200 string arg = args [i];
201 ++i;
202 string next_arg = i < args.Length ? args [i] : null;
204 switch (arg) {
205 case "-h":
206 case "--help":
207 PrintUsage ();
208 Environment.Exit (0);
209 break;
211 case "--list-backends":
212 Console.WriteLine ("Current available backends:");
213 Console.Write (QueryDriver.ListBackends ());
214 Environment.Exit (0);
215 break;
217 case "--fg":
218 case "--foreground":
219 arg_fg = true;
220 break;
222 case "--bg":
223 case "--background":
224 arg_fg = false;
225 break;
227 case "--replace":
228 arg_replace = true;
229 break;
231 case "--debug":
232 arg_debug = true;
233 break;
235 case "--debug-memory":
236 arg_debug = true;
237 arg_debug_memory = true;
238 break;
240 case "--allow-backend":
241 if (next_arg != null)
242 QueryDriver.Allow (next_arg);
243 ++i; // we used next_arg
244 break;
246 case "--deny-backend":
247 if (next_arg != null)
248 QueryDriver.Deny (next_arg);
249 ++i; // we used next_arg
250 break;
252 case "--add-static-backend":
253 if (next_arg != null)
254 QueryDriver.AddStaticQueryable (next_arg);
255 ++i;
256 break;
258 case "--disable-scheduler":
259 arg_disable_scheduler = true;
260 break;
262 case "--autostarted":
263 if (! Conf.Searching.Autostart) {
264 Console.WriteLine ("Autostarting is disabled, not starting");
265 Environment.Exit (0);
267 break;
268 #if ENABLE_WEBSERVICES
269 case "--web-global":
270 WebServiceBackEnd.web_global = true;
271 WebServiceBackEnd.web_start = true;
272 break;
274 case "--web-port":
275 WebServiceBackEnd.web_port = next_arg;
276 ++i;
277 WebServiceBackEnd.web_start = true;
278 break;
280 case "--web-root":
281 WebServiceBackEnd.web_rootDir = next_arg;
282 ++i;
283 WebServiceBackEnd.web_start = true;
284 break;
286 case "--web-disable":
287 WebServiceBackEnd.web_start = false;
288 break;
289 #endif
290 default:
291 Console.WriteLine ("Ignoring unknown argument '{0}'", arg);
292 break;
297 // Bail out if we are trying to run as root
298 if (Environment.UserName == "root") {
299 Console.WriteLine ("You can not run beagle as root.");
300 Console.WriteLine ("Beagle is designed to be run from your own user account.");
301 Environment.Exit (-1);
304 MainLoopThread = Thread.CurrentThread;
306 // Initialize logging.
307 // If we saw the --debug arg, set the default logging level
308 // accordingly.
309 if (arg_debug)
310 Logger.DefaultLevel = LogLevel.Debug;
312 Logger.LogToFile (PathFinder.LogDir, "Beagle", arg_fg);
314 Logger.Log.Info ("Starting Beagle Daemon (version {0})", ExternalStringsHack.Version);
316 // FIXME: This try/catch is to work around a bug in mono 1.1.3
317 try {
318 Logger.Log.Debug ("Command Line: {0}",
319 Environment.CommandLine != null ? Environment.CommandLine : "(null)");
320 } catch (Exception ex) { }
322 if (! ExtendedAttribute.Supported) {
323 Logger.Log.Warn ("Extended attributes are not supported on this filesystem. " +
324 "Many search backends will not be available");
327 // Start our memory-logging thread
328 if (arg_debug_memory)
329 ExceptionHandlingThread.Start (new ThreadStart (LogMemoryUsage));
331 // Do BEAGLE_EXERCISE_THE_DOG_HARDER-related processing.
332 ExerciseTheDogHarder ();
334 Application.InitCheck ("beagled", ref args);
336 // Defer all actual startup until the main loop is
337 // running. That way shutdowns during the startup
338 // process work correctly.
339 GLib.Idle.Add (new GLib.IdleHandler (StartupProcess));
341 // Start our event loop.
342 Logger.Log.Debug ("Starting main loop");
344 Application.Run ();
346 // If we placed our sockets in a temp directory, try to clean it up
347 // Note: this may fail because the helper is still running
348 if (PathFinder.GetRemoteStorageDir (false) != PathFinder.StorageDir) {
349 try {
350 Directory.Delete (PathFinder.GetRemoteStorageDir (false));
351 } catch (IOException) { }
354 Logger.Log.Debug ("Leaving BeagleDaemon.Main");
356 if (Logger.Log.Level == LogLevel.Debug) {
357 Thread.Sleep (500);
358 ExceptionHandlingThread.SpewLiveThreads ();
361 return 0;
365 /////////////////////////////////////////////////////////////////////////////
367 // The integer values of the Mono.Posix.Signal enumeration don't actually
368 // match the Linux signal numbers of Linux. Oops!
369 // This is fixed in Mono.Unix, but for the moment we want to maintain
370 // compatibility with mono 1.0.x.
371 const int ACTUAL_LINUX_SIGINT = 2;
372 const int ACTUAL_LINUX_SIGQUIT = 3;
373 const int ACTUAL_LINUX_SIGTERM = 15;
375 static void SetupSignalHandlers ()
377 // Force OurSignalHandler to be JITed
378 OurSignalHandler (-1);
380 // Set up our signal handler
381 Mono.Posix.Syscall.sighandler_t sig_handler;
382 sig_handler = new Mono.Posix.Syscall.sighandler_t (OurSignalHandler);
383 Mono.Posix.Syscall.signal (ACTUAL_LINUX_SIGINT, sig_handler);
384 Mono.Posix.Syscall.signal (ACTUAL_LINUX_SIGQUIT, sig_handler);
385 Mono.Posix.Syscall.signal (ACTUAL_LINUX_SIGTERM, sig_handler);
388 // Our handler triggers an orderly shutdown when it receives a signal.
389 // However, this can be annoying if the process gets wedged during
390 // shutdown. To deal with that case, we make a note of the time when
391 // the first signal comes in, and we allow signals to unconditionally
392 // kill the process after 5 seconds have passed.
393 static DateTime signal_time = DateTime.MinValue;
394 static void OurSignalHandler (int signal)
396 // This allows us to call OurSignalHandler w/o doing anything.
397 // We want to call it once to ensure that it is pre-JITed.
398 if (signal < 0)
399 return;
401 Logger.Log.Debug ("Handling signal {0}", signal);
403 bool first_signal = false;
404 if (signal_time == DateTime.MinValue) {
405 signal_time = DateTime.Now;
406 first_signal = true;
409 if (Shutdown.ShutdownRequested) {
411 if (first_signal) {
412 Logger.Log.Debug ("Shutdown already in progress.");
413 } else {
414 double t = (DateTime.Now - signal_time).TotalSeconds;
415 const double min_t = 5;
417 if (t < min_t) {
418 Logger.Log.Debug ("Signals can force an immediate shutdown in {0:0.00}s", min_t-t);
419 } else {
420 Logger.Log.Debug ("Forcing immediate shutdown.");
421 Environment.Exit (0);
425 } else {
426 Logger.Log.Debug ("Initiating shutdown in response to signal.");
427 Shutdown.BeginShutdown ();
431 /////////////////////////////////////////////////////////////////////////////
433 private static void OnShutdown ()
435 #if ENABLE_WEBSERVICES
436 WebServiceBackEnd.Stop();
437 #endif
438 // Stop our Inotify threads
439 Inotify.Stop ();
441 // Shut down the global scheduler
442 Scheduler.Global.Stop ();
444 // Stop the messaging server
445 server.Stop ();
448 /////////////////////////////////////////////////////////////////////////////
451 private static ArrayList exercise_files = new ArrayList ();
453 private static void ExerciseTheDogHarder ()
455 string path;
456 path = Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG_HARDER");
457 if (path == null)
458 return;
460 DirectoryInfo dir = new DirectoryInfo (path);
461 foreach (FileInfo file in dir.GetFiles ())
462 exercise_files.Add (file);
463 if (exercise_files.Count == 0)
464 return;
466 int N = 5;
467 if (N > exercise_files.Count)
468 N = exercise_files.Count;
470 for (int i = 0; i < N; ++i)
471 ExceptionHandlingThread.Start (new ThreadStart (ExerciseTheDogHarderWorker));
474 private static void ExerciseTheDogHarderWorker ()
476 Random rng = new Random ();
478 while (! Shutdown.ShutdownRequested) {
480 FileInfo file = null;
481 int i;
484 lock (exercise_files) {
485 do {
486 i = rng.Next (exercise_files.Count);
487 file = exercise_files [i] as FileInfo;
488 } while (file == null);
489 exercise_files [i] = null;
492 string target;
493 target = Path.Combine (PathFinder.HomeDir, "_HARDER_" + file.Name);
495 Logger.Log.Debug ("ETDH: Copying {0}", file.Name);
496 file.CopyTo (target, true);
498 lock (exercise_files)
499 exercise_files [i] = file;
501 Thread.Sleep (500 + rng.Next (500));