Commit a8e5eec6 authored by Lubomir Bulej's avatar Lubomir Bulej

DiSL-Agent: Extensively refactored the agent and split the code into multiple files.

DiSL-Agent: Added support for passing code transformation options to server.
DiSL-Agent: Added support for BypassCheck redefinition after JVM bootstrap.
DiSL-Agent: Renamed src-agent-c to src-agent.

DiSL: DynamicBypassCheck->BypassCheck, it is used for all checks, not only dynamic.
DiSL: Removed Bootstrap class for redefinition of the BypassCheck class.
DiSL: Removed DiSL Java agent.
DiSL: Updated server to accept the new protocol (it does not take advatage of it yet).
DiSL: Moved default BypassCheck from src-dynbypass into src.
DiSL: Removed src-dybypass-act.

Updated build.properties and build.xml to reflect all the changes.
Updated runClient.sh to include the new bypass mode options.
parent e9e2356c
......@@ -3,7 +3,6 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="src-re-disp"/>
<classpathentry kind="src" path="src-re-server"/>
<classpathentry kind="src" path="src-agent-java"/>
<classpathentry kind="src" path="src-test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/asm-debug-all-4.0.jar"/>
......
......@@ -2,19 +2,16 @@ asm.lib=asm-debug-all-4.0.jar
asm.path=lib/${asm.lib}
src.disl=src
src.agent=src-agent
src.dynbypass=src-dynbypass
src.dynbypass.act=src-dynbypass-act
src.agent.java=src-agent-java
src.agent.c=src-agent-c
src.reserver=src-re-server
src.redispatch=src-re-disp
src.reagent=src-re-agent
src.redispatch=src-re-disp
src.reserver=src-re-server
src.test=src-test
bin=bin
build=build
build.thread=build-thread
build.afterbootstrap=build-abs
extendedthread.path=${build}/extendedthread.jar
......
......@@ -41,14 +41,10 @@
</javac>
</target>
<target name="compile-agent-java" depends="compile-dynamicbypass">
<javac srcdir="${src.agent.java}" destdir="${bin}" debug="true" includeAntRuntime="false">
<classpath refid="buildpath" />
</javac>
</target>
<target name="compile-agent-c">
<exec executable="make" dir="${src.agent.c}" />
<target name="compile-agent">
<exec executable="make" dir="${src.agent}">
<arg value="whole" />
</exec>
</target>
<target name="compile-reserver">
......@@ -73,7 +69,7 @@
</javac>
</target>
<target name="compile" depends="compile-disl,compile-agent-java,compile-agent-c,compile-test,compile-reserver,compile-redispatch" />
<target name="compile" depends="compile-disl,compile-agent,compile-test,compile-reserver,compile-redispatch" />
<target name="package-dislserver" depends="compile-disl">
<jar basedir="${bin}" destfile="${build}/disl-server.jar"
......@@ -88,36 +84,6 @@
</jar>
</target>
<target name="compile-dynamicbypass-afterbootstrap">
<mkdir dir="${build.afterbootstrap}" />
<javac srcdir="${src.dynbypass.act}" destdir="${build.afterbootstrap}" debug="true" includeAntRuntime="false">
<classpath refid="buildpath" />
</javac>
</target>
<target name="package-dislagent-java" depends="compile-agent-java,compile-dynamicbypass,compile-dynamicbypass-afterbootstrap">
<!-- rename after bootstrap class -->
<move file="${build.afterbootstrap}/ch/usi/dag/disl/dynamicbypass/DynamicBypassCheck.class"
tofile="${build.afterbootstrap}/DynamicBypassCheck-AfterBootstrap.class" />
<jar basedir="${bin}" destfile="${build}/disl-agent.jar"
includes="ch/usi/dag/dislagent/**,ch/usi/dag/disl/dynamicbypass/**">
<manifest>
<attribute name="Premain-Class" value="ch.usi.dag.dislagent.DiSLAgent" />
<attribute name="Can-Redefine-Classes" value="true" />
<attribute name="DiSL-Version" value="${disl.version}" />
</manifest>
<!-- include after bootstrap class -->
<fileset file="${build.afterbootstrap}/DynamicBypassCheck-AfterBootstrap.class" />
</jar>
<!-- delete dir with after bootstrap class -->
<delete dir="${build.afterbootstrap}" />
</target>
<target name="package-reserver" depends="compile-reserver">
<jar basedir="${bin}" destfile="${build}/dislre-server.jar"
includes="ch/usi/dag/dislreserver/**">
......@@ -135,7 +101,7 @@
</jar>
</target>
<target name="package" depends="package-dislserver,package-dislagent-java,package-reserver,package-redispatch,compile-agent-c,compile-reagent" />
<target name="package" depends="package-dislserver,package-reserver,package-redispatch,compile-agent,compile-reagent" />
<target name="eclipse-agent" description="creates simple agent jar file for eclipse">
<mkdir dir="build" />
......@@ -165,8 +131,7 @@
<delete dir="${bin}" />
<delete dir="${build}" />
<delete dir="${build.thread}" />
<delete dir="${build.afterbootstrap}" />
<exec executable="make" dir="${src.agent.c}">
<exec executable="make" dir="${src.agent}">
<arg value="clean" />
</exec>
<exec executable="make" dir="${src.reagent}">
......
......@@ -14,10 +14,10 @@ fi
# determine libs depending on the OS
OS=`uname`
if [ "${OS}" = "Darwin" ]; then
C_AGENT="${DISL_LIB_P}/libdislagent.jnilib"
AGENT="${DISL_LIB_P}/libdislagent.jnilib"
RE_AGENT="${DISL_LIB_P}/libdislreagent.jnilib"
else
C_AGENT="${DISL_LIB_P}/libdislagent.so"
AGENT="${DISL_LIB_P}/libdislagent.so"
RE_AGENT="${DISL_LIB_P}/libdislreagent.so"
fi
......@@ -26,8 +26,13 @@ INSTR_LIB=$1
shift
# start the client
# options available:
# -Ddisl.bypass=never \
# -Ddisl.bypass=bootstrap \
# -Ddisl.bypass=dynamic \
# -Ddisl.splitmethods=false \
# -Ddisl.excepthandler=true \
${JAVA_HOME:+$JAVA_HOME/jre/bin/}java \
-agentpath:${C_AGENT} -agentpath:${RE_AGENT} \
-javaagent:${DISL_LIB_P}/disl-agent.jar \
-Xbootclasspath/a:${DISL_LIB_P}/disl-agent.jar:${INSTR_LIB}:${DISL_LIB_P}/dislre-dispatch.jar \
-agentpath:${AGENT} -agentpath:${RE_AGENT} \
-Xbootclasspath/a:${INSTR_LIB}:${DISL_LIB_P}/dislre-dispatch.jar \
"$@"
This diff is collapsed.
package ch.usi.dag.dislagent;
import java.lang.instrument.Instrumentation;
import ch.usi.dag.disl.dynamicbypass.Bootstrap;
public class DiSLAgent {
private static volatile Instrumentation myInstrumentation;
public static void premain(String agentArguments,
Instrumentation instrumentation) {
myInstrumentation = instrumentation;
if (!Boolean.getBoolean("dislserver.noBootstrap")) {
Bootstrap.completed(instrumentation);
}
}
public static Instrumentation getInstrumentation() {
return myInstrumentation;
}
}
......@@ -7,15 +7,22 @@ BUILDDIR=../build
# Source lists
LIBNAME=dislagent
SOURCES=dislagent.c
SOURCES=bytecode.c common.c jvmtiutil.c connection.c \
connpool.c msgchannel.c network.c dislagent.c
HEADERS=$(wildcard *.h) codeflags.h
# Object files needed to create library
OBJECTS=$(SOURCES:%.c=%.o)
# Library name and options needed to build it
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
LIBRARY=lib$(LIBNAME).so
endif
ifeq ($(UNAME), Darwin)
LIBRARY=lib$(LIBNAME).jnilib
endif
......@@ -25,31 +32,55 @@ endif
LINK_SHARED=$(LINK.c) -shared -o $@
# GNU Compiler options needed to build it
COMMON_FLAGS=-fPIC
COMMON_FLAGS=-fPIC -std=gnu99
# Options that help find errors
COMMON_FLAGS+= -W -Wall -Wextra -O3 -Wno-unused-parameter
COMMON_FLAGS+= -W -Wall -Wextra -Wno-unused-parameter
CFLAGS += $(COMMON_FLAGS)
CFLAGS += -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
# add debugging output
ifeq ($(DEBUG), TRUE)
ifneq ($(DEBUG),)
CFLAGS += -DDEBUG
else
CFLAGS += -DNDEBUG -O3
endif
build: $(LIBRARY)
cp $(LIBRARY) $(BUILDDIR)
# Build native library
$(LIBRARY): $(OBJECTS)
ifneq ($(WHOLE),)
$(LIBRARY): $(HEADERS) $(SOURCES)
gcc $(CFLAGS) -DWHOLE -fwhole-program -flto -shared -o $@ $(SOURCES)
else
$(LIBRARY): $(HEADERS) $(OBJECTS)
$(LINK_SHARED) $(OBJECTS) $(LIBRARIES)
endif
BPC_CLASSES := $(wildcard bypass/*/*.class)
bytecode.c: $(BPC_CLASSES) bin2class
./bin2class \
$(foreach BPC,$(BPC_CLASSES),$(BPC) $(subst bypass/,bpc_,$(BPC:/BypassCheck.class=))) \
> $@
codeflags.h: codeflags
./codeflags
# Cleanup the built bits
clean:
rm -f $(LIBRARY) $(OBJECTS)
debug:
$(MAKE) DEBUG=TRUE
$(MAKE) DEBUG=1
whole:
$(MAKE) WHOLE=1
all: build
\ No newline at end of file
all: build
#!/bin/bash
cat <<- EOT
/**
* DO NOT EDIT!
*
* This file was generated using the bin2class script.
*/
#include <jvmti.h>
EOT
while [ $# -gt 1 ]; do
FILE=$1; shift
NAME=$1; shift
cat << EOT
jvmtiClassDefinition ${NAME}_classdef = {
.class_byte_count = $(stat --format '%s' ${FILE}),
.class_bytes = (unsigned char *)
$(hexdump -e '"\t\t" "Q" 16/1 "xxx%02x" "Q" "\n"' ${FILE} |sed 's/xxx //g; y/Q/"/; s/xxx/\\x/g'),
};
EOT
done
package ch.usi.dag.disl.dynamicbypass;
/**
* Checks whether to bypass instrumented code. This version is to be used when
* dynamic bypass is disabled. After the JVM bootstrap phase, this will completely
* disable execution of uninstrumented code.
*/
public final class BypassCheck {
public static boolean executeUninstrumented () {
return true;
}
}
package ch.usi.dag.disl.dynamicbypass;
/**
* Checks whether to bypass instrumented code. This version is to be used with
* dynamic bypass and results in execution of uninstrumented code when in the
* scope of instrumentation code.
*/
public final class BypassCheck {
public static boolean executeUninstrumented () {
return DynamicBypass.isActivated ();
}
}
package ch.usi.dag.disl.dynamicbypass;
/**
* Checks whether to bypass instrumented code. This version is to be used when
* dynamic bypass is disabled. After the JVM bootstrap phase, this will completely
* disable execution of uninstrumented code.
*/
public final class BypassCheck {
public static boolean executeUninstrumented () {
return false;
}
}
/**
* DO NOT EDIT!
*
* This file was generated using the bin2class script.
*/
#include <jvmti.h>
jvmtiClassDefinition bpc_never_classdef = {
.class_byte_count = 296,
.class_bytes = (unsigned char *)
"\xca\xfe\xba\xbe\x00\x00\x00\x30\x00\x0f\x0a\x00\x03\x00\x0c\x07"
"\x00\x0d\x07\x00\x0e\x01\x00\x06\x3c\x69\x6e\x69\x74\x3e\x01\x00"
"\x03\x28\x29\x56\x01\x00\x04\x43\x6f\x64\x65\x01\x00\x0f\x4c\x69"
"\x6e\x65\x4e\x75\x6d\x62\x65\x72\x54\x61\x62\x6c\x65\x01\x00\x15"
"\x65\x78\x65\x63\x75\x74\x65\x55\x6e\x69\x6e\x73\x74\x72\x75\x6d"
"\x65\x6e\x74\x65\x64\x01\x00\x03\x28\x29\x5a\x01\x00\x0a\x53\x6f"
"\x75\x72\x63\x65\x46\x69\x6c\x65\x01\x00\x10\x42\x79\x70\x61\x73"
"\x73\x43\x68\x65\x63\x6b\x2e\x6a\x61\x76\x61\x0c\x00\x04\x00\x05"
"\x01\x00\x29\x63\x68\x2f\x75\x73\x69\x2f\x64\x61\x67\x2f\x64\x69"
"\x73\x6c\x2f\x64\x79\x6e\x61\x6d\x69\x63\x62\x79\x70\x61\x73\x73"
"\x2f\x42\x79\x70\x61\x73\x73\x43\x68\x65\x63\x6b\x01\x00\x10\x6a"
"\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x4f\x62\x6a\x65\x63\x74\x00"
"\x31\x00\x02\x00\x03\x00\x00\x00\x00\x00\x02\x00\x01\x00\x04\x00"
"\x05\x00\x01\x00\x06\x00\x00\x00\x1d\x00\x01\x00\x01\x00\x00\x00"
"\x05\x2a\xb7\x00\x01\xb1\x00\x00\x00\x01\x00\x07\x00\x00\x00\x06"
"\x00\x01\x00\x00\x00\x08\x00\x09\x00\x08\x00\x09\x00\x01\x00\x06"
"\x00\x00\x00\x1a\x00\x01\x00\x00\x00\x00\x00\x02\x03\xac\x00\x00"
"\x00\x01\x00\x07\x00\x00\x00\x06\x00\x01\x00\x00\x00\x0b\x00\x01"
"\x00\x0a\x00\x00\x00\x02\x00\x0b",
};
jvmtiClassDefinition bpc_always_classdef = {
.class_byte_count = 296,
.class_bytes = (unsigned char *)
"\xca\xfe\xba\xbe\x00\x00\x00\x30\x00\x0f\x0a\x00\x03\x00\x0c\x07"
"\x00\x0d\x07\x00\x0e\x01\x00\x06\x3c\x69\x6e\x69\x74\x3e\x01\x00"
"\x03\x28\x29\x56\x01\x00\x04\x43\x6f\x64\x65\x01\x00\x0f\x4c\x69"
"\x6e\x65\x4e\x75\x6d\x62\x65\x72\x54\x61\x62\x6c\x65\x01\x00\x15"
"\x65\x78\x65\x63\x75\x74\x65\x55\x6e\x69\x6e\x73\x74\x72\x75\x6d"
"\x65\x6e\x74\x65\x64\x01\x00\x03\x28\x29\x5a\x01\x00\x0a\x53\x6f"
"\x75\x72\x63\x65\x46\x69\x6c\x65\x01\x00\x10\x42\x79\x70\x61\x73"
"\x73\x43\x68\x65\x63\x6b\x2e\x6a\x61\x76\x61\x0c\x00\x04\x00\x05"
"\x01\x00\x29\x63\x68\x2f\x75\x73\x69\x2f\x64\x61\x67\x2f\x64\x69"
"\x73\x6c\x2f\x64\x79\x6e\x61\x6d\x69\x63\x62\x79\x70\x61\x73\x73"
"\x2f\x42\x79\x70\x61\x73\x73\x43\x68\x65\x63\x6b\x01\x00\x10\x6a"
"\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x4f\x62\x6a\x65\x63\x74\x00"
"\x31\x00\x02\x00\x03\x00\x00\x00\x00\x00\x02\x00\x01\x00\x04\x00"
"\x05\x00\x01\x00\x06\x00\x00\x00\x1d\x00\x01\x00\x01\x00\x00\x00"
"\x05\x2a\xb7\x00\x01\xb1\x00\x00\x00\x01\x00\x07\x00\x00\x00\x06"
"\x00\x01\x00\x00\x00\x08\x00\x09\x00\x08\x00\x09\x00\x01\x00\x06"
"\x00\x00\x00\x1a\x00\x01\x00\x00\x00\x00\x00\x02\x04\xac\x00\x00"
"\x00\x01\x00\x07\x00\x00\x00\x06\x00\x01\x00\x00\x00\x0b\x00\x01"
"\x00\x0a\x00\x00\x00\x02\x00\x0b",
};
jvmtiClassDefinition bpc_dynamic_classdef = {
.class_byte_count = 371,
.class_bytes = (unsigned char *)
"\xca\xfe\xba\xbe\x00\x00\x00\x30\x00\x14\x0a\x00\x04\x00\x0d\x0a"
"\x00\x0e\x00\x0f\x07\x00\x10\x07\x00\x11\x01\x00\x06\x3c\x69\x6e"
"\x69\x74\x3e\x01\x00\x03\x28\x29\x56\x01\x00\x04\x43\x6f\x64\x65"
"\x01\x00\x0f\x4c\x69\x6e\x65\x4e\x75\x6d\x62\x65\x72\x54\x61\x62"
"\x6c\x65\x01\x00\x15\x65\x78\x65\x63\x75\x74\x65\x55\x6e\x69\x6e"
"\x73\x74\x72\x75\x6d\x65\x6e\x74\x65\x64\x01\x00\x03\x28\x29\x5a"
"\x01\x00\x0a\x53\x6f\x75\x72\x63\x65\x46\x69\x6c\x65\x01\x00\x10"
"\x42\x79\x70\x61\x73\x73\x43\x68\x65\x63\x6b\x2e\x6a\x61\x76\x61"
"\x0c\x00\x05\x00\x06\x07\x00\x12\x0c\x00\x13\x00\x0a\x01\x00\x29"
"\x63\x68\x2f\x75\x73\x69\x2f\x64\x61\x67\x2f\x64\x69\x73\x6c\x2f"
"\x64\x79\x6e\x61\x6d\x69\x63\x62\x79\x70\x61\x73\x73\x2f\x42\x79"
"\x70\x61\x73\x73\x43\x68\x65\x63\x6b\x01\x00\x10\x6a\x61\x76\x61"
"\x2f\x6c\x61\x6e\x67\x2f\x4f\x62\x6a\x65\x63\x74\x01\x00\x2b\x63"
"\x68\x2f\x75\x73\x69\x2f\x64\x61\x67\x2f\x64\x69\x73\x6c\x2f\x64"
"\x79\x6e\x61\x6d\x69\x63\x62\x79\x70\x61\x73\x73\x2f\x44\x79\x6e"
"\x61\x6d\x69\x63\x42\x79\x70\x61\x73\x73\x01\x00\x0b\x69\x73\x41"
"\x63\x74\x69\x76\x61\x74\x65\x64\x00\x31\x00\x03\x00\x04\x00\x00"
"\x00\x00\x00\x02\x00\x01\x00\x05\x00\x06\x00\x01\x00\x07\x00\x00"
"\x00\x1d\x00\x01\x00\x01\x00\x00\x00\x05\x2a\xb7\x00\x01\xb1\x00"
"\x00\x00\x01\x00\x08\x00\x00\x00\x06\x00\x01\x00\x00\x00\x08\x00"
"\x09\x00\x09\x00\x0a\x00\x01\x00\x07\x00\x00\x00\x1c\x00\x01\x00"
"\x00\x00\x00\x00\x04\xb8\x00\x02\xac\x00\x00\x00\x01\x00\x08\x00"
"\x00\x00\x06\x00\x01\x00\x00\x00\x0b\x00\x01\x00\x0b\x00\x00\x00"
"\x02\x00\x0c",
};
#ifndef _BYTECODE_H_
#define _BYTECODE_H_
#include <jvmti.h>
/**
* Name of the class used for bypass checks.
*/
#define BPC_CLASS_NAME "ch/usi/dag/disl/dynamicbypass/BypassCheck"
/**
* Externs for various implementations of the BypassCheck class.
*/
extern jvmtiClassDefinition bpc_always_classdef;
extern jvmtiClassDefinition bpc_dynamic_classdef;
extern jvmtiClassDefinition bpc_never_classdef;
#endif /* _BYTECODE_H_ */
#!/bin/bash
${JAVA_HOME:+$JAVA_HOME/bin/}javah \
-cp ../bin -o codeflags.h \
ch.usi.dag.disl.DiSL.CodeOption.Flag
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ch_usi_dag_disl_DiSL_CodeOption_Flag */
#ifndef _Included_ch_usi_dag_disl_DiSL_CodeOption_Flag
#define _Included_ch_usi_dag_disl_DiSL_CodeOption_Flag
#ifdef __cplusplus
extern "C" {
#endif
#undef ch_usi_dag_disl_DiSL_CodeOption_Flag_CREATE_BYPASS
#define ch_usi_dag_disl_DiSL_CodeOption_Flag_CREATE_BYPASS 1L
#undef ch_usi_dag_disl_DiSL_CodeOption_Flag_DYNAMIC_BYPASS
#define ch_usi_dag_disl_DiSL_CodeOption_Flag_DYNAMIC_BYPASS 2L
#undef ch_usi_dag_disl_DiSL_CodeOption_Flag_SPLIT_METHODS
#define ch_usi_dag_disl_DiSL_CodeOption_Flag_SPLIT_METHODS 4L
#undef ch_usi_dag_disl_DiSL_CodeOption_Flag_CATCH_EXCEPTIONS
#define ch_usi_dag_disl_DiSL_CodeOption_Flag_CATCH_EXCEPTIONS 8L
#ifdef __cplusplus
}
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
/**
* Reports an actual general error. This function implements the slow path of
* check_error(). It prints the given error message and exits with an error code
* indicating a general error.
*/
void
report_error (const char * message) {
fprintf (stderr, "%s%s\n", ERROR_PREFIX, message);
exit (ERROR);
}
/**
* Reports an actual standard library error. This function implements the slow path
* of check_std_error(). It prints the given error message along with the error
* message provided by the standard library, and exits with an error code indicating
* failure in standard library call.
*/
void
report_std_error (const char * message) {
char msgbuf [1024];
snprintf (msgbuf, sizeof (msgbuf), "%s%s", ERROR_PREFIX, message);
perror (msgbuf);
exit (ERROR_STD);
}
/**
* Returns the index of the given value in the given array of values.
* Returns -1 if the value could not be found among the allowed values.
*/
int
find_value_index (const char * strval, const char * values [], int nvals) {
for (int i = 0; i < nvals; i++) {
if (strcasecmp (values [i], strval) == 0) {
return i;
}
}
return -1;
}
#ifndef _COMMON_H_
#define _COMMON_H_
#include <stdio.h>
#include <stdbool.h>
#define ERROR 10000
#define ERROR_SERVER 10003
#define ERROR_STD 10002
#define ERROR_JVMTI 10003
#define ERROR_PREFIX "DiSL agent error: "
void report_error (const char * message);
void report_std_error (const char * message);
/**
* Returns size of an array in array elements.
*/
#define sizeof_array(array) \
(sizeof (array) / sizeof ((array) [0]))
/**
* Checks for a general error by testing the results of an error condition.
* Reports a general error and terminates the program if the condition is true.
*/
inline static void
check_error (bool error, const char * message) {
if (error) {
report_error (message);
}
}
/**
* Checks the return value of a standard library call for error. Reports a standard
* library error and terminates the program if the the returned value matches the
* specified error value.
*/
inline static void
check_std_error (int retval, int errorval, const char * message) {
if (retval == errorval) {
report_std_error (message);
}
}
int find_value_index (const char * strval, const char * values [], int nvals);
#endif /* _COMMON_H_ */
#define _POSIX_C_SOURCE 200908L
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include "common.h"
#include "connection.h"
static void
__connection_init (struct connection * connection, const int sockfd) {
connection->sockfd = sockfd;
list_init (& connection->cp_link);
#ifdef DEBUG
connection->sent_bytes = 0;
connection->recv_bytes = 0;
#endif /* DEBUG */
}
/**
* Creates a new connection to the remote server.
*/
struct connection * connection_open (struct addrinfo * addr) {
//
// Create a stream socket to the given address and connect to the server.
// Upon connection, disable the Nagle algorithm to avoid delays on the
// sender side and create a wrapper object for the connection.
//
int sockfd = socket(addr->ai_family, SOCK_STREAM, 0);
check_std_error(sockfd, -1, "failed to create socket");
int connect_result = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
check_std_error(connect_result, -1, "failed to connect to server");
int tcp_nodelay = 1;
int sso_result = setsockopt (
sockfd, IPPROTO_TCP, TCP_NODELAY,
&tcp_nodelay, sizeof (tcp_nodelay)
);
check_std_error(sso_result, -1, "failed to enable TCP_NODELAY");
//
struct connection * connection =
(struct connection *) malloc (sizeof (struct connection));
check_error (connection == NULL, "failed to allocate connection structure");
__connection_init (connection, sockfd);
return connection;
}
/**
* Closes the connection and destroys the connection structure.
*/
void
connection_close (struct connection * connection) {
assert (connection != NULL);
#if DEBUG
fprintf (
stderr, "socket %d: sent bytes %llu, recv bytes %llu\n",
connection->sockfd, connection->sent_bytes, connection->recv_bytes
);
#endif /* DEBUG */
close (connection->sockfd);
free (connection);
}
//
static void
__socket_send (const int sockfd, const void * buf, const ssize_t len) {
unsigned char * buf_tail = (unsigned char *) buf;
size_t remaining = len;
while (remaining > 0) {
int sent = send (sockfd, buf_tail, remaining, 0);
check_std_error (sent, -1, "error sending data to server");
remaining -= sent;
buf_tail += sent;
}
}
/**
* Sends data into the given connection.
*/
void
connection_send (struct connection * connection, const void * buf, const ssize_t len) {
assert (connection != NULL);
assert (buf != NULL);
assert (len >= 0);
__socket_send (connection->sockfd, buf, len);
#ifdef DEBUG
connection->sent_bytes += len;
#endif /* DEBUG*/
}
/**
* Sends vectored data into the given connection.
*/
void
connection_send_iov (struct connection * connection, const struct iovec * iov, int iovcnt) {
assert (connection != NULL);
assert (iov != NULL);
ssize_t written = writev (connection->sockfd, iov, iovcnt);
check_std_error (written, -1, "error sending data to server");
#ifdef DEBUG
connection->sent_bytes += written;
#endif /* DEBUG */
}
//
static void
__socket_recv (const int sockfd, void * buf, ssize_t len) {
unsigned char * buf_tail = (unsigned char *) buf;
ssize_t remaining = len;
while (remaining > 0) {
int received = recv (sockfd, buf_tail, remaining, 0);
check_std_error(received, -1, "error receiving data from server");
remaining -= received;
buf_tail += received;
}
check_error (remaining < 0, "received more data than expected");
}
/**
* Receives a predefined amount of data from the given connection.
*/
void
connection_recv (struct connection * connection, void * buf, const ssize_t len) {
assert (connection != NULL);
assert (buf != NULL);
assert (len >= 0);
__socket_recv (connection->sockfd, buf, len);
#ifdef DEBUG
connection->recv_bytes += len;
#endif /* DEBUG */