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