Manual pages: mcmcdiffmceditmcview

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

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