summaryrefslogtreecommitdiff
path: root/src/distcc.c
blob: b6db148fbe451c1865a8e3741d6b8655c84d69c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
 *
 * distcc -- A simple distributed compiler system
 *
 * Copyright (C) 2002, 2003, 2004 by Martin Pool <mbp@samba.org>
 * Copyright 2007 Google Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */


            /* 4: The noise of a multitude in the
             * mountains, like as of a great people; a
             * tumultuous noise of the kingdoms of nations
             * gathered together: the LORD of hosts
             * mustereth the host of the battle.
             *        -- Isaiah 13 */



#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#if defined (HAVE_LIBIBERTY_H)
#include <libiberty.h>
#elif defined (HAVE_LIBIBERTY_LIBIBERTY_H)
#include <libiberty/libiberty.h>
#else
#error Need libiberty.h
#endif

#include "distcc.h"
#include "trace.h"
#include "exitcode.h"
#include "util.h"
#include "hosts.h"
#include "bulk.h"
#include "implicit.h"
#include "compile.h"
#include "emaillog.h"


/* Name of this program, for trace.c */
const char *rs_program_name = "distcc";

/**
 * @file
 *
 * Entry point for the distcc client.
 *
 * There are three methods of use for distcc: explicit (distcc gcc -c
 * foo.c), implicit (distcc -c foo.c) and masqueraded (gcc -c foo.c,
 * where gcc is really a link to distcc).
 *
 * Detecting these is relatively easy by examining the first one or
 * two words of the command.  We also need to make sure that when we
 * go to run the compiler, we run the one intended by the user.
 *
 * In particular, for masqueraded mode, we want to make sure that we
 * don't invoke distcc recursively.
 **/

static void dcc_show_usage(void)
{
    dcc_show_version("distcc");
    printf(
"Usage:\n"
"   distcc [--scan-includes] [COMPILER] [compile options] -o OBJECT -c SOURCE\n"
"   distcc [--help|--version|--show-hosts|-j]\n"
"\n"
"Options:\n"
"   COMPILER                   Defaults to \"cc\".\n"
"   --help                     Explain usage, and exit.\n"
"   --version                  Show version, and exit.\n"
"   --show-hosts               Show host list, and exit.\n"
"   -j                         Show the concurrency level, as calculated from\n"
"                              the host list, and exit.\n"
"   --scan-includes            Show the files that distcc would send to the\n"
"                              remote machine, and exit.  (Pump mode only.)\n"
#ifdef HAVE_GSSAPI
"   --show-principal           Show current distccd GSS-API principal and exit.\n"
#endif
"\n"
"Environment variables:\n"
"   See the manual page for a complete list.\n"
"   DISTCC_VERBOSE=1           Give debug messages.\n"
"   DISTCC_LOG                 Send messages to file, not stderr.\n"
"   DISTCC_SSH                 Command to run to open SSH connections.\n"
"   DISTCC_DIR                 Directory for host list and locks.\n"
#ifdef HAVE_GSSAPI
"   DISTCC_PRINCIPAL	      The name of the server principal to connect to.\n"
#endif
"\n"
"Server specification:\n"
"A list of servers is taken from the environment variable $DISTCC_HOSTS, or\n"
"$DISTCC_DIR/hosts, or ~/.distcc/hosts, or %s/distcc/hosts.\n"
"Each host can be given in any of these forms, see the manual for details:\n"
"\n"
"   localhost                  Run in place.\n"
"   HOST                       TCP connection, port %d.\n"
"   HOST:PORT                  TCP connection, specified port.\n"
"   @HOST                      SSH connection to specified host.\n"
"   USER@HOST                  SSH connection to specified username at host.\n"
"   HOSTSPEC,lzo               Enable compression.\n"
"   HOSTSPEC,cpp,lzo           Use pump mode (remote preprocessing).\n"
"   HOSTSPEC,auth              Enable GSS-API based mutual authenticaton.\n"
"   --randomize                Randomize the server list before execution.\n"
"\n"
"distcc distributes compilation jobs across volunteer machines running\n"
"distccd.  Jobs that cannot be distributed, such as linking, are run locally.\n"
"distcc should be used with make's -jN option to execute in parallel on\n"
"several machines.\n",
    SYSCONFDIR,
    DISTCC_DEFAULT_PORT);
}


static RETSIGTYPE dcc_client_signalled (int whichsig)
{
    signal(whichsig, SIG_DFL);

#ifdef HAVE_STRSIGNAL
    rs_log_info("%s", strsignal(whichsig));
#else
    rs_log_info("terminated by signal %d", whichsig);
#endif

    dcc_cleanup_tempfiles_from_signal_handler();

    raise(whichsig);

}


static void dcc_client_catch_signals(void)
{
    signal(SIGTERM, &dcc_client_signalled);
    signal(SIGINT, &dcc_client_signalled);
    signal(SIGHUP, &dcc_client_signalled);
}

static void dcc_free_hostlist(struct dcc_hostdef *list) {
    while (list) {
        struct dcc_hostdef *l = list;
        list = list->next;
        dcc_free_hostdef(l);
    }
}

