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