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.
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 *);
53 static RETSIGTYPE
sig_handler(int);
54 static char *bugger_off
= "killme\n";
56 /* a queue of messages for FvwmCommand to receive */
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
[])
76 fprintf(stderr
, "%s version %s should only be executed by fvwm!\n",
81 if (argc
== FARGS
+ 1)
83 fifoname
= argv
[FARGS
];
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
);
102 sigact
.sa_flags
= SA_RESTART
;
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
);
114 #ifdef USE_BSD_SIGNALS
115 fvwmSetSignalMask(sigmask(SIGINT
) | sigmask(SIGHUP
) | sigmask(SIGQUIT
)
116 | sigmask(SIGTERM
) | sigmask(SIGPIPE
));
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);
132 Fd
[0] = atoi(argv
[1]);
133 Fd
[1] = atoi(argv
[2]);
144 sig_handler(int signo
)
146 fvwmSetTerminate(signo
);
151 * setup server and communicate with fvwm and the client
153 void server (char *name
)
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];
168 if ((f_stem
= fifos_get_default_name()) == NULL
)
178 if (open_fifos(f_stem
) < 0)
185 /*Accept reply-messages */
186 SetMessageMask(Fd
, MX_REPLY
);
187 /* tell fvwm we're running */
188 SendFinishedStartupNotification(Fd
);
190 while (!isTerminated
)
196 FD_SET(FfdC
, &fdrset
);
197 FD_SET(Fd
[1], &fdrset
);
199 FD_SET(FfdM
, &fdwset
);
201 ret
= fvwmSelect(FD_SETSIZE
, &fdrset
, &fdwset
, 0, queueing
? &tv
: NULL
);
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 */
212 /* do nothing if there are no messages (should never happen) */
220 /* if the first message has been partially sent don't remove it */
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
) {
235 /* there is either one message left (partially complete) or none */
241 if (FD_ISSET(Fd
[1], &fdrset
))
243 FvwmPacket
* packet
= ReadFvwmPacket(Fd
[1]);
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
);
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?)
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
++)
285 if (cmd
[cix
] == '\n')
289 if (StrHasPrefix(bugger_off
, cmd
))
290 /* fvwm will close our pipes when it has processed this */
291 SendQuitNotification(Fd
);
293 SendText(Fd
, cmd
, 0);
295 else if (cix
>= MAX_MODULE_INPUT_TEXT_LEN
)
297 err_msg("command too long");
307 if (queueing
&& FD_ISSET(FfdM
, &fdwset
))
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
) {
319 if (Qstart
== NULL
) {
323 } else if (sent
>= 0)
331 * close fifos and pipes
333 void close_pipes(void)
335 static char is_closed
= 0;
337 /* prevent that this is executed twice */
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
))
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
))
359 /* construct the name of the lock file and remove it */
360 FfdM_name
[strlen(FfdM_name
)-1] = 'R';
371 int open_fifos(const char *f_stem
)
374 FfdC_name
= malloc(strlen(f_stem
) + 2);
375 if (FfdC_name
== NULL
)
377 err_msg( "allocating command" );
380 FfdM_name
= malloc(strlen(f_stem
) + 2);
381 if (FfdM_name
== NULL
)
383 err_msg("allocating message");
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 */
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 */
401 write(FfdC
, bugger_off
, strlen(bugger_off
));
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)
412 if (mkfifo(FfdC_name
, FVWM_S_IRUSR
| FVWM_S_IWUSR
) < 0)
418 if ((FfdM
= open(FfdM_name
, O_RDWR
| O_NONBLOCK
| O_NOFOLLOW
)) < 0)
420 err_msg("opening message fifo");
423 if ((FfdC
= open(FfdC_name
, O_RDWR
| O_NONBLOCK
| O_NOFOLLOW
)) < 0)
425 err_msg("opening command fifo");
429 /* get the inode numbers for use when quiting */
430 if (fstat(FfdC
, &stat_buf
) != 0)
432 err_msg("stat()ing command fifo");
437 FfdC_ino
= stat_buf
.st_ino
;
438 if (!(S_IFIFO
& stat_buf
.st_mode
))
440 err_msg("command fifo is not a fifo");
443 if (stat_buf
.st_mode
& (S_IRWXG
| S_IRWXO
))
445 err_msg("command fifo is too permissive");
449 if (fstat(FfdM
, &stat_buf
) != 0)
451 err_msg("stat()ing message fifo");
456 FfdM_ino
= stat_buf
.st_ino
;
457 if (!(S_IFIFO
& stat_buf
.st_mode
))
459 err_msg("message fifo is not a fifo");
462 if (stat_buf
.st_mode
& (S_IRWXG
| S_IRWXO
))
464 err_msg("message fifo is too permissive");
474 * Process window list messages
477 void process_message(unsigned long type
,unsigned long *body
)
484 case M_CONFIGURE_WINDOW
:
485 relay_packet(type
, sizeof(struct ConfigWinPacket
) * SOL
, body
);
488 case M_ICON_LOCATION
:
490 relay_packet(type
, 7 * SOL
, body
);
494 relay_packet(type
, 6 * SOL
, body
);
498 relay_packet(type
, 5 * SOL
, body
);
502 relay_packet(type
, 1 * SOL
, body
);
505 case M_END_WINDOWLIST
:
506 case M_END_CONFIG_INFO
:
507 relay_packet(type
, 0 * SOL
, body
);
520 msglen
= strlen((char *)&body
[3]);
521 relay_packet(type
, msglen
+ 1 + 3 * SOL
, body
);
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
)
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
,
562 new = (Q
*)safemalloc(sizeof(Q
));
564 new->length
= length
+ 2 * SOL
;
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
);