static void dcc_show_hosts(void) {
    struct dcc_hostdef *list, *l;
    int nhosts;

    if (dcc_get_hostlist(&list, &nhosts) != 0) {
        rs_log_crit("Failed to get host list");
        return;
    }

    for (l = list; l; l = l->next)
        printf("%s\n", l->hostdef_string);

    dcc_free_hostlist(list);
}

static void dcc_concurrency_level(void) {
    struct dcc_hostdef *list, *l;
    int nhosts;
    int nslots = 0;

    if (dcc_get_hostlist(&list, &nhosts) != 0) {
        rs_log_crit("Failed to get host list");
        return;
    }

    for (l = list; l; l = l->next)
        nslots += l->n_slots;

    dcc_free_hostlist(list);

    printf("%i\n", nslots);
}

#ifdef HAVE_GSSAPI
/*
 * Print out the name of the principal.
 */
static void dcc_gssapi_show_principal(void) {
    char *princ_env_val = NULL;

    if((princ_env_val = getenv("DISTCC_PRINCIPAL"))) {
	    printf("Principal is\t: %s\n", princ_env_val);
    } else {
        printf("Principal\t: Not Set.\n");
    }
}
#endif

/**
 * distcc client entry point.
 *
 * This is typically called by make in place of the real compiler.
 *
 * Performs basic setup and checks for distcc arguments, and then kicks off
 * dcc_build_somewhere().
 **/
int main(int argc, char **argv)
{
    int status, sg_level, tweaked_path = 0;
    char **compiler_args = NULL; /* dynamically allocated */
    char *compiler_name; /* points into argv[0] */
    int ret;

    dcc_client_catch_signals();
    atexit(dcc_cleanup_tempfiles);
    atexit(dcc_remove_state_file);

    dcc_set_trace_from_env();
    dcc_setup_log_email();

    dcc_trace_version();

    compiler_name = (char *) dcc_find_basename(argv[0]);

    /* Expand @FILE arguments. */
    expandargv(&argc, &argv);

    /* Ignore SIGPIPE; we consistently check error codes and will
     * see the EPIPE. */
    dcc_ignore_sigpipe(1);

    sg_level = dcc_recursion_safeguard();

    rs_trace("compiler name is \"%s\"", compiler_name);

    if (strstr(compiler_name, "distcc") != NULL) {
        /* Either "distcc -c hello.c" or "distcc gcc -c hello.c" */
        if (argc <= 1) {
            fprintf (stderr,
                     "%s: missing option/operand\n"
                     "Try `%s --help' for more information.\n",
                     argv[0], argv[0]);
            ret = EXIT_BAD_ARGUMENTS;
            goto out;
        }

        if (!strcmp(argv[1], "--help")) {
            dcc_show_usage();
            ret = 0;
            goto out;
        }

        if (!strcmp(argv[1], "--version")) {
            dcc_show_version("distcc");
            ret = 0;
            goto out;
        }

        if (!strcmp(argv[1], "--show-hosts")) {
            dcc_show_hosts();
            ret = 0;
            goto out;
        }

        if (!strcmp(argv[1], "-j")) {
            dcc_concurrency_level();
            ret = 0;
            goto out;
        }

        if (!strcmp(argv[1], "--scan-includes")) {
            if (argc <= 2) {
                fprintf (stderr,
                         "%s: missing operand\n"
                         "Try `%s --help' for more information.\n",
                         argv[0], argv[0]);
                ret = EXIT_BAD_ARGUMENTS;
                goto out;
            }
            dcc_scan_includes = 1;
            argv++;
        }

#ifdef HAVE_GSSAPI
	    if (!strcmp(argv[1], "--show-principal")) {
	        dcc_gssapi_show_principal();
	        ret = 0;
	        goto out;
	    }
#endif

        if ((ret = dcc_find_compiler(argv, &compiler_args)) != 0) {
            goto out;
        }
        /* compiler_args is now respectively either "cc -c hello.c" or
         * "gcc -c hello.c" */

#if 0
        /* I don't think we need to call this: if we reached this
         * line, our invocation name is something like 'distcc', and
         * that's never a problem for masquerading loops. */
        if ((ret = dcc_trim_path(compiler_name)) != 0)
            goto out;
#endif
    } else {
        /* Invoked as "cc -c hello.c", with masqueraded path */
        if ((ret = dcc_support_masquerade(argv, compiler_name,
                                          &tweaked_path)) != 0)
            goto out;

        if ((ret = dcc_copy_argv(argv, &compiler_args, 0)) != 0) {
            goto out;
        }
        free(compiler_args[0]);
        compiler_args[0] = strdup(compiler_name);
        if (!compiler_args[0]) {
            rs_log_error("strdup failed - out of memory?");
            ret = EXIT_OUT_OF_MEMORY;
            goto out;
        }
    }

    if (sg_level - tweaked_path > 0) {
        rs_log_crit("distcc seems to have invoked itself recursively!");
        ret = EXIT_RECURSION;
        goto out;
    }

    ret = dcc_build_somewhere_timed(compiler_args, sg_level, &status);
    compiler_args = NULL; /* dcc_build_somewhere_timed already free'd it. */

    out:
    if (compiler_args) {
      dcc_free_argv(compiler_args);
    }
    dcc_maybe_send_email();
    dcc_exit(ret);
}