*** empty log message ***
[midnight-commander.git] / src / background.c
blob643276ed1fb768070da64b01b2ad8f84427470bc
1 /* {{{ Copyright */
3 /* Background support.
4 Copyright (C) 1996 The Free Software Foundation
6 Written by: 1996 Miguel de Icaza
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22 /* }}} */
24 #include <config.h>
25 #include <stdarg.h>
26 #include <sys/types.h>
27 #include <errno.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <sys/stat.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <stdio.h>
36 #include "global.h"
37 #include "tty.h"
38 #include "dlg.h"
39 #include "widget.h"
40 #include "wtools.h"
43 * We currenlty only support one way of comunicating the background
44 * and foreground process by using the socketpair system call
46 #ifdef WITH_BACKGROUND
47 # include <sys/socket.h>
48 #endif
49 #include "dialog.h"
50 #include "fileopctx.h"
51 #include "key.h" /* For add_select_channel(), delete_select_channel() */
52 #include "eregex.h"
53 #include "file.h"
54 #include "filegui.h"
56 /* If true, this is a background process */
57 int we_are_background = 0;
59 #ifdef WITH_BACKGROUND
60 /* If set background tasks wait to be attached */
61 int background_wait = 0;
63 #ifndef HAVE_SOCKETPAIR
64 int socketpair(int, int, int, int fd[2]);
65 #endif
67 /* File descriptor for talking to our parent */
68 static int parent_fd;
70 #define MAXCALLARGS 4 /* Number of arguments supported */
71 #define mymsg "Desde el hijo\n\r"
73 struct TaskList *task_list = NULL;
75 static int background_attention (int fd, void *closure);
77 static void
78 register_task_running (FileOpContext *ctx, pid_t pid, int fd, char *info)
80 TaskList *new;
82 new = g_new (TaskList, 1);
83 new->pid = pid;
84 new->info = info;
85 new->state = Task_Running;
86 new->next = task_list;
87 new->fd = fd;
88 task_list = new;
90 add_select_channel (fd, background_attention, ctx);
93 void
94 unregister_task_running (pid_t pid, int fd)
96 TaskList *p = task_list;
97 TaskList *prev = 0;
99 while (p){
100 if (p->pid == pid){
101 if (prev)
102 prev->next = p->next;
103 else
104 task_list = p->next;
105 g_free (p->info);
106 g_free (p);
107 break;
109 prev = p;
110 p = p->next;
112 delete_select_channel (fd);
116 * Try to make the Midnight Commander a background job
118 * Returns:
119 * 1 for parent
120 * 0 for child
121 * -1 on failure
124 do_background (FileOpContext *ctx, char *info)
126 int comm [2]; /* control connection stream */
127 pid_t pid;
129 if (socketpair (AF_UNIX, SOCK_STREAM, 0, comm) == -1)
130 return -1;
132 if ((pid = fork ()) == -1)
133 return -1;
135 if (pid == 0){
136 int nullfd;
138 parent_fd = comm [1];
139 we_are_background = 1;
141 /* Make stdin/stdout/stderr point somewhere */
142 close (0);
143 close (1);
144 close (2);
146 if ((nullfd = open ("/dev/null", O_RDONLY)) != -1){
147 while (dup2 (nullfd, 0) == -1 && errno == EINTR)
149 while (dup2 (nullfd, 1) == -1 && errno == EINTR)
151 while (dup2 (nullfd, 2) == -1 && errno == EINTR)
155 /* To make it obvious if it fails, there is a bug report on this */
156 write (2, mymsg, sizeof (mymsg));
157 write (1, mymsg, sizeof (mymsg));
159 /* Just for debugging the background back end */
160 if (background_wait){
161 volatile int i = 1;
163 while (i)
166 return 0;
167 } else {
168 close (comm [1]);
169 ctx->pid = pid;
170 register_task_running (ctx, pid, comm [0], info);
171 return 1;
175 static char *
176 background_title (char *str)
178 return g_strconcat (_("Background process:"), str, NULL);
181 /* {{{ Routines that do the real job */
182 static void
183 real_message_1s (enum OperationMode mode, int *flags, char *title, const char *str1)
185 if (mode == Background)
186 title = background_title (title);
188 message (*flags, title, "%s", str1);
190 if (mode == Background)
191 g_free (title);
194 static void
195 real_message_2s (enum OperationMode mode, int *flags, char *title,
196 const char *str1, const char *str2)
198 if (mode == Background)
199 title = background_title (title);
201 message (*flags, title, str1, str2);
203 if (mode == Background)
204 g_free (title);
207 static void
208 real_message_3s (enum OperationMode mode, int *flags, char *title,
209 const char *str1, const char *str2, const char *str3)
211 if (mode == Background)
212 title = background_title (title);
214 message (*flags, title, str1, str2, str3);
216 if (mode == Background)
217 g_free (title);
219 /* }}} */
221 /* {{{ Parent handlers */
223 /* Parent/child protocol
225 * the child (the background) process send the following:
226 * void *routine -- routine to be invoked in the parent
227 * int nargc -- number of arguments
228 * int type -- Return argument type.
230 * If the routine is zero, then it is a way to tell the parent
231 * that the process is dying.
233 * nargc arguments in the following format:
234 * int size of the coming block
235 * size bytes with the block
237 * Now, the parent loads all those and then invokes
238 * the routine with pointers to the information passed
239 * (we just support pointers).
241 * If the return type is integer:
243 * the parent then writes an int to the child with
244 * the return value from the routine and the values
245 * of any global variable that is modified in the parent
246 * currently: do_append and recursive_result.
248 * If the return type is a string:
250 * the parent writes the resulting string lenght
251 * if the result string was NULL or the empty string,
252 * then the lenght is zero.
253 * The parent then writes the string lenght and frees
254 * the result string.
257 * Receive requests from background process and invoke the
258 * specified routine
261 static int
262 background_attention (int fd, void *closure)
264 FileOpContext *ctx;
265 int have_ctx;
266 void *routine;
267 int argc, i, result, status;
268 char *data [MAXCALLARGS];
269 int bytes;
270 enum ReturnType type;
272 ctx = closure;
274 bytes = read (fd, &routine, sizeof (routine));
275 if (bytes < (sizeof (routine))){
276 char *background_process_error = _(" Background process error ");
278 if (errno == ECHILD)
279 message (1, background_process_error, _(" Child died unexpectedly "));
280 else
281 message (1, background_process_error, _(" Unknown error in child "));
282 unregister_task_running (ctx->pid, fd);
283 waitpid (ctx->pid, &status, 0);
284 return 0;
287 /* If the routine is zero, then the child is telling us that he is dying */
288 if ((long) routine == MSG_CHILD_EXITING){
289 unregister_task_running (ctx->pid, fd);
290 waitpid (ctx->pid, &status, 0);
291 return 0;
294 read (fd, &argc, sizeof (argc));
295 if (argc > MAXCALLARGS){
296 message (1, _(" Background protocol error "),
297 _(" Background process sent us a request for more arguments \n"
298 " than we can handle. \n"));
300 read (fd, &type, sizeof (type));
301 read (fd, &have_ctx, sizeof (have_ctx));
302 if (have_ctx)
303 read (fd, ctx, sizeof (FileOpContext));
305 for (i = 0; i < argc; i++){
306 int size;
308 read (fd, &size, sizeof (size));
309 data [i] = g_malloc (size+1);
310 read (fd, data [i], size);
312 data [i][size] = 0; /* NULL terminate the blocks (they could be strings) */
315 /* Handle the call */
316 if (type == Return_Integer){
317 if (have_ctx)
318 switch (argc){
319 case 1:
320 result = (*(int (*)(int, char *))routine)(Background, data [0]);
321 break;
322 case 2:
323 result = (*(int (*)(int, char *, char *))routine)
324 (Background, data [0], data [1]);
325 break;
326 case 3:
327 result = (*(int (*)(int, char *, char *, char *))routine)
328 (Background, data [0], data [1], data [2]);
329 break;
330 case 4:
331 result = (*(int (*)(int, char *, char *, char *, char *))routine)
332 (Background, data [0], data [1], data [2], data [3]);
333 break;
335 else
336 switch (argc){
337 case 1:
338 result = (*(int (*)(FileOpContext *, int, char *))routine)
339 (ctx, Background, data [0]);
340 break;
341 case 2:
342 result = (*(int (*)(FileOpContext *, int, char *, char *))routine)
343 (ctx, Background, data [0], data [1]);
344 break;
345 case 3:
346 result = (*(int (*)(FileOpContext *, int, char *, char *, char *))routine)
347 (ctx, Background, data [0], data [1], data [2]);
348 break;
349 case 4:
350 result = (*(int (*)(FileOpContext *, int, char *, char *, char *, char *))routine)
351 (ctx, Background, data [0], data [1], data [2], data [3]);
352 break;
355 /* Send the result code and the value for shared variables */
356 write (fd, &result, sizeof (int));
357 if (have_ctx)
358 write (fd, ctx, sizeof (FileOpContext));
359 } else if (type == Return_String) {
360 int len;
361 char *resstr = NULL;
363 /* FIXME: string routines should also use the Foreground/Background
364 * parameter. Currently, this is not used here
366 switch (argc){
367 case 1:
368 resstr = (*(char * (*)(char *))routine)(data [0]);
369 break;
370 case 2:
371 resstr = (*(char * (*)(char *, char *))routine)
372 (data [0], data [1]);
373 break;
374 case 3:
375 resstr = (*(char * (*)(char *, char *, char *))routine)
376 (data [0], data [1], data [2]);
377 break;
378 case 4:
379 resstr = (*(char * (*)(char *, char *, char *, char *))routine)
380 (data [0], data [1], data [2], data [3]);
381 break;
382 default: g_assert_not_reached();
384 if (resstr){
385 len = strlen (resstr);
386 write (fd, &len, sizeof (len));
387 if (len){
388 write (fd, resstr, len);
389 g_free (resstr);
391 } else {
392 len = 0;
393 write (fd, &len, sizeof (len));
396 for (i = 0; i < argc; i++)
397 g_free (data [i]);
399 do_refresh ();
400 mc_refresh ();
401 doupdate ();
402 return 0;
406 /* }}} */
408 /* {{{ client RPC routines */
410 /* Sends the header for a call to a routine in the parent process. If the file
411 * operation context is not NULL, then it requests that the first parameter of
412 * the call be a file operation context.
414 static void
415 parent_call_header (void *routine, int argc, enum ReturnType type, FileOpContext *ctx)
417 int have_ctx;
419 have_ctx = (ctx != NULL);
421 write (parent_fd, &routine, sizeof (routine));
422 write (parent_fd, &argc, sizeof (int));
423 write (parent_fd, &type, sizeof (type));
424 write (parent_fd, &have_ctx, sizeof (have_ctx));
426 if (have_ctx)
427 write (parent_fd, ctx, sizeof (FileOpContext));
431 parent_call (void *routine, FileOpContext *ctx, int argc, ...)
433 va_list ap;
434 int i;
436 va_start (ap, argc);
437 parent_call_header (routine, argc, Return_Integer, ctx);
438 for (i = 0; i < argc; i++) {
439 int len;
440 void *value;
442 len = va_arg (ap, int);
443 value = va_arg (ap, void *);
444 write (parent_fd, &len, sizeof (int));
445 write (parent_fd, value, len);
447 read (parent_fd, &i, sizeof (int));
448 if (ctx)
449 read (parent_fd, ctx, sizeof (FileOpContext));
451 return i;
454 static char *
455 parent_call_string (void *routine, int argc, ...)
457 va_list ap;
458 char *str;
459 int i;
461 va_start (ap, argc);
462 parent_call_header (routine, argc, Return_String, NULL);
463 for (i = 0; i < argc; i++){
464 int len;
465 void *value;
467 len = va_arg (ap, int);
468 value = va_arg (ap, void *);
469 write (parent_fd, &len, sizeof (int));
470 write (parent_fd, value, len);
472 read (parent_fd, &i, sizeof (int));
473 if (!i)
474 return NULL;
475 str = g_malloc (i + 1);
476 read (parent_fd, str, i);
477 str [i] = 0;
478 return str;
481 void
482 tell_parent (int msg)
484 write (parent_fd, &msg, sizeof (int));
487 void
488 message_1s (int flags, char *title, const char *str1)
490 if (we_are_background)
491 parent_call ((void *)real_message_1s, NULL, 3, sizeof (flags), &flags,
492 strlen (title), title, strlen (str1), str1);
493 else
494 real_message_1s (Foreground, &flags, title, str1);
497 void
498 message_2s (int flags, char *title, const char *str1, const char *str2)
500 if (we_are_background)
501 parent_call ((void *)real_message_2s, NULL, 4, sizeof (flags), &flags,
502 strlen (title), title, strlen (str1), str1,
503 strlen (str2), str2);
504 else
505 real_message_2s (Foreground, &flags, title, str1, str2);
508 void
509 message_3s (int flags, char *title, const char *str1,
510 const char *str2, const char *str3)
512 if (we_are_background)
513 parent_call ((void *)real_message_3s, NULL, 3, sizeof (flags), &flags,
514 strlen (title), title, strlen (str1), str1,
515 strlen (str2), str2, strlen (str3), str3);
516 else
517 real_message_3s (Foreground, &flags, title, str1, str2, str3);
520 char *
521 input_dialog_help (char *header, char *text, char *help, char *def_text)
523 if (we_are_background)
524 return parent_call_string ((void *)real_input_dialog_help, 4,
525 strlen (header), header,
526 strlen (text), text,
527 strlen (help), help,
528 strlen (def_text), def_text);
529 else
530 return real_input_dialog_help (header, text, help, def_text);
533 #else /* Else => No background code support */
535 /* {{{ Stubs if background code is not supported */
536 void
537 message_1s (int flags, char *title, const char *str1)
539 message (flags, title, "%s", str1);
542 void
543 message_2s (int flags, char *title, const char *str1, const char *str2)
545 message (flags, title, str1, str2);
548 void
549 message_3s (int flags, char *title, const char *str1,
550 const char *str2, const char *str3)
552 message (flags, title, str1, str2, str3);
555 char *
556 input_dialog_help (char *header, char *text, char *help, char *def_text)
558 return real_input_dialog_help (header, text, help, def_text);
560 /* }}} */
562 #endif /* !WITH_BACKGROUND */
564 /* {{{ Functions shared between background and foreground */
566 void
567 message_1s1d (int flags, char *title, const char *str, int d)
569 char *p = g_strdup_printf (str, d);
570 message_1s (flags, title, p);
571 g_free (p);
574 /* }}} */