4 // Copyright (C) 2005 Novell, Inc.
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.
28 using System
.Diagnostics
;
30 using System
.Threading
;
36 namespace Beagle
.Daemon
{
38 public class RemoteIndexer
: IIndexer
{
40 static string helper_path
;
41 static int helper_pid
= -1;
43 string remote_index_name
;
44 int remote_index_minor_version
= 0;
45 int last_item_count
= -1;
47 static RemoteIndexer ()
49 string bihp
= Environment
.GetEnvironmentVariable ("_BEAGLED_INDEX_HELPER_PATH");
51 throw new Exception ("_BEAGLED_INDEX_HELPER_PATH not set!");
53 helper_path
= Path
.GetFullPath (Path
.Combine (bihp
, "beagled-index-helper"));
54 if (! File
.Exists (helper_path
))
55 throw new Exception ("Could not find " + helper_path
);
56 Logger
.Log
.Debug ("Found index helper at {0}", helper_path
);
59 static public IIndexer
NewRemoteIndexer (string name
, int minor_version
)
61 return new RemoteIndexer (name
, minor_version
);
64 public RemoteIndexer (string name
, int minor_version
)
66 this.remote_index_name
= name
;
67 this.remote_index_minor_version
= minor_version
;
70 public IndexerReceipt
[] Flush (IndexerRequest request
)
72 // If there isn't actually any work to do, just return
75 return new IndexerReceipt
[0];
77 // Iterate through the items in the IndexerRequest to
78 // store the streams before passing them to the helper.
79 foreach (Indexable indexable
in request
.Indexables
) {
80 if (indexable
.Type
== IndexableType
.Add
)
81 indexable
.StoreStream ();
84 RemoteIndexerRequest remote_request
;
85 remote_request
= new RemoteIndexerRequest ();
86 remote_request
.RemoteIndexName
= this.remote_index_name
;
87 remote_request
.RemoteIndexMinorVersion
= this.remote_index_minor_version
;
88 remote_request
.Request
= request
;
90 RemoteIndexerResponse response
;
91 response
= SendRequest (remote_request
);
93 if (response
== null) {
94 Logger
.Log
.Error ("Something terrible happened --- Flush failed");
99 last_item_count
= response
.ItemCount
;
101 return response
.Receipts
;
104 public int GetItemCount ()
106 if (last_item_count
== -1) {
107 // Send an empty indexing request to cause the last item count to be
109 RemoteIndexerRequest request
;
110 request
= new RemoteIndexerRequest ();
112 RemoteIndexerResponse response
;
113 response
= SendRequest (request
);
114 if (response
!= null)
115 last_item_count
= response
.ItemCount
;
117 Logger
.Log
.Error ("Something terrible happened --- GetItemCount failed");
120 return last_item_count
;
123 /////////////////////////////////////////////////////////
125 private RemoteIndexerResponse
SendRequest (RemoteIndexerRequest request
)
127 RemoteIndexerResponse response
= null;
128 int exception_count
= 0;
129 bool start_helper_by_hand
= false;
131 if (Environment
.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null)
132 start_helper_by_hand
= true;
134 request
.RemoteIndexName
= remote_index_name
;
135 request
.RemoteIndexMinorVersion
= remote_index_minor_version
;
137 while (response
== null
138 && exception_count
< 5
139 && ! Shutdown
.ShutdownRequested
) {
141 bool need_helper
= false;
143 //Logger.Log.Debug ("Sending request!");
145 response
= request
.Send () as RemoteIndexerResponse
;
146 //Logger.Log.Debug ("Done sending request");
147 } catch (ResponseMessageException ex
) {
148 Logger
.Log
.Debug ("Caught ResponseMessageException: {0}", ex
.Message
);
150 if (ex
.InnerException
is System
.Net
.Sockets
.SocketException
) {
151 Logger
.Log
.Debug ("InnerException is SocketException -- we probably need to launch a helper");
153 } else if (ex
.InnerException
is IOException
) {
154 Logger
.Log
.Debug ("InnerException is IOException -- we probably need to launch a helper");
157 Logger
.Log
.Debug ("Exception was unexpected. Details: {0}", ex
);
161 // If we caught an exception...
162 if (response
== null) {
163 if (! start_helper_by_hand
|| ! need_helper
)
166 if (start_helper_by_hand
) {
167 // Sleep briefly before trying again.
170 // Try to activate the helper.
176 if (response
== null && exception_count
>= 5)
177 Logger
.Log
.Error ("Exception limit exceeded trying to activate a helper. Giving up on indexing!");
182 /////////////////////////////////////////////////////////
184 static bool CheckHelper ()
186 string storage_dir
= PathFinder
.GetRemoteStorageDir (false);
188 if (storage_dir
== null)
191 // FIXME: We shouldn't need to know the path to the helper socket.
192 string socket_name
= Path
.Combine (storage_dir
, "socket-helper");
194 if (! File
.Exists (socket_name
))
197 // Open, and then immediately close, a connection to the helper's socket.
199 UnixClient test_client
;
200 test_client
= new UnixClient (socket_name
);
201 test_client
.Close ();
203 } catch (Exception ex
) {
208 static object helper_lock
= new object ();
210 static void LaunchHelper ()
212 // If we are in the process of shutting down, return immediately.
213 if (Shutdown
.ShutdownRequested
)
218 // If a helper appears to be running, return immediately.
222 Logger
.Log
.Debug ("Launching helper process");
224 SafeProcess p
= new SafeProcess ();
225 p
.Arguments
= new string [] { helper_path }
;
228 Logger
.Log
.Debug ("IndexHelper PID is {0}", p
.Id
);
230 // Poll the helper's socket. Wait up to a minute
231 // (500 ms * 120 times) for the helper to be ready
232 // to handle requests.
233 Stopwatch watch
= new Stopwatch ();
240 found_helper
= CheckHelper ();
241 } while (poll_count
< 120
243 && ! Shutdown
.ShutdownRequested
);
247 throw new Exception (String
.Format ("Couldn't launch helper process {0}", p
.Id
));
249 Logger
.Log
.Debug ("Found IndexHelper ({0}) in {1}", p
.Id
, watch
);
254 static public void SignalRemoteIndexer ()
256 // No helper running right now
257 if (helper_pid
== -1 || ! CheckHelper ()) {
262 Mono
.Unix
.Native
.Syscall
.kill (helper_pid
, Mono
.Unix
.Native
.Signum
.SIGUSR1
);