1
/* Bluefish HTML Editor
2
* msg_queue.c - message queue handling
4
* Copyright (C) 2003 Olivier Sessink
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
#include <sys/types.h>
29
#include <sys/ipc.h> /* msgsnd() */
30
#include <sys/msg.h> /* msgsnd() */
32
#include <errno.h> /* errno */
33
#include <unistd.h> /* getpid, getuid */
34
#include <string.h> /* strncpy */
35
#include <stdlib.h> /* exit() */
37
#include "stringlist.h"
38
#include "gtk_easy.h" /* *_dialog */
39
#include "gui.h" /* notebook_changed() */
43
#define BLUEFISH_MSG_QUEUE 9723475
44
#define MSQ_QUEUE_SIZE 1024
45
#define MSQ_QUEUE_SMALL_SIZE 7
46
#define MSQ_QUEUE_CHECK_TIME 300 /* miliseconds for gtk_timeout*/
48
/* send alive must have the highest number, because it is the only type that
49
* should not be read by the master process. The sending processes reads this
50
* to check if the queue is alive
52
#define MSG_QUEUE_SEND_ALIVE 46064
53
#define MSG_QUEUE_OPENFILE 46063
54
#define MSG_QUEUE_OPENPROJECT 46062
55
#define MSG_QUEUE_OPENNEWWIN 46061
56
/* from man msgrcv: 'the first message on the queue with the lowest type less
57
* than or equal to the absolute value of msgtyp will be read'
58
* that means the requestalive should have the lowest number, because
59
* it has the highest priority
61
#define MSG_QUEUE_ASK_ALIVE 46010
63
#define MSG_QUEUE_PER_DOCUMENT_TIMEOUT 20000000 /* nanoseconds */
66
the message queue system is quite easy:
68
if there is no queue --> open it and start listening
69
on a MSG_QUEUE_OPENFILE, open the file, on a MSG_QUEUE_ASK_ALIVE return
70
the same data with type MSG_QUEUE_SEND_ALIVE (which we are not
73
if there is a queue and there are files loaded on the commandline -->
74
check the server (send a keepalive containing the pid) and send the
75
files to the queue, after sending the files check if a
76
MSG_QUEUE_SEND_ALIVE is received contaning the pid. If so, there is
77
a server and we can quit. If not, we continue starting and load them
79
if there is a queue and we do not have files loaded on the commandline
80
we just start, but we don't listen to the queue!
87
GList *file_error_list;
90
/******************************/
91
/* global var for this module */
92
/******************************/
93
Tmsg_queue msg_queue = { TRUE, FALSE, -1, NULL};
96
* msg_queue_check_alive:
98
* checks the message queue for messages of type MSG_QUEUE_SEND_ALIVE
99
* if we receive such a message, there must be another process
100
* active on this message queue
102
static gboolean msg_queue_check_alive(gboolean wait_first)
104
struct small_msgbuf {
106
char mtext[MSQ_QUEUE_SMALL_SIZE];
108
gchar *pid_string = g_strdup_printf("%d", (int) getpid());
111
static struct timespec const req = { 0, MSQ_QUEUE_CHECK_TIME * 1000000};
112
static struct timespec rem;
113
nanosleep(&req, &rem);
117
(msg_queue.msgid, &small_msgp, MSQ_QUEUE_SMALL_SIZE * sizeof(char), MSG_QUEUE_SEND_ALIVE,
119
DEBUG_MSG("msg_queue_check_alive, received a keepalive, mtext=%s!\n", small_msgp.mtext);
120
if (strncmp(pid_string, small_msgp.mtext, MSQ_QUEUE_SMALL_SIZE - 1) == 0) {
121
DEBUG_MSG("msg_queue_check_alive, keepalive matches request!\n");
122
/* we did receive a keepalive on our request !! */
126
DEBUG_MSG("msg_queue_check_alive, keepalive does NOT match request %s\n", pid_string);
129
DEBUG_MSG("msg_queue_check_alive, errno=%d, error=%s\n", errno, g_strerror(errno));
137
* returns 1 if another process has the queue open already
138
* returns 0 if we opened the queue
140
static gboolean msg_queue_open(void)
142
DEBUG_MSG("msg_queue_open, started\n");
143
msg_queue.msgid = msgget((key_t) BLUEFISH_MSG_QUEUE + getuid(), 0666 | IPC_CREAT | IPC_EXCL);
145
if (msg_queue.msgid == -1) {
146
DEBUG_MSG("msg_queue_open, errno=%d, error=%s\n", errno, g_strerror(errno));
149
/* if msg_queue.msgid == -1 the message queue was already opened by another process */
150
DEBUG_MSG("msg_queue_open, msg_queue.msgid=%d\n", msg_queue.msgid);
151
if (msg_queue.msgid == -1) {
152
msg_queue.msgid = msgget((key_t) BLUEFISH_MSG_QUEUE + getuid(), 0666);
153
DEBUG_MSG("msg_queue_open, connected to existing message queue, id=%d\n", msg_queue.msgid);
155
/* now we want to avoid the situation where the message queue is full (because the server died)
156
so we cannot send a keepalive, so we check if the queue is filled (assume when there are >5 messages)
157
and the last completed msgrcv() call was > 5 seconds ago */
159
struct msqid_ds msg_stat;
161
/* check if there are messages on the queue, if so, check when the last msgrcv() call was on this queue */
162
msgctl(msg_queue.msgid, IPC_STAT, &msg_stat);
163
if (msg_stat.msg_qnum > 5) {
164
timediff = time(NULL) - msg_stat.msg_ctime;
166
DEBUG_MSG("msg_queue_request_alive, more then 2 seconds no reads on message_queue, timediff=%d, deleting queue\n", timediff);
167
msgctl(msg_queue.msgid, IPC_RMID, NULL);
168
msg_queue.msgid = msgget((key_t) BLUEFISH_MSG_QUEUE + getuid(), 0666 | IPC_CREAT | IPC_EXCL);
173
if (msg_queue.msgid == -1) {
174
DEBUG_MSG("msg_queue_open, errno=%d, error=%s\n", errno, g_strerror(errno));
175
msg_queue.functional = FALSE;
184
* checks the queue for any messages
185
* this is called by the master program, usually by gtk_timeout_add()
186
* the messages it will listen to are the types:
187
* - MSG_QUEUE_ASK_ALIVE - we should respond with a type MSG_QUEUE_SEND_ALIVE
188
* - MSG_QUEUE_OPENFILE - open a filename
189
* - MSG_QUEUE_OPENPROJECT - open a filename as project
190
* - MSG_QUEUE_OPENNEWWIN - open a new window
192
static gboolean msg_queue_check(gint started_by_gtk_timeout)
196
char mtext[MSQ_QUEUE_SIZE];
199
if (main_v->bfwinlist == NULL || BFWIN(main_v->bfwinlist->data)->documentlist == NULL) {
200
DEBUG_MSG("msg_queue_check, no documentlist yet, so we do not continue\n");
204
if (msg_queue.msgid == -1) {
207
retval = msgrcv(msg_queue.msgid, &msgp, MSQ_QUEUE_SIZE, -MSG_QUEUE_OPENFILE, IPC_NOWAIT);
209
DEBUG_MSG("msg_queue_check, found type %ld\n", msgp.mtype);
210
if (msgp.mtype == MSG_QUEUE_ASK_ALIVE) {
211
struct small_msgbuf {
213
char mtext[MSQ_QUEUE_SMALL_SIZE];
215
DEBUG_MSG("msg_queue_check, a keepalive is asked from %s, sending!\n", msgp.mtext);
216
small_msgp.mtype = MSG_QUEUE_SEND_ALIVE;
217
strncpy(small_msgp.mtext, msgp.mtext, MSQ_QUEUE_SMALL_SIZE - 1);
218
msgsnd(msg_queue.msgid, (void *) &small_msgp, MSQ_QUEUE_SMALL_SIZE * sizeof(char),
220
} else if (msgp.mtype == MSG_QUEUE_OPENFILE) {
221
GList *lastlist = g_list_last(main_v->bfwinlist);
222
DEBUG_MSG("msg_queue_check, a filename %s is received\n", msgp.mtext);
223
if (!doc_new_with_file(BFWIN(lastlist->data),msgp.mtext, TRUE, FALSE)) {
224
msg_queue.file_error_list = g_list_append(msg_queue.file_error_list, g_strdup(msgp.mtext));
226
msg_queue_check(0); /* call myself again, there may have been multiple files */
227
if (started_by_gtk_timeout) {
228
if (msg_queue.file_error_list) {
229
gchar *message, *tmp;
230
tmp = stringlist_to_string(msg_queue.file_error_list, "\n");
231
free_stringlist(msg_queue.file_error_list);
232
msg_queue.file_error_list = NULL;
233
message = g_strconcat(_("These files were not opened:\n"), tmp, NULL);
235
warning_dialog(BFWIN(main_v->bfwinlist->data)->main_window,_("Unable to open file(s)\n"), message);
238
/* gtk_notebook_set_page(GTK_NOTEBOOK(main_v->notebook),g_list_length(main_v->documentlist) - 1);
239
notebook_changed(-1);*/
241
} else if (msgp.mtype == MSG_QUEUE_OPENPROJECT) {
242
GList *lastlist = g_list_last(main_v->bfwinlist);
243
DEBUG_MSG("msg_queue_check, a project %s is received\n", msgp.mtext);
244
project_open_from_file(BFWIN(lastlist->data), msgp.mtext);
245
msg_queue_check(0); /* call myself again, there may have been multiple projects */
246
} else if (msgp.mtype == MSG_QUEUE_OPENNEWWIN) {
247
/* now check if this is indeed send by another process
248
if the message queue was dead during the startup of this process,
249
it might be started by this very process */
250
int otherpid = atoi(msgp.mtext);
251
DEBUG_MSG("msg_queue_check, a new window is requested by PID=%d\n",otherpid);
252
if (otherpid != (int) getpid()) {
253
DEBUG_MSG("msg_queue_check, the PID is not ours, opening new window\n");
254
gui_new_window(NULL, NULL);
259
DEBUG_MSG("msg_queue_check, unknown message queue type %ld\n", msgp.mtype);
264
#ifdef MSG_QUEUE_DEBUG
265
DEBUG_MSG("msg_queue_check, found errno(%d)=%s\n", errno, g_strerror(errno));
268
43 = Identifier removed
270
if (errno == 22 || errno == 43) {
271
DEBUG_MSG("msg_queue_check, re-opening message queue ?!?!?\n");
272
/* the msg_queue was removed !?!?! */
273
if (msg_queue_open()) {
274
DEBUG_MSG("msg_queue_check, another process has opened the message_queue, stopping server\n");
275
msg_queue.server = FALSE;
284
* msg_queue_send_names:
287
* returns FALSE if we never received a keepalive, so the server process seems to be non-responsive
289
static gboolean msg_queue_send_names(gint send_with_id, GList * names, gboolean received_keepalive)
293
char mtext[MSQ_QUEUE_SIZE];
295
gint success = 1, check_keepalive_cnt = 0, send_failure_cnt = 0;
298
/* we have a message queue now, opened by another bluefish process */
299
msgp.mtype = send_with_id;
300
tmplist = g_list_first(names);
301
while (tmplist && success) {
303
gint len = strlen((gchar *) tmplist->data);
305
/* we start with checking for keepalives */
306
if (!received_keepalive) {
307
if (msg_queue_check_alive(TRUE)) {
308
received_keepalive = TRUE;
309
DEBUG_MSG("msg_queue_send_files, received keepalive\n");
311
check_keepalive_cnt++;
312
DEBUG_MSG("msg_queue_send_files, no keepalive (try %d)\n", check_keepalive_cnt);
316
if (len < MSQ_QUEUE_SIZE - 1) {
317
strncpy(msgp.mtext, (gchar *) tmplist->data, MSQ_QUEUE_SIZE - 1);
318
retval = msgsnd(msg_queue.msgid, (void *) &msgp, MSQ_QUEUE_SIZE * sizeof(char), IPC_NOWAIT);
320
DEBUG_MSG("msg_queue_send_files, failed sending, errno=%d\n", errno);
321
if (errno == EAGAIN) { /* EAGAIN = 11 */
322
static struct timespec const req = { 0, MSG_QUEUE_PER_DOCUMENT_TIMEOUT};
323
static struct timespec rem;
324
nanosleep(&req, &rem);
327
DEBUG_MSG("msg_queue_send_files, failing to send, errno=%d, aborting\n", errno);
331
if (!received_keepalive) {
332
/* if we fill the message queue with loads of data, the server
333
process doesn't even get a chance of reply-ing. So as long as we
334
don't know a thing about it, we give it some time and check for
336
if (msg_queue_check_alive(TRUE)) {
337
received_keepalive = TRUE;
338
DEBUG_MSG("msg_queue_send_files, received keepalive\n");
340
check_keepalive_cnt++;
341
DEBUG_MSG("msg_queue_send_files, no keepalive (try %d)\n", check_keepalive_cnt);
344
DEBUG_MSG("msg_queue_send_files, sending %s succeeded\n", (gchar *) tmplist->data);
345
send_failure_cnt = 0;
346
tmplist = g_list_next(tmplist);
349
DEBUG_MSG("msg_queue_send_files, failed sending, length increased message size\n");
352
if ((check_keepalive_cnt > 5) || (send_failure_cnt > 60)) {
354
("msg_queue_send_files, to many tries, check_keepalive_cnt=%d, send_failure_cnt=%d\n",
355
check_keepalive_cnt, send_failure_cnt);
361
("msg_queue_send_files, sending filenames complete and successfull, received_keepalive=%d\n",
363
/* / * all filenames send to other process, test if it is alive * /
364
if (received_keepalive) {
367
/ * the other process should have enough time to check the queue * /
368
/ * the macro is in milliseconds, usleep is microseconds * /
369
if (msg_queue_check_alive(TRUE)) {
370
/ * we did receive a keep alive message! * /
374
return received_keepalive;
379
static gboolean msg_queue_send_files(GList * filenames, gboolean received_keepalive) {
380
return msg_queue_send_names(MSG_QUEUE_OPENFILE, filenames, received_keepalive);
383
static gboolean msg_queue_send_projects(GList * filenames, gboolean received_keepalive) {
384
return msg_queue_send_names(MSG_QUEUE_OPENPROJECT, filenames, received_keepalive);
388
* msg_queue_request_alive:
390
* sends a message of type MSG_QUEUE_ASK_ALIVE to the already existing queue
391
* to check if the queue is alive
393
static void msg_queue_request_alive(void)
396
struct small_msgbuf {
398
char mtext[MSQ_QUEUE_SMALL_SIZE];
400
gchar *pid_string = g_strdup_printf("%d", (int) getpid());
402
DEBUG_MSG("msg_queue_request_alive, asking for keepalive, string %s\n", pid_string);
403
small_msgp.mtype = MSG_QUEUE_ASK_ALIVE;
404
strncpy(small_msgp.mtext, pid_string, MSQ_QUEUE_SMALL_SIZE - 1);
406
msgsnd(msg_queue.msgid, (void *) &small_msgp, MSQ_QUEUE_SMALL_SIZE * sizeof(char),
409
if (ask_alive == -1) {
411
/* the resource is temporary unavailable - perhaps the queue is full, this could mean a very busy
412
message queue or a dead server */
413
struct msqid_ds msg_stat;
416
/* check the last time a process listened to the queue */
417
msgctl(msg_queue.msgid, IPC_STAT, &msg_stat);
418
timediff = time(NULL) - msg_stat.msg_rtime;
420
DEBUG_MSG("msg_queue_request_alive, more then 2 seconds no reads on message_queue, timediff=%d, deleting queue\n", timediff);
424
DEBUG_MSG("msg_queue_request_alive, errno=%d, error=%s\n", errno, g_strerror(errno));
425
msg_queue.functional = FALSE;
429
static void msg_queue_send_new_window(void) {
433
char mtext[MSQ_QUEUE_SMALL_SIZE];
435
/* perhaps we should first check if the queue is alive */
436
gchar *pid_string = g_strdup_printf("%d", (int) getpid());
437
DEBUG_MSG("msg_queue_send_new_window, requesting new window using our PID %s!\n",pid_string);
438
small_msgp.mtype = MSG_QUEUE_OPENNEWWIN;
439
strncpy(small_msgp.mtext, pid_string, MSQ_QUEUE_SMALL_SIZE - 1);
440
retval = msgsnd(msg_queue.msgid,(void *) &small_msgp, MSQ_QUEUE_SMALL_SIZE * sizeof(char),IPC_NOWAIT);
442
/* hmm an error, we have to do some error handling here */
447
static struct timespec const req = { 0, 200000000};
448
static struct timespec rem;
449
nanosleep(&req, &rem);
451
void msg_queue_start(GList * filenames, GList *projectfiles, gboolean open_new_window) {
452
gboolean received_keepalive = FALSE;
453
gboolean queue_already_open;
455
DEBUG_MSG("msg_queue_start, open message queue\n");
456
queue_already_open = msg_queue_open();
457
if (queue_already_open && msg_queue.functional) {
458
msg_queue_request_alive();
459
if (open_new_window) {
460
msg_queue_send_new_window();
462
/* if we have filenames to open, we start sending them now, else we just check if we have to be master or not */
463
if (filenames || projectfiles) {
465
received_keepalive = msg_queue_send_files(filenames, received_keepalive);
468
received_keepalive = msg_queue_send_projects(projectfiles, received_keepalive);
470
DEBUG_MSG("msg_queue_start, after sending files and projects, keepalive=%d\n",received_keepalive);
473
if (!received_keepalive) {
474
gint check_keepalive_cnt = 0;
475
/* if the message queue is still open and the process listening is killed
476
we should be the server process --> we have to check if the process is still running */
477
while (!received_keepalive && check_keepalive_cnt < 10) {
478
DEBUG_MSG("msg_queue_start, no keepalive yet, check_keepalive_cnt=%d\n", check_keepalive_cnt);
479
if (msg_queue_check_alive(TRUE)) {
480
received_keepalive = TRUE;
482
check_keepalive_cnt++;
484
if ((filenames || projectfiles || open_new_window) && received_keepalive) {
485
DEBUG_MSG("msg_queue_start, we did send all our messages to an active queue, exiting!\n");
489
DEBUG_MSG("msg_queue_start, we did send all our messages to an active queue, exiting!\n");
494
/* if (queue_already_open) */
495
/* if we opened the queue, or we did not get a keepalive */
496
if (msg_queue.functional
497
&& (!queue_already_open || (queue_already_open && !received_keepalive))) {
498
msg_queue.server = TRUE;
500
("msg_queue_start, we opened the queue, or we didn't get a keepalive, we will be server!\n");
501
gtk_timeout_add(MSQ_QUEUE_CHECK_TIME, (GtkFunction)msg_queue_check, GINT_TO_POINTER(1));
503
DEBUG_MSG("msg_queue_start, we didn't open the queue, and we received a keepalive, further ignoring the mssage queue\n");
507
void msg_queue_cleanup(void)
509
if (msg_queue.functional && msg_queue.server) {
510
DEBUG_MSG("msg_queue_cleanup, removing msg_queue()\n");
511
msgctl(msg_queue.msgid, IPC_RMID, NULL);
515
#endif /* WITH_MSG_QUEUE */