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