Fix move command parsing.
[fvwm.git] / modules / FvwmCommand / FvwmCommandS.c
blob2e7d456bb763e7b6095744d94c79b45a407fe4b6
1 /* -*-c-*- */
2 /*
3 * Fvwm command input interface.
5 * Copyright 1997, Toshi Isogai. No guarantees or warantees or anything
6 * are provided. Use this program at your own risk. Permission to use
7 * this program for any purpose is given,
8 * as long as the copyright is kept intact.
12 /* This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "config.h"
29 #include "FvwmCommand.h"
30 #include "libs/fvwmlib.h"
31 #include "libs/fvwmsignal.h"
32 #include "libs/Strings.h"
34 #define MYNAME "FvwmCommandS"
36 static int Fd[2]; /* pipes to fvwm */
37 static int FfdC; /* command fifo file discriptors */
38 static int FfdM; /* message fifo file discriptors */
39 static struct stat stat_buf;
41 static char *FfdC_name = NULL, *FfdM_name = NULL; /* fifo names */
43 static ino_t FfdC_ino, FfdM_ino; /* fifo inode numbers */
45 int open_fifos(const char *f_stem);
46 void close_fifos(void);
47 void close_pipes(void);
48 void err_msg(const char *msg);
49 void err_quit(const char *msg) __attribute__((noreturn));
50 void process_message(unsigned long type,unsigned long *body);
51 void relay_packet(unsigned long, unsigned long, unsigned long *);
52 void server(char *);
53 static RETSIGTYPE sig_handler(int);
54 static char *bugger_off = "killme\n";
56 /* a queue of messages for FvwmCommand to receive */
57 typedef struct Q
59 unsigned long length;
60 unsigned long sent;
61 char *body;
62 struct Q *next;
63 } Q;
64 /* head and tail of the queue */
65 static Q *Qstart = NULL;
66 static Q *Qlast = NULL;
67 /* flag to indicate new queue items available */
68 static Bool queueing = False;
70 int main(int argc, char *argv[])
72 char *fifoname;
74 if (argc < FARGS)
76 fprintf(stderr, "%s version %s should only be executed by fvwm!\n",
77 MYNAME, VERSION);
78 exit(1);
81 if (argc == FARGS + 1)
83 fifoname = argv[FARGS];
85 else
87 fifoname = NULL;
90 #ifdef HAVE_SIGACTION
92 struct sigaction sigact;
94 sigemptyset(&sigact.sa_mask);
95 sigaddset(&sigact.sa_mask, SIGINT);
96 sigaddset(&sigact.sa_mask, SIGHUP);
97 sigaddset(&sigact.sa_mask, SIGQUIT);
98 sigaddset(&sigact.sa_mask, SIGTERM);
99 sigaddset(&sigact.sa_mask, SIGPIPE);
101 #ifdef SA_RESTART
102 sigact.sa_flags = SA_RESTART;
103 #else
104 sigact.sa_flags = 0;
105 #endif
106 sigact.sa_handler = sig_handler;
107 sigaction(SIGINT, &sigact, NULL);
108 sigaction(SIGHUP, &sigact, NULL);
109 sigaction(SIGQUIT, &sigact, NULL);
110 sigaction(SIGTERM, &sigact, NULL);
111 sigaction(SIGPIPE, &sigact, NULL);
113 #else
114 #ifdef USE_BSD_SIGNALS
115 fvwmSetSignalMask(sigmask(SIGINT) | sigmask(SIGHUP) | sigmask(SIGQUIT)
116 | sigmask(SIGTERM) | sigmask(SIGPIPE));
117 #endif
118 signal(SIGPIPE, sig_handler);
119 signal(SIGINT, sig_handler);
120 signal(SIGQUIT, sig_handler);
121 signal(SIGHUP, sig_handler);
122 signal(SIGTERM, sig_handler);
123 #ifdef HAVE_SIGINTERRUPT
124 siginterrupt(SIGINT, 0);
125 siginterrupt(SIGHUP, 0);
126 siginterrupt(SIGQUIT, 0);
127 siginterrupt(SIGTERM, 0);
128 siginterrupt(SIGPIPE, 0);
129 #endif
130 #endif
132 Fd[0] = atoi(argv[1]);
133 Fd[1] = atoi(argv[2]);
135 server(fifoname);
136 close_pipes();
137 return 1;
141 * Signal handler
143 static RETSIGTYPE
144 sig_handler(int signo)
146 fvwmSetTerminate(signo);
147 SIGNAL_RETURN;
151 * setup server and communicate with fvwm and the client
153 void server (char *name)
155 char *f_stem;
156 int len;
157 fd_set fdrset, fdwset;
158 char buf[MAX_MODULE_INPUT_TEXT_LEN + 1]; /* command receiving buffer */
159 char cmd[MAX_MODULE_INPUT_TEXT_LEN + 1];
160 int ix,cix;
161 struct timeval tv;
163 tv.tv_sec = 10;
164 tv.tv_usec = 0;
166 if (name == NULL)
168 if ((f_stem = fifos_get_default_name()) == NULL)
170 exit(-1);
173 else
175 f_stem = name;
178 if (open_fifos(f_stem) < 0)
180 exit (-1);
183 cix = 0;
185 /*Accept reply-messages */
186 SetMessageMask(Fd, MX_REPLY);
187 /* tell fvwm we're running */
188 SendFinishedStartupNotification(Fd);
190 while (!isTerminated)
192 int ret;
194 FD_ZERO(&fdrset);
195 FD_ZERO(&fdwset);
196 FD_SET(FfdC, &fdrset);
197 FD_SET(Fd[1], &fdrset);
198 if (queueing)
199 FD_SET(FfdM, &fdwset);
201 ret = fvwmSelect(FD_SETSIZE, &fdrset, &fdwset, 0, queueing ? &tv : NULL);
203 if (ret < 0)
204 continue;
206 if (queueing && ret == 0) {
207 /* a timeout has occurred, this means the pipe to FvwmCommand is full
208 * dump any messages in the queue that have not been partially sent */
209 Q *q1, *q2;
211 #ifdef PARANOMIA
212 /* do nothing if there are no messages (should never happen) */
213 if (!Qstart) {
214 queueing = False;
215 continue;
217 #endif
218 q1 = Qstart->next;
220 /* if the first message has been partially sent don't remove it */
221 if (!Qstart->sent) {
222 free(Qstart->body);
223 free(Qstart);
224 Qstart = NULL;
225 } else
226 fprintf(stderr, "FvwmCommandS: leaving a partially sent message in the queue\n");
228 /* now remove the rest of the message queue */
229 while ((q2 = q1) != NULL) {
230 q1 = q1->next;
231 free(q2->body);
232 free(q2);
235 /* there is either one message left (partially complete) or none */
236 Qlast = Qstart;
237 queueing = False;
238 continue;
241 if (FD_ISSET(Fd[1], &fdrset))
243 FvwmPacket* packet = ReadFvwmPacket(Fd[1]);
245 if (packet == NULL)
247 break;
249 process_message(packet->type, packet->body);
252 if (FD_ISSET(FfdC, &fdrset))
255 * This descriptor is non-blocking, hence we should never
256 * block here! Also, the select() call should guarantee that
257 * there is something here to read, and the signal handling
258 * should be restarting interrupted reads. Together, these
259 * should severely restrict the types of errors that we can see.
261 len = read(FfdC, buf, MAX_MODULE_INPUT_TEXT_LEN);
262 if (len <= 0)
264 if ((len < 0) && (errno == EAGAIN))
267 * This probably won't happen - the select() has told us that
268 * there's something to read. The only possible thing that could
269 * cause this is if somebody else read the data first ... (who?)
271 continue;
275 * Any other error, such as an invalid descriptor (?) or someone
276 * closing the remote end of our pipe, and we give up!
278 err_quit("reading fifo");
281 /* in case of multiple long lines */
282 for (ix = 0; ix < len; ix++)
284 cmd[cix] = buf[ix];
285 if (cmd[cix] == '\n')
287 cmd[cix] = '\0';
288 cix = 0;
289 if (StrHasPrefix(bugger_off, cmd))
290 /* fvwm will close our pipes when it has processed this */
291 SendQuitNotification(Fd);
292 else
293 SendText(Fd, cmd, 0);
295 else if (cix >= MAX_MODULE_INPUT_TEXT_LEN)
297 err_msg("command too long");
298 cix = 0;
300 else
302 cix++;
304 } /* for */
305 } /* FD_ISSET */
307 if (queueing && FD_ISSET(FfdM, &fdwset))
309 int sent;
310 Q *q = Qstart;
312 /* send one packet to FvwmCommand, try to send it all but cope
313 * with partial success */
314 sent = write(FfdM, q->body + q->sent, q->length - q->sent);
315 if (sent == q->length - q->sent) {
316 Qstart = q->next;
317 free(q->body);
318 free(q);
319 if (Qstart == NULL) {
320 Qlast = NULL;
321 queueing = False;
323 } else if (sent >= 0)
324 q->sent += sent;
327 } /* while */
331 * close fifos and pipes
333 void close_pipes(void)
335 static char is_closed = 0;
337 /* prevent that this is executed twice */
338 if (!is_closed)
340 is_closed = 1;
341 close(Fd[0]);
342 close(Fd[1]);
343 close_fifos();
347 void close_fifos(void)
349 struct stat stat_buf;
351 /* only unlink this file if it is the same as FfdC */
352 /* I know there's a race here between the stat and the unlink, if you know
353 * of a way to unlink from a fildes use it here */
354 if ((stat(FfdC_name, &stat_buf) == 0) && (stat_buf.st_ino == FfdC_ino))
355 unlink(FfdC_name);
356 /* only unlink this file if it is the same as FfdM */
357 if ((stat(FfdM_name, &stat_buf) == 0) && (stat_buf.st_ino == FfdM_ino))
358 unlink(FfdM_name);
359 /* construct the name of the lock file and remove it */
360 FfdM_name[strlen(FfdM_name)-1] = 'R';
361 unlink(FfdM_name);
362 close(FfdM);
363 close(FfdC);
364 FfdC = -1;
365 FfdM = -1;
369 * open fifos
371 int open_fifos(const char *f_stem)
373 /* create 2 fifos */
374 FfdC_name = malloc(strlen(f_stem) + 2);
375 if (FfdC_name == NULL)
377 err_msg( "allocating command" );
378 return -1;
380 FfdM_name = malloc(strlen(f_stem) + 2);
381 if (FfdM_name == NULL)
383 err_msg("allocating message");
384 return -1;
386 strcpy(FfdC_name, f_stem);
387 strcpy(FfdM_name, f_stem);
388 strcat(FfdC_name, "C");
389 strcat(FfdM_name, "M");
391 /* check to see if another FvwmCommandS is running by trying to talk to it */
392 FfdC = open(FfdC_name, O_RDWR | O_NONBLOCK | O_NOFOLLOW);
393 /* remove the fifo's if they exist */
394 unlink(FfdM_name);
395 unlink(FfdC_name);
396 /* If a previous FvwmCommandS is lingering tell it to go away
397 * It will check that it owns the fifo's before removing them so no
398 * there is no need to wait before creating our own */
399 if (FfdC > 0)
401 write(FfdC, bugger_off, strlen(bugger_off));
402 close(FfdC);
405 /* now we can create the fifos knowing that they don't already exist
406 * and any lingering FvwmCommandS will not share them or remove them */
407 if (mkfifo(FfdM_name, FVWM_S_IRUSR | FVWM_S_IWUSR) < 0)
409 err_msg(FfdM_name);
410 return -1;
412 if (mkfifo(FfdC_name, FVWM_S_IRUSR | FVWM_S_IWUSR) < 0)
414 err_msg(FfdC_name);
415 return -1;
418 if ((FfdM = open(FfdM_name, O_RDWR | O_NONBLOCK | O_NOFOLLOW)) < 0)
420 err_msg("opening message fifo");
421 return -1;
423 if ((FfdC = open(FfdC_name, O_RDWR | O_NONBLOCK | O_NOFOLLOW)) < 0)
425 err_msg("opening command fifo");
426 return -1;
429 /* get the inode numbers for use when quiting */
430 if (fstat(FfdC, &stat_buf) != 0)
432 err_msg("stat()ing command fifo");
433 return -1;
435 else
437 FfdC_ino = stat_buf.st_ino;
438 if (!(S_IFIFO & stat_buf.st_mode))
440 err_msg("command fifo is not a fifo");
441 return -1;
443 if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
445 err_msg("command fifo is too permissive");
446 return -1;
449 if (fstat(FfdM, &stat_buf) != 0)
451 err_msg("stat()ing message fifo");
452 return -1;
454 else
456 FfdM_ino = stat_buf.st_ino;
457 if (!(S_IFIFO & stat_buf.st_mode))
459 err_msg("message fifo is not a fifo");
460 return -1;
462 if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
464 err_msg("message fifo is too permissive");
465 return -1;
469 return 0;
474 * Process window list messages
477 void process_message(unsigned long type,unsigned long *body)
479 int msglen;
481 switch (type)
483 case M_ADD_WINDOW:
484 case M_CONFIGURE_WINDOW:
485 relay_packet(type, sizeof(struct ConfigWinPacket) * SOL, body);
486 break;
488 case M_ICON_LOCATION:
489 case M_ICONIFY:
490 relay_packet(type, 7 * SOL, body);
491 break;
493 case M_MINI_ICON:
494 relay_packet(type, 6 * SOL, body);
495 break;
497 case M_NEW_PAGE:
498 relay_packet(type, 5 * SOL, body);
499 break;
501 case M_NEW_DESK:
502 relay_packet(type, 1 * SOL, body);
503 break;
505 case M_END_WINDOWLIST:
506 case M_END_CONFIG_INFO:
507 relay_packet(type, 0 * SOL, body);
508 break;
510 case M_WINDOW_NAME:
511 case M_ICON_NAME:
512 case M_RES_CLASS:
513 case M_RES_NAME:
514 case M_ICON_FILE:
515 case M_DEFAULTICON:
516 case M_ERROR:
517 case M_STRING:
518 case M_CONFIG_INFO:
519 case MX_REPLY:
520 msglen = strlen((char *)&body[3]);
521 relay_packet(type, msglen + 1 + 3 * SOL, body);
522 break;
524 default:
525 relay_packet(type, 4 * SOL, body);
532 * print error message on stderr and exit
535 void err_msg(const char *msg)
537 fprintf(stderr, "%s server error in %s, %s\n", MYNAME, msg, strerror(errno));
540 void err_quit(const char *msg)
542 err_msg(msg);
543 close_pipes();
544 exit(1);
548 * relay packet to front-end
549 * this is now implemented by simply adding the packet to a queue
550 * and letting the main select loop handle sending from the queue to
551 * the front end. In this way FvwmCommandS is always responsive to commands
552 * from the input pipes. (it will also die a lot faster when fvwm quits)
554 void relay_packet(unsigned long type, unsigned long length,
555 unsigned long *body)
557 Q *new;
559 if (!body)
560 return;
562 new = (Q *)safemalloc(sizeof(Q));
564 new->length = length + 2 * SOL;
565 new->sent = 0L;
566 new->body = safemalloc(new->length);
567 memcpy(new->body, &type, SOL);
568 memcpy(new->body + SOL, &length, SOL);
569 memcpy(new->body + 2 * SOL, body, length);
570 new->next = NULL;
572 if (Qlast)
573 Qlast->next = new;
574 Qlast = new;
575 if (!Qstart)
576 Qstart = Qlast;
577 queueing = True;