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