1 #include "config.h"
2 
3 #ifdef CONFIG_SESSION
4 
5 #include "ysmapp.h"
6 #include "sysdep.h"
7 #include "base.h"
8 #include "yconfig.h"
9 
10 #include "intl.h"
11 
12 #include <X11/SM/SMlib.h>
13 
14 YSMApplication *smapp = nullptr;
15 static int IceSMfd = -1;
16 static IceConn IceSMconn = nullptr;
17 static SmcConn SMconn = nullptr;
18 static char *oldSessionId = nullptr;
19 static char *newSessionId = nullptr;
20 static char *sessionProg;
21 
getsesfile()22 upath getsesfile() {
23     upath path(YApplication::getPrivConfDir());
24     if (false == path.dirExists())
25         path.mkdir();
26     path += mstring("/.session-", newSessionId);
27     return path;
28 }
29 
iceWatchFD(IceConn conn,IcePointer,Bool opening,IcePointer *)30 static void iceWatchFD(IceConn conn,
31                        IcePointer /*client_data*/,
32                        Bool opening,
33                        IcePointer* /*watch_data*/)
34 {
35     if (opening) {
36         if (IceSMfd != -1) { // shouldn't happen
37             warn("TOO MANY ICE CONNECTIONS -- not supported");
38         } else {
39             IceSMfd = IceConnectionNumber(conn);
40             fcntl(IceSMfd, F_SETFD, FD_CLOEXEC);
41         }
42     } else {
43         if (IceConnectionNumber(conn) == IceSMfd)
44             IceSMfd = -1;
45     }
46 }
47 
saveYourselfPhase2Proc(SmcConn,SmPointer)48 static void saveYourselfPhase2Proc(SmcConn /*conn*/, SmPointer /*client_data*/) {
49     smapp->smSaveYourselfPhase2();
50 }
51 
saveYourselfProc(SmcConn,SmPointer,int,Bool shutdown,int,Bool fast)52 static void saveYourselfProc(SmcConn /*conn*/,
53                       SmPointer /*client_data*/,
54                       int /*save_type*/,
55                       Bool shutdown,
56                       int /*interact_style*/,
57                       Bool fast)
58 {
59     smapp->smSaveYourself(shutdown ? true : false, fast ? true : false);
60 }
61 
shutdownCancelledProc(SmcConn,SmPointer)62 static void shutdownCancelledProc(SmcConn /*conn*/, SmPointer /*client_data*/) {
63     smapp->smShutdownCancelled();
64 }
65 
saveCompleteProc(SmcConn,SmPointer)66 static void saveCompleteProc(SmcConn /*conn*/, SmPointer /*client_data*/) {
67     smapp->smSaveComplete();
68 }
69 
dieProc(SmcConn,SmPointer)70 static void dieProc(SmcConn /*conn*/, SmPointer /*client_data*/) {
71     smapp->smDie();
72 }
73 
setSMProperties()74 static void setSMProperties() {
75     SmPropValue programVal = { 0, nullptr };
76     SmPropValue userIDVal = { 0, nullptr };
77     SmPropValue restartVal[3] = { { 0, nullptr }, { 0, nullptr }, { 0, nullptr } };
78     SmPropValue discardVal[4] = { { 0, nullptr }, { 0, nullptr }, { 0, nullptr } };
79 
80     // broken headers in SMlib?
81     SmProp programProp = { (char *)SmProgram, (char *)SmLISTofARRAY8, 1, &programVal };
82     SmProp userIDProp = { (char *)SmUserID, (char *)SmARRAY8, 1, &userIDVal };
83     SmProp restartProp = { (char *)SmRestartCommand, (char *)SmLISTofARRAY8, 3, (SmPropValue *)&restartVal };
84     SmProp cloneProp = { (char *)SmCloneCommand, (char *)SmLISTofARRAY8, 1, (SmPropValue *)&restartVal };
85     SmProp discardProp = { (char *)SmDiscardCommand, (char *)SmLISTofARRAY8, 3, (SmPropValue *)&discardVal };
86     SmProp *props[] = {
87         &programProp,
88         &userIDProp,
89         &restartProp,
90         &cloneProp,
91         &discardProp
92     };
93 
94     char *user = getenv("USER");
95     if (!user) // not a user?
96         user = getenv("LOGNAME");
97     if (!user) {
98         msg(_("$USER or $LOGNAME not set?"));
99         return ;
100     }
101     const char *clientId = "--client-id";
102 
103     programVal.length = strlen(sessionProg);
104     programVal.value = sessionProg;
105     userIDVal.length = strlen(user);
106     userIDVal.value = (SmPointer)user;
107 
108     restartVal[0].length = strlen(sessionProg);
109     restartVal[0].value = sessionProg;
110     restartVal[1].length = strlen(clientId);
111     restartVal[1].value = (char *)clientId;
112     restartVal[2].length = strlen(newSessionId);
113     restartVal[2].value = newSessionId;
114 
115     const char *rmprog = "/bin/rm";
116     const char *rmarg = "-f";
117     upath sidfile = getsesfile();
118 
119     discardVal[0].length = strlen(rmprog);
120     discardVal[0].value = (char *)rmprog;
121     discardVal[1].length = strlen(rmarg);
122     discardVal[1].value = (char *)rmarg;
123     discardVal[2].length = sidfile.length();
124     discardVal[2].value = (char *)sidfile.string();
125 
126     SmcSetProperties(SMconn,
127                      (int) ACOUNT(props),
128                      (SmProp **)&props);
129 }
130 
initSM()131 static void initSM() {
132     if (getenv("SESSION_MANAGER") == nullptr)
133         return;
134     if (IceAddConnectionWatch(&iceWatchFD, nullptr) == 0) {
135         warn("Session Manager: IceAddConnectionWatch failed.");
136         return ;
137     }
138 
139     char error_str[256];
140     SmcCallbacks smcall;
141 
142     memset(&smcall, 0, sizeof(smcall));
143     smcall.save_yourself.callback = &saveYourselfProc;
144     smcall.save_yourself.client_data = nullptr;
145     smcall.die.callback = &dieProc;
146     smcall.die.client_data = nullptr;
147     smcall.save_complete.callback = &saveCompleteProc;
148     smcall.save_complete.client_data = nullptr;
149     smcall.shutdown_cancelled.callback = &shutdownCancelledProc;
150     smcall.shutdown_cancelled.client_data = nullptr;
151 
152     if ((SMconn = SmcOpenConnection(nullptr, /* network ids */
153                                     nullptr, /* context */
154                                     1, 0, /* protocol major, minor */
155                                     SmcSaveYourselfProcMask |
156                                     SmcSaveCompleteProcMask |
157                                     SmcShutdownCancelledProcMask |
158                                     SmcDieProcMask,
159                                     &smcall,
160                                     oldSessionId, &newSessionId,
161                                     sizeof(error_str), error_str)) == nullptr)
162     {
163         warn("Session Manager: Init error: %s", error_str);
164         return ;
165     }
166     IceSMconn = SmcGetIceConnection(SMconn);
167 
168     setSMProperties();
169 }
170 
smSaveYourself(bool,bool)171 void YSMApplication::smSaveYourself(bool /*shutdown*/, bool /*fast*/) {
172     SmcRequestSaveYourselfPhase2(SMconn, &saveYourselfPhase2Proc, nullptr);
173 }
174 
smSaveYourselfPhase2()175 void YSMApplication::smSaveYourselfPhase2() {
176     SmcSaveYourselfDone(SMconn, True);
177 }
178 
smSaveDone()179 void YSMApplication::smSaveDone() {
180     SmcSaveYourselfDone(SMconn, True);
181 }
182 
smSaveComplete()183 void YSMApplication::smSaveComplete() {
184 }
185 
smShutdownCancelled()186 void YSMApplication::smShutdownCancelled() {
187     SmcSaveYourselfDone(SMconn, False);
188 }
189 
smCancelShutdown()190 void YSMApplication::smCancelShutdown() {
191     SmcSaveYourselfDone(SMconn, False); /// !!! broken
192 }
193 
smDie()194 void YSMApplication::smDie() {
195     this->exit(0);
196 }
197 
haveSessionManager()198 bool YSMApplication::haveSessionManager() {
199     if (SMconn != nullptr)
200         return true;
201     return false;
202 }
203 
smRequestShutdown()204 void YSMApplication::smRequestShutdown() {
205     // !!! doesn't seem to work with xsm
206     SmcRequestSaveYourself(SMconn,
207                            SmSaveLocal, //!!!
208                            True,
209                            SmInteractStyleAny,
210                            False,
211                            True);
212 }
213 
YSMApplication(int * argc,char *** argv,const char * displayName)214 YSMApplication::YSMApplication(int *argc, char ***argv, const char *displayName):
215 YXApplication(argc, argv, displayName)
216 {
217     smapp = this;
218     for (char ** arg = *argv + 1; arg < *argv + *argc; ++arg) {
219         if (**arg == '-') {
220             char *value;
221             if (GetLongArgument(value, "client-id", arg, *argv+*argc))
222             {
223                 oldSessionId = value;
224                 continue;
225             }
226         }
227     }
228 
229     sessionProg = (*argv)[0]; //ICEWMEXE;
230     initSM();
231     psm.registerPoll(IceSMfd);
232 }
233 
~YSMApplication()234 YSMApplication::~YSMApplication() {
235     if (SMconn != nullptr) {
236         SmcCloseConnection(SMconn, 0, nullptr);
237         SMconn = nullptr;
238         IceSMconn = nullptr;
239         IceSMfd = -1;
240         unregisterPoll(&psm);
241     }
242 }
243 
notifyRead()244 void YSMPoll::notifyRead() {
245     Bool rep;
246     if (IceProcessMessages(IceSMconn, nullptr, &rep)
247         == IceProcessMessagesIOError)
248     {
249         SmcCloseConnection(SMconn, 0, nullptr);
250         IceSMconn = nullptr;
251         IceSMfd = -1;
252         unregisterPoll();
253     }
254 }
255 
256 #endif /* CONFIG_SESSION */
257 
258 // vim: set sw=4 ts=4 et:
259