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