2626#include < sys/types.h>
2727
2828#include < stdio.h>
29+ #include < libgen.h>
2930
3031// read
3132#include < unistd.h>
3233#include < sys/uio.h>
3334#include < sys/types.h>
3435
36+ #include < ostream>
37+
3538namespace rubinius {
3639 using namespace utilities ;
3740
@@ -53,37 +56,31 @@ namespace rubinius {
5356 return fd;
5457 }
5558
56- Request::Request (STATE, Console* console)
59+ Request::Request (STATE, Console* console, Response* response )
5760 : InternalThread(state, " rbx.console.request" )
5861 , console_(console)
59- , response_(console-> response () )
62+ , response_(response)
6063 , enabled_(false )
6164 , fd_(-1 )
6265 , fsevent_(state)
6366 {
6467 }
6568
6669 void Request::initialize (STATE) {
67- path_ = state->shared ().fsapi_path + " /console-request" ;
68- console_->server_class (state)->set_const (state, state->symbol (" RequestPath" ),
69- String::create (state, path_.c_str ()));
70- }
71-
72- void Request::setup_request (STATE) {
73- if ((fd_ = open_file (state, path_)) < 0 ) {
70+ if ((fd_ = open_file (state, console_->request_path ())) < 0 ) {
7471 logger::error (" %s: console request: unable to open file" , strerror (errno));
7572 return ;
7673 }
7774
7875 FSEvent* fsevent = FSEvent::create (state);
79- fsevent->watch_file (state, fd_, path_ .c_str ());
76+ fsevent->watch_file (state, fd_, console_-> request_path () .c_str ());
8077 fsevent_.set (fsevent);
8178
8279 enabled_ = true ;
8380 }
8481
8582 void Request::start_thread (STATE) {
86- setup_request (state) ;
83+ if (!enabled_) return ;
8784
8885 InternalThread::start_thread (state);
8986 }
@@ -96,7 +93,7 @@ namespace rubinius {
9693 }
9794 }
9895
99- void Request::close_request (STATE ) {
96+ void Request::close_request () {
10097 if (fd_ > 0 ) {
10198 close (fd_);
10299 fd_ = -1 ;
@@ -106,22 +103,11 @@ namespace rubinius {
106103 void Request::stop_thread (STATE) {
107104 InternalThread::stop_thread (state);
108105
109- close_request (state);
110- unlink (path_.c_str ());
111- }
112-
113- void Request::before_exec (STATE) {
114- stop_thread (state);
115- }
116-
117- void Request::after_exec (STATE) {
118- start (state);
106+ close_request ();
119107 }
120108
121109 void Request::after_fork_child (STATE) {
122- close_request (state);
123-
124- InternalThread::after_fork_child (state);
110+ close_request ();
125111 }
126112
127113 char * Request::read_request (STATE) {
@@ -182,9 +168,15 @@ namespace rubinius {
182168 Response::Response (STATE, Console* console)
183169 : InternalThread(state, " rbx.console.response" )
184170 , console_(console)
171+ , inbox_(state)
172+ , outbox_(state)
185173 , fd_(-1 )
186174 , request_list_(NULL )
187175 {
176+ inbox_.set (as<Channel>(
177+ console_->ruby_console ()->get_ivar (state, state->symbol (" @inbox" ))));
178+ outbox_.set (as<Channel>(
179+ console_->ruby_console ()->get_ivar (state, state->symbol (" @outbox" ))));
188180 }
189181
190182 Response::~Response () {
@@ -193,15 +185,11 @@ namespace rubinius {
193185 }
194186
195187 void Response::initialize (STATE) {
196- path_ = state->shared ().fsapi_path + " /console-response" ;
197- console_->server_class (state)->set_const (state, state->symbol (" ResponsePath" ),
198- String::create (state, path_.c_str ()));
199-
200188 Thread::create (state, vm ());
201189 }
202190
203191 void Response::start_thread (STATE) {
204- if ((fd_ = open_file (state, path_ )) < 0 ) {
192+ if ((fd_ = open_file (state, console_-> response_path () )) < 0 ) {
205193 logger::error (" %s: console response: unable to open file" , strerror (errno));
206194 return ;
207195 }
@@ -218,17 +206,20 @@ namespace rubinius {
218206 void Response::wakeup (STATE) {
219207 InternalThread::wakeup (state);
220208
209+ GCTokenImpl gct;
210+ inbox_.get ()->send (state, gct, String::create (state, " " ), 0 );
211+
221212 response_cond_.signal ();
222213 }
223214
224- void Response::close_response (STATE ) {
215+ void Response::close_response () {
225216 if (fd_ > 0 ) {
226217 close (fd_);
227218 fd_ = -1 ;
228219 }
229220 }
230221
231- void Response::clear_requests (STATE ) {
222+ void Response::clear_requests () {
232223 if (request_list_) {
233224 for (RequestList::const_iterator i = request_list_->begin ();
234225 i != request_list_->end ();
@@ -244,25 +235,11 @@ namespace rubinius {
244235 void Response::stop_thread (STATE) {
245236 InternalThread::stop_thread (state);
246237
247- clear_requests (state);
248-
249- close_response (state);
250- unlink (path_.c_str ());
251- }
252-
253- void Response::before_exec (STATE) {
254- stop_thread (state);
255- }
256-
257- void Response::after_exec (STATE) {
258- start (state);
238+ close_response ();
259239 }
260240
261241 void Response::after_fork_child (STATE) {
262- close_response (state);
263- clear_requests (state);
264-
265- InternalThread::after_fork_child (state);
242+ close_response ();
266243 }
267244
268245 void Response::send_request (STATE, const char * request) {
@@ -301,10 +278,8 @@ namespace rubinius {
301278 size_t pending_requests = 0 ;
302279 char * request = NULL ;
303280
304- Channel* inbox = as<Channel>(
305- console_->ruby_console ()->get_ivar (state, state->symbol (" @inbox" )));
306- Channel* outbox = as<Channel>(
307- console_->ruby_console ()->get_ivar (state, state->symbol (" @outbox" )));
281+ Channel* inbox = inbox_.get ();
282+ Channel* outbox = outbox_.get ();
308283
309284 String* response = 0 ;
310285 OnStack<3 > os (state, inbox, outbox, response);
@@ -352,23 +327,135 @@ namespace rubinius {
352327 }
353328 }
354329
330+ Listener::Listener (STATE, Console* console)
331+ : InternalThread(state, " rbx.console.listener" )
332+ , console_(console)
333+ , fsevent_(state)
334+ , fd_(-1 )
335+ {
336+ }
337+
338+ Listener::~Listener () {
339+ close (fd_);
340+ }
341+
342+ void Listener::initialize (STATE) {
343+ fd_ = ::open (console_->console_path ().c_str (),
344+ O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC,
345+ state->shared ().config .system_console_access .value );
346+
347+ if (fd_ < 0 ) {
348+ utilities::logger::error (" %s: unable to open Console connection file" ,
349+ strerror (errno));
350+ }
351+
352+ // The umask setting will override our permissions for open().
353+ if (chmod (console_->console_path ().c_str (),
354+ state->shared ().config .system_console_access .value ) < 0 ) {
355+ utilities::logger::error (" %s: unable to set mode for Console connection file" ,
356+ strerror (errno));
357+ }
358+
359+ FSEvent* fsevent = FSEvent::create (state);
360+ fsevent->watch_file (state, fd_, console_->console_path ().c_str ());
361+ fsevent_.set (fsevent);
362+ }
363+
364+ void Listener::start_thread (STATE) {
365+ InternalThread::start_thread (state);
366+ }
367+
368+ void Listener::wakeup (STATE) {
369+ InternalThread::wakeup (state);
370+
371+ if (write (fd_, " \0 " , 1 ) < 0 ) {
372+ logger::error (" %s: console: unable to wake listener thread" , strerror (errno));
373+ }
374+ }
375+
376+ bool Listener::connection_initiated () {
377+ struct stat st;
378+
379+ bool req = stat (console_->request_path ().c_str (), &st) == 0 && S_ISREG (st.st_mode );
380+ bool res = stat (console_->response_path ().c_str (), &st) == 0 && S_ISREG (st.st_mode );
381+
382+ return req && res;
383+ }
384+
385+ void Listener::run (STATE) {
386+ while (!thread_exit_) {
387+ Object* status = fsevent_.get ()->wait_for_event (state);
388+
389+ if (thread_exit_) break ;
390+
391+ if (status->nil_p ()) {
392+ utilities::logger::error (" %s: console: listener: wait for event failed" ,
393+ strerror (errno));
394+ continue ;
395+ }
396+
397+ if (console_->connected_p ()) continue ;
398+
399+ if (connection_initiated ()) {
400+ console_->accept (state);
401+ }
402+ }
403+ }
404+
355405 Console::Console (STATE)
356- : path_(std::string(state-> shared ().fsapi_path + "/console") )
357- , response_(new Response(state, this ) )
358- , request_(new Request(state, this ) )
406+ : listener_( 0 )
407+ , response_(0 )
408+ , request_(0 )
359409 , ruby_console_(state)
360410 {
361- ruby_console_.set (server_class (state)->send (state, 0 , state->symbol (" new" )));
411+ console_path_ = state->shared ().config .system_console_path .value ;
412+
413+ std::ostringstream basename;
414+ basename << state->shared ().config .system_console_path .value << " -"
415+ << state->shared ().pid ;
416+
417+ request_path_ = basename.str () + " -request" ;
418+ response_path_ = basename.str () + " -response" ;
419+
420+ listener_ = new Listener (state, this );
362421 }
363422
364423 Console::~Console () {
365- delete request_;
366- delete response_;
424+ if (listener_) delete listener_;
425+ reset ();
426+ }
427+
428+ bool Console::connected_p () {
429+ return request_ && request_->enabled_p ();
367430 }
368431
369432 void Console::start (STATE) {
370- request_->start (state);
371- response_->start (state);
433+ listener_->start (state);
434+ }
435+
436+ void Console::accept (STATE) {
437+ ruby_console_.set (server_class (state)->send (state, 0 , state->symbol (" new" )));
438+
439+ response_ = new Response (state, this );
440+ request_ = new Request (state, this , response_);
441+ }
442+
443+ void Console::reset () {
444+ if (request_) {
445+ delete request_;
446+ request_ = 0 ;
447+ }
448+
449+ if (response_) {
450+ delete response_;
451+ response_ = 0 ;
452+ }
453+
454+ ruby_console_.set (cNil);
455+ }
456+
457+ void Console::after_fork_child (STATE) {
458+ reset ();
372459 }
373460
374461 Class* Console::server_class (STATE) {
0 commit comments