in nailgun-client/c/ng.c [653:907]
int main(int argc, char *argv[], char *env[]) {
int i;
struct sockaddr *server_addr;
socklen_t server_addr_len;
struct sockaddr_in server_addr_in;
#ifndef WIN32
struct sockaddr_un server_addr_un;
#endif
char *nailgun_server; /* server as specified by user */
char *nailgun_port; /* port as specified by user */
char *cwd;
u_short port; /* port */
struct hostent *hostinfo;
char *cmd;
int firstArgIndex; /* the first argument _to pass to the server_ */
char isattybuf[] = NAILGUN_TTY_FORMAT;
#ifndef WIN32
fd_set readfds;
int eof = 0;
struct timeval readtimeout;
struct timeval currenttime;
memset(&sendtime, '\0', sizeof(sendtime));
#endif
#ifdef WIN32
initSockets();
#endif
/* start with environment variable. default to localhost if not defined. */
nailgun_server = getenv("NAILGUN_SERVER");
if (nailgun_server == NULL) {
nailgun_server = "127.0.0.1";
}
/* start with environment variable. default to normal nailgun port if not defined */
nailgun_port = getenv("NAILGUN_PORT");
if (nailgun_port == NULL) {
nailgun_port = NAILGUN_PORT_DEFAULT;
}
/* look at the command used to launch this program. if it was "ng", then the actual
command to issue to the server must be specified as another argument. if it
wasn't ng, assume that the desired command name was symlinked to ng in the user's
filesystem, and use the symlink name (without path info) as the command for the server. */
cmd = shortClientName(argv[0]);
if (isNailgunClientName(cmd)) {
cmd = NULL;
}
/* if executing just the ng client with no arguments or -h|--help, then
display usage and exit. Don't handle -h|--help if a command other than
ng or ng.exe was used, since the appropriate nail should then handle
--help. */
if (cmd == NULL &&
(argc == 1 ||
(argc == 2 && strcmp("--help", argv[1]) == 0) ||
(argc == 2 && strcmp("-h", argv[1]) == 0))) usage(0);
firstArgIndex = 1;
/* quite possibly the lamest commandline parsing ever.
look for the two args we care about (--nailgun-server and
--nailgun-port) and NULL them and their parameters after
reading them if found. later, when we send args to the
server, skip the null args. */
for (i = 1; i < argc; ++i) {
if (!strcmp("--nailgun-server", argv[i])) {
if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS);
nailgun_server = argv[i + 1];
argv[i] = argv[i + 1] = NULL;
++i;
} else if(!strcmp("--nailgun-port", argv[i])) {
if (i == argc - 1) usage(NAILGUN_BAD_ARGUMENTS);
nailgun_port = argv[i + 1];
argv[i] = argv[i + 1]= NULL;
++i;
} else if (!strcmp("--nailgun-filearg", argv[i])) {
/* just verify usage here. do the rest when sending args. */
if (i == argc - 1) usage (NAILGUN_BAD_ARGUMENTS);
} else if (!strcmp("--nailgun-version", argv[i])) {
printf("NailGun client version %s\n", NAILGUN_VERSION);
cleanUpAndExit(0);
} else if (!strcmp("--nailgun-showversion", argv[i])) {
printf("NailGun client version %s\n", NAILGUN_VERSION);
argv[i] = NULL;
} else if (!strcmp("--nailgun-help", argv[i])) {
usage(0);
} else if (cmd == NULL) {
cmd = argv[i];
firstArgIndex = i + 1;
}
}
/* if there's no command, we should only display usage info
if the version number was not displayed. */
if (cmd == NULL) {
usage(NAILGUN_BAD_ARGUMENTS);
}
#ifndef WIN32
if (strncmp(nailgun_server, "local:", 6) == 0) {
const char *socket_path = nailgun_server + 6;
size_t socket_path_len = strlen(socket_path);
if (socket_path_len > sizeof(server_addr_un.sun_path) - 1) {
fprintf(stderr, "Socket path [%s] too long (%ld)\n", socket_path, (long) socket_path_len);
cleanUpAndExit(NAILGUN_SOCKET_FAILED);
}
if ((nailgunsocket = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) {
perror("socket");
cleanUpAndExit(NAILGUN_SOCKET_FAILED);
}
server_addr_un.sun_family = AF_LOCAL;
strncpy(server_addr_un.sun_path, socket_path, socket_path_len);
server_addr_un.sun_path[socket_path_len] = '\0';
#ifdef BSD
server_addr_un.sun_len = offsetof(struct sockaddr_un, sun_path) + socket_path_len;
#endif
server_addr = (struct sockaddr *)&server_addr_un;
server_addr_len = sizeof(server_addr_un);
} else {
#endif
/* jump through a series of connection hoops */
hostinfo = gethostbyname(nailgun_server);
if (hostinfo == NULL) {
fprintf(stderr, "Unknown host: %s\n", nailgun_server);
cleanUpAndExit(NAILGUN_CONNECT_FAILED);
}
port = atoi(nailgun_port);
if ((nailgunsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
cleanUpAndExit(NAILGUN_SOCKET_FAILED);
}
server_addr_in.sin_family = AF_INET;
server_addr_in.sin_port = htons(port);
server_addr_in.sin_addr = *(struct in_addr *) hostinfo->h_addr;
memset(&(server_addr_in.sin_zero), '\0', 8);
server_addr = (struct sockaddr *)&server_addr_in;
server_addr_len = sizeof(server_addr_in);
#ifndef WIN32
}
#endif
#ifndef MSG_NOSIGNAL
#ifdef SO_NOSIGPIPE
int option_value = 1;
if (setsockopt(nailgunsocket, SOL_SOCKET, SO_NOSIGPIPE, &option_value, sizeof(option_value)) < 0) {
perror("setsockopt");
}
#endif
#endif
if (connect(nailgunsocket, server_addr, server_addr_len) == -1) {
perror("connect");
cleanUpAndExit(NAILGUN_CONNECT_FAILED);
}
/* ok, now we're connected. first send all of the command line
arguments for the server, if any. remember that we may have
marked some arguments NULL if we read them to specify the
nailgun server and/or port */
for(i = firstArgIndex; i < argc; ++i) {
if (argv[i] != NULL) {
if (!strcmp("--nailgun-filearg", argv[i])) {
int sendResult = sendFileArg(argv[++i]);
if (sendResult != 0) {
perror("send");
handleSocketClose();
}
} else sendText(CHUNKTYPE_ARG, argv[i]);
}
}
/* now send environment */
sendText(CHUNKTYPE_ENV, NAILGUN_FILESEPARATOR);
sendText(CHUNKTYPE_ENV, NAILGUN_PATHSEPARATOR);
#ifndef WIN32
/* notify isatty for standard pipes */
for(i = 0; i < 3; i++) {
sprintf(isattybuf, NAILGUN_TTY_FORMAT, i, isatty(i));
sendText(CHUNKTYPE_ENV, isattybuf);
}
#endif
/* forward the client process environment */
for(i = 0; env[i]; ++i) {
sendText(CHUNKTYPE_ENV, env[i]);
}
/* now send the working directory */
cwd = getcwd(NULL, 0);
sendText(CHUNKTYPE_DIR, cwd);
free(cwd);
/* and finally send the command. this marks the point at which
streams are linked between client and server. */
sendText(CHUNKTYPE_CMD, cmd);
/* initialise the std-* handles and the thread to send stdin to the server */
#ifdef WIN32
initIo();
winStartInput();
#endif
/* stream forwarding loop */
while(1) {
#ifndef WIN32
FD_ZERO(&readfds);
/* don't select on stdin if we've already reached its end */
if (readyToSend && !eof) {
FD_SET(NG_STDIN_FILENO, &readfds);
}
FD_SET(nailgunsocket, &readfds);
memset(&readtimeout, '\0', sizeof(readtimeout));
readtimeout.tv_usec = HEARTBEAT_TIMEOUT_MILLIS * 1000;
if(select (nailgunsocket + 1, &readfds, NULL, NULL, &readtimeout) == -1) {
perror("select");
}
if (FD_ISSET(nailgunsocket, &readfds)) {
#endif
processnailgunstream();
#ifndef WIN32
} else if (FD_ISSET(NG_STDIN_FILENO, &readfds)) {
int result = processStdin();
if (result == -1) {
perror("read");
handleSocketClose();
} else if (result == 0) {
FD_CLR(NG_STDIN_FILENO, &readfds);
eof = 1;
}
}
gettimeofday(¤ttime, NULL);
if (intervalMillis(currenttime, sendtime) > HEARTBEAT_TIMEOUT_MILLIS) {
sendHeartbeat();
}
#endif
}
/* normal termination is triggered by the server, and so occurs in processExit(), above */
}