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