1 /*
2 * Copyright (c) 1999 Ethan Fischer <allanon@crystaltokyo.com>
3 * Copyright (C) 1998 Guylhem Aznar
4 * Copyright (C) 1993 Robert Nation
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
22 /***********************************************************************
23 *
24 * code for launching afterstep modules.
25 *
26 ***********************************************************************/
27
28 #define LOCAL_DEBUG
29
30 #include "../../configure.h"
31
32 #include "asinternals.h"
33
34 #include <signal.h>
35 #include <stdarg.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <sys/file.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h> /* for chmod() */
41 #include <sys/types.h>
42 #include <sys/un.h> /* for struct sockaddr_un */
43
44 #if TIME_WITH_SYS_TIME
45 # include <sys/time.h>
46 # include <time.h>
47 #else
48 # if HAVE_SYS_TIME_H
49 # include <sys/time.h>
50 # else
51 # include <time.h>
52 # endif
53 #endif
54
55 #include <X11/keysym.h>
56
57 #include "../../libAfterStep/desktop_category.h"
58 #include "../../libAfterStep/module.h"
59 #include "../../libAfterStep/wmprops.h"
60
61 static DECL_VECTOR (send_data_type, module_output_buffer);
62
63 static void DeleteQueueBuff (module_t * module);
64 static void AddToQueue (module_t * module, send_data_type * ptr, int size,
65 int done);
66
67 int module_listen (const char *socket_name);
68
69
70 /* create a named UNIX socket, and start watching for connections */
module_setup_socket()71 Bool module_setup_socket ()
72 {
73 char *tmp;
74 #ifdef __CYGWIN__
75 { /* there are problems under Windows as home dir could be anywhere -
76 * so we just use /tmp since there will only be 1 user anyways :) */
77 tmp = safemalloc (4 + 9 + 32 + 1);
78 /*sprintf (tmp, "/tmp/connect.%s", display_string); */
79 sprintf (tmp, "/tmp/as-connect.%ld", Scr.screen);
80 fprintf (stderr, "using %s for intermodule communications.\n", tmp);
81 }
82 #else
83 {
84 char *display = XDisplayString (dpy);
85 char *tmpdir = getenv ("TMPDIR");
86 static char *default_tmp_dir = "/tmp";
87 if (tmpdir != NULL)
88 if (CheckDir (tmpdir) < 0)
89 tmpdir = NULL;
90
91 if (tmpdir == NULL)
92 tmpdir = default_tmp_dir;
93 if (access (tmpdir, W_OK) != 0)
94 if ((tmpdir = getenv ("HOME")) == NULL)
95 return False;
96
97 tmp = safemalloc (strlen (tmpdir) + 11 + 32 + strlen (display) + 1);
98 sprintf (tmp, "%s/afterstep-%d.%s", tmpdir, getuid (), display);
99 LOCAL_DEBUG_OUT ("using socket \"%s\" for intermodule communications",
100 tmp);
101 }
102 #endif
103 set_as_module_socket (Scr.wmprops, tmp);
104 Module_fd = socket_listen (tmp);
105 free (tmp);
106
107 XSync (dpy, 0);
108
109 return (Module_fd >= 0);
110 }
111
KillModule(module_t * module,Bool dont_free_memory)112 void KillModule (module_t * module, Bool dont_free_memory)
113 {
114 LOCAL_DEBUG_OUT ("module %p ", module);
115 LOCAL_DEBUG_OUT ("module name \"%s\"", module->name);
116 if (module->fd > 0)
117 close (module->fd);
118
119 if (!dont_free_memory) {
120 while (module->output_queue != NULL)
121 DeleteQueueBuff (module);
122 if (module->name != NULL)
123 destroy_string (&(module->name));
124 destroy_string (&(module->cmd_line));
125 destroy_string (&(module->ibuf.text));
126
127 if (module->ibuf.func != NULL) {
128 free_func_data (module->ibuf.func);
129 free (module->ibuf.func);
130 }
131 memset (module, 0x00, sizeof (module_t));
132 } else {
133 module->output_queue = NULL;
134 module->name = NULL;
135 module->ibuf.text = NULL;
136 module->ibuf.func = NULL;
137 }
138
139 module->fd = -1;
140 module->active = -1;
141 }
142
143
144 /*
145 * ReadModuleInput Does actuall read from the module pipe.
146 * returns :
147 * 0, -1 - error or not enough data
148 * 1 - SUCCESS
149 */
150
151
152
153 static int
ReadModuleInput(module_t * module,size_t * offset,size_t size,void * ptr)154 ReadModuleInput (module_t * module, size_t * offset, size_t size,
155 void *ptr)
156 {
157 size_t done_this = module->ibuf.done - *offset;
158 int n = size;
159
160 if (done_this >= 0 && done_this < size) {
161 ptr += done_this;
162 n = read (module->fd, ptr, size - done_this);
163 if (n > 0) {
164 module->ibuf.done += n;
165 if (module->ibuf.done < *offset + size)
166 return 0; /* No more data available */
167 } else
168 return (n == -1 && (errno == EINTR || errno == EAGAIN)) ? 0 : -1;
169 }
170 if (n > 0)
171 *offset += n;
172 return 1; /* Success */
173 }
174
175 static void
CheckCmdTextSize(module_t * module,CARD32 * size,CARD32 * curr_len,char ** text)176 CheckCmdTextSize (module_t * module, CARD32 * size, CARD32 * curr_len,
177 char **text)
178 {
179 /* max command length is 1024 */
180 if (*size > 1024) {
181 show_error ("command from module '%s' is too big (%d)", module->name,
182 *size);
183 *size = 1024;
184 }
185
186 /* need to be able to read in command */
187 if (*curr_len < *size + 1) {
188 *curr_len = *size + 1;
189 *text = realloc (*text, *curr_len);
190 }
191 }
192
193 /*
194 * Higher level protocol handler
195 *
196 * Two possible protocols :
197 * 1. Text command line :
198 * <window>< 0<size<256 >< size bytes of text >
199 * < continuation_indicator == F_FUNCTIONS_NUM >
200 *
201 * 2. Preprocessed AS Function data :
202 * <window>< size==0 ><function_code>
203 * < 0<name_size<256 >[< name_size bytes of function name >|<nothing if size == 0>]
204 * < 0<text_size<256 >[< size bytes of text >|< nothing if size == 0>]
205 * < 2*sizeof(long) of func_val[] >< 2*sizeof(long) of unit_val >
206 * < continuation_indicator == F_FUNCTIONS_NUM >
207 *
208 * Returns :
209 * -2 - bad module
210 * 0, -1 - error or not enough data
211 * > 0 - command execution result
212 */
213
HandleModuleInput(module_t * module)214 int HandleModuleInput (module_t * module)
215 {
216 size_t offset = 0;
217 int res = 1;
218 Bool invalid_func = False;
219 register module_ibuf_t *ibuf;
220
221 ibuf = &(module->ibuf);
222
223 /* read a window id */
224 res =
225 ReadModuleInput (module, &offset, sizeof (ibuf->window),
226 &(ibuf->window));
227 if (res > 0) {
228 module->active = 1;
229 res =
230 ReadModuleInput (module, &offset, sizeof (ibuf->size),
231 &(ibuf->size));
232 }
233
234 LOCAL_DEBUG_OUT ("res(%d)->window(0x%X)->size(%d)", res, ibuf->window,
235 ibuf->size);
236 if (res > 0) {
237 if (ibuf->size > 0) { /* Protocol 1 */
238 LOCAL_DEBUG_OUT ("Incoming message in proto 1%s", "");
239 CheckCmdTextSize (module, &(ibuf->size), &(ibuf->len),
240 &(ibuf->text));
241 res = ReadModuleInput (module, &offset, ibuf->size, ibuf->text);
242
243 if (res > 0) {
244 /* null-terminate the command line */
245 ibuf->text[ibuf->size] = '\0';
246 ibuf->func = String2Func (ibuf->text, ibuf->func, False);
247 invalid_func = (ibuf->func == NULL);
248 }
249 } else { /* Protocol 2 */
250
251 /* for module->afterstep communications - 32 bit values are always used : */
252 CARD32 curr_len;
253 CARD32 tmp32, tmp32_val[2] = { 0, 0 }
254 , tmp32_unit[2] = {
255 0, 0};
256 register FunctionData *pfunc = ibuf->func;
257
258 LOCAL_DEBUG_OUT ("Incoming message in proto 2%s", "");
259 if (pfunc == NULL) {
260 pfunc = (FunctionData *) safecalloc (1, sizeof (FunctionData));
261 init_func_data (pfunc);
262 ibuf->func = pfunc;
263 }
264
265 res = ReadModuleInput (module, &offset, sizeof (CARD32), &tmp32);
266 pfunc->func = tmp32;
267
268 if (res > 0) {
269 if (!IsValidFunc (pfunc->func)) {
270 res = 0;
271 ibuf->done = 0;
272 invalid_func = True;
273 } else
274 res =
275 ReadModuleInput (module, &offset, sizeof (ibuf->name_size),
276 &(ibuf->name_size));
277 }
278 if (res > 0 && ibuf->name_size > 0) {
279 curr_len = (pfunc->name) ? strlen (pfunc->name) + 1 : 0;
280 CheckCmdTextSize (module, &(ibuf->name_size), &curr_len,
281 &(pfunc->name));
282 res =
283 ReadModuleInput (module, &offset, ibuf->name_size,
284 pfunc->name);
285 pfunc->name[ibuf->name_size] = '\0';
286 }
287 LOCAL_DEBUG_OUT ("name_size = %d, pfunc->name = %p", ibuf->name_size,
288 pfunc->name);
289 if (res > 0)
290 res =
291 ReadModuleInput (module, &offset, sizeof (ibuf->text_size),
292 &(ibuf->text_size));
293
294 if (res > 0 && ibuf->text_size > 0) {
295 curr_len = (pfunc->text) ? strlen (pfunc->text) + 1 : 0;
296 CheckCmdTextSize (module, &(ibuf->text_size), &curr_len,
297 &(pfunc->text));
298 res =
299 ReadModuleInput (module, &offset, ibuf->text_size,
300 pfunc->text);
301 pfunc->text[ibuf->text_size] = '\0';
302 }
303 LOCAL_DEBUG_OUT ("text_size = %d, pfunc->text = %p", ibuf->text_size,
304 pfunc->text);
305 if (res > 0) {
306 res =
307 ReadModuleInput (module, &offset, sizeof (tmp32_val),
308 &(tmp32_val[0]));
309 if (res > 0) {
310 pfunc->func_val[0] = tmp32_val[0];
311 pfunc->func_val[1] = tmp32_val[1];
312 }
313 }
314
315
316 if (res > 0) {
317 res =
318 ReadModuleInput (module, &offset, sizeof (tmp32_unit),
319 &(tmp32_unit[0]));
320 if (res > 0) {
321 pfunc->unit_val[0] = tmp32_unit[0];
322 pfunc->unit_val[1] = tmp32_unit[1];
323 }
324 }
325
326 if (res > 0 && IsValidFunc (pfunc->func))
327 invalid_func = False;
328 }
329 }
330
331 /* get continue command */
332 if (res > 0) {
333 res =
334 ReadModuleInput (module, &offset, sizeof (ibuf->cont),
335 &(ibuf->cont));
336 if (res > 0) {
337 if (ibuf->cont != F_FUNCTIONS_NUM)
338 if (ibuf->cont != 1)
339 res = -1;
340 } else
341 ibuf->cont = 0;
342 }
343
344 if (res < 0)
345 KillModule (module, False);
346 else if (res > 0) {
347 ibuf->done = 0; /* done reading command */
348 if (invalid_func)
349 res = -1;
350 }
351 LOCAL_DEBUG_OUT ("result(%d)", res);
352 PRINT_MEM_STATS (NULL);
353 return res;
354 }
355
356 #define PREALLOCED_QUEUE_LEN 256
357 #define PREALLOCED_QUEUE_DATA_LEN 128
358 static unsigned char _as_prealloced_queue_data[PREALLOCED_QUEUE_LEN][PREALLOCED_QUEUE_DATA_LEN];
359 static Bool _as_prealloced_init = False;
360 static struct queue_buff_struct _as_prealloced_queue_elems[PREALLOCED_QUEUE_LEN];
361
362 static void
AddToQueue(module_t * module,send_data_type * ptr,int size,int done)363 AddToQueue (module_t * module, send_data_type * ptr, int size, int done)
364 {
365 register struct queue_buff_struct *new_elem, **tail;
366 int i = 0;
367
368 if (!_as_prealloced_init) {
369 memset (&_as_prealloced_queue_elems[0], 0x00,
370 sizeof (_as_prealloced_queue_elems));
371 _as_prealloced_init = True;
372 }
373 #if 1
374 if (size < PREALLOCED_QUEUE_DATA_LEN)
375 while (++i < PREALLOCED_QUEUE_LEN)
376 if (_as_prealloced_queue_elems[i].prealloced_idx == 0)
377 break;
378
379 if (i > 0 && i < PREALLOCED_QUEUE_LEN) {
380 new_elem = &_as_prealloced_queue_elems[i];
381 new_elem->prealloced_idx = i;
382 new_elem->data = &_as_prealloced_queue_data[i][0];
383 new_elem->next = NULL;
384 } else
385 #endif
386 {
387 new_elem = safecalloc (1, sizeof (struct queue_buff_struct));
388 new_elem->data = safemalloc (size);
389 }
390 new_elem->size = size;
391 new_elem->done = done;
392 memcpy (new_elem->data, ptr, size);
393 LOCAL_DEBUG_OUT ("que_buff %p: size = %d, done = %d, data = %p",
394 new_elem, size, done, new_elem->data);
395 for (tail = &(module->output_queue); *tail; tail = &((*tail)->next)) {
396 /*LOCAL_DEBUG_OUT ("*tail = %p, (*tail)->next = %p", *tail, ((*tail)->next))*/;
397 }
398 *tail = new_elem;
399 }
400
DeleteQueueBuff(module_t * module)401 static void DeleteQueueBuff (module_t * module)
402 {
403 register struct queue_buff_struct *a = module->output_queue;
404 if (a) {
405 module->output_queue = a->next;
406 LOCAL_DEBUG_OUT ("deleting buffer %p sent to module %p - next %p ", a,
407 module, a->next);
408 if (a->prealloced_idx == 0) {
409 free (a->data);
410 free (a);
411 } else
412 a->prealloced_idx = 0;
413 }
414 }
415
FlushQueue(module_t * module)416 int FlushQueue (module_t * module)
417 {
418 extern int errno;
419 int fd;
420 register struct queue_buff_struct *curr;
421 LOCAL_DEBUG_OUT ("module \"%s\", active= %d, out_queue = %p", module->name, module->active, module->output_queue);
422 if (module->active <= 0)
423 return -1;
424 if (module->output_queue == NULL)
425 return 1;
426
427 fd = module->fd;
428 while ((curr = module->output_queue) != NULL) {
429 register unsigned char *dptr = curr->data;
430 int bytes_written = 0;
431
432 do {
433 if ((bytes_written =
434 write (fd, &dptr[curr->done], curr->size - curr->done)) > 0)
435 curr->done += bytes_written;
436 LOCAL_DEBUG_OUT ("wrote %d bytes into the module %p pipe",
437 bytes_written, module);
438 } while (curr->done < curr->size && bytes_written > 0);
439
440 /* the write returns EWOULDBLOCK or EAGAIN if the pipe is full.
441 * (This is non-blocking I/O). SunOS returns EWOULDBLOCK, OSF/1
442 * returns EAGAIN under these conditions. Hopefully other OSes
443 * return one of these values too. Solaris 2 doesn't seem to have
444 * a man page for write(2) (!) */
445 if (bytes_written < 0) {
446 if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR) {
447 return 0;
448 } else {
449 KillModule (module, False);
450 return -1;
451 }
452 }
453 DeleteQueueBuff (module);
454 }
455 return 1;
456 }
457
FlushAllQueues()458 void FlushAllQueues ()
459 {
460 fd_set out_fdset;
461 int retval = -1;
462 struct timeval tv;
463 struct timeval *t = NULL;
464
465 do {
466 int max_fd = -1;
467 register int i = MIN (MODULES_NUM, Module_npipes);
468 register module_t *list = MODULES_LIST;
469
470 FD_ZERO (&out_fdset);
471
472 while (--i >= 0) {
473 if (list[i].fd >= 0) {
474
475 int res = 0;
476 if (list[i].output_queue && (retval < 0 || FD_ISSET (list[i].fd, &out_fdset)))
477 res = FlushQueue (&(list[i]));
478 if (res >= 0 && list[i].output_queue != NULL) {
479 FD_SET (list[i].fd, &out_fdset);
480 if (max_fd < list[i].fd)
481 max_fd = list[i].fd;
482 }
483 }
484 }
485
486 if (max_fd < 0)
487 return; /* no more output left */
488
489 tv.tv_sec = 0;
490 tv.tv_usec = 15000;
491 t = &tv;
492 retval =
493 PORTABLE_SELECT (min (max_fd + 1, fd_width), NULL, &out_fdset,
494 NULL, t);
495 if (retval <= 0)
496 return;
497 } while (1);
498 }
499
500
501
502
503 #include <sys/errno.h>
504 static inline int
PositiveWrite(unsigned int channel,send_data_type * ptr,int size)505 PositiveWrite (unsigned int channel, send_data_type * ptr, int size)
506 {
507 module_t *module = &(MODULES_LIST[channel]);
508 register CARD32 mask = ptr[1];
509
510 LOCAL_DEBUG_OUT ("module(%p)->name(\"%s\")->active(%d)->module_mask(0x%X)->mask(0x%X)",
511 module, module->name, module->active, module->mask, mask);
512 if (module->active < 0 || !get_flags (module->mask, mask))
513 return -1;
514
515 AddToQueue (module, ptr, size, 0);
516 LOCAL_DEBUG_OUT("lock_on_send_mask = %d,is_server_grabbed =%d", get_flags (module->lock_on_send_mask, mask), is_server_grabbed ());
517 if (get_flags (module->lock_on_send_mask, mask) && !is_server_grabbed ()) {
518 int res;
519 int wait_count = 0;
520 do {
521 LOCAL_DEBUG_OUT ("Attempting to FlushQueue for module %d", module);
522 if ((res = FlushQueue (module)) >= 0) {
523 sleep_a_millisec (10); /* give it some time to react */
524 /* LOCAL_DEBUG_OUT ("HandleModuleInput for module %d", module);*/
525 res = HandleModuleInput (module);
526 }
527 if (res > 0) { /* need to run command */
528 LOCAL_DEBUG_OUT
529 ("replay received while waiting for UNLOCK, func = %ld, F_UNLOCK = %d",
530 module->ibuf.func->func, F_UNLOCK);
531 if (module->ibuf.func->func == F_UNLOCK)
532 return size;
533 RunCommand (module->ibuf.func, channel, module->ibuf.window);
534 }
535 sleep_a_millisec (10); /* give it some time to react */
536 ++wait_count;
537 /* module has no more then 20 seconds to unlock us */
538 } while (res >= 0 && wait_count < 2000);
539 }
540 LOCAL_DEBUG_OUT ("all done %d", size);
541 return size;
542 }
543
make_msg_header(send_data_type msg_type,send_data_type size)544 static send_data_type *make_msg_header (send_data_type msg_type,
545 send_data_type size)
546 {
547 static send_data_type msg_header[MSG_HEADER_SIZE];
548
549 msg_header[0] = START_FLAG;
550 msg_header[1] = msg_type;
551 msg_header[2] = size + MSG_HEADER_SIZE;
552 return &(msg_header[0]);
553 }
554
SendBuffer(int channel)555 void SendBuffer (int channel)
556 {
557 send_data_type *b = VECTOR_HEAD (send_data_type, module_output_buffer);
558 send_data_type size_to_send;
559
560 size_to_send = b[2];
561 if (size_to_send > 0 && Modules) {
562 /* lets make sure that we will not overrun the buffer : */
563 realloc_vector (&module_output_buffer, size_to_send);
564 b = VECTOR_HEAD (send_data_type, module_output_buffer);
565 LOCAL_DEBUG_OUT ("sending %ld words to module # %d of %d",
566 size_to_send, channel, MODULES_NUM);
567 if (channel >= 0 && channel < MODULES_NUM)
568 PositiveWrite (channel, b, size_to_send * sizeof (send_data_type));
569 else {
570 register int i = MODULES_NUM;
571 while (--i >= 0) {
572 LOCAL_DEBUG_OUT ("sending to module %d ...", i);
573 PositiveWrite (i, b, size_to_send * sizeof (send_data_type));
574 LOCAL_DEBUG_OUT ("done sending to module %d ...", i);
575 }
576 }
577 }
578 }
579
580
581
582 /********************************************************************************/
583 /* public interfaces : */
584 /********************************************************************************/
AcceptModuleConnection(int socket_fd)585 int AcceptModuleConnection (int socket_fd)
586 {
587 int fd;
588 unsigned int len = sizeof (struct sockaddr_un);
589 struct sockaddr_un name;
590
591 fd = accept (socket_fd, (struct sockaddr *)&name, &len);
592
593 if (fd < 0 && errno != EWOULDBLOCK)
594 show_system_error ("error accepting connection");
595
596 /* set non-blocking I/O mode */
597 if (fd >= 0) {
598 if (fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK) == -1) {
599 show_system_error
600 ("unable to set non-blocking I/O for module socket");
601 close (fd);
602 fd = -1;
603 }
604 }
605
606 /* mark as close-on-exec so other programs won't inherit the socket */
607 if (fd >= 0) {
608 if (fcntl (fd, F_SETFD, 1) == -1) {
609 show_system_error ("unable to set close-on-exec for module socket");
610 close (fd);
611 fd = -1;
612 }
613 }
614
615 if (fd >= 0 && Modules) {
616 int channel = -1;
617
618 /* Look for an available pipe slot */
619 if (MODULES_NUM < Module_npipes - 1) {
620 module_t new_module;
621 memset (&new_module, 0x00, sizeof (module_t));
622 /* add pipe to afterstep's active pipe list */
623 new_module.name = mystrdup ("unknown module");
624 new_module.fd = fd;
625 new_module.active = 0;
626 new_module.mask = MAX_MASK;
627 new_module.output_queue = NULL;
628 /* adding new module to the end of the list */
629 LOCAL_DEBUG_OUT
630 ("adding new module: total modules %d. list starts at %p",
631 MODULES_NUM, MODULES_LIST);
632 channel = vector_insert_elem (Modules, &new_module, 1, NULL, False);
633 LOCAL_DEBUG_OUT
634 ("added module # %d : total modules %d. list starts at %p",
635 channel, MODULES_NUM, MODULES_LIST);
636
637 }
638 if (channel < 0) {
639 show_error ("too many modules!");
640 close (fd);
641 fd = -1;
642 }
643 }
644
645 return fd;
646 }
647
648
ShutdownModules(Bool dont_free_memory)649 void ShutdownModules (Bool dont_free_memory)
650 {
651 if (Modules != NULL) {
652 register int i = MODULES_NUM;
653 register module_t *list = MODULES_LIST;
654 LOCAL_DEBUG_OUT ("pid(%d),total modules %d. list starts at %p",
655 getpid (), MODULES_NUM, MODULES_LIST);
656 while (--i >= 0)
657 KillModule (&(list[i]), dont_free_memory);
658 if (!dont_free_memory) {
659 LOCAL_DEBUG_OUT ("pid(%d),destroy_asvector", getpid ());
660 destroy_asvector (&Modules);
661 LOCAL_DEBUG_OUT ("pid(%d),free_vector", getpid ());
662 free_vector (&module_output_buffer);
663 LOCAL_DEBUG_OUT ("pid(%d),modules are down", getpid ());
664 }
665 Modules = NULL;
666 }
667 }
668
SetupModules(void)669 void SetupModules (void)
670 {
671 if (Modules)
672 ShutdownModules (False);
673
674 Module_npipes = get_fd_width ();
675 LOCAL_DEBUG_OUT ("max Module pipes = %d", Module_npipes);
676 Modules = create_asvector (sizeof (module_t));
677 module_setup_socket ();
678 }
679
ExecModule(char * action,Window win,int context)680 void ExecModule (char *action, Window win, int context)
681 {
682 char *module, *cmd, *args;
683
684 if (action == NULL || Modules == NULL)
685 return;
686 args = parse_filename (action, &module);
687 if (module == NULL)
688 return;
689 if ((cmd = find_file (module, Environment->module_path, X_OK)) == NULL) {
690 show_error ("no such module %s in path %s\n", module,
691 Environment->module_path);
692 free (module);
693 return;
694 }
695 free (module);
696
697 args = stripcpy (args);
698 spawn_child (cmd, -1, Scr.screen, NULL, win, context, True, True, args,
699 NULL);
700
701 if (args)
702 free (args);
703 free (cmd);
704 }
705
706
707 void
HandleModuleInOut(unsigned int channel,Bool has_input,Bool has_output)708 HandleModuleInOut (unsigned int channel, Bool has_input, Bool has_output)
709 {
710 int res = 0;
711
712 if (Modules && channel < MODULES_NUM) {
713 register module_t *module = &(MODULES_LIST[channel]);
714 LOCAL_DEBUG_OUT ("module %d has %s input and %s output", channel,
715 has_input ? "" : "no", has_output ? "" : "no");
716 if (has_input) {
717 res = HandleModuleInput (module);
718 if (res < 0)
719 has_output = 0;
720 else if (res > 0) /* need to run command */
721 RunCommand (module->ibuf.func, channel, module->ibuf.window);
722 }
723 /* module could be killed inside RunCoimmand ! */
724 if (has_output && module->fd > 0)
725 FlushQueue (module);
726 }
727 }
728
DeadPipe(int nonsense)729 void DeadPipe (int nonsense)
730 {
731 signal (SIGPIPE, DeadPipe);
732 }
733
734
FindModuleByName(char * name)735 int FindModuleByName (char *name)
736 {
737 int module = -1;
738 if (Modules && name) {
739 wild_reg_exp *wrexp = compile_wild_reg_exp (name);
740
741 if (wrexp != NULL) {
742 register int i = MODULES_NUM;
743 register module_t *list = MODULES_LIST;
744
745 while (--i >= 0)
746 if (list[i].fd > 0) {
747 LOCAL_DEBUG_OUT
748 ("checking if module %d \"%s\" matches regexp \"%s\"", i,
749 list[i].name, name);
750 if (match_wild_reg_exp (list[i].name, wrexp) == 0) {
751 module = i;
752 break;
753 }
754 }
755 destroy_wild_reg_exp (wrexp);
756 }
757 }
758 return module;
759 }
760
GetModuleCmdLineByName(char * name)761 char *GetModuleCmdLineByName (char *name)
762 {
763 int module = FindModuleByName (name);
764 if (module >= 0) {
765 register module_t *list = MODULES_LIST;
766 if (list[module].cmd_line)
767 return mystrdup (list[module].cmd_line);
768 }
769 return NULL;
770 }
771
KillModuleByName(char * name)772 void KillModuleByName (char *name)
773 {
774 int module = FindModuleByName (name);
775 if (module >= 0) {
776 register module_t *list = MODULES_LIST;
777 KillModule (&(list[module]), False);
778 }
779 }
780
KillAllModulesByName(char * name)781 void KillAllModulesByName (char *name)
782 {
783 int module;
784 while ((module = FindModuleByName (name)) >= 0) {
785 register module_t *list = MODULES_LIST;
786 KillModule (&(list[module]), False);
787 }
788 }
789
KillAllModules()790 int KillAllModules ()
791 {
792 int count = 0;
793 if (Modules != NULL) {
794 register int i = MODULES_NUM;
795 register module_t *list = MODULES_LIST;
796 LOCAL_DEBUG_OUT ("pid(%d),total modules %d. list starts at %p",
797 getpid (), MODULES_NUM, MODULES_LIST);
798 while (--i >= 0) {
799 KillModule (&(list[i]), False);
800 count++;
801 }
802 }
803 return count;
804 }
805
806
807 void
SendPacket(int channel,send_data_type msg_type,send_data_type num_datum,...)808 SendPacket (int channel, send_data_type msg_type, send_data_type num_datum,
809 ...)
810 {
811 va_list ap;
812 register send_data_type *body;
813 int i;
814
815 flush_vector (&module_output_buffer);
816 append_vector (&module_output_buffer,
817 make_msg_header (msg_type, num_datum), MSG_HEADER_SIZE);
818
819 append_vector (&module_output_buffer, NULL, num_datum);
820 body = VECTOR_TAIL (send_data_type, module_output_buffer);
821
822 va_start (ap, num_datum);
823 for (i = 0; i < num_datum; i++)
824 *(body++) = va_arg (ap, send_data_type);
825
826 VECTOR_USED (module_output_buffer) += num_datum;
827 va_end (ap);
828
829 LOCAL_DEBUG_OUT("Sending buffer , used = %d", VECTOR_USED (module_output_buffer));
830 SendBuffer (channel);
831 LOCAL_DEBUG_OUT("Done sending buffer , used = %d", VECTOR_USED (module_output_buffer));
832 }
833
834
SendConfig(int module,send_data_type event_type,ASWindow * t)835 void SendConfig (int module, send_data_type event_type, ASWindow * t)
836 {
837 send_signed_data_type frame_x = 0, frame_y = 0, frame_width =
838 0, frame_height = 0;
839 send_ID_type icon_title_w = None, icon_pixmap_w = None;
840 send_signed_data_type icon_x = 0, icon_y = 0, icon_width =
841 0, icon_height = 0;
842 send_signed_data_type pid = -1;
843 union {
844 ASWindow *asw;
845 send_data_type id;
846 } asw_id;
847
848 if (t->frame_canvas) {
849 frame_x = t->frame_canvas->root_x;
850 frame_y = t->frame_canvas->root_y;
851 if (!ASWIN_GET_FLAGS (t, AS_Sticky)) {
852 frame_x += t->status->viewport_x;
853 frame_y += t->status->viewport_y;
854 }
855 frame_width = t->frame_canvas->width + t->frame_canvas->bw * 2;
856 frame_height = t->frame_canvas->height + t->frame_canvas->bw * 2;
857 }
858
859 if (t->icon_canvas)
860 icon_pixmap_w = t->icon_canvas->w;
861 if (t->icon_title_canvas && t->icon_title_canvas != t->icon_canvas)
862 icon_title_w = t->icon_title_canvas->w;
863
864 if (ASWIN_GET_FLAGS (t, AS_Iconic)) {
865 ASCanvas *ic = t->icon_canvas ? t->icon_canvas : t->icon_title_canvas;
866 if (ic != NULL) {
867 icon_x = ic->root_x;
868 icon_y = ic->root_y;
869 icon_width = ic->width + ic->bw * 2;
870 icon_height = ic->height + ic->bw * 2;
871 }
872 }
873
874 if (ASWIN_HFLAGS (t, AS_PID))
875 pid = t->hints->pid;
876
877 asw_id.asw = t;
878 SendPacket (module, event_type, 27,
879 t->w, t->frame, asw_id.id,
880 frame_x, frame_y, frame_width, frame_height,
881 ASWIN_DESK (t), t->status->flags, t->hints->flags,
882 t->hints->client_icon_flags, t->hints->base_width,
883 t->hints->base_height, t->hints->width_inc,
884 t->hints->height_inc, t->hints->min_width,
885 t->hints->min_height, t->hints->max_width,
886 t->hints->max_height, t->hints->gravity, icon_title_w,
887 icon_pixmap_w, icon_x, icon_y, icon_width, icon_height, pid);
888 }
889
890 void
SendString(int channel,send_data_type msg_type,Window w,Window frame,ASWindow * asw_ptr,char * string,send_data_type encoding)891 SendString (int channel, send_data_type msg_type,
892 Window w, Window frame, ASWindow * asw_ptr,
893 char *string, send_data_type encoding)
894 {
895 send_data_type data[3];
896 send_signed_data_type len = 0;
897 union {
898 ASWindow *asw;
899 send_data_type id;
900 } asw_id;
901
902 if (string == NULL)
903 return;
904 len = strlen (string);
905
906 flush_vector (&module_output_buffer);
907 append_vector (&module_output_buffer,
908 make_msg_header (msg_type,
909 3 + 1 + (len >> 2) + 1 +
910 MSG_HEADER_SIZE), MSG_HEADER_SIZE);
911 data[0] = w;
912 data[1] = frame;
913 asw_id.asw = asw_ptr;
914 data[2] = asw_id.id;
915 append_vector (&module_output_buffer, &(data[0]), 3);
916 append_vector (&module_output_buffer, &encoding, 1);
917 serialize_string (string, &module_output_buffer);
918 SendBuffer (channel);
919 }
920
SendVector(int channel,send_data_type msg_type,ASVector * vector)921 void SendVector (int channel, send_data_type msg_type, ASVector * vector)
922 {
923 if (vector == NULL)
924 return;
925
926 flush_vector (&module_output_buffer);
927 append_vector (&module_output_buffer,
928 make_msg_header (msg_type, VECTOR_USED (*vector)),
929 MSG_HEADER_SIZE);
930 append_vector (&module_output_buffer,
931 VECTOR_HEAD (send_data_type, *vector),
932 VECTOR_USED (*vector));
933
934 SendBuffer (channel);
935 }
936
937 void
SendStackingOrder(int channel,send_data_type msg_type,send_data_type desk,ASVector * ids)938 SendStackingOrder (int channel, send_data_type msg_type,
939 send_data_type desk, ASVector * ids)
940 {
941 send_data_type data[2];
942
943 if (ids == NULL)
944 return;
945
946 flush_vector (&module_output_buffer);
947 append_vector (&module_output_buffer,
948 make_msg_header (msg_type, VECTOR_USED (*ids) + 2),
949 MSG_HEADER_SIZE);
950 data[0] = desk;
951 data[1] = VECTOR_USED (*ids);
952 append_vector (&module_output_buffer, &(data[0]), 2);
953 append_vector (&module_output_buffer, VECTOR_HEAD (send_data_type, *ids),
954 VECTOR_USED (*ids));
955
956 SendBuffer (channel);
957 }
958
959 static void
check_module_name_collision(unsigned int channel,const char * name,Bool kill_new)960 check_module_name_collision (unsigned int channel, const char *name,
961 Bool kill_new)
962 {
963 register int i = MODULES_NUM;
964 register module_t *list = MODULES_LIST;
965 LOCAL_DEBUG_OUT
966 ("pid(%d),total modules %d. name = \"%s\", kill_new = %d", getpid (),
967 MODULES_NUM, name, kill_new);
968 while (--i >= 0)
969 if (i != channel) {
970 LOCAL_DEBUG_OUT ("checking module %d, name = \"%s\"", i,
971 list[i].name ? list[i].name : "(null)");
972 if (mystrcasecmp (list[i].name, name) == 0) {
973 KillModule (&(list[kill_new ? channel : i]), False);
974 break;
975 }
976 }
977 }
978
979
980 /* this will run command received from module */
RunCommand(FunctionData * fdata,unsigned int channel,Window w)981 void RunCommand (FunctionData * fdata, unsigned int channel, Window w)
982 {
983 ASWindow *tmp_win;
984 module_t *module;
985
986 LOCAL_DEBUG_CALLER_OUT
987 ("fdata(%p)->func(%ld,MOD_FS=%d)->channel(%d)->w(%lX)->Modules(%p)",
988 fdata, fdata->func, F_MODULE_FUNC_START, channel, w, Modules);
989 /*fprintf( stderr,"Function parsed: [%s] [%s] [%d] [%d] [%c]\n",fdata.name,fdata.text,fdata.func_val[0], fdata.func_val[1] );
990 */
991 if (Modules == NULL || fdata == NULL || channel >= MODULES_NUM)
992 return;
993 if (!IsValidFunc (fdata->func))
994 return;
995 module = &(MODULES_LIST[channel]);
996 switch (fdata->func) {
997 case F_SET_MASK:
998 module->mask = fdata->func_val[0];
999 module->lock_on_send_mask = fdata->func_val[1];
1000 break;
1001 case F_SET_NAME:
1002 set_string (&(module->name), fdata->name);
1003 fdata->name = NULL;
1004 if (fdata->text) {
1005 set_string (&(module->cmd_line), fdata->text);
1006 fdata->text = NULL;
1007 }
1008 if (Environment->module_name_collision != ASE_AllowModuleNameCollision)
1009 check_module_name_collision (channel, module->name,
1010 (Environment->module_name_collision ==
1011 ASE_KillNewModuleOnNameCollision));
1012 break;
1013 case F_UNLOCK:
1014 break;
1015 case F_SET_FLAGS:
1016 {
1017 int xorflag;
1018 Bool update = False;
1019
1020 if ((tmp_win = window2ASWindow (w)) == NULL)
1021 break;
1022 xorflag = tmp_win->hints->flags ^ fdata->func_val[0];
1023 /*if (xorflag & STICKY)
1024 Stick (tmp_win); */
1025 if (xorflag & AS_SkipWinList) {
1026 tmp_win->hints->flags ^= AS_SkipWinList;
1027 update = True;
1028 }
1029 if (xorflag & AS_AvoidCover) {
1030 tmp_win->hints->flags ^= AS_AvoidCover;
1031 update = True;
1032 }
1033 if (xorflag & AS_Transient) {
1034 tmp_win->hints->flags ^= AS_Transient;
1035 update = True;
1036 }
1037 if (xorflag & AS_DontCirculate) {
1038 tmp_win->hints->flags ^= AS_DontCirculate;
1039 update = True;
1040 }
1041 if (update)
1042 broadcast_config (M_CONFIGURE_WINDOW, tmp_win);
1043 break;
1044 }
1045 default:
1046 {
1047 ASEvent event;
1048 Bool defered = False;
1049 memset (&event, 0x00, sizeof (ASEvent));
1050 event.w = w;
1051 if ((event.client = window2ASWindow (w)) == NULL) {
1052 event.w = None;
1053 event.x.xbutton.x_root = 0;
1054 event.x.xbutton.y_root = 0;
1055 } else {
1056 event.x.xbutton.x_root = event.client->frame_canvas->root_x + 1;
1057 event.x.xbutton.y_root = event.client->frame_canvas->root_y + 1;
1058 }
1059
1060 event.x.xany.type = ButtonRelease;
1061 event.x.xbutton.button = 1;
1062 event.x.xbutton.x = 0;
1063 event.x.xbutton.y = 0;
1064 event.x.xbutton.subwindow = None;
1065 event.context = C_FRAME;
1066 event.scr = ASDefaultScr;
1067 event.event_time = Scr.last_Timestamp;
1068 /* there must be no deffering on module commands ! */
1069
1070 defered = (w != None || !IsWindowFunc (fdata->func));
1071 ExecuteFunctionExt (fdata, &event, channel, defered);
1072 }
1073 }
1074 free_func_data (fdata);
1075 }
1076
1077 /*******************************************************************************/
1078 /* usefull functions to simplify life in other places : */
1079 /*******************************************************************************/
broadcast_focus_change(ASWindow * asw,Bool focused)1080 void broadcast_focus_change (ASWindow * asw, Bool focused)
1081 {
1082 union {
1083 ASWindow *asw;
1084 send_data_type id;
1085 } asw_id;
1086
1087 asw_id.asw = asw;
1088 if (asw)
1089 SendPacket (-1, M_FOCUS_CHANGE, 4, asw->w, asw->frame, asw_id.id,
1090 (send_data_type) focused);
1091 }
1092
broadcast_window_name(ASWindow * asw)1093 void broadcast_window_name (ASWindow * asw)
1094 {
1095 if (asw) {
1096 SendString (-1, M_WINDOW_NAME, asw->w, asw->frame,
1097 asw, ASWIN_NAME (asw), get_hint_name_encoding (asw->hints,
1098 0));
1099 SendString (-1, M_WINDOW_NAME_MATCHED, asw->w, asw->frame, asw,
1100 asw->hints->matched_name0,
1101 asw->hints->matched_name0_encoding);
1102 }
1103 }
1104
broadcast_icon_name(ASWindow * asw)1105 void broadcast_icon_name (ASWindow * asw)
1106 {
1107 if (asw) {
1108 SendString (-1, M_ICON_NAME, asw->w, asw->frame,
1109 asw, ASWIN_ICON_NAME (asw),
1110 get_hint_name_encoding (asw->hints,
1111 asw->hints->icon_name_idx));
1112 }
1113 }
1114
broadcast_res_names(ASWindow * asw)1115 void broadcast_res_names (ASWindow * asw)
1116 {
1117 if (asw) {
1118 SendString (-1, M_RES_CLASS, asw->w, asw->frame,
1119 asw, asw->hints->res_class,
1120 get_hint_name_encoding (asw->hints,
1121 asw->hints->res_class_idx));
1122 SendString (-1, M_RES_NAME, asw->w, asw->frame, asw,
1123 asw->hints->res_name, get_hint_name_encoding (asw->hints,
1124 asw->hints->
1125 res_name_idx));
1126 }
1127 }
1128
broadcast_status_change(int message,ASWindow * asw)1129 void broadcast_status_change (int message, ASWindow * asw)
1130 {
1131 union {
1132 ASWindow *asw;
1133 send_data_type id;
1134 } asw_id;
1135
1136 asw_id.asw = asw;
1137
1138 if (message == M_MAP)
1139 SendPacket (-1, M_MAP, 3, asw->w, asw->frame, asw_id.id);
1140 }
1141
broadcast_config(send_data_type event_type,ASWindow * t)1142 void broadcast_config (send_data_type event_type, ASWindow * t)
1143 {
1144 SendConfig (-1, event_type, t);
1145 }
1146
1147 /********************************************************************************/
1148 /* module list menus regeneration : */
1149 /********************************************************************************/
1150
1151 static inline void
module_t2func_data(FunctionCode func,module_t * module,FunctionData * fdata,char * scut)1152 module_t2func_data (FunctionCode func, module_t * module,
1153 FunctionData * fdata, char *scut)
1154 {
1155 fdata->func = func;
1156 fdata->name = mystrdup (module->name);
1157 fdata->text = mystrdup (module->name);
1158 if (++(*scut) == ('9' + 1))
1159 (*scut) = 'A'; /* Next shortcut key */
1160 fdata->hotkey = (*scut);
1161 }
1162
make_module_menu(FunctionCode func,const char * title,int sort_order)1163 MenuData *make_module_menu (FunctionCode func, const char *title,
1164 int sort_order)
1165 {
1166 MenuData *md;
1167 MenuDataItem *mdi;
1168 FunctionData fdata;
1169 char scut = '0'; /* Current short cut key */
1170 module_t *modules;
1171 int i, max_i;
1172 MinipixmapData minipixmaps[MINIPIXMAP_TypesNum] = { {0}, {0} };
1173
1174 if (Modules == NULL)
1175 return NULL;
1176
1177 if ((md = create_menu_data ("@#%module_menu%#@")) == NULL)
1178 return NULL;
1179
1180 modules = MODULES_LIST;
1181 max_i = MODULES_NUM;
1182
1183 memset (&fdata, 0x00, sizeof (FunctionData));
1184 fdata.func = F_TITLE;
1185 fdata.name = mystrdup (title);
1186 add_menu_fdata_item (md, &fdata, NULL);
1187
1188 if (sort_order == ASO_Alpha) {
1189 FunctionData **menuitems = safecalloc (sizeof (FunctionData *), max_i);
1190
1191 for (i = 0; i < max_i; ++i) {
1192 menuitems[i] = safecalloc (1, sizeof (FunctionData));
1193 module_t2func_data (func, &(modules[i]), menuitems[i], &scut);
1194 }
1195 qsort (menuitems, i, sizeof (FunctionData *), compare_func_data_name);
1196 for (i = 0; i < max_i; ++i) {
1197 ASDesktopEntry *de;
1198 de = fetch_desktop_entry (AfterStepCategories, menuitems[i]->name);
1199
1200 if (de)
1201 minipixmaps[MINIPIXMAP_Icon].filename = de->Icon;
1202
1203 if ((mdi =
1204 add_menu_fdata_item (md, menuitems[i],
1205 &(minipixmaps[0]))) != NULL) {
1206 set_flags (mdi->flags, MD_ScaleMinipixmapDown);
1207 if (de) {
1208 char *comment = NULL;
1209 if (dup_desktop_entry_Comment (de, &comment))
1210 set_flags (mdi->flags, MD_CommentIsUTF8);
1211 if (comment) {
1212 mdi->comment = interpret_ascii_string (comment);
1213 free (comment);
1214 }
1215 }
1216 }
1217 safefree (menuitems[i]); /* scrubba-dub-dub */
1218 }
1219 safefree (menuitems);
1220 } else { /* if( sort_order == ASO_Circulation || sort_order == ASO_Stacking ) */
1221
1222 for (i = 0; i < max_i; ++i) {
1223 ASDesktopEntry *de;
1224 module_t2func_data (func, &(modules[i]), &fdata, &scut);
1225 de = fetch_desktop_entry (AfterStepCategories, fdata.name);
1226 if (de)
1227 minipixmaps[MINIPIXMAP_Icon].filename = de->Icon;
1228 if ((mdi = add_menu_fdata_item (md, &fdata, &(minipixmaps[0]))) != NULL)
1229 set_flags (mdi->flags, MD_ScaleMinipixmapDown);
1230 }
1231 }
1232 return md;
1233 }
1234