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

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