Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include "sal/config.h"
21 :
22 : #include <cassert>
23 :
24 : #include <string.h>
25 : #include <unistd.h>
26 : #include <sys/poll.h>
27 : #include <fcntl.h>
28 :
29 : #include <stdio.h>
30 :
31 : #include <rtl/strbuf.hxx>
32 :
33 : #include <rtl/process.h>
34 : #include <osl/security.h>
35 : #include <osl/conditn.h>
36 :
37 : #include <tools/prex.h>
38 : #include <X11/Xatom.h>
39 : #include <tools/postx.h>
40 :
41 : #include <unx/sm.hxx>
42 : #include <unx/saldata.hxx>
43 : #include <unx/saldisp.hxx>
44 : #include <unx/salframe.h>
45 : #include <unx/salinst.h>
46 :
47 : #include <vcl/svapp.hxx>
48 : #include <vcl/window.hxx>
49 :
50 : #include "salsession.hxx"
51 :
52 : namespace {
53 :
54 : class IceSalSession : public SalSession
55 : {
56 : public:
57 0 : IceSalSession() {}
58 :
59 : private:
60 0 : virtual ~IceSalSession() {}
61 :
62 : virtual void queryInteraction();
63 : virtual void interactionDone();
64 : virtual void saveDone();
65 : virtual bool cancelShutdown();
66 : };
67 :
68 : }
69 :
70 0 : SalSession* X11SalInstance::CreateSalSession()
71 : {
72 0 : SalSession * p = new IceSalSession;
73 0 : SessionManagerClient::open(p);
74 0 : return p;
75 : }
76 :
77 0 : void IceSalSession::queryInteraction()
78 : {
79 0 : if( ! SessionManagerClient::queryInteraction() )
80 : {
81 0 : SalSessionInteractionEvent aEvent( false );
82 0 : CallCallback( &aEvent );
83 : }
84 0 : }
85 :
86 0 : void IceSalSession::interactionDone()
87 : {
88 0 : SessionManagerClient::interactionDone( false );
89 0 : }
90 :
91 0 : void IceSalSession::saveDone()
92 : {
93 0 : SessionManagerClient::saveDone();
94 0 : }
95 :
96 0 : bool IceSalSession::cancelShutdown()
97 : {
98 0 : SessionManagerClient::interactionDone( true );
99 0 : return false;
100 : }
101 :
102 : extern "C" void ICEWatchProc(
103 : IceConn ice_conn, IcePointer client_data, Bool opening,
104 : IcePointer * watch_data);
105 :
106 : extern "C" void SAL_CALL ICEConnectionWorker(void * data);
107 :
108 0 : class ICEConnectionObserver
109 : {
110 : friend void ICEWatchProc(IceConn, IcePointer, Bool, IcePointer *);
111 :
112 : friend void ICEConnectionWorker(void *);
113 :
114 : struct pollfd* m_pFilehandles;
115 : int m_nConnections;
116 : IceConn* m_pConnections;
117 : int m_nWakeupFiles[2];
118 : oslThread m_ICEThread;
119 : IceIOErrorHandler m_origIOErrorHandler;
120 : IceErrorHandler m_origErrorHandler;
121 :
122 : void wakeup();
123 :
124 : public:
125 : osl::Mutex m_ICEMutex;
126 :
127 0 : ICEConnectionObserver():
128 : m_pFilehandles(NULL), m_nConnections(0), m_pConnections(NULL),
129 0 : m_ICEThread(NULL)
130 0 : { m_nWakeupFiles[0] = m_nWakeupFiles[1] = 0; }
131 :
132 : void activate();
133 : void deactivate();
134 : void terminate(oslThread iceThread);
135 : };
136 :
137 : SalSession * SessionManagerClient::m_pSession = NULL;
138 : boost::scoped_ptr< ICEConnectionObserver >
139 0 : SessionManagerClient::m_pICEConnectionObserver;
140 : SmcConn SessionManagerClient::m_pSmcConnection = NULL;
141 0 : rtl::OString SessionManagerClient::m_aClientID;
142 : bool SessionManagerClient::m_bDocSaveDone = false; // HACK
143 :
144 : extern "C" {
145 :
146 0 : static void IgnoreIceErrors(
147 : SAL_UNUSED_PARAMETER IceConn, SAL_UNUSED_PARAMETER Bool,
148 : SAL_UNUSED_PARAMETER int, SAL_UNUSED_PARAMETER unsigned long,
149 : SAL_UNUSED_PARAMETER int, SAL_UNUSED_PARAMETER int,
150 : SAL_UNUSED_PARAMETER IcePointer)
151 0 : {}
152 :
153 0 : static void IgnoreIceIOErrors(SAL_UNUSED_PARAMETER IceConn) {}
154 :
155 : }
156 :
157 : static SmProp* pSmProps = NULL;
158 : static SmProp** ppSmProps = NULL;
159 : static int nSmProps = 0;
160 : static unsigned char *pSmRestartHint = NULL;
161 :
162 :
163 0 : static void BuildSmPropertyList()
164 : {
165 0 : if( ! pSmProps )
166 : {
167 0 : rtl::OString aExec(rtl::OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding()));
168 :
169 0 : nSmProps = 5;
170 0 : pSmProps = new SmProp[ nSmProps ];
171 :
172 0 : pSmProps[ 0 ].name = const_cast<char*>(SmCloneCommand);
173 0 : pSmProps[ 0 ].type = const_cast<char*>(SmLISTofARRAY8);
174 0 : pSmProps[ 0 ].num_vals = 1;
175 0 : pSmProps[ 0 ].vals = new SmPropValue;
176 0 : pSmProps[ 0 ].vals->length = aExec.getLength()+1;
177 0 : pSmProps[ 0 ].vals->value = strdup( aExec.getStr() );
178 :
179 0 : pSmProps[ 1 ].name = const_cast<char*>(SmProgram);
180 0 : pSmProps[ 1 ].type = const_cast<char*>(SmARRAY8);
181 0 : pSmProps[ 1 ].num_vals = 1;
182 0 : pSmProps[ 1 ].vals = new SmPropValue;
183 0 : pSmProps[ 1 ].vals->length = aExec.getLength()+1;
184 0 : pSmProps[ 1 ].vals->value = strdup( aExec.getStr() );
185 :
186 0 : pSmProps[ 2 ].name = const_cast<char*>(SmRestartCommand);
187 0 : pSmProps[ 2 ].type = const_cast<char*>(SmLISTofARRAY8);
188 0 : pSmProps[ 2 ].num_vals = 3;
189 0 : pSmProps[ 2 ].vals = new SmPropValue[3];
190 0 : pSmProps[ 2 ].vals[0].length = aExec.getLength()+1;
191 0 : pSmProps[ 2 ].vals[0].value = strdup( aExec.getStr() );
192 0 : rtl::OStringBuffer aRestartOption;
193 0 : aRestartOption.append(RTL_CONSTASCII_STRINGPARAM("--session="));
194 0 : aRestartOption.append(SessionManagerClient::getSessionID());
195 0 : pSmProps[ 2 ].vals[1].length = aRestartOption.getLength()+1;
196 0 : pSmProps[ 2 ].vals[1].value = strdup(aRestartOption.getStr());
197 0 : rtl::OString aRestartOptionNoLogo(RTL_CONSTASCII_STRINGPARAM("--nologo"));
198 0 : pSmProps[ 2 ].vals[2].length = aRestartOptionNoLogo.getLength()+1;
199 0 : pSmProps[ 2 ].vals[2].value = strdup(aRestartOptionNoLogo.getStr());
200 :
201 0 : rtl::OUString aUserName;
202 0 : rtl::OString aUser;
203 0 : oslSecurity aSec = osl_getCurrentSecurity();
204 0 : if( aSec )
205 : {
206 0 : osl_getUserName( aSec, &aUserName.pData );
207 0 : aUser = rtl::OUStringToOString( aUserName, osl_getThreadTextEncoding() );
208 0 : osl_freeSecurityHandle( aSec );
209 : }
210 :
211 0 : pSmProps[ 3 ].name = const_cast<char*>(SmUserID);
212 0 : pSmProps[ 3 ].type = const_cast<char*>(SmARRAY8);
213 0 : pSmProps[ 3 ].num_vals = 1;
214 0 : pSmProps[ 3 ].vals = new SmPropValue;
215 0 : pSmProps[ 3 ].vals->value = strdup( aUser.getStr() );
216 0 : pSmProps[ 3 ].vals->length = rtl_str_getLength( (char *)pSmProps[ 3 ].vals->value )+1;
217 :
218 0 : pSmProps[ 4 ].name = const_cast<char*>(SmRestartStyleHint);
219 0 : pSmProps[ 4 ].type = const_cast<char*>(SmCARD8);
220 0 : pSmProps[ 4 ].num_vals = 1;
221 0 : pSmProps[ 4 ].vals = new SmPropValue;
222 0 : pSmProps[ 4 ].vals->value = malloc(1);
223 0 : pSmRestartHint = (unsigned char *)pSmProps[ 4 ].vals->value;
224 0 : *pSmRestartHint = SmRestartIfRunning;
225 0 : pSmProps[ 4 ].vals->length = 1;
226 :
227 0 : ppSmProps = new SmProp*[ nSmProps ];
228 0 : for( int i = 0; i < nSmProps; i++ )
229 0 : ppSmProps[ i ] = &pSmProps[i];
230 : }
231 0 : }
232 :
233 0 : bool SessionManagerClient::checkDocumentsSaved()
234 : {
235 0 : return m_bDocSaveDone;
236 : }
237 :
238 0 : IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, SaveYourselfHdl, void*, pStateVal )
239 : {
240 : // Decode argument smuggled in as void*:
241 0 : sal_uIntPtr nStateVal = reinterpret_cast< sal_uIntPtr >(pStateVal);
242 0 : Bool shutdown = nStateVal != 0;
243 :
244 : SAL_INFO("vcl.sm", "posting save documents event shutdown = " << (shutdown ? "true" : "false" ));
245 :
246 : static bool bFirstShutdown=true;
247 0 : if (shutdown && bFirstShutdown) //first shutdown request
248 : {
249 0 : bFirstShutdown = false;
250 : /*
251 : If we have no actual frames open, e.g. we launched a quickstarter,
252 : and then shutdown all our frames leaving just a quickstarter running,
253 : then we don't want to launch an empty toplevel frame on the next
254 : start. (The job of scheduling the restart of the quick-starter is a
255 : task of the quick-starter)
256 : */
257 0 : *pSmRestartHint = SmRestartNever;
258 0 : const std::list< SalFrame* >& rFrames = GetGenericData()->GetSalDisplay()->getFrames();
259 0 : for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
260 : {
261 0 : Window *pWindow = (*it)->GetWindow();
262 0 : if (pWindow && pWindow->IsVisible())
263 : {
264 0 : *pSmRestartHint = SmRestartIfRunning;
265 0 : break;
266 : }
267 : }
268 : }
269 :
270 0 : if( m_pSession )
271 : {
272 0 : SalSessionSaveRequestEvent aEvent( shutdown, false );
273 0 : m_pSession->CallCallback( &aEvent );
274 : }
275 : else
276 0 : saveDone();
277 :
278 0 : return 0;
279 : }
280 :
281 0 : IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, InteractionHdl, void*, EMPTYARG )
282 : {
283 : SAL_INFO("vcl.sm", "interaction link");
284 0 : if( m_pSession )
285 : {
286 0 : SalSessionInteractionEvent aEvent( true );
287 0 : m_pSession->CallCallback( &aEvent );
288 : }
289 :
290 0 : return 0;
291 : }
292 :
293 0 : IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownCancelHdl, void*, EMPTYARG )
294 : {
295 : SAL_INFO("vcl.sm", "shutdown cancel");
296 0 : if( m_pSession )
297 : {
298 0 : SalSessionShutdownCancelEvent aEvent;
299 0 : m_pSession->CallCallback( &aEvent );
300 : }
301 :
302 0 : return 0;
303 : }
304 :
305 0 : void SessionManagerClient::SaveYourselfProc(
306 : SmcConn,
307 : SmPointer,
308 : int save_type,
309 : Bool shutdown,
310 : int interact_style,
311 : Bool
312 : )
313 : {
314 : SAL_INFO("vcl.sm", "Session: save yourself," <<
315 : "save_type " <<
316 : " local: " << (save_type == SmSaveLocal) <<
317 : " global: " << (save_type == SmSaveGlobal) <<
318 : " both: " << (save_type == SmSaveBoth) <<
319 : " shutdown: " << shutdown <<
320 : " interact_style: " <<
321 : " SmInteractStyleNone: " << (interact_style == SmInteractStyleNone) <<
322 : " SmInteractStyleErrors: " << (interact_style == SmInteractStyleErrors) <<
323 : " SmInteractStyleErrors: " << (interact_style == SmInteractStyleAny));
324 0 : BuildSmPropertyList();
325 0 : m_bDocSaveDone = false;
326 : /* #i49875# some session managers send a "die" message if the
327 : * saveDone does not come early enough for their convenience
328 : * this can occasionally happen on startup, especially the first
329 : * startup. So shortcut the "not shutting down" case since the
330 : * upper layers are currently not interested in that event anyway.
331 : */
332 0 : if( ! shutdown )
333 : {
334 0 : SessionManagerClient::saveDone();
335 0 : return;
336 : }
337 : // Smuggle argument in as void*:
338 0 : sal_uIntPtr nStateVal = shutdown;
339 0 : Application::PostUserEvent( STATIC_LINK( 0, SessionManagerClient, SaveYourselfHdl ), reinterpret_cast< void * >(nStateVal) );
340 : SAL_INFO("vcl.sm", "waiting for save yourself event to be processed" );
341 : }
342 :
343 0 : IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownHdl, void*, EMPTYARG )
344 : {
345 0 : if( m_pSession )
346 : {
347 0 : SalSessionQuitEvent aEvent;
348 0 : m_pSession->CallCallback( &aEvent );
349 : }
350 :
351 0 : const std::list< SalFrame* >& rFrames = GetGenericData()->GetSalDisplay()->getFrames();
352 : SAL_INFO("vcl.sm", (rFrames.begin() != rFrames.end() ? "shutdown on first frame" : "shutdown event but no frame"));
353 0 : if( rFrames.begin() != rFrames.end() )
354 0 : rFrames.front()->CallCallback( SALEVENT_SHUTDOWN, 0 );
355 0 : return 0;
356 : }
357 :
358 0 : void SessionManagerClient::DieProc(
359 : SmcConn connection,
360 : SmPointer
361 : )
362 : {
363 : SAL_INFO("vcl.sm", "Session: die");
364 0 : if( connection == m_pSmcConnection )
365 : {
366 0 : Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownHdl ) );
367 : SAL_INFO("vcl.sm", "waiting for shutdown event to be processed" );
368 : }
369 0 : }
370 :
371 0 : void SessionManagerClient::SaveCompleteProc(
372 : SmcConn,
373 : SmPointer
374 : )
375 : {
376 : SAL_INFO("vcl.sm", "Session: save complete");
377 0 : }
378 :
379 0 : void SessionManagerClient::ShutdownCanceledProc(
380 : SmcConn connection,
381 : SmPointer )
382 : {
383 : SAL_INFO("vcl.sm", "Session: shutdown canceled" );
384 0 : if( connection == m_pSmcConnection )
385 0 : Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownCancelHdl ) );
386 0 : }
387 :
388 0 : void SessionManagerClient::InteractProc(
389 : SmcConn connection,
390 : SmPointer )
391 : {
392 : SAL_INFO("vcl.sm", "Session: interaction request completed" );
393 0 : if( connection == m_pSmcConnection )
394 0 : Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, InteractionHdl ) );
395 0 : }
396 :
397 0 : void SessionManagerClient::saveDone()
398 : {
399 0 : if( m_pSmcConnection )
400 : {
401 : assert(m_pICEConnectionObserver);
402 0 : osl::MutexGuard g(m_pICEConnectionObserver->m_ICEMutex);
403 0 : SmcSetProperties( m_pSmcConnection, nSmProps, ppSmProps );
404 0 : SmcSaveYourselfDone( m_pSmcConnection, True );
405 : SAL_INFO("vcl.sm", "sent SaveYourselfDone SmRestartHint of " << *pSmRestartHint );
406 0 : m_bDocSaveDone = true;
407 : }
408 0 : }
409 :
410 :
411 0 : void SessionManagerClient::open(SalSession * pSession)
412 : {
413 : assert(!m_pSession && !m_pICEConnectionObserver && !m_pSmcConnection);
414 : // must only be called once
415 0 : m_pSession = pSession;
416 : // This is the way Xt does it, so we can too:
417 0 : if( getenv( "SESSION_MANAGER" ) )
418 : {
419 0 : m_pICEConnectionObserver.reset(new ICEConnectionObserver);
420 0 : m_pICEConnectionObserver->activate();
421 :
422 : {
423 0 : osl::MutexGuard g(m_pICEConnectionObserver->m_ICEMutex);
424 :
425 : static SmcCallbacks aCallbacks; // does this need to be static?
426 0 : aCallbacks.save_yourself.callback = SaveYourselfProc;
427 0 : aCallbacks.save_yourself.client_data = NULL;
428 0 : aCallbacks.die.callback = DieProc;
429 0 : aCallbacks.die.client_data = NULL;
430 0 : aCallbacks.save_complete.callback = SaveCompleteProc;
431 0 : aCallbacks.save_complete.client_data = NULL;
432 0 : aCallbacks.shutdown_cancelled.callback = ShutdownCanceledProc;
433 0 : aCallbacks.shutdown_cancelled.client_data = NULL;
434 0 : rtl::OString aPrevId(getPreviousSessionID());
435 0 : char* pClientID = NULL;
436 : char aErrBuf[1024];
437 : m_pSmcConnection = SmcOpenConnection( NULL,
438 : NULL,
439 : SmProtoMajor,
440 : SmProtoMinor,
441 : SmcSaveYourselfProcMask |
442 : SmcDieProcMask |
443 : SmcSaveCompleteProcMask |
444 : SmcShutdownCancelledProcMask ,
445 : &aCallbacks,
446 0 : aPrevId.isEmpty() ? NULL : const_cast<char*>(aPrevId.getStr()),
447 : &pClientID,
448 : sizeof( aErrBuf ),
449 0 : aErrBuf );
450 0 : if( !m_pSmcConnection )
451 : SAL_INFO("vcl.sm", "SmcOpenConnection failed: " << aErrBuf);
452 : else
453 : SAL_INFO("vcl.sm", "SmcOpenConnection succeeded, client ID is " << pClientID );
454 0 : m_aClientID = rtl::OString(pClientID);
455 0 : free( pClientID );
456 0 : pClientID = NULL;
457 : }
458 :
459 0 : SalDisplay* pDisp = GetGenericData()->GetSalDisplay();
460 0 : if( pDisp->GetDrawable(pDisp->GetDefaultXScreen()) && !m_aClientID.isEmpty() )
461 : {
462 : XChangeProperty( pDisp->GetDisplay(),
463 : pDisp->GetDrawable( pDisp->GetDefaultXScreen() ),
464 : XInternAtom( pDisp->GetDisplay(), "SM_CLIENT_ID", False ),
465 : XA_STRING,
466 : 8,
467 : PropModeReplace,
468 0 : (unsigned char*)m_aClientID.getStr(),
469 0 : m_aClientID.getLength()
470 0 : );
471 : }
472 : }
473 : else
474 : {
475 : SAL_INFO("vcl.sm", "no SESSION_MANAGER");
476 : }
477 0 : }
478 :
479 0 : rtl::OString SessionManagerClient::getSessionID()
480 : {
481 0 : return m_aClientID;
482 : }
483 :
484 0 : void SessionManagerClient::close()
485 : {
486 0 : if( m_pSmcConnection )
487 : {
488 : assert(m_pICEConnectionObserver);
489 : {
490 0 : osl::MutexGuard g(m_pICEConnectionObserver->m_ICEMutex);
491 : SAL_INFO("vcl.sm", "attempting SmcCloseConnection");
492 0 : SmcCloseConnection( m_pSmcConnection, 0, NULL );
493 0 : SAL_INFO("vcl.sm", "SmcConnection closed");
494 : }
495 0 : m_pICEConnectionObserver->deactivate();
496 0 : m_pICEConnectionObserver.reset();
497 0 : m_pSmcConnection = NULL;
498 : }
499 0 : }
500 :
501 0 : bool SessionManagerClient::queryInteraction()
502 : {
503 0 : bool bRet = false;
504 0 : if( m_pSmcConnection )
505 : {
506 : assert(m_pICEConnectionObserver);
507 0 : osl::MutexGuard g(m_pICEConnectionObserver->m_ICEMutex);
508 0 : if( SmcInteractRequest( m_pSmcConnection, SmDialogNormal, InteractProc, NULL ) )
509 0 : bRet = true;
510 : }
511 0 : return bRet;
512 : }
513 :
514 0 : void SessionManagerClient::interactionDone( bool bCancelShutdown )
515 : {
516 0 : if( m_pSmcConnection )
517 : {
518 : assert(m_pICEConnectionObserver);
519 0 : osl::MutexGuard g(m_pICEConnectionObserver->m_ICEMutex);
520 0 : SmcInteractDone( m_pSmcConnection, bCancelShutdown ? True : False );
521 : }
522 0 : }
523 :
524 :
525 0 : rtl::OUString SessionManagerClient::getExecName()
526 : {
527 0 : rtl::OUString aExec, aSysExec;
528 0 : osl_getExecutableFile( &aExec.pData );
529 0 : osl_getSystemPathFromFileURL( aExec.pData, &aSysExec.pData );
530 :
531 0 : if( aSysExec.endsWith(".bin") )
532 0 : aSysExec = aSysExec.copy( 0, aSysExec.getLength() - RTL_CONSTASCII_LENGTH(".bin") );
533 0 : return aSysExec;
534 : }
535 :
536 :
537 0 : rtl::OString SessionManagerClient::getPreviousSessionID()
538 : {
539 0 : rtl::OString aPrevId;
540 :
541 0 : sal_uInt32 n = rtl_getAppCommandArgCount();
542 0 : for (sal_uInt32 i = 0; i != n; ++i)
543 : {
544 0 : ::rtl::OUString aArg;
545 0 : rtl_getAppCommandArg( i, &aArg.pData );
546 0 : if(aArg.match("--session="))
547 : {
548 : aPrevId = rtl::OUStringToOString(
549 : aArg.copy(RTL_CONSTASCII_LENGTH("--session=")),
550 0 : osl_getThreadTextEncoding());
551 : break;
552 : }
553 0 : }
554 :
555 : SAL_INFO("vcl.sm", "previous ID = " << aPrevId.getStr());
556 0 : return aPrevId;
557 : }
558 :
559 0 : void ICEConnectionObserver::activate()
560 : {
561 : /*
562 : * Default handlers call exit, we don't care that strongly if something
563 : * happens to fail
564 : */
565 0 : m_origIOErrorHandler = IceSetIOErrorHandler( IgnoreIceIOErrors );
566 0 : m_origErrorHandler = IceSetErrorHandler( IgnoreIceErrors );
567 0 : IceAddConnectionWatch( ICEWatchProc, this );
568 0 : }
569 :
570 0 : void ICEConnectionObserver::deactivate()
571 : {
572 : oslThread t;
573 : {
574 0 : osl::MutexGuard g(m_ICEMutex);
575 0 : IceRemoveConnectionWatch( ICEWatchProc, this );
576 0 : IceSetErrorHandler( m_origErrorHandler );
577 0 : IceSetIOErrorHandler( m_origIOErrorHandler );
578 0 : m_nConnections = 0;
579 0 : t = m_ICEThread;
580 0 : m_ICEThread = NULL;
581 : }
582 0 : if (t)
583 : {
584 0 : terminate(t);
585 : }
586 0 : }
587 :
588 0 : void ICEConnectionObserver::wakeup()
589 : {
590 0 : char cChar = 'w';
591 0 : OSL_VERIFY(write(m_nWakeupFiles[1], &cChar, 1) == 1);
592 0 : }
593 :
594 0 : void ICEConnectionObserver::terminate(oslThread iceThread)
595 : {
596 0 : osl_terminateThread(iceThread);
597 0 : wakeup();
598 0 : osl_joinWithThread(iceThread);
599 0 : osl_destroyThread(iceThread);
600 0 : close(m_nWakeupFiles[1]);
601 0 : close(m_nWakeupFiles[0]);
602 0 : }
603 :
604 0 : void ICEConnectionWorker(void * data)
605 : {
606 : ICEConnectionObserver * pThis = static_cast< ICEConnectionObserver * >(
607 0 : data);
608 0 : for (;;)
609 : {
610 : oslThread t;
611 : {
612 0 : osl::MutexGuard g(pThis->m_ICEMutex);
613 0 : if (pThis->m_ICEThread == NULL || pThis->m_nConnections == 0)
614 : {
615 : break;
616 : }
617 0 : t = pThis->m_ICEThread;
618 : }
619 0 : if (!osl_scheduleThread(t))
620 : {
621 : break;
622 : }
623 :
624 : int nConnectionsBefore;
625 : struct pollfd* pLocalFD;
626 : {
627 0 : osl::MutexGuard g(pThis->m_ICEMutex);
628 0 : nConnectionsBefore = pThis->m_nConnections;
629 0 : int nBytes = sizeof( struct pollfd )*(nConnectionsBefore+1);
630 0 : pLocalFD = (struct pollfd*)rtl_allocateMemory( nBytes );
631 0 : memcpy( pLocalFD, pThis->m_pFilehandles, nBytes );
632 : }
633 :
634 0 : int nRet = poll( pLocalFD,nConnectionsBefore+1,-1 );
635 0 : bool bWakeup = (pLocalFD[0].revents & POLLIN);
636 0 : rtl_freeMemory( pLocalFD );
637 :
638 0 : if( nRet < 1 )
639 0 : continue;
640 :
641 : // clear wakeup pipe
642 0 : if( bWakeup )
643 : {
644 : char buf[4];
645 0 : while( read( pThis->m_nWakeupFiles[0], buf, sizeof( buf ) ) > 0 )
646 : ;
647 : SAL_INFO("vcl.sm", "file handles active in wakeup: " << nRet);
648 0 : if( nRet == 1 )
649 0 : continue;
650 : }
651 :
652 : // check fd's after we obtained the lock
653 0 : osl::MutexGuard g(pThis->m_ICEMutex);
654 0 : if( pThis->m_nConnections > 0 && pThis->m_nConnections == nConnectionsBefore )
655 : {
656 0 : nRet = poll( pThis->m_pFilehandles+1, pThis->m_nConnections, 0 );
657 0 : if( nRet > 0 )
658 : {
659 : SAL_INFO("vcl.sm", "IceProcessMessages");
660 : Bool bReply;
661 0 : for( int i = 0; i < pThis->m_nConnections; i++ )
662 0 : if( pThis->m_pFilehandles[i+1].revents & POLLIN )
663 0 : IceProcessMessages( pThis->m_pConnections[i], NULL, &bReply );
664 : }
665 : }
666 0 : }
667 : SAL_INFO("vcl.sm", "shutting down ICE dispatch thread");
668 0 : }
669 :
670 0 : void ICEWatchProc(
671 : IceConn ice_conn, IcePointer client_data, Bool opening,
672 : SAL_UNUSED_PARAMETER IcePointer *)
673 : {
674 : // Note: This is a callback function for ICE; this implicitly means that a
675 : // call into ICE lib is calling this, so the m_ICEMutex MUST already be
676 : // locked by the caller.
677 : ICEConnectionObserver * pThis = static_cast< ICEConnectionObserver * >(
678 0 : client_data);
679 0 : if( opening )
680 : {
681 0 : int fd = IceConnectionNumber( ice_conn );
682 0 : pThis->m_nConnections++;
683 0 : pThis->m_pConnections = (IceConn*)rtl_reallocateMemory( pThis->m_pConnections, sizeof( IceConn )*pThis->m_nConnections );
684 0 : pThis->m_pFilehandles = (struct pollfd*)rtl_reallocateMemory( pThis->m_pFilehandles, sizeof( struct pollfd )*(pThis->m_nConnections+1) );
685 0 : pThis->m_pConnections[ pThis->m_nConnections-1 ] = ice_conn;
686 0 : pThis->m_pFilehandles[ pThis->m_nConnections ].fd = fd;
687 0 : pThis->m_pFilehandles[ pThis->m_nConnections ].events = POLLIN;
688 0 : if( pThis->m_nConnections == 1 )
689 : {
690 0 : if (!pipe(pThis->m_nWakeupFiles))
691 : {
692 : int flags;
693 0 : pThis->m_pFilehandles[0].fd = pThis->m_nWakeupFiles[0];
694 0 : pThis->m_pFilehandles[0].events = POLLIN;
695 : // set close-on-exec and nonblock descriptor flag.
696 0 : if ((flags = fcntl(pThis->m_nWakeupFiles[0], F_GETFD)) != -1)
697 : {
698 0 : flags |= FD_CLOEXEC;
699 0 : fcntl(pThis->m_nWakeupFiles[0], F_SETFD, flags);
700 : }
701 0 : if ((flags = fcntl(pThis->m_nWakeupFiles[0], F_GETFL)) != -1)
702 : {
703 0 : flags |= O_NONBLOCK;
704 0 : fcntl(pThis->m_nWakeupFiles[0], F_SETFL, flags);
705 : }
706 : // set close-on-exec and nonblock descriptor flag.
707 0 : if ((flags = fcntl(pThis->m_nWakeupFiles[1], F_GETFD)) != -1)
708 : {
709 0 : flags |= FD_CLOEXEC;
710 0 : fcntl(pThis->m_nWakeupFiles[1], F_SETFD, flags);
711 : }
712 0 : if ((flags = fcntl(pThis->m_nWakeupFiles[1], F_GETFL)) != -1)
713 : {
714 0 : flags |= O_NONBLOCK;
715 0 : fcntl(pThis->m_nWakeupFiles[1], F_SETFL, flags);
716 : }
717 : pThis->m_ICEThread = osl_createThread(
718 0 : ICEConnectionWorker, pThis);
719 : }
720 : }
721 : }
722 : else // closing
723 : {
724 0 : for( int i = 0; i < pThis->m_nConnections; i++ )
725 : {
726 0 : if( pThis->m_pConnections[i] == ice_conn )
727 : {
728 0 : if( i < pThis->m_nConnections-1 )
729 : {
730 0 : memmove( pThis->m_pConnections+i, pThis->m_pConnections+i+1, sizeof( IceConn )*(pThis->m_nConnections-i-1) );
731 0 : memmove( pThis->m_pFilehandles+i+1, pThis->m_pFilehandles+i+2, sizeof( struct pollfd )*(pThis->m_nConnections-i-1) );
732 : }
733 0 : pThis->m_nConnections--;
734 0 : pThis->m_pConnections = (IceConn*)rtl_reallocateMemory( pThis->m_pConnections, sizeof( IceConn )*pThis->m_nConnections );
735 0 : pThis->m_pFilehandles = (struct pollfd*)rtl_reallocateMemory( pThis->m_pFilehandles, sizeof( struct pollfd )*(pThis->m_nConnections+1) );
736 0 : break;
737 : }
738 : }
739 0 : if( pThis->m_nConnections == 0 && pThis->m_ICEThread )
740 : {
741 : SAL_INFO("vcl.sm", "terminating ICEThread");
742 0 : oslThread t = pThis->m_ICEThread;
743 0 : pThis->m_ICEThread = NULL;
744 :
745 : // must release the mutex here
746 0 : pThis->m_ICEMutex.release();
747 :
748 0 : pThis->terminate(t);
749 :
750 : // acquire the mutex again, because the caller does not expect
751 : // it to be released when calling into SM
752 0 : pThis->m_ICEMutex.acquire();
753 : }
754 : }
755 : SAL_INFO( "vcl.sm", "ICE connection on " << IceConnectionNumber( ice_conn ) << " " << (opening ? "inserted" : "removed"));
756 : SAL_INFO( "vcl.sm", "Display connection is " << ConnectionNumber( GetGenericData()->GetSalDisplay()->GetDisplay() ) );
757 0 : }
758 :
759 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|