/* -*-c-*- */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <errno.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
/* for F_CMD_ARGS */
#include "fvwm/fvwm.h"
#include "execcontext.h"
/* end of for CMD_ARGS */
/*for debug message*/
#include "fvwm.h"
#include "misc.h"
/* end of for debug message */
/* for get_current_read_file */
#include "read.h"
/* for busy cursor */
#include "cursor.h"
/* for Scr global */
#include "screen.h"
#include "externs.h"
/* for module syncronous */
#include "libs/ftime.h"
#include "libs/System.c"
#include "libs/envvar.h"
#include "libs/Parse.h"
#include "libs/Strings.h"
#include "libs/wild.h"
#include "libs/fvwmsignal.h"
#include "events.h"
#include "bindings.h"
/* for positive write */
#include "module_list.h"
#include "module_interface.h"
/*
* Use POSIX behaviour if we can, otherwise use SysV instead
* should this be here?
*/
#ifndef O_NONBLOCK
# define O_NONBLOCK O_NDELAY
#endif
#define MOD_NOGRABMASK(m) ((m)->xNoGrabMask)
#define MOD_SYNCMASK(m) ((m)->xSyncMask)
typedef struct
{
unsigned long *data;
int size;
int done;
} mqueue_object_type;
/* the linked list pointers to the first and last modules */
static fmodule_list module_list = NULL;
/* keep to-be-deleted modules in a deathrow until they are deleted safely. */
static fmodule_list death_row = NULL;
/*
* static functions
*/
static fmodule *module_alloc(void);
static void module_free(fmodule *module);
/*
* list handling functions
*/
static inline void module_list_insert(fmodule *module, fmodule_list *list);
static inline fmodule *module_list_remove(fmodule *module, fmodule_list *list);
static inline void module_list_destroy(fmodule_list *list);
static inline int module_list_len(fmodule_list *list);
static void KillModuleByName(char *name, char *alias);
static char *get_pipe_name(fmodule *module);
static void DeleteMessageQueueBuff(fmodule *module);
static inline void msg_mask_set(
msg_masks_t *msg_mask, unsigned long m1, unsigned long m2);
static void set_message_mask(msg_masks_t *mask, unsigned long msg);
void module_kill_all(void)
{
module_list_destroy(&module_list);
return;
}
static fmodule *module_alloc(void)
{
fmodule *module;
module = (fmodule *)safemalloc(sizeof(fmodule));
MOD_SET_CMDLINE(module, 0);
MOD_READFD(module) = -1;
MOD_WRITEFD(module) = -1;
fqueue_init(&MOD_PIPEQUEUE(module));
msg_mask_set(&MOD_PIPEMASK(module), DEFAULT_MASK, DEFAULT_MASK);
msg_mask_set(&MOD_NOGRABMASK(module), 0, 0);
msg_mask_set(&MOD_SYNCMASK(module), 0, 0);
MOD_NAME(module) = NULL;
MOD_ALIAS(module) = NULL;
return module;
}
/* closes the pipes and frees every data associated with a module record */
static void module_free(fmodule *module)
{
if (module == NULL)
{
return;
}
close(MOD_WRITEFD(module));
close(MOD_READFD(module));
if (MOD_NAME(module) != NULL)
{
free(MOD_NAME(module));
}
if (MOD_ALIAS(module) != NULL)
{
free(MOD_ALIAS(module));
}
while (!FQUEUE_IS_EMPTY(&(MOD_PIPEQUEUE(module))))
{
DeleteMessageQueueBuff(module);
}
free(module);
return;
}
static inline void module_list_insert(fmodule *module, fmodule_list *list)
{
fmodule_store *new_store;
DBUG("module_list_insert", "inserting module");
if (module == NULL)
{
return;
}
new_store = (fmodule_store*)safemalloc(sizeof(fmodule_store));
new_store->module = module;
new_store->next = *list;
*list = new_store;
return;
}
static inline fmodule *module_list_remove(fmodule *module, fmodule_list *list)
{
fmodule_store *current;
if (module == NULL)
{
return NULL;
}
if (module == (*list)->module)
{
DBUG("module_list_remove", "Removing from module list");
current=*list;
*list = (*list)->next;
free(current);
}
else
{
fmodule_store *parent;
/* find it*/
for (
current = (*list)->next, parent = (*list);
current != NULL;
parent = current, current = current->next)
{
if (current->module == module)
{
break;
}
}
/* remove from the list */
if (current != NULL)
{
DBUG("module_list_remove", "Removing from module list");
parent->next = current->next;
free(current);
}
else
{
DBUG("module_list_remove",
"Tried to remove a not listed module!");
return NULL;
}
}
return module;
}
static inline void module_list_destroy(fmodule_list *list)
{
fmodule_store *current;
fmodule_store *next;
for (current = *list; current != NULL; current = next)
{
next = current->next;
module_free(current->module);
free(current);
}
*list = NULL;
return;
}
/*static*/ fmodule *do_execute_module(
F_CMD_ARGS, Bool desperate, Bool do_listen_only)
{
int fvwm_to_app[2], app_to_fvwm[2];
int i, val, nargs = 0;
char *cptr = NULL;
char **args = NULL;
char *arg1 = NULL;
char arg2[20];
char arg3[20];
char arg5[20];
char arg6[20];
char *token;
extern char *ModulePath;
Window win;
FvwmWindow * const fw = exc->w.fw;
fmodule *module;
fvwm_to_app[0] = -1;
fvwm_to_app[1] = -1;
app_to_fvwm[1] = -1;
app_to_fvwm[0] = -1;
args = (char **)safemalloc(7 * sizeof(char *));
/* Olivier: Why ? */
/* if (eventp->type != KeyPress) */
/* UngrabEm(); */
if (action == NULL)
{
goto err_exit;
}
if (fw)
{
win = FW_W(fw);
}
else
{
win = None;
}
action = GetNextToken(action, &cptr);
if (!cptr)
{
goto err_exit;
}
arg1 = searchPath(ModulePath, cptr, EXECUTABLE_EXTENSION, X_OK);
if (arg1 == NULL)
{
/* If this function is called in 'desparate' mode this means
* fvwm is trying a module name as a last resort. In this case
* the error message is inappropriate because it was most
* likely a typo in a command, not a module name. */
if (!desperate)
{
fvwm_msg(
ERR, "executeModule",
"No such module '%s' in ModulePath '%s'",
cptr, ModulePath);
}
goto err_exit;
}
#ifdef REMOVE_EXECUTABLE_EXTENSION
{
char *p;
p = arg1 + strlen(arg1) - strlen(EXECUTABLE_EXTENSION);
if (strcmp(p, EXECUTABLE_EXTENSION) == 0)
{
*p = 0;
}
}
#endif
/* I want one-ended pipes, so I open two two-ended pipes,
* and close one end of each. I need one ended pipes so that
* I can detect when the module crashes/malfunctions */
if (do_listen_only == True)
{
fvwm_to_app[0] = -1;
fvwm_to_app[1] = -1;
}
else if (pipe(fvwm_to_app) != 0)
{
fvwm_msg(ERR, "executeModule", "Failed to open pipe");
goto err_exit;
}
if (pipe(app_to_fvwm) != 0)
{
fvwm_msg(ERR, "executeModule", "Failed to open pipe2");
goto err_exit;
}
if (
fvwm_to_app[0] >= fvwmlib_max_fd ||
fvwm_to_app[1] >= fvwmlib_max_fd ||
app_to_fvwm[0] >= fvwmlib_max_fd ||
app_to_fvwm[1] >= fvwmlib_max_fd)
{
fvwm_msg(ERR, "executeModule", "too many open fds");
goto err_exit;
}
/* all ok, create the space and fill up */
module = module_alloc();
MOD_NAME(module) = stripcpy(cptr);
free(cptr);
sprintf(arg2, "%d", app_to_fvwm[1]);
sprintf(arg3, "%d", fvwm_to_app[0]);
sprintf(arg5, "%lx", (unsigned long)win);
sprintf(arg6, "%lx", (unsigned long)exc->w.wcontext);
args[0] = arg1;
args[1] = arg2;
args[2] = arg3;
args[3] = (char *)get_current_read_file();
if (!args[3])
{
args[3] = "none";
}
args[4] = arg5;
args[5] = arg6;
for (nargs = 6; action = GetNextToken(action, &token), token; nargs++)
{
args = (char **)saferealloc(
(void *)args, (nargs + 2) * sizeof(char *));
args[nargs] = token;
if (MOD_ALIAS(module) == NULL)
{
const char *ptr = skipModuleAliasToken(args[nargs]);
if (ptr && *ptr == '\0')
{
MOD_ALIAS(module) = stripcpy(args[nargs]);
}
}
}
args[nargs] = NULL;
/* Try vfork instead of fork. The man page says that vfork is better!
*/
/* Also, had to change exit to _exit() */
/* Not everyone has vfork! */
val = fork();
if (val > 0)
{
/* This fork remains running fvwm */
/* close appropriate descriptors from each pipe so
* that fvwm will be able to tell when the app dies */
close(app_to_fvwm[1]);
/* dont't care that this may be -1 */
close(fvwm_to_app[0]);
/* add these pipes to fvwm's active pipe list */
MOD_WRITEFD(module) = fvwm_to_app[1];
MOD_READFD(module) = app_to_fvwm[0];
msg_mask_set(
&MOD_PIPEMASK(module), DEFAULT_MASK, DEFAULT_MASK);
free(arg1);
if (DoingCommandLine)
{
/* add to the list of command line modules */
DBUG("executeModule", "starting commandline module\n");
MOD_SET_CMDLINE(module, 1);
}
/* make the PositiveWrite pipe non-blocking. Don't want to jam
* up fvwm because of an uncooperative module */
if (MOD_WRITEFD(module) >= 0)
{
fcntl(MOD_WRITEFD(module), F_SETFL, O_NONBLOCK);
}
/* Mark the pipes close-on exec so other programs
* won`t inherit them */
if (fcntl(MOD_READFD(module), F_SETFD, 1) == -1)
{
fvwm_msg(
ERR, "executeModule",
"module close-on-exec failed");
}
if (
MOD_WRITEFD(module) >= 0 &&
fcntl(MOD_WRITEFD(module), F_SETFD, 1) == -1)
{
fvwm_msg(
ERR, "executeModule",
"module close-on-exec failed");
}
/* module struct is completed, insert into the list */
module_list_insert(module, &module_list);
for (i = 6; i < nargs; i++)
{
if (args[i] != 0)
{
free(args[i]);
}
}
}
else if (val ==0)
{
/* this is the child */
/* this fork execs the module */
#ifdef FORK_CREATES_CHILD
/* dont't care that this may be -1 */
close(fvwm_to_app[1]);
close(app_to_fvwm[0]);
#endif
if (!Pdefault)
{
char visualid[32];
char colormap[32];
sprintf(
visualid, "FVWM_VISUALID=%lx",
XVisualIDFromVisual(Pvisual));
flib_putenv("FVWM_VISUALID", visualid);
sprintf(colormap, "FVWM_COLORMAP=%lx", Pcmap);
flib_putenv("FVWM_COLORMAP", colormap);
}
else
{
flib_unsetenv("FVWM_VISUALID");
flib_unsetenv("FVWM_COLORMAP");
}
/* Why is this execvp?? We've already searched the module
* path! */
execvp(arg1,args);
fvwm_msg(
ERR, "executeModule", "Execution of module failed: %s",
arg1);
perror("");
close(app_to_fvwm[1]);
/* dont't care that this may be -1 */
close(fvwm_to_app[0]);
#ifdef FORK_CREATES_CHILD
exit(1);
#endif
}
else
{
fvwm_msg(ERR, "executeModule", "Fork failed");
free(arg1);
for (i = 6; i < nargs; i++)
{
if (args[i] != 0)
{
free(args[i]);
}
}
free(args);
module_free(module);
return NULL;
}
free(args);
return module;
err_exit:
if (arg1 != NULL)
{
free(arg1);
}
if (cptr != NULL)
{
free(cptr);
}
if (args != NULL)
{
free(args);
}
/* dont't care that these may be -1 */
close(fvwm_to_app[0]);
close(fvwm_to_app[1]);
close(app_to_fvwm[0]);
close(app_to_fvwm[1]);
return NULL;
}
fmodule *executeModuleDesperate(F_CMD_ARGS)
{
fmodule *m;
m = do_execute_module(F_PASS_ARGS, True, False);
return m;
}
void module_kill(fmodule *module)
{
module_list_insert(
module_list_remove(module, &module_list), &death_row);
return;
}
/* free modules in the deathrow */
void module_cleanup(void)
{
module_list_destroy(&death_row);
return;
}
/* void module_send(fmodule *module, unsigned long *ptr, int size) */
/* This used to be marked "fvwm_inline". I removed this
when I added the lockonsend logic. The routine seems too big to
want to inline. dje 9/4/98 */
extern int myxgrabcount; /* defined in libs/Grab.c */
extern char *ModuleUnlock; /* defined in libs/Module.c */
void PositiveWrite(fmodule *module, unsigned long *ptr, int size)
{
extern int moduleTimeout;
msg_masks_t mask;
if (ptr == NULL)
{
return;
}
if (MOD_WRITEFD(module) == -1)
{
return;
}
if (!IS_MESSAGE_IN_MASK(&(MOD_PIPEMASK(module)), ptr[1]))
{
return;
}
/* a dirty hack to prevent FvwmAnimate triggering during Recapture */
/* would be better to send RecaptureStart and RecaptureEnd messages. */
/* If module is lock on send for iconify message and it's an
* iconify event and server grabbed, then return */
mask.m1 = (MOD_NOGRABMASK(module).m1 & MOD_SYNCMASK(module).m1);
mask.m2 = (MOD_NOGRABMASK(module).m2 & MOD_SYNCMASK(module).m2);
if (IS_MESSAGE_IN_MASK(&mask, ptr[1]) && myxgrabcount != 0)
{
return;
}
/* DV: This was once the AddToMessageQueue function. Since it was only
* called once, put it in here for better performance. */
{
mqueue_object_type *c;
c = (mqueue_object_type *)malloc(
sizeof(mqueue_object_type) + size);
if (c == NULL)
{
fvwm_msg(ERR, "PositiveWrite", "malloc failed\n");
exit(1);
}
c->size = size;
c->done = 0;
c->data = (unsigned long *)(c + 1);
memcpy((void*)c->data, (const void*)ptr, size);
fqueue_add_at_end(&(MOD_PIPEQUEUE(module)), c);
}
/* dje, from afterstep, for FvwmAnimate, allows modules to sync with
* fvwm. this is disabled when the server is grabbed, otherwise
* deadlocks happen. M_LOCKONSEND has been replaced by a separated
* mask which defines on which messages the fvwm-to-module
* communication need to be lock on send. olicha Nov 13, 1999 */
/* migo (19-Aug-2000): removed !myxgrabcount to sync M_DESTROY_WINDOW
*/
/* dv (06-Jul-2002): added the !myxgrabcount again. Deadlocks *do*
* happen without it. There must be another way to fix
* M_DESTROY_WINDOW handling in FvwmEvent. */
/*if (IS_MESSAGE_IN_MASK(&(MOD_SYNCMASK(module)), ptr[1]))*/
if (
IS_MESSAGE_IN_MASK(
&(MOD_SYNCMASK(module)), ptr[1]) && !myxgrabcount)
{
fd_set readSet;
int channel = MOD_READFD(module);
struct timeval timeout;
Bool done = False;
fmodule_input *input;
FlushMessageQueue(module);
while (!done)
{
int rc = 0;
/*
* We give the read a long timeout; if the module
* fails to respond within this time then it deserves
* to be KILLED!
*
* NOTE: rather than impose an arbitrary timeout on the
* user, we will make this a configuration parameter.
*/
do
{
timeout.tv_sec = moduleTimeout;
timeout.tv_usec = 0;
FD_ZERO(&readSet);
FD_SET(channel, &readSet);
/* Wait for input to arrive on just one
* descriptor, with a timeout (fvwmSelect <= 0)
* or read() returning wrong size is bad news
*/
rc = fvwmSelect(
channel + 1, &readSet, NULL, NULL,
&timeout);
/* retry if select() failed with EINTR */
} while (rc < 0 && !isTerminated && (errno == EINTR));
if ( isTerminated )
{
break;
}
if (rc > 0)
{
input = module_receive(module);
if (
input == NULL ||
module_input_expect(input,
ModuleUnlockResponse))
{
module_input_discard(input);
done = True;
}
else
{
module_input_execute(input);
}
}
else
{
char *name;
name = get_pipe_name(module);
/* Doh! Something has gone wrong - get rid of
* the offender! */
fvwm_msg(ERR, "PositiveWrite",
"Failed to read descriptor from"
" '%s':\n"
"- data available=%c\n"
"- terminate signal=%c\n",
name,
(FD_ISSET(channel, &readSet) ?
'Y' : 'N'),
isTerminated ? 'Y' : 'N');
module_kill(module);
break;
}
/* Execute all messages from the module until UNLOCK is
* received N.B. This may cause recursion if a command
* results in a sync message to another module, which
* in turn may send a command that results in another
* sync message to this module.
* Hippo: I don't think this will cause deadlocks, but
* the third time we get here the first times UNLOCK
* will be read and then on returning up the third
* level UNLOCK will be read at the first level. This
* could be difficult to fix without turning queueing
* on. Turning queueing on may be bad because it can
* be useful for modules to be able to inject commands
* from modules in a synchronous manner. e.g.
* FvwmIconMan can tell FvwmAnimate to do an animation
* when a window is de-iconified from the IconMan,
* queueing make s this happen too late. */
}
}
return;
}
fmodule_input *module_receive(fmodule *module)
{
unsigned long size;
unsigned long cont;
Window win;
int n;
fmodule_input *input = NULL;
n = read(MOD_READFD(module), &win, sizeof(Window));
if (n < sizeof(Window))
{
/* exit silently, module should have died.. */
goto err;
}
n = read(MOD_READFD(module), &size, sizeof(size));
if (n < sizeof(size))
{
fvwm_msg(
ERR, "module_receive",
"Fail to read command size (Module: %p, read: %i, "
"size: %i)", module, n, (int)sizeof(size));
goto err;
}
if (size > MAX_MODULE_INPUT_TEXT_LEN)
{
fvwm_msg(ERR, "module_receive",
"Module(%p) command is too big (%ld), limit is %d",
module, size, MAX_MODULE_INPUT_TEXT_LEN);
/* The rest of the output from this module is going to be
* scrambled so let's kill it rather than risk interpreting
* garbage */
goto err;
}
/* allocate all storage at once */
/* also save space for the '\0' termination character */
input = (fmodule_input *)safemalloc(
sizeof(fmodule_input) + sizeof(char)*(size + 1));
input->module = module;
input->window = win;
input->command = (char*)input + sizeof(fmodule_input);
n = read(MOD_READFD(module), input->command, size);
if (n < size)
{
fvwm_msg(
ERR, "module_receive",
"Fail to read command (Module: %p, read: %i, size:"
" %ld)", module, n, size);
goto err;
}
input->command[n] = '\0';
n = read(MOD_READFD(module), &cont, sizeof(cont));
if (n < sizeof(cont))
{
fvwm_msg(ERR, "module_receive",
"Module %p, Size Problems (read: %d, size: %d)",
module, n, (int)sizeof(cont));
goto err;
}
if (cont == 0)
{
/* this is documented as a valid way for a module to quit
* so let's not complain */
module_kill(module);
}
return input;
err:
module_kill(module);
module_input_discard(input);
return NULL;
}
/* frees the module input data struct */
void module_input_discard(fmodule_input *input)
{
if (input==NULL)
{
return;
}
free(input);
}
/* returns true if the module command matches "expect", false otherwise */
Bool module_input_expect(fmodule_input *input, char *expect)
{
if (input == NULL || input->command == NULL || expect == NULL)
{
return False;
}
if (strncasecmp(input->command, expect, strlen(expect)) == 0)
{
/* the module sent the expected string */
return True;
}
return False;
}
void module_list_itr_init(fmodule_list_itr *itr)
{
*itr = module_list;
return;
}
fmodule *module_list_itr_next(fmodule_list_itr *itr)
{
fmodule *module;
if (*itr == NULL)
{
return NULL;
}
module = (*itr)->module;
*itr = (*itr)->next;
return module;
}
int module_list_len(fmodule_list *list)
{
int count=0;
fmodule_store *current=*list;
while (current != NULL)
{
current = current->next;
count++;
}
return count;
}
static void KillModuleByName(char *name, char *alias)
{
fmodule_list_itr moditr;
fmodule *module;
if (name == NULL)
{
return;
}
module_list_itr_init(&moditr);
while ( (module = module_list_itr_next(&moditr)) != NULL)
{
if (
MOD_NAME(module) != NULL &&
matchWildcards(name, MOD_NAME(module)) &&
(!alias || (
MOD_ALIAS(module) &&
matchWildcards(alias, MOD_ALIAS(module)))))
{
module_kill(module);
}
}
return;
}
static char *get_pipe_name(fmodule *module)
{
char *name="";
if (MOD_NAME(module) != NULL)
{
if (MOD_ALIAS(module) != NULL)
{
name = CatString3(
MOD_NAME(module), " ", MOD_ALIAS(module));
}
else
{
name = MOD_NAME(module);
}
}
else
{
name = CatString3("(null)", "", "");
}
return name;
}
/*
* returns a pointer inside a string (just after the alias) if ok or NULL
*/
char *skipModuleAliasToken(const char *string)
{
#define is_valid_first_alias_char(ch) (isalpha(ch) || (ch) == '/')
#define is_valid_alias_char(ch) (is_valid_first_alias_char(ch) \
|| isalnum(ch) || (ch) == '-' || \
(ch) == '.' || (ch) == '/')
if (is_valid_first_alias_char(*string))
{
int len = 1;
string++;
while (*string && is_valid_alias_char(*string))
{
if (++len > MAX_MODULE_ALIAS_LEN)
{
return NULL;
}
string++;
}
return (char *)string;
}
return NULL;
#undef is_valid_first_alias_char
#undef is_valid_alias_char
}
/* message mask handling - does this belong here? */
static inline void msg_mask_set(
msg_masks_t *msg_mask, unsigned long m1, unsigned long m2)
{
msg_mask->m1 = m1;
msg_mask->m2 = m2;
return;
}
/*
* Sets the mask to the specific value. If M_EXTENDED_MSG is set in mask, the
* function operates only on the extended messages, otherwise it operates only
* on normal messages.
*/
static void set_message_mask(msg_masks_t *mask, unsigned long msg)
{
if (msg & M_EXTENDED_MSG)
{
mask->m2 = (msg & ~M_EXTENDED_MSG);
}
else
{
mask->m1 = msg;
}
return;
}
/* message queues */
static void DeleteMessageQueueBuff(fmodule *module)
{
mqueue_object_type *obj;
if (fqueue_get_first(&(MOD_PIPEQUEUE(module)), (void **)&obj) == 1)
{
/* remove from queue */
fqueue_remove_or_operate_from_front(
&(MOD_PIPEQUEUE(module)), NULL, NULL, NULL, NULL);
/* we don't need to free the obj->data here because it's in the
* same malloced block as the obj itself. */
free(obj);
}
return;
}
void FlushMessageQueue(fmodule *module)
{
extern int moduleTimeout;
mqueue_object_type *obj;
char *dptr;
int a;
if (module == NULL)
{
return;
}
while (fqueue_get_first(&(MOD_PIPEQUEUE(module)), (void **)&obj) == 1)
{
dptr = (char *)obj->data;
while (obj->done < obj->size)
{
a = write(MOD_WRITEFD(module), &dptr[obj->done],
obj->size - obj->done);
if (a >=0)
{
obj->done += a;
}
/* the write returns EWOULDBLOCK or EAGAIN if the pipe
* is full. (This is non-blocking I/O). SunOS returns
* EWOULDBLOCK, OSF/1 returns EAGAIN under these
* conditions. Hopefully other OSes return one of these
* values too. Solaris 2 doesn't seem to have a man
* page for write(2) (!) */
else if (errno == EWOULDBLOCK || errno == EAGAIN)
{
fd_set writeSet;
struct timeval timeout;
int channel = MOD_WRITEFD(module);
int rc = 0;
do
{
/* Wait until the pipe accepts further
* input */
timeout.tv_sec = moduleTimeout;
timeout.tv_usec = 0;
FD_ZERO(&writeSet);
FD_SET(channel, &writeSet);
rc = fvwmSelect(
channel + 1, NULL, &writeSet,
NULL, &timeout);
/* retry if select() failed with EINTR
*/
} while ((rc < 0) && !isTerminated &&
(errno == EINTR));
if ( isTerminated )
{
return;
}
if (!FD_ISSET(channel, &writeSet))
{
char *name;
name = get_pipe_name(module);
/* Doh! Something has gone wrong - get
* rid of the offender! */
fvwm_msg(
ERR, "FlushMessageQueue",
"Failed to write descriptor to"
" '%s':\n"
"- select rc=%d\n"
"- terminate signal=%c\n",
name, rc, isTerminated ?
'Y' : 'N');
module_kill(module);
return;
}
/* pipe accepts further input; continue */
continue;
}
else if (errno != EINTR)
{
module_kill(module);
return;
}
}
DeleteMessageQueueBuff(module);
}
return;
}
void FlushAllMessageQueues(void)
{
fmodule_list_itr moditr;
fmodule *module;
module_list_itr_init(&moditr);
while ( (module = module_list_itr_next(&moditr)) != NULL)
{
FlushMessageQueue(module);
}
return;
}
/* empty, only here so that the signal handling initialization code is the
* same for modules and fvwm */
RETSIGTYPE DeadPipe(int sig)
{
SIGNAL_RETURN;
}
void CMD_Module(F_CMD_ARGS)
{
do_execute_module(F_PASS_ARGS, False, False);
return;
}
void CMD_ModuleListenOnly(F_CMD_ARGS)
{
do_execute_module(F_PASS_ARGS, False, True);
return;
}
void CMD_KillModule(F_CMD_ARGS)
{
char *name;
char *alias = NULL;
action = GetNextToken(action,&name);
if (!name)
{
return;
}
GetNextToken(action, &alias);
KillModuleByName(name, alias);
free(name);
if (alias)
{
free(alias);
}
return;
}
void CMD_ModuleSynchronous(F_CMD_ARGS)
{
int sec = 0;
char *next;
char *token;
char *expect = ModuleFinishedStartupResponse;
fmodule *module;
fd_set in_fdset;
fd_set out_fdset;
time_t start_time;
Bool done = False;
Bool need_ungrab = False;
char *escape = NULL;
XEvent tmpevent;
if (!action)
{
return;
}
token = PeekToken(action, &next);
if (StrEquals(token, "expect"))
{
token = PeekToken(next, &next);
if (token)
{
expect = alloca(strlen(token) + 1);
strcpy(expect, token);
}
action = next;
token = PeekToken(action, &next);
}
if (token && StrEquals(token, "timeout"))
{
if (GetIntegerArguments(next, &next, &sec, 1) > 0 && sec > 0)
{
/* we have a delay, skip the number */
action = next;
}
else
{
fvwm_msg(ERR, "executeModuleSync", "illegal timeout");
return;
}
}
if (!action)
{
/* no module name */
return;
}
module = do_execute_module(F_PASS_ARGS, False, False);
if (module == NULL)
{
/* executing the module failed, just return */
return;
}
/* Busy cursor stuff */
if (Scr.BusyCursor & BUSY_MODULESYNCHRONOUS)
{
if (GrabEm(CRS_WAIT, GRAB_BUSY))
need_ungrab = True;
}
/* wait for module input */
start_time = time(NULL);
while (!done)
{
struct timeval timeout;
int num_fd;
/* A signal here could interrupt the select call. We would
* then need to restart our select, unless the signal was
* a "terminate" signal. Note that we need to reinitialise
* all of select's parameters after it has returned. */
do
{
FD_ZERO(&in_fdset);
FD_ZERO(&out_fdset);
FD_SET(MOD_READFD(module), &in_fdset);
if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
{
FD_SET(MOD_WRITEFD(module), &out_fdset);
}
timeout.tv_sec = 0;
timeout.tv_usec = 1;
num_fd = fvwmSelect(
fvwmlib_max_fd, &in_fdset, &out_fdset, 0,
&timeout);
} while (num_fd < 0 && !isTerminated);
/* Exit if we have received a "terminate" signal */
if (isTerminated)
{
break;
}
if (num_fd > 0)
{
if (FD_ISSET(MOD_READFD(module), &in_fdset))
{
fmodule_input * input;
/* Check for module input. */
input = module_receive(module);
if (
input == NULL ||
module_input_expect(input,expect))
{
module_input_discard(input);
done = True;
}
else
{
module_input_execute(input);
}
}
if ((MOD_WRITEFD(module) >= 0) &&
FD_ISSET(MOD_WRITEFD(module), &out_fdset))
{
FlushMessageQueue(module);
}
}
usleep(1000);
if (difftime(time(NULL), start_time) >= sec && sec)
{
/* timeout */
done = True;
}
/* Check for "escape function" */
if (FPending(dpy) &&
FCheckMaskEvent(dpy, KeyPressMask, &tmpevent))
{
int context;
XClassHint *class;
char *name;
Window w;
context = GetContext(
NULL, exc->w.fw, &tmpevent, &w);
if (exc->w.fw != NULL)
{
class = &(exc->w.fw->class);
name = exc->w.fw->name.name;
}
else
{
class = NULL;
name = NULL;
}
escape = CheckBinding(
Scr.AllBindings, STROKE_ARG(0)
tmpevent.xkey.keycode, tmpevent.xkey.state,
GetUnusedModifiers(), context, BIND_KEYPRESS,
class, name);
if (escape != NULL)
{
if (!strcasecmp(escape,"escapefunc"))
{
done = True;
}
}
}
} /* while */
if (need_ungrab)
{
UngrabEm(GRAB_BUSY);
}
return;
}
/* mask handling - does this belong here? */
void CMD_set_mask(F_CMD_ARGS)
{
unsigned long val;
if (exc->m.module == NULL)
{
return;
}
if (!action || sscanf(action, "%lu", &val) != 1)
{
val = 0;
}
set_message_mask(&(MOD_PIPEMASK(exc->m.module)), (unsigned long)val);
return;
}
void CMD_set_sync_mask(F_CMD_ARGS)
{
unsigned long val;
if (exc->m.module == NULL)
{
return;
}
if (!action || sscanf(action,"%lu",&val) != 1)
{
val = 0;
}
set_message_mask(&(MOD_SYNCMASK(exc->m.module)), (unsigned long)val);
return;
}
void CMD_set_nograb_mask(F_CMD_ARGS)
{
unsigned long val;
if (exc->m.module == NULL)
{
return;
}
if (!action || sscanf(action,"%lu",&val) != 1)
{
val = 0;
}
set_message_mask(&(MOD_NOGRABMASK(exc->m.module)), (unsigned long)val);
return;
}
syntax highlighted by Code2HTML, v. 0.9.1