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-2024
   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 #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, i, status;
 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         unregister_task_running (ctx->pid, fd);
 229 
 230         if (waitpid (ctx->pid, &status, WNOHANG) == 0)
 231         {
 232             /* the process is still running, but it misbehaves - kill it */
 233             kill (ctx->pid, SIGTERM);
 234             message (D_ERROR, background_process_error, "%s", _("Unknown error in child"));
 235             return 0;
 236         }
 237 
 238         /* 0 means happy end */
 239         if (WIFEXITED (status) && (WEXITSTATUS (status) == 0))
 240             return 0;
 241 
 242         message (D_ERROR, background_process_error, "%s", _("Child died unexpectedly"));
 243 
 244         return 0;
 245     }
 246 
 247     if (read (fd, &argc, sizeof (argc)) != sizeof (argc) ||
 248         read (fd, &type, sizeof (type)) != sizeof (type) ||
 249         read (fd, &have_ctx, sizeof (have_ctx)) != sizeof (have_ctx))
 250         return reading_failed (-1, data);
 251 
 252     if (argc > MAXCALLARGS)
 253         message (D_ERROR, _("Background protocol error"), "%s",
 254                  _("Background process sent us a request for more arguments\n"
 255                    "than we can handle."));
 256 
 257     if (have_ctx != 0 && read (fd, ctx, sizeof (*ctx)) != sizeof (*ctx))
 258         return reading_failed (-1, data);
 259 
 260     for (i = 0; i < argc; i++)
 261     {
 262         int size;
 263 
 264         if (read (fd, &size, sizeof (size)) != sizeof (size))
 265             return reading_failed (i - 1, data);
 266 
 267         data[i] = g_malloc (size + 1);
 268 
 269         if (read (fd, data[i], size) != size)
 270             return reading_failed (i, data);
 271 
 272         data[i][size] = '\0';   /* NULL terminate the blocks (they could be strings) */
 273     }
 274 
 275     /* Find child task info by descriptor */
 276     /* Find before call, because process can destroy self after */
 277     for (p = task_list; p != NULL; p = p->next)
 278         if (p->fd == fd)
 279             break;
 280 
 281     if (p != NULL)
 282         to_child_fd = p->to_child_fd;
 283 
 284     if (to_child_fd == -1)
 285         message (D_ERROR, background_process_error, "%s", _("Unknown error in child"));
 286     else if (type == Return_Integer)
 287     {
 288         /* Handle the call */
 289 
 290         int result = 0;
 291 
 292         if (have_ctx == 0)
 293             switch (argc)
 294             {
 295             case 0:
 296                 result = routine.have_ctx0 (Background);
 297                 break;
 298             case 1:
 299                 result = routine.have_ctx1 (Background, data[0]);
 300                 break;
 301             case 2:
 302                 result = routine.have_ctx2 (Background, data[0], data[1]);
 303                 break;
 304             case 3:
 305                 result = routine.have_ctx3 (Background, data[0], data[1], data[2]);
 306                 break;
 307             case 4:
 308                 result = routine.have_ctx4 (Background, data[0], data[1], data[2], data[3]);
 309                 break;
 310             default:
 311                 break;
 312             }
 313         else
 314             switch (argc)
 315             {
 316             case 0:
 317                 result = routine.non_have_ctx0 (ctx, Background);
 318                 break;
 319             case 1:
 320                 result = routine.non_have_ctx1 (ctx, Background, data[0]);
 321                 break;
 322             case 2:
 323                 result = routine.non_have_ctx2 (ctx, Background, data[0], data[1]);
 324                 break;
 325             case 3:
 326                 result = routine.non_have_ctx3 (ctx, Background, data[0], data[1], data[2]);
 327                 break;
 328             case 4:
 329                 result =
 330                     routine.non_have_ctx4 (ctx, Background, data[0], data[1], data[2], data[3]);
 331                 break;
 332             default:
 333                 break;
 334             }
 335 
 336         /* Send the result code and the value for shared variables */
 337         ret = write (to_child_fd, &result, sizeof (result));
 338         if (have_ctx != 0 && to_child_fd != -1)
 339             ret = write (to_child_fd, ctx, sizeof (*ctx));
 340     }
 341     else if (type == Return_String)
 342     {
 343         int len;
 344         char *resstr = NULL;
 345 
 346         /* FIXME: string routines should also use the Foreground/Background
 347          * parameter.  Currently, this is not used here
 348          */
 349         switch (argc)
 350         {
 351         case 0:
 352             resstr = routine.ret_str0 ();
 353             break;
 354         case 1:
 355             resstr = routine.ret_str1 (data[0]);
 356             break;
 357         case 2:
 358             resstr = routine.ret_str2 (data[0], data[1]);
 359             break;
 360         case 3:
 361             resstr = routine.ret_str3 (data[0], data[1], data[2]);
 362             break;
 363         case 4:
 364             resstr = routine.ret_str4 (data[0], data[1], data[2], data[3]);
 365             break;
 366         default:
 367             g_assert_not_reached ();
 368         }
 369 
 370         if (resstr != NULL)
 371         {
 372             len = strlen (resstr);
 373             ret = write (to_child_fd, &len, sizeof (len));
 374             if (len != 0)
 375                 ret = write (to_child_fd, resstr, len);
 376             g_free (resstr);
 377         }
 378         else
 379         {
 380             len = 0;
 381             ret = write (to_child_fd, &len, sizeof (len));
 382         }
 383     }
 384 
 385     for (i = 0; i < argc; i++)
 386         g_free (data[i]);
 387 
 388     repaint_screen ();
 389 
 390     (void) ret;
 391 
 392     return 0;
 393 }
 394 
 395 /* --------------------------------------------------------------------------------------------- */
 396 /* }}} */
 397 
 398 /* {{{ client RPC routines */
 399 
 400 /* Sends the header for a call to a routine in the parent process.  If the file
 401  * operation context is not NULL, then it requests that the first parameter of
 402  * the call be a file operation context.
 403  */
 404 
 405 static void
 406 parent_call_header (void *routine, int argc, enum ReturnType type, file_op_context_t *ctx)
     /* [previous][next][first][last][top][bottom][index][help]  */
 407 {
 408     int have_ctx;
 409     ssize_t ret;
 410 
 411     have_ctx = ctx != NULL ? 1 : 0;
 412 
 413     ret = write (parent_fd, &routine, sizeof (routine));
 414     ret = write (parent_fd, &argc, sizeof (argc));
 415     ret = write (parent_fd, &type, sizeof (type));
 416     ret = write (parent_fd, &have_ctx, sizeof (have_ctx));
 417     if (have_ctx != 0)
 418         ret = write (parent_fd, ctx, sizeof (*ctx));
 419 
 420     (void) ret;
 421 }
 422 
 423 /* --------------------------------------------------------------------------------------------- */
 424 
 425 static int
 426 parent_va_call (void *routine, gpointer data, int argc, va_list ap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 427 {
 428     int i;
 429     ssize_t ret;
 430     file_op_context_t *ctx = (file_op_context_t *) data;
 431 
 432     parent_call_header (routine, argc, Return_Integer, ctx);
 433     for (i = 0; i < argc; i++)
 434     {
 435         int len;
 436         void *value;
 437 
 438         len = va_arg (ap, int);
 439         value = va_arg (ap, void *);
 440         ret = write (parent_fd, &len, sizeof (len));
 441         ret = write (parent_fd, value, len);
 442     }
 443 
 444     ret = read (from_parent_fd, &i, sizeof (i));
 445     if (ctx != NULL)
 446         ret = read (from_parent_fd, ctx, sizeof (*ctx));
 447 
 448     (void) ret;
 449 
 450     return i;
 451 }
 452 
 453 /* --------------------------------------------------------------------------------------------- */
 454 
 455 static char *
 456 parent_va_call_string (void *routine, int argc, va_list ap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 457 {
 458     char *str;
 459     int i;
 460 
 461     parent_call_header (routine, argc, Return_String, NULL);
 462     for (i = 0; i < argc; i++)
 463     {
 464         int len;
 465         void *value;
 466 
 467         len = va_arg (ap, int);
 468         value = va_arg (ap, void *);
 469         if (write (parent_fd, &len, sizeof (len)) != sizeof (len) ||
 470             write (parent_fd, value, len) != len)
 471             return NULL;
 472     }
 473 
 474     if (read (from_parent_fd, &i, sizeof (i)) != sizeof (i) || i == 0)
 475         return NULL;
 476 
 477     str = g_malloc (i + 1);
 478     if (read (from_parent_fd, str, i) != i)
 479     {
 480         g_free (str);
 481         return NULL;
 482     }
 483     str[i] = '\0';
 484     return str;
 485 }
 486 
 487 /* --------------------------------------------------------------------------------------------- */
 488 /*** public functions ****************************************************************************/
 489 /* --------------------------------------------------------------------------------------------- */
 490 
 491 void
 492 unregister_task_running (pid_t pid, int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 493 {
 494     destroy_task (pid);
 495     delete_select_channel (fd);
 496 }
 497 
 498 /* --------------------------------------------------------------------------------------------- */
 499 
 500 void
 501 unregister_task_with_pid (pid_t pid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 502 {
 503     int fd;
 504 
 505     fd = destroy_task (pid);
 506     if (fd != -1)
 507         delete_select_channel (fd);
 508 }
 509 
 510 /* --------------------------------------------------------------------------------------------- */
 511 /**
 512  * Try to make the Midnight Commander a background job
 513  *
 514  * Returns:
 515  *  1 for parent
 516  *  0 for child
 517  * -1 on failure
 518  */
 519 int
 520 do_background (file_op_context_t *ctx, char *info)
     /* [previous][next][first][last][top][bottom][index][help]  */
 521 {
 522     int comm[2];                /* control connection stream */
 523     int back_comm[2];           /* back connection */
 524     pid_t pid;
 525 
 526     if (pipe (comm) == -1)
 527         return (-1);
 528 
 529     if (pipe (back_comm) == -1)
 530     {
 531         (void) close (comm[0]);
 532         (void) close (comm[1]);
 533 
 534         return (-1);
 535     }
 536 
 537     pid = my_fork ();
 538     if (pid == -1)
 539     {
 540         int saved_errno = errno;
 541 
 542         (void) close (comm[0]);
 543         (void) close (comm[1]);
 544         (void) close (back_comm[0]);
 545         (void) close (back_comm[1]);
 546         errno = saved_errno;
 547 
 548         return (-1);
 549     }
 550 
 551     if (pid == 0)
 552     {
 553         int nullfd;
 554 
 555         (void) close (comm[0]);
 556         parent_fd = comm[1];
 557 
 558         (void) close (back_comm[1]);
 559         from_parent_fd = back_comm[0];
 560 
 561         mc_global.we_are_background = TRUE;
 562         top_dlg = NULL;
 563 
 564         /* Make stdin/stdout/stderr point somewhere */
 565         close (STDIN_FILENO);
 566         close (STDOUT_FILENO);
 567         close (STDERR_FILENO);
 568 
 569         nullfd = open ("/dev/null", O_RDWR);
 570         if (nullfd != -1)
 571         {
 572             while (dup2 (nullfd, STDIN_FILENO) == -1 && errno == EINTR)
 573                 ;
 574             while (dup2 (nullfd, STDOUT_FILENO) == -1 && errno == EINTR)
 575                 ;
 576             while (dup2 (nullfd, STDERR_FILENO) == -1 && errno == EINTR)
 577                 ;
 578             close (nullfd);
 579         }
 580 
 581         return 0;
 582     }
 583     else
 584     {
 585         (void) close (comm[1]);
 586         (void) close (back_comm[0]);
 587         ctx->pid = pid;
 588         register_task_running (ctx, pid, comm[0], back_comm[1], info);
 589         return 1;
 590     }
 591 }
 592 
 593 /* --------------------------------------------------------------------------------------------- */
 594 
 595 int
 596 parent_call (void *routine, file_op_context_t *ctx, int argc, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 597 {
 598     int ret;
 599     va_list ap;
 600 
 601     va_start (ap, argc);
 602     ret = parent_va_call (routine, (gpointer) ctx, argc, ap);
 603     va_end (ap);
 604 
 605     return ret;
 606 }
 607 
 608 /* --------------------------------------------------------------------------------------------- */
 609 
 610 char *
 611 parent_call_string (void *routine, int argc, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 612 {
 613     va_list ap;
 614     char *str;
 615 
 616     va_start (ap, argc);
 617     str = parent_va_call_string (routine, argc, ap);
 618     va_end (ap);
 619 
 620     return str;
 621 }
 622 
 623 /* --------------------------------------------------------------------------------------------- */
 624 
 625 /* event callback */
 626 gboolean
 627 background_parent_call (const gchar *event_group_name, const gchar *event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 628                         gpointer init_data, gpointer data)
 629 {
 630     ev_background_parent_call_t *event_data = (ev_background_parent_call_t *) data;
 631 
 632     (void) event_group_name;
 633     (void) event_name;
 634     (void) init_data;
 635 
 636     event_data->ret.i =
 637         parent_va_call (event_data->routine, event_data->ctx, event_data->argc, event_data->ap);
 638 
 639     return TRUE;
 640 }
 641 
 642 /* --------------------------------------------------------------------------------------------- */
 643 
 644 /* event callback */
 645 gboolean
 646 background_parent_call_string (const gchar *event_group_name, const gchar *event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 647                                gpointer init_data, gpointer data)
 648 {
 649     ev_background_parent_call_t *event_data = (ev_background_parent_call_t *) data;
 650 
 651     (void) event_group_name;
 652     (void) event_name;
 653     (void) init_data;
 654 
 655     event_data->ret.s =
 656         parent_va_call_string (event_data->routine, event_data->argc, event_data->ap);
 657 
 658     return TRUE;
 659 }
 660 
 661 /* --------------------------------------------------------------------------------------------- */

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