mirror of https://github.com/ossrs/srs.git
Merge branch 'develop' into merge/develop
commit
3f3c62b673
@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
PWD=$(cd `dirname $0`/.. && pwd)
|
||||
|
||||
pushd $PWD
|
||||
echo "Run UTest in $(pwd)"
|
||||
|
||||
IS_LINUX=yes
|
||||
uname -s|grep Darwin >/dev/null && IS_DARWIN=yes && IS_LINUX=no
|
||||
echo "IS_LINUX: $IS_LINUX, IS_DARWIN: $IS_DARWIN"
|
||||
|
||||
echo "Clean gcda files"
|
||||
rm -f ./obj/*.gcda
|
||||
|
||||
echo "Build and run utest"
|
||||
if [[ $IS_DARWIN == yes ]]; then
|
||||
make darwin-debug-gcov && ./obj/st_utest
|
||||
else
|
||||
make linux-debug-gcov && ./obj/st_utest
|
||||
fi
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then echo "Make ST utest fail, ret=$ret"; exit $ret; fi
|
||||
|
||||
echo "Generating coverage"
|
||||
mkdir -p coverage &&
|
||||
gcovr -r . -e LINUX -e DARWIN -e examples --html --html-details -o coverage/st.html &&
|
||||
echo "Coverage report at coverage/st.html" &&
|
||||
open coverage/st.html
|
||||
|
||||
popd
|
||||
echo "UTest done, restore $(pwd)"
|
@ -1,115 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
##########################
|
||||
# Supported OSes:
|
||||
#
|
||||
# AIX
|
||||
# FREEBSD
|
||||
# HPUX
|
||||
# HPUX_64
|
||||
# IRIX
|
||||
# IRIX_64
|
||||
# LINUX
|
||||
# LINUX_IA64
|
||||
# NETBSD
|
||||
# OPENBSD
|
||||
# OSF1
|
||||
# SOLARIS
|
||||
# SOLARIS_64
|
||||
|
||||
##########################
|
||||
|
||||
CC = cc
|
||||
|
||||
SHELL = /bin/sh
|
||||
ECHO = /bin/echo
|
||||
|
||||
DEPTH = ..
|
||||
BUILD =
|
||||
TARGETDIR =
|
||||
|
||||
DEFINES =
|
||||
CFLAGS =
|
||||
OTHER_FLAGS =
|
||||
|
||||
OBJDIR = $(DEPTH)/$(TARGETDIR)
|
||||
INCDIR = $(DEPTH)/$(TARGETDIR)
|
||||
LIBST = $(OBJDIR)/libst.a
|
||||
HEADER = $(INCDIR)/st.h
|
||||
|
||||
LIBRESOLV =
|
||||
EXTRALIBS =
|
||||
|
||||
ifeq ($(OS),)
|
||||
EXAMPLES = unknown
|
||||
else
|
||||
EXAMPLES = $(OBJDIR)/lookupdns $(OBJDIR)/proxy $(OBJDIR)/server
|
||||
endif
|
||||
|
||||
|
||||
##########################
|
||||
# Platform section.
|
||||
#
|
||||
|
||||
ifeq (DARWIN, $(findstring DARWIN, $(OS)))
|
||||
LIBRESOLV = -lresolv
|
||||
endif
|
||||
|
||||
ifeq (LINUX, $(findstring LINUX, $(OS)))
|
||||
LIBRESOLV = -lresolv
|
||||
endif
|
||||
|
||||
ifeq (SOLARIS, $(findstring SOLARIS, $(OS)))
|
||||
LIBRESOLV = -lresolv
|
||||
EXTRALIBS = -lsocket -lnsl
|
||||
endif
|
||||
|
||||
#
|
||||
# End of platform section.
|
||||
##########################
|
||||
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
$(OBJDIR)/lookupdns: lookupdns.c $(OBJDIR)/res.o $(LIBST) $(HEADER)
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) lookupdns.c $(OBJDIR)/res.o $(LIBST) $(LIBRESOLV) $(EXTRALIBS) -o $@
|
||||
|
||||
$(OBJDIR)/proxy: proxy.c $(LIBST) $(HEADER)
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) proxy.c $(LIBST) $(EXTRALIBS) -o $@
|
||||
|
||||
$(OBJDIR)/server: server.c $(OBJDIR)/error.o $(LIBST) $(HEADER)
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) server.c $(OBJDIR)/error.o $(LIBST) $(EXTRALIBS) -o $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c
|
||||
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
|
||||
|
||||
.DEFAULT:
|
||||
@cd $(DEPTH); $(MAKE) $@
|
||||
|
@ -1,98 +0,0 @@
|
||||
Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
|
||||
All Rights Reserved.
|
||||
|
||||
|
||||
This directory contains three example programs.
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
PROGRAM
|
||||
|
||||
lookupdns
|
||||
|
||||
FILES
|
||||
|
||||
lookupdns.c
|
||||
res.c
|
||||
|
||||
USAGE
|
||||
|
||||
lookupdns <hostname1> [<hostname2>] ...
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This program performs asynchronous DNS host name resolution and reports
|
||||
IP address for each <hostname> specified as a command line argument.
|
||||
One ST thread is created for each host name. All threads do host name
|
||||
resolution concurrently.
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
PROGRAM
|
||||
|
||||
proxy
|
||||
|
||||
FILES
|
||||
|
||||
proxy.c
|
||||
|
||||
USAGE
|
||||
|
||||
proxy -l <local_addr> -r <remote_addr> [-p <num_processes>] [-S]
|
||||
|
||||
-l <local_addr> bind to local address specified as [<host>]:<port>
|
||||
-r <remote_addr> connect to remote address specified as <host>:<port>
|
||||
-p <num_processes> create specified number of processes
|
||||
-S serialize accept() calls from different processes
|
||||
on the same listening socket (if needed).
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This program acts as a generic gateway. It listens for connections to a
|
||||
local address. Upon accepting a client connection, it connects to the
|
||||
specified remote address and then just pumps the data through without any
|
||||
modification.
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
PROGRAM
|
||||
|
||||
server
|
||||
|
||||
FILES
|
||||
|
||||
server.c
|
||||
error.c
|
||||
|
||||
USAGE
|
||||
|
||||
server -l <log_directory> [<options>]
|
||||
|
||||
-l <log_directory> open all log files in specified directory.
|
||||
|
||||
Possible options:
|
||||
|
||||
-b <host>:<port> bind to specified address (multiple addresses
|
||||
are permitted)
|
||||
-p <num_processes> create specified number of processes
|
||||
-t <min_thr>:<max_thr> specify thread limits per listening socket
|
||||
across all processes
|
||||
-u <user> change server's user id to specified value
|
||||
-q <backlog> set max length of pending connections queue
|
||||
-a enable access logging
|
||||
-i run in interactive mode (useful for debugging)
|
||||
-S serialize accept() calls from different processes
|
||||
on the same listening socket (if needed).
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This program is a general server example. It accepts a client connection
|
||||
and outputs a short HTML page. It can be easily adapted to provide
|
||||
other services.
|
||||
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* 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 <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "st.h"
|
||||
|
||||
/*
|
||||
* Simple error reporting functions.
|
||||
* Suggested in W. Richard Stevens' "Advanced Programming in UNIX
|
||||
* Environment".
|
||||
*/
|
||||
|
||||
#define MAXLINE 4096 /* max line length */
|
||||
|
||||
static void err_doit(int, int, const char *, va_list);
|
||||
|
||||
|
||||
/*
|
||||
* Nonfatal error related to a system call.
|
||||
* Print a message and return.
|
||||
*/
|
||||
void err_sys_report(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(fd, 1, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fatal error related to a system call.
|
||||
* Print a message and terminate.
|
||||
*/
|
||||
void err_sys_quit(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(fd, 1, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fatal error related to a system call.
|
||||
* Print a message, dump core, and terminate.
|
||||
*/
|
||||
void err_sys_dump(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(fd, 1, fmt, ap);
|
||||
va_end(ap);
|
||||
abort(); /* dump core and terminate */
|
||||
exit(1); /* shouldn't get here */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Nonfatal error unrelated to a system call.
|
||||
* Print a message and return.
|
||||
*/
|
||||
void err_report(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(fd, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fatal error unrelated to a system call.
|
||||
* Print a message and terminate.
|
||||
*/
|
||||
void err_quit(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err_doit(fd, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a pointer to a string containing current time.
|
||||
*/
|
||||
char *err_tstamp(void)
|
||||
{
|
||||
static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
static char str[32];
|
||||
static time_t lastt = 0;
|
||||
struct tm *tmp;
|
||||
time_t currt = st_time();
|
||||
|
||||
if (currt == lastt)
|
||||
return str;
|
||||
|
||||
tmp = localtime(&currt);
|
||||
sprintf(str, "[%02d/%s/%d:%02d:%02d:%02d] ", tmp->tm_mday,
|
||||
months[tmp->tm_mon], 1900 + tmp->tm_year, tmp->tm_hour,
|
||||
tmp->tm_min, tmp->tm_sec);
|
||||
lastt = currt;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Print a message and return to caller.
|
||||
* Caller specifies "errnoflag".
|
||||
*/
|
||||
static void err_doit(int fd, int errnoflag, const char *fmt, va_list ap)
|
||||
{
|
||||
int errno_save;
|
||||
char buf[MAXLINE];
|
||||
|
||||
errno_save = errno; /* value caller might want printed */
|
||||
strcpy(buf, err_tstamp()); /* prepend a message with time stamp */
|
||||
vsprintf(buf + strlen(buf), fmt, ap);
|
||||
if (errnoflag)
|
||||
sprintf(buf + strlen(buf), ": %s\n", strerror(errno_save));
|
||||
else
|
||||
strcat(buf, "\n");
|
||||
write(fd, buf, strlen(buf));
|
||||
errno = errno_save;
|
||||
}
|
||||
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include "st.h"
|
||||
|
||||
#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)
|
||||
#define NETDB_INTERNAL h_NETDB_INTERNAL
|
||||
#endif
|
||||
|
||||
/* Resolution timeout (in microseconds) */
|
||||
#define TIMEOUT (2*1000000LL)
|
||||
|
||||
/* External function defined in the res.c file */
|
||||
int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout);
|
||||
|
||||
|
||||
void *do_resolve(void *host)
|
||||
{
|
||||
struct in_addr addr;
|
||||
|
||||
/* Use dns_getaddr() instead of gethostbyname(3) to get IP address */
|
||||
if (dns_getaddr(host, &addr, TIMEOUT) < 0) {
|
||||
fprintf(stderr, "dns_getaddr: can't resolve %s: ", (char *)host);
|
||||
if (h_errno == NETDB_INTERNAL)
|
||||
perror("");
|
||||
else
|
||||
herror("");
|
||||
} else
|
||||
printf("%-40s %s\n", (char *)host, inet_ntoa(addr));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Asynchronous DNS host name resolution. This program creates one
|
||||
* ST thread for each host name (specified as command line arguments).
|
||||
* All threads do host name resolution concurrently.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s <hostname1> [<hostname2>] ...\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (st_init() < 0) {
|
||||
perror("st_init");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
/* Create a separate thread for each host name */
|
||||
if (st_thread_create(do_resolve, argv[i], 0, 0) == NULL) {
|
||||
perror("st_thread_create");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
st_thread_exit(NULL);
|
||||
|
||||
/* NOTREACHED */
|
||||
return 1;
|
||||
}
|
||||
|
@ -1,541 +0,0 @@
|
||||
/*
|
||||
* 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 <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"
|
||||
|
||||
#define IOBUFSIZE (16*1024)
|
||||
|
||||
#define IOV_LEN 256
|
||||
#define IOV_COUNT (IOBUFSIZE / IOV_LEN)
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
#define INADDR_NONE 0xffffffff
|
||||
#endif
|
||||
|
||||
static char *prog; /* Program name */
|
||||
static struct sockaddr_in rmt_addr; /* Remote address */
|
||||
|
||||
static unsigned long testing;
|
||||
#define TESTING_VERBOSE 0x1
|
||||
#define TESTING_READV 0x2
|
||||
#define TESTING_READ_RESID 0x4
|
||||
#define TESTING_WRITEV 0x8
|
||||
#define TESTING_WRITE_RESID 0x10
|
||||
|
||||
static void read_address(const char *str, struct sockaddr_in *sin);
|
||||
static void start_daemon(void);
|
||||
static int cpu_count(void);
|
||||
static void set_concurrency(int nproc);
|
||||
static void *handle_request(void *arg);
|
||||
static void print_sys_error(const char *msg);
|
||||
|
||||
|
||||
/*
|
||||
* This program acts as a generic gateway. It listens for connections
|
||||
* to a local address ('-l' option). Upon accepting a client connection,
|
||||
* it connects to the specified remote address ('-r' option) and then
|
||||
* just pumps the data through without any modification.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
extern char *optarg;
|
||||
int opt, sock, n;
|
||||
int laddr, raddr, num_procs, alt_ev, one_process;
|
||||
int serialize_accept = 0;
|
||||
struct sockaddr_in lcl_addr, cli_addr;
|
||||
st_netfd_t cli_nfd, srv_nfd;
|
||||
|
||||
prog = argv[0];
|
||||
num_procs = laddr = raddr = alt_ev = one_process = 0;
|
||||
|
||||
/* Parse arguments */
|
||||
while((opt = getopt(argc, argv, "l:r:p:Saht:X")) != EOF) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
alt_ev = 1;
|
||||
break;
|
||||
case 'l':
|
||||
read_address(optarg, &lcl_addr);
|
||||
laddr = 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 'p':
|
||||
num_procs = atoi(optarg);
|
||||
if (num_procs < 1) {
|
||||
fprintf(stderr, "%s: invalid number of processes: %s\n", prog, optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
/*
|
||||
* Serialization decision is tricky on some platforms. For example,
|
||||
* Solaris 2.6 and above has kernel sockets implementation, so supposedly
|
||||
* there is no need for serialization. The ST library may be compiled
|
||||
* on one OS version, but used on another, so the need for serialization
|
||||
* should be determined at run time by the application. Since it's just
|
||||
* an example, the serialization decision is left up to user.
|
||||
* Only on platforms where the serialization is never needed on any OS
|
||||
* version st_netfd_serialize_accept() is a no-op.
|
||||
*/
|
||||
serialize_accept = 1;
|
||||
break;
|
||||
case 't':
|
||||
testing = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case 'X':
|
||||
one_process = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
fprintf(stderr, "Usage: %s [options] -l <[host]:port> -r <host:port>\n",
|
||||
prog);
|
||||
fprintf(stderr, "options are:\n");
|
||||
fprintf(stderr, " -p <num_processes> number of parallel processes\n");
|
||||
fprintf(stderr, " -S serialize accepts\n");
|
||||
fprintf(stderr, " -a use alternate event system\n");
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, " -t mask testing/debugging mode\n");
|
||||
fprintf(stderr, " -X one process, don't daemonize\n");
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (!laddr) {
|
||||
fprintf(stderr, "%s: local address required\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
if (!raddr) {
|
||||
fprintf(stderr, "%s: remote address required\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
if (num_procs == 0)
|
||||
num_procs = cpu_count();
|
||||
|
||||
fprintf(stderr, "%s: starting proxy daemon on %s:%d\n", prog,
|
||||
inet_ntoa(lcl_addr.sin_addr), ntohs(lcl_addr.sin_port));
|
||||
|
||||
/* Start the daemon */
|
||||
if (one_process)
|
||||
num_procs = 1;
|
||||
else
|
||||
start_daemon();
|
||||
|
||||
if (alt_ev)
|
||||
st_set_eventsys(ST_EVENTSYS_ALT);
|
||||
|
||||
/* Initialize the ST library */
|
||||
if (st_init() < 0) {
|
||||
print_sys_error("st_init");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Create and bind listening socket */
|
||||
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
print_sys_error("socket");
|
||||
exit(1);
|
||||
}
|
||||
n = 1;
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) {
|
||||
print_sys_error("setsockopt");
|
||||
exit(1);
|
||||
}
|
||||
if (bind(sock, (struct sockaddr *)&lcl_addr, sizeof(lcl_addr)) < 0) {
|
||||
print_sys_error("bind");
|
||||
exit(1);
|
||||
}
|
||||
listen(sock, 128);
|
||||
if ((srv_nfd = st_netfd_open_socket(sock)) == NULL) {
|
||||
print_sys_error("st_netfd_open");
|
||||
exit(1);
|
||||
}
|
||||
/* See the comment regarding serialization decision above */
|
||||
if (num_procs > 1 && serialize_accept && st_netfd_serialize_accept(srv_nfd)
|
||||
< 0) {
|
||||
print_sys_error("st_netfd_serialize_accept");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Start server processes */
|
||||
if (!one_process)
|
||||
set_concurrency(num_procs);
|
||||
|
||||
for ( ; ; ) {
|
||||
n = sizeof(cli_addr);
|
||||
cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&cli_addr, &n,
|
||||
ST_UTIME_NO_TIMEOUT);
|
||||
if (cli_nfd == NULL) {
|
||||
print_sys_error("st_accept");
|
||||
exit(1);
|
||||
}
|
||||
if (st_thread_create(handle_request, cli_nfd, 0, 0) == NULL) {
|
||||
print_sys_error("st_thread_create");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static void read_address(const char *str, struct sockaddr_in *sin)
|
||||
{
|
||||
char host[128], *p;
|
||||
struct hostent *hp;
|
||||
unsigned short port;
|
||||
|
||||
strcpy(host, str);
|
||||
if ((p = strchr(host, ':')) == NULL) {
|
||||
fprintf(stderr, "%s: invalid address: %s\n", prog, host);
|
||||
exit(1);
|
||||
}
|
||||
*p++ = '\0';
|
||||
port = (unsigned 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);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void show_iov(const struct iovec *iov, int niov)
|
||||
{
|
||||
int i;
|
||||
size_t total;
|
||||
|
||||
printf("iov %p has %d entries:\n", iov, niov);
|
||||
total = 0;
|
||||
for (i = 0; i < niov; i++) {
|
||||
printf("iov[%3d] iov_base=%p iov_len=0x%lx(%lu)\n",
|
||||
i, iov[i].iov_base, (unsigned long) iov[i].iov_len,
|
||||
(unsigned long) iov[i].iov_len);
|
||||
total += iov[i].iov_len;
|
||||
}
|
||||
printf("total 0x%lx(%ld)\n", (unsigned long) total, (unsigned long) total);
|
||||
}
|
||||
|
||||
/*
|
||||
* This version is tricked out to test all the
|
||||
* st_(read|write)v?(_resid)? variants. Use the non-DEBUG version for
|
||||
* anything serious. st_(read|write) are all this function really
|
||||
* needs.
|
||||
*/
|
||||
static int pass(st_netfd_t in, st_netfd_t out)
|
||||
{
|
||||
char buf[IOBUFSIZE];
|
||||
struct iovec iov[IOV_COUNT];
|
||||
int ioviter, nw, nr;
|
||||
|
||||
if (testing & TESTING_READV) {
|
||||
for (ioviter = 0; ioviter < IOV_COUNT; ioviter++) {
|
||||
iov[ioviter].iov_base = &buf[ioviter * IOV_LEN];
|
||||
iov[ioviter].iov_len = IOV_LEN;
|
||||
}
|
||||
if (testing & TESTING_VERBOSE) {
|
||||
printf("readv(%p)...\n", in);
|
||||
show_iov(iov, IOV_COUNT);
|
||||
}
|
||||
if (testing & TESTING_READ_RESID) {
|
||||
struct iovec *riov = iov;
|
||||
int riov_cnt = IOV_COUNT;
|
||||
if (st_readv_resid(in, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {
|
||||
if (testing & TESTING_VERBOSE) {
|
||||
printf("resid\n");
|
||||
show_iov(riov, riov_cnt);
|
||||
printf("full\n");
|
||||
show_iov(iov, IOV_COUNT);
|
||||
}
|
||||
nr = 0;
|
||||
for (ioviter = 0; ioviter < IOV_COUNT; ioviter++)
|
||||
nr += iov[ioviter].iov_len;
|
||||
nr = IOBUFSIZE - nr;
|
||||
} else
|
||||
nr = -1;
|
||||
} else
|
||||
nr = (int) st_readv(in, iov, IOV_COUNT, ST_UTIME_NO_TIMEOUT);
|
||||
} else {
|
||||
if (testing & TESTING_READ_RESID) {
|
||||
size_t resid = IOBUFSIZE;
|
||||
if (st_read_resid(in, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)
|
||||
nr = IOBUFSIZE - resid;
|
||||
else
|
||||
nr = -1;
|
||||
} else
|
||||
nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);
|
||||
}
|
||||
if (testing & TESTING_VERBOSE)
|
||||
printf("got 0x%x(%d) E=%d\n", nr, nr, errno);
|
||||
|
||||
if (nr <= 0)
|
||||
return 0;
|
||||
|
||||
if (testing & TESTING_WRITEV) {
|
||||
for (nw = 0, ioviter = 0; nw < nr;
|
||||
nw += iov[ioviter].iov_len, ioviter++) {
|
||||
iov[ioviter].iov_base = &buf[nw];
|
||||
iov[ioviter].iov_len = nr - nw;
|
||||
if (iov[ioviter].iov_len > IOV_LEN)
|
||||
iov[ioviter].iov_len = IOV_LEN;
|
||||
}
|
||||
if (testing & TESTING_VERBOSE) {
|
||||
printf("writev(%p)...\n", out);
|
||||
show_iov(iov, ioviter);
|
||||
}
|
||||
if (testing & TESTING_WRITE_RESID) {
|
||||
struct iovec *riov = iov;
|
||||
int riov_cnt = ioviter;
|
||||
if (st_writev_resid(out, &riov, &riov_cnt, ST_UTIME_NO_TIMEOUT) == 0) {
|
||||
if (testing & TESTING_VERBOSE) {
|
||||
printf("resid\n");
|
||||
show_iov(riov, riov_cnt);
|
||||
printf("full\n");
|
||||
show_iov(iov, ioviter);
|
||||
}
|
||||
nw = 0;
|
||||
while (--ioviter >= 0)
|
||||
nw += iov[ioviter].iov_len;
|
||||
nw = nr - nw;
|
||||
} else
|
||||
nw = -1;
|
||||
} else
|
||||
nw = st_writev(out, iov, ioviter, ST_UTIME_NO_TIMEOUT);
|
||||
} else {
|
||||
if (testing & TESTING_WRITE_RESID) {
|
||||
size_t resid = nr;
|
||||
if (st_write_resid(out, buf, &resid, ST_UTIME_NO_TIMEOUT) == 0)
|
||||
nw = nr - resid;
|
||||
else
|
||||
nw = -1;
|
||||
} else
|
||||
nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);
|
||||
}
|
||||
if (testing & TESTING_VERBOSE)
|
||||
printf("put 0x%x(%d) E=%d\n", nw, nw, errno);
|
||||
|
||||
if (nw != nr)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#else /* DEBUG */
|
||||
/*
|
||||
* This version is the simple one suitable for serious use.
|
||||
*/
|
||||
static int pass(st_netfd_t in, st_netfd_t out)
|
||||
{
|
||||
char buf[IOBUFSIZE];
|
||||
int nw, nr;
|
||||
|
||||
nr = (int) st_read(in, buf, IOBUFSIZE, ST_UTIME_NO_TIMEOUT);
|
||||
if (nr <= 0)
|
||||
return 0;
|
||||
|
||||
nw = st_write(out, buf, nr, ST_UTIME_NO_TIMEOUT);
|
||||
if (nw != nr)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *handle_request(void *arg)
|
||||
{
|
||||
struct pollfd pds[2];
|
||||
st_netfd_t cli_nfd, rmt_nfd;
|
||||
int sock;
|
||||
|
||||
cli_nfd = (st_netfd_t) arg;
|
||||
pds[0].fd = st_netfd_fileno(cli_nfd);
|
||||
pds[0].events = POLLIN;
|
||||
|
||||
/* Connect to remote host */
|
||||
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
print_sys_error("socket");
|
||||
goto done;
|
||||
}
|
||||
if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL) {
|
||||
print_sys_error("st_netfd_open_socket");
|
||||
close(sock);
|
||||
goto done;
|
||||
}
|
||||
if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr,
|
||||
sizeof(rmt_addr), ST_UTIME_NO_TIMEOUT) < 0) {
|
||||
print_sys_error("st_connect");
|
||||
st_netfd_close(rmt_nfd);
|
||||
goto done;
|
||||
}
|
||||
pds[1].fd = sock;
|
||||
pds[1].events = POLLIN;
|
||||
|
||||
/*
|
||||
* Now just pump the data through.
|
||||
* XXX This should use one thread for each direction for true full-duplex.
|
||||
*/
|
||||
for ( ; ; ) {
|
||||
pds[0].revents = 0;
|
||||
pds[1].revents = 0;
|
||||
|
||||
if (st_poll(pds, 2, ST_UTIME_NO_TIMEOUT) <= 0) {
|
||||
print_sys_error("st_poll");
|
||||
break;
|
||||
}
|
||||
|
||||
if (pds[0].revents & POLLIN) {
|
||||
if (!pass(cli_nfd, rmt_nfd))
|
||||
break;
|
||||
}
|
||||
|
||||
if (pds[1].revents & POLLIN) {
|
||||
if (!pass(rmt_nfd, cli_nfd))
|
||||
break;
|
||||
}
|
||||
}
|
||||
st_netfd_close(rmt_nfd);
|
||||
|
||||
done:
|
||||
|
||||
st_netfd_close(cli_nfd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void start_daemon(void)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
/* Start forking */
|
||||
if ((pid = fork()) < 0) {
|
||||
print_sys_error("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (pid > 0)
|
||||
exit(0); /* parent */
|
||||
|
||||
/* First child process */
|
||||
setsid(); /* become session leader */
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
print_sys_error("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (pid > 0) /* first child */
|
||||
exit(0);
|
||||
|
||||
chdir("/");
|
||||
umask(022);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create separate processes ("virtual processors"). Since it's just an
|
||||
* example, there is no watchdog - the parent just exits leaving children
|
||||
* on their own.
|
||||
*/
|
||||
static void set_concurrency(int nproc)
|
||||
{
|
||||
pid_t pid;
|
||||
int i;
|
||||
|
||||
if (nproc < 1)
|
||||
nproc = 1;
|
||||
|
||||
for (i = 0; i < nproc; i++) {
|
||||
if ((pid = fork()) < 0) {
|
||||
print_sys_error("fork");
|
||||
exit(1);
|
||||
}
|
||||
/* Child returns */
|
||||
if (pid == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parent just exits */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int cpu_count(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
#if defined (_SC_NPROCESSORS_ONLN)
|
||||
n = (int) sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#elif defined (_SC_NPROC_ONLN)
|
||||
n = (int) sysconf(_SC_NPROC_ONLN);
|
||||
#elif defined (HPUX)
|
||||
#include <sys/mpctl.h>
|
||||
n = mpctl(MPC_GETNUMSPUS, 0, 0);
|
||||
#else
|
||||
n = -1;
|
||||
errno = ENOSYS;
|
||||
#endif
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void print_sys_error(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno));
|
||||
}
|
||||
|
@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1985, 1988, 1993
|
||||
* The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University 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 REGENTS 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 REGENTS OR 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if defined (DARWIN)
|
||||
#define BIND_8_COMPAT
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <resolv.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include "st.h"
|
||||
|
||||
#define MAXPACKET 1024
|
||||
|
||||
#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)
|
||||
#define NETDB_INTERNAL h_NETDB_INTERNAL
|
||||
#endif
|
||||
|
||||
/* New in Solaris 7 */
|
||||
#if !defined(_getshort) && defined(ns_get16)
|
||||
#define _getshort(cp) ns_get16(cp)
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
HEADER hdr;
|
||||
u_char buf[MAXPACKET];
|
||||
} querybuf_t;
|
||||
|
||||
|
||||
static int parse_answer(querybuf_t *ans, int len, struct in_addr *addr)
|
||||
{
|
||||
char buf[MAXPACKET];
|
||||
HEADER *ahp;
|
||||
u_char *cp, *eoa;
|
||||
int type, n;
|
||||
|
||||
ahp = &ans->hdr;
|
||||
eoa = ans->buf + len;
|
||||
cp = ans->buf + sizeof(HEADER);
|
||||
|
||||
while (ahp->qdcount > 0) {
|
||||
ahp->qdcount--;
|
||||
cp += dn_skipname(cp, eoa) + QFIXEDSZ;
|
||||
}
|
||||
while (ahp->ancount > 0 && cp < eoa) {
|
||||
ahp->ancount--;
|
||||
if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0)
|
||||
break;
|
||||
cp += n;
|
||||
type = _getshort(cp);
|
||||
cp += 8;
|
||||
n = _getshort(cp);
|
||||
cp += 2;
|
||||
if (type == T_CNAME) {
|
||||
cp += n;
|
||||
continue;
|
||||
}
|
||||
memcpy(addr, cp, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
h_errno = TRY_AGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int query_domain(st_netfd_t nfd, const char *name, struct in_addr *addr,
|
||||
st_utime_t timeout)
|
||||
{
|
||||
querybuf_t qbuf;
|
||||
u_char *buf = qbuf.buf;
|
||||
HEADER *hp = &qbuf.hdr;
|
||||
int blen = sizeof(qbuf);
|
||||
int i, len, id;
|
||||
|
||||
for (i = 0; i < _res.nscount; i++) {
|
||||
len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen);
|
||||
if (len <= 0) {
|
||||
h_errno = NO_RECOVERY;
|
||||
return -1;
|
||||
}
|
||||
id = hp->id;
|
||||
|
||||
if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]),
|
||||
sizeof(struct sockaddr), timeout) != len) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
/* EINTR means interrupt by other thread, NOT by a caught signal */
|
||||
if (errno == EINTR)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Wait for reply */
|
||||
do {
|
||||
len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout);
|
||||
if (len <= 0)
|
||||
break;
|
||||
} while (id != hp->id);
|
||||
|
||||
if (len < HFIXEDSZ) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
if (len >= 0)
|
||||
errno = EMSGSIZE;
|
||||
else if (errno == EINTR) /* see the comment above */
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
hp->ancount = ntohs(hp->ancount);
|
||||
hp->qdcount = ntohs(hp->qdcount);
|
||||
if ((hp->rcode != NOERROR) || (hp->ancount == 0)) {
|
||||
switch (hp->rcode) {
|
||||
case NXDOMAIN:
|
||||
h_errno = HOST_NOT_FOUND;
|
||||
break;
|
||||
case SERVFAIL:
|
||||
h_errno = TRY_AGAIN;
|
||||
break;
|
||||
case NOERROR:
|
||||
h_errno = NO_DATA;
|
||||
break;
|
||||
case FORMERR:
|
||||
case NOTIMP:
|
||||
case REFUSED:
|
||||
default:
|
||||
h_errno = NO_RECOVERY;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parse_answer(&qbuf, len, addr) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#define CLOSE_AND_RETURN(ret) \
|
||||
{ \
|
||||
n = errno; \
|
||||
st_netfd_close(nfd); \
|
||||
errno = n; \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
|
||||
int dns_getaddr(const char *host, struct in_addr *addr, st_utime_t timeout)
|
||||
{
|
||||
char name[MAXDNAME], **domain;
|
||||
const char *cp;
|
||||
int s, n, maxlen, dots;
|
||||
int trailing_dot, tried_as_is;
|
||||
st_netfd_t nfd;
|
||||
|
||||
if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
return -1;
|
||||
}
|
||||
if (_res.options & RES_USEVC) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
if (!host || *host == '\0') {
|
||||
h_errno = HOST_NOT_FOUND;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create UDP socket */
|
||||
if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
return -1;
|
||||
}
|
||||
if ((nfd = st_netfd_open_socket(s)) == NULL) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
n = errno;
|
||||
close(s);
|
||||
errno = n;
|
||||
return -1;
|
||||
}
|
||||
|
||||
maxlen = sizeof(name) - 1;
|
||||
n = 0;
|
||||
dots = 0;
|
||||
trailing_dot = 0;
|
||||
tried_as_is = 0;
|
||||
|
||||
for (cp = host; *cp && n < maxlen; cp++) {
|
||||
dots += (*cp == '.');
|
||||
name[n++] = *cp;
|
||||
}
|
||||
if (name[n - 1] == '.')
|
||||
trailing_dot = 1;
|
||||
|
||||
/*
|
||||
* If there are dots in the name already, let's just give it a try
|
||||
* 'as is'. The threshold can be set with the "ndots" option.
|
||||
*/
|
||||
if (dots >= _res.ndots) {
|
||||
if (query_domain(nfd, host, addr, timeout) == 0)
|
||||
CLOSE_AND_RETURN(0);
|
||||
if (h_errno == NETDB_INTERNAL && errno == EINTR)
|
||||
CLOSE_AND_RETURN(-1);
|
||||
tried_as_is = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We do at least one level of search if
|
||||
* - there is no dot and RES_DEFNAME is set, or
|
||||
* - there is at least one dot, there is no trailing dot,
|
||||
* and RES_DNSRCH is set.
|
||||
*/
|
||||
if ((!dots && (_res.options & RES_DEFNAMES)) ||
|
||||
(dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
|
||||
name[n++] = '.';
|
||||
for (domain = _res.dnsrch; *domain; domain++) {
|
||||
strncpy(name + n, *domain, maxlen - n);
|
||||
if (query_domain(nfd, name, addr, timeout) == 0)
|
||||
CLOSE_AND_RETURN(0);
|
||||
if (h_errno == NETDB_INTERNAL && errno == EINTR)
|
||||
CLOSE_AND_RETURN(-1);
|
||||
if (!(_res.options & RES_DNSRCH))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have not already tried the name "as is", do that now.
|
||||
* note that we do this regardless of how many dots were in the
|
||||
* name or whether it ends with a dot.
|
||||
*/
|
||||
if (!tried_as_is) {
|
||||
if (query_domain(nfd, host, addr, timeout) == 0)
|
||||
CLOSE_AND_RETURN(0);
|
||||
}
|
||||
|
||||
CLOSE_AND_RETURN(-1);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,91 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
CC = cc
|
||||
|
||||
SHELL = /bin/sh
|
||||
ECHO = /bin/echo
|
||||
|
||||
DEPTH = ..
|
||||
BUILD =
|
||||
TARGETDIR = obj
|
||||
|
||||
DEFINES =
|
||||
OTHER_FLAGS =
|
||||
CFLAGS =
|
||||
|
||||
OBJDIR = $(DEPTH)/$(TARGETDIR)
|
||||
INCDIR = $(DEPTH)/$(TARGETDIR)
|
||||
|
||||
LIBRESOLV =
|
||||
EXTRALIBS =
|
||||
|
||||
SLIBRARY = $(OBJDIR)/libstx.a
|
||||
OBJS = $(OBJDIR)/dnscache.o $(OBJDIR)/dnsres.o $(OBJDIR)/lrucache.o
|
||||
|
||||
|
||||
CFLAGS += -Wall -I$(INCDIR)
|
||||
AR = ar
|
||||
ARFLAGS = rv
|
||||
RANLIB = ranlib
|
||||
|
||||
|
||||
##########################
|
||||
# Platform section.
|
||||
#
|
||||
|
||||
ifeq (LINUX, $(findstring LINUX, $(OS)))
|
||||
LIBRESOLV = -lresolv
|
||||
endif
|
||||
|
||||
ifeq ($(OS), SOLARIS)
|
||||
LIBRESOLV = -lresolv
|
||||
EXTRALIBS = -lsocket -lnsl
|
||||
endif
|
||||
|
||||
#
|
||||
# End of platform section.
|
||||
##########################
|
||||
|
||||
|
||||
all: $(SLIBRARY)
|
||||
|
||||
$(SLIBRARY): $(OBJS)
|
||||
$(AR) $(ARFLAGS) $@ $(OBJS)
|
||||
$(RANLIB) $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c stx.h common.h
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJS) $(SLIBRARY)
|
||||
|
||||
#.DEFAULT:
|
||||
# @cd $(DEPTH); $(MAKE) $@
|
||||
|
@ -1,42 +0,0 @@
|
||||
This directory contains extensions to the core State Threads Library
|
||||
that were contributed by users. All files hereunder are not part of the
|
||||
State Threads Library itself. They are provided as-is, without warranty
|
||||
or support, and under whatever license terms their authors provided. To
|
||||
contribute your own extensions, just mail them to the project
|
||||
administrators or to one of the project's mailing lists; see
|
||||
state-threads.sourceforge.net. Please indicate the license terms under
|
||||
which the project may distribute your contribution.
|
||||
|
||||
========================================================================
|
||||
|
||||
stx_fileio
|
||||
----------
|
||||
Contributed by Jeff <jlb-st@houseofdistraction.com>, 4 Nov 2002.
|
||||
|
||||
Provides non-blocking random access file reading capability for
|
||||
programs using the State Threads library. There is one public function:
|
||||
|
||||
ssize_t stx_file_read(st_netfd_t fd, off_t offset,
|
||||
void *buf, size_t nbytes, st_utime_t timeout);
|
||||
|
||||
The implementation is not optimal in that the data is copied at least once
|
||||
more than should be necessary. Its usefulness is limited to cases where
|
||||
random access to a file is required and where starvation of other threads
|
||||
is unacceptable.
|
||||
|
||||
The particular application which motivated this implementation was a UDP
|
||||
file transfer protocol. Because the OS does very little buffering of UDP
|
||||
traffic it is important that UDP transmission threads are not starved for
|
||||
periods of time which are long relative to the interval required to
|
||||
maintain a steady send rate.
|
||||
|
||||
Licensed under the same dual MPL/GPL as core State Threads.
|
||||
|
||||
========================================================================
|
||||
|
||||
stx_dns
|
||||
-------
|
||||
|
||||
Documentation coming.
|
||||
|
||||
========================================================================
|
@ -1,77 +0,0 @@
|
||||
#ifndef _STX_COMMON_H_
|
||||
#define _STX_COMMON_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define STX_BEGIN_MACRO {
|
||||
#define STX_END_MACRO }
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Circular linked list definitions
|
||||
*/
|
||||
|
||||
typedef struct _stx_clist {
|
||||
struct _stx_clist *next;
|
||||
struct _stx_clist *prev;
|
||||
} stx_clist_t;
|
||||
|
||||
/* Insert element "_e" into the list, before "_l" */
|
||||
#define STX_CLIST_INSERT_BEFORE(_e,_l) \
|
||||
STX_BEGIN_MACRO \
|
||||
(_e)->next = (_l); \
|
||||
(_e)->prev = (_l)->prev; \
|
||||
(_l)->prev->next = (_e); \
|
||||
(_l)->prev = (_e); \
|
||||
STX_END_MACRO
|
||||
|
||||
/* Insert element "_e" into the list, after "_l" */
|
||||
#define STX_CLIST_INSERT_AFTER(_e,_l) \
|
||||
STX_BEGIN_MACRO \
|
||||
(_e)->next = (_l)->next; \
|
||||
(_e)->prev = (_l); \
|
||||
(_l)->next->prev = (_e); \
|
||||
(_l)->next = (_e); \
|
||||
STX_END_MACRO
|
||||
|
||||
/* Append an element "_e" to the end of the list "_l" */
|
||||
#define STX_CLIST_APPEND_LINK(_e,_l) STX_CLIST_INSERT_BEFORE(_e,_l)
|
||||
|
||||
/* Remove the element "_e" from it's circular list */
|
||||
#define STX_CLIST_REMOVE_LINK(_e) \
|
||||
STX_BEGIN_MACRO \
|
||||
(_e)->prev->next = (_e)->next; \
|
||||
(_e)->next->prev = (_e)->prev; \
|
||||
STX_END_MACRO
|
||||
|
||||
/* Return the head/tail of the list */
|
||||
#define STX_CLIST_HEAD(_l) (_l)->next
|
||||
#define STX_CLIST_TAIL(_l) (_l)->prev
|
||||
|
||||
/* Return non-zero if the given circular list "_l" is empty, */
|
||||
/* zero if the circular list is not empty */
|
||||
#define STX_CLIST_IS_EMPTY(_l) \
|
||||
((_l)->next == (_l))
|
||||
|
||||
/* Initialize a circular list */
|
||||
#define STX_CLIST_INIT_CLIST(_l) \
|
||||
STX_BEGIN_MACRO \
|
||||
(_l)->next = (_l); \
|
||||
(_l)->prev = (_l); \
|
||||
STX_END_MACRO
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Useful macros
|
||||
*/
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier))
|
||||
#endif
|
||||
|
||||
#define STX_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
#endif /* !_STX_COMMON_H_ */
|
||||
|
@ -1,190 +0,0 @@
|
||||
#include "stx.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Basic types definitions
|
||||
*/
|
||||
|
||||
typedef struct _stx_dns_data {
|
||||
struct in_addr *addrs;
|
||||
int num_addrs;
|
||||
int cur;
|
||||
time_t expires;
|
||||
} stx_dns_data_t;
|
||||
|
||||
|
||||
#define MAX_HOST_ADDRS 1024
|
||||
|
||||
static struct in_addr addr_list[MAX_HOST_ADDRS];
|
||||
|
||||
stx_cache_t *_stx_dns_cache = NULL;
|
||||
|
||||
extern int _stx_dns_ttl;
|
||||
extern int _stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,
|
||||
int *num_addrs, st_utime_t timeout);
|
||||
|
||||
|
||||
static unsigned long hash_hostname(const void *key)
|
||||
{
|
||||
const char *name = (const char *)key;
|
||||
unsigned long hash = 0;
|
||||
|
||||
while (*name)
|
||||
hash = (hash << 4) - hash + *name++; /* hash = hash * 15 + *name++ */
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void cleanup_entry(void *key, void *data)
|
||||
{
|
||||
if (key)
|
||||
free(key);
|
||||
|
||||
if (data) {
|
||||
if (((stx_dns_data_t *)data)->addrs)
|
||||
free(((stx_dns_data_t *)data)->addrs);
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
static int lookup_entry(const char *host, struct in_addr *addrs,
|
||||
int *num_addrs, int rotate)
|
||||
{
|
||||
stx_cache_entry_t *entry;
|
||||
stx_dns_data_t *data;
|
||||
int n;
|
||||
|
||||
entry = stx_cache_entry_lookup(_stx_dns_cache, host);
|
||||
if (entry) {
|
||||
data = (stx_dns_data_t *)stx_cache_entry_getdata(entry);
|
||||
if (st_time() <= data->expires) {
|
||||
if (*num_addrs == 1) {
|
||||
if (rotate) {
|
||||
*addrs = data->addrs[data->cur++];
|
||||
if (data->cur >= data->num_addrs)
|
||||
data->cur = 0;
|
||||
} else {
|
||||
*addrs = data->addrs[0];
|
||||
}
|
||||
} else {
|
||||
n = STX_MIN(*num_addrs, data->num_addrs);
|
||||
memcpy(addrs, data->addrs, n * sizeof(*addrs));
|
||||
*num_addrs = n;
|
||||
}
|
||||
|
||||
stx_cache_entry_release(_stx_dns_cache, entry);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache entry expired: decrement its refcount and purge it from cache.
|
||||
*/
|
||||
stx_cache_entry_release(_stx_dns_cache, entry);
|
||||
stx_cache_entry_delete(_stx_dns_cache, entry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void insert_entry(const char *host, struct in_addr *addrs, int count)
|
||||
{
|
||||
stx_cache_entry_t *entry;
|
||||
stx_dns_data_t *data;
|
||||
char *key;
|
||||
size_t n;
|
||||
|
||||
if (_stx_dns_ttl > 0) {
|
||||
key = strdup(host);
|
||||
data = (stx_dns_data_t *)malloc(sizeof(stx_dns_data_t));
|
||||
n = count * sizeof(*addrs);
|
||||
if (data) {
|
||||
data->addrs = (struct in_addr *)malloc(n);
|
||||
if (data->addrs)
|
||||
memcpy(data->addrs, addrs, n);
|
||||
data->num_addrs = count;
|
||||
data->cur = 0;
|
||||
data->expires = st_time() + _stx_dns_ttl;
|
||||
}
|
||||
entry = stx_cache_entry_create(key, data, strlen(host) + 1 +
|
||||
sizeof(stx_dns_data_t) + n +
|
||||
stx_cache_entry_sizeof());
|
||||
if (key && data && data->addrs && entry &&
|
||||
stx_cache_entry_insert(_stx_dns_cache, entry) == 0) {
|
||||
stx_cache_entry_release(_stx_dns_cache, entry);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry)
|
||||
stx_cache_entry_delete(_stx_dns_cache, entry);
|
||||
else
|
||||
cleanup_entry(key, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _stx_dns_cache_getaddrlist(const char *hostname, struct in_addr *addrs,
|
||||
int *num_addrs, st_utime_t timeout,
|
||||
int rotate)
|
||||
{
|
||||
char host[128];
|
||||
int n, count;
|
||||
|
||||
if (!_stx_dns_cache)
|
||||
return _stx_dns_getaddrlist(hostname, addrs, num_addrs, timeout);
|
||||
|
||||
for (n = 0; n < sizeof(host) - 1 && hostname[n]; n++) {
|
||||
host[n] = tolower(hostname[n]);
|
||||
}
|
||||
host[n] = '\0';
|
||||
|
||||
if (lookup_entry(host, addrs, num_addrs, rotate))
|
||||
return 0;
|
||||
|
||||
count = MAX_HOST_ADDRS;
|
||||
if (_stx_dns_getaddrlist(host, addr_list, &count, timeout) < 0)
|
||||
return -1;
|
||||
n = STX_MIN(*num_addrs, count);
|
||||
memcpy(addrs, addr_list, n * sizeof(*addrs));
|
||||
*num_addrs = n;
|
||||
|
||||
insert_entry(host, addr_list, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size)
|
||||
{
|
||||
_stx_dns_cache = stx_cache_create(max_size, max_bytes, hash_size,
|
||||
hash_hostname,
|
||||
(long (*)(const void *, const void *))strcmp,
|
||||
cleanup_entry);
|
||||
if (!_stx_dns_cache)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void stx_dns_cache_getinfo(stx_cache_info_t *info)
|
||||
{
|
||||
if (_stx_dns_cache)
|
||||
stx_cache_getinfo(_stx_dns_cache, info);
|
||||
else
|
||||
memset(info, 0, sizeof(stx_cache_info_t));
|
||||
}
|
||||
|
||||
int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,
|
||||
int *num_addrs, st_utime_t timeout)
|
||||
{
|
||||
return _stx_dns_cache_getaddrlist(hostname, addrs, num_addrs, timeout, 0);
|
||||
}
|
||||
|
||||
int stx_dns_getaddr(const char *hostname, struct in_addr *addr,
|
||||
st_utime_t timeout)
|
||||
{
|
||||
int n = 1;
|
||||
|
||||
return _stx_dns_cache_getaddrlist(hostname, addr, &n, timeout, 1);
|
||||
}
|
||||
|
@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1985, 1988, 1993
|
||||
* The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University 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 REGENTS 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 REGENTS OR 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.
|
||||
*
|
||||
* 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 "stx.h"
|
||||
|
||||
#define MAXPACKET 1024
|
||||
|
||||
#if !defined(NETDB_INTERNAL) && defined(h_NETDB_INTERNAL)
|
||||
#define NETDB_INTERNAL h_NETDB_INTERNAL
|
||||
#endif
|
||||
|
||||
/* New in Solaris 7 */
|
||||
#if !defined(_getshort) && defined(ns_get16)
|
||||
#define _getshort(cp) ns_get16(cp)
|
||||
#define _getlong(cp) ns_get32(cp)
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
HEADER hdr;
|
||||
u_char buf[MAXPACKET];
|
||||
} querybuf_t;
|
||||
|
||||
int _stx_dns_ttl;
|
||||
|
||||
|
||||
static int parse_answer(querybuf_t *ans, int len, struct in_addr *addrs,
|
||||
int *num_addrs)
|
||||
{
|
||||
char buf[MAXPACKET];
|
||||
HEADER *ahp;
|
||||
u_char *cp, *eoa;
|
||||
int type, n, i;
|
||||
|
||||
ahp = &ans->hdr;
|
||||
eoa = ans->buf + len;
|
||||
cp = ans->buf + sizeof(HEADER);
|
||||
h_errno = TRY_AGAIN;
|
||||
_stx_dns_ttl = -1;
|
||||
i = 0;
|
||||
|
||||
while (ahp->qdcount > 0) {
|
||||
ahp->qdcount--;
|
||||
cp += dn_skipname(cp, eoa) + QFIXEDSZ;
|
||||
}
|
||||
while (ahp->ancount > 0 && cp < eoa && i < *num_addrs) {
|
||||
ahp->ancount--;
|
||||
if ((n = dn_expand(ans->buf, eoa, cp, buf, sizeof(buf))) < 0)
|
||||
return -1;
|
||||
cp += n;
|
||||
if (cp + 4 + 4 + 2 >= eoa)
|
||||
return -1;
|
||||
type = _getshort(cp);
|
||||
cp += 4;
|
||||
if (type == T_A)
|
||||
_stx_dns_ttl = _getlong(cp);
|
||||
cp += 4;
|
||||
n = _getshort(cp);
|
||||
cp += 2;
|
||||
if (type == T_A) {
|
||||
if (n > sizeof(*addrs) || cp + n > eoa)
|
||||
return -1;
|
||||
memcpy(&addrs[i++], cp, n);
|
||||
}
|
||||
cp += n;
|
||||
}
|
||||
|
||||
*num_addrs = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int query_domain(st_netfd_t nfd, const char *name,
|
||||
struct in_addr *addrs, int *num_addrs,
|
||||
st_utime_t timeout)
|
||||
{
|
||||
querybuf_t qbuf;
|
||||
u_char *buf = qbuf.buf;
|
||||
HEADER *hp = &qbuf.hdr;
|
||||
int blen = sizeof(qbuf);
|
||||
int i, len, id;
|
||||
|
||||
for (i = 0; i < _res.nscount; i++) {
|
||||
len = res_mkquery(QUERY, name, C_IN, T_A, NULL, 0, NULL, buf, blen);
|
||||
if (len <= 0) {
|
||||
h_errno = NO_RECOVERY;
|
||||
return -1;
|
||||
}
|
||||
id = hp->id;
|
||||
|
||||
if (st_sendto(nfd, buf, len, (struct sockaddr *)&(_res.nsaddr_list[i]),
|
||||
sizeof(struct sockaddr), timeout) != len) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
/* EINTR means interrupt by other thread, NOT by a caught signal */
|
||||
if (errno == EINTR)
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Wait for reply */
|
||||
do {
|
||||
len = st_recvfrom(nfd, buf, blen, NULL, NULL, timeout);
|
||||
if (len <= 0)
|
||||
break;
|
||||
} while (id != hp->id);
|
||||
|
||||
if (len < HFIXEDSZ) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
if (len >= 0)
|
||||
errno = EMSGSIZE;
|
||||
else if (errno == EINTR) /* see the comment above */
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
hp->ancount = ntohs(hp->ancount);
|
||||
hp->qdcount = ntohs(hp->qdcount);
|
||||
if ((hp->rcode != NOERROR) || (hp->ancount == 0)) {
|
||||
switch (hp->rcode) {
|
||||
case NXDOMAIN:
|
||||
h_errno = HOST_NOT_FOUND;
|
||||
break;
|
||||
case SERVFAIL:
|
||||
h_errno = TRY_AGAIN;
|
||||
break;
|
||||
case NOERROR:
|
||||
h_errno = NO_DATA;
|
||||
break;
|
||||
case FORMERR:
|
||||
case NOTIMP:
|
||||
case REFUSED:
|
||||
default:
|
||||
h_errno = NO_RECOVERY;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parse_answer(&qbuf, len, addrs, num_addrs) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#define CLOSE_AND_RETURN(ret) \
|
||||
{ \
|
||||
n = errno; \
|
||||
st_netfd_close(nfd); \
|
||||
errno = n; \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
|
||||
int _stx_dns_getaddrlist(const char *host, struct in_addr *addrs,
|
||||
int *num_addrs, st_utime_t timeout)
|
||||
{
|
||||
char name[MAXDNAME], **domain;
|
||||
const char *cp;
|
||||
int s, n, maxlen, dots;
|
||||
int trailing_dot, tried_as_is;
|
||||
st_netfd_t nfd;
|
||||
|
||||
if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
return -1;
|
||||
}
|
||||
if (_res.options & RES_USEVC) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
if (!host || *host == '\0') {
|
||||
h_errno = HOST_NOT_FOUND;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create UDP socket */
|
||||
if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
return -1;
|
||||
}
|
||||
if ((nfd = st_netfd_open_socket(s)) == NULL) {
|
||||
h_errno = NETDB_INTERNAL;
|
||||
n = errno;
|
||||
close(s);
|
||||
errno = n;
|
||||
return -1;
|
||||
}
|
||||
|
||||
maxlen = sizeof(name) - 1;
|
||||
n = 0;
|
||||
dots = 0;
|
||||
trailing_dot = 0;
|
||||
tried_as_is = 0;
|
||||
|
||||
for (cp = host; *cp && n < maxlen; cp++) {
|
||||
dots += (*cp == '.');
|
||||
name[n++] = *cp;
|
||||
}
|
||||
if (name[n - 1] == '.')
|
||||
trailing_dot = 1;
|
||||
|
||||
/*
|
||||
* If there are dots in the name already, let's just give it a try
|
||||
* 'as is'. The threshold can be set with the "ndots" option.
|
||||
*/
|
||||
if (dots >= _res.ndots) {
|
||||
if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0)
|
||||
CLOSE_AND_RETURN(0);
|
||||
if (h_errno == NETDB_INTERNAL && errno == EINTR)
|
||||
CLOSE_AND_RETURN(-1);
|
||||
tried_as_is = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We do at least one level of search if
|
||||
* - there is no dot and RES_DEFNAME is set, or
|
||||
* - there is at least one dot, there is no trailing dot,
|
||||
* and RES_DNSRCH is set.
|
||||
*/
|
||||
if ((!dots && (_res.options & RES_DEFNAMES)) ||
|
||||
(dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
|
||||
name[n++] = '.';
|
||||
for (domain = _res.dnsrch; *domain; domain++) {
|
||||
strncpy(name + n, *domain, maxlen - n);
|
||||
if (query_domain(nfd, name, addrs, num_addrs, timeout) == 0)
|
||||
CLOSE_AND_RETURN(0);
|
||||
if (h_errno == NETDB_INTERNAL && errno == EINTR)
|
||||
CLOSE_AND_RETURN(-1);
|
||||
if (!(_res.options & RES_DNSRCH))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have not already tried the name "as is", do that now.
|
||||
* note that we do this regardless of how many dots were in the
|
||||
* name or whether it ends with a dot.
|
||||
*/
|
||||
if (!tried_as_is) {
|
||||
if (query_domain(nfd, host, addrs, num_addrs, timeout) == 0)
|
||||
CLOSE_AND_RETURN(0);
|
||||
}
|
||||
|
||||
CLOSE_AND_RETURN(-1);
|
||||
}
|
||||
|
@ -1,343 +0,0 @@
|
||||
#include "stx.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Basic types definitions
|
||||
*/
|
||||
|
||||
struct _stx_centry {
|
||||
void *key; /* key for doing lookups */
|
||||
void *data; /* data in the cache */
|
||||
size_t weight; /* "weight" of this entry */
|
||||
struct _stx_centry *next; /* next entry */
|
||||
struct _stx_centry **pthis;
|
||||
stx_clist_t lru_link; /* for putting this entry on LRU list */
|
||||
int ref_count; /* use count for this entry */
|
||||
int delete_pending; /* pending delete flag */
|
||||
};
|
||||
|
||||
struct _stx_cache {
|
||||
size_t max_size; /* max size of cache */
|
||||
size_t cur_size; /* current size of cache */
|
||||
|
||||
size_t max_weight; /* cache capacity */
|
||||
size_t cur_weight; /* current total "weight" of all entries */
|
||||
|
||||
size_t hash_size; /* size of hash table */
|
||||
stx_cache_entry_t **table; /* hash table for this cache */
|
||||
|
||||
stx_clist_t lru_list; /* least-recently-used list */
|
||||
|
||||
/* Cache stats */
|
||||
unsigned long hits; /* num cache hits */
|
||||
unsigned long lookups; /* num cache lookups */
|
||||
unsigned long inserts; /* num inserts */
|
||||
unsigned long deletes; /* num deletes */
|
||||
|
||||
/* Functions */
|
||||
unsigned long (*key_hash_fn)(const void *);
|
||||
long (*key_cmp_fn)(const void *, const void *);
|
||||
void (*cleanup_fn)(void *, void *);
|
||||
};
|
||||
|
||||
|
||||
#define STX_CACHE_ENTRY_PTR(_qp) \
|
||||
((stx_cache_entry_t *)((char *)(_qp) - offsetof(stx_cache_entry_t, lru_link)))
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Cache methods
|
||||
*/
|
||||
|
||||
stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight,
|
||||
size_t hash_size,
|
||||
unsigned long (*key_hash_fn)(const void *key),
|
||||
long (*key_cmp_fn)(const void *key1,
|
||||
const void *key2),
|
||||
void (*cleanup_fn)(void *key, void *data))
|
||||
{
|
||||
stx_cache_t *newcache;
|
||||
|
||||
newcache = (stx_cache_t *)calloc(1, sizeof(stx_cache_t));
|
||||
if (newcache == NULL)
|
||||
return NULL;
|
||||
newcache->table = (stx_cache_entry_t **)calloc(hash_size,
|
||||
sizeof(stx_cache_entry_t *));
|
||||
if (newcache->table == NULL) {
|
||||
free(newcache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newcache->max_size = max_size;
|
||||
newcache->max_weight = max_weight;
|
||||
newcache->hash_size = hash_size;
|
||||
STX_CLIST_INIT_CLIST(&(newcache->lru_list));
|
||||
newcache->key_hash_fn = key_hash_fn;
|
||||
newcache->key_cmp_fn = key_cmp_fn;
|
||||
newcache->cleanup_fn = cleanup_fn;
|
||||
|
||||
return newcache;
|
||||
}
|
||||
|
||||
|
||||
void stx_cache_empty(stx_cache_t *cache)
|
||||
{
|
||||
size_t i;
|
||||
stx_cache_entry_t *entry, *next_entry;
|
||||
|
||||
for (i = 0; i < cache->hash_size; i++) {
|
||||
entry = cache->table[i];
|
||||
while (entry) {
|
||||
next_entry = entry->next;
|
||||
stx_cache_entry_delete(cache, entry);
|
||||
entry = next_entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void stx_cache_traverse(stx_cache_t *cache,
|
||||
void (*callback)(void *key, void *data))
|
||||
{
|
||||
size_t i;
|
||||
stx_cache_entry_t *entry;
|
||||
|
||||
for (i = 0; i < cache->hash_size; i++) {
|
||||
for (entry = cache->table[i]; entry; entry = entry->next) {
|
||||
if (!entry->delete_pending)
|
||||
(*callback)(entry->key, entry->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void stx_cache_traverse_lru(stx_cache_t *cache,
|
||||
void (*callback)(void *key, void *data),
|
||||
unsigned int n)
|
||||
{
|
||||
stx_clist_t *q;
|
||||
stx_cache_entry_t *entry;
|
||||
|
||||
for (q = STX_CLIST_HEAD(&cache->lru_list); q != &cache->lru_list && n;
|
||||
q = q->next, n--) {
|
||||
entry = STX_CACHE_ENTRY_PTR(q);
|
||||
(*callback)(entry->key, entry->data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void stx_cache_traverse_mru(stx_cache_t *cache,
|
||||
void (*callback)(void *key, void *data),
|
||||
unsigned int n)
|
||||
{
|
||||
stx_clist_t *q;
|
||||
stx_cache_entry_t *entry;
|
||||
|
||||
for (q = STX_CLIST_TAIL(&cache->lru_list); q != &cache->lru_list && n;
|
||||
q = q->prev, n--) {
|
||||
entry = STX_CACHE_ENTRY_PTR(q);
|
||||
(*callback)(entry->key, entry->data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t stx_cache_getsize(stx_cache_t *cache)
|
||||
{
|
||||
return cache->cur_size;
|
||||
}
|
||||
|
||||
|
||||
size_t stx_cache_getweight(stx_cache_t *cache)
|
||||
{
|
||||
return cache->cur_weight;
|
||||
}
|
||||
|
||||
|
||||
void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info)
|
||||
{
|
||||
info->max_size = cache->max_size;
|
||||
info->max_weight = cache->max_weight;
|
||||
info->hash_size = cache->hash_size;
|
||||
info->cur_size = cache->cur_size;
|
||||
info->cur_weight = cache->cur_weight;
|
||||
info->hits = cache->hits;
|
||||
info->lookups = cache->lookups;
|
||||
info->inserts = cache->inserts;
|
||||
info->deletes = cache->deletes;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Cache entry methods
|
||||
*/
|
||||
|
||||
stx_cache_entry_t *stx_cache_entry_create(void *key, void *data,
|
||||
size_t weight)
|
||||
{
|
||||
stx_cache_entry_t *newentry;
|
||||
|
||||
newentry = (stx_cache_entry_t *)calloc(1, sizeof(stx_cache_entry_t));
|
||||
if (newentry == NULL)
|
||||
return NULL;
|
||||
|
||||
newentry->key = key;
|
||||
newentry->data = data;
|
||||
newentry->weight = weight;
|
||||
|
||||
return newentry;
|
||||
}
|
||||
|
||||
|
||||
void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry)
|
||||
{
|
||||
entry->delete_pending = 1;
|
||||
|
||||
if (entry->ref_count > 0)
|
||||
return;
|
||||
|
||||
if (entry->pthis) {
|
||||
*entry->pthis = entry->next;
|
||||
if (entry->next)
|
||||
entry->next->pthis = entry->pthis;
|
||||
|
||||
cache->cur_size--;
|
||||
cache->cur_weight -= entry->weight;
|
||||
cache->deletes++;
|
||||
STX_CLIST_REMOVE_LINK(&(entry->lru_link));
|
||||
}
|
||||
|
||||
if (cache->cleanup_fn)
|
||||
cache->cleanup_fn(entry->key, entry->data);
|
||||
|
||||
entry->pthis = NULL;
|
||||
entry->key = NULL;
|
||||
entry->data = NULL;
|
||||
free(entry);
|
||||
}
|
||||
|
||||
|
||||
stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key)
|
||||
{
|
||||
unsigned long bucket;
|
||||
stx_cache_entry_t *entry;
|
||||
|
||||
cache->lookups++;
|
||||
bucket = cache->key_hash_fn(key) % cache->hash_size;
|
||||
for (entry = cache->table[bucket]; entry; entry = entry->next) {
|
||||
if (!entry->delete_pending && cache->key_cmp_fn(key, entry->key) == 0)
|
||||
break;
|
||||
}
|
||||
if (entry) {
|
||||
cache->hits++;
|
||||
if (entry->ref_count == 0)
|
||||
STX_CLIST_REMOVE_LINK(&(entry->lru_link));
|
||||
entry->ref_count++;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
void stx_cache_entry_release(stx_cache_t *cache, stx_cache_entry_t *entry)
|
||||
{
|
||||
if (entry->ref_count == 0)
|
||||
return;
|
||||
|
||||
entry->ref_count--;
|
||||
|
||||
if (entry->ref_count == 0) {
|
||||
STX_CLIST_APPEND_LINK(&(entry->lru_link), &(cache->lru_list));
|
||||
if (entry->delete_pending)
|
||||
stx_cache_entry_delete(cache, entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry)
|
||||
{
|
||||
stx_cache_entry_t *old_entry;
|
||||
unsigned long bucket;
|
||||
|
||||
/*
|
||||
* If cache capacity is exceeded, try to remove LRU entries till there is
|
||||
* enough room or LRU list is empty.
|
||||
*/
|
||||
while (cache->cur_weight + entry->weight > cache->max_weight) {
|
||||
old_entry = stx_cache_entry_getlru(cache);
|
||||
if (!old_entry) {
|
||||
/* cache capacity is exceeded and all entries are in use */
|
||||
return -1;
|
||||
}
|
||||
stx_cache_entry_delete(cache, old_entry);
|
||||
}
|
||||
|
||||
/* If cache size is exceeded, remove LRU entry */
|
||||
if (cache->cur_size >= cache->max_size) {
|
||||
old_entry = stx_cache_entry_getlru(cache);
|
||||
if (!old_entry) {
|
||||
/* cache size is exceeded and all entries are in use */
|
||||
return -1;
|
||||
}
|
||||
stx_cache_entry_delete(cache, old_entry);
|
||||
}
|
||||
|
||||
/* Don't add duplicate entries in the cache */
|
||||
bucket = cache->key_hash_fn(entry->key) % cache->hash_size;
|
||||
for (old_entry = cache->table[bucket]; old_entry;
|
||||
old_entry = old_entry->next) {
|
||||
if (!old_entry->delete_pending &&
|
||||
cache->key_cmp_fn(entry->key, old_entry->key) == 0)
|
||||
break;
|
||||
}
|
||||
if (old_entry)
|
||||
stx_cache_entry_delete(cache, old_entry);
|
||||
|
||||
/* Insert in the hash table */
|
||||
entry->next = cache->table[bucket];
|
||||
cache->table[bucket] = entry;
|
||||
entry->pthis = &cache->table[bucket];
|
||||
if (entry->next)
|
||||
entry->next->pthis = &entry->next;
|
||||
entry->ref_count++;
|
||||
|
||||
cache->inserts++;
|
||||
cache->cur_size++;
|
||||
cache->cur_weight += entry->weight;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache)
|
||||
{
|
||||
if (STX_CLIST_IS_EMPTY(&(cache->lru_list)))
|
||||
return NULL;
|
||||
|
||||
return STX_CACHE_ENTRY_PTR(STX_CLIST_HEAD(&(cache->lru_list)));
|
||||
}
|
||||
|
||||
|
||||
int stx_cache_entry_sizeof(void)
|
||||
{
|
||||
return (int)sizeof(stx_cache_entry_t);
|
||||
}
|
||||
|
||||
|
||||
void *stx_cache_entry_getdata(stx_cache_entry_t *entry)
|
||||
{
|
||||
return entry->data;
|
||||
}
|
||||
|
||||
|
||||
void *stx_cache_entry_getkey(stx_cache_entry_t *entry)
|
||||
{
|
||||
return entry->key;
|
||||
}
|
||||
|
||||
|
||||
size_t stx_cache_entry_getweight(stx_cache_entry_t *entry)
|
||||
{
|
||||
return entry->weight;
|
||||
}
|
||||
|
@ -1,367 +0,0 @@
|
||||
Michael Abd-El-Malek contributed this patch. He wrote:
|
||||
----------------------------------------
|
||||
Hello,
|
||||
|
||||
This is a patch that enables programmatically dumping the stack of
|
||||
every thread. This has been useful in debugging deadlocks, etc...
|
||||
Our usage model is that the SIGUSR2 handler calls the new
|
||||
_st_print_thread_stacks function, which dumps the stack for all
|
||||
threads. A convenient feature is that for thread stacks that are the
|
||||
same (which is common for application with a lot of worker threads
|
||||
waiting for work), only one stack trace is printed, along with a
|
||||
count of how many threads have that same stack.
|
||||
|
||||
I use the glibc backtrace function to get the backtrace, and then use
|
||||
popen to execute addr2line and convert memory addresses to file
|
||||
names, function names, and line numbers. If glibc isn't available,
|
||||
_st_print_thread_stacks just prints a warning. And this feature is
|
||||
only available if DEBUG is turned on.
|
||||
|
||||
We've found this feature extremely helpful when debugging.
|
||||
|
||||
The patch can be a bit more robust (it assumes addr2line exists).
|
||||
But I didn't want to go through the hassle of doing this, if the
|
||||
StateThreads community doesn't want to use this patch. (In our
|
||||
environment, addr2line will always be there.)
|
||||
|
||||
Cheers,
|
||||
Mike
|
||||
----------------------------------------
|
||||
Invoking complex functions from a signal handler is not recommended,
|
||||
plus this patch changes the behavior of existing API hooks. It will
|
||||
not become part of State Threads proper but you may find it useful
|
||||
nonetheless. This patch applies to st-1.5.2.
|
||||
|
||||
diff -Nur Makefile.1.5.2 Makefile
|
||||
--- Makefile.1.5.2 Wed Sep 7 14:19:50 2005
|
||||
+++ Makefile Wed Sep 7 14:33:08 2005
|
||||
@@ -255,7 +255,8 @@
|
||||
$(TARGETDIR)/stk.o \
|
||||
$(TARGETDIR)/sync.o \
|
||||
$(TARGETDIR)/key.o \
|
||||
- $(TARGETDIR)/io.o
|
||||
+ $(TARGETDIR)/io.o \
|
||||
+ $(TARGETDIR)/backtrace.o
|
||||
OBJS += $(EXTRA_OBJS)
|
||||
HEADER = $(TARGETDIR)/st.h
|
||||
SLIBRARY = $(TARGETDIR)/libst.a
|
||||
diff -Nur backtrace.c.1.5.2 backtrace.c
|
||||
--- backtrace.c.1.5.2 Wed Dec 31 16:00:00 1969
|
||||
+++ backtrace.c Wed Sep 7 13:40:21 2005
|
||||
@@ -0,0 +1,211 @@
|
||||
+/*
|
||||
+ * The contents of this file are subject to the Mozilla Public
|
||||
+ * License Version 1.1 (the "License"); you may not use this file
|
||||
+ * except in compliance with the License. You may obtain a copy of
|
||||
+ * the License at http://www.mozilla.org/MPL/
|
||||
+ *
|
||||
+ * Software distributed under the License is distributed on an "AS
|
||||
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
+ * implied. See the License for the specific language governing
|
||||
+ * rights and limitations under the License.
|
||||
+ *
|
||||
+ * Contributor(s): Michael Abd-El-Malek (mabdelmalek@cmu.edu)
|
||||
+ * Carnegie Mellon University
|
||||
+ *
|
||||
+ * Alternatively, the contents of this file may be used under the
|
||||
+ * terms of the GNU General Public License Version 2 or later (the
|
||||
+ * "GPL"), in which case the provisions of the GPL are applicable
|
||||
+ * instead of those above. If you wish to allow use of your
|
||||
+ * version of this file only under the terms of the GPL and not to
|
||||
+ * allow others to use your version of this file under the MPL,
|
||||
+ * indicate your decision by deleting the provisions above and
|
||||
+ * replace them with the notice and other provisions required by
|
||||
+ * the GPL. If you do not delete the provisions above, a recipient
|
||||
+ * may use your version of this file under either the MPL or the
|
||||
+ * GPL.
|
||||
+ */
|
||||
+
|
||||
+
|
||||
+
|
||||
+/*
|
||||
+ * This file contains routines for printing a stack trace of all threads.
|
||||
+ * Only works when DEBUG is defined and where glibc is available, since it
|
||||
+ * provides the backtrace() function.
|
||||
+ */
|
||||
+
|
||||
+#define _GNU_SOURCE /* to get program_invocation_name */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+
|
||||
+#if defined(DEBUG) && defined(__GLIBC__)
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include "common.h"
|
||||
+#include <execinfo.h>
|
||||
+#include <inttypes.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+
|
||||
+/* The maximum number of frames to get a stack trace for. If a thread has more
|
||||
+ * frames than this, then we only show the latest X frames. */
|
||||
+#define MAX_NUM_FRAMES 64
|
||||
+
|
||||
+
|
||||
+typedef struct thread_stack_s {
|
||||
+ uint32_t num_frames;
|
||||
+ void* addresses[MAX_NUM_FRAMES]; /* frame pointers */
|
||||
+ char* locations[MAX_NUM_FRAMES]; /* file/function/line numbers */
|
||||
+ uint32_t num_matches;
|
||||
+
|
||||
+ struct thread_stack_s* next;
|
||||
+} thread_stack_t;
|
||||
+
|
||||
+static thread_stack_t* stacks = NULL;
|
||||
+
|
||||
+
|
||||
+/* Converts the function's memory addresses to function names, file names, and
|
||||
+ * line numbers. Calls binutil's addr2line program. */
|
||||
+static void get_symbol_names(thread_stack_t *stack)
|
||||
+{
|
||||
+ char program_to_run[1024], function[256], filename_lineno[256], temp[19];
|
||||
+ FILE* output;
|
||||
+ int num_bytes_left;
|
||||
+ uint32_t i;
|
||||
+
|
||||
+ /* Construct the arguments to addr2line */
|
||||
+ num_bytes_left = sizeof(program_to_run);
|
||||
+ num_bytes_left -= snprintf(program_to_run, sizeof(program_to_run),
|
||||
+ "addr2line -fCe %s", program_invocation_name);
|
||||
+ for (i = 0; i < stack->num_frames && num_bytes_left > 0; ++i) {
|
||||
+ num_bytes_left -= snprintf(temp, sizeof(temp), " %p", stack->addresses[i]);
|
||||
+ strncat(program_to_run, temp, num_bytes_left);
|
||||
+ }
|
||||
+
|
||||
+ /* Use popen to execute addr2line and read its ouput */
|
||||
+ output = popen(program_to_run, "r");
|
||||
+ for (i = 0; i < stack->num_frames; ++i) {
|
||||
+ char* function_listing = (char*) malloc(512);
|
||||
+ fscanf(output, "%255s\n", function);
|
||||
+ fscanf(output, "%255s\n", filename_lineno);
|
||||
+ snprintf(function_listing, 512, "%s at %s", function, filename_lineno);
|
||||
+ stack->locations[i] = function_listing;
|
||||
+ }
|
||||
+ pclose(output);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static void print_stack(thread_stack_t* stack)
|
||||
+{
|
||||
+ int skip_offset = 0, cmp_len;
|
||||
+ uint32_t i;
|
||||
+
|
||||
+ /* Get the function names/filenames/line numbers */
|
||||
+ get_symbol_names(stack);
|
||||
+
|
||||
+ cmp_len = strlen("_st_iterate_threads_helper");
|
||||
+
|
||||
+ /* Print the backtrace */
|
||||
+ for (i = 0; i < stack->num_frames; ++i) {
|
||||
+ /* Skip frames we don't have location info for */
|
||||
+ if (!strncmp(stack->locations[i], "??", 2)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Skip the frames that are used for printing the stack trace */
|
||||
+ if (skip_offset) {
|
||||
+ printf("\t#%2d %s %p\n", i - skip_offset, stack->locations[i],
|
||||
+ stack->addresses[i]);
|
||||
+ } else if (!strncmp(stack->locations[i], "_st_iterate_threads_helper",
|
||||
+ cmp_len)) {
|
||||
+ skip_offset = i + 1;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static void add_current_thread_stack(void)
|
||||
+{
|
||||
+ thread_stack_t *new_stack = malloc(sizeof(thread_stack_t));
|
||||
+ thread_stack_t *search;
|
||||
+
|
||||
+ /* Call glibc function to get the backtrace */
|
||||
+ new_stack->num_frames = backtrace(new_stack->addresses, MAX_NUM_FRAMES);
|
||||
+
|
||||
+ /* Check if we have another stacks that is equivalent. If so, then coaelsce
|
||||
+ * two stacks into one, to minimize output to user. */
|
||||
+ search = stacks;
|
||||
+ while (search) {
|
||||
+ if (search->num_frames == new_stack->num_frames &&
|
||||
+ !memcmp(search->addresses, new_stack->addresses,
|
||||
+ search->num_frames * sizeof(void*))) {
|
||||
+ /* Found an existing stack that is the same as this thread's stack */
|
||||
+ ++search->num_matches;
|
||||
+ free(new_stack);
|
||||
+ return;
|
||||
+ } else {
|
||||
+ search = search->next;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* This is a new stack. Add it to the list of stacks. */
|
||||
+ new_stack->num_matches = 1;
|
||||
+ new_stack->next = stacks;
|
||||
+ stacks = new_stack;
|
||||
+}
|
||||
+
|
||||
+static void print_stack_frames(void)
|
||||
+{
|
||||
+ while (stacks) {
|
||||
+ printf("\n%u thread(s) with this backtrace:\n", stacks->num_matches);
|
||||
+ print_stack(stacks);
|
||||
+ stacks = stacks->next;
|
||||
+ }
|
||||
+ printf("\n");
|
||||
+}
|
||||
+
|
||||
+static void free_stacks(void)
|
||||
+{
|
||||
+ uint32_t i;
|
||||
+ while (stacks) {
|
||||
+ thread_stack_t *next = stacks->next;
|
||||
+ for (i = 0; i < stacks->num_frames; ++i) {
|
||||
+ free(stacks->locations[i]);
|
||||
+ }
|
||||
+ free(stacks);
|
||||
+ stacks = next;
|
||||
+ }
|
||||
+ stacks = NULL;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static void st_print_thread_stack(_st_thread_t *thread, int start_flag,
|
||||
+ int end_flag)
|
||||
+{
|
||||
+ if (end_flag == 0) {
|
||||
+ add_current_thread_stack();
|
||||
+ } else {
|
||||
+ print_stack_frames();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
+void _st_print_thread_stacks(int ignore)
|
||||
+{
|
||||
+ _st_iterate_threads_flag = 1;
|
||||
+ _st_iterate_threads_helper(st_print_thread_stack);
|
||||
+ _st_iterate_threads_flag = 0;
|
||||
+
|
||||
+ /* Deallocate memory */
|
||||
+ free_stacks();
|
||||
+}
|
||||
+
|
||||
+#else /* defined(DEBUG) && defined(__GLIBC__) */
|
||||
+
|
||||
+void _st_print_thread_stacks(int ignore)
|
||||
+{
|
||||
+ printf("%s: need DEBUG mode and glibc-specific functions to read stack.\n",
|
||||
+ __FUNCTION__);
|
||||
+}
|
||||
+#endif /* defined(DEBUG) && defined(__GLIBC__) */
|
||||
diff -Nur common.h.1.5.2 common.h
|
||||
--- common.h.1.5.2 Wed Sep 7 14:18:37 2005
|
||||
+++ common.h Wed Sep 7 14:35:36 2005
|
||||
@@ -371,8 +371,18 @@
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
-void _st_iterate_threads(void);
|
||||
-#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads()
|
||||
+typedef void(*_st_func_ptr_t)(_st_thread_t *thread,
|
||||
+ int start_flag,
|
||||
+ int end_flag);
|
||||
+/* Pointer to function that will be called on thread switch */
|
||||
+extern _st_func_ptr_t _st_iterate_func_ptr;
|
||||
+extern int _st_iterate_threads_flag;
|
||||
+/* Thread iteration function that will call an arbitrary function */
|
||||
+extern void _st_iterate_threads_helper(_st_func_ptr_t func);
|
||||
+#define ST_DEBUG_ITERATE_THREADS() \
|
||||
+ if (_st_iterate_func_ptr) { \
|
||||
+ _st_iterate_threads_helper(_st_iterate_func_ptr); \
|
||||
+ }
|
||||
#else
|
||||
#define ST_DEBUG_ITERATE_THREADS()
|
||||
#endif
|
||||
diff -Nur public.h.1.5.2 public.h
|
||||
--- public.h.1.5.2 Wed Sep 7 11:46:58 2005
|
||||
+++ public.h Wed Sep 7 13:38:46 2005
|
||||
@@ -171,8 +171,10 @@
|
||||
extern st_netfd_t st_open(const char *path, int oflags, mode_t mode);
|
||||
|
||||
#ifdef DEBUG
|
||||
-extern void _st_show_thread_stack(st_thread_t thread, const char *messg);
|
||||
+extern void _st_show_thread_stack(st_thread_t thread, int start_flag,
|
||||
+ int end_flag);
|
||||
extern void _st_iterate_threads(void);
|
||||
+extern void _st_print_thread_stacks(int ignore);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
diff -Nur sched.c.1.5.2 sched.c
|
||||
--- sched.c.1.5.2 Wed Sep 7 10:48:05 2005
|
||||
+++ sched.c Wed Sep 7 13:38:46 2005
|
||||
@@ -919,16 +919,13 @@
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
-/* ARGSUSED */
|
||||
-void _st_show_thread_stack(_st_thread_t *thread, const char *messg)
|
||||
-{
|
||||
-
|
||||
-}
|
||||
-
|
||||
/* To be set from debugger */
|
||||
int _st_iterate_threads_flag = 0;
|
||||
+/* Thread iteration function that will call an arbitrary function */
|
||||
+_st_func_ptr_t _st_iterate_func_ptr = NULL;
|
||||
|
||||
-void _st_iterate_threads(void)
|
||||
+/* This function iterates over all threads, calling "func" for each thread. */
|
||||
+void _st_iterate_threads_helper(_st_func_ptr_t func)
|
||||
{
|
||||
static _st_thread_t *thread = NULL;
|
||||
static jmp_buf orig_jb, save_jb;
|
||||
@@ -944,16 +941,20 @@
|
||||
|
||||
if (thread) {
|
||||
memcpy(thread->context, save_jb, sizeof(jmp_buf));
|
||||
- _st_show_thread_stack(thread, NULL);
|
||||
+ func(thread, 0, 0);
|
||||
} else {
|
||||
if (MD_SETJMP(orig_jb)) {
|
||||
_st_iterate_threads_flag = 0;
|
||||
+ _st_iterate_func_ptr = NULL;
|
||||
thread = NULL;
|
||||
- _st_show_thread_stack(thread, "Iteration completed");
|
||||
+ /* Last thread to iterate through */
|
||||
+ func(thread, 0, 1);
|
||||
return;
|
||||
}
|
||||
+ /* First thread to iterate through */
|
||||
thread = _ST_CURRENT_THREAD();
|
||||
- _st_show_thread_stack(thread, "Iteration started");
|
||||
+ _st_iterate_func_ptr = func;
|
||||
+ func(thread, 1, 0);
|
||||
}
|
||||
|
||||
q = thread->tlink.next;
|
||||
@@ -966,5 +967,17 @@
|
||||
memcpy(save_jb, thread->context, sizeof(jmp_buf));
|
||||
MD_LONGJMP(thread->context, 1);
|
||||
}
|
||||
+
|
||||
+/* ARGSUSED */
|
||||
+void _st_show_thread_stack(_st_thread_t *thread, int start_flag, int end_flag)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+/* Iterate over threads inside debugger; see st/README */
|
||||
+void _st_iterate_threads(void)
|
||||
+{
|
||||
+ _st_iterate_threads_helper(_st_show_thread_stack);
|
||||
+}
|
||||
+
|
||||
#endif /* DEBUG */
|
||||
|
@ -1,91 +0,0 @@
|
||||
#ifndef _STX_H_
|
||||
#define _STX_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <resolv.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include "st.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Basic types definitions
|
||||
*/
|
||||
|
||||
typedef struct _stx_centry stx_cache_entry_t;
|
||||
typedef struct _stx_cache stx_cache_t;
|
||||
|
||||
/* This is public type */
|
||||
typedef struct _stx_cache_info {
|
||||
size_t max_size;
|
||||
size_t max_weight;
|
||||
size_t hash_size;
|
||||
size_t cur_size;
|
||||
size_t cur_weight;
|
||||
unsigned long hits;
|
||||
unsigned long lookups;
|
||||
unsigned long inserts;
|
||||
unsigned long deletes;
|
||||
} stx_cache_info_t;
|
||||
|
||||
|
||||
/*****************************************
|
||||
* Cache and cache entry methods
|
||||
*/
|
||||
|
||||
stx_cache_t *stx_cache_create(size_t max_size, size_t max_weight,
|
||||
size_t hash_size,
|
||||
unsigned long (*key_hash_fn)(const void *key),
|
||||
long (*key_cmp_fn)(const void *key1,
|
||||
const void *key2),
|
||||
void (*cleanup_fn)(void *key, void *data));
|
||||
void stx_cache_empty(stx_cache_t *cache);
|
||||
void stx_cache_traverse(stx_cache_t *cache,
|
||||
void (*callback)(void *key, void *data));
|
||||
void stx_cache_traverse_lru(stx_cache_t *, void (*)(void *, void *),
|
||||
unsigned int);
|
||||
void stx_cache_traverse_mru(stx_cache_t *, void (*)(void *, void *),
|
||||
unsigned int);
|
||||
void stx_cache_getinfo(stx_cache_t *cache, stx_cache_info_t *info);
|
||||
size_t stx_cache_getsize(stx_cache_t *cache);
|
||||
size_t stx_cache_getweight(stx_cache_t *cache);
|
||||
|
||||
|
||||
stx_cache_entry_t *stx_cache_entry_create(void *key, void *data,
|
||||
size_t weight);
|
||||
void stx_cache_entry_delete(stx_cache_t *cache, stx_cache_entry_t *entry);
|
||||
stx_cache_entry_t *stx_cache_entry_lookup(stx_cache_t *cache, const void *key);
|
||||
void stx_cache_entry_release(stx_cache_t *, stx_cache_entry_t *);
|
||||
int stx_cache_entry_insert(stx_cache_t *cache, stx_cache_entry_t *entry);
|
||||
stx_cache_entry_t *stx_cache_entry_getlru(stx_cache_t *cache);
|
||||
int stx_cache_entry_sizeof(void);
|
||||
void *stx_cache_entry_getdata(stx_cache_entry_t *entry);
|
||||
void *stx_cache_entry_getkey(stx_cache_entry_t *entry);
|
||||
size_t stx_cache_entry_getweight(stx_cache_entry_t *entry);
|
||||
|
||||
|
||||
int stx_dns_cache_init(size_t max_size, size_t max_bytes, size_t hash_size);
|
||||
void stx_dns_cache_getinfo(stx_cache_info_t *info);
|
||||
int stx_dns_getaddrlist(const char *hostname, struct in_addr *addrs,
|
||||
int *num_addrs, st_utime_t timeout);
|
||||
int stx_dns_getaddr(const char *hostname, struct in_addr *addr,
|
||||
st_utime_t timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_STX_H_ */
|
||||
|
@ -1,197 +0,0 @@
|
||||
/*
|
||||
* File I/O extension to the State Threads Library.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the file I/O extension to the State Threads Library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Jeff
|
||||
* <jlb-st@houseofdistraction.com>. Portions created by the Initial
|
||||
* Developer are Copyright (C) 2002 the Initial Developer. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributor(s): (none)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU General Public License Version 2 or later (the
|
||||
* "GPL"), in which case the provisions of the GPL are applicable
|
||||
* instead of those above. If you wish to allow use of your
|
||||
* version of this file only under the terms of the GPL and not to
|
||||
* allow others to use your version of this file under the MPL,
|
||||
* indicate your decision by deleting the provisions above and
|
||||
* replace them with the notice and other provisions required by
|
||||
* the GPL. If you do not delete the provisions above, a recipient
|
||||
* may use your version of this file under either the MPL or the
|
||||
* GPL.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "stx_fileio.h"
|
||||
|
||||
#define STX_FILEIO_SIGNUM SIGUSR2
|
||||
|
||||
typedef struct {
|
||||
st_netfd_t data_fd;
|
||||
st_netfd_t control_fd;
|
||||
pid_t pid;
|
||||
} fileio_data_t;
|
||||
|
||||
#define FILEREADER_MAX_READ 1024
|
||||
|
||||
typedef struct {
|
||||
off_t offset;
|
||||
ssize_t nbytes;
|
||||
} file_reader_cb_t;
|
||||
|
||||
/**
|
||||
* Fork a process to read a file and return its pid. Receives
|
||||
* offset/length commands from control stream and sends corresponding data
|
||||
* to out stream. A zero length on the control stream signals an end.
|
||||
*
|
||||
* @param fd stream from which to read
|
||||
* @param control_out receives the file descriptor to which control commands can be sent
|
||||
* @param fd_out receives the file descriptor from which the output of the command can be read.
|
||||
* @return PID of the process created to execute the command
|
||||
*/
|
||||
pid_t
|
||||
file_reader(int fd, int *fd_control, int *fd_out)
|
||||
{
|
||||
pid_t pid;
|
||||
int control_pipe[2], out_pipe[2];
|
||||
|
||||
if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0)
|
||||
return (pid_t)-1;
|
||||
|
||||
pid = fork();
|
||||
if (pid == (pid_t) -1)
|
||||
{
|
||||
close(control_pipe[0]);
|
||||
close(control_pipe[1]);
|
||||
close(out_pipe[0]);
|
||||
close(out_pipe[1]);
|
||||
return pid;
|
||||
}
|
||||
else if (pid == (pid_t) 0)
|
||||
{
|
||||
// child
|
||||
off_t pos = 0;
|
||||
file_reader_cb_t cb;
|
||||
char buf[FILEREADER_MAX_READ];
|
||||
if (fd == -1)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) {
|
||||
ssize_t nb;
|
||||
if (0 >= cb.nbytes)
|
||||
goto clean_exit;
|
||||
if (pos != cb.offset) {
|
||||
pos = lseek(fd, cb.offset, SEEK_SET);
|
||||
if (pos == (off_t)-1)
|
||||
break;
|
||||
}
|
||||
nb = read(fd, buf, cb.nbytes);
|
||||
if (nb == (ssize_t)-1)
|
||||
break;
|
||||
pos += nb;
|
||||
write(out_pipe[1], (char *)&nb, sizeof(nb));
|
||||
write(out_pipe[1], buf, nb);
|
||||
}
|
||||
perror("ERROR: file_reader: ");
|
||||
clean_exit:
|
||||
close(control_pipe[0]);
|
||||
close(control_pipe[1]);
|
||||
close(out_pipe[0]);
|
||||
close(out_pipe[1]);
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// parent
|
||||
close(out_pipe[1]);
|
||||
close(control_pipe[0]);
|
||||
*fd_out = out_pipe[0];
|
||||
*fd_control = control_pipe[1];
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* fileio_data_t destructor callback
|
||||
*/
|
||||
static void
|
||||
fileio_data_destructor(void *dat_in)
|
||||
{
|
||||
if (dat_in) {
|
||||
fileio_data_t *dat = (fileio_data_t *)dat_in;
|
||||
file_reader_cb_t cb;
|
||||
cb.offset = 0;
|
||||
cb.nbytes = 0;
|
||||
st_write(dat->control_fd, (char *)&cb, sizeof(cb),
|
||||
ST_UTIME_NO_TIMEOUT);
|
||||
waitpid(dat->pid, NULL, 0);
|
||||
st_netfd_close(dat->control_fd);
|
||||
st_netfd_close(dat->data_fd);
|
||||
free(dat_in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve fileio_data_t struct from an st descriptor. Create and store
|
||||
* a new one if needed.
|
||||
*/
|
||||
static fileio_data_t *get_fileio_data(st_netfd_t fd)
|
||||
{
|
||||
fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd);
|
||||
if (!dat) {
|
||||
int fd_control, fd_out;
|
||||
pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out);
|
||||
if (pid != (pid_t)-1) {
|
||||
dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t));
|
||||
dat->control_fd = st_netfd_open(fd_control);
|
||||
dat->data_fd = st_netfd_open(fd_out);
|
||||
dat->pid = pid;
|
||||
st_netfd_setspecific(fd, dat, fileio_data_destructor);
|
||||
}
|
||||
}
|
||||
return dat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from the specified section of a file. Uses a forked
|
||||
* file_reader process to do the actual reading so as to avoid causing all
|
||||
* State Threads to block.
|
||||
*
|
||||
* @param fd must refer to a seekable file.
|
||||
* @param offset absolute offset within the file
|
||||
* @param buf output buffer
|
||||
* @param nbytes size of the output buffer
|
||||
* @param timeout
|
||||
*/
|
||||
ssize_t
|
||||
stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout)
|
||||
{
|
||||
fileio_data_t *dat = get_fileio_data(fd);
|
||||
if (dat) {
|
||||
file_reader_cb_t cb;
|
||||
ssize_t ret = (ssize_t)-1;
|
||||
cb.offset = offset;
|
||||
cb.nbytes = nbytes;
|
||||
st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout);
|
||||
if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) {
|
||||
return st_read(dat->data_fd, buf, ret, timeout);
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return (ssize_t)-1;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* File I/O extension to the State Threads Library.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is the file I/O extension to the State Threads Library.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Jeff
|
||||
* <jlb-st@houseofdistraction.com>. Portions created by the Initial
|
||||
* Developer are Copyright (C) 2002 the Initial Developer. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributor(s): (none)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU General Public License Version 2 or later (the
|
||||
* "GPL"), in which case the provisions of the GPL are applicable
|
||||
* instead of those above. If you wish to allow use of your
|
||||
* version of this file only under the terms of the GPL and not to
|
||||
* allow others to use your version of this file under the MPL,
|
||||
* indicate your decision by deleting the provisions above and
|
||||
* replace them with the notice and other provisions required by
|
||||
* the GPL. If you do not delete the provisions above, a recipient
|
||||
* may use your version of this file under either the MPL or the
|
||||
* GPL.
|
||||
*/
|
||||
|
||||
#ifndef __STX_FILEIO_H__
|
||||
#define __STX_FILEIO_H__
|
||||
|
||||
#include <st.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern ssize_t stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !__STX_FILEIO_H__ */
|
@ -1,112 +0,0 @@
|
||||
#include "stx.h"
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
#define MAX_ADDRS 128
|
||||
#define TIMEOUT (4*1000000LL)
|
||||
|
||||
static void do_resolve(const char *host)
|
||||
{
|
||||
struct in_addr addrs[MAX_ADDRS];
|
||||
int i, n = MAX_ADDRS;
|
||||
|
||||
if (stx_dns_getaddrlist(host, addrs, &n, TIMEOUT) < 0) {
|
||||
fprintf(stderr, "stx_dns_getaddrlist: can't resolve %s: ", host);
|
||||
if (h_errno == NETDB_INTERNAL)
|
||||
perror("");
|
||||
else
|
||||
herror("");
|
||||
} else {
|
||||
if (n > 0)
|
||||
printf("%-40s %s\n", (char *)host, inet_ntoa(addrs[0]));
|
||||
for (i = 1; i < n; i++)
|
||||
printf("%-40s %s\n", "", inet_ntoa(addrs[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void show_info(void)
|
||||
{
|
||||
stx_cache_info_t info;
|
||||
|
||||
stx_dns_cache_getinfo(&info);
|
||||
printf("DNS cache info:\n\n");
|
||||
printf("max_size: %8d\n", (int)info.max_size);
|
||||
printf("capacity: %8d bytes\n", (int)info.max_weight);
|
||||
printf("hash_size: %8d\n", (int)info.hash_size);
|
||||
printf("cur_size: %8d\n"
|
||||
"cur_mem: %8d bytes\n"
|
||||
"hits: %8d\n"
|
||||
"lookups: %8d\n"
|
||||
"inserts: %8d\n"
|
||||
"deletes: %8d\n",
|
||||
(int)info.cur_size, (int)info.cur_weight, (int)info.hits,
|
||||
(int)info.lookups, (int)info.inserts, (int)info.deletes);
|
||||
}
|
||||
|
||||
extern stx_cache_t *_stx_dns_cache;
|
||||
|
||||
static void printhost(void *host, void *data)
|
||||
{
|
||||
printf("%s\n", (char *)host);
|
||||
}
|
||||
|
||||
static void show_lru(void)
|
||||
{
|
||||
printf("LRU hosts:\n\n");
|
||||
stx_cache_traverse_lru(_stx_dns_cache, printhost, 10);
|
||||
}
|
||||
|
||||
static void show_mru(void)
|
||||
{
|
||||
printf("MRU hosts:\n\n");
|
||||
stx_cache_traverse_mru(_stx_dns_cache, printhost, 10);
|
||||
}
|
||||
|
||||
static void flush_cache(void)
|
||||
{
|
||||
stx_cache_empty(_stx_dns_cache);
|
||||
printf("DNS cache is empty\n");
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
char line[256];
|
||||
char str[sizeof(line)];
|
||||
|
||||
st_init();
|
||||
stx_dns_cache_init(100, 10000, 101);
|
||||
|
||||
for ( ; ; ) {
|
||||
fputs("> ", stdout);
|
||||
fflush(stdout);
|
||||
if (!fgets(line, sizeof(line), stdin))
|
||||
break;
|
||||
if (sscanf(line, "%s", str) != 1)
|
||||
continue;
|
||||
if (strcmp(str, "exit") == 0 || strcmp(str, "quit") == 0)
|
||||
break;
|
||||
if (strcmp(str, "info") == 0) {
|
||||
show_info();
|
||||
continue;
|
||||
}
|
||||
if (strcmp(str, "lru") == 0) {
|
||||
show_lru();
|
||||
continue;
|
||||
}
|
||||
if (strcmp(str, "mru") == 0) {
|
||||
show_mru();
|
||||
continue;
|
||||
}
|
||||
if (strcmp(str, "flush") == 0) {
|
||||
flush_cache();
|
||||
continue;
|
||||
}
|
||||
|
||||
do_resolve(str);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
EXPORTS
|
||||
st_accept @62
|
||||
st_cond_broadcast @63
|
||||
st_cond_destroy @64
|
||||
st_cond_new @65
|
||||
st_cond_signal @66
|
||||
st_cond_timedwait @67
|
||||
st_cond_wait @68
|
||||
st_connect @69
|
||||
st_getfdlimit @70
|
||||
st_init @71
|
||||
st_key_create @72
|
||||
st_key_getlimit @73
|
||||
st_mutex_destroy @74
|
||||
st_mutex_lock @75
|
||||
st_mutex_new @76
|
||||
st_mutex_trylock @77
|
||||
st_mutex_unlock @78
|
||||
st_netfd_close @79
|
||||
st_netfd_fileno @80
|
||||
st_netfd_free @81
|
||||
st_netfd_getspecific @82
|
||||
st_netfd_open @83
|
||||
st_netfd_open_socket @84
|
||||
st_netfd_poll @85
|
||||
st_netfd_serialize_accept @86
|
||||
st_netfd_setspecific @87
|
||||
st_open @88
|
||||
st_poll @89
|
||||
st_randomize_stacks @90
|
||||
st_read @91
|
||||
st_read_fully @92
|
||||
st_read_resid @93
|
||||
st_recvfrom @94
|
||||
st_sendto @95
|
||||
st_sleep @96
|
||||
st_thread_create @97
|
||||
st_thread_exit @98
|
||||
st_thread_getspecific @99
|
||||
st_thread_interrupt @100
|
||||
st_thread_join @101
|
||||
st_thread_self @102
|
||||
st_thread_setspecific @103
|
||||
st_time @104
|
||||
st_timecache_set @105
|
||||
st_usleep @106
|
||||
st_utime @107
|
||||
st_utime_last_clock @108
|
||||
st_write @109
|
||||
st_write_resid @110
|
||||
st_writev @111
|
@ -1,45 +0,0 @@
|
||||
#
|
||||
# This script can be used to automatically guess target OS.
|
||||
# It requires the config.guess utility which is a part of GNU Autoconf.
|
||||
# GNU Autoconf can be downloaded from ftp://ftp.gnu.org/gnu/autoconf/
|
||||
#
|
||||
# Use "default" as a make target for automatic builds.
|
||||
#
|
||||
|
||||
|
||||
# Specify path to the config.guess utility (unless set via environment)
|
||||
#CONFIG_GUESS_PATH=
|
||||
|
||||
|
||||
if [ x"$CONFIG_GUESS_PATH" = x ]; then
|
||||
echo "Error: CONFIG_GUESS_PATH variable is not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_GUESS_PATH/config.guess" ]; then
|
||||
echo "Can't find $CONFIG_GUESS_PATH/config.guess utility. Wrong path?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sys_info=`/bin/sh $CONFIG_GUESS_PATH/config.guess`
|
||||
|
||||
echo "Building for $sys_info"
|
||||
|
||||
case "$sys_info" in
|
||||
*-ibm-aix4* ) OS=AIX ;;
|
||||
*-freebsd* ) OS=FREEBSD ;;
|
||||
hppa*-hp-hpux11*) OS=HPUX ;;
|
||||
*-sgi-irix6* ) OS=IRIX ;;
|
||||
*-linux* ) OS=LINUX ;;
|
||||
*-netbsd* ) OS=NETBSD ;;
|
||||
*-openbsd* ) OS=OPENBSD ;;
|
||||
*-dec-osf* ) OS=OSF1 ;;
|
||||
*-solaris2* ) OS=SOLARIS ;;
|
||||
*-darwin* ) OS=DARWIN ;;
|
||||
* ) OS=
|
||||
echo "Sorry, unsupported OS"
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
echo "Making with OS=$OS"
|
||||
|
@ -1,79 +0,0 @@
|
||||
Summary: State Threads Library
|
||||
Name: st
|
||||
Version: 1.9
|
||||
Release: 1
|
||||
Copyright: MPL 1.2 or GPL 2+
|
||||
Packager: Wesley W. Terpstra <wesley@terpstra.ca>
|
||||
Source: http://prdownloads.sourceforge.net/state-threads/st-%{version}.tar.gz
|
||||
Prefix: /usr
|
||||
BuildRoot: /tmp/%{name}-%{version}-build
|
||||
Group: Development/Libraries
|
||||
|
||||
%description
|
||||
The State Threads library has an interface similar to POSIX threads.
|
||||
|
||||
However, the threads are actually all run in-process. This type of
|
||||
threading allows for controlled schedualing points. It is highly useful
|
||||
for designing robust and extremely scalable internet applications since
|
||||
there is no resource contention and locking is generally unnecessary.
|
||||
|
||||
It can be combined with traditional threading or multiple process
|
||||
parallelism to take advantage of multiple processors.
|
||||
|
||||
See: <http://state-threads.sourceforge.net/docs/st.html> for further
|
||||
information about how state threads improve performance.
|
||||
|
||||
%package -n libst-devel
|
||||
Summary: State Threads Library - Development Files
|
||||
Group: Development/Libraries
|
||||
Requires: libst1
|
||||
|
||||
%description -n libst-devel
|
||||
Development headers and documentation for libst
|
||||
|
||||
%package -n libst1
|
||||
Summary: State Threads Library - Shared Libs Major 1
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n libst1
|
||||
Shared libraries for running applications linked against api version 1.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
make CONFIG_GUESS_PATH=/usr/share/automake default-optimized
|
||||
|
||||
%install
|
||||
if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi
|
||||
|
||||
mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig
|
||||
mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/include
|
||||
mkdir -m 0755 -p ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel
|
||||
cp -a obj/libst.* ${RPM_BUILD_ROOT}/%{prefix}/lib
|
||||
cp -a obj/st.h ${RPM_BUILD_ROOT}/%{prefix}/include
|
||||
sed "s*@prefix@*%{prefix}*g" <st.pc >${RPM_BUILD_ROOT}/%{prefix}/lib/pkgconfig/st.pc
|
||||
cp -a docs/* ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/
|
||||
cp -a examples ${RPM_BUILD_ROOT}/%{prefix}/share/doc/libst-devel/
|
||||
|
||||
%post -n libst1
|
||||
/sbin/ldconfig %{prefix}/lib
|
||||
|
||||
%files -n libst1
|
||||
%defattr(-,root,root)
|
||||
%{prefix}/lib/lib*.so.*
|
||||
|
||||
%files -n libst-devel
|
||||
%defattr(-,root,root)
|
||||
%{prefix}/include/*
|
||||
%{prefix}/lib/lib*.a
|
||||
%{prefix}/lib/lib*.so
|
||||
%{prefix}/lib/pkgconfig/st.pc
|
||||
%{prefix}/share/doc/libst-devel/*
|
||||
|
||||
%clean
|
||||
if [ -d ${RPM_BUILD_ROOT} ]; then rm -rf ${RPM_BUILD_ROOT}; fi
|
||||
|
||||
%changelog
|
||||
* Wed Dec 26 2001 Wesley W. Terpstra <wesley@terpstra.ca>
|
||||
- first rpms for libst-1.3.tar.gz
|
Loading…
Reference in New Issue