Dont index style nodes.
[beagle.git] / beagled / BeagleDaemon.cs
blob3d47e48c37a7d3a486dc685c2775a1cd82d6c71e
1 //
2 // BeagleDaemon.cs
3 //
4 // Copyright (C) 2004-2006 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.Runtime.InteropServices;
32 using System.Threading;
34 using Gtk;
36 using Beagle.Util;
38 #if ENABLE_WEBSERVICES
39 using Beagle.WebService;
40 #endif
42 namespace Beagle.Daemon {
43 class BeagleDaemon {
45 public static Thread MainLoopThread = null;
47 private static Server server = null;
49 private static bool arg_replace = false;
50 private static bool arg_disable_scheduler = false;
51 private static bool arg_indexing_test_mode = false;
53 public static bool StartServer ()
55 Logger.Log.Debug ("Starting messaging server");
56 try {
57 server = new Server ("socket");
58 server.Start ();
59 } catch (InvalidOperationException) {
60 return false;
63 return true;
66 public static void ReplaceExisting ()
68 Logger.Log.Info ("Attempting to replace another beagled.");
70 do {
71 ShutdownRequest request = new ShutdownRequest ();
72 Logger.Log.Info ("Sending Shutdown");
73 request.Send ();
74 // Give it a second to shut down the messaging server
75 Thread.Sleep (1000);
76 } while (! StartServer ());
79 private static void LogMemoryUsage ()
81 while (! Shutdown.ShutdownRequested) {
82 int vm_size = SystemInformation.VmSize;
83 int vm_rss = SystemInformation.VmRss;
85 Logger.Log.Debug ("Memory usage: VmSize={0:.0} MB, VmRSS={1:.0} MB, GC.GetTotalMemory={2}",
86 vm_size/1024.0, vm_rss/1024.0, GC.GetTotalMemory (false));
88 if (vm_size > 300 * 1024) {
89 Logger.Log.Debug ("VmSize too large --- shutting down");
90 Shutdown.BeginShutdown ();
93 Thread.Sleep (5000);
97 private static void PrintUsage ()
99 string usage =
100 "beagled: The daemon to the Beagle search system.\n" +
101 "Web page: http://beagle-project.org\n" +
102 "Copyright (C) 2004-2006 Novell, Inc.\n\n";
104 usage +=
105 "Usage: beagled [OPTIONS]\n\n" +
106 "Options:\n" +
107 " --foreground, --fg\tRun the daemon in the foreground.\n" +
108 " --background, --bg\tRun the daemon in the background.\n" +
109 " --replace\t\tReplace a running daemon with a new instance.\n" +
110 " --debug\t\tWrite out debugging information.\n" +
111 " --debug-memory\tWrite out debugging information about memory use.\n" +
112 " --indexing-test-mode\tRun in foreground, and exit when fully indexed.\n" +
113 " --indexing-delay\tTime to wait before indexing. (Default 60 seconds)\n" +
114 " --backend\t\tConfigure which backends to use. Specifically:\n" +
115 " --backend <name>\tOnly start backend 'name'\n" +
116 " --backend +<name>\tAdditionally start backend 'name'\n" +
117 " --backend -<name>\tDisable backend 'name'\n" +
118 " --allow-backend\t(DEPRECATED) Start only the specific backend.\n" +
119 " --deny-backend\t(DEPRECATED) Deny a specific backend.\n" +
120 " --list-backends\tList all the available backends.\n" +
121 " --add-static-backend\tAdd a static backend by path.\n" +
122 " --disable-scheduler\tDisable the use of the scheduler.\n" +
123 " --help\t\tPrint this usage message.\n";
125 #if ENABLE_WEBSERVICES
126 usage += "\n" +
127 " --web-global\t\tAllow global access to the Web & WebService interfaces.\n" +
128 " --web-port\t\tPort to use for the internal web server.\n" +
129 " --web-root\t\tRoot directory to use for the internal web server.\n" +
130 " --web-disable\t\tDisable Web & WebServices functionality.\n";
131 #endif
133 Console.WriteLine (usage);
136 public static bool StartupProcess ()
138 // Profile our initialization
139 Stopwatch stopwatch = new Stopwatch ();
140 stopwatch.Start ();
142 SetupSignalHandlers ();
144 // Fire up our server
145 if (! StartServer ()) {
146 if (arg_replace)
148 #if ENABLE_WEBSERVICES
149 WebServiceBackEnd.Stop();
150 #endif
151 ReplaceExisting ();
153 else {
154 Logger.Log.Error ("Could not set up the listener for beagle requests. "
155 + "There is probably another beagled instance running. "
156 + "Use --replace to replace the running service");
157 Environment.Exit (1);
161 // Set up out-of-process indexing
162 if (Environment.GetEnvironmentVariable ("BEAGLE_ENABLE_IN_PROCESS_INDEXING") == null)
163 LuceneQueryable.IndexerHook = new LuceneQueryable.IndexerCreator (RemoteIndexer.NewRemoteIndexer);
165 // Initialize syncronization to keep the indexes local if PathFinder.HomeDir
166 // is on a non-block device, or if BEAGLE_SYNCHRONIZE_LOCALLY is set
167 if ((! SystemInformation.IsPathOnBlockDevice (PathFinder.HomeDir) && Conf.Daemon.IndexSynchronization) ||
168 Environment.GetEnvironmentVariable ("BEAGLE_SYNCHRONIZE_LOCALLY") != null)
169 IndexSynchronization.Initialize ();
171 // Start the query driver.
172 Logger.Log.Debug ("Starting QueryDriver");
173 QueryDriver.Start ();
175 bool initially_on_battery = SystemInformation.UsingBattery && ! Conf.Indexing.IndexOnBattery;
177 // Start the Global Scheduler thread
178 if (! arg_disable_scheduler) {
179 if (! initially_on_battery) {
180 Logger.Log.Debug ("Starting Scheduler thread");
181 Scheduler.Global.Start ();
182 } else
183 Log.Debug ("Beagle started on battery, not starting scheduler thread");
186 // Poll the battery status so we can shut down the
187 // scheduler if needed. Ideally at some point this
188 // will become some sort of D-BUS signal, probably from
189 // something like gnome-power-manager.
190 prev_on_battery = initially_on_battery;
191 GLib.Timeout.Add (5000, CheckBatteryStatus);
193 // Start our Inotify threads
194 Inotify.Start ();
196 // Test if the FileAdvise stuff is working: This will print a
197 // warning if not. The actual advice calls will fail silently.
198 FileAdvise.TestAdvise ();
200 #if ENABLE_WEBSERVICES
201 //Beagle Web, WebService access initialization code:
202 WebServiceBackEnd.Start();
203 #endif
204 Shutdown.ShutdownEvent += OnShutdown;
206 Conf.WatchForUpdates ();
208 stopwatch.Stop ();
210 Logger.Log.Debug ("Daemon initialization finished after {0}", stopwatch);
212 if (arg_indexing_test_mode) {
213 Thread.Sleep (1000); // Ugly paranoia: wait a second for the backends to settle.
214 Logger.Log.Debug ("Running in indexing test mode");
215 Scheduler.Global.EmptyQueueEvent += OnEmptySchedulerQueue;
216 Scheduler.Global.Add (null); // pulse the scheduler
218 return false;
221 static void OnEmptySchedulerQueue ()
223 Logger.Log.Debug ("Scheduler queue is empty: terminating immediately");
224 Shutdown.BeginShutdown ();
225 Environment.Exit (0); // Ugly work-around: We need to call Exit here to avoid deadlocking.
228 public static void Main (string[] args)
230 try {
231 DoMain (args);
232 } catch (Exception ex) {
233 Logger.Log.Error ("Unhandled exception thrown. Exiting immediately.");
234 Logger.Log.Error (ex);
235 Environment.Exit (1);
239 public static void DoMain (string[] args)
241 SystemInformation.SetProcessName ("beagled");
243 // Process the command-line arguments
244 bool arg_debug = false;
245 bool arg_debug_memory = false;
246 bool arg_fg = false;
248 int i = 0;
249 while (i < args.Length) {
251 string arg = args [i];
252 ++i;
253 string next_arg = i < args.Length ? args [i] : null;
255 switch (arg) {
256 case "-h":
257 case "--help":
258 PrintUsage ();
259 Environment.Exit (0);
260 break;
262 case "--heap-buddy":
263 case "--mdb":
264 // Silently ignore the --heap-buddy argument: it gets handled
265 // in the wrapper script.
266 break;
268 case "--list-backends":
269 Console.WriteLine ("Current available backends:");
270 Console.Write (QueryDriver.ListBackends ());
271 Environment.Exit (0);
272 break;
274 case "--fg":
275 case "--foreground":
276 arg_fg = true;
277 break;
279 case "--bg":
280 case "--background":
281 arg_fg = false;
282 break;
284 case "--replace":
285 arg_replace = true;
286 break;
288 case "--debug":
289 arg_debug = true;
290 break;
292 case "--debug-memory":
293 arg_debug = true;
294 arg_debug_memory = true;
295 break;
297 case "--indexing-test-mode":
298 arg_indexing_test_mode = true;
299 arg_fg = true;
300 break;
302 case "--backend":
303 if (next_arg == null) {
304 Console.WriteLine ("--backend requires a backend name");
305 Environment.Exit (1);
306 break;
309 if (next_arg.StartsWith ("--")) {
310 Console.WriteLine ("--backend requires a backend name. Invalid name '{0}'", next_arg);
311 Environment.Exit (1);
312 break;
315 if (next_arg [0] != '+' && next_arg [0] != '-')
316 QueryDriver.OnlyAllow (next_arg);
317 else {
318 if (next_arg [0] == '+')
319 QueryDriver.Allow (next_arg.Substring (1));
320 else
321 QueryDriver.Deny (next_arg.Substring (1));
324 ++i; // we used next_arg
325 break;
327 case "--allow-backend":
328 // --allow-backend is deprecated, use --backends 'name' instead
329 // it will disable reading the list of enabled/disabled backends
330 // from conf and start the backend given
331 if (next_arg != null)
332 QueryDriver.OnlyAllow (next_arg);
333 ++i; // we used next_arg
334 break;
336 case "--deny-backend":
337 // deprecated: use --backends -'name' instead
338 if (next_arg != null)
339 QueryDriver.Deny (next_arg);
340 ++i; // we used next_arg
341 break;
343 case "--add-static-backend":
344 if (next_arg != null)
345 QueryDriver.AddStaticQueryable (next_arg);
346 ++i;
347 break;
349 case "--disable-scheduler":
350 arg_disable_scheduler = true;
351 break;
353 case "--indexing-delay":
354 if (next_arg != null) {
355 try {
356 QueryDriver.IndexingDelay = Int32.Parse (next_arg);
357 } catch {
358 Console.WriteLine ("'{0}' is not a valid number of seconds", next_arg);
359 Environment.Exit (1);
363 ++i;
364 break;
366 case "--autostarted":
367 if (! Conf.Searching.Autostart) {
368 Console.WriteLine ("Autostarting is disabled, not starting");
369 Environment.Exit (0);
371 break;
372 #if ENABLE_WEBSERVICES
373 case "--web-global":
374 WebServiceBackEnd.web_global = true;
375 WebServiceBackEnd.web_start = true;
376 break;
378 case "--web-port":
379 WebServiceBackEnd.web_port = next_arg;
380 ++i;
381 WebServiceBackEnd.web_start = true;
382 break;
384 case "--web-root":
385 WebServiceBackEnd.web_rootDir = next_arg;
386 ++i;
387 WebServiceBackEnd.web_start = true;
388 break;
390 case "--web-disable":
391 WebServiceBackEnd.web_start = false;
392 break;
393 #endif
394 default:
395 Console.WriteLine ("Unknown argument '{0}'", arg);
396 Environment.Exit (1);
397 break;
402 if (arg_indexing_test_mode) {
403 LuceneQueryable.OptimizeRightAway = true;
407 // Bail out if we are trying to run as root
408 if (Environment.UserName == "root" && Environment.GetEnvironmentVariable ("SUDO_USER") != null) {
409 Console.WriteLine ("You appear to be running beagle using sudo. This can cause problems with");
410 Console.WriteLine ("permissions in your .beagle and .wapi directories if you later try to run");
411 Console.WriteLine ("as an unprivileged user. If you need to run beagle as root, please use");
412 Console.WriteLine ("'su -c' instead.");
413 Environment.Exit (-1);
416 if (Environment.UserName == "root" && ! Conf.Daemon.AllowRoot) {
417 Console.WriteLine ("You can not run beagle as root. Beagle is designed to run from your own");
418 Console.WriteLine ("user account. If you want to create multiuser or system-wide indexes, use");
419 Console.WriteLine ("the beagle-build-index tool.");
420 Console.WriteLine ();
421 Console.WriteLine ("You can override this setting using the beagle-config or beagle-settings tools.");
422 Environment.Exit (-1);
425 try {
426 string tmp = PathFinder.HomeDir;
427 } catch (Exception e) {
428 Console.WriteLine ("Unable to start the daemon: {0}", e.Message);
429 Environment.Exit (-1);
432 MainLoopThread = Thread.CurrentThread;
434 Log.Initialize (PathFinder.LogDir,
435 "Beagle",
436 // FIXME: We always turn on full debugging output! We are still
437 // debugging this code, after all...
438 //arg_debug ? LogLevel.Debug : LogLevel.Warn,
439 LogLevel.Debug,
440 arg_fg);
442 Logger.Log.Info ("Starting Beagle Daemon (version {0})", ExternalStringsHack.Version);
444 Logger.Log.Info ("Running on {0}", SystemInformation.MonoRuntimeVersion);
446 Logger.Log.Debug ("Command Line: {0}",
447 Environment.CommandLine != null ? Environment.CommandLine : "(null)");
449 if (! ExtendedAttribute.Supported) {
450 Logger.Log.Warn ("Extended attributes are not supported on this filesystem. " +
451 "Performance will suffer as a result.");
454 // Start our memory-logging thread
455 if (arg_debug_memory)
456 ExceptionHandlingThread.Start (new ThreadStart (LogMemoryUsage));
458 // Do BEAGLE_EXERCISE_THE_DOG_HARDER-related processing.
459 ExerciseTheDogHarder ();
461 if (Application.InitCheck ("beagled", ref args))
462 Logger.Log.Debug ("Established a connection to the X server");
463 else
464 Logger.Log.Debug ("Unable to establish a connection to the X server");
466 XSetIOErrorHandler (BeagleXIOErrorHandler);
468 // Defer all actual startup until the main loop is
469 // running. That way shutdowns during the startup
470 // process work correctly.
471 GLib.Idle.Add (new GLib.IdleHandler (StartupProcess));
473 // Start our event loop.
474 Logger.Log.Debug ("Starting main loop");
476 Application.Run ();
478 // If we placed our sockets in a temp directory, try to clean it up
479 // Note: this may fail because the helper is still running
480 if (PathFinder.GetRemoteStorageDir (false) != PathFinder.StorageDir) {
481 try {
482 Directory.Delete (PathFinder.GetRemoteStorageDir (false));
483 } catch (IOException) { }
486 Logger.Log.Debug ("Leaving BeagleDaemon.Main");
488 if (arg_debug) {
489 Thread.Sleep (500);
490 ExceptionHandlingThread.SpewLiveThreads ();
494 /////////////////////////////////////////////////////////////////////////////
496 private static bool prev_on_battery = false;
498 private static bool CheckBatteryStatus ()
500 if (prev_on_battery && (! SystemInformation.UsingBattery || Conf.Indexing.IndexOnBattery)) {
501 if (! SystemInformation.UsingBattery)
502 Log.Info ("Deletected a switch from battery to AC power. Restarting scheduler.");
503 Scheduler.Global.Start ();
504 prev_on_battery = false;
505 } else if (! prev_on_battery && SystemInformation.UsingBattery && ! Conf.Indexing.IndexOnBattery) {
506 Log.Info ("Detected a switch from AC power to battery. Stopping scheduler.");
507 Scheduler.Global.Stop ();
508 prev_on_battery = true;
511 return true;
514 /////////////////////////////////////////////////////////////////////////////
516 private delegate int XIOErrorHandler (IntPtr display);
518 [DllImport ("libX11.so.6")]
519 extern static private int XSetIOErrorHandler (XIOErrorHandler handler);
521 private static int BeagleXIOErrorHandler (IntPtr display)
523 Logger.Log.Debug ("Lost our connection to the X server! Trying to shut down gracefully");
525 if (! Shutdown.ShutdownRequested)
526 Shutdown.BeginShutdown ();
528 Logger.Log.Debug ("Xlib is forcing us to exit!");
530 ExceptionHandlingThread.SpewLiveThreads ();
532 // Returning will cause xlib to exit immediately.
533 return 0;
536 /////////////////////////////////////////////////////////////////////////////
538 static void SetupSignalHandlers ()
540 // Force OurSignalHandler to be JITed
541 OurSignalHandler (-1);
543 // Set up our signal handler
544 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGINT, OurSignalHandler);
545 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGTERM, OurSignalHandler);
546 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGUSR1, OurSignalHandler);
548 // Ignore SIGPIPE
549 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGPIPE, Mono.Unix.Native.Stdlib.SIG_IGN);
552 // Our handler triggers an orderly shutdown when it receives a signal.
553 // However, this can be annoying if the process gets wedged during
554 // shutdown. To deal with that case, we make a note of the time when
555 // the first signal comes in, and we allow signals to unconditionally
556 // kill the process after 5 seconds have passed.
557 static DateTime signal_time = DateTime.MinValue;
558 static void OurSignalHandler (int signal)
560 // This allows us to call OurSignalHandler w/o doing anything.
561 // We want to call it once to ensure that it is pre-JITed.
562 if (signal < 0)
563 return;
565 Logger.Log.Debug ("Handling signal {0} ({1})", signal, (Mono.Unix.Native.Signum) signal);
567 // If we get SIGUSR1, turn the debugging level up.
568 if ((Mono.Unix.Native.Signum) signal == Mono.Unix.Native.Signum.SIGUSR1) {
569 LogLevel old_level = Log.Level;
571 Log.Level = LogLevel.Debug;
573 Log.Debug ("Moving from log level {0} to Debug", old_level);
575 GLib.Idle.Add (new GLib.IdleHandler (delegate () { RemoteIndexer.SignalRemoteIndexer (); return false; }));
577 return;
580 bool first_signal = false;
581 if (signal_time == DateTime.MinValue) {
582 signal_time = DateTime.Now;
583 first_signal = true;
586 if (Shutdown.ShutdownRequested) {
588 if (first_signal) {
589 Logger.Log.Debug ("Shutdown already in progress.");
590 } else {
591 double t = (DateTime.Now - signal_time).TotalSeconds;
592 const double min_t = 5;
594 if (t < min_t) {
595 Logger.Log.Debug ("Signals can force an immediate shutdown in {0:0.00}s", min_t-t);
596 } else {
597 Logger.Log.Debug ("Forcing immediate shutdown.");
598 Environment.Exit (0);
602 } else {
603 Logger.Log.Debug ("Initiating shutdown in response to signal.");
604 Shutdown.BeginShutdown ();
608 /////////////////////////////////////////////////////////////////////////////
610 private static void OnShutdown ()
612 #if ENABLE_WEBSERVICES
613 WebServiceBackEnd.Stop();
614 #endif
615 // Stop our Inotify threads
616 Inotify.Stop ();
618 // Shut down the global scheduler
619 Scheduler.Global.Stop ();
621 // Stop the messaging server
622 server.Stop ();
625 /////////////////////////////////////////////////////////////////////////////
628 private static ArrayList exercise_files = new ArrayList ();
630 private static void ExerciseTheDogHarder ()
632 string path;
633 path = Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG_HARDER");
634 if (path == null)
635 return;
637 DirectoryInfo dir = new DirectoryInfo (path);
638 foreach (FileInfo file in dir.GetFiles ())
639 exercise_files.Add (file);
640 if (exercise_files.Count == 0)
641 return;
643 int N = 5;
644 if (N > exercise_files.Count)
645 N = exercise_files.Count;
647 for (int i = 0; i < N; ++i)
648 ExceptionHandlingThread.Start (new ThreadStart (ExerciseTheDogHarderWorker));
651 private static void ExerciseTheDogHarderWorker ()
653 Random rng = new Random ();
655 while (! Shutdown.ShutdownRequested) {
657 FileInfo file = null;
658 int i;
661 lock (exercise_files) {
662 do {
663 i = rng.Next (exercise_files.Count);
664 file = exercise_files [i] as FileInfo;
665 } while (file == null);
666 exercise_files [i] = null;
669 string target;
670 target = Path.Combine (PathFinder.HomeDir, "_HARDER_" + file.Name);
672 Logger.Log.Debug ("ETDH: Copying {0}", file.Name);
673 file.CopyTo (target, true);
675 lock (exercise_files)
676 exercise_files [i] = file;
678 Thread.Sleep (500 + rng.Next (500));