/*
* client program to read data from server
*
* based on proxy.c
* $Id: client.c,v 1.5 2003/11/28 20:48:23 ca Exp $
*/
/*
* Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Silicon Graphics, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "sm/generic.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "st.h"
#if !HAVE_SNPRINTF
# define snprintf sm_snprintf
# include "sm/string.h"
#endif /* !HAVE_SNPRINTF */
#define IOBUFSIZE (16*1024)
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
#define MAXTC 512L /* max. number of total connections */
#define REQUEST_TIMEOUT 3
#define SEC2USEC(s) ((s)*1000000LL)
static char *prog; /* Program name */
static struct sockaddr_in rmt_addr; /* Remote address */
static int conns;
static int debug = 0;
static int busy = 0;
static long int total = 0;
static void read_address(const char *str, struct sockaddr_in *sin);
static void *handle_request(void *arg);
static void print_sys_error(const char *msg);
/*
* This program acts as client for the server program.
* It connects to the specified remote address ('-r' option) and
* asks for data.
*/
int main(int argc, char *argv[])
{
extern char *optarg;
int opt, n, threads;
int raddr;
long int tc;
prog = argv[0];
raddr = 0;
conns = threads = 1;
/* Parse arguments */
while((opt = getopt(argc, argv, "c:d:hr:t:S")) != -1) {
switch (opt) {
case 'c':
conns = atoi(optarg);
if (conns < 1 || conns > 100) {
fprintf(stderr, "%s: invalid number of connections: %s\n", prog, optarg);
exit(1);
}
break;
case 'd':
debug = atoi(optarg);
if (debug < 0) {
fprintf(stderr, "%s: invalid number for debug: %s\n", prog, optarg);
exit(1);
}
break;
case 'r':
read_address(optarg, &rmt_addr);
if (rmt_addr.sin_addr.s_addr == INADDR_ANY) {
fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg);
exit(1);
}
raddr = 1;
break;
case 't':
threads = atoi(optarg);
if (threads < 1 || threads > 100) {
fprintf(stderr, "%s: invalid number of threads: %s\n", prog, optarg);
exit(1);
}
break;
case 'h':
case '?':
fprintf(stderr, "Usage: %s -l <[host]:port> -r <host:port> "
"[-p <num_processes>] [-S]\n", prog);
exit(1);
}
}
if (!raddr) {
fprintf(stderr, "%s: remote address required\n", prog);
exit(1);
}
tc = (long) threads * (long) conns;
if (tc > MAXTC) {
fprintf(stderr, "%s: too many total connections (%ld > %ld)\n", prog,
tc, MAXTC);
exit(1);
}
if (debug)
fprintf(stderr, "%s: starting client [%d]\n", prog, threads);
/* Initialize the ST library */
if (st_init() < 0) {
print_sys_error("st_init");
exit(1);
}
for (n = 0; n < threads; n++)
{
if (debug)
fprintf(stderr, "%s: starting client %d/%d\n", prog, n, threads);
if (st_thread_create(handle_request, (void *) n, 0, 0) == NULL) {
print_sys_error("st_thread_create");
exit(1);
}
}
/* wait for them... */
st_sleep(1);
while (busy > 0)
st_sleep(1);
/* XXX how? */
fprintf(stderr, "%s: total=%ld (should be %ld)\n", prog, total,
(long) threads * (long) conns);
return 0;
}
static void read_address(const char *str, struct sockaddr_in *sin)
{
char host[128], *p;
struct hostent *hp;
short port;
strlcpy(host, str, sizeof(host));
if ((p = strchr(host, ':')) == NULL) {
fprintf(stderr, "%s: invalid address: %s\n", prog, host);
exit(1);
}
*p++ = '\0';
port = (short) atoi(p);
if (port < 1) {
fprintf(stderr, "%s: invalid port: %s\n", prog, p);
exit(1);
}
memset(sin, 0, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
if (host[0] == '\0') {
sin->sin_addr.s_addr = INADDR_ANY;
return;
}
sin->sin_addr.s_addr = inet_addr(host);
if (sin->sin_addr.s_addr == INADDR_NONE) {
/* not dotted-decimal */
if ((hp = gethostbyname(host)) == NULL) {
fprintf(stderr, "%s: can't resolve address: %s\n", prog, host);
exit(1);
}
memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
}
}
static void *handle_request(void *arg)
{
struct pollfd pds[1];
st_netfd_t rmt_nfd;
int sock, n, i, tid, r;
char buf[IOBUFSIZE];
++busy;
tid = (int) arg;
if (debug)
fprintf(stderr, "client[%d]: conns=%d\n", tid, conns);
/*
* currently the server only answers one request...
*
* hence this breaks for too many connections since too many sckets
* are opened
*/
for (i = 0; i < conns; i++) {
/* Connect to remote host */
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
snprintf(buf, sizeof(buf), "[%d] socket i=%d", tid, i);
print_sys_error(buf);
goto done;
}
if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) {
snprintf(buf, sizeof(buf), "[%d] st_netfd_open_socket i=%d", tid, i);
print_sys_error(buf);
close(sock);
goto done;
}
if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr,
sizeof(rmt_addr), SEC2USEC(REQUEST_TIMEOUT)) < 0) {
snprintf(buf, sizeof(buf), "[%d] connect i=%d", tid, i);
print_sys_error(buf);
st_netfd_close(rmt_nfd);
goto done;
}
pds[0].fd = sock;
pds[0].events = POLLIN;
if (debug > 2)
fprintf(stderr, "client[%d]: connected (%d/%d)\n", tid, i, conns);
n = strlcpy(buf, "gimme\n\r", sizeof(buf));
if ((r = st_write(rmt_nfd, buf, n, SEC2USEC(REQUEST_TIMEOUT))) != n)
{
fprintf(stderr, "[%d] server went away? i=%d, n=%d, r=%d, errno=%d\n",
tid, i, n, r, errno);
break;
}
for ( ; ; ) {
if (st_poll(pds, 1, -1) <= 0) {
print_sys_error("st_poll");
break;
}
if (pds[0].revents & POLLIN) {
if ((n = (int) st_read(rmt_nfd, buf, IOBUFSIZE, -1)) <= 0)
break;
}
}
++total;
st_netfd_close(rmt_nfd);
}
done:
--busy;
return NULL;
}
static void print_sys_error(const char *msg)
{
fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno));
}
syntax highlighted by Code2HTML, v. 0.9.1