1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, see: <http://www.gnu.org/licenses/>
14  */
15 
16 #include "config.h"
17 
18 #include <errno.h>
19 #ifdef HAVE_FCNTL_H
20 #include <fcntl.h>
21 #endif
22 
23 /* for F_CMD_ARGS */
24 #include "fvwm/fvwm.h"
25 #include "execcontext.h"
26 /* end of for CMD_ARGS */
27 
28 /*for debug message*/
29 #include "fvwm.h"
30 #include "misc.h"
31 /* end of for debug message */
32 
33 /* for get_current_read_file */
34 #include "read.h"
35 /* for busy cursor */
36 #include "cursor.h"
37 
38 /* for Scr global */
39 #include "screen.h"
40 
41 #include "externs.h"
42 
43 /* for module syncronous */
44 #include "libs/ftime.h"
45 #include "libs/System.c"
46 #include "libs/envvar.h"
47 #include "libs/Parse.h"
48 #include "libs/Strings.h"
49 #include "libs/wild.h"
50 #include "libs/fvwmsignal.h"
51 #include "events.h"
52 #include "bindings.h"
53 
54 /* for positive write */
55 
56 #include "module_list.h"
57 #include "module_interface.h"
58 /*
59  * Use POSIX behaviour if we can, otherwise use SysV instead
60  * should this be here?
61  */
62 #ifndef O_NONBLOCK
63 #  define O_NONBLOCK  O_NDELAY
64 #endif
65 
66 #define MOD_NOGRABMASK(m) ((m)->xNoGrabMask)
67 #define MOD_SYNCMASK(m) ((m)->xSyncMask)
68 
69 typedef struct
70 {
71 	unsigned long *data;
72 	int size;
73 	int done;
74 } mqueue_object_type;
75 
76 
77 /* the linked list pointers to the first and last modules */
78 static fmodule_list module_list = NULL;
79 /* keep to-be-deleted modules in a deathrow until they are deleted safely. */
80 static fmodule_list death_row = NULL;
81 
82 /*
83  * static functions
84  */
85 static fmodule *module_alloc(void);
86 static void module_free(fmodule *module);
87 
88 /*
89  * list handling functions
90  */
91 static inline void module_list_insert(fmodule *module, fmodule_list *list);
92 static inline fmodule *module_list_remove(fmodule *module, fmodule_list *list);
93 static inline void module_list_destroy(fmodule_list *list);
94 
95 static void KillModuleByName(char *name, char *alias);
96 static char *get_pipe_name(fmodule *module);
97 static void DeleteMessageQueueBuff(fmodule *module);
98 
99 static inline void msg_mask_set(
100 	msg_masks_t *msg_mask, unsigned long m1, unsigned long m2);
101 static void set_message_mask(msg_masks_t *mask, unsigned long msg);
102 
103 
module_kill_all(void)104 void module_kill_all(void)
105 {
106 	module_list_destroy(&module_list);
107 
108 	return;
109 }
110 
module_alloc(void)111 static fmodule *module_alloc(void)
112 {
113 	fmodule *module;
114 
115 	module = (fmodule *)safemalloc(sizeof(fmodule));
116 	MOD_SET_CMDLINE(module, 0);
117 	MOD_READFD(module) = -1;
118 	MOD_WRITEFD(module) = -1;
119 	fqueue_init(&MOD_PIPEQUEUE(module));
120 	msg_mask_set(&MOD_PIPEMASK(module), DEFAULT_MASK, DEFAULT_MASK);
121 	msg_mask_set(&MOD_NOGRABMASK(module), 0, 0);
122 	msg_mask_set(&MOD_SYNCMASK(module), 0, 0);
123 	MOD_NAME(module) = NULL;
124 	MOD_ALIAS(module) = NULL;
125 
126 	return module;
127 }
128 
129 /* closes the pipes and frees every data associated with a module record */
module_free(fmodule * module)130 static void module_free(fmodule *module)
131 {
132 	if (module == NULL)
133 	{
134 		return;
135 	}
136 	close(MOD_WRITEFD(module));
137 	close(MOD_READFD(module));
138 
139 	if (MOD_NAME(module) != NULL)
140 	{
141 		free(MOD_NAME(module));
142 	}
143 	if (MOD_ALIAS(module) != NULL)
144 	{
145 		free(MOD_ALIAS(module));
146 	}
147 	while (!FQUEUE_IS_EMPTY(&(MOD_PIPEQUEUE(module))))
148 	{
149 		DeleteMessageQueueBuff(module);
150 	}
151 	free(module);
152 
153 	return;
154 }
155 
module_list_insert(fmodule * module,fmodule_list * list)156 static inline void module_list_insert(fmodule *module, fmodule_list *list)
157 {
158 	fmodule_store *new_store;
159 	DBUG("module_list_insert", "inserting module");
160 	if (module == NULL)
161 	{
162 		return;
163 	}
164 	new_store = (fmodule_store*)safemalloc(sizeof(fmodule_store));
165 	new_store->module = module;
166 	new_store->next = *list;
167 	*list = new_store;
168 
169 	return;
170 }
171 
module_list_remove(fmodule * module,fmodule_list * list)172 static inline fmodule *module_list_remove(fmodule *module, fmodule_list *list)
173 {
174 	fmodule_store **position;
175 
176 	if (module == NULL)
177 	{
178 		return NULL;
179 	}
180 
181 	for (
182 		position = list; *position != NULL;
183 		position = &((*position)->next))
184 	{
185 		if ((*position)->module == module)
186 		{
187 			/* found it */
188 			fmodule_store *current;
189 
190 			DBUG("module_list_remove", "Removing from module list");
191 			current = *position;
192 			*position = (*position)->next;
193 			free(current);
194 
195 			return module;
196 		}
197 	}
198 
199 	/* module not found */
200 	DBUG("module_list_remove", "Tried to remove a not listed module!");
201 
202 	return NULL;
203 }
204 
module_list_destroy(fmodule_list * list)205 static inline void module_list_destroy(fmodule_list *list)
206 {
207 	fmodule_store *current;
208 	fmodule_store *next;
209 
210 	for (current = *list; current != NULL; current = next)
211 	{
212 		next = current->next;
213 		module_free(current->module);
214 		free(current);
215 	}
216 	*list = NULL;
217 
218 	return;
219 }
220 
do_execute_module(F_CMD_ARGS,Bool desperate,Bool do_listen_only)221 static fmodule *do_execute_module(
222 	F_CMD_ARGS, Bool desperate, Bool do_listen_only)
223 {
224 	int fvwm_to_app[2], app_to_fvwm[2];
225 	int i, val, nargs = 0;
226 	char *cptr = NULL;
227 	char **args = NULL;
228 	char *arg1 = NULL;
229 	char arg2[20];
230 	char arg3[20];
231 	char arg5[20];
232 	char arg6[20];
233 	char *token;
234 	extern char *ModulePath;
235 	Window win;
236 	FvwmWindow * const fw = exc->w.fw;
237 	fmodule *module;
238 
239 	fvwm_to_app[0] = -1;
240 	fvwm_to_app[1] = -1;
241 	app_to_fvwm[1] = -1;
242 	app_to_fvwm[0] = -1;
243 	args = (char **)safemalloc(7 * sizeof(char *));
244 	/* Olivier: Why ? */
245 	/*   if (eventp->type != KeyPress) */
246 	/*     UngrabEm(); */
247 	if (action == NULL)
248 	{
249 		goto err_exit;
250 	}
251 	if (fw)
252 	{
253 		win = FW_W(fw);
254 	}
255 	else
256 	{
257 		win = None;
258 	}
259 	action = GetNextToken(action, &cptr);
260 	if (!cptr)
261 	{
262 		goto err_exit;
263 	}
264 	arg1 = searchPath(ModulePath, cptr, EXECUTABLE_EXTENSION, X_OK);
265 	if (arg1 == NULL)
266 	{
267 		/* If this function is called in 'desparate' mode this means
268 		 * fvwm is trying a module name as a last resort.  In this case
269 		 * the error message is inappropriate because it was most
270 		 * likely a typo in a command, not a module name. */
271 		if (!desperate)
272 		{
273 			fvwm_msg(
274 				ERR, "executeModule",
275 				"No such module '%s' in ModulePath '%s'",
276 				cptr, ModulePath);
277 		}
278 		goto err_exit;
279 	}
280 #ifdef REMOVE_EXECUTABLE_EXTENSION
281 	{
282 		char *p;
283 
284 		p = arg1 + strlen(arg1) - strlen(EXECUTABLE_EXTENSION);
285 		if (strcmp(p, EXECUTABLE_EXTENSION) == 0)
286 		{
287 			*p = 0;
288 		}
289 	}
290 #endif
291 
292 	/* I want one-ended pipes, so I open two two-ended pipes,
293 	 * and close one end of each. I need one ended pipes so that
294 	 * I can detect when the module crashes/malfunctions */
295 	if (do_listen_only == True)
296 	{
297 		fvwm_to_app[0] = -1;
298 		fvwm_to_app[1] = -1;
299 	}
300 	else if (pipe(fvwm_to_app) != 0)
301 	{
302 		fvwm_msg(ERR, "executeModule", "Failed to open pipe");
303 		goto err_exit;
304 	}
305 	if (pipe(app_to_fvwm) != 0)
306 	{
307 		fvwm_msg(ERR, "executeModule", "Failed to open pipe2");
308 		goto err_exit;
309 	}
310 	if (
311 		fvwm_to_app[0] >= fvwmlib_max_fd ||
312 		fvwm_to_app[1] >= fvwmlib_max_fd ||
313 		app_to_fvwm[0] >= fvwmlib_max_fd ||
314 		app_to_fvwm[1] >= fvwmlib_max_fd)
315 	{
316 		fvwm_msg(ERR, "executeModule", "too many open fds");
317 		goto err_exit;
318 	}
319 
320 	/* all ok, create the space and fill up */
321 	module = module_alloc();
322 
323 	MOD_NAME(module) = stripcpy(cptr);
324 	free(cptr);
325 	sprintf(arg2, "%d", app_to_fvwm[1]);
326 	sprintf(arg3, "%d", fvwm_to_app[0]);
327 	sprintf(arg5, "%lx", (unsigned long)win);
328 	sprintf(arg6, "%lx", (unsigned long)exc->w.wcontext);
329 	args[0] = arg1;
330 	args[1] = arg2;
331 	args[2] = arg3;
332 	args[3] = (char *)get_current_read_file();
333 	if (!args[3])
334 	{
335 		args[3] = "none";
336 	}
337 	args[4] = arg5;
338 	args[5] = arg6;
339 	for (nargs = 6; action = GetNextToken(action, &token), token; nargs++)
340 	{
341 		args = (char **)saferealloc(
342 			(void *)args, (nargs + 2) * sizeof(char *));
343 		args[nargs] = token;
344 		if (MOD_ALIAS(module) == NULL)
345 		{
346 			const char *ptr = skipModuleAliasToken(args[nargs]);
347 
348 			if (ptr && *ptr == '\0')
349 			{
350 				MOD_ALIAS(module) = stripcpy(args[nargs]);
351 			}
352 		}
353 	}
354 	args[nargs] = NULL;
355 
356 	/* Try vfork instead of fork. The man page says that vfork is better!
357 	 */
358 	/* Also, had to change exit to _exit() */
359 	/* Not everyone has vfork! */
360 	val = fork();
361 	if (val > 0)
362 	{
363 		/* This fork remains running fvwm */
364 		/* close appropriate descriptors from each pipe so
365 		 * that fvwm will be able to tell when the app dies */
366 		close(app_to_fvwm[1]);
367 		/* dont't care that this may be -1 */
368 		close(fvwm_to_app[0]);
369 
370 		/* add these pipes to fvwm's active pipe list */
371 		MOD_WRITEFD(module) = fvwm_to_app[1];
372 		MOD_READFD(module) = app_to_fvwm[0];
373 		msg_mask_set(
374 			&MOD_PIPEMASK(module), DEFAULT_MASK, DEFAULT_MASK);
375 		free(arg1);
376 		if (DoingCommandLine)
377 		{
378 			/* add to the list of command line modules */
379 			DBUG("executeModule", "starting commandline module\n");
380 			MOD_SET_CMDLINE(module, 1);
381 		}
382 
383 		/* make the PositiveWrite pipe non-blocking. Don't want to jam
384 		 * up fvwm because of an uncooperative module */
385 		if (MOD_WRITEFD(module) >= 0)
386 		{
387 			fcntl(MOD_WRITEFD(module), F_SETFL, O_NONBLOCK);
388 		}
389 		/* Mark the pipes close-on exec so other programs
390 		 * won`t inherit them */
391 		if (fcntl(MOD_READFD(module), F_SETFD, 1) == -1)
392 		{
393 			fvwm_msg(
394 				ERR, "executeModule",
395 				"module close-on-exec failed");
396 		}
397 		if (
398 			MOD_WRITEFD(module) >= 0 &&
399 			fcntl(MOD_WRITEFD(module), F_SETFD, 1) == -1)
400 		{
401 			fvwm_msg(
402 				ERR, "executeModule",
403 				"module close-on-exec failed");
404 		}
405 		/* module struct is completed, insert into the list */
406 		module_list_insert(module, &module_list);
407 
408 		for (i = 6; i < nargs; i++)
409 		{
410 			if (args[i] != 0)
411 			{
412 				free(args[i]);
413 			}
414 		}
415 	}
416 	else if (val ==0)
417 	{
418 		/* this is the child */
419 		/* this fork execs the module */
420 #ifdef FORK_CREATES_CHILD
421 		/* dont't care that this may be -1 */
422 		close(fvwm_to_app[1]);
423 		close(app_to_fvwm[0]);
424 #endif
425 		fvmm_deinstall_signals();
426 		if (!Pdefault)
427 		{
428 			char visualid[32];
429 			char colormap[32];
430 
431 			sprintf(
432 				visualid, "FVWM_VISUALID=%lx",
433 				XVisualIDFromVisual(Pvisual));
434 			flib_putenv("FVWM_VISUALID", visualid);
435 			sprintf(colormap, "FVWM_COLORMAP=%lx", Pcmap);
436 			flib_putenv("FVWM_COLORMAP", colormap);
437 		}
438 		else
439 		{
440 			flib_unsetenv("FVWM_VISUALID");
441 			flib_unsetenv("FVWM_COLORMAP");
442 		}
443 
444 		/* Why is this execvp??  We've already searched the module
445 		 * path! */
446 		execvp(arg1,args);
447 		fvwm_msg(
448 			ERR, "executeModule", "Execution of module failed: %s",
449 			arg1);
450 		perror("");
451 		close(app_to_fvwm[1]);
452 		/* dont't care that this may be -1 */
453 		close(fvwm_to_app[0]);
454 #ifdef FORK_CREATES_CHILD
455 		exit(1);
456 #endif
457 	}
458 	else
459 	{
460 		fvwm_msg(ERR, "executeModule", "Fork failed");
461 		free(arg1);
462 		for (i = 6; i < nargs; i++)
463 		{
464 			if (args[i] != 0)
465 			{
466 				free(args[i]);
467 			}
468 		}
469 		free(args);
470 		module_free(module);
471 
472 		return NULL;
473 	}
474 	free(args);
475 
476 	return module;
477 
478   err_exit:
479 	if (arg1 != NULL)
480 	{
481 		free(arg1);
482 	}
483 	if (cptr != NULL)
484 	{
485 		free(cptr);
486 	}
487 	if (args != NULL)
488 	{
489 		free(args);
490 	}
491 	/* dont't care that these may be -1 */
492 	close(fvwm_to_app[0]);
493 	close(fvwm_to_app[1]);
494 	close(app_to_fvwm[0]);
495 	close(app_to_fvwm[1]);
496 
497 	return NULL;
498 }
499 
executeModuleDesperate(F_CMD_ARGS)500 fmodule *executeModuleDesperate(F_CMD_ARGS)
501 {
502 	fmodule *m;
503 
504 	m = do_execute_module(F_PASS_ARGS, True, False);
505 
506 	return m;
507 }
508 
module_kill(fmodule * module)509 void module_kill(fmodule *module)
510 {
511 	module_list_insert(
512 		module_list_remove(module, &module_list), &death_row);
513 
514 	return;
515 }
516 
517 /* free modules in the deathrow */
module_cleanup(void)518 void module_cleanup(void)
519 {
520 	module_list_destroy(&death_row);
521 
522 	return;
523 }
524 
525 /* void module_send(fmodule *module, unsigned long *ptr, int size) */
526 /* This used to be marked "fvwm_inline".  I removed this
527    when I added the lockonsend logic.  The routine seems too big to
528    want to inline.  dje 9/4/98 */
529 extern int myxgrabcount;                /* defined in libs/Grab.c */
530 extern char *ModuleUnlock;              /* defined in libs/Module.c */
PositiveWrite(fmodule * module,unsigned long * ptr,int size)531 void PositiveWrite(fmodule *module, unsigned long *ptr, int size)
532 {
533 	extern int moduleTimeout;
534 	msg_masks_t mask;
535 
536 	if (ptr == NULL)
537 	{
538 		return;
539 	}
540 	if (MOD_WRITEFD(module) == -1)
541 	{
542 		return;
543 	}
544 	if (!IS_MESSAGE_IN_MASK(&(MOD_PIPEMASK(module)), ptr[1]))
545 	{
546 		return;
547 	}
548 
549 	/* a dirty hack to prevent FvwmAnimate triggering during Recapture */
550 	/* would be better to send RecaptureStart and RecaptureEnd messages. */
551 	/* If module is lock on send for iconify message and it's an
552 	 * iconify event and server grabbed, then return */
553 	mask.m1 = (MOD_NOGRABMASK(module).m1 & MOD_SYNCMASK(module).m1);
554 	mask.m2 = (MOD_NOGRABMASK(module).m2 & MOD_SYNCMASK(module).m2);
555 	if (IS_MESSAGE_IN_MASK(&mask, ptr[1]) && myxgrabcount != 0)
556 	{
557 		return;
558 	}
559 
560 	/* DV: This was once the AddToMessageQueue function.  Since it was only
561 	 * called once, put it in here for better performance. */
562 	{
563 		mqueue_object_type *c;
564 
565 		c = (mqueue_object_type *)malloc(
566 			sizeof(mqueue_object_type) + size);
567 		if (c == NULL)
568 		{
569 			fvwm_msg(ERR, "PositiveWrite", "malloc failed\n");
570 			exit(1);
571 		}
572 		c->size = size;
573 		c->done = 0;
574 		c->data = (unsigned long *)(c + 1);
575 		memcpy((void*)c->data, (const void*)ptr, size);
576 		fqueue_add_at_end(&(MOD_PIPEQUEUE(module)), c);
577 	}
578 
579 	/* dje, from afterstep, for FvwmAnimate, allows modules to sync with
580 	 * fvwm. this is disabled when the server is grabbed, otherwise
581 	 * deadlocks happen. M_LOCKONSEND has been replaced by a separated
582 	 * mask which defines on which messages the fvwm-to-module
583 	 * communication need to be lock on send. olicha Nov 13, 1999 */
584 	/* migo (19-Aug-2000): removed !myxgrabcount to sync M_DESTROY_WINDOW
585 	 */
586 	/* dv (06-Jul-2002): added the !myxgrabcount again.  Deadlocks *do*
587 	 * happen without it.  There must be another way to fix
588 	 * M_DESTROY_WINDOW handling in FvwmEvent. */
589 	/*if (IS_MESSAGE_IN_MASK(&(MOD_SYNCMASK(module)), ptr[1]))*/
590 	if (
591 		IS_MESSAGE_IN_MASK(
592 			&(MOD_SYNCMASK(module)), ptr[1]) && !myxgrabcount)
593 	{
594 		fd_set readSet;
595 		int channel = MOD_READFD(module);
596 		struct timeval timeout;
597 		Bool done = False;
598 		fmodule_input *input;
599 
600 		FlushMessageQueue(module);
601 
602 		while (!done)
603 		{
604 			int rc = 0;
605 			/*
606 			 * We give the read a long timeout; if the module
607 			 * fails to respond within this time then it deserves
608 			 * to be KILLED!
609 			 *
610 			 * NOTE: rather than impose an arbitrary timeout on the
611 			 * user, we will make this a configuration parameter.
612 			 */
613 			do
614 			{
615 				timeout.tv_sec = moduleTimeout;
616 				timeout.tv_usec = 0;
617 				FD_ZERO(&readSet);
618 				FD_SET(channel, &readSet);
619 
620 				/* Wait for input to arrive on just one
621 				 * descriptor, with a timeout (fvwmSelect <= 0)
622 				 * or read() returning wrong size is bad news
623 				 */
624 				rc = fvwmSelect(
625 					channel + 1, &readSet, NULL, NULL,
626 					&timeout);
627 				/* retry if select() failed with EINTR */
628 			} while (rc < 0 && !isTerminated && (errno == EINTR));
629 
630 			if ( isTerminated )
631 			{
632 				break;
633 			}
634 
635 			if (rc > 0)
636 			{
637 				input = module_receive(module);
638 				if (
639 					input == NULL ||
640 					module_input_expect(input,
641 						ModuleUnlockResponse))
642 				{
643 					module_input_discard(input);
644 					done = True;
645 				}
646 				else
647 				{
648 					module_input_execute(input);
649 				}
650 
651 			}
652 			else
653 			{
654 				char *name;
655 
656 				name = get_pipe_name(module);
657 				/* Doh! Something has gone wrong - get rid of
658 				 * the offender! */
659 				fvwm_msg(ERR, "PositiveWrite",
660 					 "Failed to read descriptor from"
661 					 " '%s':\n"
662 					 "- data available=%c\n"
663 					 "- terminate signal=%c\n",
664 					 name,
665 					 (FD_ISSET(channel, &readSet) ?
666 					  'Y' : 'N'),
667 					 isTerminated ? 'Y' : 'N');
668 				module_kill(module);
669 				break;
670 			}
671 
672 
673 			/* Execute all messages from the module until UNLOCK is
674 			 * received N.B. This may cause recursion if a command
675 			 * results in a sync message to another module, which
676 			 * in turn may send a command that results in another
677 			 * sync message to this module.
678 			 * Hippo: I don't think this will cause deadlocks, but
679 			 * the third time we get here the first times UNLOCK
680 			 * will be read and then on returning up the third
681 			 * level UNLOCK will be read at the first level. This
682 			 * could be difficult to fix without turning queueing
683 			 * on.  Turning queueing on may be bad because it can
684 			 * be useful for modules to be able to inject commands
685 			 * from modules in a synchronous manner. e.g.
686 			 * FvwmIconMan can tell FvwmAnimate to do an animation
687 			 * when a window is de-iconified from the IconMan,
688 			 * queueing make s this happen too late. */
689 		}
690 	}
691 
692 	return;
693 }
694 
module_receive(fmodule * module)695 fmodule_input *module_receive(fmodule *module)
696 {
697 	unsigned long size;
698 	unsigned long cont;
699 	Window win;
700 	int n;
701 	fmodule_input *input = NULL;
702 
703 	n = read(MOD_READFD(module), &win, sizeof(Window));
704 	if (n < sizeof(Window))
705 	{
706 		/* exit silently, module should have died.. */
707 		goto err;
708 	}
709 
710 	n = read(MOD_READFD(module), &size, sizeof(size));
711 	if (n < sizeof(size))
712 	{
713 		fvwm_msg(
714 			ERR, "module_receive",
715 			"Fail to read command size (Module: %p, read: %i, "
716 			"size: %i)", module, n, (int)sizeof(size));
717 		goto err;
718 	}
719 
720 	if (size > MAX_MODULE_INPUT_TEXT_LEN)
721 	{
722 		fvwm_msg(ERR, "module_receive",
723 			 "Module(%p) command is too big (%ld), limit is %d",
724 			 module, size, MAX_MODULE_INPUT_TEXT_LEN);
725 		/* The rest of the output from this module is going to be
726 		 * scrambled so let's kill it rather than risk interpreting
727 		 * garbage */
728 		goto err;
729 	}
730 
731 	/* allocate all storage at once */
732 	/* also save space for the '\0' termination character */
733 	input = (fmodule_input *)safemalloc(
734 		sizeof(fmodule_input) + sizeof(char)*(size + 1));
735 
736 	input->module = module;
737 	input->window = win;
738 	input->command = (char*)input + sizeof(fmodule_input);
739 
740 	n = read(MOD_READFD(module), input->command, size);
741 	if (n < size)
742 	{
743 		fvwm_msg(
744 			ERR, "module_receive",
745 			"Fail to read command (Module: %p, read: %i, size:"
746 			" %ld)", module, n, size);
747 		goto err;
748 	}
749 	input->command[n] = '\0';
750 	n = read(MOD_READFD(module), &cont, sizeof(cont));
751 	if (n < sizeof(cont))
752 	{
753 		fvwm_msg(ERR, "module_receive",
754 			 "Module %p, Size Problems (read: %d, size: %d)",
755 			 module, n, (int)sizeof(cont));
756 		goto err;
757 	}
758 	if (cont == 0)
759 	{
760 		/* this is documented as a valid way for a module to quit
761 		 * so let's not complain */
762 		module_kill(module);
763 	}
764 
765 	return input;
766 err:
767 	module_kill(module);
768 	module_input_discard(input);
769 	return NULL;
770 }
771 
772 /* frees the module input data struct */
module_input_discard(fmodule_input * input)773 void module_input_discard(fmodule_input *input)
774 {
775 	if (input==NULL)
776 	{
777 		return;
778 	}
779 	free(input);
780 }
781 
782 /* returns true if the module command matches "expect", false otherwise */
module_input_expect(fmodule_input * input,char * expect)783 Bool module_input_expect(fmodule_input *input, char *expect)
784 {
785 	if (input == NULL || input->command == NULL || expect == NULL)
786 	{
787 		return False;
788 	}
789 
790 	if (strncasecmp(input->command, expect, strlen(expect))	== 0)
791 	{
792 		/* the module sent the expected string */
793 		return True;
794 	}
795 
796 	return False;
797 }
798 
module_list_itr_init(fmodule_list_itr * itr)799 void module_list_itr_init(fmodule_list_itr *itr)
800 {
801 	*itr = module_list;
802 
803 	return;
804 }
805 
module_list_itr_next(fmodule_list_itr * itr)806 fmodule *module_list_itr_next(fmodule_list_itr *itr)
807 {
808 	fmodule *module;
809 
810 	if (*itr == NULL)
811 	{
812 		return NULL;
813 	}
814 	module = (*itr)->module;
815 	*itr = (*itr)->next;
816 
817 	return module;
818 }
819 
KillModuleByName(char * name,char * alias)820 static void KillModuleByName(char *name, char *alias)
821 {
822 	fmodule_list_itr moditr;
823 	fmodule *module;
824 
825 	if (name == NULL)
826 	{
827 		return;
828 	}
829 	module_list_itr_init(&moditr);
830 	while ( (module = module_list_itr_next(&moditr)) != NULL)
831 	{
832 		if (
833 			MOD_NAME(module) != NULL &&
834 			matchWildcards(name, MOD_NAME(module)) &&
835 			(!alias || (
836 				 MOD_ALIAS(module) &&
837 				 matchWildcards(alias, MOD_ALIAS(module)))))
838 		{
839 			module_kill(module);
840 		}
841 	}
842 
843 	return;
844 }
845 
get_pipe_name(fmodule * module)846 static char *get_pipe_name(fmodule *module)
847 {
848 	char *name="";
849 
850 	if (MOD_NAME(module) != NULL)
851 	{
852 		if (MOD_ALIAS(module) != NULL)
853 		{
854 			name = CatString3(
855 				MOD_NAME(module), " ", MOD_ALIAS(module));
856 		}
857 		else
858 		{
859 			name = MOD_NAME(module);
860 		}
861 	}
862 	else
863 	{
864 		name = CatString3("(null)", "", "");
865 	}
866 
867 	return name;
868 }
869 
870 /*
871  * returns a pointer inside a string (just after the alias) if ok or NULL
872  */
skipModuleAliasToken(const char * string)873 char *skipModuleAliasToken(const char *string)
874 {
875 #define is_valid_first_alias_char(ch) (isalpha(ch) || (ch) == '/')
876 #define is_valid_alias_char(ch) (is_valid_first_alias_char(ch)  \
877 				|| isalnum(ch) || (ch) == '-' || \
878 				(ch) == '.' || (ch) == '/')
879 
880 	if (is_valid_first_alias_char(*string))
881 	{
882 		int len = 1;
883 		string++;
884 		while (*string && is_valid_alias_char(*string))
885 		{
886 			if (++len > MAX_MODULE_ALIAS_LEN)
887 			{
888 				return NULL;
889 			}
890 			string++;
891 		}
892 		return (char *)string;
893 	}
894 
895 	return NULL;
896 #undef is_valid_first_alias_char
897 #undef is_valid_alias_char
898 }
899 
900 /* message mask handling - does this belong here? */
901 
msg_mask_set(msg_masks_t * msg_mask,unsigned long m1,unsigned long m2)902 static inline void msg_mask_set(
903 	msg_masks_t *msg_mask, unsigned long m1, unsigned long m2)
904 {
905 	msg_mask->m1 = m1;
906 	msg_mask->m2 = m2;
907 
908 	return;
909 }
910 
911 /*
912  * Sets the mask to the specific value.  If M_EXTENDED_MSG is set in mask, the
913  * function operates only on the extended messages, otherwise it operates only
914  * on normal messages.
915  */
set_message_mask(msg_masks_t * mask,unsigned long msg)916 static void set_message_mask(msg_masks_t *mask, unsigned long msg)
917 {
918 	if (msg & M_EXTENDED_MSG)
919 	{
920 		mask->m2 = (msg & ~M_EXTENDED_MSG);
921 	}
922 	else
923 	{
924 		mask->m1 = msg;
925 	}
926 
927 	return;
928 }
929 
930 
931 /* message queues */
932 
DeleteMessageQueueBuff(fmodule * module)933 static void DeleteMessageQueueBuff(fmodule *module)
934 {
935 	mqueue_object_type *obj;
936 
937 	if (fqueue_get_first(&(MOD_PIPEQUEUE(module)), (void **)&obj) == 1)
938 	{
939 		/* remove from queue */
940 		fqueue_remove_or_operate_from_front(
941 			&(MOD_PIPEQUEUE(module)), NULL, NULL, NULL, NULL);
942 		/* we don't need to free the obj->data here because it's in the
943 		 * same malloced block as the obj itself. */
944 		free(obj);
945 	}
946 
947 	return;
948 }
949 
FlushMessageQueue(fmodule * module)950 void FlushMessageQueue(fmodule *module)
951 {
952 	extern int moduleTimeout;
953 	mqueue_object_type *obj;
954 	char *dptr;
955 	int a;
956 
957 	if (module == NULL)
958 	{
959 		return;
960 	}
961 
962 	while (fqueue_get_first(&(MOD_PIPEQUEUE(module)), (void **)&obj) == 1)
963 	{
964 		dptr = (char *)obj->data;
965 		while (obj->done < obj->size)
966 		{
967 			a = write(MOD_WRITEFD(module), &dptr[obj->done],
968 				  obj->size - obj->done);
969 			if (a >=0)
970 			{
971 				obj->done += a;
972 			}
973 			/* the write returns EWOULDBLOCK or EAGAIN if the pipe
974 			 * is full. (This is non-blocking I/O). SunOS returns
975 			 * EWOULDBLOCK, OSF/1 returns EAGAIN under these
976 			 * conditions. Hopefully other OSes return one of these
977 			 * values too. Solaris 2 doesn't seem to have a man
978 			 * page for write(2) (!) */
979 			else if (errno == EWOULDBLOCK || errno == EAGAIN)
980 			{
981 				fd_set writeSet;
982 				struct timeval timeout;
983 				int channel = MOD_WRITEFD(module);
984 				int rc = 0;
985 
986 				do
987 				{
988 					/* Wait until the pipe accepts further
989 					 * input */
990 					timeout.tv_sec = moduleTimeout;
991 					timeout.tv_usec = 0;
992 					FD_ZERO(&writeSet);
993 					FD_SET(channel, &writeSet);
994 					rc = fvwmSelect(
995 						channel + 1, NULL, &writeSet,
996 						NULL, &timeout);
997 					/* retry if select() failed with EINTR
998 					 */
999 				} while ((rc < 0) && !isTerminated &&
1000 					 (errno == EINTR));
1001 
1002 				if ( isTerminated )
1003 				{
1004 					return;
1005 				}
1006 				if (!FD_ISSET(channel, &writeSet))
1007 				{
1008 					char *name;
1009 
1010 					name = get_pipe_name(module);
1011 					/* Doh! Something has gone wrong - get
1012 					 * rid of the offender! */
1013 					fvwm_msg(
1014 						ERR, "FlushMessageQueue",
1015 						"Failed to write descriptor to"
1016 						" '%s':\n"
1017 						"- select rc=%d\n"
1018 						"- terminate signal=%c\n",
1019 						name, rc, isTerminated ?
1020 						'Y' : 'N');
1021 					module_kill(module);
1022 
1023 					return;
1024 				}
1025 
1026 				/* pipe accepts further input; continue */
1027 				continue;
1028 			}
1029 			else if (errno != EINTR)
1030 			{
1031 				module_kill(module);
1032 
1033 				return;
1034 			}
1035 		}
1036 		DeleteMessageQueueBuff(module);
1037 	}
1038 
1039 	return;
1040 }
1041 
FlushAllMessageQueues(void)1042 void FlushAllMessageQueues(void)
1043 {
1044 	fmodule_list_itr moditr;
1045 	fmodule *module;
1046 
1047 	module_list_itr_init(&moditr);
1048 	while ( (module = module_list_itr_next(&moditr)) != NULL)
1049 	{
1050 		FlushMessageQueue(module);
1051 	}
1052 
1053 	return;
1054 }
1055 
1056 /* empty, only here so that the signal handling initialization code is the
1057  * same for modules and fvwm  */
DeadPipe(int sig)1058 RETSIGTYPE DeadPipe(int sig)
1059 {
1060 	SIGNAL_RETURN;
1061 }
1062 
CMD_Module(F_CMD_ARGS)1063 void CMD_Module(F_CMD_ARGS)
1064 {
1065 	do_execute_module(F_PASS_ARGS, False, False);
1066 
1067 	return;
1068 }
1069 
CMD_ModuleListenOnly(F_CMD_ARGS)1070 void CMD_ModuleListenOnly(F_CMD_ARGS)
1071 {
1072 	do_execute_module(F_PASS_ARGS, False, True);
1073 
1074 	return;
1075 }
1076 
CMD_KillModule(F_CMD_ARGS)1077 void CMD_KillModule(F_CMD_ARGS)
1078 {
1079 	char *name;
1080 	char *alias = NULL;
1081 
1082 	action = GetNextToken(action,&name);
1083 	if (!name)
1084 	{
1085 		return;
1086 	}
1087 
1088 	GetNextToken(action, &alias);
1089 	KillModuleByName(name, alias);
1090 	free(name);
1091 	if (alias)
1092 	{
1093 		free(alias);
1094 	}
1095 	return;
1096 }
1097 
CMD_ModuleSynchronous(F_CMD_ARGS)1098 void CMD_ModuleSynchronous(F_CMD_ARGS)
1099 {
1100 	int sec = 0;
1101 	char *next;
1102 	char *token;
1103 	char *expect = ModuleFinishedStartupResponse;
1104 	fmodule *module;
1105 	fd_set in_fdset;
1106 	fd_set out_fdset;
1107 	time_t start_time;
1108 	Bool done = False;
1109 	Bool need_ungrab = False;
1110 	char *escape = NULL;
1111 	XEvent tmpevent;
1112 
1113 	if (!action)
1114 	{
1115 		return;
1116 	}
1117 
1118 	token = PeekToken(action, &next);
1119 	if (StrEquals(token, "expect"))
1120 	{
1121 		token = PeekToken(next, &next);
1122 		if (token)
1123 		{
1124 			expect = alloca(strlen(token) + 1);
1125 			strcpy(expect, token);
1126 		}
1127 		action = next;
1128 		token = PeekToken(action, &next);
1129 	}
1130 	if (token && StrEquals(token, "timeout"))
1131 	{
1132 		if (GetIntegerArguments(next, &next, &sec, 1) > 0 && sec > 0)
1133 		{
1134 			/* we have a delay, skip the number */
1135 			action = next;
1136 		}
1137 		else
1138 		{
1139 			fvwm_msg(ERR, "executeModuleSync", "illegal timeout");
1140 			return;
1141 		}
1142 	}
1143 
1144 	if (!action)
1145 	{
1146 		/* no module name */
1147 		return;
1148 	}
1149 
1150 	module = do_execute_module(F_PASS_ARGS, False, False);
1151 	if (module == NULL)
1152 	{
1153 		/* executing the module failed, just return */
1154 		return;
1155 	}
1156 
1157 	/* Busy cursor stuff */
1158 	if (Scr.BusyCursor & BUSY_MODULESYNCHRONOUS)
1159 	{
1160 		if (GrabEm(CRS_WAIT, GRAB_BUSY))
1161 			need_ungrab = True;
1162 	}
1163 
1164 	/* wait for module input */
1165 	start_time = time(NULL);
1166 
1167 	while (!done)
1168 	{
1169 		struct timeval timeout;
1170 		int num_fd;
1171 
1172 		/* A signal here could interrupt the select call. We would
1173 		 * then need to restart our select, unless the signal was
1174 		 * a "terminate" signal. Note that we need to reinitialise
1175 		 * all of select's parameters after it has returned. */
1176 		do
1177 		{
1178 			FD_ZERO(&in_fdset);
1179 			FD_ZERO(&out_fdset);
1180 
1181 			FD_SET(MOD_READFD(module), &in_fdset);
1182 
1183 			if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
1184 			{
1185 				FD_SET(MOD_WRITEFD(module), &out_fdset);
1186 			}
1187 			timeout.tv_sec = 0;
1188 			timeout.tv_usec = 1;
1189 			num_fd = fvwmSelect(
1190 				fvwmlib_max_fd, &in_fdset, &out_fdset, 0,
1191 				&timeout);
1192 		} while (num_fd < 0 && !isTerminated);
1193 
1194 		/* Exit if we have received a "terminate" signal */
1195 		if (isTerminated)
1196 		{
1197 			break;
1198 		}
1199 
1200 		if (num_fd > 0)
1201 		{
1202 			if (FD_ISSET(MOD_READFD(module), &in_fdset))
1203 			{
1204 				fmodule_input * input;
1205 
1206 				/* Check for module input. */
1207 				input = module_receive(module);
1208 				if (
1209 					input == NULL ||
1210 					module_input_expect(input,expect))
1211 				{
1212 					module_input_discard(input);
1213 					done = True;
1214 				}
1215 				else
1216 				{
1217 					module_input_execute(input);
1218 				}
1219 
1220 			}
1221 
1222 			if ((MOD_WRITEFD(module) >= 0) &&
1223 			    FD_ISSET(MOD_WRITEFD(module), &out_fdset))
1224 			{
1225 				FlushMessageQueue(module);
1226 			}
1227 		}
1228 
1229 		usleep(1000);
1230 		if (difftime(time(NULL), start_time) >= sec && sec)
1231 		{
1232 			/* timeout */
1233 			done = True;
1234 		}
1235 
1236 		/* Check for "escape function" */
1237 		if (FPending(dpy) &&
1238 		    FCheckMaskEvent(dpy, KeyPressMask, &tmpevent))
1239 		{
1240 			int context;
1241 			XClassHint *class;
1242 			char *name;
1243 			Window w;
1244 
1245 			context = GetContext(
1246 				NULL, exc->w.fw, &tmpevent, &w);
1247 			if (exc->w.fw != NULL)
1248 			{
1249 				class = &(exc->w.fw->class);
1250 				name = exc->w.fw->name.name;
1251 			}
1252 			else
1253 			{
1254 				class = NULL;
1255 				name = NULL;
1256 			}
1257 			escape = CheckBinding(
1258 				Scr.AllBindings, STROKE_ARG(0)
1259 				tmpevent.xkey.keycode, tmpevent.xkey.state,
1260 				GetUnusedModifiers(), context, BIND_KEYPRESS,
1261 				class, name);
1262 			if (escape != NULL)
1263 			{
1264 				if (!strcasecmp(escape,"escapefunc"))
1265 				{
1266 					done = True;
1267 				}
1268 			}
1269 		}
1270 	} /* while */
1271 
1272 	if (need_ungrab)
1273 	{
1274 		UngrabEm(GRAB_BUSY);
1275 	}
1276 
1277 	return;
1278 }
1279 
1280 /* mask handling - does this belong here? */
1281 
CMD_set_mask(F_CMD_ARGS)1282 void CMD_set_mask(F_CMD_ARGS)
1283 {
1284 	unsigned long val;
1285 
1286 	if (exc->m.module == NULL)
1287 	{
1288 		return;
1289 	}
1290 	if (!action || sscanf(action, "%lu", &val) != 1)
1291 	{
1292 		val = 0;
1293 	}
1294 	set_message_mask(&(MOD_PIPEMASK(exc->m.module)), (unsigned long)val);
1295 
1296 	return;
1297 }
1298 
CMD_set_sync_mask(F_CMD_ARGS)1299 void CMD_set_sync_mask(F_CMD_ARGS)
1300 {
1301 	unsigned long val;
1302 
1303 	if (exc->m.module == NULL)
1304 	{
1305 		return;
1306 	}
1307 	if (!action || sscanf(action,"%lu",&val) != 1)
1308 	{
1309 		val = 0;
1310 	}
1311 	set_message_mask(&(MOD_SYNCMASK(exc->m.module)), (unsigned long)val);
1312 
1313 	return;
1314 }
1315 
CMD_set_nograb_mask(F_CMD_ARGS)1316 void CMD_set_nograb_mask(F_CMD_ARGS)
1317 {
1318 	unsigned long val;
1319 
1320 	if (exc->m.module == NULL)
1321 	{
1322 		return;
1323 	}
1324 	if (!action || sscanf(action,"%lu",&val) != 1)
1325 	{
1326 		val = 0;
1327 	}
1328 	set_message_mask(&(MOD_NOGRABMASK(exc->m.module)), (unsigned long)val);
1329 
1330 	return;
1331 }
1332