4 // Copyright (C) 2005 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // 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 FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 using System
.Collections
;
31 using SNS
= System
.Net
.Sockets
;
32 using System
.Threading
;
39 namespace Beagle
.IndexHelper
{
41 class IndexHelperTool
{
43 static DateTime last_activity
;
46 static void Main (string [] args
)
50 } catch (Exception ex
) {
51 Logger
.Log
.Error ("Unhandled exception thrown. Exiting immediately.");
52 Logger
.Log
.Error (ex
);
57 static void DoMain (string [] args
)
59 SystemInformation
.SetProcessName ("beagled-helper");
61 bool run_by_hand
= (Environment
.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
62 bool log_in_fg
= (Environment
.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
64 // FIXME: We always turn on full debugging output! We are still
65 // debugging this code, after all...
66 //bool debug = (Environment.GetEnvironmentVariable ("BEAGLE_DEBUG_FLAG_IS_SET") != null);
68 last_activity
= DateTime
.Now
;
70 Log
.Initialize (PathFinder
.LogDir
,
72 //debug ? LogLevel.Debug : LogLevel.Warn,
74 run_by_hand
|| log_in_fg
);
76 Application
.InitCheck ("IndexHelper", ref args
);
78 SetupSignalHandlers ();
80 Shutdown
.ShutdownEvent
+= OnShutdown
;
83 Logger
.Log
.Debug ("Starting messaging server");
84 bool server_has_been_started
= false;
86 server
= new Server ("socket-helper");
88 server_has_been_started
= true;
89 } catch (InvalidOperationException ex
) {
90 Logger
.Log
.Error ("Couldn't start server:");
91 Logger
.Log
.Error (ex
);
94 if (server_has_been_started
) {
95 // Set the IO priority to idle so we don't slow down the system
96 if (Environment
.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG") == null)
97 IoPriority
.SetIdle ();
99 // Start the monitor thread, which keeps an eye on memory usage and idle time.
100 ExceptionHandlingThread
.Start (new ThreadStart (MemoryAndIdleMonitorWorker
));
102 // Start a thread that watches the daemon and begins a shutdown
104 ExceptionHandlingThread
.Start (new ThreadStart (DaemonMonitorWorker
));
108 // If we palced our sockets in a temp directory, try to clean it up
109 // Note: this may fail because the daemon is still running
110 if (PathFinder
.GetRemoteStorageDir (false) != PathFinder
.StorageDir
) {
112 Directory
.Delete (PathFinder
.GetRemoteStorageDir (false));
113 } catch (IOException
) { }
118 static public void ReportActivity ()
120 last_activity
= DateTime
.Now
;
123 static void MemoryAndIdleMonitorWorker ()
125 int vmrss_original
= SystemInformation
.VmRss
;
127 const double max_idle_time
= 30; // minutes
129 const double threshold
= 5.0;
130 const int max_request_count
= 0;
133 while (! Shutdown
.ShutdownRequested
) {
136 idle_time
= (DateTime
.Now
- last_activity
).TotalMinutes
;
137 if (idle_time
> max_idle_time
&& RemoteIndexerExecutor
.Count
> 0) {
138 Logger
.Log
.Debug ("No activity for {0:0.0} minutes, shutting down", idle_time
);
139 Shutdown
.BeginShutdown ();
143 // Check resident memory usage
144 int vmrss
= SystemInformation
.VmRss
;
145 double size
= vmrss
/ (double) vmrss_original
;
146 if (vmrss
!= last_vmrss
)
147 Logger
.Log
.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
148 vmrss
/1024.0, size
, 100.0 * (size
- 1) / (threshold
- 1));
151 || (max_request_count
> 0 && RemoteIndexerExecutor
.Count
> max_request_count
)) {
152 if (RemoteIndexerExecutor
.Count
> 0) {
153 Logger
.Log
.Debug ("Process too big, shutting down!");
154 Shutdown
.BeginShutdown ();
157 // Paranoia: don't shut down if we haven't done anything yet
158 Logger
.Log
.Debug ("Deferring shutdown until we've actually done something.");
167 static void DaemonMonitorWorker ()
169 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
171 if (storage_dir
== null) {
172 Logger
.Log
.Debug ("The daemon doesn't appear to have started");
173 Logger
.Log
.Debug ("Shutting down helper.");
174 Shutdown
.BeginShutdown ();
178 // FIXME: We shouldn't need to know the name of the daemon's socket.
180 socket_name
= Path
.Combine (storage_dir
, "socket");
184 socket
= new SNS
.Socket (SNS
.AddressFamily
.Unix
, SNS
.SocketType
.Stream
, 0);
185 socket
.Connect (new Mono
.Unix
.UnixEndPoint (socket_name
));
187 ArrayList socket_list
= new ArrayList ();
189 while (! Shutdown
.ShutdownRequested
) {
190 socket_list
.Add (socket
);
191 SNS
.Socket
.Select (socket_list
, null, null, 1000000); // 1000000 microseconds = 1 second
192 if (socket_list
.Count
!= 0) {
193 Logger
.Log
.Debug ("The daemon appears to have gone away.");
194 Logger
.Log
.Debug ("Shutting down helper.");
195 Shutdown
.BeginShutdown ();
198 } catch (SNS
.SocketException
) {
199 Logger
.Log
.Debug ("Caught a SocketException while trying to monitor the daemon");
200 Logger
.Log
.Debug ("Shutting down");
201 Shutdown
.BeginShutdown ();
205 /////////////////////////////////////////////////////////////////////////////
207 static void SetupSignalHandlers ()
209 // Force OurSignalHandler to be JITed
210 OurSignalHandler (-1);
212 // Set up our signal handler
213 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGINT
, OurSignalHandler
);
214 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGTERM
, OurSignalHandler
);
215 if (Environment
.GetEnvironmentVariable("BEAGLE_THERE_BE_NO_QUITTIN") == null)
216 Mono
.Unix
.Native
.Stdlib
.signal (Mono
.Unix
.Native
.Signum
.SIGQUIT
, OurSignalHandler
);
219 // Our handler triggers an orderly shutdown when it receives a signal.
220 // However, this can be annoying if the process gets wedged during
221 // shutdown. To deal with that case, we make a note of the time when
222 // the first signal comes in, and we allow signals to unconditionally
223 // kill the process after 5 seconds have passed.
224 static DateTime signal_time
= DateTime
.MinValue
;
225 static void OurSignalHandler (int signal
)
227 // This allows us to call OurSignalHandler w/o doing anything.
228 // We want to call it once to ensure that it is pre-JITed.
231 Logger
.Log
.Debug ("Handling signal {0}", signal
);
233 bool first_signal
= false;
234 if (signal_time
== DateTime
.MinValue
) {
235 signal_time
= DateTime
.Now
;
239 if (Shutdown
.ShutdownRequested
) {
242 Logger
.Log
.Debug ("Shutdown already in progress.");
244 double t
= (DateTime
.Now
- signal_time
).TotalSeconds
;
245 const double min_t
= 5;
248 Logger
.Log
.Debug ("Signals can force an immediate shutdown in {0:0.00}s", min_t
-t
);
250 Logger
.Log
.Debug ("Forcing immediate shutdown.");
251 Environment
.Exit (0);
256 Logger
.Log
.Debug ("Initiating shutdown in response to signal.");
257 Shutdown
.BeginShutdown ();
261 static void OnShutdown ()