(root)/
make-4.4/
src/
remote-cstms.c
       1  /* GNU Make remote job exportation interface to the Customs daemon.
       2     THIS CODE IS NOT SUPPORTED BY THE GNU PROJECT.
       3     Please do not send bug reports or questions about it to
       4     the Make maintainers.
       5  
       6  Copyright (C) 1988-2022 Free Software Foundation, Inc.
       7  This file is part of GNU Make.
       8  
       9  GNU Make is free software; you can redistribute it and/or modify it under the
      10  terms of the GNU General Public License as published by the Free Software
      11  Foundation; either version 3 of the License, or (at your option) any later
      12  version.
      13  
      14  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      15  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      16  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      17  
      18  You should have received a copy of the GNU General Public License along with
      19  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      20  
      21  #include "makeint.h"
      22  
      23  #include "filedef.h"
      24  #include "job.h"
      25  #include "commands.h"
      26  #include "debug.h"
      27  
      28  #if HAVE_SYS_TIME_H
      29  # include <sys/time.h>
      30  #endif
      31  #include <netdb.h>
      32  
      33  #include "customs.h"
      34  
      35  char *remote_description = "Customs";
      36  
      37  /* File name of the Customs 'export' client command.
      38     A full path name can be used to avoid some path-searching overhead.  */
      39  #define EXPORT_COMMAND  "/usr/local/bin/export"
      40  
      41  /* ExportPermit gotten by start_remote_job_p, and used by start_remote_job.  */
      42  static ExportPermit permit;
      43  
      44  /* Normalized path name of the current directory.  */
      45  static char *normalized_cwd;
      46  
      47  /* Call once at startup even if no commands are run.  */
      48  
      49  void
      50  remote_setup (void)
      51  {
      52  }
      53  
      54  /* Called before exit.  */
      55  
      56  void
      57  remote_cleanup (void)
      58  {
      59  }
      60  
      61  /* Return nonzero if the next job should be done remotely.  */
      62  
      63  int
      64  start_remote_job_p (int first_p)
      65  {
      66    static int inited = 0;
      67    int status;
      68    int njobs;
      69  
      70    if (!inited)
      71      {
      72        /* Allow the user to turn off job exportation (useful while he is
      73           debugging Customs, for example).  */
      74        if (getenv ("GNU_MAKE_NO_CUSTOMS") != 0)
      75          {
      76            inited = -1;
      77            return 0;
      78          }
      79  
      80        if (ISDB (DB_JOBS))
      81          Rpc_Debug (1);
      82  
      83        /* Ping the daemon once to see if it is there.  */
      84        inited = Customs_Ping () == RPC_SUCCESS ? 1 : -1;
      85  
      86        if (starting_directory == 0)
      87          /* main couldn't figure it out.  */
      88          inited = -1;
      89        else
      90          {
      91            /* Normalize the current directory path name to something
      92               that should work on all machines exported to.  */
      93  
      94            normalized_cwd = xmalloc (GET_PATH_MAX);
      95            strcpy (normalized_cwd, starting_directory);
      96            if (Customs_NormPath (normalized_cwd, GET_PATH_MAX) < 0)
      97              /* Path normalization failure means using Customs
      98                 won't work, but it's not really an error.  */
      99              inited = -1;
     100          }
     101      }
     102  
     103    if (inited < 0)
     104      return 0;
     105  
     106    njobs = job_slots_used;
     107    if (!first_p)
     108      njobs -= 1;         /* correction for being called from reap_children() */
     109  
     110    /* the first job should run locally, or, if the -l flag is given, we use
     111       that as clue as to how many local jobs should be scheduled locally */
     112    if (max_load_average < 0 && njobs == 0 || njobs < max_load_average)
     113       return 0;
     114  
     115    status = Customs_Host (EXPORT_SAME, &permit);
     116    if (status != RPC_SUCCESS)
     117      {
     118        DB (DB_JOBS, (_("Customs won't export: %s\n"),
     119                      Rpc_ErrorMessage (status)));
     120        return 0;
     121      }
     122  
     123    return !CUSTOMS_FAIL (&permit.addr);
     124  }
     125  
     126  /* Start a remote job running the command in ARGV, with environment from
     127     ENVP.  It gets standard input from STDIN_FD.  On failure, return
     128     nonzero.  On success, return zero, and set *USED_STDIN to nonzero if it
     129     will actually use STDIN_FD, zero if not, set *ID_PTR to a unique
     130     identification, and set *IS_REMOTE to nonzero if the job is remote, zero
     131     if it is local (meaning *ID_PTR is a process ID).  */
     132  
     133  int
     134  start_remote_job (char **argv, char **envp, int stdin_fd,
     135                    int *is_remote, pid_t *id_ptr, int *used_stdin)
     136  {
     137    char waybill[MAX_DATA_SIZE], msg[128];
     138    struct hostent *host;
     139    struct timeval timeout;
     140    struct sockaddr_in sin;
     141    int len;
     142    int retsock, retport, sock;
     143    Rpc_Stat status;
     144    pid_t pid;
     145  
     146    /* Create the return socket.  */
     147    retsock = Rpc_UdpCreate (True, 0);
     148    if (retsock < 0)
     149      {
     150        O (error, NILF, "exporting: Couldn't create return socket.");
     151        return 1;
     152      }
     153  
     154    /* Get the return socket's port number.  */
     155    len = sizeof (sin);
     156    if (getsockname (retsock, (struct sockaddr *) &sin, &len) < 0)
     157      {
     158        (void) close (retsock);
     159        perror_with_name ("exporting: ", "getsockname");
     160        return 1;
     161      }
     162    retport = sin.sin_port;
     163  
     164    /* Create the TCP socket for talking to the remote child.  */
     165    sock = Rpc_TcpCreate (False, 0);
     166  
     167    /* Create a WayBill to give to the server.  */
     168    len = Customs_MakeWayBill (&permit, normalized_cwd, argv[0], argv,
     169                               envp, retport, waybill);
     170  
     171    /* Modify the waybill for the child's uid/gid.  */
     172    {
     173      WayBill *wb = (WayBill *) waybill;
     174      wb->ruid = wb->euid;
     175      wb->rgid = wb->egid;
     176    }
     177  
     178    /* Send the request to the server, timing out in 20 seconds.  */
     179    timeout.tv_usec = 0;
     180    timeout.tv_sec = 20;
     181    sin.sin_family = AF_INET;
     182    sin.sin_port = htons (Customs_Port ());
     183    sin.sin_addr = permit.addr;
     184    status = Rpc_Call (sock, &sin, (Rpc_Proc) CUSTOMS_IMPORT,
     185                       len, (Rpc_Opaque) waybill,
     186                       sizeof (msg), (Rpc_Opaque) msg,
     187                       1, &timeout);
     188  
     189    host = gethostbyaddr ((char *)&permit.addr, sizeof(permit.addr), AF_INET);
     190  
     191    {
     192      const char *hnm = host ? host->h_name : inet_ntoa (permit.addr);
     193      size_t hlen = strlen (hnm);
     194  
     195      if (status != RPC_SUCCESS)
     196        {
     197          const char *err = Rpc_ErrorMessage (status);
     198          (void) close (retsock);
     199          (void) close (sock);
     200          error (NILF, hlen + strlen (err),
     201                 "exporting to %s: %s", hnm, err);
     202          return 1;
     203        }
     204      else if (msg[0] != 'O' || msg[1] != 'k' || msg[2] != '\0')
     205        {
     206          (void) close (retsock);
     207          (void) close (sock);
     208          error (NILF, hlen + strlen (msg), "exporting to %s: %s", hnm, msg);
     209          return 1;
     210        }
     211      else
     212        {
     213          error (NILF, hlen + INTSTR_LENGTH,
     214                 "*** exported to %s (id %u)", hnm, permit.id);
     215        }
     216  
     217      fflush (stdout);
     218      fflush (stderr);
     219    }
     220  
     221    pid = vfork ();
     222    if (pid < 0)
     223      {
     224        /* The fork failed!  */
     225        perror_with_name ("fork", "");
     226        return 1;
     227      }
     228    else if (pid == 0)
     229      {
     230        /* Child side.  Run 'export' to handle the connection.  */
     231        static char sock_buf[INTSTR_LENGTH], retsock_buf[INTSTR_LENGTH];
     232        static char id_buf[INTSTR_LENGTH];
     233        static char *new_argv[6] =
     234          { EXPORT_COMMAND, "-id", sock_buf, retsock_buf, id_buf, 0 };
     235  
     236        /* Set up the arguments.  */
     237        (void) sprintf (sock_buf, "%d", sock);
     238        (void) sprintf (retsock_buf, "%d", retsock);
     239        (void) sprintf (id_buf, "%x", permit.id);
     240  
     241        /* Get the right stdin.  */
     242        if (stdin_fd != 0)
     243          (void) dup2 (stdin_fd, 0);
     244  
     245        /* Unblock signals in the child.  */
     246        unblock_all_sigs ();
     247  
     248        /* Run the command.  */
     249        exec_command (new_argv, envp);
     250      }
     251  
     252    /* Parent side.  Return the 'export' process's ID.  */
     253    (void) close (retsock);
     254    (void) close (sock);
     255    *is_remote = 0;
     256    *id_ptr = pid;
     257    *used_stdin = 1;
     258    return 0;
     259  }
     260  
     261  /* Get the status of a dead remote child.  Block waiting for one to die
     262     if BLOCK is nonzero.  Set *EXIT_CODE_PTR to the exit status, *SIGNAL_PTR
     263     to the termination signal or zero if it exited normally, and *COREDUMP_PTR
     264     nonzero if it dumped core.  Return the ID of the child that died,
     265     0 if we would have to block and !BLOCK, or < 0 if there were none.  */
     266  
     267  int
     268  remote_status (int *exit_code_ptr, int *signal_ptr, int *coredump_ptr,
     269                 int block)
     270  {
     271    return -1;
     272  }
     273  
     274  /* Block asynchronous notification of remote child death.
     275     If this notification is done by raising the child termination
     276     signal, do not block that signal.  */
     277  void
     278  block_remote_children (void)
     279  {
     280    return;
     281  }
     282  
     283  /* Restore asynchronous notification of remote child death.
     284     If this is done by raising the child termination signal,
     285     do not unblock that signal.  */
     286  void
     287  unblock_remote_children (void)
     288  {
     289    return;
     290  }
     291  
     292  /* Send signal SIG to child ID.  Return 0 if successful, -1 if not.  */
     293  int
     294  remote_kill (pid_t id, int sig)
     295  {
     296    return -1;
     297  }