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