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