mirror of https://github.com/eclipse/paho.mqtt.c
New asynchronous MQTT API (see MQTTAsync.h) and
SSL support for both old and new APIs.
This commit is contained in:
parent
125edbb634
commit
97c7280470
304
build/Makefile
304
build/Makefile
|
|
@ -6,38 +6,134 @@ ifndef MQTTCLIENT_DIR
|
|||
MQTTCLIENT_DIR = $(CURDIR)
|
||||
endif
|
||||
|
||||
MQTTLIB = mqttv3c
|
||||
VPATH = ${MQTTCLIENT_DIR}
|
||||
SOURCE_FILES = $(MQTTCLIENT_DIR)/*.c
|
||||
HEADERS = $(MQTTCLIENT_DIR)/*.h
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
OSTYPE = $(OS)
|
||||
else
|
||||
OSTYPE = $(shell uname -s)
|
||||
MACHINETYPE = $(shell uname -m)
|
||||
ifndef SSL_DIR
|
||||
SSL_DIR = ${MQTTCLIENT_DIR}/../../../../source/openssl-1.0.1c/
|
||||
endif
|
||||
|
||||
ifndef SAMPLES_DIR
|
||||
SAMPLES_DIR = ${SSL_DIR}/../com.ibm.mq.mqxr.listener/SDK/clients/c/samples/
|
||||
endif
|
||||
|
||||
MQTTLIB = mqttv3c
|
||||
VPATH = ${MQTTCLIENT_DIR}
|
||||
ALL_SOURCE_FILES = $(wildcard $(MQTTCLIENT_DIR)/*.c)
|
||||
SOURCE_FILES = $(filter-out $(MQTTCLIENT_DIR)/MQTTAsync.c $(MQTTCLIENT_DIR)/MQTTVersion.c $(MQTTCLIENT_DIR)/SSLSocket.c, $(ALL_SOURCE_FILES))
|
||||
ALL_HEADERS = $(MQTTCLIENT_DIR)/*.h
|
||||
HEADERS = $(filter-out $(MQTTCLIENT_DIR)/MQTTAsync.h, $(ALL_HEADERS))
|
||||
|
||||
MQTTLIB_S = mqttv3cs
|
||||
SOURCE_FILES_S = $(filter-out $(MQTTCLIENT_DIR)/MQTTAsync.c $(MQTTCLIENT_DIR)/MQTTVersion.c, $(ALL_SOURCE_FILES))
|
||||
|
||||
MQTTLIB_A = mqttv3a
|
||||
SOURCE_FILES_A = $(filter-out $(MQTTCLIENT_DIR)/MQTTClient.c $(MQTTCLIENT_DIR)/MQTTVersion.c $(MQTTCLIENT_DIR)/SSLSocket.c, $(ALL_SOURCE_FILES))
|
||||
HEADERS_A = $(MQTTCLIENT_DIR)/*.h
|
||||
|
||||
MQTTLIB_AS = mqttv3as
|
||||
SOURCE_FILES_AS = $(filter-out $(MQTTCLIENT_DIR)/MQTTClient.c $(MQTTCLIENT_DIR)/MQTTVersion.c, $(ALL_SOURCE_FILES))
|
||||
|
||||
# In most cases the targets will be built for the native platform. However, to allow for cross compiling,
|
||||
# a target platform variable can be set to override the platform choice. If this variable is set to
|
||||
# a recognised string (currently just 'Arm') the appropriate values of OSTYPE and MACHINETYPE are set.
|
||||
ifeq ($(TARGET_PLATFORM),Arm)
|
||||
OSTYPE = Linux
|
||||
MACHINETYPE = Arm
|
||||
else
|
||||
ifeq ($(OS),Windows_NT)
|
||||
OSTYPE = $(OS)
|
||||
else
|
||||
OSTYPE = $(shell uname -s)
|
||||
MACHINETYPE = $(shell uname -m)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(OSTYPE),Linux)
|
||||
|
||||
ifeq ($(MACHINETYPE),x86_64)
|
||||
CC = gcc
|
||||
CC = gcc
|
||||
|
||||
CCFLAGS_SO = -fPIC -Os -Wall
|
||||
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
|
||||
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
|
||||
LDFLAGS_EXE = -lpthread
|
||||
CCFLAGS_SO = -fPIC -Os -Wall -I ${SSL_DIR}/include
|
||||
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
|
||||
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
|
||||
LDFLAGS_S = -shared -Wl,-soname,lib${MQTTLIB_S}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
|
||||
LDFLAGS_A = -shared -Wl,-soname,lib${MQTTLIB_A}.so
|
||||
LDFLAGS_AS = -shared -Wl,-soname,lib${MQTTLIB_AS}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
|
||||
LDFLAGS_EXE = -lpthread
|
||||
|
||||
MQTTLIB_IA64 = linux_ia64/lib${MQTTLIB}.so
|
||||
|
||||
all: ${MQTTLIB_IA64}
|
||||
MQTTLIB_IA64 = linux_ia64/lib${MQTTLIB}.so
|
||||
MQTTLIB_IA64_S = linux_ia64/lib${MQTTLIB_S}.so
|
||||
MQTTLIB_IA64_A = linux_ia64/lib${MQTTLIB_A}.so
|
||||
MQTTLIB_IA64_AS = linux_ia64/lib${MQTTLIB_AS}.so
|
||||
|
||||
ifdef BUILD_SAMPLES
|
||||
all: linux_ia64 ${MQTTLIB_IA64} ${MQTTLIB_IA64_A} ${MQTTLIB_IA64_S} ${MQTTLIB_IA64_AS} samples
|
||||
else
|
||||
all: linux_ia64 ${MQTTLIB_IA64} ${MQTTLIB_IA64_A} ${MQTTLIB_IA64_S} ${MQTTLIB_IA64_AS}
|
||||
endif
|
||||
|
||||
linux_ia64:
|
||||
-mkdir linux_ia64
|
||||
|
||||
samples:
|
||||
-mkdir ${SAMPLES_DIR}/linux_ia64
|
||||
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3Sample ${SAMPLES_DIR}/MQTTV3Sample.c -lmqttv3c -lpthread
|
||||
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3SSample ${SAMPLES_DIR}/MQTTV3SSample.c -lmqttv3cs -lpthread
|
||||
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3ASample ${SAMPLES_DIR}/MQTTV3ASample.c -lmqttv3a -lpthread
|
||||
${CC} -m64 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia64 -o ${SAMPLES_DIR}/linux_ia64/MQTTV3ASSample ${SAMPLES_DIR}/MQTTV3ASSample.c -lmqttv3as -lpthread
|
||||
|
||||
${MQTTLIB_IA64}: ${SOURCE_FILES} ${HEADERS}
|
||||
-mkdir linux_ia64
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_IA64_S}: ${SOURCE_FILES_S} ${HEADERS}
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_S} -o $@ ${SOURCE_FILES_S} -DOPENSSL
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_IA64_A}: ${SOURCE_FILES_A} ${HEADERS_A}
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_A} -o $@ ${SOURCE_FILES_A}
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_IA64_AS}: ${SOURCE_FILES_AS} ${HEADERS_A}
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_AS} -o $@ ${SOURCE_FILES_AS} -DOPENSSL
|
||||
strip $@
|
||||
|
||||
else
|
||||
ifeq ($(MACHINETYPE),ppc64)
|
||||
CC = gcc
|
||||
|
||||
CCFLAGS_SO = -fPIC -Os -Wall -I ${SSL_DIR}/include
|
||||
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
|
||||
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
|
||||
LDFLAGS_S = -shared -Wl,-soname,lib${MQTTLIB_S}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
|
||||
LDFLAGS_A = -shared -Wl,-soname,lib${MQTTLIB_A}.so
|
||||
LDFLAGS_AS = -shared -Wl,-soname,lib${MQTTLIB_AS}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
|
||||
LDFLAGS_EXE = -lpthread
|
||||
|
||||
MQTTLIB_PPC64 = linux_ppc64/lib${MQTTLIB}.so
|
||||
MQTTLIB_PPC64_S = linux_ppc64/lib${MQTTLIB_S}.so
|
||||
MQTTLIB_PPC64_A = linux_ppc64/lib${MQTTLIB_A}.so
|
||||
MQTTLIB_PPC64_AS = linux_ppc64/lib${MQTTLIB_AS}.so
|
||||
|
||||
all: linux_ppc64 ${MQTTLIB_PPC64} ${MQTTLIB_PPC64_A} #${MQTTLIB_PPC64_S} ${MQTTLIB_PPC64_AS}
|
||||
|
||||
linux_ppc64:
|
||||
-mkdir linux_ppc64
|
||||
|
||||
${MQTTLIB_PPC64}: ${SOURCE_FILES} ${HEADERS}
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_PPC64_S}: ${SOURCE_FILES_S} ${HEADERS}
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_S} -o $@ ${SOURCE_FILES_S} -DOPENSSL
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_PPC64_A}: ${SOURCE_FILES_A} ${HEADERS_A}
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_A} -o $@ ${SOURCE_FILES_A}
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_PPC64_AS}: ${SOURCE_FILES_AS} ${HEADERS_A}
|
||||
${CC} -m64 ${CCFLAGS_SO} ${LDFLAGS_AS} -o $@ ${SOURCE_FILES_AS} -DOPENSSL
|
||||
strip $@
|
||||
|
||||
else
|
||||
ifeq ($(MACHINETYPE),s390x)
|
||||
CC = gcc
|
||||
|
|
@ -57,35 +153,101 @@ ${MQTTLIB_390}: ${SOURCE_FILES} ${HEADERS}
|
|||
strip $@
|
||||
|
||||
else
|
||||
CC = gcc
|
||||
ARM_GLIBC_PREFIX = arm-linux-
|
||||
ARM_UCLIBC_PREFIX = ~/x-tools/arm-unknown-linux-uclibcgnueabi/bin/arm-unknown-linux-uclibcgnueabi-
|
||||
ifeq ($(MACHINETYPE),Arm)
|
||||
|
||||
CCFLAGS_SO = -fPIC -Os -Wall
|
||||
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
|
||||
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
|
||||
LDFLAGS_EXE = -lpthread
|
||||
CC = gcc
|
||||
# ARM_GLIBC_CC = arm-linux-gcc
|
||||
# ARM_UCLIBC_CC = ~/x-tools/arm-unknown-linux-uclibcgnueabi/bin/arm-unknown-linux-uclibcgnueabi-gcc
|
||||
|
||||
MQTTLIB_IA32 = linux_ia32/lib${MQTTLIB}.so
|
||||
MQTTLIB_ARM_GLIBC = linux_ARM_glibc/lib${MQTTLIB}.so
|
||||
MQTTLIB_ARM_UCLIBC = linux_ARM_uclibc/lib${MQTTLIB}.so
|
||||
ifndef ARM_GLIBC_CC
|
||||
ARM_GLIBC_CC = arm-linux-gcc
|
||||
endif
|
||||
ifndef ARM_UCLIBC_CC
|
||||
ARM_UCLIBC_CC = arm-unknown-linux-uclibcgnueabi-gcc
|
||||
endif
|
||||
|
||||
all: ${MQTTLIB_IA32} ${MQTTLIB_ARM_GLIBC} ${MQTTLIB_ARM_UCLIBC}
|
||||
ifndef ARM_GLIBC_STRIP
|
||||
ARM_GLIBC_STRIP = $(subst -gcc,-strip,${ARM_GLIBC_CC})
|
||||
endif
|
||||
ifndef ARM_UCLIBC_STRIP
|
||||
ARM_UCLIBC_STRIP = $(subst -gcc,-strip,${ARM_UCLIBC_CC})
|
||||
endif
|
||||
|
||||
CCFLAGS_SO = -fPIC -Os -Wall
|
||||
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
|
||||
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
|
||||
LDFLAGS_EXE = -lpthread
|
||||
|
||||
MQTTLIB_ARM_GLIBC = linux_ARM_glibc/lib${MQTTLIB}.so
|
||||
MQTTLIB_ARM_UCLIBC = linux_ARM_uclibc/lib${MQTTLIB}.so
|
||||
|
||||
MQTTLIB_ARM_GLIBC_A = linux_ARM_glibc/lib${MQTTLIB_A}.so
|
||||
MQTTLIB_ARM_UCLIBC_A = linux_ARM_uclibc/lib${MQTTLIB_A}.so
|
||||
|
||||
all: ${MQTTLIB_ARM_GLIBC} ${MQTTLIB_ARM_UCLIBC}
|
||||
|
||||
${MQTTLIB_IA32}: ${SOURCE_FILES} ${HEADERS}
|
||||
-mkdir linux_ia32
|
||||
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_ARM_GLIBC}: ${SOURCE_FILES} ${HEADERS}
|
||||
-mkdir linux_ARM_glibc
|
||||
${ARM_GLIBC_PREFIX}gcc ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
${ARM_GLIBC_PREFIX}strip $@
|
||||
${ARM_GLIBC_CC} ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
${ARM_GLIBC_STRIP} $@
|
||||
|
||||
${MQTTLIB_ARM_UCLIBC}: ${SOURCE_FILES} ${HEADERS}
|
||||
-mkdir linux_ARM_uclibc
|
||||
${ARM_UCLIBC_PREFIX}gcc ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
${ARM_UCLIBC_PREFIX}strip $@
|
||||
${ARM_UCLIBC_CC} ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
${ARM_UCLIBC_STRIP} $@
|
||||
|
||||
else
|
||||
|
||||
CC = gcc
|
||||
|
||||
CCFLAGS_SO = -fPIC -Os -Wall -I ${SSL_DIR}/include
|
||||
CCFLAGS_EXE = -I ${MQTTCLIENT_DIR}
|
||||
LDFLAGS = -shared -Wl,-soname,lib${MQTTLIB}.so
|
||||
LDFLAGS_S = -shared -Wl,-soname,lib${MQTTLIB_S}.so -ldl -Wl,-whole-archive -L${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
|
||||
LDFLAGS_A = -shared -Wl,-soname,lib${MQTTLIB_A}.so
|
||||
LDFLAGS_AS = -shared -Wl,-soname,lib${MQTTLIB_AS}.so -ldl -Wl,-whole-archive -L/${SSL_DIR}/output/lib -lcrypto -lssl -Wl,-no-whole-archive
|
||||
LDFLAGS_EXE = -lpthread
|
||||
|
||||
MQTTLIB_IA32 = linux_ia32/lib${MQTTLIB}.so
|
||||
MQTTLIB_IA32_S = linux_ia32/lib${MQTTLIB_S}.so
|
||||
MQTTLIB_IA32_A = linux_ia32/lib${MQTTLIB_A}.so
|
||||
MQTTLIB_IA32_AS = linux_ia32/lib${MQTTLIB_AS}.so
|
||||
|
||||
ifdef BUILD_SAMPLES
|
||||
all: linux_ia32 ${MQTTLIB_IA32_A} ${MQTTLIB_IA32} ${MQTTLIB_IA32_S} ${MQTTLIB_IA32_AS} samples
|
||||
else
|
||||
all: linux_ia32 ${MQTTLIB_IA32_A} ${MQTTLIB_IA32} ${MQTTLIB_IA32_S} ${MQTTLIB_IA32_AS}
|
||||
endif
|
||||
|
||||
samples:
|
||||
-mkdir ${SAMPLES_DIR}/linux_ia32
|
||||
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3Sample ${SAMPLES_DIR}/MQTTV3Sample.c -lmqttv3c -lpthread
|
||||
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3SSample ${SAMPLES_DIR}/MQTTV3SSample.c -lmqttv3cs -lpthread
|
||||
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3ASample ${SAMPLES_DIR}/MQTTV3ASample.c -lmqttv3a -lpthread
|
||||
${CC} -m32 -I ${MQTTCLIENT_DIR} -L ${MQTTCLIENT_DIR}/linux_ia32 -o ${SAMPLES_DIR}/linux_ia32/MQTTV3ASSample ${SAMPLES_DIR}/MQTTV3ASSample.c -lmqttv3as -lpthread
|
||||
|
||||
linux_ia32:
|
||||
-mkdir linux_ia32
|
||||
|
||||
${MQTTLIB_IA32}: ${SOURCE_FILES} ${HEADERS}
|
||||
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS} -o $@ ${SOURCE_FILES}
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_IA32_S}: ${SOURCE_FILES_S} ${HEADERS}
|
||||
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS_S} -o $@ ${SOURCE_FILES_S} -DOPENSSL
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_IA32_A}: ${SOURCE_FILES_A} ${HEADERS_A}
|
||||
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS_A} -o $@ ${SOURCE_FILES_A}
|
||||
strip $@
|
||||
|
||||
${MQTTLIB_IA32_AS}: ${SOURCE_FILES_AS} ${HEADERS_A}
|
||||
${CC} -m32 ${CCFLAGS_SO} ${LDFLAGS_AS} -o $@ ${SOURCE_FILES_AS} -DOPENSSL
|
||||
strip $@
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
endif
|
||||
|
|
@ -95,9 +257,9 @@ endif
|
|||
|
||||
ifeq ($(OSTYPE),AIX)
|
||||
# applications must be linked with -I. -L. -lmqttv3c -Wl,-brtl -pthread
|
||||
CC = gcc
|
||||
CC = gxlc
|
||||
|
||||
CCFLAGS_SO = -fPIC -Os -Wall -shared -DREVERSED
|
||||
CCFLAGS_SO = -fPIC -Os -shared -DREVERSED -DAIX
|
||||
LDFLAGS = -Wl,-G
|
||||
|
||||
MQTTLIB_AIX = aix/lib${MQTTLIB}.so
|
||||
|
|
@ -118,16 +280,20 @@ ifeq ($(OSTYPE),Windows_NT)
|
|||
# compiler flags
|
||||
CPPFLAGS = /D "WIN32" /D "_UNICODE" /D "UNICODE" /D "_CRT_SECURE_NO_WARNINGS"
|
||||
CFLAGS = /nologo /c /O2 /W3 /Fd /MD /TC
|
||||
CPPFLAGS_S = /D "OPENSSL" /I ${SSL_DIR}/include
|
||||
|
||||
# linker flags
|
||||
LINKFLAGS = /nologo /machine:x86 /manifest
|
||||
WINLIBS = kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib
|
||||
WINLIBS_S = crypt32.lib ssleay32.lib libeay32.lib
|
||||
SSLLIBPATH = /LIBPATH:${SSL_DIR}/output/lib
|
||||
# Flags to link C libraries
|
||||
LIBLINKFLAGS = /dll
|
||||
IMP = /implib:$(@:.dll=.lib)
|
||||
LIBPDB = /pdb:$(@:.dll=.pdb)
|
||||
LIBMAP = /map:$(@:.dll=.map)
|
||||
LIBLDFLAGS = $(LINKFLAGS) $(WINLIBS) $(LIBLINKFLAGS) $(IMP) $(LIBPDB) $(LIBMAP)
|
||||
LIBLDFLAGS_S = $(LINKFLAGS) $(SSLLIBPATH) $(WINLIBS) $(WINLIBS_S) $(LIBLINKFLAGS) $(IMP) $(LIBPDB) $(LIBMAP)
|
||||
# Flags to link C programs
|
||||
MAINLINKFLAGS = /subsystem:console
|
||||
EXEPDB = /pdb:$(@:.exe=.pdb)
|
||||
|
|
@ -136,14 +302,64 @@ ifeq ($(OSTYPE),Windows_NT)
|
|||
|
||||
MQTTLDLIB = windows_ia32/$(MQTTLIB).lib
|
||||
MQTTDLL = windows_ia32/$(MQTTLIB).dll
|
||||
MQTTLDLIB_A = windows_ia32/$(MQTTLIB_A).lib
|
||||
MQTTDLL_A = windows_ia32/$(MQTTLIB_A).dll
|
||||
MQTTLDLIB_AS = windows_ia32/$(MQTTLIB_AS).lib
|
||||
MQTTDLL_AS = windows_ia32/$(MQTTLIB_AS).dll
|
||||
MQTTLDLIB_S = windows_ia32/$(MQTTLIB_S).lib
|
||||
MQTTDLL_S = windows_ia32/$(MQTTLIB_S).dll
|
||||
|
||||
all: ${MQTTDLL}
|
||||
ifdef BUILD_SAMPLES
|
||||
all: windows_ia32 ${MQTTDLL} ${MQTTDLL_A} ${MQTTDLL_AS} ${MQTTDLL_S} samples
|
||||
else
|
||||
all: windows_ia32 ${MQTTDLL} ${MQTTDLL_A} ${MQTTDLL_AS} ${MQTTDLL_S}
|
||||
endif
|
||||
|
||||
$(MQTTDLL): $(shell cygpath -au ${SOURCE_FILES}) $(shell cygpath -au ${HEADERS})
|
||||
samples:
|
||||
-mkdir ${SAMPLES_DIR}/windows_ia32
|
||||
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3Sample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3c.lib
|
||||
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3CSSample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3cs.lib
|
||||
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3ASample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3a.lib
|
||||
${CC} /nologo /D "WIN32" /I ${MQTTCLIENT_DIR} ${SAMPLES_DIR}/MQTTV3ASSample.c /Fe${SAMPLES_DIR}/windows_ia32/ /link /LIBPATH:${MQTTCLIENT_DIR}/windows_ia32 /nologo /subsystem:console /manifest /machine:x86 mqttv3as.lib
|
||||
|
||||
windows_ia32:
|
||||
-mkdir windows_ia32
|
||||
${CC} ${CPPFLAGS} ${CFLAGS} /I $(shell cygpath -am ${MQTTCLIENT_DIR}) /I $(shell cygpath -am ${MQTTCLIENT_DIR})/.. /Fo $(shell cygpath -am ${SOURCE_FILES})
|
||||
|
||||
$(MQTTDLL): ${SOURCE_FILES} ${HEADERS}
|
||||
-rm -f *.obj
|
||||
${CC} ${CPPFLAGS} ${CFLAGS} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES}
|
||||
$(LD) $(LIBLDFLAGS) *.obj /out:$(MQTTDLL)
|
||||
mt -manifest windows_ia32/mqttv3c.dll.manifest -outputresource:$(MQTTDLL)\;2
|
||||
|
||||
$(MQTTDLL_A): ${SOURCE_FILES_A} ${HEADERS_A}
|
||||
-rm -f *.obj
|
||||
${CC} ${CPPFLAGS} ${CFLAGS} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES_A}
|
||||
$(LD) $(LIBLDFLAGS) *.obj /out:$(MQTTDLL_A)
|
||||
mt -manifest windows_ia32/mqttv3a.dll.manifest -outputresource:$(MQTTDLL_A)\;2
|
||||
|
||||
$(MQTTDLL_S): ${SOURCE_FILES_S} ${HEADERS}
|
||||
-rm -f *.obj
|
||||
${CC} ${CPPFLAGS} ${CFLAGS} ${CPPFLAGS_S} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES_S}
|
||||
$(LD) $(LIBLDFLAGS_S) *.obj /out:$(MQTTDLL_S)
|
||||
mt -manifest windows_ia32/mqttv3cs.dll.manifest -outputresource:$(MQTTDLL_S)\;2
|
||||
|
||||
$(MQTTDLL_AS): ${SOURCE_FILES} ${HEADERS}
|
||||
-rm -f *.obj
|
||||
${CC} ${CPPFLAGS} ${CFLAGS} ${CPPFLAGS_S} /I ${MQTTCLIENT_DIR} /I ${MQTTCLIENT_DIR}/.. /Fo ${SOURCE_FILES_AS}
|
||||
$(LD) $(LIBLDFLAGS_S) *.obj /out:$(MQTTDLL_AS)
|
||||
mt -manifest windows_ia32/mqttv3as.dll.manifest -outputresource:$(MQTTDLL_AS)\;2
|
||||
|
||||
endif
|
||||
|
||||
#########################################################################################################
|
||||
# The following was how the C Clients were built using cygwin GNU make and has been left here as a reference
|
||||
# This has been converted to use ming GNU make (which uses the native format of file names)
|
||||
#
|
||||
#$(MQTTDLL): $(shell cygpath -au ${SOURCE_FILES}) $(shell cygpath -au ${HEADERS})
|
||||
# -mkdir windows_ia32
|
||||
# ${CC} ${CPPFLAGS} ${CFLAGS} /I $(shell cygpath -am ${MQTTCLIENT_DIR}) /I $(shell cygpath -am ${MQTTCLIENT_DIR})/.. /Fo $(shell cygpath -am ${SOURCE_FILES})
|
||||
# $(LD) $(LIBLDFLAGS) *.obj /out:$(MQTTDLL)
|
||||
# mt -manifest windows_ia32/mqttv3c.dll.manifest -outputresource:$(MQTTDLL)\;2
|
||||
#
|
||||
#endif
|
||||
#########################################################################################################
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - add SSL support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -46,5 +47,5 @@ int clientSocketCompare(void* a, void* b)
|
|||
{
|
||||
Clients* client = (Clients*)a;
|
||||
/*printf("comparing %d with %d\n", (char*)a, (char*)b); */
|
||||
return client->socket == *(int*)b;
|
||||
return client->net.socket == *(int*)b;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,12 +8,20 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - add SSL support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(CLIENTS_H)
|
||||
#define CLIENTS_H
|
||||
|
||||
#include <time.h>
|
||||
#if defined(OPENSSL)
|
||||
#if defined(WIN32)
|
||||
#include "winsock2.h"
|
||||
#endif
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
#include "MQTTClient.h"
|
||||
#include "LinkedList.h"
|
||||
#include "MQTTClientPersistence.h"
|
||||
/*BE
|
||||
|
|
@ -116,6 +124,7 @@ def CLIENTS
|
|||
at 4 n8 bits 7:6 dec "connect_state"
|
||||
at 8
|
||||
n32 dec "socket"
|
||||
n32 ptr "SSL"
|
||||
n32 dec "msgID"
|
||||
n32 dec "keepAliveInterval"
|
||||
n32 dec "maxInflightMessages"
|
||||
|
|
@ -132,6 +141,15 @@ defList(CLIENTS)
|
|||
|
||||
BE*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int socket;
|
||||
#if defined(OPENSSL)
|
||||
SSL* ssl;
|
||||
SSL_CTX* ctx;
|
||||
#endif
|
||||
} networkHandles;
|
||||
|
||||
/**
|
||||
* Data related to one client
|
||||
*/
|
||||
|
|
@ -144,8 +162,8 @@ typedef struct
|
|||
unsigned int connected : 1; /**< whether it is currently connected */
|
||||
unsigned int good : 1; /**< if we have an error on the socket we turn this off */
|
||||
unsigned int ping_outstanding : 1;
|
||||
unsigned int connect_state : 2;
|
||||
int socket;
|
||||
int connect_state : 4;
|
||||
networkHandles net;
|
||||
int msgID;
|
||||
int keepAliveInterval;
|
||||
int retryInterval;
|
||||
|
|
@ -155,9 +173,13 @@ typedef struct
|
|||
List* inboundMsgs;
|
||||
List* outboundMsgs; /**< in flight */
|
||||
List* messageQueue;
|
||||
unsigned int qentry_seqno;
|
||||
void* phandle; /* the persistence handle */
|
||||
MQTTClient_persistence* persistence; /* a persistence implementation */
|
||||
int connectOptionsVersion;
|
||||
#if defined(OPENSSL)
|
||||
MQTTClient_SSLOptions *sslopts;
|
||||
SSL_SESSION* session; /***< SSL session pointer for fast handhake */
|
||||
#endif
|
||||
} Clients;
|
||||
|
||||
int clientIDCompare(void* a, void* b);
|
||||
|
|
|
|||
418
src/Heap.c
418
src/Heap.c
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - use tree data structure instead of list
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions to manage the heap with the goal of eliminating memory leaks
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
*
|
||||
* */
|
||||
|
||||
#include "LinkedList.h"
|
||||
#include "Tree.h"
|
||||
#include "Log.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Thread.h"
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
|
|
@ -37,20 +38,88 @@
|
|||
#undef realloc
|
||||
#undef free
|
||||
|
||||
static heap_info state = {0, 0};
|
||||
#if defined(WIN32)
|
||||
mutex_type heap_mutex;
|
||||
#else
|
||||
static pthread_mutex_t heap_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type heap_mutex = &heap_mutex_store;
|
||||
#endif
|
||||
|
||||
int ListRemoveCurrentItem(List* aList);
|
||||
static heap_info state = {0, 0}; /**< global heap state information */
|
||||
static int eyecatcher = 0x88888888;
|
||||
|
||||
/**
|
||||
* Each item on the heap is recorded with this structure.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char* file;
|
||||
int line;
|
||||
void* ptr;
|
||||
size_t size;
|
||||
char* stack;
|
||||
char* file; /**< the name of the source file where the storage was allocated */
|
||||
int line; /**< the line no in the source file where it was allocated */
|
||||
void* ptr; /**< pointer to the allocated storage */
|
||||
int size; /**< size of the allocated storage */
|
||||
} storageElement;
|
||||
|
||||
static List heap = {NULL, NULL, NULL};
|
||||
static Tree heap; /**< Tree that holds the allocation records */
|
||||
static char* errmsg = "Memory allocation error";
|
||||
|
||||
/**
|
||||
* Round allocation size up to a multiple of the size of an int. Apart from possibly reducing fragmentation,
|
||||
* on the old v3 gcc compilers I was hitting some weird behaviour, which might have been errors in
|
||||
* sizeof() used on structures and related to packing. In any case, this fixes that too.
|
||||
* @param size the size actually needed
|
||||
* @return the rounded up size
|
||||
*/
|
||||
int roundup(int size)
|
||||
{
|
||||
static int multsize = 4*sizeof(int);
|
||||
|
||||
if (size % multsize != 0)
|
||||
size += multsize - (size % multsize);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing storage elements
|
||||
* @param a pointer to the current content in the tree (storageElement*)
|
||||
* @param b pointer to the memory to free
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int ptrCompare(void* a, void* b, int value)
|
||||
{
|
||||
a = ((storageElement*)a)->ptr;
|
||||
if (value)
|
||||
b = ((storageElement*)b)->ptr;
|
||||
|
||||
return (a > b) ? -1 : (a == b) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
void Heap_check(char* string, void* ptr)
|
||||
{
|
||||
return;
|
||||
/*Node* curnode = NULL;
|
||||
storageElement* prev, *s = NULL;
|
||||
|
||||
printf("Heap_check start %p\n", ptr);
|
||||
while ((curnode = TreeNextElement(&heap, curnode)) != NULL)
|
||||
{
|
||||
prev = s;
|
||||
s = (storageElement*)(curnode->content);
|
||||
|
||||
if (prev)
|
||||
{
|
||||
if (ptrCompare(s, prev, 1) != -1)
|
||||
{
|
||||
printf("%s: heap order error %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
|
||||
exit(99);
|
||||
}
|
||||
else
|
||||
printf("%s: heap order good %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates a block of memory. A direct replacement for malloc, but keeps track of items
|
||||
|
|
@ -63,90 +132,61 @@ static List heap = {NULL, NULL, NULL};
|
|||
*/
|
||||
void* mymalloc(char* file, int line, size_t size)
|
||||
{
|
||||
ListElement* e = NULL;
|
||||
storageElement* s = NULL;
|
||||
static char* errmsg = "Memory allocation error";
|
||||
int space = sizeof(storageElement);
|
||||
int filenamelen = strlen(file)+1;
|
||||
|
||||
Thread_lock_mutex(heap_mutex);
|
||||
size = roundup(size);
|
||||
if ((s = malloc(sizeof(storageElement))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, -1, errmsg);
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
return NULL;
|
||||
}
|
||||
if ((s->file = malloc(strlen(file)+1)) == NULL)
|
||||
s->size = size; /* size without eyecatchers */
|
||||
if ((s->file = malloc(filenamelen)) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, -1, errmsg);
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
space += filenamelen;
|
||||
strcpy(s->file, file);
|
||||
s->line = line;
|
||||
if ((s->ptr = malloc(size)) == NULL)
|
||||
/* Add space for eyecatcher at each end */
|
||||
if ((s->ptr = malloc(size + 2*sizeof(int))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, -1, errmsg);
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s->file);
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
s->size = size;
|
||||
s->stack = NULL;
|
||||
if (trace_settings.trace_level == TRACE_MAXIMUM)
|
||||
{
|
||||
if ((s->stack = StackTrace_get(Thread_getid())) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, -1, errmsg);
|
||||
free(s->ptr);
|
||||
free(s->file);
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(TRACE_MAX, -1, "Allocating %d bytes in heap at line %d of file %s, ptr %p, heap use now %d bytes",
|
||||
s->size, line, file, s->ptr, state.current_size);
|
||||
Log(TRACE_MAX, -1, "Stack trace is %s", s->stack);
|
||||
}
|
||||
}
|
||||
if ((e = malloc(sizeof(ListElement))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, -1, errmsg);
|
||||
if (s->stack)
|
||||
free(s->stack);
|
||||
free(s->ptr);
|
||||
free(s->file);
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
ListAppendNoMalloc(&heap, s, e, sizeof(ListElement));
|
||||
space += size + 2*sizeof(int);
|
||||
*(int*)(s->ptr) = eyecatcher; /* start eyecatcher */
|
||||
*(int*)(((char*)(s->ptr)) + (sizeof(int) + size)) = eyecatcher; /* end eyecatcher */
|
||||
Log(TRACE_MAX, -1, "Allocating %d bytes in heap at file %s line %d ptr %p\n", size, file, line, s->ptr);
|
||||
TreeAdd(&heap, s, space);
|
||||
state.current_size += size;
|
||||
if (state.current_size > state.max_size)
|
||||
state.max_size = state.current_size;
|
||||
return s->ptr;
|
||||
Thread_unlock_mutex(heap_mutex);
|
||||
return ((int*)(s->ptr)) + 1; /* skip start eyecatcher */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing storage elements
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int ptrCompare(void* a, void* b)
|
||||
void checkEyecatchers(char* file, int line, void* p, int size)
|
||||
{
|
||||
storageElement* s = (storageElement*)a;
|
||||
return s->ptr == b;
|
||||
}
|
||||
int *sp = (int*)p;
|
||||
char *cp = (char*)p;
|
||||
int us;
|
||||
static char* msg = "Invalid %s eyecatcher %d in heap item at file %s line %d";
|
||||
|
||||
if ((us = *--sp) != eyecatcher)
|
||||
Log(LOG_ERROR, 13, msg, "start", us, file, line);
|
||||
|
||||
/**
|
||||
* Utility to find an item in the heap. Lets you know if the heap already contains
|
||||
* the memory location in question.
|
||||
* @param p pointer to a memory location
|
||||
* @return pointer to the storage element if found, or NULL
|
||||
*/
|
||||
void* Heap_findItem(void* p)
|
||||
{
|
||||
ListElement* e = ListFindItem(&heap, p, ptrCompare);
|
||||
return (e == NULL) ? NULL : e->content;
|
||||
cp += size;
|
||||
if ((us = *(int*)cp) != eyecatcher)
|
||||
Log(LOG_ERROR, 13, msg, "end", us, file, line);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -157,36 +197,28 @@ void* Heap_findItem(void* p)
|
|||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be removed
|
||||
*/
|
||||
void Internal_heap_unlink(char* file, int line, void* p)
|
||||
int Internal_heap_unlink(char* file, int line, void* p)
|
||||
{
|
||||
ListElement* e = NULL;
|
||||
Node* e = NULL;
|
||||
int rc = 0;
|
||||
|
||||
if ((e = ListFindItem(&heap, p, ptrCompare)) == NULL)
|
||||
Log(LOG_ERROR, -1, "Failed to remove heap item at file %s line %d", file, line);
|
||||
e = TreeFind(&heap, ((int*)p)-1);
|
||||
if (e == NULL)
|
||||
Log(LOG_ERROR, 13, "Failed to remove heap item at file %s line %d", file, line);
|
||||
else
|
||||
{
|
||||
storageElement* s = (storageElement*)(heap.current->content);
|
||||
Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes",
|
||||
storageElement* s = (storageElement*)(e->content);
|
||||
Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes\n",
|
||||
s->size, file, line, state.current_size);
|
||||
checkEyecatchers(file, line, p, s->size);
|
||||
//free(s->ptr);
|
||||
free(s->file);
|
||||
if (s->stack)
|
||||
free(s->stack);
|
||||
state.current_size -= s->size;
|
||||
ListRemoveCurrentItem(&heap);
|
||||
TreeRemoveNodeIndex(&heap, e, 0);
|
||||
free(s);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from the recorded heap without actually freeing it.
|
||||
* Use sparingly!
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be removed
|
||||
*/
|
||||
void Heap_unlink(char* file, int line, void* p)
|
||||
{
|
||||
Internal_heap_unlink(file, line, p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -199,8 +231,25 @@ void Heap_unlink(char* file, int line, void* p)
|
|||
*/
|
||||
void myfree(char* file, int line, void* p)
|
||||
{
|
||||
Thread_lock_mutex(heap_mutex);
|
||||
if (Internal_heap_unlink(file, line, p))
|
||||
free(((int*)p)-1);
|
||||
Thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from the recorded heap without actually freeing it.
|
||||
* Use sparingly!
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be removed
|
||||
*/
|
||||
void Heap_unlink(char* file, int line, void* p)
|
||||
{
|
||||
Thread_lock_mutex(heap_mutex);
|
||||
Internal_heap_unlink(file, line, p);
|
||||
free(p);
|
||||
Thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -208,6 +257,8 @@ void myfree(char* file, int line, void* p)
|
|||
* Reallocates a block of memory. A direct replacement for realloc, but keeps track of items
|
||||
* allocated in a list, so that free can check that a item is being freed correctly and that
|
||||
* we can check that all memory is freed at shutdown.
|
||||
* We have to remove the item from the tree, as the memory is in order and so it needs to
|
||||
* be reinserted in the correct place.
|
||||
* @param file use the __FILE__ macro to indicate which file this item was reallocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was reallocated at
|
||||
* @param p pointer to the item to be reallocated
|
||||
|
|
@ -217,56 +268,58 @@ void myfree(char* file, int line, void* p)
|
|||
void *myrealloc(char* file, int line, void* p, size_t size)
|
||||
{
|
||||
void* rc = NULL;
|
||||
|
||||
ListElement* e = ListFindItem(&heap, p, ptrCompare);
|
||||
if (e == NULL)
|
||||
Log(LOG_ERROR, -1, "Failed to reallocate heap item at file %s line %d", file, line);
|
||||
storageElement* s = NULL;
|
||||
|
||||
Thread_lock_mutex(heap_mutex);
|
||||
s = TreeRemoveKey(&heap, ((int*)p)-1);
|
||||
if (s == NULL)
|
||||
Log(LOG_ERROR, 13, "Failed to reallocate heap item at file %s line %d", file, line);
|
||||
else
|
||||
{
|
||||
storageElement* s = (storageElement*)(heap.current->content);
|
||||
int space = sizeof(storageElement);
|
||||
int filenamelen = strlen(file)+1;
|
||||
|
||||
checkEyecatchers(file, line, p, s->size);
|
||||
size = roundup(size);
|
||||
state.current_size += size - s->size;
|
||||
if (state.current_size > state.max_size)
|
||||
state.max_size = state.current_size;
|
||||
rc = s->ptr = realloc(s->ptr, size);
|
||||
if ((s->ptr = realloc(s->ptr, size + 2*sizeof(int))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
return NULL;
|
||||
}
|
||||
space += size + 2*sizeof(int) - s->size;
|
||||
*(int*)(s->ptr) = eyecatcher; /* start eyecatcher */
|
||||
*(int*)(((char*)(s->ptr)) + (sizeof(int) + size)) = eyecatcher; /* end eyecatcher */
|
||||
s->size = size;
|
||||
s->file = realloc(s->file, strlen(file)+1);
|
||||
space -= strlen(s->file);
|
||||
s->file = realloc(s->file, filenamelen);
|
||||
space += filenamelen;
|
||||
strcpy(s->file, file);
|
||||
s->line = line;
|
||||
if (s->stack)
|
||||
{
|
||||
free(s->stack);
|
||||
s->stack = StackTrace_get(Thread_getid());
|
||||
}
|
||||
rc = s->ptr;
|
||||
TreeAdd(&heap, s, space);
|
||||
}
|
||||
return rc;
|
||||
Thread_unlock_mutex(heap_mutex);
|
||||
return (rc == NULL) ? NULL : ((int*)(rc)) + 1; /* skip start eyecatcher */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees the current item in a list. A list function only used by myfree.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
* Utility to find an item in the heap. Lets you know if the heap already contains
|
||||
* the memory location in question.
|
||||
* @param p pointer to a memory location
|
||||
* @return pointer to the storage element if found, or NULL
|
||||
*/
|
||||
int ListRemoveCurrentItem(List* aList)
|
||||
void* Heap_findItem(void* p)
|
||||
{
|
||||
ListElement* next = NULL;
|
||||
Node* e = NULL;
|
||||
|
||||
if (aList->current->prev == NULL)
|
||||
/* so this is the first element, and we have to update the "first" pointer */
|
||||
aList->first = aList->current->next;
|
||||
else
|
||||
aList->current->prev->next = aList->current->next;
|
||||
|
||||
if (aList->current->next == NULL)
|
||||
aList->last = aList->current->prev;
|
||||
else
|
||||
aList->current->next->prev = aList->current->prev;
|
||||
|
||||
next = aList->current->next;
|
||||
free(aList->current->content);
|
||||
free(aList->current);
|
||||
aList->current = next;
|
||||
return 1; /* successfully removed item */
|
||||
Thread_lock_mutex(heap_mutex);
|
||||
e = TreeFind(&heap, ((int*)p)-1);
|
||||
Thread_unlock_mutex(heap_mutex);
|
||||
return (e == NULL) ? NULL : e->content;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -274,19 +327,20 @@ int ListRemoveCurrentItem(List* aList)
|
|||
* Scans the heap and reports any items currently allocated.
|
||||
* To be used at shutdown if any heap items have not been freed.
|
||||
*/
|
||||
void HeapScan()
|
||||
void HeapScan(int log_level)
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
Log(TRACE_MIN, -1, "Heap scan start, total %d bytes", state.current_size);
|
||||
while (ListNextElement(&heap, ¤t))
|
||||
Node* current = NULL;
|
||||
|
||||
Thread_lock_mutex(heap_mutex);
|
||||
Log(log_level, -1, "Heap scan start, total %d bytes", state.current_size);
|
||||
while ((current = TreeNextElement(&heap, current)) != NULL)
|
||||
{
|
||||
storageElement* s = (storageElement*)(current->content);
|
||||
Log(TRACE_MIN, -1, "Heap element size %d, line %d, file %s, ptr %p", s->size, s->line, s->file, s->ptr);
|
||||
Log(TRACE_MIN, -1, " Content %*.s", (10 > s->size) ? s->size : 10, (char*)s->ptr);
|
||||
if (s->stack)
|
||||
Log(TRACE_MIN, -1, " Stack trace: %s", s->stack);
|
||||
Log(log_level, -1, "Heap element size %d, line %d, file %s, ptr %p", s->size, s->line, s->file, s->ptr);
|
||||
Log(log_level, -1, " Content %*.s", (10 > current->size) ? s->size : 10, (char*)(((int*)s->ptr) + 1));
|
||||
}
|
||||
Log(TRACE_MIN, -1, "Heap scan end");
|
||||
Log(log_level, -1, "Heap scan end");
|
||||
Thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -295,6 +349,8 @@ void HeapScan()
|
|||
*/
|
||||
int Heap_initialize()
|
||||
{
|
||||
TreeInitializeNoMalloc(&heap, ptrCompare);
|
||||
heap.heap_tracking = 0; /* no recursive heap tracking! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +364,7 @@ void Heap_terminate()
|
|||
if (state.current_size > 20) /* One log list is freed after this function is called */
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Some memory not freed at shutdown, possible memory leak");
|
||||
HeapScan();
|
||||
HeapScan(LOG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -326,29 +382,19 @@ heap_info* Heap_get_info()
|
|||
/**
|
||||
* Dump a string from the heap so that it can be displayed conveniently
|
||||
* @param file file handle to dump the heap contents to
|
||||
* @param str the string to dump
|
||||
* @param str the string to dump, could be NULL
|
||||
*/
|
||||
int HeapDumpString(FILE* file, char* str)
|
||||
{
|
||||
int rc = 0;
|
||||
int len = str ? strlen(str) + 1 : 0; /* include the trailing null */
|
||||
|
||||
if (str)
|
||||
{
|
||||
int* i = (int*)str;
|
||||
int j;
|
||||
int len = strlen(str);
|
||||
|
||||
if (fwrite(&(str), sizeof(int), 1, file) != 1)
|
||||
rc = -1;
|
||||
if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
|
||||
rc = -1;
|
||||
for (j = 0; j < len; j++)
|
||||
{
|
||||
if (fwrite(i, sizeof(int), 1, file) != 1)
|
||||
rc = -1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (fwrite(&(str), sizeof(char*), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
|
||||
rc = -1;
|
||||
else if (len > 0 && fwrite(str, len, 1, file) != 1)
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -356,32 +402,58 @@ int HeapDumpString(FILE* file, char* str)
|
|||
/**
|
||||
* Dump the state of the heap
|
||||
* @param file file handle to dump the heap contents to
|
||||
* @return completion code, success == 0
|
||||
*/
|
||||
int HeapDump(FILE* file)
|
||||
{
|
||||
int rc = 0;
|
||||
Node* current = NULL;
|
||||
|
||||
if (file != NULL)
|
||||
while (rc == 0 && (current = TreeNextElement(&heap, current)))
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
while (ListNextElement(&heap, ¤t))
|
||||
{
|
||||
storageElement* s = (storageElement*)(current->content);
|
||||
int* i = s->ptr;
|
||||
unsigned int j;
|
||||
storageElement* s = (storageElement*)(current->content);
|
||||
|
||||
if (fwrite(&(s->ptr), sizeof(int), 1, file) != 1)
|
||||
rc = -1;
|
||||
if (fwrite(&(s->size), sizeof(int), 1, file) != 1)
|
||||
rc = -1;
|
||||
for (j = 0; j < s->size; j++)
|
||||
{
|
||||
if (fwrite(i, sizeof(int), 1, file) != -1)
|
||||
rc = -1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (fwrite(&(s->ptr), sizeof(s->ptr), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(&(current->size), sizeof(current->size), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(s->ptr, current->size, 1, file) != 1)
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if defined(HEAP_UNIT_TESTS)
|
||||
|
||||
void Log(int log_level, int msgno, char* format, ...)
|
||||
{
|
||||
printf("Log %s", format);
|
||||
}
|
||||
|
||||
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
|
||||
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
|
||||
#define free(x) myfree(__FILE__, __LINE__, x)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char* h = NULL;
|
||||
Heap_initialize();
|
||||
|
||||
h = malloc(12);
|
||||
free(h);
|
||||
printf("freed h\n");
|
||||
|
||||
h = malloc(12);
|
||||
h = realloc(h, 14);
|
||||
h = realloc(h, 25);
|
||||
h = realloc(h, 255);
|
||||
h = realloc(h, 2225);
|
||||
h = realloc(h, 22225);
|
||||
printf("freeing h\n");
|
||||
free(h);
|
||||
Heap_terminate();
|
||||
printf("Finishing\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
35
src/Heap.h
35
src/Heap.h
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,19 +8,22 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - use tree data structure instead of list
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined(HEAP_H)
|
||||
#define HEAP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#if defined(HIGH_PERFORMANCE)
|
||||
#define NO_HEAP_TRACKING 1
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HEAP_TRACKING 1
|
||||
#if HEAP_TRACKING
|
||||
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
/**
|
||||
* redefines malloc to use "mymalloc" so that heap allocation can be tracked
|
||||
* @param x the size of the item to be allocated
|
||||
|
|
@ -44,23 +47,27 @@
|
|||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Information about the state of the heap.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int current_size; /**< current size of the heap in bytes */
|
||||
int max_size; /**< max size the heap has reached in bytes */
|
||||
} heap_info;
|
||||
|
||||
|
||||
void* mymalloc(char*, int, size_t size);
|
||||
void* myrealloc(char*, int, void* p, size_t size);
|
||||
void myfree(char*, int, void* p);
|
||||
void* Heap_findItem(void* p);
|
||||
void Heap_unlink(char*, int, void* p);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int current_size;
|
||||
int max_size;
|
||||
} heap_info;
|
||||
|
||||
void HeapScan();
|
||||
void Heap_scan(FILE* file);
|
||||
int Heap_initialize(void);
|
||||
void Heap_terminate(void);
|
||||
heap_info* Heap_get_info(void);
|
||||
int HeapDump(FILE* file);
|
||||
int HeapDumpString(FILE* file, char* str);
|
||||
void* Heap_findItem(void* p);
|
||||
void Heap_unlink(char* file, int line, void* p);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -253,9 +254,9 @@ int ListRemove(List* aList, void* content)
|
|||
* @param aList the list from which the item is to be removed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemoveHead(List* aList)
|
||||
void* ListDetachHead(List* aList)
|
||||
{
|
||||
int rc = 0;
|
||||
void *content = NULL;
|
||||
if (aList->count > 0)
|
||||
{
|
||||
ListElement* first = aList->first;
|
||||
|
|
@ -263,14 +264,26 @@ int ListRemoveHead(List* aList)
|
|||
aList->current = first->next;
|
||||
if (aList->last == first) /* i.e. no of items in list == 1 */
|
||||
aList->last = NULL;
|
||||
free(first->content);
|
||||
content = first->content;
|
||||
aList->first = aList->first->next;
|
||||
if (aList->first)
|
||||
aList->first->prev = NULL;
|
||||
free(first);
|
||||
--(aList->count);
|
||||
}
|
||||
return rc;
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an the first item in a list.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemoveHead(List* aList)
|
||||
{
|
||||
free(ListDetachHead(aList));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(LINKEDLIST_H)
|
||||
|
|
@ -74,6 +75,7 @@ void ListInsert(List* aList, void* content, int size, ListElement* index);
|
|||
|
||||
int ListRemove(List* aList, void* content);
|
||||
int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*));
|
||||
void* ListDetachHead(List* aList);
|
||||
int ListRemoveHead(List* aList);
|
||||
void* ListPopTail(List* aList);
|
||||
|
||||
|
|
|
|||
132
src/Log.c
132
src/Log.c
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Log.h"
|
||||
|
|
@ -17,6 +18,7 @@
|
|||
#include "Messages.h"
|
||||
#include "LinkedList.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -26,6 +28,7 @@
|
|||
|
||||
#if !defined(WIN32)
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#define GETTIMEOFDAY 1
|
||||
#else
|
||||
#define snprintf _snprintf
|
||||
|
|
@ -37,6 +40,13 @@
|
|||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#if !defined(WIN32)
|
||||
/**
|
||||
* _unlink mapping for linux
|
||||
*/
|
||||
#define _unlink unlink
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(min)
|
||||
#define min(A,B) ( (A) < (B) ? (A):(B))
|
||||
|
|
@ -75,7 +85,13 @@ static traceEntry* trace_queue = NULL;
|
|||
static int trace_queue_size = 0;
|
||||
|
||||
static FILE* trace_destination = NULL; /**< flag to indicate if trace is to be sent to a stream */
|
||||
static char* trace_destination_name = NULL; /**< the name of the trace file */
|
||||
static char* trace_destination_backup_name = NULL; /**< the name of the backup trace file */
|
||||
static int lines_written = 0; /**< number of lines written to the current output file */
|
||||
static int max_lines_per_file = 1000; /**< maximum number of lines to write to one trace file */
|
||||
static int trace_output_level = -1;
|
||||
static Log_traceCallback* trace_callback = NULL;
|
||||
static void Log_output(int log_level, char* msg);
|
||||
|
||||
static int sametime_count = 0;
|
||||
#if defined(GETTIMEOFDAY)
|
||||
|
|
@ -85,8 +101,15 @@ struct timeb ts, last_ts;
|
|||
#endif
|
||||
static char msg_buf[512];
|
||||
|
||||
#if defined(WIN32)
|
||||
mutex_type log_mutex;
|
||||
#else
|
||||
static pthread_mutex_t log_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type log_mutex = &log_mutex_store;
|
||||
#endif
|
||||
|
||||
int Log_initialize()
|
||||
|
||||
int Log_initialize(Log_nameValue* info)
|
||||
{
|
||||
int rc = -1;
|
||||
char* envval = NULL;
|
||||
|
|
@ -97,8 +120,21 @@ int Log_initialize()
|
|||
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "wt")) == NULL)
|
||||
if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "w")) == NULL)
|
||||
trace_destination = stdout;
|
||||
else
|
||||
{
|
||||
trace_destination_name = malloc(strlen(envval) + 1);
|
||||
strcpy(trace_destination_name, envval);
|
||||
trace_destination_backup_name = malloc(strlen(envval) + 3);
|
||||
sprintf(trace_destination_backup_name, "%s.0", trace_destination_name);
|
||||
}
|
||||
}
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE_MAX_LINES")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
max_lines_per_file = atoi(envval);
|
||||
if (max_lines_per_file <= 0)
|
||||
max_lines_per_file = 1000;
|
||||
}
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE_LEVEL")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
|
|
@ -110,11 +146,58 @@ int Log_initialize()
|
|||
trace_settings.trace_level = TRACE_MINIMUM;
|
||||
else if (strcmp(envval, "PROTOCOL") == 0 || strcmp(envval, "TRACE_PROTOCOL") == 0)
|
||||
trace_output_level = TRACE_PROTOCOL;
|
||||
else if (strcmp(envval, "ERROR") == 0 || strcmp(envval, "TRACE_ERROR") == 0)
|
||||
trace_output_level = LOG_ERROR;
|
||||
}
|
||||
Log_output(TRACE_MINIMUM, "=========================================================");
|
||||
Log_output(TRACE_MINIMUM, " Trace Output");
|
||||
if (info)
|
||||
{
|
||||
while (info->name)
|
||||
{
|
||||
sprintf(msg_buf, "%s: %s", info->name, info->value);
|
||||
Log_output(TRACE_MINIMUM, msg_buf);
|
||||
info++;
|
||||
}
|
||||
}
|
||||
#if !defined(WIN32)
|
||||
struct stat buf;
|
||||
if (stat("/proc/version", &buf) != -1)
|
||||
{
|
||||
FILE* vfile;
|
||||
|
||||
if ((vfile = fopen("/proc/version", "r")) != NULL)
|
||||
{
|
||||
int len;
|
||||
|
||||
strcpy(msg_buf, "/proc/version: ");
|
||||
len = strlen(msg_buf);
|
||||
if (fgets(&msg_buf[len], sizeof(msg_buf) - len, vfile))
|
||||
Log_output(TRACE_MINIMUM, msg_buf);
|
||||
fclose(vfile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Log_output(TRACE_MINIMUM, "=========================================================");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void Log_setTraceCallback(Log_traceCallback* callback)
|
||||
{
|
||||
trace_callback = callback;
|
||||
}
|
||||
|
||||
|
||||
void Log_setTraceLevel(enum LOG_LEVELS level)
|
||||
{
|
||||
if (level < TRACE_MINIMUM) /* the lowest we can go is TRACE_MINIMUM*/
|
||||
trace_settings.trace_level = level;
|
||||
trace_output_level = level;
|
||||
}
|
||||
|
||||
|
||||
void Log_terminate()
|
||||
{
|
||||
free(trace_queue);
|
||||
|
|
@ -126,6 +209,10 @@ void Log_terminate()
|
|||
fclose(trace_destination);
|
||||
trace_destination = NULL;
|
||||
}
|
||||
if (trace_destination_name)
|
||||
free(trace_destination_name);
|
||||
if (trace_destination_backup_name)
|
||||
free(trace_destination_backup_name);
|
||||
start_index = -1;
|
||||
next_index = 0;
|
||||
trace_output_level = -1;
|
||||
|
|
@ -222,13 +309,44 @@ static char* Log_formatTraceEntry(traceEntry* cur_entry)
|
|||
}
|
||||
|
||||
|
||||
static void Log_output(int log_level, char* msg)
|
||||
{
|
||||
if (trace_destination)
|
||||
{
|
||||
Thread_lock_mutex(log_mutex); /* need to lock around the fiddling with the log files */
|
||||
fprintf(trace_destination, "%s\n", msg);
|
||||
|
||||
if (trace_destination != stdout && ++lines_written >= max_lines_per_file)
|
||||
{
|
||||
|
||||
fclose(trace_destination);
|
||||
_unlink(trace_destination_backup_name); /* remove any old backup trace file */
|
||||
rename(trace_destination_name, trace_destination_backup_name); /* rename recently closed to backup */
|
||||
trace_destination = fopen(trace_destination_name, "w"); /* open new trace file */
|
||||
if (trace_destination == NULL)
|
||||
trace_destination = stdout;
|
||||
lines_written = 0;
|
||||
}
|
||||
else
|
||||
fflush(trace_destination);
|
||||
Thread_unlock_mutex(log_mutex);
|
||||
}
|
||||
|
||||
if (trace_callback)
|
||||
(*trace_callback)(log_level, msg);
|
||||
}
|
||||
|
||||
|
||||
static void Log_posttrace(int log_level, traceEntry* cur_entry)
|
||||
{
|
||||
if (trace_destination &&
|
||||
((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
|
||||
if (((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
|
||||
{
|
||||
fprintf(trace_destination, "%s\n", &Log_formatTraceEntry(cur_entry)[7]);
|
||||
fflush(trace_destination);
|
||||
char* msg = NULL;
|
||||
|
||||
if (trace_destination || trace_callback)
|
||||
msg = &Log_formatTraceEntry(cur_entry)[7];
|
||||
|
||||
Log_output(log_level, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
17
src/Log.h
17
src/Log.h
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(LOG_H)
|
||||
|
|
@ -27,7 +28,7 @@ map LOG_LEVELS
|
|||
}
|
||||
BE*/
|
||||
|
||||
enum {
|
||||
enum LOG_LEVELS {
|
||||
TRACE_MAXIMUM = 1,
|
||||
TRACE_MEDIUM,
|
||||
TRACE_MINIMUM,
|
||||
|
|
@ -60,10 +61,20 @@ extern trace_settings_type trace_settings;
|
|||
#define TRACE_MIN TRACE_MINIMUM
|
||||
#define TRACE_MED TRACE_MEDIUM
|
||||
|
||||
int Log_initialize();
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* value;
|
||||
} Log_nameValue;
|
||||
|
||||
int Log_initialize(Log_nameValue*);
|
||||
void Log_terminate();
|
||||
|
||||
void Log(int, int, char *, ...);
|
||||
void Log_stackTrace(int, int, int, int, const char*, int, int*);
|
||||
|
||||
typedef void Log_traceCallback(enum LOG_LEVELS level, char* message);
|
||||
void Log_setTraceCallback(Log_traceCallback* callback);
|
||||
void Log_setTraceLevel(enum LOG_LEVELS level);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
281
src/MQTTClient.c
281
src/MQTTClient.c
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - bug 384016 - segv setting will message
|
||||
* Ian Craggs - bug 384053 - v1.0.0.7 - stop MQTTClient_receive on socket error
|
||||
* Ian Craggs, Allan Stockdill-Mander - add ability to connect with SSL
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
|
@ -29,10 +30,14 @@
|
|||
#include "StackTrace.h"
|
||||
#include "Heap.h"
|
||||
|
||||
#if defined(OPENSSL)
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#define URI_TCP "tcp://"
|
||||
|
||||
#define BUILD_TIMESTAMP __DATE__ " " __TIME__ /* __TIMESTAMP__ */
|
||||
#define CLIENT_VERSION "1.0.0.7" /* __VERSION__ */
|
||||
#define CLIENT_VERSION "1.0.0.8"
|
||||
|
||||
char* client_timestamp_eye = "MQTTClientV3_Timestamp " BUILD_TIMESTAMP;
|
||||
char* client_version_eye = "MQTTClientV3_Version " CLIENT_VERSION;
|
||||
|
|
@ -50,6 +55,8 @@ MQTTProtocol state;
|
|||
#if defined(WIN32)
|
||||
static mutex_type mqttclient_mutex = NULL;
|
||||
extern mutex_type stack_mutex;
|
||||
extern mutex_type heap_mutex;
|
||||
extern mutex_type log_mutex;
|
||||
BOOL APIENTRY DllMain(HANDLE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved)
|
||||
|
|
@ -62,6 +69,8 @@ BOOL APIENTRY DllMain(HANDLE hModule,
|
|||
{
|
||||
mqttclient_mutex = CreateMutex(NULL, 0, NULL);
|
||||
stack_mutex = CreateMutex(NULL, 0, NULL);
|
||||
heap_mutex = CreateMutex(NULL, 0, NULL);
|
||||
log_mutex = CreateMutex(NULL, 0, NULL);
|
||||
}
|
||||
case DLL_THREAD_ATTACH:
|
||||
Log(TRACE_MAX, -1, "DLL thread attach");
|
||||
|
|
@ -104,6 +113,9 @@ typedef struct
|
|||
typedef struct
|
||||
{
|
||||
char* serverURI;
|
||||
#if defined(OPENSSL)
|
||||
int ssl;
|
||||
#endif
|
||||
Clients* c;
|
||||
MQTTClient_connectionLost* cl;
|
||||
MQTTClient_messageArrived* ma;
|
||||
|
|
@ -119,7 +131,6 @@ typedef struct
|
|||
|
||||
} MQTTClients;
|
||||
|
||||
|
||||
void MQTTClient_sleep(long milliseconds)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
|
|
@ -210,10 +221,13 @@ int MQTTClient_create(MQTTClient* handle, char* serverURI, char* clientId,
|
|||
#if defined(HEAP_H)
|
||||
Heap_initialize();
|
||||
#endif
|
||||
Log_initialize();
|
||||
Log_initialize(NULL);
|
||||
bstate->clients = ListInitialize();
|
||||
Socket_outInitialize();
|
||||
handles = ListInitialize();
|
||||
#if defined(OPENSSL)
|
||||
SSLSocket_initialize();
|
||||
#endif
|
||||
initialized = 1;
|
||||
}
|
||||
m = malloc(sizeof(MQTTClients));
|
||||
|
|
@ -221,6 +235,13 @@ int MQTTClient_create(MQTTClient* handle, char* serverURI, char* clientId,
|
|||
memset(m, '\0', sizeof(MQTTClients));
|
||||
if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0)
|
||||
serverURI += strlen(URI_TCP);
|
||||
#if defined(OPENSSL)
|
||||
else if (strncmp(URI_SSL, serverURI, strlen(URI_SSL)) == 0)
|
||||
{
|
||||
serverURI += strlen(URI_SSL);
|
||||
m->ssl = 1;
|
||||
}
|
||||
#endif
|
||||
m->serverURI = malloc(strlen(serverURI)+1);
|
||||
strcpy(m->serverURI, serverURI);
|
||||
ListAppend(handles, m, sizeof(MQTTClients));
|
||||
|
|
@ -304,7 +325,7 @@ void MQTTClient_destroy(MQTTClient* handle)
|
|||
|
||||
if (m->c)
|
||||
{
|
||||
int saved_socket = m->c->socket;
|
||||
int saved_socket = m->c->net.socket;
|
||||
char* saved_clientid = malloc(strlen(m->c->clientID)+1);
|
||||
strcpy(saved_clientid, m->c->clientID);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
|
|
@ -332,11 +353,6 @@ exit:
|
|||
}
|
||||
|
||||
|
||||
#if defined(HEAP_H)
|
||||
#undef malloc
|
||||
#undef realloc
|
||||
#undef free
|
||||
#endif
|
||||
void MQTTClient_freeMessage(MQTTClient_message** message)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
|
|
@ -354,13 +370,6 @@ void MQTTClient_free(void* memory)
|
|||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
#if defined(HEAP_H)
|
||||
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
|
||||
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
|
||||
#define free(x) myfree(__FILE__, __LINE__, x)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* topicLen, MQTTClient_message** message)
|
||||
{
|
||||
|
|
@ -373,9 +382,6 @@ int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* top
|
|||
if (strlen(*topicName) != *topicLen)
|
||||
rc = MQTTCLIENT_TOPICNAME_TRUNCATED;
|
||||
ListRemove(m->c->messageQueue, m->c->messageQueue->first->content);
|
||||
Heap_unlink(__FILE__, __LINE__, (*message)->payload);
|
||||
Heap_unlink(__FILE__, __LINE__, *message);
|
||||
Heap_unlink(__FILE__, __LINE__, *topicName);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -390,7 +396,7 @@ int MQTTClient_deliverMessage(int rc, MQTTClients* m, char** topicName, int* top
|
|||
int clientSockCompare(void* a, void* b)
|
||||
{
|
||||
MQTTClients* m = (MQTTClients*)a;
|
||||
return m->c->socket == *(int*)b;
|
||||
return m->c->net.socket == *(int*)b;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -457,8 +463,6 @@ thread_return_type WINAPI MQTTClient_run(void* n)
|
|||
{
|
||||
qEntry* qe = (qEntry*)(m->c->messageQueue->first->content);
|
||||
int topicLen = qe->topicLen;
|
||||
void* payload_ptr = qe->msg->payload; /* saved so we can unlink it after a successful messageArrived call,
|
||||
because it is held in a structure which might be freed */
|
||||
|
||||
if (strlen(qe->topicName) == topicLen)
|
||||
topicLen = 0;
|
||||
|
|
@ -473,12 +477,7 @@ thread_return_type WINAPI MQTTClient_run(void* n)
|
|||
* so we must be careful how we use it.
|
||||
*/
|
||||
if (rc)
|
||||
{
|
||||
Heap_unlink(__FILE__, __LINE__, qe->topicName);
|
||||
Heap_unlink(__FILE__, __LINE__, payload_ptr);
|
||||
Heap_unlink(__FILE__, __LINE__, qe->msg);
|
||||
ListRemove(m->c->messageQueue, qe);
|
||||
}
|
||||
else
|
||||
Log(TRACE_MIN, -1, "False returned from messageArrived for client %s, message remains on queue",
|
||||
m->c->clientID);
|
||||
|
|
@ -509,11 +508,31 @@ thread_return_type WINAPI MQTTClient_run(void* n)
|
|||
int error;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
if ((m->rc = getsockopt(m->c->socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
|
||||
if ((m->rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
|
||||
m->rc = error;
|
||||
Log(TRACE_MIN, -1, "Posting connect semaphore for client %s rc %d", m->c->clientID, m->rc);
|
||||
Thread_post_sem(m->connect_sem);
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (m->c->connect_state == 2 && !Thread_check_sem(m->connect_sem))
|
||||
{
|
||||
rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket);
|
||||
if (rc == 1)
|
||||
{
|
||||
if (!m->c->cleansession && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
m->rc = rc;
|
||||
Log(TRACE_MIN, -1, "Posting connect semaphore for SSL client %s rc %d", m->c->clientID, m->rc);
|
||||
Thread_post_sem(m->connect_sem);
|
||||
}
|
||||
else if (rc == SSL_FATAL)
|
||||
{
|
||||
m->rc = rc;
|
||||
//tostop = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
run_id = 0;
|
||||
|
|
@ -594,16 +613,23 @@ void MQTTProtocol_closeSession(Clients* client, int sendwill)
|
|||
{
|
||||
FUNC_ENTRY;
|
||||
client->good = 0;
|
||||
if (client->socket > 0)
|
||||
if (client->net.socket > 0)
|
||||
{
|
||||
if (client->connected || client->connect_state)
|
||||
{
|
||||
MQTTPacket_send_disconnect(client->socket, client->clientID);
|
||||
MQTTPacket_send_disconnect(&client->net, client->clientID);
|
||||
client->connected = 0;
|
||||
client->connect_state = 0;
|
||||
}
|
||||
Socket_close(client->socket);
|
||||
client->socket = 0;
|
||||
#if defined(OPENSSL)
|
||||
if (client->net.ssl)
|
||||
SSLSocket_close(client->net.ssl);
|
||||
#endif
|
||||
Socket_close(client->net.socket);
|
||||
client->net.socket = 0;
|
||||
#if defined(OPENSSL)
|
||||
client->net.ssl = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (client->cleansession)
|
||||
|
|
@ -685,7 +711,7 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (strncmp(options->struct_id, "MQTC", 4) != 0 || options->struct_version != 0)
|
||||
if (strncmp(options->struct_id, "MQTC", 4) != 0 || (options->struct_version != 0 && options->struct_version != 1))
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
|
|
@ -699,6 +725,17 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
|||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */
|
||||
{
|
||||
if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version != 0)
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_STRUCTURE;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((options->username && !UTF8_validateString(options->username)) ||
|
||||
(options->password && !UTF8_validateString(options->password)))
|
||||
|
|
@ -726,20 +763,30 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
|||
|
||||
if (options->will && options->will->struct_version == 0)
|
||||
{
|
||||
m->c->will = malloc(sizeof(willMessages));
|
||||
m->c->will = malloc(sizeof(willMessages));
|
||||
m->c->will->msg = options->will->message;
|
||||
m->c->will->qos = options->will->qos;
|
||||
m->c->will->retained = options->will->retained;
|
||||
m->c->will->topic = options->will->topicName;
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (options->struct_version != 0 && options->ssl)
|
||||
m->c->sslopts = options->ssl;
|
||||
#endif
|
||||
|
||||
m->c->username = options->username;
|
||||
m->c->password = options->password;
|
||||
m->c->retryInterval = options->retryInterval;
|
||||
m->c->connectOptionsVersion = options->struct_version;
|
||||
|
||||
Log(TRACE_MIN, -1, "Connecting to serverURI %s", m->serverURI);
|
||||
#if defined(OPENSSL)
|
||||
rc = MQTTProtocol_connect(m->serverURI, m->c, m->ssl);
|
||||
#else
|
||||
rc = MQTTProtocol_connect(m->serverURI, m->c);
|
||||
#endif
|
||||
if (rc == SOCKET_ERROR)
|
||||
goto exit;
|
||||
|
||||
if (m->c->connect_state == 0)
|
||||
{
|
||||
|
|
@ -747,7 +794,7 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (m->c->connect_state == 1)
|
||||
if (m->c->connect_state == 1) /* TCP connect started - wait for completion */
|
||||
{
|
||||
Thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start));
|
||||
|
|
@ -757,16 +804,67 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
|||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
m->c->connect_state = 2; /* TCP connect completed, in which case send the MQTT connect packet */
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->ssl)
|
||||
{
|
||||
if ((m->c->net.ssl = SSLSocket_setSocketForSSL(m->c->net.socket, m->c->sslopts)) != NULL)
|
||||
{
|
||||
if (m->c->session != NULL)
|
||||
if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1)
|
||||
Log(TRACE_MIN, -1, "Failed to set SSL session with stored data, non critical");
|
||||
rc = SSLSocket_connect(m->c->net.ssl, m->c->net.socket);
|
||||
if (rc == -1)
|
||||
m->c->connect_state = 2;
|
||||
else if (rc == SSL_FATAL)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
else if (rc == 1 && !m->c->cleansession && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */
|
||||
if (MQTTPacket_send_connect(m->c) == SOCKET_ERROR)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (m->c->connect_state == 2) /* SSL connect sent - wait for completion */
|
||||
{
|
||||
Thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_waitfor(handle, CONNECT, &rc, millisecsTimeout - MQTTClient_elapsed(start));
|
||||
Thread_lock_mutex(mqttclient_mutex);
|
||||
if (rc != 1)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
m->c->connect_state = 3; /* TCP connect completed, in which case send the MQTT connect packet */
|
||||
if (MQTTPacket_send_connect(m->c) == SOCKET_ERROR)
|
||||
{
|
||||
rc = SOCKET_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m->c->connect_state == 2)
|
||||
if (m->c->connect_state == 3) /* MQTT connect sent - wait for CONNACK */
|
||||
{
|
||||
MQTTPacket* pack = NULL;
|
||||
|
||||
|
|
@ -778,11 +876,12 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
|||
else
|
||||
{
|
||||
Connack* connack = (Connack*)pack;
|
||||
Log(LOG_PROTOCOL, 1, NULL, m->c->net.socket, m->c->clientID, connack->rc);
|
||||
if ((rc = connack->rc) == MQTTCLIENT_SUCCESS)
|
||||
{
|
||||
m->c->connected = 1;
|
||||
m->c->good = 1;
|
||||
m->c->connect_state = 3;
|
||||
m->c->connect_state = 0; //3;
|
||||
time(&(m->c->lastContact));
|
||||
if (m->c->cleansession)
|
||||
rc = MQTTClient_cleanSession(m->c);
|
||||
|
|
@ -813,11 +912,11 @@ int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options)
|
|||
}
|
||||
|
||||
exit:
|
||||
if (m->c->will)
|
||||
{
|
||||
free(m->c->will);
|
||||
m->c->will = NULL;
|
||||
}
|
||||
if (m->c->will)
|
||||
{
|
||||
free(m->c->will);
|
||||
m->c->will = NULL;
|
||||
}
|
||||
Thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
|
|
@ -935,6 +1034,12 @@ int MQTTClient_subscribeMany(MQTTClient handle, int count, char** topic, int* qo
|
|||
rc = MQTTCLIENT_BAD_UTF8_STRING;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if(qos[i] < 0 || qos[i] > 2)
|
||||
{
|
||||
rc = MQTTCLIENT_BAD_QOS;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
|
|
@ -955,7 +1060,7 @@ int MQTTClient_subscribeMany(MQTTClient handle, int count, char** topic, int* qo
|
|||
Thread_lock_mutex(mqttclient_mutex);
|
||||
if (pack != NULL)
|
||||
{
|
||||
rc = MQTTProtocol_handleSubacks(pack, m->c->socket);
|
||||
rc = MQTTProtocol_handleSubacks(pack, m->c->net.socket);
|
||||
m->pack = NULL;
|
||||
}
|
||||
else
|
||||
|
|
@ -1033,7 +1138,7 @@ int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char** topic)
|
|||
Thread_lock_mutex(mqttclient_mutex);
|
||||
if (pack != NULL)
|
||||
{
|
||||
rc = MQTTProtocol_handleUnsubacks(pack, m->c->socket);
|
||||
rc = MQTTProtocol_handleUnsubacks(pack, m->c->net.socket);
|
||||
m->pack = NULL;
|
||||
}
|
||||
else
|
||||
|
|
@ -1122,7 +1227,7 @@ int MQTTClient_publish(MQTTClient handle, char* topicName, int payloadlen, void*
|
|||
*/
|
||||
if (rc == TCPSOCKET_INTERRUPTED)
|
||||
{
|
||||
while (m->c->connected == 1 && SocketBuffer_getWrite(m->c->socket))
|
||||
while (m->c->connected == 1 && SocketBuffer_getWrite(m->c->net.socket))
|
||||
{
|
||||
Thread_unlock_mutex(mqttclient_mutex);
|
||||
MQTTClient_yield();
|
||||
|
|
@ -1210,18 +1315,28 @@ MQTTPacket* MQTTClient_cycle(int* sock, unsigned long timeout, int* rc)
|
|||
tp.tv_usec = (timeout % 1000) * 1000; /* this field is microseconds! */
|
||||
}
|
||||
|
||||
/* 0 from getReadySocket indicates no work to do, -1 == error, but can happen normally */
|
||||
*sock = Socket_getReadySocket(0, &tp);
|
||||
#if defined(OPENSSL)
|
||||
if ((*sock = SSLSocket_getPendingRead()) == -1)
|
||||
{
|
||||
/* 0 from getReadySocket indicates no work to do, -1 == error, but can happen normally */
|
||||
#endif
|
||||
*sock = Socket_getReadySocket(0, &tp);
|
||||
#if defined(OPENSSL)
|
||||
}
|
||||
#endif
|
||||
Thread_lock_mutex(mqttclient_mutex);
|
||||
if (*sock > 0)
|
||||
{
|
||||
MQTTClients* m = NULL;
|
||||
if (ListFindItem(handles, sock, clientSockCompare) != NULL)
|
||||
m = (MQTTClient)(handles->current->content);
|
||||
if (m != NULL && m->c->connect_state == 1)
|
||||
*rc = 0; /* waiting for connect state to clear */
|
||||
else
|
||||
pack = MQTTPacket_Factory(*sock, rc);
|
||||
if (m != NULL)
|
||||
{
|
||||
if (m->c->connect_state == 1 || m->c->connect_state == 2)
|
||||
*rc = 0; /* waiting for connect state to clear */
|
||||
else
|
||||
pack = MQTTPacket_Factory(&m->c->net, rc);
|
||||
}
|
||||
if (pack)
|
||||
{
|
||||
int freed = 1;
|
||||
|
|
@ -1257,7 +1372,7 @@ MQTTPacket* MQTTClient_cycle(int* sock, unsigned long timeout, int* rc)
|
|||
}
|
||||
MQTTClient_retry();
|
||||
Thread_unlock_mutex(mqttclient_mutex);
|
||||
FUNC_EXIT;
|
||||
FUNC_EXIT_RC(*rc);
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
|
@ -1299,7 +1414,7 @@ MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, long
|
|||
{
|
||||
int sock = -1;
|
||||
pack = MQTTClient_cycle(&sock, 100L, rc);
|
||||
if (sock == m->c->socket)
|
||||
if (sock == m->c->net.socket)
|
||||
{
|
||||
if (pack && (pack->header.bits.type == packet_type))
|
||||
break;
|
||||
|
|
@ -1308,10 +1423,22 @@ MQTTPacket* MQTTClient_waitfor(MQTTClient handle, int packet_type, int* rc, long
|
|||
int error;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
if ((*rc = getsockopt(m->c->socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
|
||||
if ((*rc = getsockopt(m->c->net.socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len)) == 0)
|
||||
*rc = error;
|
||||
break;
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (m->c->connect_state == 2)
|
||||
{
|
||||
*rc = SSLSocket_connect(m->c->net.ssl, sock);
|
||||
if (*rc == 1 || *rc == SSL_FATAL)
|
||||
{
|
||||
if (*rc == 1 && !m->c->cleansession && m->c->session == NULL)
|
||||
m->c->session = SSL_get1_session(m->c->net.ssl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (MQTTClient_elapsed(start) > timeout)
|
||||
{
|
||||
|
|
@ -1359,15 +1486,13 @@ int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTC
|
|||
{
|
||||
int sock = 0;
|
||||
MQTTClient_cycle(&sock, (timeout > elapsed) ? timeout - elapsed : 0L, &rc);
|
||||
|
||||
|
||||
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
if (ListFindItem(handles, &sock, clientSockCompare) && /* find client corresponding to socket */
|
||||
(MQTTClient)(handles->current->content) == handle)
|
||||
break; /* there was an error on the socket we are interested in */
|
||||
}
|
||||
|
||||
elapsed = MQTTClient_elapsed(start);
|
||||
}
|
||||
while (elapsed < timeout && m->c->messageQueue->count == 0);
|
||||
|
|
@ -1493,7 +1618,7 @@ int MQTTClient_getPendingDeliveryTokens(MQTTClient handle, MQTTClient_deliveryTo
|
|||
int count = 0;
|
||||
|
||||
*tokens = malloc(sizeof(MQTTClient_deliveryToken) * (m->c->outboundMsgs->count + 1));
|
||||
Heap_unlink(__FILE__, __LINE__, *tokens);
|
||||
/*Heap_unlink(__FILE__, __LINE__, *tokens);*/
|
||||
while (ListNextElement(m->c->outboundMsgs, ¤t))
|
||||
{
|
||||
Messages* m = (Messages*)(current->content);
|
||||
|
|
@ -1507,3 +1632,35 @@ exit:
|
|||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MQTTClient_nameValue* MQTTClient_getVersionInfo()
|
||||
{
|
||||
#define MAX_INFO_STRINGS 7
|
||||
static MQTTClient_nameValue libinfo[MAX_INFO_STRINGS + 1];
|
||||
int i = 0;
|
||||
|
||||
libinfo[i].name = "version";
|
||||
libinfo[i++].value = CLIENT_VERSION;
|
||||
|
||||
libinfo[i].name = "build level";
|
||||
libinfo[i++].value = BUILD_TIMESTAMP;
|
||||
#if defined(OPENSSL)
|
||||
libinfo[i].name = "OpenSSL version";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_VERSION);
|
||||
|
||||
libinfo[i].name = "OpenSSL flags";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_CFLAGS);
|
||||
|
||||
libinfo[i].name = "OpenSSL build timestamp";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_BUILT_ON);
|
||||
|
||||
libinfo[i].name = "OpenSSL platform";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_PLATFORM);
|
||||
|
||||
libinfo[i].name = "OpenSSL directory";
|
||||
libinfo[i++].value = SSLeay_version(SSLEAY_DIR);
|
||||
#endif
|
||||
libinfo[i].name = NULL;
|
||||
libinfo[i].value = NULL;
|
||||
return libinfo;
|
||||
}
|
||||
|
|
|
|||
110
src/MQTTClient.h
110
src/MQTTClient.h
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,19 +8,31 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @mainpage MQTT Client library for C
|
||||
* © Copyright IBM Corp. 2009, 2011
|
||||
* © Copyright IBM Corp. 2009, 2012
|
||||
*
|
||||
* @brief An MQTT client library in C.
|
||||
*
|
||||
* An MQTT client application connects to MQTT-capable servers.
|
||||
* An MQTT client application connects to MQTT-capable servers.
|
||||
* A typical client is responsible for collecting information from a telemetry
|
||||
* device and publishing the information to the server. It can also subscribe
|
||||
* to topics, receive messages, and use this information to control the
|
||||
* telemetry device. MQTT clients implement the published MQTT v3 protocol.
|
||||
* telemetry device.
|
||||
*
|
||||
* MQTT clients implement the published MQTT v3 protocol. You can write your own
|
||||
* API to the MQTT protocol using the programming language and platform of your
|
||||
* choice. This can be time-consuming and error-prone.
|
||||
*
|
||||
* To simplify writing MQTT client applications, WebSphere MQ Telemetry provides
|
||||
* C and Java client libraries that encapsulate the MQTT v3 protocol for a
|
||||
* number of platforms. If you incorporate these libraries in your MQTT
|
||||
* applications, a fully functional MQTT client can be written in a few lines of
|
||||
* code. The information presented here documents the API provided by the IBM
|
||||
* MQTT Client library for C.
|
||||
*
|
||||
* <b>Using the client</b><br>
|
||||
* Applications that use the client library typically use a similar structure:
|
||||
|
|
@ -112,6 +124,10 @@
|
|||
* and version number.
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_STRUCTURE -8
|
||||
/**
|
||||
* Return code: A QoS value that falls outside of the acceptable range (0,1,2)
|
||||
*/
|
||||
#define MQTTCLIENT_BAD_QOS -9
|
||||
|
||||
/**
|
||||
* A handle representing an MQTT client. A valid client handle is available
|
||||
|
|
@ -129,6 +145,7 @@ typedef void* MQTTClient;
|
|||
* MQTTClient_getPendingDeliveryTokens()).
|
||||
*/
|
||||
typedef int MQTTClient_deliveryToken;
|
||||
typedef int MQTTClient_token;
|
||||
|
||||
/**
|
||||
* A structure representing the payload and attributes of an MQTT message. The
|
||||
|
|
@ -261,7 +278,6 @@ typedef void MQTTClient_deliveryComplete(void* context, MQTTClient_deliveryToken
|
|||
*/
|
||||
typedef void MQTTClient_connectionLost(void* context, char* cause);
|
||||
|
||||
|
||||
/**
|
||||
* This function sets the callback functions for a specific client.
|
||||
* If your client application doesn't use a particular callback, set the
|
||||
|
|
@ -290,7 +306,8 @@ typedef void MQTTClient_connectionLost(void* context, char* cause);
|
|||
* ::MQTTCLIENT_FAILURE if an error occurred.
|
||||
*/
|
||||
DLLExport int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl,
|
||||
MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc);
|
||||
MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc);
|
||||
|
||||
|
||||
/**
|
||||
* This function creates an MQTT client ready for connection to the
|
||||
|
|
@ -373,6 +390,57 @@ typedef struct
|
|||
|
||||
#define MQTTClient_willOptions_initializer { "MQTW", 0, NULL, NULL, 0, 0 }
|
||||
|
||||
/**
|
||||
* MQTTClient_sslProperties defines the settings to establish an SSL/TLS connection using the
|
||||
* OpenSSL library. It covers the following scenarios:
|
||||
* - Server authentication: The client needs the digital certificate of the server. It is included
|
||||
* in a store containting trusted material (also known as "trust store").
|
||||
* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In
|
||||
* addition to the digital certificate of the server in a trust store, the client will need its own
|
||||
* digital certificate and the private key used to sign its digital certificate stored in a "key store".
|
||||
* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed
|
||||
* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to
|
||||
* man-in-the-middle attacks.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQTS */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
int struct_version;
|
||||
|
||||
/** The file in PEM format containing the public digital certificates trusted by the client. */
|
||||
char* trustStore;
|
||||
|
||||
/** The file in PEM format containing the public certificate chain of the client. It may also include
|
||||
* the client's private key.
|
||||
*/
|
||||
char* keyStore;
|
||||
|
||||
/** If not included in the sslKeyStore, this setting points to the file in PEM format containing
|
||||
* the client's private key.
|
||||
*/
|
||||
char* privateKey;
|
||||
/** The password to load the client's privateKey if encrypted. */
|
||||
char* privateKeyPassword;
|
||||
|
||||
/**
|
||||
* The list of cipher suites that the client will present to the server during the SSL handshake. For a
|
||||
* full explanation of the cipher list format, please see the OpenSSL on-line documentation:
|
||||
* http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT
|
||||
* If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding
|
||||
* those offering no encryption- will be considered.
|
||||
* This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance).
|
||||
*/
|
||||
char* enabledCipherSuites;
|
||||
|
||||
/** True/False option to enable verification of the server certificate **/
|
||||
int enableServerCertAuth;
|
||||
|
||||
} MQTTClient_SSLOptions;
|
||||
|
||||
#define MQTTClient_SSLOptions_initializer { "MQTS", 0, NULL, NULL, NULL, NULL, NULL, 1 }
|
||||
|
||||
/**
|
||||
* MQTTClient_connectOptions defines several settings that control the way the
|
||||
* client connects to an MQTT server.
|
||||
|
|
@ -391,7 +459,7 @@ typedef struct
|
|||
{
|
||||
/** The eyecatcher for this structure. must be MQTC. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0 */
|
||||
/** The version number of this structure. Must be 0 or 1. 0 signifies no SSL options */
|
||||
int struct_version;
|
||||
/** The "keep alive" interval, measured in seconds, defines the maximum time
|
||||
* that should pass without communication between the client and the server
|
||||
|
|
@ -461,9 +529,34 @@ typedef struct
|
|||
* The time interval in seconds
|
||||
*/
|
||||
int retryInterval;
|
||||
/**
|
||||
* This is a pointer to an MQTTClient_SSLOptions structure. If your
|
||||
* application does not make use of SSL, set this pointer to NULL.
|
||||
*/
|
||||
MQTTClient_SSLOptions* ssl;
|
||||
} MQTTClient_connectOptions;
|
||||
|
||||
#define MQTTClient_connectOptions_initializer { "MQTC", 0, 60, 1, 1, NULL, NULL, NULL, 30, 20 }
|
||||
#define MQTTClient_connectOptions_initializer { "MQTC", 1, 60, 1, 1, NULL, NULL, NULL, 30, 20, NULL }
|
||||
|
||||
/**
|
||||
* MQTTClient_libraryInfo is used to store details relating to the currently used
|
||||
* library such as the version in use, the time it was built and relevant openSSL
|
||||
* options.
|
||||
* There is one static instance of this struct in MQTTClient.c
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* value;
|
||||
} MQTTClient_nameValue;
|
||||
|
||||
/**
|
||||
* This function returns version information about the library.
|
||||
* no trace information will be returned.
|
||||
* @return an array of strings describing the library. The last entry is a NULL pointer.
|
||||
*/
|
||||
DLLExport MQTTClient_nameValue* MQTTClient_getVersionInfo();
|
||||
|
||||
/**
|
||||
* This function attempts to connect a previously-created client (see
|
||||
|
|
@ -602,7 +695,6 @@ DLLExport int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char** to
|
|||
*/
|
||||
DLLExport int MQTTClient_publish(MQTTClient handle, char* topicName, int payloadlen, void* payload, int qos, int retained,
|
||||
MQTTClient_deliveryToken* dt);
|
||||
|
||||
/**
|
||||
* This function attempts to publish a message to a given topic (see also
|
||||
* MQTTClient_publish()). An ::MQTTClient_deliveryToken is issued when
|
||||
|
|
|
|||
105
src/MQTTPacket.c
105
src/MQTTPacket.c
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -44,6 +45,8 @@ static char* packet_names[] =
|
|||
"PINGREQ", "PINGRESP", "DISCONNECT"
|
||||
};
|
||||
|
||||
char** MQTTClient_packet_names = packet_names;
|
||||
|
||||
|
||||
/**
|
||||
* Converts an MQTT packet code into its name
|
||||
|
|
@ -84,7 +87,7 @@ pf new_packets[] =
|
|||
* @param error pointer to the error code which is completed if no packet is returned
|
||||
* @return the packet structure or NULL if there was an error
|
||||
*/
|
||||
void* MQTTPacket_Factory(int socket, int* error)
|
||||
void* MQTTPacket_Factory(networkHandles* net, int* error)
|
||||
{
|
||||
char* data = NULL;
|
||||
static Header header;
|
||||
|
|
@ -96,15 +99,26 @@ void* MQTTPacket_Factory(int socket, int* error)
|
|||
*error = SOCKET_ERROR; /* indicate whether an error occurred, or not */
|
||||
|
||||
/* read the packet data from the socket */
|
||||
if ((*error = Socket_getch(socket, &(header.byte))) != TCPSOCKET_COMPLETE) /* first byte is the header byte */
|
||||
#if defined(OPENSSL)
|
||||
*error = (net->ssl) ? SSLSocket_getch(net->ssl, net->socket, &header.byte) : Socket_getch(net->socket, &header.byte);
|
||||
#else
|
||||
*error = Socket_getch(net->socket, &header.byte);
|
||||
#endif
|
||||
if (*error != TCPSOCKET_COMPLETE) /* first byte is the header byte */
|
||||
goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
|
||||
|
||||
/* now read the remaining length, so we know how much more to read */
|
||||
if ((*error = MQTTPacket_decode(socket, &remaining_length)) != TCPSOCKET_COMPLETE)
|
||||
if ((*error = MQTTPacket_decode(net, &remaining_length)) != TCPSOCKET_COMPLETE)
|
||||
goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
|
||||
|
||||
/* now read the rest, the variable header and payload */
|
||||
if ((data = Socket_getdata(socket, remaining_length, &actual_len)) == NULL)
|
||||
#if defined(OPENSSL)
|
||||
data = (net->ssl) ? SSLSocket_getdata(net->ssl, net->socket, remaining_length, &actual_len) :
|
||||
Socket_getdata(net->socket, remaining_length, &actual_len);
|
||||
#else
|
||||
data = Socket_getdata(net->socket, remaining_length, &actual_len);
|
||||
#endif
|
||||
if (data == NULL)
|
||||
{
|
||||
*error = SOCKET_ERROR;
|
||||
goto exit; /* socket error */
|
||||
|
|
@ -128,7 +142,7 @@ void* MQTTPacket_Factory(int socket, int* error)
|
|||
char *buf = malloc(10);
|
||||
buf[0] = header.byte;
|
||||
buf0len = 1 + MQTTPacket_encode(&buf[1], remaining_length);
|
||||
*error = MQTTPersistence_put(socket, buf, buf0len, 1,
|
||||
*error = MQTTPersistence_put(net->socket, buf, buf0len, 1,
|
||||
&data, &remaining_length, header.bits.type, ((Publish *)pack)->msgId, 1);
|
||||
free(buf);
|
||||
}
|
||||
|
|
@ -149,7 +163,7 @@ exit:
|
|||
* @param buflen the length of the data in buffer to be written
|
||||
* @return the completion code (TCPSOCKET_COMPLETE etc)
|
||||
*/
|
||||
int MQTTPacket_send(int socket, Header header, char* buffer, int buflen)
|
||||
int MQTTPacket_send(networkHandles* net, Header header, char* buffer, int buflen)
|
||||
{
|
||||
int rc, buf0len;
|
||||
char *buf;
|
||||
|
|
@ -163,11 +177,18 @@ int MQTTPacket_send(int socket, Header header, char* buffer, int buflen)
|
|||
{
|
||||
char* ptraux = buffer;
|
||||
int msgId = readInt(&ptraux);
|
||||
rc = MQTTPersistence_put(socket, buf, buf0len, 1, &buffer, &buflen,
|
||||
rc = MQTTPersistence_put(net->socket, buf, buf0len, 1, &buffer, &buflen,
|
||||
header.bits.type, msgId, 0);
|
||||
}
|
||||
#endif
|
||||
rc = Socket_putdatas(socket, buf, buf0len, 1, &buffer, &buflen);
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (net->ssl)
|
||||
rc = SSLSocket_putdatas(net->ssl, net->socket, buf, buf0len, 1, &buffer, &buflen);
|
||||
else
|
||||
#endif
|
||||
rc = Socket_putdatas(net->socket, buf, buf0len, 1, &buffer, &buflen);
|
||||
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
|
||||
|
|
@ -185,7 +206,7 @@ int MQTTPacket_send(int socket, Header header, char* buffer, int buflen)
|
|||
* @param buflens the lengths of the data in the array of buffers to be written
|
||||
* @return the completion code (TCPSOCKET_COMPLETE etc)
|
||||
*/
|
||||
int MQTTPacket_sends(int socket, Header header, int count, char** buffers, int* buflens)
|
||||
int MQTTPacket_sends(networkHandles* net, Header header, int count, char** buffers, int* buflens)
|
||||
{
|
||||
int i, rc, buf0len, total = 0;
|
||||
char *buf;
|
||||
|
|
@ -201,11 +222,16 @@ int MQTTPacket_sends(int socket, Header header, int count, char** buffers, int*
|
|||
{ /* persist PUBLISH QoS1 and Qo2 */
|
||||
char *ptraux = buffers[2];
|
||||
int msgId = readInt(&ptraux);
|
||||
rc = MQTTPersistence_put(socket, buf, buf0len, count, buffers, buflens,
|
||||
rc = MQTTPersistence_put(net->socket, buf, buf0len, count, buffers, buflens,
|
||||
header.bits.type, msgId, 0);
|
||||
}
|
||||
#endif
|
||||
rc = Socket_putdatas(socket, buf, buf0len, count, buffers, buflens);
|
||||
#if defined(OPENSSL)
|
||||
if (net->ssl)
|
||||
rc = SSLSocket_putdatas(net->ssl, net->socket, buf, buf0len, count, buffers, buflens);
|
||||
else
|
||||
#endif
|
||||
rc = Socket_putdatas(net->socket, buf, buf0len, count, buffers, buflens);
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
FUNC_EXIT_RC(rc);
|
||||
|
|
@ -244,7 +270,7 @@ int MQTTPacket_encode(char* buf, int length)
|
|||
* @param value the decoded length returned
|
||||
* @return the number of bytes read from the socket
|
||||
*/
|
||||
int MQTTPacket_decode(int socket, int* value)
|
||||
int MQTTPacket_decode(networkHandles* net, int* value)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
char c;
|
||||
|
|
@ -261,8 +287,13 @@ int MQTTPacket_decode(int socket, int* value)
|
|||
rc = SOCKET_ERROR; /* bad data */
|
||||
goto exit;
|
||||
}
|
||||
if ((rc = Socket_getch(socket, &c)) != TCPSOCKET_COMPLETE)
|
||||
goto exit;
|
||||
#if defined(OPENSSL)
|
||||
rc = (net->ssl) ? SSLSocket_getch(net->ssl, net->socket, &c) : Socket_getch(net->socket, &c);
|
||||
#else
|
||||
rc = Socket_getch(net->socket, &c);
|
||||
#endif
|
||||
if (rc != TCPSOCKET_COMPLETE)
|
||||
goto exit;
|
||||
*value += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((c & 128) != 0);
|
||||
|
|
@ -409,7 +440,7 @@ void* MQTTPacket_header_only(unsigned char aHeader, char* data, int datalen)
|
|||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_disconnect(int socket, char* clientID)
|
||||
int MQTTPacket_send_disconnect(networkHandles *net, char* clientID)
|
||||
{
|
||||
Header header;
|
||||
int rc = 0;
|
||||
|
|
@ -417,8 +448,8 @@ int MQTTPacket_send_disconnect(int socket, char* clientID)
|
|||
FUNC_ENTRY;
|
||||
header.byte = 0;
|
||||
header.bits.type = DISCONNECT;
|
||||
rc = MQTTPacket_send(socket, header, NULL, 0);
|
||||
Log(LOG_PROTOCOL, 28, NULL, socket, clientID, rc);
|
||||
rc = MQTTPacket_send(net, header, NULL, 0);
|
||||
Log(LOG_PROTOCOL, 28, NULL, net->socket, clientID, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -479,7 +510,7 @@ void MQTTPacket_freePublish(Publish* pack)
|
|||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_ack(int type, int msgid, int dup, int socket)
|
||||
int MQTTPacket_send_ack(int type, int msgid, int dup, networkHandles *net)
|
||||
{
|
||||
Header header;
|
||||
int rc;
|
||||
|
|
@ -491,7 +522,7 @@ int MQTTPacket_send_ack(int type, int msgid, int dup, int socket)
|
|||
header.bits.type = type;
|
||||
header.bits.dup = dup;
|
||||
writeInt(&ptr, msgid);
|
||||
if ((rc = MQTTPacket_send(socket, header, buf, 2)) != TCPSOCKET_INTERRUPTED)
|
||||
if ((rc = MQTTPacket_send(net, header, buf, 2)) != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
|
|
@ -504,13 +535,13 @@ int MQTTPacket_send_ack(int type, int msgid, int dup, int socket)
|
|||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_puback(int msgid, int socket, char* clientID)
|
||||
int MQTTPacket_send_puback(int msgid, networkHandles* net, char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(PUBACK, msgid, 0, socket);
|
||||
Log(LOG_PROTOCOL, 12, NULL, socket, clientID, msgid, rc);
|
||||
rc = MQTTPacket_send_ack(PUBACK, msgid, 0, net);
|
||||
Log(LOG_PROTOCOL, 12, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -536,13 +567,13 @@ void MQTTPacket_freeSuback(Suback* pack)
|
|||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pubrec(int msgid, int socket, char* clientID)
|
||||
int MQTTPacket_send_pubrec(int msgid, networkHandles* net, char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(PUBREC, msgid, 0, socket);
|
||||
Log(LOG_PROTOCOL, 13, NULL, socket, clientID, msgid, rc);
|
||||
rc = MQTTPacket_send_ack(PUBREC, msgid, 0, net);
|
||||
Log(LOG_PROTOCOL, 13, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -555,13 +586,13 @@ int MQTTPacket_send_pubrec(int msgid, int socket, char* clientID)
|
|||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pubrel(int msgid, int dup, int socket, char* clientID)
|
||||
int MQTTPacket_send_pubrel(int msgid, int dup, networkHandles* net, char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(PUBREL, msgid, dup, socket);
|
||||
Log(LOG_PROTOCOL, 16, NULL, socket, clientID, msgid, rc);
|
||||
rc = MQTTPacket_send_ack(PUBREL, msgid, dup, net);
|
||||
Log(LOG_PROTOCOL, 16, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -573,13 +604,13 @@ int MQTTPacket_send_pubrel(int msgid, int dup, int socket, char* clientID)
|
|||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pubcomp(int msgid, int socket, char* clientID)
|
||||
int MQTTPacket_send_pubcomp(int msgid, networkHandles* net, char* clientID)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_ack(PUBCOMP, msgid, 0, socket);
|
||||
Log(LOG_PROTOCOL, 18, NULL, socket, clientID, msgid, rc);
|
||||
rc = MQTTPacket_send_ack(PUBCOMP, msgid, 0, net);
|
||||
Log(LOG_PROTOCOL, 18, NULL, net->socket, clientID, msgid, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -614,7 +645,7 @@ void* MQTTPacket_ack(unsigned char aHeader, char* data, int datalen)
|
|||
* @param socket the open socket to send the data to
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int socket, char* clientID)
|
||||
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, char* clientID)
|
||||
{
|
||||
Header header;
|
||||
char *topiclen;
|
||||
|
|
@ -636,7 +667,7 @@ int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int s
|
|||
writeInt(&ptr, pack->msgId);
|
||||
ptr = topiclen;
|
||||
writeInt(&ptr, lens[1]);
|
||||
rc = MQTTPacket_sends(socket, header, 4, bufs, lens);
|
||||
rc = MQTTPacket_sends(net, header, 4, bufs, lens);
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
}
|
||||
|
|
@ -646,14 +677,14 @@ int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int s
|
|||
char* bufs[3] = {topiclen, pack->topic, pack->payload};
|
||||
int lens[3] = {2, strlen(pack->topic), pack->payloadlen};
|
||||
writeInt(&ptr, lens[1]);
|
||||
rc = MQTTPacket_sends(socket, header, 3, bufs, lens);
|
||||
rc = MQTTPacket_sends(net, header, 3, bufs, lens);
|
||||
}
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(topiclen);
|
||||
if (qos == 0)
|
||||
Log(LOG_PROTOCOL, 27, NULL, socket, clientID, retained, rc);
|
||||
Log(LOG_PROTOCOL, 27, NULL, net->socket, clientID, retained, rc);
|
||||
else
|
||||
Log(LOG_PROTOCOL, 10, NULL, socket, clientID, pack->msgId, qos, retained, rc,
|
||||
Log(LOG_PROTOCOL, 10, NULL, net->socket, clientID, pack->msgId, qos, retained, rc,
|
||||
min(20, pack->payloadlen), pack->payload);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,12 +8,16 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPACKET_H)
|
||||
#define MQTTPACKET_H
|
||||
|
||||
#include "Socket.h"
|
||||
#if defined(OPENSSL)
|
||||
#include "SSLSocket.h"
|
||||
#endif
|
||||
#include "LinkedList.h"
|
||||
#include "Clients.h"
|
||||
|
||||
|
|
@ -191,7 +195,7 @@ typedef Ack Pubcomp;
|
|||
typedef Ack Unsuback;
|
||||
|
||||
int MQTTPacket_encode(char* buf, int length);
|
||||
int MQTTPacket_decode(int socket, int* value);
|
||||
int MQTTPacket_decode(networkHandles* net, int* value);
|
||||
int readInt(char** pptr);
|
||||
char* readUTF(char** pptr, char* enddata);
|
||||
char readChar(char** pptr);
|
||||
|
|
@ -201,23 +205,23 @@ void writeUTF(char** pptr, char* string);
|
|||
|
||||
char* MQTTPacket_name(int ptype);
|
||||
|
||||
void* MQTTPacket_Factory(int socket, int* error);
|
||||
int MQTTPacket_send(int socket, Header header, char* buffer, int buflen);
|
||||
int MQTTPacket_sends(int socket, Header header, int count, char** buffers, int* buflens);
|
||||
void* MQTTPacket_Factory(networkHandles* net, int* error);
|
||||
int MQTTPacket_send(networkHandles* net, Header header, char* buffer, int buflen);
|
||||
int MQTTPacket_sends(networkHandles* net, Header header, int count, char** buffers, int* buflens);
|
||||
|
||||
void* MQTTPacket_header_only(unsigned char aHeader, char* data, int datalen);
|
||||
int MQTTPacket_send_disconnect(int socket, char* clientID);
|
||||
int MQTTPacket_send_disconnect(networkHandles* net, char* clientID);
|
||||
|
||||
void* MQTTPacket_publish(unsigned char aHeader, char* data, int datalen);
|
||||
void MQTTPacket_freePublish(Publish* pack);
|
||||
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, int socket, char* clientID);
|
||||
int MQTTPacket_send_puback(int msgid, int socket, char* clientID);
|
||||
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, char* clientID);
|
||||
int MQTTPacket_send_puback(int msgid, networkHandles* net, char* clientID);
|
||||
void* MQTTPacket_ack(unsigned char aHeader, char* data, int datalen);
|
||||
|
||||
void MQTTPacket_freeSuback(Suback* pack);
|
||||
int MQTTPacket_send_pubrec(int msgid, int socket, char* clientID);
|
||||
int MQTTPacket_send_pubrel(int msgid, int dup, int socket, char* clientID);
|
||||
int MQTTPacket_send_pubcomp(int msgid, int socket, char* clientID);
|
||||
int MQTTPacket_send_pubrec(int msgid, networkHandles* net, char* clientID);
|
||||
int MQTTPacket_send_pubrel(int msgid, int dup, networkHandles* net, char* clientID);
|
||||
int MQTTPacket_send_pubcomp(int msgid, networkHandles* net, char* clientID);
|
||||
|
||||
void MQTTPacket_free_packet(MQTTPacket* pack);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -83,8 +84,8 @@ int MQTTPacket_send_connect(Clients* client)
|
|||
if (client->password)
|
||||
writeUTF(&ptr, client->password);
|
||||
|
||||
rc = MQTTPacket_send(client->socket, packet.header, buf, len);
|
||||
Log(LOG_PROTOCOL, 0, NULL, client->socket, client->clientID, client->cleansession, rc);
|
||||
rc = MQTTPacket_send(&client->net, packet.header, buf, len);
|
||||
Log(LOG_PROTOCOL, 0, NULL, client->net.socket, client->clientID, client->cleansession, rc);
|
||||
free(buf);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
|
|
@ -118,7 +119,7 @@ void* MQTTPacket_connack(unsigned char aHeader, char* data, int datalen)
|
|||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pingreq(int socket, char* clientID)
|
||||
int MQTTPacket_send_pingreq(networkHandles* net, char* clientID)
|
||||
{
|
||||
Header header;
|
||||
int rc = 0;
|
||||
|
|
@ -126,7 +127,7 @@ int MQTTPacket_send_pingreq(int socket, char* clientID)
|
|||
FUNC_ENTRY;
|
||||
header.byte = 0;
|
||||
header.bits.type = PINGREQ;
|
||||
rc = MQTTPacket_send(socket, header, NULL, 0);
|
||||
rc = MQTTPacket_send(net, header, NULL, 0);
|
||||
Log(LOG_PROTOCOL, 20, NULL, socket, clientID, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
|
|
@ -143,7 +144,7 @@ int MQTTPacket_send_pingreq(int socket, char* clientID)
|
|||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, int socket, char* clientID)
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, networkHandles* net, char* clientID)
|
||||
{
|
||||
Header header;
|
||||
char *data, *ptr;
|
||||
|
|
@ -170,8 +171,8 @@ int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, int
|
|||
writeUTF(&ptr, (char*)(elem->content));
|
||||
writeChar(&ptr, *(int*)(qosElem->content));
|
||||
}
|
||||
rc = MQTTPacket_send(socket, header, data, datalen);
|
||||
Log(LOG_PROTOCOL, 22, NULL, socket, clientID, msgid, rc);
|
||||
rc = MQTTPacket_send(net, header, data, datalen);
|
||||
Log(LOG_PROTOCOL, 22, NULL, net->socket, clientID, msgid, rc);
|
||||
free(data);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
|
|
@ -215,7 +216,7 @@ void* MQTTPacket_suback(unsigned char aHeader, char* data, int datalen)
|
|||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, int socket, char* clientID)
|
||||
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, networkHandles* net, char* clientID)
|
||||
{
|
||||
Header header;
|
||||
char *data, *ptr;
|
||||
|
|
@ -238,8 +239,8 @@ int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, int socket, ch
|
|||
elem = NULL;
|
||||
while (ListNextElement(topics, &elem))
|
||||
writeUTF(&ptr, (char*)(elem->content));
|
||||
rc = MQTTPacket_send(socket, header, data, datalen);
|
||||
Log(LOG_PROTOCOL, 25, NULL, socket, clientID, msgid, rc);
|
||||
rc = MQTTPacket_send(net, header, data, datalen);
|
||||
Log(LOG_PROTOCOL, 25, NULL, net->socket, clientID, msgid, rc);
|
||||
free(data);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPACKETOUT_H)
|
||||
|
|
@ -18,11 +19,11 @@
|
|||
int MQTTPacket_send_connect(Clients* client);
|
||||
void* MQTTPacket_connack(unsigned char aHeader, char* data, int datalen);
|
||||
|
||||
int MQTTPacket_send_pingreq(int socket, char* clientID);
|
||||
int MQTTPacket_send_pingreq(networkHandles* net, char* clientID);
|
||||
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, int socket, char* clientID);
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, int msgid, int dup, networkHandles* net, char* clientID);
|
||||
void* MQTTPacket_suback(unsigned char aHeader, char* data, int datalen);
|
||||
|
||||
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, int socket, char* clientID);
|
||||
int MQTTPacket_send_unsubscribe(List* topics, int msgid, int dup, networkHandles* net, char* clientID);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -165,17 +166,23 @@ int MQTTPersistence_clear(Clients *c)
|
|||
int MQTTPersistence_restore(Clients *c)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys, *buffer;
|
||||
char **msgkeys = NULL,
|
||||
*buffer = NULL;
|
||||
int nkeys, buflen;
|
||||
int i = 0;
|
||||
int msgs_sent = 0;
|
||||
int msgs_rcvd = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ( c->persistence != NULL &&
|
||||
(rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0 )
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
|
||||
{
|
||||
while( rc == 0 && i < nkeys)
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
if ( (rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0 )
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) == 0)
|
||||
;
|
||||
else if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) == 0)
|
||||
;
|
||||
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0)
|
||||
{
|
||||
MQTTPacket* pack = MQTTPersistence_restorePacket(buffer, buflen);
|
||||
if ( pack != NULL )
|
||||
|
|
@ -190,6 +197,7 @@ int MQTTPersistence_restore(Clients *c)
|
|||
ListAppend(c->inboundMsgs, msg, msg->len);
|
||||
publish->topic = NULL;
|
||||
MQTTPacket_freePublish(publish);
|
||||
msgs_rcvd++;
|
||||
}
|
||||
else if ( strstr(msgkeys[i],PERSISTENCE_PUBLISH_SENT) != NULL )
|
||||
{
|
||||
|
|
@ -208,6 +216,7 @@ int MQTTPersistence_restore(Clients *c)
|
|||
publish->topic = NULL;
|
||||
MQTTPacket_freePublish(publish);
|
||||
free(key);
|
||||
msgs_sent++;
|
||||
}
|
||||
else if ( strstr(msgkeys[i],PERSISTENCE_PUBREL) != NULL )
|
||||
{
|
||||
|
|
@ -224,16 +233,20 @@ int MQTTPersistence_restore(Clients *c)
|
|||
else /* pack == NULL -> bad persisted record */
|
||||
rc = c->persistence->premove(c->phandle, msgkeys[i]);
|
||||
}
|
||||
if ( buffer != NULL )
|
||||
if (buffer)
|
||||
{
|
||||
free(buffer);
|
||||
if ( msgkeys[i] != NULL )
|
||||
buffer = NULL;
|
||||
}
|
||||
if (msgkeys[i])
|
||||
free(msgkeys[i]);
|
||||
i++;
|
||||
}
|
||||
if ( msgkeys != NULL )
|
||||
if (msgkeys)
|
||||
free(msgkeys);
|
||||
}
|
||||
|
||||
Log(TRACE_MINIMUM, -1, "%d sent messages and %d received messages restored for client %s\n",
|
||||
msgs_sent, msgs_rcvd, c->clientID);
|
||||
MQTTPersistence_wrapMsgID(c);
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Clients.h"
|
||||
|
|
@ -18,7 +19,11 @@
|
|||
#define PERSISTENCE_PUBREL "sc-"
|
||||
/** Stem of the key for a received PUBLISH QoS2 */
|
||||
#define PERSISTENCE_PUBLISH_RECEIVED "r-"
|
||||
|
||||
/** Stem of the key for an async client command */
|
||||
#define PERSISTENCE_COMMAND_KEY "c-"
|
||||
/** Stem of the key for an async client message queue */
|
||||
#define PERSISTENCE_QUEUE_KEY "q-"
|
||||
#define PERSISTENCE_MAX_KEY_LENGTH 8
|
||||
|
||||
int MQTTPersistence_create(MQTTClient_persistence** per, int type, void* pcontext);
|
||||
int MQTTPersistence_initialize(Clients* c, char* serverURI);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -395,7 +396,10 @@ int containskeyUnix(char *dirname, char *key)
|
|||
{
|
||||
while((dir_entry = readdir(dp)) != NULL && notFound)
|
||||
{
|
||||
lstat(dir_entry->d_name, &stat_info);
|
||||
char* filename = malloc(strlen(dirname) + strlen(dir_entry->d_name) + 2);
|
||||
sprintf(filename, "%s/%s", dirname, dir_entry->d_name);
|
||||
lstat(filename, &stat_info);
|
||||
free(filename);
|
||||
if(S_ISREG(stat_info.st_mode))
|
||||
{
|
||||
filekey = malloc(strlen(dir_entry->d_name) + 1);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROTOCOL_H)
|
||||
#define MQTTPROTOCOL_H
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -80,7 +81,7 @@ void MQTTProtocol_storeQoS0(Clients* pubclient, Publish* publish)
|
|||
pw = malloc(sizeof(pending_write));
|
||||
Log(TRACE_MIN, 12, NULL);
|
||||
pw->p = MQTTProtocol_storePublication(publish, &len);
|
||||
pw->socket = pubclient->socket;
|
||||
pw->socket = pubclient->net.socket;
|
||||
ListAppend(&(state.pending_writes), pw, sizeof(pending_write)+len);
|
||||
/* we don't copy QoS 0 messages unless we have to, so now we have to tell the socket buffer where
|
||||
the saved copy is */
|
||||
|
|
@ -103,7 +104,7 @@ int MQTTProtocol_startPublishCommon(Clients* pubclient, Publish* publish, int qo
|
|||
int rc = TCPSOCKET_COMPLETE;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_publish(publish, 0, qos, retained, pubclient->socket, pubclient->clientID);
|
||||
rc = MQTTPacket_send_publish(publish, 0, qos, retained, &pubclient->net, pubclient->clientID);
|
||||
if (qos == 0 && rc == TCPSOCKET_INTERRUPTED)
|
||||
MQTTProtocol_storeQoS0(pubclient, publish);
|
||||
FUNC_EXIT_RC(rc);
|
||||
|
|
@ -253,7 +254,7 @@ int MQTTProtocol_handlePublishes(void* pack, int sock)
|
|||
else if (publish->header.bits.qos == 1)
|
||||
{
|
||||
/* send puback before processing the publications because a lot of return publications could fill up the socket buffer */
|
||||
rc = MQTTPacket_send_puback(publish->msgId, sock, client->clientID);
|
||||
rc = MQTTPacket_send_puback(publish->msgId, &client->net, client->clientID);
|
||||
/* if we get a socket error from sending the puback, should we ignore the publication? */
|
||||
Protocol_processPublication(publish, client);
|
||||
}
|
||||
|
|
@ -277,7 +278,7 @@ int MQTTProtocol_handlePublishes(void* pack, int sock)
|
|||
ListRemove(client->inboundMsgs, msg);
|
||||
} else
|
||||
ListAppend(client->inboundMsgs, m, sizeof(Messages) + len);
|
||||
rc = MQTTPacket_send_pubrec(publish->msgId, sock, client->clientID);
|
||||
rc = MQTTPacket_send_pubrec(publish->msgId, &client->net, client->clientID);
|
||||
publish->topic = NULL;
|
||||
}
|
||||
MQTTPacket_freePublish(publish);
|
||||
|
|
@ -363,7 +364,7 @@ int MQTTProtocol_handlePubrecs(void* pack, int sock)
|
|||
}
|
||||
else
|
||||
{
|
||||
rc = MQTTPacket_send_pubrel(pubrec->msgId, 0, sock, client->clientID);
|
||||
rc = MQTTPacket_send_pubrel(pubrec->msgId, 0, &client->net, client->clientID);
|
||||
m->nextMessageType = PUBCOMP;
|
||||
time(&(m->lastTouch));
|
||||
}
|
||||
|
|
@ -397,7 +398,7 @@ int MQTTProtocol_handlePubrels(void* pack, int sock)
|
|||
Log(TRACE_MIN, 3, NULL, "PUBREL", client->clientID, pubrel->msgId);
|
||||
else
|
||||
/* Apparently this is "normal" behaviour, so we don't need to issue a warning */
|
||||
rc = MQTTPacket_send_pubcomp(pubrel->msgId, sock, client->clientID);
|
||||
rc = MQTTPacket_send_pubcomp(pubrel->msgId, &client->net, client->clientID);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -411,7 +412,7 @@ int MQTTProtocol_handlePubrels(void* pack, int sock)
|
|||
Publish publish;
|
||||
|
||||
/* send pubcomp before processing the publications because a lot of return publications could fill up the socket buffer */
|
||||
rc = MQTTPacket_send_pubcomp(pubrel->msgId, sock, client->clientID);
|
||||
rc = MQTTPacket_send_pubcomp(pubrel->msgId, &client->net, client->clientID);
|
||||
publish.header.bits.qos = m->qos;
|
||||
publish.header.bits.retain = m->retain;
|
||||
publish.msgId = m->msgid;
|
||||
|
|
@ -497,10 +498,10 @@ void MQTTProtocol_keepalive(time_t now)
|
|||
{
|
||||
Clients* client = (Clients*)(current->content);
|
||||
ListNextElement(bstate->clients, ¤t);
|
||||
if (client->connected && client->keepAliveInterval > 0
|
||||
if (client->connected && client->keepAliveInterval > 0 && Socket_noPendingWrites(client->net.socket)
|
||||
&& (difftime(now, client->lastContact) >= client->keepAliveInterval))
|
||||
{
|
||||
MQTTPacket_send_pingreq(client->socket, client->clientID);
|
||||
MQTTPacket_send_pingreq(&client->net, client->clientID);
|
||||
client->lastContact = now;
|
||||
client->ping_outstanding = 1;
|
||||
}
|
||||
|
|
@ -533,17 +534,17 @@ void MQTTProtocol_retries(time_t now, Clients* client)
|
|||
Publish publish;
|
||||
int rc;
|
||||
|
||||
Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->socket, m->msgid);
|
||||
Log(TRACE_MIN, 7, NULL, "PUBLISH", client->clientID, client->net.socket, m->msgid);
|
||||
publish.msgId = m->msgid;
|
||||
publish.topic = m->publish->topic;
|
||||
publish.payload = m->publish->payload;
|
||||
publish.payloadlen = m->publish->payloadlen;
|
||||
rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, client->socket, client->clientID);
|
||||
rc = MQTTPacket_send_publish(&publish, 1, m->qos, m->retain, &client->net, client->clientID);
|
||||
if (rc == SOCKET_ERROR)
|
||||
{
|
||||
client->good = 0;
|
||||
Log(TRACE_MIN, 8, NULL, client->clientID, client->socket,
|
||||
Socket_getpeer(client->socket));
|
||||
Log(TRACE_MIN, 8, NULL, client->clientID, client->net.socket,
|
||||
Socket_getpeer(client->net.socket));
|
||||
MQTTProtocol_closeSession(client, 1);
|
||||
client = NULL;
|
||||
}
|
||||
|
|
@ -556,12 +557,12 @@ void MQTTProtocol_retries(time_t now, Clients* client)
|
|||
}
|
||||
else if (m->qos && m->nextMessageType == PUBCOMP)
|
||||
{
|
||||
Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->socket, m->msgid);
|
||||
if (MQTTPacket_send_pubrel(m->msgid, 1, client->socket, client->clientID) != TCPSOCKET_COMPLETE)
|
||||
Log(TRACE_MIN, 7, NULL, "PUBREL", client->clientID, client->net.socket, m->msgid);
|
||||
if (MQTTPacket_send_pubrel(m->msgid, 1, &client->net, client->clientID) != TCPSOCKET_COMPLETE)
|
||||
{
|
||||
client->good = 0;
|
||||
Log(TRACE_MIN, 8, NULL, client->clientID, client->socket,
|
||||
Socket_getpeer(client->socket));
|
||||
Log(TRACE_MIN, 8, NULL, client->clientID, client->net.socket,
|
||||
Socket_getpeer(client->net.socket));
|
||||
MQTTProtocol_closeSession(client, 1);
|
||||
client = NULL;
|
||||
}
|
||||
|
|
@ -599,7 +600,7 @@ void MQTTProtocol_retry(time_t now, int doRetry)
|
|||
MQTTProtocol_closeSession(client, 1);
|
||||
continue;
|
||||
}
|
||||
if (Socket_noPendingWrites(client->socket) == 0)
|
||||
if (Socket_noPendingWrites(client->net.socket) == 0)
|
||||
continue;
|
||||
if (doRetry)
|
||||
MQTTProtocol_retries(now, client);
|
||||
|
|
@ -620,13 +621,16 @@ void MQTTProtocol_freeClient(Clients* client)
|
|||
MQTTProtocol_freeMessageList(client->inboundMsgs);
|
||||
ListFree(client->messageQueue);
|
||||
free(client->clientID);
|
||||
/*if (client->will != NULL)
|
||||
if (client->will)
|
||||
{
|
||||
willMessages* w = client->will;
|
||||
free(w->msg);
|
||||
free(w->topic);
|
||||
free(client->will->msg);
|
||||
free(client->will->topic);
|
||||
free(client->will);
|
||||
}*/
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
if (client->sslopts)
|
||||
free(client->sslopts);
|
||||
#endif
|
||||
/* don't free the client structure itself... this is done elsewhere */
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROTOCOLCLIENT_H)
|
||||
|
|
@ -28,6 +29,7 @@ Messages* MQTTProtocol_createMessage(Publish* publish, Messages** mm, int qos, i
|
|||
Publications* MQTTProtocol_storePublication(Publish* publish, int* len);
|
||||
int messageIDCompare(void* a, void* b);
|
||||
int MQTTProtocol_assignMsgId(Clients* client);
|
||||
void MQTTProtocol_removePublication(Publications* p);
|
||||
|
||||
int MQTTProtocol_handlePublishes(void* pack, int sock);
|
||||
int MQTTProtocol_handlePubacks(void* pack, int sock);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 201 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -80,7 +81,11 @@ char* MQTTProtocol_addressPort(char* ip_address, int* port)
|
|||
* @param password MQTT 3.1 password, or NULL
|
||||
* @return the new client structure
|
||||
*/
|
||||
#if defined(OPENSSL)
|
||||
int MQTTProtocol_connect(char* ip_address, Clients* aClient, int ssl)
|
||||
#else
|
||||
int MQTTProtocol_connect(char* ip_address, Clients* aClient)
|
||||
#endif
|
||||
{
|
||||
int rc, port;
|
||||
char* addr;
|
||||
|
|
@ -90,15 +95,33 @@ int MQTTProtocol_connect(char* ip_address, Clients* aClient)
|
|||
time(&(aClient->lastContact));
|
||||
|
||||
addr = MQTTProtocol_addressPort(ip_address, &port);
|
||||
rc = Socket_new(addr, port, &(aClient->socket));
|
||||
rc = Socket_new(addr, port, &(aClient->net.socket));
|
||||
if (rc == EINPROGRESS || rc == EWOULDBLOCK)
|
||||
aClient->connect_state = 1; /* TCP connect called */
|
||||
aClient->connect_state = 1; /* TCP connect called - wait for connect completion */
|
||||
else if (rc == 0)
|
||||
{
|
||||
if ((rc = MQTTPacket_send_connect(aClient)) == 0)
|
||||
aClient->connect_state = 2; /* TCP connect completed, in which case send the MQTT connect packet */
|
||||
else
|
||||
aClient->connect_state = 0;
|
||||
{ /* TCP connect completed. If SSL, send SSL connect */
|
||||
#if defined(OPENSSL)
|
||||
if (ssl)
|
||||
{
|
||||
if((aClient->net.ssl = SSLSocket_setSocketForSSL(aClient->net.socket, aClient->sslopts)) != NULL)
|
||||
{
|
||||
rc = SSLSocket_connect(aClient->net.ssl, aClient->net.socket);
|
||||
if (rc == -1)
|
||||
aClient->connect_state = 2; /* SSL connect called - wait for completion */
|
||||
}
|
||||
else
|
||||
rc = SOCKET_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
/* Now send the MQTT connect packet */
|
||||
if ((rc = MQTTPacket_send_connect(aClient)) == 0)
|
||||
aClient->connect_state = 3; /* MQTT Connect sent - wait for CONNACK */
|
||||
else
|
||||
aClient->connect_state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
|
|
@ -139,7 +162,7 @@ int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss)
|
|||
|
||||
FUNC_ENTRY;
|
||||
/* we should stack this up for retry processing too */
|
||||
rc = MQTTPacket_send_subscribe(topics, qoss, MQTTProtocol_assignMsgId(client), 0, client->socket, client->clientID);
|
||||
rc = MQTTPacket_send_subscribe(topics, qoss, MQTTProtocol_assignMsgId(client), 0, &client->net, client->clientID);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -178,7 +201,7 @@ int MQTTProtocol_unsubscribe(Clients* client, List* topics)
|
|||
|
||||
FUNC_ENTRY;
|
||||
/* we should stack this up for retry processing too? */
|
||||
rc = MQTTPacket_send_unsubscribe(topics, MQTTProtocol_assignMsgId(client), 0, client->socket, client->clientID);
|
||||
rc = MQTTPacket_send_unsubscribe(topics, MQTTProtocol_assignMsgId(client), 0, &client->net, client->clientID);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROTOCOLOUT_H)
|
||||
|
|
@ -24,7 +25,11 @@
|
|||
#define DEFAULT_PORT 1883
|
||||
|
||||
void MQTTProtocol_reconnect(char* ip_address, Clients* client);
|
||||
#if defined(OPENSSL)
|
||||
int MQTTProtocol_connect(char* ip_address, Clients* acClients, int ssl);
|
||||
#else
|
||||
int MQTTProtocol_connect(char* ip_address, Clients* acClients);
|
||||
#endif
|
||||
int MQTTProtocol_handlePingresps(void* pack, int sock);
|
||||
int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss);
|
||||
int MQTTProtocol_handleSubacks(void* pack, int sock);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2012 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <memory.h>
|
||||
#include <ctype.h>
|
||||
#include "MQTTAsync.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <io.h>
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @file
|
||||
* \brief MQTTVersion - display the version and build information strings for a library.
|
||||
*
|
||||
* With no arguments, we try to load and call the version string for the libraries we
|
||||
* know about: mqttv3c, mqttv3cs, mqttv3a, mqttv3as.
|
||||
* With an argument:
|
||||
* 1) we try to load the named library, call getVersionInfo and display those values.
|
||||
* 2) If that doesn't work, we look through the binary for eyecatchers, and display those.
|
||||
* This will work if the library is not executable in the current environment.
|
||||
*
|
||||
* */
|
||||
|
||||
|
||||
char* libraries[] = {"mqttv3c", "mqttv3cs", "mqttv3a", "mqttv3as"};
|
||||
char* eyecatchers[] = {"MQTTAsyncV3_Version", "MQTTAsyncV3_Timestamp",
|
||||
"MQTTClientV3_Version", "MQTTClientV3_Timestamp"};
|
||||
|
||||
|
||||
/**
|
||||
* Finds an eyecatcher in a binary file and returns the following value.
|
||||
* @param filename the name of the file
|
||||
* @param eyecatcher_input the eyecatcher string to look for
|
||||
* @return the value found - "" if not found
|
||||
*/
|
||||
char* FindString(char* filename, char* eyecatcher_input)
|
||||
{
|
||||
FILE* infile = NULL;
|
||||
static char value[100];
|
||||
char* eyecatcher = eyecatcher_input;
|
||||
|
||||
memset(value, 0, 100);
|
||||
if ((infile = fopen(filename, "rb")) != NULL)
|
||||
{
|
||||
int buflen = strlen(eyecatcher);
|
||||
char* buffer = (char*) malloc(buflen);
|
||||
int count = 0;
|
||||
int c = fgetc(infile);
|
||||
|
||||
while (feof(infile) == 0)
|
||||
{
|
||||
buffer[count++] = c;
|
||||
if (memcmp(eyecatcher, buffer, buflen) == 0)
|
||||
{
|
||||
char* ptr = value;
|
||||
c = fgetc(infile); /* skip space */
|
||||
c = fgetc(infile);
|
||||
while (isprint(c))
|
||||
{
|
||||
*ptr++ = c;
|
||||
c = fgetc(infile);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (count == buflen)
|
||||
{
|
||||
memmove(buffer, &buffer[1], buflen - 1);
|
||||
count--;
|
||||
}
|
||||
c = fgetc(infile);
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
int printVersionInfo(MQTTAsync_nameValue* info)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (info->name)
|
||||
{
|
||||
printf("%s: %s\n", info->name, info->value);
|
||||
info++;
|
||||
rc = 1; /* at least one value printed */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef MQTTAsync_nameValue* (*func_type)(void);
|
||||
|
||||
int loadandcall(char* libname)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTAsync_nameValue* (*func_address)(void) = NULL;
|
||||
#if defined(WIN32)
|
||||
HMODULE APILibrary = LoadLibrary(libname);
|
||||
|
||||
if (APILibrary == NULL)
|
||||
printf("Error loading library %s, error code %d\n", libname, GetLastError());
|
||||
else
|
||||
{
|
||||
func_address = (func_type)GetProcAddress(APILibrary, "MQTTAsync_getVersionInfo");
|
||||
if (func_address == NULL)
|
||||
func_address = (func_type)GetProcAddress(APILibrary, "MQTTClient_getVersionInfo");
|
||||
if (func_address)
|
||||
rc = printVersionInfo((*func_address)());
|
||||
FreeLibrary(APILibrary);
|
||||
}
|
||||
#else
|
||||
void* APILibrary = dlopen(libname, RTLD_LAZY); /* Open the Library in question */
|
||||
char* ErrorOutput = dlerror(); /* Check it opened properly */
|
||||
if (ErrorOutput != NULL)
|
||||
printf("Error loading library %s, error %s\n", libname, ErrorOutput);
|
||||
else
|
||||
{
|
||||
*(void **) (&func_address) = dlsym(APILibrary, "MQTTAsync_getVersionInfo");
|
||||
if (func_address == NULL)
|
||||
func_address = dlsym(APILibrary, "MQTTClient_getVersionInfo");
|
||||
if (func_address)
|
||||
rc = printVersionInfo((*func_address)());
|
||||
dlclose(APILibrary);
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(ARRAY_SIZE)
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
void printEyecatchers(char* filename)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eyecatchers); ++i)
|
||||
{
|
||||
char* value = FindString(filename, eyecatchers[i]);
|
||||
if (value[0])
|
||||
printf("%s: %s\n", eyecatchers[i], value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("MQTTVersion: print the version strings of an MQTT client library\n");
|
||||
printf("Copyright (c) 2012 IBM Corp.\n");
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
int i = 0;
|
||||
char namebuf[60];
|
||||
|
||||
printf("Specify a particular library name if it is not in the current directory, or not executable on this platform\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(libraries); ++i)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
sprintf(namebuf, "%s.dll", libraries[i]);
|
||||
#else
|
||||
sprintf(namebuf, "lib%s.so", libraries[i]);
|
||||
#endif
|
||||
printf("--- Trying library %s ---\n", libraries[i]);
|
||||
if (!loadandcall(namebuf))
|
||||
printEyecatchers(namebuf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!loadandcall(argv[1]))
|
||||
printEyecatchers(argv[1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,682 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs, Allan Stockdill-Mander - initial implementation
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief SSL related functions
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(OPENSSL)
|
||||
|
||||
#include "SocketBuffer.h"
|
||||
#include "MQTTClient.h"
|
||||
#include "SSLSocket.h"
|
||||
#include "Log.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Socket.h"
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
extern Sockets s;
|
||||
|
||||
void SSLSocket_addPendingRead(int sock);
|
||||
|
||||
static ssl_mutex_type* sslLocks = NULL;
|
||||
static ssl_mutex_type sslCoreMutex;
|
||||
|
||||
#if defined(WIN32)
|
||||
#define iov_len len
|
||||
#define iov_base buf
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the specific error corresponding to SOCKET_ERROR
|
||||
* @param aString the function that was being used when the error occurred
|
||||
* @param sock the socket on which the error occurred
|
||||
* @return the specific TCP error code
|
||||
*/
|
||||
int SSLSocket_error(char* aString, SSL* ssl, int sock, int rc)
|
||||
{
|
||||
int error;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (ssl)
|
||||
error = SSL_get_error(ssl, rc);
|
||||
else
|
||||
error = ERR_get_error();
|
||||
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "SSLSocket error WANT_READ/WANT_WRITE");
|
||||
}
|
||||
else
|
||||
{
|
||||
static char buf[120];
|
||||
|
||||
if (strcmp(aString, "shutdown") != 0)
|
||||
Log(TRACE_MIN, -1, "SSLSocket error %s(%d) in %s for socket %d rc %d errno %d %s\n", buf, error, aString, sock, rc, errno, strerror(errno));
|
||||
//ERR_print_errors_fp(stderr);
|
||||
if (error == SSL_ERROR_SSL || error == SSL_ERROR_SYSCALL)
|
||||
error = SSL_FATAL;
|
||||
}
|
||||
FUNC_EXIT_RC(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
int code;
|
||||
char* string;
|
||||
}
|
||||
X509_message_table[] =
|
||||
{
|
||||
{ X509_V_OK, "X509_V_OK" },
|
||||
{ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT" },
|
||||
{ X509_V_ERR_UNABLE_TO_GET_CRL, "X509_V_ERR_UNABLE_TO_GET_CRL" },
|
||||
{ X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE, "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE" },
|
||||
{ X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE, "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE" },
|
||||
{ X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY" },
|
||||
{ X509_V_ERR_CERT_SIGNATURE_FAILURE, "X509_V_ERR_CERT_SIGNATURE_FAILURE" },
|
||||
{ X509_V_ERR_CRL_SIGNATURE_FAILURE, "X509_V_ERR_CRL_SIGNATURE_FAILURE" },
|
||||
{ X509_V_ERR_CERT_NOT_YET_VALID, "X509_V_ERR_CERT_NOT_YET_VALID" },
|
||||
{ X509_V_ERR_CERT_HAS_EXPIRED, "X509_V_ERR_CERT_HAS_EXPIRED" },
|
||||
{ X509_V_ERR_CRL_NOT_YET_VALID, "X509_V_ERR_CRL_NOT_YET_VALID" },
|
||||
{ X509_V_ERR_CRL_HAS_EXPIRED, "X509_V_ERR_CRL_HAS_EXPIRED" },
|
||||
{ X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD, "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD" },
|
||||
{ X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD, "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD" },
|
||||
{ X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD, "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD" },
|
||||
{ X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD, "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD" },
|
||||
{ X509_V_ERR_OUT_OF_MEM, "X509_V_ERR_OUT_OF_MEM" },
|
||||
{ X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT" },
|
||||
{ X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN" },
|
||||
{ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY" },
|
||||
{ X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE" },
|
||||
{ X509_V_ERR_CERT_CHAIN_TOO_LONG, "X509_V_ERR_CERT_CHAIN_TOO_LONG" },
|
||||
{ X509_V_ERR_CERT_REVOKED, "X509_V_ERR_CERT_REVOKED" },
|
||||
{ X509_V_ERR_INVALID_CA, "X509_V_ERR_INVALID_CA" },
|
||||
{ X509_V_ERR_PATH_LENGTH_EXCEEDED, "X509_V_ERR_PATH_LENGTH_EXCEEDED" },
|
||||
{ X509_V_ERR_INVALID_PURPOSE, "X509_V_ERR_INVALID_PURPOSE" },
|
||||
{ X509_V_ERR_CERT_UNTRUSTED, "X509_V_ERR_CERT_UNTRUSTED" },
|
||||
{ X509_V_ERR_CERT_REJECTED, "X509_V_ERR_CERT_REJECTED" },
|
||||
{ X509_V_ERR_SUBJECT_ISSUER_MISMATCH, "X509_V_ERR_SUBJECT_ISSUER_MISMATCH" },
|
||||
{ X509_V_ERR_AKID_SKID_MISMATCH, "X509_V_ERR_AKID_SKID_MISMATCH" },
|
||||
{ X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH, "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH" },
|
||||
{ X509_V_ERR_KEYUSAGE_NO_CERTSIGN, "X509_V_ERR_KEYUSAGE_NO_CERTSIGN" },
|
||||
{ X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER" },
|
||||
{ X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION" },
|
||||
{ X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN" },
|
||||
{ X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION" },
|
||||
{ X509_V_ERR_INVALID_NON_CA, "X509_V_ERR_INVALID_NON_CA" },
|
||||
{ X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED" },
|
||||
{ X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE" },
|
||||
{ X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED" },
|
||||
{ X509_V_ERR_INVALID_EXTENSION, "X509_V_ERR_INVALID_EXTENSION" },
|
||||
{ X509_V_ERR_INVALID_POLICY_EXTENSION, "X509_V_ERR_INVALID_POLICY_EXTENSION" },
|
||||
{ X509_V_ERR_NO_EXPLICIT_POLICY, "X509_V_ERR_NO_EXPLICIT_POLICY" },
|
||||
{ X509_V_ERR_DIFFERENT_CRL_SCOPE, "X509_V_ERR_DIFFERENT_CRL_SCOPE" },
|
||||
{ X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE" },
|
||||
{ X509_V_ERR_UNNESTED_RESOURCE, "X509_V_ERR_UNNESTED_RESOURCE" },
|
||||
{ X509_V_ERR_PERMITTED_VIOLATION, "X509_V_ERR_PERMITTED_VIOLATION" },
|
||||
{ X509_V_ERR_EXCLUDED_VIOLATION, "X509_V_ERR_EXCLUDED_VIOLATION" },
|
||||
{ X509_V_ERR_SUBTREE_MINMAX, "X509_V_ERR_SUBTREE_MINMAX" },
|
||||
{ X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE" },
|
||||
{ X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX" },
|
||||
{ X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX" },
|
||||
};
|
||||
|
||||
#if !defined(ARRAY_SIZE)
|
||||
/**
|
||||
* Macro to calculate the number of entries in an array
|
||||
*/
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
char* SSL_get_verify_result_string(int rc)
|
||||
{
|
||||
int i;
|
||||
char* retstring = "undef";
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(X509_message_table); ++i)
|
||||
{
|
||||
if (X509_message_table[i].code == rc)
|
||||
{
|
||||
retstring = X509_message_table[i].string;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return retstring;
|
||||
}
|
||||
|
||||
void SSL_CTX_info_callback(const SSL* ssl, int where, int ret)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
|
||||
if (where & SSL_CB_LOOP)
|
||||
{
|
||||
Log(TRACE_PROTOCOL, 1, "SSL state %s:%s:%s",
|
||||
(where & SSL_ST_CONNECT) ? "connect" : (where & SSL_ST_ACCEPT) ? "accept" : "undef",
|
||||
SSL_state_string_long(ssl), SSL_get_cipher_name(ssl));
|
||||
}
|
||||
else if (where & SSL_CB_EXIT)
|
||||
{
|
||||
Log(TRACE_PROTOCOL, 1, "SSL %s:%s",
|
||||
(where & SSL_ST_CONNECT) ? "connect" : (where & SSL_ST_ACCEPT) ? "accept" : "undef",
|
||||
SSL_state_string_long(ssl));
|
||||
}
|
||||
else if (where & SSL_CB_ALERT)
|
||||
{
|
||||
Log(TRACE_PROTOCOL, 1, "SSL alert %s:%s:%s",
|
||||
(where & SSL_CB_READ) ? "read" : "write",
|
||||
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
|
||||
}
|
||||
else if (where & SSL_CB_HANDSHAKE_START)
|
||||
{
|
||||
Log(TRACE_PROTOCOL, 1, "SSL handshake started %s:%s:%s",
|
||||
(where & SSL_CB_READ) ? "read" : "write",
|
||||
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
|
||||
}
|
||||
else if (where & SSL_CB_HANDSHAKE_DONE)
|
||||
{
|
||||
Log(TRACE_PROTOCOL, 1, "SSL handshake done %s:%s:%s",
|
||||
(where & SSL_CB_READ) ? "read" : "write",
|
||||
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
|
||||
Log(TRACE_PROTOCOL, 1, "SSL certificate verification: %s",
|
||||
SSL_get_verify_result_string(SSL_get_verify_result(ssl)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(TRACE_PROTOCOL, 1, "SSL state %s:%s:%s", SSL_state_string_long(ssl),
|
||||
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
int pem_passwd_cb(char* buf, int size, int rwflag, void* userdata)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
|
||||
if (!rwflag)
|
||||
{
|
||||
strncpy(buf, (char*)(userdata), size);
|
||||
buf[size-1] = '\0';
|
||||
}
|
||||
FUNC_EXIT;
|
||||
if (!rwflag) return strlen(buf);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int SSL_create_mutex(ssl_mutex_type* mutex)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if defined(WIN32)
|
||||
mutex = CreateMutex(NULL, 0, NULL);
|
||||
#else
|
||||
rc = pthread_mutex_init(mutex, NULL);
|
||||
#endif
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int SSL_lock_mutex(ssl_mutex_type* mutex)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
|
||||
#if defined(WIN32)
|
||||
if (WaitForSingleObject(mutex, INFINITE) != WAIT_FAILED)
|
||||
#else
|
||||
if ((rc = pthread_mutex_lock(mutex)) == 0)
|
||||
#endif
|
||||
rc = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int SSL_unlock_mutex(ssl_mutex_type* mutex)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
/* don't add entry/exit trace points as the stack log uses mutexes - recursion beckons */
|
||||
#if defined(WIN32)
|
||||
if (ReleaseMutex(mutex) != 0)
|
||||
#else
|
||||
if ((rc = pthread_mutex_unlock(mutex)) == 0)
|
||||
#endif
|
||||
rc = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void SSL_destroy_mutex(ssl_mutex_type* mutex)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if defined(WIN32)
|
||||
rc = CloseHandle(mutex);
|
||||
#else
|
||||
rc = pthread_mutex_destroy(mutex);
|
||||
free(mutex);
|
||||
#endif
|
||||
FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
extern void SSLThread_id(CRYPTO_THREADID *id)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
CRYPTO_THREADID_set_numeric(id, (unsigned long)GetCurrentThreadId());
|
||||
#else
|
||||
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
|
||||
#endif
|
||||
}
|
||||
|
||||
extern void SSLLocks_callback(int mode, int n, const char *file, int line)
|
||||
{
|
||||
if (mode & CRYPTO_LOCK)
|
||||
SSL_lock_mutex(&sslLocks[n]);
|
||||
else
|
||||
SSL_unlock_mutex(&sslLocks[n]);
|
||||
}
|
||||
|
||||
int SSLSocket_initialize()
|
||||
{
|
||||
int rc = 0;
|
||||
/*int prc;*/
|
||||
int i;
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
if ((rc = SSL_library_init()) != 1)
|
||||
rc = -1;
|
||||
|
||||
ERR_load_crypto_strings();
|
||||
SSL_load_error_strings();
|
||||
|
||||
/* OpenSSL 0.9.8o and 1.0.0a and later added SHA2 algorithms to SSL_library_init().
|
||||
Applications which need to use SHA2 in earlier versions of OpenSSL should call
|
||||
OpenSSL_add_all_algorithms() as well. */
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
sslLocks = calloc(CRYPTO_num_locks(), sizeof(ssl_mutex_type));
|
||||
if (!sslLocks)
|
||||
{
|
||||
rc = -1;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < CRYPTO_num_locks(); i++)
|
||||
{
|
||||
/* prc = */SSL_create_mutex(&sslLocks[i]);
|
||||
}
|
||||
|
||||
CRYPTO_THREADID_set_callback(SSLThread_id);
|
||||
CRYPTO_set_locking_callback(SSLLocks_callback);
|
||||
|
||||
SSL_create_mutex(&sslCoreMutex);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
SSL_CTX* SSLSocket_createContext(int socket, MQTTClient_SSLOptions* opts)
|
||||
{
|
||||
int rc = 1;
|
||||
SSL_CTX* ctx = NULL;
|
||||
char* ciphers = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) /* SSLv23 for compatibility with SSLv2, SSLv3 and TLSv1 */
|
||||
{
|
||||
SSLSocket_error("SSL_CTX_new", NULL, socket, rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (opts->keyStore)
|
||||
{
|
||||
if ((rc = SSL_CTX_use_certificate_chain_file(ctx, opts->keyStore)) != 1)
|
||||
{
|
||||
SSLSocket_error("SSL_CTX_use_certificate_chain_file", NULL, socket, rc);
|
||||
goto free_ctx; /*If we can't load the certificate (chain) file then loading the privatekey won't work either as it needs a matching cert already loaded */
|
||||
}
|
||||
|
||||
if (opts->privateKey == NULL)
|
||||
opts->privateKey = opts->keyStore; /* the privateKey can be included in the keyStore */
|
||||
|
||||
if (opts->privateKeyPassword != NULL)
|
||||
{
|
||||
SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
|
||||
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)opts->privateKeyPassword);
|
||||
}
|
||||
|
||||
/* support for ASN.1 == DER format? DER can contain only one certificate? */
|
||||
if ((rc = SSL_CTX_use_PrivateKey_file(ctx, opts->privateKey, SSL_FILETYPE_PEM)) != 1)
|
||||
{
|
||||
SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, socket, rc);
|
||||
goto free_ctx;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->trustStore)
|
||||
{
|
||||
if ((rc = SSL_CTX_load_verify_locations(ctx, opts->trustStore, NULL)) != 1)
|
||||
{
|
||||
SSLSocket_error("SSL_CTX_load_verify_locations", NULL, socket, rc);
|
||||
goto free_ctx;
|
||||
}
|
||||
}
|
||||
else if ((rc = SSL_CTX_set_default_verify_paths(ctx)) != 1)
|
||||
{
|
||||
SSLSocket_error("SSL_CTX_set_default_verify_paths", NULL, socket, rc);
|
||||
goto free_ctx;
|
||||
}
|
||||
|
||||
if (opts->enabledCipherSuites == NULL)
|
||||
ciphers = "DEFAULT";
|
||||
else
|
||||
ciphers = opts->enabledCipherSuites;
|
||||
|
||||
if ((rc = SSL_CTX_set_cipher_list(ctx, ciphers)) != 1)
|
||||
{
|
||||
SSLSocket_error("SSL_CTX_set_cipher_list", NULL, socket, rc);
|
||||
goto free_ctx;
|
||||
}
|
||||
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
|
||||
goto exit;
|
||||
free_ctx:
|
||||
SSL_CTX_free(ctx);
|
||||
ctx = NULL;
|
||||
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
SSL* SSLSocket_setSocketForSSL(int socket, MQTTClient_SSLOptions* opts)
|
||||
{
|
||||
int rc = 1;
|
||||
SSL_CTX* ctx = NULL;
|
||||
SSL* ssl = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
if ((ctx = SSLSocket_createContext(socket, opts)) != NULL)
|
||||
{
|
||||
int i;
|
||||
SSL_CTX_set_info_callback(ctx, SSL_CTX_info_callback);
|
||||
if (opts->enableServerCertAuth)
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
|
||||
|
||||
ssl = SSL_new(ctx);
|
||||
|
||||
/* Log all ciphers available to the SSL sessions (loaded in ctx) */
|
||||
for (i = 0; ;i++)
|
||||
{
|
||||
const char* cipher = SSL_get_cipher_list(ssl, i);
|
||||
if (cipher == NULL) break;
|
||||
Log(TRACE_MIN, 1, "SSL cipher available: %d:%s", i, cipher);
|
||||
}
|
||||
|
||||
if ((rc = SSL_set_fd(ssl, socket)) != 1)
|
||||
SSLSocket_error("SSL_set_fd", ssl, socket, rc);
|
||||
}
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return ssl;
|
||||
}
|
||||
|
||||
|
||||
int SSLSocket_connect(SSL* ssl, int sock)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
||||
rc = SSL_connect(ssl);
|
||||
if (rc != 1)
|
||||
{
|
||||
int error;
|
||||
error = SSLSocket_error("SSL_connect", ssl, sock, rc);
|
||||
if (error == SSL_FATAL)
|
||||
rc = error;
|
||||
}
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reads one byte from a socket
|
||||
* @param socket the socket to read from
|
||||
* @param c the character read, returned
|
||||
* @return completion code
|
||||
*/
|
||||
int SSLSocket_getch(SSL* ssl, int socket, char* c)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((rc = SocketBuffer_getQueuedChar(socket, c)) != SOCKETBUFFER_INTERRUPTED)
|
||||
goto exit;
|
||||
|
||||
if ((rc = SSL_read(ssl, c, (size_t)1)) < 0)
|
||||
{
|
||||
int err = SSLSocket_error("SSL_read - getch", ssl, socket, rc);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
rc = TCPSOCKET_INTERRUPTED;
|
||||
SocketBuffer_interrupted(socket, 0);
|
||||
}
|
||||
}
|
||||
else if (rc == 0)
|
||||
rc = SOCKET_ERROR; /* The return value from recv is 0 when the peer has performed an orderly shutdown. */
|
||||
else if (rc == 1)
|
||||
{
|
||||
SocketBuffer_queueChar(socket, *c);
|
||||
rc = TCPSOCKET_COMPLETE;
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to read a number of bytes from a socket, non-blocking. If a previous read did not
|
||||
* finish, then retrieve that data.
|
||||
* @param socket the socket to read from
|
||||
* @param bytes the number of bytes to read
|
||||
* @param actual_len the actual number of bytes read
|
||||
* @return completion code
|
||||
*/
|
||||
char *SSLSocket_getdata(SSL* ssl, int socket, int bytes, int* actual_len)
|
||||
{
|
||||
int rc;
|
||||
char* buf;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (bytes == 0)
|
||||
{
|
||||
buf = SocketBuffer_complete(socket);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
buf = SocketBuffer_getQueuedData(socket, bytes, actual_len);
|
||||
|
||||
if ((rc = SSL_read(ssl, buf + (*actual_len), (size_t)(bytes - (*actual_len)))) < 0)
|
||||
{
|
||||
rc = SSLSocket_error("SSL_read - getdata", ssl, socket, rc);
|
||||
if (rc != SSL_ERROR_WANT_READ && rc != SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
buf = NULL;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else if (rc == 0) /* rc 0 means the other end closed the socket */
|
||||
{
|
||||
buf = NULL;
|
||||
goto exit;
|
||||
}
|
||||
else
|
||||
*actual_len += rc;
|
||||
|
||||
if (*actual_len == bytes)
|
||||
{
|
||||
SocketBuffer_complete(socket);
|
||||
/* if we read the whole packet, there might still be data waiting in the SSL buffer, which
|
||||
isn't picked up by select. So here we should check for any data remaining in the SSL buffer, and
|
||||
if so, add this socket to a new "pending SSL reads" list.
|
||||
*/
|
||||
if (SSL_pending(ssl) > 0) /* return no of bytes pending */
|
||||
SSLSocket_addPendingRead(socket);
|
||||
}
|
||||
else /* we didn't read the whole packet */
|
||||
{
|
||||
SocketBuffer_interrupted(socket, *actual_len);
|
||||
Log(TRACE_MAX, -1, "SSL_read: %d bytes expected but %d bytes now received", bytes, *actual_len);
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
int SSLSocket_close(SSL* ssl)
|
||||
{
|
||||
return SSL_shutdown(ssl);
|
||||
}
|
||||
|
||||
|
||||
/* No SSL_writev() provided by OpenSSL. Boo. */
|
||||
int SSLSocket_putdatas(SSL* ssl, int socket, char* buf0, int buf0len, int count, char** buffers, int* buflens)
|
||||
{
|
||||
int rc = 0;
|
||||
int i;
|
||||
char *ptr;
|
||||
iobuf iovec;
|
||||
int sslerror;
|
||||
|
||||
FUNC_ENTRY;
|
||||
iovec.iov_len = buf0len;
|
||||
for (i = 0; i < count; i++)
|
||||
iovec.iov_len += buflens[i];
|
||||
|
||||
ptr = iovec.iov_base = (char *)malloc(iovec.iov_len);
|
||||
memcpy(ptr, buf0, buf0len);
|
||||
ptr += buf0len;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
memcpy(ptr, buffers[i], buflens[i]);
|
||||
ptr += buflens[i];
|
||||
}
|
||||
|
||||
SSL_lock_mutex(&sslCoreMutex);
|
||||
if ((rc = SSL_write(ssl, iovec.iov_base, iovec.iov_len)) == iovec.iov_len)
|
||||
rc = TCPSOCKET_COMPLETE;
|
||||
else
|
||||
{
|
||||
sslerror = SSLSocket_error("SSL_write", ssl, socket, rc);
|
||||
|
||||
if (sslerror == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
int* sockmem = (int*)malloc(sizeof(int));
|
||||
Log(TRACE_MIN, -1, "Partial write: incomplete write of %d bytes on SSL socket %d",
|
||||
iovec.iov_len, socket);
|
||||
SocketBuffer_pendingWrite(socket, ssl, 1, &iovec, iovec.iov_len, 0);
|
||||
*sockmem = socket;
|
||||
ListAppend(s.write_pending, sockmem, sizeof(int));
|
||||
FD_SET(socket, &(s.pending_wset));
|
||||
rc = TCPSOCKET_INTERRUPTED;
|
||||
iovec.iov_base = NULL; /* don't free it because it hasn't been completely written yet */
|
||||
}
|
||||
else
|
||||
rc = SOCKET_ERROR;
|
||||
}
|
||||
SSL_unlock_mutex(&sslCoreMutex);
|
||||
|
||||
if (iovec.iov_base)
|
||||
free(iovec.iov_base);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static List pending_reads = {NULL, NULL, NULL, 0, 0};
|
||||
|
||||
void SSLSocket_addPendingRead(int sock)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (ListFindItem(&pending_reads, &sock, intcompare) == NULL) /* make sure we don't add the same socket twice */
|
||||
{
|
||||
int* psock = (int*)malloc(sizeof(sock));
|
||||
*psock = sock;
|
||||
ListAppend(&pending_reads, psock, sizeof(sock));
|
||||
}
|
||||
else
|
||||
Log(TRACE_MIN, -1, "SSLSocket_addPendingRead: socket %d already in the list", sock);
|
||||
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
int SSLSocket_getPendingRead()
|
||||
{
|
||||
int sock = -1;
|
||||
|
||||
if (pending_reads.count > 0)
|
||||
{
|
||||
sock = *(int*)(pending_reads.first->content);
|
||||
ListRemoveHead(&pending_reads);
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
int SSLSocket_continueWrite(pending_writes* pw)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((rc = SSL_write(pw->ssl, pw->iovecs[0].iov_base, pw->iovecs[0].iov_len)) == pw->iovecs[0].iov_len)
|
||||
{
|
||||
/* topic and payload buffers are freed elsewhere, when all references to them have been removed */
|
||||
free(pw->iovecs[0].iov_base);
|
||||
if (pw->count > 1)
|
||||
{
|
||||
free(pw->iovecs[1].iov_base);
|
||||
if (pw->count == 5)
|
||||
free(pw->iovecs[3].iov_base);
|
||||
}
|
||||
Log(TRACE_MIN, -1, "SSL continueWrite: partial write now complete for socket %d", pw->socket);
|
||||
rc = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sslerror = SSLSocket_error("SSL_write", pw->ssl, pw->socket, rc);
|
||||
if (sslerror == SSL_ERROR_WANT_WRITE)
|
||||
rc = 0; /* indicate we haven't finished writing the payload yet */
|
||||
}
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs, Allan Stockdill-Mander - initial implementation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTClient.h"
|
||||
|
||||
#if !defined(SSLSOCKET_H)
|
||||
#define SSLSOCKET_H
|
||||
|
||||
#if defined(WIN32)
|
||||
#define ssl_mutex_type HANDLE
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#define ssl_mutex_type pthread_mutex_t
|
||||
#endif
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include "SocketBuffer.h"
|
||||
|
||||
#define URI_SSL "ssl://"
|
||||
|
||||
int SSLSocket_initialize();
|
||||
SSL* SSLSocket_setSocketForSSL(int socket, MQTTClient_SSLOptions* opts);
|
||||
int SSLSocket_getch(SSL* ssl, int socket, char* c);
|
||||
char *SSLSocket_getdata(SSL* ssl, int socket, int bytes, int* actual_len);
|
||||
|
||||
int SSLSocket_close(SSL* ssl);
|
||||
int SSLSocket_putdatas(SSL* ssl, int socket, char* buf0, int buf0len, int count, char** buffers, int* buflens);
|
||||
int SSLSocket_connect(SSL* ssl, int socket);
|
||||
|
||||
int SSLSocket_getPendingRead();
|
||||
int SSLSocket_continueWrite(pending_writes* pw);
|
||||
|
||||
#endif
|
||||
75
src/Socket.c
75
src/Socket.c
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
* Ian Craggs - async client updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -23,6 +24,9 @@
|
|||
#include "SocketBuffer.h"
|
||||
#include "Messages.h"
|
||||
#include "StackTrace.h"
|
||||
#if defined(OPENSSL)
|
||||
#include "SSLSocket.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -37,14 +41,12 @@ int Socket_continueWrites(fd_set* pwset);
|
|||
#if defined(WIN32)
|
||||
#define iov_len len
|
||||
#define iov_base buf
|
||||
#else
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Structure to hold all socket data for the module
|
||||
*/
|
||||
static Sockets s;
|
||||
Sockets s;
|
||||
static fd_set wset;
|
||||
|
||||
/**
|
||||
|
|
@ -92,7 +94,7 @@ int Socket_error(char* aString, int sock)
|
|||
if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != EWOULDBLOCK)
|
||||
{
|
||||
if (strcmp(aString, "shutdown") != 0 || (errno != ENOTCONN && errno != ECONNRESET))
|
||||
Log(TRACE_MIN, -1, "Socket error %d in %s for socket %d", errno, aString, sock);
|
||||
Log(TRACE_MIN, -1, "Socket error %s in %s for socket %d", strerror(errno), aString, sock);
|
||||
}
|
||||
FUNC_EXIT_RC(errno);
|
||||
return errno;
|
||||
|
|
@ -461,7 +463,11 @@ int Socket_putdatas(int socket, char* buf0, int buf0len, int count, char** buffe
|
|||
int* sockmem = (int*)malloc(sizeof(int));
|
||||
Log(TRACE_MIN, -1, "Partial write: %ld bytes of %d actually written on socket %d",
|
||||
bytes, total, socket);
|
||||
#if defined(OPENSSL)
|
||||
SocketBuffer_pendingWrite(socket, NULL, count+1, iovecs, total, bytes);
|
||||
#else
|
||||
SocketBuffer_pendingWrite(socket, count+1, iovecs, total, bytes);
|
||||
#endif
|
||||
*sockmem = socket;
|
||||
ListAppend(s.write_pending, sockmem, sizeof(int));
|
||||
FD_SET(socket, &(s.pending_wset));
|
||||
|
|
@ -474,6 +480,29 @@ exit:
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a socket to the pending write list, so that it is checked for writing in select. This is used
|
||||
* in connect processing when the TCP connect is incomplete, as we need to check the socket for both
|
||||
* ready to read and write states.
|
||||
* @param socket the socket to add
|
||||
*/
|
||||
void Socket_addPendingWrite(int socket)
|
||||
{
|
||||
FD_SET(socket, &(s.pending_wset));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear a socket from the pending write list - if one was added with Socket_addPendingWrite
|
||||
* @param socket the socket to remove
|
||||
*/
|
||||
void Socket_clearPendingWrite(int socket)
|
||||
{
|
||||
if (FD_ISSET(socket, &(s.pending_wset)))
|
||||
FD_CLR(socket, &(s.pending_wset));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close a socket without removing it from the select list.
|
||||
* @param socket the socket to close
|
||||
|
|
@ -490,7 +519,9 @@ int Socket_close_only(int socket)
|
|||
if ((rc = closesocket(socket)) == SOCKET_ERROR)
|
||||
Socket_error("close", socket);
|
||||
#else
|
||||
if (shutdown(socket, SHUT_RDWR) == SOCKET_ERROR)
|
||||
if (shutdown(socket, SHUT_WR) == SOCKET_ERROR)
|
||||
Socket_error("shutdown", socket);
|
||||
if ((rc = recv(socket, NULL, (size_t)0, 0)) == SOCKET_ERROR)
|
||||
Socket_error("shutdown", socket);
|
||||
if ((rc = close(socket)) == SOCKET_ERROR)
|
||||
Socket_error("close", socket);
|
||||
|
|
@ -613,6 +644,13 @@ int Socket_new(char* addr, int port, int* sock)
|
|||
rc = Socket_error("socket", *sock);
|
||||
else
|
||||
{
|
||||
#if defined(NOSIGPIPE)
|
||||
int opt = 1;
|
||||
|
||||
if (setsockopt(*sock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0)
|
||||
Log(TRACE_MIN, -1, "Could not set SO_NOSIGPIPE for socket %d", *sock);
|
||||
#endif
|
||||
|
||||
Log(TRACE_MIN, -1, "New socket %d for %s, port %d", *sock, addr, port);
|
||||
if (Socket_addSocket(*sock) == SOCKET_ERROR)
|
||||
rc = Socket_error("setnonblocking", *sock);
|
||||
|
|
@ -642,6 +680,13 @@ int Socket_new(char* addr, int port, int* sock)
|
|||
}
|
||||
|
||||
|
||||
static Socket_writeComplete* writecomplete = NULL;
|
||||
|
||||
void Socket_setWriteCompleteCallback(Socket_writeComplete* mywritecomplete)
|
||||
{
|
||||
writecomplete = mywritecomplete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Continue an outstanding write for a particular socket
|
||||
* @param socket that socket
|
||||
|
|
@ -658,6 +703,14 @@ int Socket_continueWrite(int socket)
|
|||
|
||||
FUNC_ENTRY;
|
||||
pw = SocketBuffer_getWrite(socket);
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (pw->ssl)
|
||||
{
|
||||
rc = SSLSocket_continueWrite(pw);
|
||||
goto exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < pw->count; ++i)
|
||||
{
|
||||
|
|
@ -687,11 +740,14 @@ int Socket_continueWrite(int socket)
|
|||
free(pw->iovecs[1].iov_base);
|
||||
if (pw->count == 5)
|
||||
free(pw->iovecs[3].iov_base);
|
||||
Log(TRACE_MIN, -1, "ContinueWrite: partial write now complete for socket %d", socket);
|
||||
Log(TRACE_MIN, -1, "ContinueWrite: partial write now complete for socket %d", socket);
|
||||
}
|
||||
else
|
||||
Log(TRACE_MIN, -1, "ContinueWrite wrote +%lu bytes on socket %d", bytes, socket);
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
exit:
|
||||
#endif
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -722,6 +778,9 @@ int Socket_continueWrites(fd_set* pwset)
|
|||
ListNextElement(s.write_pending, &curpending);
|
||||
}
|
||||
curpending = s.write_pending->current;
|
||||
|
||||
if (writecomplete)
|
||||
(*writecomplete)(socket);
|
||||
}
|
||||
else
|
||||
ListNextElement(s.write_pending, &curpending);
|
||||
|
|
|
|||
14
src/Socket.h
14
src/Socket.h
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
* Ian Craggs - async client updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(SOCKET_H)
|
||||
|
|
@ -50,7 +51,8 @@
|
|||
#define SOCKET_ERROR -1
|
||||
#endif
|
||||
/** must be the same as SOCKETBUFFER_INTERRUPTED */
|
||||
#define TCPSOCKET_INTERRUPTED -2
|
||||
#define TCPSOCKET_INTERRUPTED -22
|
||||
#define SSL_FATAL -3
|
||||
|
||||
#if !defined(INET6_ADDRSTRLEN)
|
||||
#define INET6_ADDRSTRLEN 46 /** only needed for gcc/cygwin on windows */
|
||||
|
|
@ -111,4 +113,10 @@ int Socket_new(char* addr, int port, int* socket);
|
|||
int Socket_noPendingWrites(int socket);
|
||||
char* Socket_getpeer(int sock);
|
||||
|
||||
void Socket_addPendingWrite(int socket);
|
||||
void Socket_clearPendingWrite(int socket);
|
||||
|
||||
typedef void Socket_writeComplete(int socket);
|
||||
void Socket_setWriteCompleteCallback(Socket_writeComplete*);
|
||||
|
||||
#endif /* SOCKET_H */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -296,7 +297,11 @@ void SocketBuffer_queueChar(int socket, char c)
|
|||
* @param total total data length to be written
|
||||
* @param bytes actual data length that was written
|
||||
*/
|
||||
#if defined(OPENSSL)
|
||||
void SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int total, int bytes)
|
||||
#else
|
||||
void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int total, int bytes)
|
||||
#endif
|
||||
{
|
||||
int i = 0;
|
||||
pending_writes* pw = NULL;
|
||||
|
|
@ -305,6 +310,9 @@ void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int total,
|
|||
/* store the buffers until the whole packet is written */
|
||||
pw = malloc(sizeof(pending_writes));
|
||||
pw->socket = socket;
|
||||
#if defined(OPENSSL)
|
||||
pw->ssl = ssl;
|
||||
#endif
|
||||
pw->bytes = bytes;
|
||||
pw->total = total;
|
||||
pw->count = count;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(SOCKETBUFFER_H)
|
||||
|
|
@ -19,6 +20,10 @@
|
|||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#if defined(OPENSSL)
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
typedef WSABUF iobuf;
|
||||
#else
|
||||
|
|
@ -38,6 +43,9 @@ typedef struct
|
|||
typedef struct
|
||||
{
|
||||
int socket, total, count;
|
||||
#if defined(OPENSSL)
|
||||
SSL* ssl;
|
||||
#endif
|
||||
unsigned long bytes;
|
||||
iobuf iovecs[5];
|
||||
} pending_writes;
|
||||
|
|
@ -46,7 +54,7 @@ typedef struct
|
|||
#if !defined(SOCKET_ERROR)
|
||||
#define SOCKET_ERROR -1
|
||||
#endif
|
||||
#define SOCKETBUFFER_INTERRUPTED -2 /* must be the same value as TCPSOCKET_INTERRUPTED */
|
||||
#define SOCKETBUFFER_INTERRUPTED -22 /* must be the same value as TCPSOCKET_INTERRUPTED */
|
||||
|
||||
void SocketBuffer_initialize(void);
|
||||
void SocketBuffer_terminate(void);
|
||||
|
|
@ -57,7 +65,11 @@ void SocketBuffer_interrupted(int socket, int actual_len);
|
|||
char* SocketBuffer_complete(int socket);
|
||||
void SocketBuffer_queueChar(int socket, char c);
|
||||
|
||||
#if defined(OPENSSL)
|
||||
void SocketBuffer_pendingWrite(int socket, SSL* ssl, int count, iobuf* iovecs, int total, int bytes);
|
||||
#else
|
||||
void SocketBuffer_pendingWrite(int socket, int count, iobuf* iovecs, int total, int bytes);
|
||||
#endif
|
||||
pending_writes* SocketBuffer_getWrite(int socket);
|
||||
int SocketBuffer_writeComplete(int socket);
|
||||
pending_writes* SocketBuffer_updateWrite(int socket, char* topic, char* payload);
|
||||
|
|
|
|||
172
src/Thread.c
172
src/Thread.c
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - initial implementation
|
||||
* Ian Craggs, Allan Stockdill-Mander - async client updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
|
|
@ -32,6 +33,10 @@
|
|||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -158,6 +163,20 @@ thread_id_type Thread_getid()
|
|||
}
|
||||
|
||||
|
||||
#if defined(USE_NAMED_SEMAPHORES)
|
||||
#define MAX_NAMED_SEMAPHORES 10
|
||||
|
||||
static int named_semaphore_count = 0;
|
||||
|
||||
static struct
|
||||
{
|
||||
sem_type sem;
|
||||
char name[NAME_MAX-4];
|
||||
} named_semaphores[MAX_NAMED_SEMAPHORES];
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Create a new semaphore
|
||||
* @return the new condition variable
|
||||
|
|
@ -175,9 +194,30 @@ sem_type Thread_create_sem()
|
|||
FALSE, // initial state is nonsignaled
|
||||
NULL // object name
|
||||
);
|
||||
#elif defined(USE_NAMED_SEMAPHORES)
|
||||
if (named_semaphore_count == 0)
|
||||
memset(named_semaphores, '\0', sizeof(named_semaphores));
|
||||
char* name = &(strrchr(tempnam("/", "MQTT"), '/'))[1]; /* skip first slash of name */
|
||||
if ((sem = sem_open(name, O_CREAT, S_IRWXU, 0)) == SEM_FAILED)
|
||||
rc = -1;
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
named_semaphore_count++;
|
||||
for (i = 0; i < MAX_NAMED_SEMAPHORES; ++i)
|
||||
{
|
||||
if (named_semaphores[i].name[0] == '\0')
|
||||
{
|
||||
named_semaphores[i].sem = sem;
|
||||
strcpy(named_semaphores[i].name, name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
sem = malloc(sizeof(sem_t));
|
||||
rc = sem_init(sem, 0, 0);
|
||||
sem = malloc(sizeof(sem_t));
|
||||
rc = sem_init(sem, 0, 0);
|
||||
#endif
|
||||
FUNC_EXIT_RC(rc);
|
||||
return sem;
|
||||
|
|
@ -185,24 +225,26 @@ sem_type Thread_create_sem()
|
|||
|
||||
|
||||
/**
|
||||
* Lock a mutex which has already been created, block until ready
|
||||
* @param mutex the mutex
|
||||
* Wait for a semaphore to be posted, or timeout.
|
||||
* @param sem the semaphore
|
||||
* @param timeout the maximum time to wait, in seconds
|
||||
* @return completion code
|
||||
*/
|
||||
int Thread_wait_sem(sem_type sem)
|
||||
int Thread_wait_sem_timeout(sem_type sem, int timeout)
|
||||
{
|
||||
/* sem_timedwait is the obvious call to use, but seemed not to work on the Viper,
|
||||
* so I've used trywait in a loop instead. Ian Craggs 23/7/2010
|
||||
*/
|
||||
int rc = -1;
|
||||
int timeout = 10; /* seconds */
|
||||
#if !defined(WIN32)
|
||||
#define USE_TRYWAIT
|
||||
#if defined(USE_TRYWAIT)
|
||||
int i = 0;
|
||||
int interval = 10000;
|
||||
int count = (1000000 / interval) * timeout;
|
||||
#elif !defined(WIN32)
|
||||
#else
|
||||
struct timespec ts;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
FUNC_ENTRY;
|
||||
|
|
@ -212,14 +254,17 @@ int Thread_wait_sem(sem_type sem)
|
|||
while (++i < count && (rc = sem_trywait(sem)) != 0)
|
||||
{
|
||||
if (rc == -1 && ((rc = errno) != EAGAIN))
|
||||
{
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
usleep(interval); /* microseconds - .1 of a second */
|
||||
}
|
||||
#else
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) != -1)
|
||||
{
|
||||
ts.tv_sec += timeout;
|
||||
rc = sem_timedwait(sem, &ts);
|
||||
rc = sem_timedwait(sem, &ts);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -228,6 +273,22 @@ int Thread_wait_sem(sem_type sem)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wait for a semaphore to be posted, or timeout after 10 seconds.
|
||||
* @param sem the semaphore
|
||||
* @return completion code
|
||||
*/
|
||||
int Thread_wait_sem(sem_type sem)
|
||||
{
|
||||
return Thread_wait_sem_timeout(sem, 10);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check to see if a semaphore has been posted, without waiting.
|
||||
* @param sem the semaphore
|
||||
* @return 0 (false) or 1 (true)
|
||||
*/
|
||||
int Thread_check_sem(sem_type sem)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
|
|
@ -241,8 +302,8 @@ int Thread_check_sem(sem_type sem)
|
|||
|
||||
|
||||
/**
|
||||
* Lock a mutex which has already been created, block until ready
|
||||
* @param mutex the mutex
|
||||
* Post a semaphore
|
||||
* @param sem the semaphore
|
||||
* @return completion code
|
||||
*/
|
||||
int Thread_post_sem(sem_type sem)
|
||||
|
|
@ -274,6 +335,19 @@ int Thread_destroy_sem(sem_type sem)
|
|||
FUNC_ENTRY;
|
||||
#if defined(WIN32)
|
||||
rc = CloseHandle(sem);
|
||||
#elif defined(USE_NAMED_SEMAPHORES)
|
||||
int i;
|
||||
rc = sem_close(sem);
|
||||
for (i = 0; i < MAX_NAMED_SEMAPHORES; ++i)
|
||||
{
|
||||
if (named_semaphores[i].sem == sem)
|
||||
{
|
||||
rc = sem_unlink(named_semaphores[i].name);
|
||||
named_semaphores[i].name[0] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
named_semaphore_count--;
|
||||
#else
|
||||
rc = sem_destroy(sem);
|
||||
free(sem);
|
||||
|
|
@ -283,6 +357,80 @@ int Thread_destroy_sem(sem_type sem)
|
|||
}
|
||||
|
||||
|
||||
#if !defined(WIN32)
|
||||
/**
|
||||
* Create a new condition variable
|
||||
* @return the condition variable struct
|
||||
*/
|
||||
cond_type Thread_create_cond()
|
||||
{
|
||||
cond_type condvar = NULL;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
condvar = malloc(sizeof(cond_type_struct));
|
||||
rc = pthread_cond_init(&condvar->cond, NULL);
|
||||
rc = pthread_mutex_init(&condvar->mutex, NULL);
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return condvar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal a condition variable
|
||||
* @return completion code
|
||||
*/
|
||||
int Thread_signal_cond(cond_type condvar)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
pthread_mutex_lock(&condvar->mutex);
|
||||
rc = pthread_cond_signal(&condvar->cond);
|
||||
pthread_mutex_unlock(&condvar->mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait with a timeout (seconds) for condition variable
|
||||
* @return completion code
|
||||
*/
|
||||
int Thread_wait_cond_timeout(cond_type condvar, int timeout)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
int rc = 0;
|
||||
struct timespec cond_timeout;
|
||||
struct timeval cur_time;
|
||||
|
||||
gettimeofday(&cur_time, NULL);
|
||||
|
||||
cond_timeout.tv_sec = cur_time.tv_sec + timeout;
|
||||
cond_timeout.tv_nsec = cur_time.tv_usec * 1000;
|
||||
|
||||
pthread_mutex_lock(&condvar->mutex);
|
||||
rc = pthread_cond_timedwait(&condvar->cond, &condvar->mutex, &cond_timeout);
|
||||
pthread_mutex_unlock(&condvar->mutex);
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a condition variable
|
||||
* @return completion code
|
||||
*/
|
||||
int Thread_destroy_cond(cond_type condvar)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = pthread_mutex_destroy(&condvar->mutex);
|
||||
rc = pthread_cond_destroy(&condvar->cond);
|
||||
free(condvar);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(THREAD_UNIT_TESTS)
|
||||
|
||||
|
|
|
|||
11
src/Thread.h
11
src/Thread.h
|
|
@ -1,5 +1,5 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
* Copyright (c) 2009, 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - initial implementation
|
||||
* Ian Craggs, Allan Stockdill-Mander - async client updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(THREAD_H)
|
||||
|
|
@ -33,6 +34,11 @@
|
|||
typedef struct { pthread_cond_t cond; pthread_mutex_t mutex; } cond_type_struct;
|
||||
typedef cond_type_struct *cond_type;
|
||||
typedef sem_t *sem_type;
|
||||
|
||||
cond_type Thread_create_cond();
|
||||
int Thread_signal_cond(cond_type);
|
||||
int Thread_wait_cond_timeout(cond_type condvar, int timeout);
|
||||
int Thread_destroy_cond(cond_type);
|
||||
#endif
|
||||
|
||||
thread_type Thread_start(thread_fn, void*);
|
||||
|
|
@ -46,6 +52,7 @@ thread_id_type Thread_getid();
|
|||
|
||||
sem_type Thread_create_sem();
|
||||
int Thread_wait_sem(sem_type sem);
|
||||
int Thread_wait_sem_timeout(sem_type sem, int timeout);
|
||||
int Thread_check_sem(sem_type sem);
|
||||
int Thread_post_sem(sem_type sem);
|
||||
int Thread_destroy_sem(sem_type sem);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,703 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
/** @file
|
||||
* \brief functions which apply to tree structures.
|
||||
*
|
||||
* These trees can hold data of any sort, pointed to by the content pointer of the
|
||||
* Node structure.
|
||||
* */
|
||||
|
||||
#define NO_HEAP_TRACKING 1
|
||||
|
||||
#include "Tree.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
|
||||
void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int))
|
||||
{
|
||||
memset(aTree, '\0', sizeof(Tree));
|
||||
aTree->heap_tracking = 1;
|
||||
aTree->index[0].compare = compare;
|
||||
aTree->indexes = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new tree structure.
|
||||
* @return a pointer to the new tree structure
|
||||
*/
|
||||
Tree* TreeInitialize(int(*compare)(void*, void*, int))
|
||||
{
|
||||
#if defined(UNIT_TESTS)
|
||||
Tree* newt = malloc(sizeof(Tree));
|
||||
#else
|
||||
Tree* newt = mymalloc(__FILE__, __LINE__, sizeof(Tree));
|
||||
#endif
|
||||
TreeInitializeNoMalloc(newt, compare);
|
||||
return newt;
|
||||
}
|
||||
|
||||
|
||||
void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int))
|
||||
{
|
||||
aTree->index[aTree->indexes].compare = compare;
|
||||
++(aTree->indexes);
|
||||
}
|
||||
|
||||
|
||||
void TreeFree(Tree* aTree)
|
||||
{
|
||||
#if defined(UNIT_TESTS)
|
||||
free(aTree);
|
||||
#else
|
||||
(aTree->heap_tracking) ? myfree(__FILE__, __LINE__, aTree) : free(aTree);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define LEFT 0
|
||||
#define RIGHT 1
|
||||
#if !defined(max)
|
||||
#define max(a, b) (a > b) ? a : b;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int isRed(Node* aNode)
|
||||
{
|
||||
return (aNode != NULL) && (aNode->red);
|
||||
}
|
||||
|
||||
|
||||
int isBlack(Node* aNode)
|
||||
{
|
||||
return (aNode == NULL) || (aNode->red == 0);
|
||||
}
|
||||
|
||||
|
||||
int TreeWalk(Node* curnode, int depth)
|
||||
{
|
||||
if (curnode)
|
||||
{
|
||||
int left = TreeWalk(curnode->child[LEFT], depth+1);
|
||||
int right = TreeWalk(curnode->child[RIGHT], depth+1);
|
||||
depth = max(left, right);
|
||||
if (curnode->red)
|
||||
{
|
||||
/*if (isRed(curnode->child[LEFT]) || isRed(curnode->child[RIGHT]))
|
||||
{
|
||||
printf("red/black tree violation %p\n", curnode->content);
|
||||
exit(-99);
|
||||
}*/;
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
|
||||
int TreeMaxDepth(Tree *aTree)
|
||||
{
|
||||
int rc = TreeWalk(aTree->index[0].root, 0);
|
||||
/*if (aTree->root->red)
|
||||
{
|
||||
printf("root node should not be red %p\n", aTree->root->content);
|
||||
exit(-99);
|
||||
}*/
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void TreeRotate(Tree* aTree, Node* curnode, int direction, int index)
|
||||
{
|
||||
Node* other = curnode->child[!direction];
|
||||
|
||||
curnode->child[!direction] = other->child[direction];
|
||||
if (other->child[direction] != NULL)
|
||||
other->child[direction]->parent = curnode;
|
||||
other->parent = curnode->parent;
|
||||
if (curnode->parent == NULL)
|
||||
aTree->index[index].root = other;
|
||||
else if (curnode == curnode->parent->child[direction])
|
||||
curnode->parent->child[direction] = other;
|
||||
else
|
||||
curnode->parent->child[!direction] = other;
|
||||
other->child[direction] = curnode;
|
||||
curnode->parent = other;
|
||||
}
|
||||
|
||||
|
||||
Node* TreeBAASub(Tree* aTree, Node* curnode, int which, int index)
|
||||
{
|
||||
Node* uncle = curnode->parent->parent->child[which];
|
||||
|
||||
if (isRed(uncle))
|
||||
{
|
||||
curnode->parent->red = uncle->red = 0;
|
||||
curnode = curnode->parent->parent;
|
||||
curnode->red = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curnode == curnode->parent->child[which])
|
||||
{
|
||||
curnode = curnode->parent;
|
||||
TreeRotate(aTree, curnode, !which, index);
|
||||
}
|
||||
curnode->parent->red = 0;
|
||||
curnode->parent->parent->red = 1;
|
||||
TreeRotate(aTree, curnode->parent->parent, which, index);
|
||||
}
|
||||
return curnode;
|
||||
}
|
||||
|
||||
|
||||
void TreeBalanceAfterAdd(Tree* aTree, Node* curnode, int index)
|
||||
{
|
||||
while (curnode && isRed(curnode->parent) && curnode->parent->parent)
|
||||
{
|
||||
if (curnode->parent == curnode->parent->parent->child[LEFT])
|
||||
curnode = TreeBAASub(aTree, curnode, RIGHT, index);
|
||||
else
|
||||
curnode = TreeBAASub(aTree, curnode, LEFT, index);
|
||||
}
|
||||
aTree->index[index].root->red = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add an item to a tree
|
||||
* @param aTree the list to which the item is to be added
|
||||
* @param content the list item content itself
|
||||
* @param size the size of the element
|
||||
*/
|
||||
void* TreeAddByIndex(Tree* aTree, void* content, int size, int index)
|
||||
{
|
||||
Node* curparent = NULL;
|
||||
Node* curnode = aTree->index[index].root;
|
||||
Node* newel = NULL;
|
||||
int left = 0;
|
||||
int result = 1;
|
||||
void* rc = NULL;
|
||||
|
||||
while (curnode)
|
||||
{
|
||||
result = aTree->index[index].compare(curnode->content, content, 1);
|
||||
left = (result > 0);
|
||||
if (result == 0)
|
||||
break;
|
||||
else
|
||||
{
|
||||
curparent = curnode;
|
||||
curnode = curnode->child[left];
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
if (aTree->allow_duplicates)
|
||||
exit(-99);
|
||||
{
|
||||
newel = curnode;
|
||||
rc = newel->content;
|
||||
if (index == 0)
|
||||
aTree->size += (size - curnode->size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(UNIT_TESTS)
|
||||
newel = malloc(sizeof(Node));
|
||||
#else
|
||||
newel = (aTree->heap_tracking) ? mymalloc(__FILE__, __LINE__, sizeof(Node)) : malloc(sizeof(Node));
|
||||
#endif
|
||||
memset(newel, '\0', sizeof(Node));
|
||||
if (curparent)
|
||||
curparent->child[left] = newel;
|
||||
else
|
||||
aTree->index[index].root = newel;
|
||||
newel->parent = curparent;
|
||||
newel->red = 1;
|
||||
if (index == 0)
|
||||
{
|
||||
++(aTree->count);
|
||||
aTree->size += size;
|
||||
}
|
||||
}
|
||||
newel->content = content;
|
||||
newel->size = size;
|
||||
TreeBalanceAfterAdd(aTree, newel, index);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void* TreeAdd(Tree* aTree, void* content, int size)
|
||||
{
|
||||
void* rc = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < aTree->indexes; ++i)
|
||||
rc = TreeAddByIndex(aTree, content, size, i);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
Node* TreeFindIndex1(Tree* aTree, void* key, int index, int value)
|
||||
{
|
||||
int result = 0;
|
||||
Node* curnode = aTree->index[index].root;
|
||||
|
||||
while (curnode)
|
||||
{
|
||||
result = aTree->index[index].compare(curnode->content, key, value);
|
||||
if (result == 0)
|
||||
break;
|
||||
else
|
||||
curnode = curnode->child[result > 0];
|
||||
}
|
||||
return curnode;
|
||||
}
|
||||
|
||||
|
||||
Node* TreeFindIndex(Tree* aTree, void* key, int index)
|
||||
{
|
||||
return TreeFindIndex1(aTree, key, index, 0);
|
||||
}
|
||||
|
||||
|
||||
Node* TreeFindContentIndex(Tree* aTree, void* key, int index)
|
||||
{
|
||||
return TreeFindIndex1(aTree, key, index, 1);
|
||||
}
|
||||
|
||||
|
||||
Node* TreeFind(Tree* aTree, void* key)
|
||||
{
|
||||
return TreeFindIndex(aTree, key, 0);
|
||||
}
|
||||
|
||||
|
||||
Node* TreeMinimum(Node* curnode)
|
||||
{
|
||||
if (curnode)
|
||||
while (curnode->child[LEFT])
|
||||
curnode = curnode->child[LEFT];
|
||||
return curnode;
|
||||
}
|
||||
|
||||
|
||||
Node* TreeSuccessor(Node* curnode)
|
||||
{
|
||||
if (curnode->child[RIGHT])
|
||||
curnode = TreeMinimum(curnode->child[RIGHT]);
|
||||
else
|
||||
{
|
||||
Node* curparent = curnode->parent;
|
||||
while (curparent && curnode == curparent->child[RIGHT])
|
||||
{
|
||||
curnode = curparent;
|
||||
curparent = curparent->parent;
|
||||
}
|
||||
curnode = curparent;
|
||||
}
|
||||
return curnode;
|
||||
}
|
||||
|
||||
|
||||
Node* TreeNextElementIndex(Tree* aTree, Node* curnode, int index)
|
||||
{
|
||||
if (curnode == NULL)
|
||||
curnode = TreeMinimum(aTree->index[index].root);
|
||||
else
|
||||
curnode = TreeSuccessor(curnode);
|
||||
return curnode;
|
||||
}
|
||||
|
||||
|
||||
Node* TreeNextElement(Tree* aTree, Node* curnode)
|
||||
{
|
||||
return TreeNextElementIndex(aTree, curnode, 0);
|
||||
}
|
||||
|
||||
|
||||
Node* TreeBARSub(Tree* aTree, Node* curnode, int which, int index)
|
||||
{
|
||||
Node* sibling = curnode->parent->child[which];
|
||||
|
||||
if (isRed(sibling))
|
||||
{
|
||||
sibling->red = 0;
|
||||
curnode->parent->red = 1;
|
||||
TreeRotate(aTree, curnode->parent, !which, index);
|
||||
sibling = curnode->parent->child[which];
|
||||
}
|
||||
if (!sibling)
|
||||
curnode = curnode->parent;
|
||||
else if (isBlack(sibling->child[!which]) && isBlack(sibling->child[which]))
|
||||
{
|
||||
sibling->red = 1;
|
||||
curnode = curnode->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isBlack(sibling->child[which]))
|
||||
{
|
||||
sibling->child[!which]->red = 0;
|
||||
sibling->red = 1;
|
||||
TreeRotate(aTree, sibling, which, index);
|
||||
sibling = curnode->parent->child[which];
|
||||
}
|
||||
sibling->red = curnode->parent->red;
|
||||
curnode->parent->red = 0;
|
||||
sibling->child[which]->red = 0;
|
||||
TreeRotate(aTree, curnode->parent, !which, index);
|
||||
curnode = aTree->index[index].root;
|
||||
}
|
||||
return curnode;
|
||||
}
|
||||
|
||||
|
||||
void TreeBalanceAfterRemove(Tree* aTree, Node* curnode, int index)
|
||||
{
|
||||
while (curnode != aTree->index[index].root && isBlack(curnode))
|
||||
{
|
||||
/* curnode->content == NULL must equal curnode == NULL */
|
||||
if (((curnode->content) ? curnode : NULL) == curnode->parent->child[LEFT])
|
||||
curnode = TreeBARSub(aTree, curnode, RIGHT, index);
|
||||
else
|
||||
curnode = TreeBARSub(aTree, curnode, LEFT, index);
|
||||
}
|
||||
curnode->red = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from a tree
|
||||
* @param aTree the list to which the item is to be added
|
||||
* @param curnode the list item content itself
|
||||
*/
|
||||
void* TreeRemoveNodeIndex(Tree* aTree, Node* curnode, int index)
|
||||
{
|
||||
Node* redundant = curnode;
|
||||
Node* curchild = NULL;
|
||||
int size = curnode->size;
|
||||
void* content = curnode->content;
|
||||
|
||||
/* if the node to remove has 0 or 1 children, it can be removed without involving another node */
|
||||
if (curnode->child[LEFT] && curnode->child[RIGHT]) /* 2 children */
|
||||
redundant = TreeSuccessor(curnode); /* now redundant must have at most one child */
|
||||
|
||||
curchild = redundant->child[(redundant->child[LEFT] != NULL) ? LEFT : RIGHT];
|
||||
if (curchild) /* we could have no children at all */
|
||||
curchild->parent = redundant->parent;
|
||||
|
||||
if (redundant->parent == NULL)
|
||||
aTree->index[index].root = curchild;
|
||||
else
|
||||
{
|
||||
if (redundant == redundant->parent->child[LEFT])
|
||||
redundant->parent->child[LEFT] = curchild;
|
||||
else
|
||||
redundant->parent->child[RIGHT] = curchild;
|
||||
}
|
||||
|
||||
if (redundant != curnode)
|
||||
{
|
||||
curnode->content = redundant->content;
|
||||
curnode->size = redundant->size;
|
||||
}
|
||||
|
||||
if (isBlack(redundant))
|
||||
{
|
||||
if (curchild == NULL)
|
||||
{
|
||||
if (redundant->parent)
|
||||
{
|
||||
Node temp;
|
||||
memset(&temp, '\0', sizeof(Node));
|
||||
temp.parent = (redundant) ? redundant->parent : NULL;
|
||||
temp.red = 0;
|
||||
TreeBalanceAfterRemove(aTree, &temp, index);
|
||||
}
|
||||
}
|
||||
else
|
||||
TreeBalanceAfterRemove(aTree, curchild, index);
|
||||
}
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
free(redundant);
|
||||
#else
|
||||
(aTree->heap_tracking) ? myfree(__FILE__, __LINE__, redundant) : free(redundant);
|
||||
#endif
|
||||
if (index == 0)
|
||||
{
|
||||
aTree->size -= size;
|
||||
--(aTree->count);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from a tree
|
||||
* @param aTree the list to which the item is to be added
|
||||
* @param curnode the list item content itself
|
||||
*/
|
||||
void* TreeRemoveIndex(Tree* aTree, void* content, int index)
|
||||
{
|
||||
Node* curnode = TreeFindContentIndex(aTree, content, index);
|
||||
|
||||
if (curnode == NULL)
|
||||
return NULL;
|
||||
|
||||
return TreeRemoveNodeIndex(aTree, curnode, index);
|
||||
}
|
||||
|
||||
|
||||
void* TreeRemove(Tree* aTree, void* content)
|
||||
{
|
||||
int i;
|
||||
void* rc = NULL;
|
||||
|
||||
for (i = 0; i < aTree->indexes; ++i)
|
||||
rc = TreeRemoveIndex(aTree, content, i);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index)
|
||||
{
|
||||
Node* curnode = TreeFindIndex(aTree, key, index);
|
||||
void* content = NULL;
|
||||
int i;
|
||||
|
||||
if (curnode == NULL)
|
||||
return NULL;
|
||||
|
||||
content = TreeRemoveNodeIndex(aTree, curnode, index);
|
||||
for (i = 0; i < aTree->indexes; ++i)
|
||||
{
|
||||
if (i != index)
|
||||
content = TreeRemoveIndex(aTree, content, i);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
void* TreeRemoveKey(Tree* aTree, void* key)
|
||||
{
|
||||
return TreeRemoveKeyIndex(aTree, key, 0);
|
||||
}
|
||||
|
||||
|
||||
int TreeIntCompare(void* a, void* b, int content)
|
||||
{
|
||||
int i = *((int*)a);
|
||||
int j = *((int*)b);
|
||||
|
||||
//printf("comparing %d %d\n", *((int*)a), *((int*)b));
|
||||
return (i > j) ? -1 : (i == j) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
int TreePtrCompare(void* a, void* b, int content)
|
||||
{
|
||||
return (a > b) ? -1 : (a == b) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
int TreeStringCompare(void* a, void* b, int content)
|
||||
{
|
||||
return strcmp((char*)a, (char*)b);
|
||||
}
|
||||
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
|
||||
int check(Tree *t)
|
||||
{
|
||||
Node* curnode = NULL;
|
||||
int rc = 0;
|
||||
|
||||
curnode = TreeNextElement(t, curnode);
|
||||
while (curnode)
|
||||
{
|
||||
Node* prevnode = curnode;
|
||||
|
||||
curnode = TreeNextElement(t, curnode);
|
||||
|
||||
if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
|
||||
{
|
||||
printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
|
||||
rc = 99;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int traverse(Tree *t, int lookfor)
|
||||
{
|
||||
Node* curnode = NULL;
|
||||
int rc = 0;
|
||||
|
||||
printf("Traversing\n");
|
||||
curnode = TreeNextElement(t, curnode);
|
||||
//printf("content int %d\n", *(int*)(curnode->content));
|
||||
while (curnode)
|
||||
{
|
||||
Node* prevnode = curnode;
|
||||
|
||||
curnode = TreeNextElement(t, curnode);
|
||||
//if (curnode)
|
||||
// printf("content int %d\n", *(int*)(curnode->content));
|
||||
if (prevnode && curnode && (*(int*)(curnode->content) < *(int*)(prevnode->content)))
|
||||
{
|
||||
printf("out of order %d < %d\n", *(int*)(curnode->content), *(int*)(prevnode->content));
|
||||
}
|
||||
if (curnode && (lookfor == *(int*)(curnode->content)))
|
||||
printf("missing item %d actually found\n", lookfor);
|
||||
}
|
||||
printf("End traverse %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int test(int limit)
|
||||
{
|
||||
int i, *ip, *todelete;
|
||||
Node* current = NULL;
|
||||
Tree* t = TreeInitialize(TreeIntCompare);
|
||||
int rc = 0;
|
||||
|
||||
printf("Tree initialized\n");
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
ip = malloc(sizeof(int));
|
||||
*ip = 2;
|
||||
TreeAdd(t, (void*)ip, sizeof(int));
|
||||
|
||||
check(t);
|
||||
|
||||
i = 2;
|
||||
void* result = TreeRemove(t, (void*)&i);
|
||||
if (result)
|
||||
free(result);
|
||||
|
||||
int actual[limit];
|
||||
for (i = 0; i < limit; i++)
|
||||
{
|
||||
void* replaced = NULL;
|
||||
|
||||
ip = malloc(sizeof(int));
|
||||
*ip = rand();
|
||||
replaced = TreeAdd(t, (void*)ip, sizeof(int));
|
||||
if (replaced) /* duplicate */
|
||||
{
|
||||
free(replaced);
|
||||
actual[i] = -1;
|
||||
}
|
||||
else
|
||||
actual[i] = *ip;
|
||||
if (i==5)
|
||||
todelete = ip;
|
||||
printf("Tree element added %d\n", *ip);
|
||||
if (1 % 1000 == 0)
|
||||
{
|
||||
rc = check(t);
|
||||
printf("%d elements, check result %d\n", i+1, rc);
|
||||
if (rc != 0)
|
||||
return 88;
|
||||
}
|
||||
}
|
||||
|
||||
check(t);
|
||||
|
||||
for (i = 0; i < limit; i++)
|
||||
{
|
||||
int parm = actual[i];
|
||||
|
||||
if (parm == -1)
|
||||
continue;
|
||||
|
||||
Node* found = TreeFind(t, (void*)&parm);
|
||||
if (found)
|
||||
printf("Tree find %d %d\n", parm, *(int*)(found->content));
|
||||
else
|
||||
{
|
||||
printf("%d not found\n", parm);
|
||||
traverse(t, parm);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
check(t);
|
||||
|
||||
for (i = limit -1; i >= 0; i--)
|
||||
{
|
||||
int parm = actual[i];
|
||||
void *found;
|
||||
|
||||
if (parm == -1) /* skip duplicate */
|
||||
continue;
|
||||
|
||||
found = TreeRemove(t, (void*)&parm);
|
||||
if (found)
|
||||
{
|
||||
printf("%d Tree remove %d %d\n", i, parm, *(int*)(found));
|
||||
free(found);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = 0;
|
||||
printf("%d %d not found\n", i, parm);
|
||||
traverse(t, parm);
|
||||
for (i = 0; i < limit; i++)
|
||||
if (actual[i] == parm)
|
||||
++count;
|
||||
printf("%d occurs %d times\n", parm, count);
|
||||
return -2;
|
||||
}
|
||||
if (i % 1000 == 0)
|
||||
{
|
||||
rc = check(t);
|
||||
printf("%d elements, check result %d\n", i+1, rc);
|
||||
if (rc != 0)
|
||||
return 88;
|
||||
}
|
||||
}
|
||||
printf("finished\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (rc == 0)
|
||||
rc = test(999999);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2012 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined(TREE_H)
|
||||
#define TREE_H
|
||||
|
||||
/*BE
|
||||
defm defTree(T) // macro to define a tree
|
||||
|
||||
def T concat Node
|
||||
{
|
||||
n32 ptr T concat Node "parent"
|
||||
n32 ptr T concat Node "left"
|
||||
n32 ptr T concat Node "right"
|
||||
n32 ptr T id2str(T)
|
||||
n32 suppress "size"
|
||||
}
|
||||
|
||||
|
||||
def T concat Tree
|
||||
{
|
||||
struct
|
||||
{
|
||||
n32 ptr T concat Node suppress "root"
|
||||
n32 ptr DATA suppress "compare"
|
||||
}
|
||||
struct
|
||||
{
|
||||
n32 ptr T concat Node suppress "root"
|
||||
n32 ptr DATA suppress "compare"
|
||||
}
|
||||
n32 dec "count"
|
||||
n32 dec suppress "size"
|
||||
}
|
||||
|
||||
endm
|
||||
|
||||
defTree(INT)
|
||||
defTree(STRING)
|
||||
defTree(TMP)
|
||||
|
||||
BE*/
|
||||
|
||||
/**
|
||||
* Structure to hold all data for one list element
|
||||
*/
|
||||
typedef struct NodeStruct
|
||||
{
|
||||
struct NodeStruct *parent, /**< pointer to parent tree node, in case we need it */
|
||||
*child[2]; /**< pointers to child tree nodes 0 = left, 1 = right */
|
||||
void* content; /**< pointer to element content */
|
||||
int size; /**< size of content */
|
||||
unsigned int red : 1;
|
||||
} Node;
|
||||
|
||||
|
||||
/**
|
||||
* Structure to hold all data for one tree
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
Node *root; /**< root node pointer */
|
||||
int (*compare)(void*, void*, int); /**< comparison function */
|
||||
} index[2];
|
||||
int indexes, /**< no of indexes into tree */
|
||||
count, /**< no of items */
|
||||
size; /**< heap storage used */
|
||||
unsigned int heap_tracking : 1; /**< switch on heap tracking for this tree? */
|
||||
unsigned int allow_duplicates : 1; /**< switch to allow duplicate entries */
|
||||
} Tree;
|
||||
|
||||
|
||||
Tree* TreeInitialize(int(*compare)(void*, void*, int));
|
||||
void TreeInitializeNoMalloc(Tree* aTree, int(*compare)(void*, void*, int));
|
||||
void TreeAddIndex(Tree* aTree, int(*compare)(void*, void*, int));
|
||||
|
||||
void* TreeAdd(Tree* aTree, void* content, int size);
|
||||
|
||||
void* TreeRemove(Tree* aTree, void* content);
|
||||
|
||||
void* TreeRemoveKey(Tree* aTree, void* key);
|
||||
void* TreeRemoveKeyIndex(Tree* aTree, void* key, int index);
|
||||
|
||||
void* TreeRemoveNodeIndex(Tree* aTree, Node* aNode, int index);
|
||||
|
||||
void TreeFree(Tree* aTree);
|
||||
|
||||
Node* TreeFind(Tree* aTree, void* key);
|
||||
Node* TreeFindIndex(Tree* aTree, void* key, int index);
|
||||
|
||||
Node* TreeNextElement(Tree* aTree, Node* curnode);
|
||||
|
||||
int TreeIntCompare(void* a, void* b, int);
|
||||
int TreePtrCompare(void* a, void* b, int);
|
||||
int TreeStringCompare(void* a, void* b, int);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue