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