root/src/background.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. register_task_running
  2. destroy_task
  3. reading_failed
  4. background_attention
  5. parent_call_header
  6. parent_va_call
  7. parent_va_call_string
  8. unregister_task_running
  9. unregister_task_with_pid
  10. do_background
  11. parent_call
  12. parent_call_string
  13. background_parent_call
  14. background_parent_call_string

   1 /* {{{ Copyright */
   2 
   3 /* Background support.
   4 
   5    Copyright (C) 1996-2019
   6    Free Software Foundation, Inc.
   7 
   8    Written by:
   9    Miguel de Icaza, 1996
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  25  */
  26 
  27 /* }}} */
  28 
  29 /** \file background.c
  30  *  \brief Source: Background support
  31  */
  32 
  33 #include <config.h>
  34 
  35 #include <stdlib.h>
  36 #include <errno.h>
  37 #include <signal.h>
  38 #include <stdarg.h>
  39 #include <stdio.h>
  40 #include <string.h>
  41 
  42 #include <sys/types.h>
  43 #include <sys/stat.h>
  44 #include <sys/wait.h>           /* waitpid() */
  45 
  46 #include "lib/global.h"
  47 
  48 #include "lib/unixcompat.h"
  49 #include "lib/tty/key.h"        /* add_select_channel(), delete_select_channel() */
  50 #include "lib/widget.h"         /* message() */
  51 #include "lib/event-types.h"
  52 
  53 #include "filemanager/fileopctx.h"      /* file_op_context_t */
  54 
  55 #include "background.h"
  56 
  57 /*** global variables ****************************************************************************/
  58 
  59 #define MAXCALLARGS 4           /* Number of arguments supported */
  60 
  61 /*** file scope macro definitions ****************************************************************/
  62 
  63 /*** file scope type declarations ****************************************************************/
  64 
  65 enum ReturnType
  66 {
  67     Return_String,
  68     Return_Integer
  69 };
  70 
  71 /*** file scope variables ************************************************************************/
  72 
  73 /* File descriptor for talking to our parent */
  74 static int parent_fd;
  75 
  76 /* File descriptor for messages from our parent */
  77 static int from_parent_fd;
  78 
  79 TaskList *task_list = NULL;
  80 
  81 static int background_attention (int fd, void *closure);
  82 
  83 /*** file scope functions ************************************************************************/
  84 /* --------------------------------------------------------------------------------------------- */
  85 
  86 static void
  87 register_task_running (file_op_context_t * ctx, pid_t pid, int fd, int to_child, char *info)
     /* [previous][next][first][last][top][bottom][index][help]  */
  88 {
  89     TaskList *new;
  90 
  91     new = g_new (TaskList, 1);
  92     new->pid = pid;
  93     new->info = info;
  94     new->state = Task_Running;
  95     new->next = task_list;
  96     new->fd = fd;
  97     new->to_child_fd = to_child;
  98     task_list = new;
  99 
 100     add_select_channel (fd, background_attention, ctx);
 101 }
 102 
 103 /* --------------------------------------------------------------------------------------------- */
 104 
 105 static int
 106 destroy_task (pid_t pid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 107 {
 108     TaskList *p = task_list;
 109     TaskList *prev = NULL;
 110 
 111     while (p != NULL)
 112     {
 113         if (p->pid == pid)
 114         {
 115             int fd = p->fd;
 116 
 117             if (prev != NULL)
 118                 prev->next = p->next;
 119             else
 120                 task_list = p->next;
 121             g_free (p->info);
 122             g_free (p);
 123             return fd;
 124         }
 125         prev = p;
 126         p = p->next;
 127     }
 128 
 129     /* pid not found */
 130     return (-1);
 131 }
 132 
 133 /* --------------------------------------------------------------------------------------------- */
 134 /* {{{ Parent handlers */
 135 
 136 /* Parent/child protocol
 137  *
 138  * the child (the background) process send the following:
 139  * void *routine -- routine to be invoked in the parent
 140  * int  nargc    -- number of arguments
 141  * int  type     -- Return argument type.
 142  *
 143  * If the routine is zero, then it is a way to tell the parent
 144  * that the process is dying.
 145  *
 146  * nargc arguments in the following format:
 147  * int size of the coming block
 148  * size bytes with the block
 149  *
 150  * Now, the parent loads all those and then invokes
 151  * the routine with pointers to the information passed
 152  * (we just support pointers).
 153  *
 154  * If the return type is integer:
 155  *
 156  *     the parent then writes an int to the child with
 157  *     the return value from the routine and the values
 158  *     of any global variable that is modified in the parent
 159  *     currently: do_append and recursive_result.
 160  *
 161  * If the return type is a string:
 162  *
 163  *     the parent writes the resulting string length
 164  *     if the result string was NULL or the empty string,
 165  *     then the length is zero.
 166  *     The parent then writes the string length and frees
 167  *     the result string.
 168  */
 169 /*
 170  * Receive requests from background process and invoke the
 171  * specified routine
 172  */
 173 
 174 static int
 175 reading_failed (int i, char **data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 176 {
 177     while (i >= 0)
 178         g_free (data[i--]);
 179     message (D_ERROR, _("Background protocol error"), "%s", _("Reading failed"));
 180     return 0;
 181 }
 182 
 183 /* --------------------------------------------------------------------------------------------- */
 184 
 185 static int
 186 background_attention (int fd, void *closure)
     /* [previous][next][first][last][top][bottom][index][help]  */
 187 {
 188     file_op_context_t *ctx;
 189     int have_ctx;
 190     union
 191     {
 192         int (*have_ctx0) (int);
 193         int (*have_ctx1) (int, char *);
 194         int (*have_ctx2) (int, char *, char *);
 195         int (*have_ctx3) (int, char *, char *, char *);
 196         int (*have_ctx4) (int, char *, char *, char *, char *);
 197 
 198         int (*non_have_ctx0) (file_op_context_t *, int);
 199         int (*non_have_ctx1) (file_op_context_t *, int, char *);
 200         int (*non_have_ctx2) (file_op_context_t *, int, char *, char *);
 201         int (*non_have_ctx3) (file_op_context_t *, int, char *, char *, char *);
 202         int (*non_have_ctx4) (file_op_context_t *, int, char *, char *, char *, char *);
 203 
 204         char *(*ret_str0) (void);
 205         char *(*ret_str1) (char *);
 206         char *(*ret_str2) (char *, char *);
 207         char *(*ret_str3) (char *, char *, char *);
 208         char *(*ret_str4) (char *, char *, char *, char *);
 209 
 210         void *pointer;
 211     } routine;
 212     /*    void *routine; */
 213     int argc, i, status;
 214     char *data[MAXCALLARGS];
 215     ssize_t bytes, ret;
 216     TaskList *p;
 217     int to_child_fd = -1;
 218     enum ReturnType type;
 219     const char *background_process_error = _("Background process error");
 220 
 221     ctx = closure;
 222 
 223     bytes = read (fd, &routine.pointer, sizeof (routine));
 224     if (bytes == -1 || (size_t) bytes < (sizeof (routine)))
 225     {
 226         unregister_task_running (ctx->pid, fd);
 227 
 228         if (waitpid (ctx->pid, &status, WNOHANG) == 0)
 229         {
 230             /* the process is still running, but it misbehaves - kill it */
 231             kill (ctx->pid, SIGTERM);
 232             message (D_ERROR, background_process_error, "%s", _("Unknown error in child"));
 233             return 0;
 234         }
 235 
 236         /* 0 means happy end */
 237         if (WIFEXITED (status) && (WEXITSTATUS (status) == 0))
 238             return 0;
 239 
 240         message (D_ERROR, background_process_error, "%s", _("Child died unexpectedly"));
 241 
 242         return 0;
 243     }
 244 
 245     if (read (fd, &argc, sizeof (argc)) != sizeof (argc) ||
 246         read (fd, &type, sizeof (type)) != sizeof (type) ||
 247         read (fd, &have_ctx, sizeof (have_ctx)) != sizeof (have_ctx))
 248         return reading_failed (-1, data);
 249 
 250     if (argc > MAXCALLARGS)
 251         message (D_ERROR, _("Background protocol error"), "%s",
 252                  _("Background process sent us a request for more arguments\n"
 253                    "than we can handle."));
 254 
 255     if (have_ctx != 0 && read (fd, ctx, sizeof (*ctx)) != sizeof (*ctx))
 256         return reading_failed (-1, data);
 257 
 258     for (i = 0; i < argc; i++)
 259     {
 260         int size;
 261 
 262         if (read (fd, &size, sizeof (size)) != sizeof (size))
 263             return reading_failed (i - 1, data);
 264 
 265         data[i] = g_malloc (size + 1);
 266 
 267         if (read (fd, data[i], size) != size)
 268             return reading_failed (i, data);
 269 
 270         data[i][size] = '\0';   /* NULL terminate the blocks (they could be strings) */
 271     }
 272 
 273     /* Find child task info by descriptor */
 274     /* Find before call, because process can destroy self after */
 275     for (p = task_list; p != NULL; p = p->next)
 276         if (p->fd == fd)
 277             break;
 278 
 279     if (p != NULL)
 280         to_child_fd = p->to_child_fd;
 281 
 282     if (to_child_fd == -1)
 283         message (D_ERROR, background_process_error, "%s", _("Unknown error in child"));
 284 
 285     /* Handle the call */
 286     if (type == Return_Integer)
 287     {
 288         int result = 0;
 289 
 290         if (have_ctx == 0)
 291             switch (argc)
 292             {
 293             case 0:
 294                 result = routine.have_ctx0 (Background);
 295                 break;
 296             case 1:
 297                 result = routine.have_ctx1 (Background, data[0]);
 298                 break;
 299             case 2:
 300                 result = routine.have_ctx2 (Background, data[0], data[1]);
 301                 break;
 302             case 3:
 303                 result = routine.have_ctx3 (Background, data[0], data[1], data[2]);
 304                 break;
 305             case 4:
 306                 result = routine.have_ctx4 (Background, data[0], data[1], data[2], data[3]);
 307                 break;
 308             default:
 309                 break;
 310             }
 311         else
 312             switch (argc)
 313             {
 314             case 0:
 315                 result = routine.non_have_ctx0 (ctx, Background);
 316                 break;
 317             case 1:
 318                 result = routine.non_have_ctx1 (ctx, Background, data[0]);
 319                 break;
 320             case 2:
 321                 result = routine.non_have_ctx2 (ctx, Background, data[0], data[1]);
 322                 break;
 323             case 3:
 324                 result = routine.non_have_ctx3 (ctx, Background, data[0], data[1], data[2]);
 325                 break;
 326             case 4:
 327                 result =
 328                     routine.non_have_ctx4 (ctx, Background, data[0], data[1], data[2], data[3]);
 329                 break;
 330             default:
 331                 break;
 332             }
 333 
 334         /* Send the result code and the value for shared variables */
 335         ret = write (to_child_fd, &result, sizeof (result));
 336         if (have_ctx != 0 && to_child_fd != -1)
 337             ret = write (to_child_fd, ctx, sizeof (*ctx));
 338     }
 339     else if (type == Return_String)
 340     {
 341         int len;
 342         char *resstr = NULL;
 343 
 344         /* FIXME: string routines should also use the Foreground/Background
 345          * parameter.  Currently, this is not used here
 346          */
 347         switch (argc)
 348         {
 349         case 0:
 350             resstr = routine.ret_str0 ();
 351             break;
 352         case 1:
 353             resstr = routine.ret_str1 (data[0]);
 354             break;
 355         case 2:
 356             resstr = routine.ret_str2 (data[0], data[1]);
 357             break;
 358         case 3:
 359             resstr = routine.ret_str3 (data[0], data[1], data[2]);
 360             break;
 361         case 4:
 362             resstr = routine.ret_str4 (data[0], data[1], data[2], data[3]);
 363             break;
 364         default:
 365             g_assert_not_reached ();
 366         }
 367 
 368         if (resstr != NULL)
 369         {
 370             len = strlen (resstr);
 371             ret = write (to_child_fd, &len, sizeof (len));
 372             if (len != 0)
 373                 ret = write (to_child_fd, resstr, len);
 374             g_free (resstr);
 375         }
 376         else
 377         {
 378             len = 0;
 379             ret = write (to_child_fd, &len, sizeof (len));
 380         }
 381     }
 382 
 383     for (i = 0; i < argc; i++)
 384         g_free (data[i]);
 385 
 386     repaint_screen ();
 387 
 388     (void) ret;
 389 
 390     return 0;
 391 }
 392 
 393 /* --------------------------------------------------------------------------------------------- */
 394 /* }}} */
 395 
 396 /* {{{ client RPC routines */
 397 
 398 /* Sends the header for a call to a routine in the parent process.  If the file
 399  * operation context is not NULL, then it requests that the first parameter of
 400  * the call be a file operation context.
 401  */
 402 
 403 static void
 404 parent_call_header (void *routine, int argc, enum ReturnType type, file_op_context_t * ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 405 {
 406     int have_ctx;
 407     ssize_t ret;
 408 
 409     have_ctx = ctx != NULL ? 1 : 0;
 410 
 411     ret = write (parent_fd, &routine, sizeof (routine));
 412     ret = write (parent_fd, &argc, sizeof (argc));
 413     ret = write (parent_fd, &type, sizeof (type));
 414     ret = write (parent_fd, &have_ctx, sizeof (have_ctx));
 415     if (have_ctx != 0)
 416         ret = write (parent_fd, ctx, sizeof (*ctx));
 417 
 418     (void) ret;
 419 }
 420 
 421 /* --------------------------------------------------------------------------------------------- */
 422 
 423 static int
 424 parent_va_call (void *routine, gpointer data, int argc, va_list ap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 425 {
 426     int i;
 427     ssize_t ret;
 428     file_op_context_t *ctx = (file_op_context_t *) data;
 429 
 430     parent_call_header (routine, argc, Return_Integer, ctx);
 431     for (i = 0; i < argc; i++)
 432     {
 433         int len;
 434         void *value;
 435 
 436         len = va_arg (ap, int);
 437         value = va_arg (ap, void *);
 438         ret = write (parent_fd, &len, sizeof (len));
 439         ret = write (parent_fd, value, len);
 440     }
 441 
 442     ret = read (from_parent_fd, &i, sizeof (i));
 443     if (ctx != NULL)
 444         ret = read (from_parent_fd, ctx, sizeof (*ctx));
 445 
 446     (void) ret;
 447 
 448     return i;
 449 }
 450 
 451 /* --------------------------------------------------------------------------------------------- */
 452 
 453 static char *
 454 parent_va_call_string (void *routine, int argc, va_list ap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 455 {
 456     char *str;
 457     int i;
 458 
 459     parent_call_header (routine, argc, Return_String, NULL);
 460     for (i = 0; i < argc; i++)
 461     {
 462         int len;
 463         void *value;
 464 
 465         len = va_arg (ap, int);
 466         value = va_arg (ap, void *);
 467         if (write (parent_fd, &len, sizeof (len)) != sizeof (len) ||
 468             write (parent_fd, value, len) != len)
 469             return NULL;
 470     }
 471 
 472     if (read (from_parent_fd, &i, sizeof (i)) != sizeof (i) || i == 0)
 473         return NULL;
 474 
 475     str = g_malloc (i + 1);
 476     if (read (from_parent_fd, str, i) != i)
 477     {
 478         g_free (str);
 479         return NULL;
 480     }
 481     str[i] = '\0';
 482     return str;
 483 }
 484 
 485 /* --------------------------------------------------------------------------------------------- */
 486 /*** public functions ****************************************************************************/
 487 /* --------------------------------------------------------------------------------------------- */
 488 
 489 void
 490 unregister_task_running (pid_t pid, int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 491 {
 492     destroy_task (pid);
 493     delete_select_channel (fd);
 494 }
 495 
 496 /* --------------------------------------------------------------------------------------------- */
 497 
 498 void
 499 unregister_task_with_pid (pid_t pid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 500 {
 501     int fd;
 502 
 503     fd = destroy_task (pid);
 504     if (fd != -1)
 505         delete_select_channel (fd);
 506 }
 507 
 508 /* --------------------------------------------------------------------------------------------- */
 509 /**
 510  * Try to make the Midnight Commander a background job
 511  *
 512  * Returns:
 513  *  1 for parent
 514  *  0 for child
 515  * -1 on failure
 516  */
 517 int
 518 do_background (file_op_context_t * ctx, char *info)
     /* [previous][next][first][last][top][bottom][index][help]  */
 519 {
 520     int comm[2];                /* control connection stream */
 521     int back_comm[2];           /* back connection */
 522     pid_t pid;
 523 
 524     if (pipe (comm) == -1)
 525         return (-1);
 526 
 527     if (pipe (back_comm) == -1)
 528         return (-1);
 529 
 530     pid = fork ();
 531     if (pid == -1)
 532     {
 533         int saved_errno = errno;
 534 
 535         (void) close (comm[0]);
 536         (void) close (comm[1]);
 537         (void) close (back_comm[0]);
 538         (void) close (back_comm[1]);
 539         errno = saved_errno;
 540 
 541         return (-1);
 542     }
 543 
 544     if (pid == 0)
 545     {
 546         int nullfd;
 547 
 548         parent_fd = comm[1];
 549         from_parent_fd = back_comm[0];
 550 
 551         mc_global.we_are_background = TRUE;
 552         top_dlg = NULL;
 553 
 554         /* Make stdin/stdout/stderr point somewhere */
 555         close (STDIN_FILENO);
 556         close (STDOUT_FILENO);
 557         close (STDERR_FILENO);
 558 
 559         nullfd = open ("/dev/null", O_RDWR);
 560         if (nullfd != -1)
 561         {
 562             while (dup2 (nullfd, STDIN_FILENO) == -1 && errno == EINTR)
 563                 ;
 564             while (dup2 (nullfd, STDOUT_FILENO) == -1 && errno == EINTR)
 565                 ;
 566             while (dup2 (nullfd, STDERR_FILENO) == -1 && errno == EINTR)
 567                 ;
 568         }
 569 
 570         return 0;
 571     }
 572     else
 573     {
 574         ctx->pid = pid;
 575         register_task_running (ctx, pid, comm[0], back_comm[1], info);
 576         return 1;
 577     }
 578 }
 579 
 580 /* --------------------------------------------------------------------------------------------- */
 581 
 582 int
 583 parent_call (void *routine, file_op_context_t * ctx, int argc, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 584 {
 585     int ret;
 586     va_list ap;
 587 
 588     va_start (ap, argc);
 589     ret = parent_va_call (routine, (gpointer) ctx, argc, ap);
 590     va_end (ap);
 591 
 592     return ret;
 593 }
 594 
 595 /* --------------------------------------------------------------------------------------------- */
 596 
 597 char *
 598 parent_call_string (void *routine, int argc, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 599 {
 600     va_list ap;
 601     char *str;
 602 
 603     va_start (ap, argc);
 604     str = parent_va_call_string (routine, argc, ap);
 605     va_end (ap);
 606 
 607     return str;
 608 }
 609 
 610 /* --------------------------------------------------------------------------------------------- */
 611 
 612 /* event callback */
 613 gboolean
 614 background_parent_call (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 615                         gpointer init_data, gpointer data)
 616 {
 617     ev_background_parent_call_t *event_data = (ev_background_parent_call_t *) data;
 618 
 619     (void) event_group_name;
 620     (void) event_name;
 621     (void) init_data;
 622 
 623     event_data->ret.i =
 624         parent_va_call (event_data->routine, event_data->ctx, event_data->argc, event_data->ap);
 625 
 626     return TRUE;
 627 }
 628 
 629 /* --------------------------------------------------------------------------------------------- */
 630 
 631 /* event callback */
 632 gboolean
 633 background_parent_call_string (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 634                                gpointer init_data, gpointer data)
 635 {
 636     ev_background_parent_call_t *event_data = (ev_background_parent_call_t *) data;
 637 
 638     (void) event_group_name;
 639     (void) event_name;
 640     (void) init_data;
 641 
 642     event_data->ret.s =
 643         parent_va_call_string (event_data->routine, event_data->argc, event_data->ap);
 644 
 645     return TRUE;
 646 }
 647 
 648 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */