#include <netdb.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <netinet/in.h>
#include "distcc.h"
#include "clinet.h"
#include "netutil.h"
#include "util.h"
#include "trace.h"
#include "rslave.h"
#include "../lzo/minilzo.h"
#ifndef RLIMIT_NOFILE
#define RLIMIT_NOFILE RLIMIT_OFILE
#endif
enum status_e { STATE_LOOKUP = 0,
STATE_CONNECT,
STATE_CONNECTING,
STATE_READ_DONEPKT,
STATE_READ_STATPKT,
STATE_READ_REST,
STATE_CLOSE,
STATE_DONE};
struct state_s {
rslave_request_t req;
rslave_result_t res;
struct timeval start;
struct timeval deadline;
char curhdrbuf[12];
int curhdrlen;
enum status_e status;
int ntries;
int fd;
int up;
};
typedef struct state_s state_t;
#define DEFAULT_FORMAT "distcc%d"
#define DEFAULT_PORT 3632
#define DEFAULT_PROTOCOL 1
#define DEFAULT_BIGTIMEOUT 7
#define DEFAULT_DNSTIMEOUT_MS 500
#define DEFAULT_CONNTIMEOUT_MS 900
#define DEFAULT_COMPTIMEOUT_MS 1500
#define DEFAULT_OVERLAP 1
#define DEFAULT_DNSGAP 0
#define DEFAULT_COMPILER "none"
char canned_query[1000];
size_t canned_query_len = 0;
int opt_latency = 0;
int opt_numeric = 0;
int opt_overlap = DEFAULT_OVERLAP;
int opt_dnsgap = DEFAULT_DNSGAP;
int opt_port = DEFAULT_PORT;
int opt_protocol = DEFAULT_PROTOCOL;
int opt_bigtimeout_sec = DEFAULT_BIGTIMEOUT;
int opt_conntimeout_ms = DEFAULT_CONNTIMEOUT_MS;
int opt_comptimeout_ms = DEFAULT_COMPTIMEOUT_MS;
int opt_dnstimeout_ms = DEFAULT_DNSTIMEOUT_MS;
int opt_verbose = 0;
int opt_domain = 0;
int opt_match = 0;
int opt_bang_down = 0;
const char *opt_compiler = NULL;
const char *protocol_suffix[] = { NULL,
"",
",lzo",
",lzo,cpp" };
#define MAXHOSTS 500
#define MAXTRIES 5
#define MAXFDS (MAXHOSTS+2)
int fd2state[MAXHOSTS+1000];
int nok;
int ndone;
const char *rs_program_name = "lsdistcc";
void usage(void);
int bitcompare(const unsigned char *a, const unsigned char *b, int nbits);
void timeout_handler(int x);
void get_thename(const char**sformat, const char *domain_name,
int i, char *thename);
int detect_distcc_servers(const char **argv, int argc, int opti,
int bigtimeout, int dnstimeout, int matchbits,
int overlap, int dnsgap);
void server_read_packet_header(state_t *sp);
void server_handle_event(state_t *sp);
void usage(void) {
printf("Usage: lsdistcc [-tTIMEOUT] [-mBITS] [-nvd] [format]\n\
Uses 'for i=1... sprintf(format, i)' to construct names of servers,\n\
stops after %d seconds or at second server that doesn't resolve,\n\
prints the names of all such servers listening on distcc's port.\n\
Default format is %s. \n\
If a list of host names are given in the command line,\n\
lsdistcc will only check those hosts. \n\
Options:\n\
-l Output latency in milliseconds after each hostname\n\
(not including DNS latency)\n\
-n Print IP address rather than name\n\
-x Append ,down to down hosts in host list\n\
-tTIMEOUT Set number of seconds to stop searching after [%d]\n\
-hHTIMEOUT Set number of milliseconds before retrying gethostbyname [%d]\n\
-cCTIMEOUT Set number of milliseconds before giving up on connect [%d]\n\
(0 to inhibit connect)\n\
-kKTIMEOUT Set number of milliseconds before giving up on compile [%d]\n\
(0 to inhibit compile)\n\
-mBITS Set number of bits of address that must match first host found [0]\n\
-oOVERLAP Set number of extra DNS requests to send [%d]\n\
-gDNSGAP Set number of missing DNS entries to tolerate [%d]\n\
-rPORT Port to connect to [%d]\n\
-PPROTOCOL Protocol version to use (1-3) [%d]\n\
-pCOMPILER Name of compiler to use [%s]\n\
-d Append DNS domain name to format\n\
-v Verbose\n\
\n\
Example:\n\
lsdistcc -l -p$COMPILER\n\
lsdistcc -p$COMPILER hosta somehost hostx hosty\n\
", DEFAULT_BIGTIMEOUT,
DEFAULT_FORMAT,
DEFAULT_BIGTIMEOUT,
DEFAULT_DNSTIMEOUT_MS,
DEFAULT_CONNTIMEOUT_MS,
DEFAULT_COMPTIMEOUT_MS,
DEFAULT_OVERLAP,
DEFAULT_DNSGAP,
DEFAULT_PORT,
DEFAULT_PROTOCOL,
DEFAULT_COMPILER);
exit(1);
}
int bitcompare(const unsigned char *a, const unsigned char *b, int nbits)
{
int fullbytes = nbits/8;
int leftoverbits = nbits & 7;
if (fullbytes) {
int d = memcmp((char *)a, (char *)b, (size_t) fullbytes);
if (d)
return d;
}
if (leftoverbits) {
int mask = 0;
int i;
for (i=0; i<leftoverbits; i++)
mask |= (1 << (7-i));
return ((a[fullbytes] ^ b[fullbytes]) & mask);
}
return 0;
}
#if 0
#include <assert.h>
main()
{
assert(bitcompare("0", "0", 8) == 0);
assert(bitcompare("0", "1", 8) != 0);
assert(bitcompare("0", "1", 7) == 0);
}
#endif
void timeout_handler(int x)
{
(void) x;
if (opt_verbose > 0)
fprintf(stderr, "Timeout!\n");
exit(0);
}
static void generate_query(void)
{
const char* program = "int foo(){return 0;}";
unsigned char lzod_program[1000];
unsigned char lzo_work_mem[LZO1X_1_MEM_COMPRESS];
lzo_uint lzod_program_len;
lzo1x_1_compress((const unsigned char *)program, strlen(program),
lzod_program, &lzod_program_len,
lzo_work_mem);
switch (opt_protocol) {
case 1: {
static const char canned_query_fmt_protocol_1[]=
"DIST00000001"
"ARGC00000005"
"ARGV%08x%s"
"ARGV00000002-c"
"ARGV00000007hello.c"
"ARGV00000002-o"
"ARGV00000007hello.o"
"DOTI%08x%s";
sprintf(canned_query,
canned_query_fmt_protocol_1,
(unsigned)strlen(opt_compiler), opt_compiler,
(unsigned)strlen(program), program);
canned_query_len = strlen(canned_query);
break;
}
case 2: {
static const char canned_query_fmt_protocol_2[]=
"DIST00000002"
"ARGC00000005"
"ARGV%08x%s"
"ARGV00000002-c"
"ARGV00000007hello.c"
"ARGV00000002-o"
"ARGV00000007hello.o"
"DOTI%08x";
sprintf(canned_query,
canned_query_fmt_protocol_2,
(unsigned)strlen(opt_compiler),
opt_compiler,
(unsigned)lzod_program_len);
canned_query_len = strlen(canned_query) + lzod_program_len;
memcpy(canned_query + strlen(canned_query),
lzod_program, lzod_program_len);
break;
}
case 3: {
static const char canned_query_fmt_protocol_3[]=
"DIST00000003"
"CDIR00000001/"
"ARGC00000005"
"ARGV%08x%s"
"ARGV00000002-c"
"ARGV00000007hello.c"
"ARGV00000002-o"
"ARGV00000007hello.o"
"NFIL00000001"
"NAME00000008/hello.c"
"FILE%08x";
sprintf(canned_query,
canned_query_fmt_protocol_3,
(unsigned)strlen(opt_compiler),
opt_compiler,
(unsigned)lzod_program_len);
canned_query_len = strlen(canned_query) + lzod_program_len;
memcpy(canned_query + strlen(canned_query),
lzod_program, lzod_program_len);
break;
}
}
}
void server_read_packet_header(state_t *sp)
{
int arg;
int nread;
nread = read(sp->fd, sp->curhdrbuf + sp->curhdrlen,
(size_t)(12 - sp->curhdrlen));
if (nread == 0) {
if (opt_verbose > 0)
fprintf(stderr, "lsdistcc: premature EOF while waiting for "
"result from server %s\n",
sp->req.hname);
sp->status = STATE_CLOSE;
return;
}
if (nread > 0)
sp->curhdrlen += nread;
if (sp->curhdrlen < 12)
return;
arg = (int)strtol(sp->curhdrbuf+4, NULL, 16);
if (opt_verbose > 2) {
int i;
printf("Got hdr '%12.12s' = ", sp->curhdrbuf);
for (i=0; i < sp->curhdrlen; i++)
printf("%2x", sp->curhdrbuf[i]);
printf("\n");
}
switch (sp->status) {
case STATE_READ_DONEPKT:
if (memcmp(sp->curhdrbuf, "DONE", 4) != 0) {
if (opt_verbose > 1)
fprintf(stderr,
"%s wrong protocol; expected DONE, got %4.4s!\n",
sp->req.hname, sp->curhdrbuf);
sp->status = STATE_CLOSE;
break;
}
if (arg != opt_protocol) {
if (opt_verbose > 1)
fprintf(stderr,
"%s wrong protocol, expected %d got %d!\n",
sp->req.hname,
opt_protocol,
arg);
sp->status = STATE_CLOSE;
break;
}
sp->curhdrlen = 0;
sp->status = STATE_READ_STATPKT;
break;
case STATE_READ_STATPKT:
if (memcmp(sp->curhdrbuf, "STAT", 4) != 0) {
if (opt_verbose > 1)
fprintf(stderr,
"%s wrong protocol! Expected STAT, got %4.4s\n",
sp->req.hname, sp->curhdrbuf);
sp->status = STATE_CLOSE;
break;
}
if (arg != 0) {
if (opt_verbose > 1) {
fprintf(stderr,
"lsdistcc: warning: test compile on %s failed! "
"status 0x%x\n",
sp->req.hname, arg);
}
sp->status = STATE_CLOSE;
break;
}
sp->curhdrlen = 0;
sp->status = STATE_READ_REST;
break;
default:
fprintf(stderr, "bug\n");
exit(1);
}
}
void server_handle_event(state_t *sp)
{
struct timeval now;
gettimeofday(&now, 0);
do {
struct sockaddr_in sa;
if (opt_verbose > 2)
fprintf(stderr,
"now %ld %ld: server_handle_event: %s: state %d\n",
now.tv_sec, (long) now.tv_usec/1000,
sp->req.hname, sp->status);
switch (sp->status) {
case STATE_CONNECT:
if (opt_conntimeout_ms == 0) {
sp->fd = -1;
sp->up = 1;
sp->status = STATE_CLOSE;
break;
}
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_port = htons(opt_port);
memcpy(&sa.sin_addr, sp->res.addr, 4);
if ((sp->fd = socket(sa.sin_family, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "failed to create socket: %s", strerror(errno));
sp->status = STATE_DONE;
} else {
dcc_set_nonblocking(sp->fd);
if (opt_verbose > 0)
fprintf(stderr,
"now %ld %ld: Connecting to %s\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname);
if (connect(sp->fd, (struct sockaddr *)&sa, sizeof(sa))
&& errno != EINPROGRESS) {
if (opt_verbose > 0)
fprintf(stderr, "failed to connect socket: %s",
strerror(errno));
sp->status = STATE_CLOSE;
} else {
sp->status = STATE_CONNECTING;
fd2state[sp->fd] = sp->res.id;
gettimeofday(&now, 0);
sp->start = now;
sp->deadline = now;
sp->deadline.tv_usec += 1000 * opt_conntimeout_ms;
sp->deadline.tv_sec += sp->deadline.tv_usec / 1000000;
sp->deadline.tv_usec = sp->deadline.tv_usec % 1000000;
}
}
break;
case STATE_CONNECTING:
{
int connecterr;
socklen_t len = sizeof(connecterr);
int nsend;
int nsent;
if (getsockopt(sp->fd, SOL_SOCKET, SO_ERROR,
(char *)&connecterr, &len) < 0) {
fprintf(stderr, "getsockopt SO_ERROR failed?!");
sp->status = STATE_CLOSE;
break;
}
if (connecterr) {
if (opt_verbose > 0)
fprintf(stderr,
"now %ld %ld: Connecting to %s failed "
"with errno %d = %s\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname,
connecterr, strerror(connecterr));
sp->status = STATE_CLOSE;
break;
}
if (opt_comptimeout_ms == 0 || !opt_compiler) {
sp->up = 1;
sp->status = STATE_CLOSE;
break;
}
if (opt_verbose > 0)
fprintf(stderr,
"now %ld %ld: %s: sending compile request\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname);
nsend = canned_query_len;
nsent = write(sp->fd, canned_query, nsend);
if (nsent != nsend) {
if (opt_verbose > 1) {
if (nsent == -1)
fprintf(stderr,
"now %ld %ld: Sending to %s failed, "
"errno %d\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname,
connecterr);
else
fprintf(stderr,
"now %ld %ld: Sending to %s failed, "
"nsent %d != nsend %d\n",
now.tv_sec, (long) now.tv_usec/1000,
sp->req.hname, nsent, nsend);
}
sp->status = STATE_CLOSE;
break;
}
sp->status=STATE_READ_DONEPKT;
sp->curhdrlen = 0;
sp->deadline = now;
sp->deadline.tv_usec += 1000 * opt_comptimeout_ms;
sp->deadline.tv_sec += sp->deadline.tv_usec / 1000000;
sp->deadline.tv_usec = sp->deadline.tv_usec % 1000000;
}
break;
case STATE_READ_DONEPKT:
case STATE_READ_STATPKT:
server_read_packet_header(sp);
break;
case STATE_READ_REST:
{
char buf[1000];
int nread;
nread = read(sp->fd, buf, sizeof(buf));
if (nread == 0) {
sp->up = 1;
sp->status = STATE_CLOSE;
}
}
break;
case STATE_CLOSE:
if (sp->fd != -1) {
close(sp->fd);
sp->fd = -1;
}
if (opt_bang_down || sp->up) {
if (opt_numeric)
printf("%d.%d.%d.%d", sp->res.addr[0], sp->res.addr[1],
sp->res.addr[2], sp->res.addr[3]);
else
printf("%s", sp->req.hname);
if (opt_port != DEFAULT_PORT)
printf(":%d", opt_port);
printf("%s", protocol_suffix[opt_protocol]);
if (opt_bang_down && !sp->up)
printf(",down");
if (opt_latency) {
int latency_ms;
gettimeofday(&now, 0);
latency_ms = (now.tv_usec - sp->start.tv_usec) /
1000 + 1000 * (now.tv_sec - sp->start.tv_sec);
printf(" %d", latency_ms);
}
putchar('\n');
if (opt_verbose)
fflush(stdout);
}
nok++;
sp->status = STATE_DONE;
ndone++;
break;
case STATE_DONE:
;
default:
;
}
} while (sp->status == STATE_CLOSE);
}
static int one_poll_loop(struct rslave_s* rs, struct state_s states[],
int start_state, int end_state,
int nwithtries[], int* ngotaddr, int* nbaddns,
unsigned char firstipaddr[4], int dnstimeout_usec,
int matchbits, int overlap, int dnsgap)
{
int i;
int nfds;
struct state_s *sp;
int nready;
int found;
struct timeval now;
struct pollfd pollfds[MAXFDS];
nfds = 0;
memset(pollfds, 0, sizeof(pollfds));
pollfds[nfds].fd = rslave_getfd_fromSlaves(rs);
pollfds[nfds++].events = POLLIN;
pollfds[nfds].fd = rslave_getfd_toSlaves(rs);
pollfds[nfds++].events = ((nwithtries[1] <= overlap) &&
(nwithtries[1]+
nwithtries[2]+
nwithtries[3]+
nwithtries[4]+
*ngotaddr < end_state)) ? POLLOUT : 0;
for (i=start_state; i<=end_state; i++) {
switch (states[i].status) {
case STATE_CONNECTING:
pollfds[nfds].fd = states[i].fd;
pollfds[nfds++].events = POLLOUT;
break;
case STATE_READ_DONEPKT:
case STATE_READ_STATPKT:
case STATE_READ_REST:
pollfds[nfds].fd = states[i].fd;
pollfds[nfds++].events = POLLIN;
break;
default: ;
}
}
nready = poll(pollfds, (unsigned)nfds, 50);
gettimeofday(&now, 0);
sp = NULL;
found = FALSE;
for (i=start_state; i<=end_state; i++) {
sp = &states[i];
if (sp->status == STATE_LOOKUP
&& sp->ntries > 0 && sp->ntries < MAXTRIES
&& (sp->deadline.tv_sec < now.tv_sec ||
(sp->deadline.tv_sec == now.tv_sec &&
sp->deadline.tv_usec < now.tv_usec))) {
found = TRUE;
nwithtries[sp->ntries]--;
sp->ntries++;
nwithtries[sp->ntries]++;
if (opt_verbose > 0)
fprintf(stderr,
"now %ld %ld: Resending %s because "
"deadline was %ld %ld\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname,
sp->deadline.tv_sec, (long) sp->deadline.tv_usec/1000);
break;
}
if (sp->status == STATE_CONNECTING
&& (sp->deadline.tv_sec < now.tv_sec ||
(sp->deadline.tv_sec == now.tv_sec &&
sp->deadline.tv_usec < now.tv_usec))) {
sp->status = STATE_CLOSE;
server_handle_event(sp);
if (opt_verbose > 0)
fprintf(stderr,
"now %ld %ld: %s timed out while connecting\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname);
}
if ((sp->status == STATE_READ_DONEPKT ||
sp->status == STATE_READ_STATPKT ||
sp->status == STATE_READ_REST)
&& (sp->deadline.tv_sec < now.tv_sec ||
(sp->deadline.tv_sec == now.tv_sec &&
sp->deadline.tv_usec < now.tv_usec))) {
sp->status = STATE_CLOSE;
server_handle_event(sp);
if (opt_verbose > 0)
fprintf(stderr,
"now %ld %ld: %s timed out while compiling\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname);
}
}
if (!found && (nwithtries[1] <= overlap) &&
(pollfds[1].revents & POLLOUT)) {
for (i=start_state; i<=end_state; i++) {
sp = &states[i];
if (sp->status == STATE_LOOKUP && sp->ntries == 0) {
found = TRUE;
nwithtries[sp->ntries]--;
sp->ntries++;
nwithtries[sp->ntries]++;
break;
}
}
}
if (found) {
if (opt_verbose)
fprintf(stderr, "now %ld %ld: Looking up %s\n",
now.tv_sec, (long) now.tv_usec/1000, sp->req.hname);
rslave_writeRequest(rs, &sp->req);
sp->deadline = now;
sp->deadline.tv_usec += dnstimeout_usec;
sp->deadline.tv_sec += sp->deadline.tv_usec / 1000000;
sp->deadline.tv_usec = sp->deadline.tv_usec % 1000000;
}
if (pollfds[0].revents & POLLIN) {
rslave_result_t result;
if (rslave_readResult(rs, &result)) {
printf("bug: can't read from pipe\n");
} else {
assert(result.id >= start_state && result.id <= end_state);
sp = &states[result.id];
if (sp->status == STATE_LOOKUP) {
nwithtries[sp->ntries]--;
sp->res = result;
(*ngotaddr)++;
if (matchbits > 0) {
if (*ngotaddr == 1) {
memcpy(firstipaddr, result.addr, 4);
} else {
if (bitcompare(firstipaddr, result.addr, matchbits))
result.err = -1;
}
}
if (result.err) {
if (opt_verbose)
fprintf(stderr, "now %ld %ld: %s not found\n",
now.tv_sec, (long) now.tv_usec/1000,
sp->req.hname);
sp->status = STATE_DONE;
ndone++;
(*nbaddns)++;
if (*nbaddns > dnsgap) {
int highest = 0;
for (i=start_state; i <= end_state; i++)
if (states[i].ntries > 0)
highest = i;
assert(highest <= end_state);
if (opt_verbose && end_state != highest)
fprintf(stderr,
"Already searching up to host %d, "
"won't search any higher\n",
highest);
end_state = highest;
assert(end_state <= MAXHOSTS);
}
} else {
sp->status = STATE_CONNECT;
server_handle_event(sp);
}
}
}
}
for (i=2; i<nfds && i < MAXFDS; i++) {
sp = states + fd2state[pollfds[i].fd];
if (pollfds[i].revents)
server_handle_event(sp);
}
return end_state;
}
void get_thename(const char**sformat, const char *domain_name, int i,
char *thename)
{
if (strstr(sformat[0], "%d") != NULL)
sprintf(thename, sformat[0], i);
else
strcpy(thename, sformat[i-1]);
if (opt_domain) {
strcat(thename, ".");
strcat(thename, domain_name);
}
}
int detect_distcc_servers(const char **argv, int argc, int opti,
int bigtimeout, int dnstimeout,
int matchbits, int overlap, int dnsgap)
{
unsigned char firstipaddr[4];
int dnstimeout_usec = dnstimeout * 1000;
int i;
int n = MAXHOSTS;
int maxfds = MAXHOSTS + 10;
char thename[256];
struct state_s states[MAXHOSTS+1];
int start_state, end_state;
int ngotaddr;
int nbaddns;
int nwithtries[MAXTRIES+1];
struct rslave_s rs;
const char *default_format = DEFAULT_FORMAT;
const char **sformat = &default_format;
const char *domain_name;
if (opt_domain) {
if (dcc_get_dns_domain(&domain_name)) {
fprintf(stderr, "Can't get domain name\n");
exit(1);
}
}
if (opti < argc) {
if (strstr(argv[opti], "%d") != NULL) {
sformat = &argv[opti++];
} else {
n = argc-opti;
sformat = &argv[opti++];
}
}
struct rlimit rlim = {0, 0};
getrlimit(RLIMIT_NOFILE, &rlim);
if (rlim.rlim_cur < (rlim_t)n + 10) {
rlim.rlim_cur = (rlim_t)n + 10;
if (rlim.rlim_cur > rlim.rlim_max)
rlim.rlim_cur = rlim.rlim_max;
setrlimit(RLIMIT_NOFILE, &rlim);
getrlimit(RLIMIT_NOFILE, &rlim);
if (rlim.rlim_cur > 14)
maxfds = (int)(rlim.rlim_cur - 10);
}
signal(SIGALRM, timeout_handler);
alarm((unsigned) bigtimeout);
if (rslave_init(&rs))
return 0;
ngotaddr = 0;
memset(nwithtries, 0, sizeof(nwithtries));
memset(states, 0, sizeof(states));
for (i=1; i<=n; i++) {
rslave_request_t *req = &states[i].req;
get_thename(sformat, domain_name, i, thename);
rslave_request_init(req, thename, i);
states[i].status = STATE_LOOKUP;
states[i].ntries = 0;
nwithtries[0]++;
}
ndone = 0;
nok = 0;
nbaddns = 0;
for (start_state = 1; start_state <= n; start_state = end_state + 1) {
int orig_end_state;
end_state = start_state + maxfds-2;
if (end_state > n)
end_state = n;
orig_end_state = end_state;
while (ndone < end_state) {
end_state = one_poll_loop(&rs, states, start_state, end_state,
nwithtries, &ngotaddr, &nbaddns,
firstipaddr, dnstimeout_usec,
matchbits, overlap, dnsgap);
}
if (end_state < orig_end_state) {
break;
}
}
return nok;
}
int main(int argc, char **argv)
{
int opti;
int nfound;
for (opti = 1; opti < argc && argv[opti][0] == '-'; opti++) {
switch (argv[opti][1]) {
case 'm':
opt_match = atoi(argv[opti]+2);
if (opt_match > 31 || opt_match < 0)
usage();
break;
case 't':
opt_bigtimeout_sec = atoi(argv[opti]+2);
if (opt_bigtimeout_sec < 0)
usage();
break;
case 'h':
opt_dnstimeout_ms = atoi(argv[opti]+2);
if (opt_dnstimeout_ms < 0)
usage();
break;
case 'c':
opt_conntimeout_ms = atoi(argv[opti]+2);
if (opt_conntimeout_ms < 0)
usage();
break;
case 'k':
opt_comptimeout_ms = atoi(argv[opti]+2);
if (opt_comptimeout_ms < 0)
usage();
break;
case 'o':
opt_overlap = atoi(argv[opti]+2);
if (opt_overlap < 0)
usage();
break;
case 'g':
opt_dnsgap = atoi(argv[opti]+2);
if (opt_dnsgap < 0)
usage();
break;
case 'P':
opt_protocol = atoi(argv[opti]+2);
if (opt_protocol <= 0 || opt_protocol > 3) {
usage();
}
break;
case 'p':
opt_compiler = argv[opti]+2;
if (! *opt_compiler)
usage();
break;
case 'r':
opt_port = atoi(argv[opti]+2);
if (opt_port <= 0)
usage();
break;
case 'l':
opt_latency = 1;
break;
case 'n':
opt_numeric = 1;
break;
case 'x':
opt_bang_down = 1;
break;
case 'v':
opt_verbose++;
break;
case 'd':
opt_domain++;
break;
default:
usage();
}
}
if (opt_compiler)
generate_query();
nfound = detect_distcc_servers((const char **)argv, argc, opti,
opt_bigtimeout_sec,
opt_dnstimeout_ms,
opt_match,
opt_overlap,
opt_dnsgap);
return (nfound > 0) ? 0 : 1;
}