Imported Upstream version 4.4
[platform/upstream/make.git] / 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 \f
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 \f
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 \f
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 }