1 /*****************************************************************************
2 *
3 * Authors: Michel Eyckmans (MCE) & Stefan De Troch (SDT)
4 *
5 * Content: This file is part of version 2.x of xautolock. It implements
6 * the program's IPC features.
7 *
8 * Please send bug reports etc. to mce@scarlet.be.
9 *
10 * --------------------------------------------------------------------------
11 *
12 * Copyright 1990, 1992-1999, 2001-2002, 2004, 2007 by Stefan De Troch and
13 * Michel Eyckmans.
14 *
15 * Versions 2.0 and above of xautolock are available under version 2 of the
16 * GNU GPL. Earlier versions are available under other conditions. For more
17 * information, see the License file.
18 *
19 *****************************************************************************/
20
21 #include "message.h"
22 #include "state.h"
23 #include "options.h"
24 #include "miscutil.h"
25
26 static Atom semaphore; /* semaphore property for locating
27 an already running xautolock */
28 static Atom messageAtom; /* message property for talking to
29 an already running xautolock */
30
31 #define SEM_PID "_SEMAPHORE_PID"
32 #define MESSAGE "_MESSAGE"
33
34 /*
35 * Message handlers.
36 */
37 static void
disableByMessage(Display * d,Window root)38 disableByMessage (Display* d, Window root)
39 {
40 /*
41 * The order in which things are done is rather important here.
42 */
43 if (!secure)
44 {
45 setLockTrigger (lockTime);
46 disableKillTrigger ();
47 disabled = True;
48 }
49 }
50
51 static void
enableByMessage(Display * d,Window root)52 enableByMessage (Display* d, Window root)
53 {
54 if (!secure)
55 {
56 resetTriggers ();
57 disabled = False;
58 }
59 }
60
61 static void
toggleByMessage(Display * d,Window root)62 toggleByMessage (Display* d, Window root)
63 {
64 if (!secure)
65 {
66 if ((disabled = !disabled)) /* = intended */
67 {
68 setLockTrigger (lockTime);
69 disableKillTrigger ();
70 }
71 else
72 {
73 resetTriggers ();
74 }
75 }
76 }
77
78 static void
exitByMessage(Display * d,Window root)79 exitByMessage (Display* d, Window root)
80 {
81 if (!secure)
82 {
83 error0 ("Exiting. Bye bye...\n");
84 exit (0);
85 }
86 }
87
88 static void
lockNowByMessage(Display * d,Window root)89 lockNowByMessage (Display* d, Window root)
90 {
91 if (!secure && !disabled) lockNow = True;
92 }
93
94 static void
unlockNowByMessage(Display * d,Window root)95 unlockNowByMessage (Display* d, Window root)
96 {
97 if (!secure && !disabled) unlockNow = True;
98 }
99
100 static void
restartByMessage(Display * d,Window root)101 restartByMessage (Display* d, Window root)
102 {
103 if (!secure)
104 {
105 XDeleteProperty (d, root, semaphore);
106 XFlush (d);
107 execv (argArray[0], argArray);
108 }
109 }
110
111 /*
112 * Function for looking for messages from another xautolock.
113 */
114 void
lookForMessages(Display * d)115 lookForMessages (Display* d)
116 {
117 Window root; /* as it says */
118 Atom type; /* actual property type */
119 int format; /* dummy */
120 unsigned long nofItems; /* dummy */
121 unsigned long after; /* dummy */
122 int* contents; /* message property value */
123 static Bool first = True; /* is this the first call? */
124
125 /*
126 * It would be cleaner (more X-like, using less cpu time and
127 * network bandwith, ...) to implement this functionality by
128 * keeping an eye open for PropertyNotify events on the root
129 * window, or (even better) by using ClientMessage events.
130 * Maybe I'll rewrite it one day to do just that, but the
131 * current approach works just fine too.
132 *
133 * Note that we must clear the property before acting on it!
134 * Otherwise funny things can happen on receipt of msg_exit.
135 */
136 root = RootWindowOfScreen (ScreenOfDisplay (d, 0));
137
138 (void) XGetWindowProperty (d, root, messageAtom, 0L, 2L,
139 False, AnyPropertyType, &type,
140 &format, &nofItems, &after,
141 (unsigned char**) &contents);
142
143 XDeleteProperty (d, root, messageAtom);
144 XFlush (d);
145
146 /*
147 * The very first time we get here, we ignore the message. Why?
148 * Well, suppose some weirdo sends a SIGSTOP to a running xautolock,
149 * then does an `xautolock -exit' and finally sends a SIGKILL to
150 * the stopped xautolock. This would leave an unread message sitting
151 * around. Unread that is, till the next xautolock is started.
152 *
153 * To solve this, we want any xautolock that enters the main event
154 * loop to consume and ignore any message that may have been around
155 * for a while and forgotten about.
156 */
157 if (first)
158 {
159 first = False;
160 }
161 else if (type == XA_INTEGER)
162 {
163 switch (*contents)
164 {
165 case msg_disable:
166 disableByMessage (d, root);
167 break;
168
169 case msg_enable:
170 enableByMessage (d, root);
171 break;
172
173 case msg_toggle:
174 toggleByMessage (d, root);
175 break;
176
177 case msg_lockNow:
178 lockNowByMessage (d, root);
179 break;
180
181 case msg_unlockNow:
182 unlockNowByMessage (d, root);
183 break;
184
185 case msg_restart:
186 restartByMessage (d, root);
187 break;
188
189 case msg_exit:
190 exitByMessage (d, root);
191 break;
192
193 default:
194 /* unknown message, ignore silently */
195 break;
196 }
197 }
198
199 (void) XFree ((char*) contents);
200 }
201
202 /*
203 * Function for creating the communication atoms.
204 */
205 static void
getAtoms(Display * d)206 getAtoms (Display* d)
207 {
208 char* sem; /* semaphore property name */
209 char* mes; /* message property name */
210 char* ptr; /* iterator */
211
212 sem = newArray (char, strlen (progName) + strlen (SEM_PID) + 1);
213 (void) sprintf (sem, "%s%s", progName, SEM_PID);
214 for (ptr = sem; *ptr; ++ptr) *ptr = (char) toupper (*ptr);
215 semaphore = XInternAtom (d, sem, False);
216 free (sem);
217
218 mes = newArray (char, strlen (progName) + strlen (MESSAGE) + 1);
219 (void) sprintf (mes, "%s%s", progName, MESSAGE);
220 for (ptr = mes; *ptr; ++ptr) *ptr = (char) toupper (*ptr);
221 messageAtom = XInternAtom (d, mes, False);
222 free (mes);
223 }
224
225 /*
226 * Function for finding out whether another xautolock is already
227 * running and for sending it a message if that's what the user
228 * wanted.
229 */
230 void
checkConnectionAndSendMessage(Display * d)231 checkConnectionAndSendMessage (Display* d)
232 {
233 pid_t pid; /* as it says */
234 Window root; /* as it says */
235 Atom type; /* actual property type */
236 int format; /* dummy */
237 unsigned long nofItems; /* dummy */
238 unsigned long after; /* dummy */
239 pid_t* contents; /* semaphore property value */
240
241 getAtoms (d);
242
243 root = RootWindowOfScreen (ScreenOfDisplay (d, 0));
244
245 (void) XGetWindowProperty (d, root, semaphore, 0L, 2L, False,
246 AnyPropertyType, &type, &format,
247 &nofItems, &after,
248 (unsigned char**) &contents);
249
250 if (type == XA_INTEGER)
251 {
252 /*
253 * This breaks if the other xautolock is not on the same machine.
254 */
255 if (kill (*contents, 0))
256 {
257 if (messageToSend)
258 {
259 error1 ("No process with PID %d, or the process "
260 "is owned by another user.\n", *contents);
261 exit (EXIT_FAILURE);
262 }
263 }
264 else if (messageToSend)
265 {
266 (void) XChangeProperty (d, root, messageAtom, XA_INTEGER,
267 8, PropModeReplace,
268 (unsigned char*) &messageToSend,
269 (int) sizeof (messageToSend));
270 XFlush (d);
271 exit (EXIT_SUCCESS);
272 }
273 else
274 {
275 #ifdef VMS
276 error2 ("%s is already running (PID %x).\n", progName, *contents);
277 #else /* VMS */
278 error2 ("%s is already running (PID %d).\n", progName, *contents);
279 #endif /* VMS */
280 exit (EXIT_FAILURE);
281 }
282 }
283 else if (messageToSend)
284 {
285 error1 ("Could not locate a running %s.\n", progName);
286 exit (EXIT_FAILURE);
287 }
288
289 pid = getpid ();
290 (void) XChangeProperty (d, root, semaphore, XA_INTEGER, 8,
291 PropModeReplace, (unsigned char*) &pid,
292 (int) sizeof (pid));
293
294 (void) XFree ((char*) contents);
295 }
296