Skip to content

Commit

Permalink
Allow connection using "hostname", allow logging during session setup (
Browse files Browse the repository at this point in the history
…#9)

- updated ami_connect() to allow the host to be specified by DNS "name"
(and not just IP address)

- the ami_set_debug_level() function currently requires a session to be
specified.  This means that there is no way to control the debug level
(and any assciated outut) before a session is established.  Updated the
code to allow one to pass a NULL session to set the default (and
pre-session) debug level.

- Allow compilation on macOS
  • Loading branch information
Allan-N authored Dec 27, 2024
1 parent 645233b commit cd2a05a
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 44 deletions.
41 changes: 24 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,43 @@
#

CC = gcc
CFLAGS = -Wall -Werror -Wno-unused-parameter -Wextra -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wformat=2 -Wshadow -std=gnu99 -pthread -O3 -g -Wstack-protector -fno-omit-frame-pointer -D_FORTIFY_SOURCE=2
CFLAGS = -Wall -Werror -Wno-unused-parameter -Wextra -Wstrict-prototypes -Wmissing-prototypes -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-format-attribute -Wformat=2 -Wshadow -std=gnu99 -pthread -O0 -g -Wstack-protector -fno-omit-frame-pointer -D_FORTIFY_SOURCE=2 -I.
EXE = cami
SAMPEXES = simpleami amicli
LIBNAME = libcami
LIBS = -lm
LIBNAME = lib$(EXE).so
LIBS = -lm -ldl
RM = rm -f
INSTALL = install
INSTALL = install

all : library
all : library examples

%.o: %.c
$(CC) $(CFLAGS) -fPIC -c $^

library: $(EXE).o
$(LIBNAME): $(EXE).o
@echo "== Linking $@"
$(CC) -shared -fPIC -o $(LIBNAME).so $^ $(LIBS)
$(CC) -shared -fPIC -o $(LIBNAME) $^ $(LIBS)

library: $(LIBNAME)
@if [ ! -d /usr/include/$(EXE) ]; then \
ln -f -s include $(EXE); \
fi

install:
$(INSTALL) -m 755 $(LIBNAME).so "/usr/lib"
$(INSTALL) -m 755 $(LIBNAME) "/usr/lib"
mkdir -p /usr/include/$(EXE)
$(INSTALL) -m 755 include/*.h "/usr/include/$(EXE)/"
$(INSTALL) -m 644 include/*.h "/usr/include/$(EXE)/"

simpleami: library install simpleami.o
$(CC) $(CFLAGS) -o simpleami simpleami.o -l$(EXE) $(LIBS) -ldl
simpleami: simpleami.o $(LIBNAME)
$(CC) $(CFLAGS) -o $@ $@.o -L. -Wl,-rpath,. -l$(EXE) $(LIBS)

amicli: library install amicli.o
$(CC) $(CFLAGS) -o amicli amicli.o -l$(EXE) $(LIBS) -ldl
amicli: amicli.o $(LIBNAME)
$(CC) $(CFLAGS) -o $@ $@.o -L. -Wl,-rpath,. -l$(EXE) $(LIBS)

examples : $(SAMPEXES)
examples: $(SAMPEXES)

clean :
$(RM) *.i *.o $(EXE) $(SAMPEXES) $(LIBNAME).so
clean:
$(RM) *.i *.o $(EXE) $(SAMPEXES) $(LIBNAME)

uninstall:
$(RM) /usr/lib/$(EXE).so
Expand All @@ -48,5 +53,7 @@ uninstall:
.PHONY: all
.PHONY: library
.PHONY: install
.PHONY: example
.PHONY: examples
.PHONY: clean

# vim: set noexpandtab shiftwidth=4 tabstop=4:
2 changes: 1 addition & 1 deletion amicli.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ int main(int argc,char *argv[])
int debug = 0;
struct ami_session *ami;

while ((c = getopt(argc, argv, getopt_settings)) != -1) {
while ((c = getopt(argc, argv, getopt_settings)) != (char) -1) {
switch (c) {
case '?':
case 'd':
Expand Down
123 changes: 100 additions & 23 deletions cami.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
if (ami->debug_level >= level) { \
struct timeval tv; \
gettimeofday(&tv, NULL); \
if (ami->debugfd != -1) dprintf(ami->debugfd, "%llu:%03lu : %d : " fmt, (((long long)tv.tv_sec)), (tv.tv_usec/1000), __LINE__, ## __VA_ARGS__); \
if (ami->debugfd != -1) dprintf(ami->debugfd, "%llu:%03lu : %d : " fmt, (((unsigned long long)tv.tv_sec)), (unsigned long)(tv.tv_usec/1000), __LINE__, ## __VA_ARGS__); \
} \
}

Expand Down Expand Up @@ -101,14 +101,19 @@ struct ami_session {
unsigned int return_null_on_error:1;
};

/* Used for debugging prior to session creation */
static int ami_initial_debugfd = -1;
static int ami_initial_debug_level = 0;

static struct ami_session *ami_session_new(void)
{
struct ami_session *ami = calloc(1, sizeof(*ami));
if (!ami) {
return NULL;
}
ami->ami_socket = -1;
ami->debugfd = -1;
ami->debugfd = ami_initial_debugfd;
ami->debug_level = ami_initial_debug_level;
ami->ami_pipe[0] = ami->ami_pipe[1] = -1;
ami->ami_read_pipe[0] = ami->ami_read_pipe[1] = -1;
ami->ami_event_pipe[0] = ami->ami_event_pipe[1] = -1;
Expand Down Expand Up @@ -358,6 +363,7 @@ struct ami_session *ami_connect(const char *hostname, int port, void (*callback)
int fd;
struct sockaddr_in saddr;
struct ami_session *ami;
int ret;

ami = ami_session_new();
if (!ami) {
Expand Down Expand Up @@ -416,13 +422,57 @@ struct ami_session *ami_connect(const char *hostname, int port, void (*callback)
goto cleanup;
}
ami->ami_socket = fd;
inet_pton(AF_INET, hostname, &(saddr.sin_addr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port); /* use network order */
if (connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
ami_error(ami, "connect failed: %s\n", strerror(errno));
ami_cleanup(ami);
goto cleanup;
if (inet_pton(AF_INET, hostname, &(saddr.sin_addr)) == 1) {
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port); /* use network order */
if (connect(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
ami_error(ami, "connect failed: %s\n", strerror(errno));
ami_cleanup(ami);
goto cleanup;
}
} else {
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG
};
struct addrinfo *res;

if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
if (res->ai_addr == NULL) {
freeaddrinfo(res);
ami_error(ami, "host %s not valid\n", hostname);
ami_cleanup(ami);
goto cleanup;
}

switch (res->ai_addr->sa_family) {
case AF_INET:
((struct sockaddr_in *)res->ai_addr)->sin_port = htons(port);
break;
case AF_INET6:
((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons(port);
break;
default:
freeaddrinfo(res);
ami_error(ami, "address for host %s not valid\n", hostname);
ami_cleanup(ami);
goto cleanup;
}

if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
freeaddrinfo(res);
ami_error(ami, "connect failed: %s\n", strerror(errno));
ami_cleanup(ami);
goto cleanup;
}

freeaddrinfo(res);
} else {
ami_error(ami, "host %s not valid\n", hostname);
ami_cleanup(ami);
goto cleanup;
}
}
ami->ami_callback = callback;
ami->disconnected_callback = dis_callback;
Expand All @@ -438,18 +488,37 @@ struct ami_session *ami_connect(const char *hostname, int port, void (*callback)
pthread_mutexattr_destroy(&attr);
}

if (pthread_create(&ami->ami_thread, NULL, ami_loop, ami)) {
ami_error(ami, "Unable to create AMI thread: %s\n", strerror(errno));
ami_cleanup(ami);
goto cleanup;
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);
ret = pthread_create(&ami->ami_thread, &attr, ami_loop, ami);
pthread_attr_destroy(&attr);
if (ret) {
ami_error(ami, "Unable to create AMI thread: %s\n", strerror(errno));
ami_cleanup(ami);
goto cleanup;
}
}
if (pthread_create(&ami->dispatch_thread, NULL, ami_event_dispatch, ami)) {
ami_error(ami, "Unable to create dispatch thread: %s\n", strerror(errno));
ami_cleanup(ami);
goto cleanup;

{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);
ret = pthread_create(&ami->dispatch_thread, &attr, ami_event_dispatch, ami);
pthread_attr_destroy(&attr);
if (ret) {
ami_error(ami, "Unable to create dispatch thread: %s\n", strerror(errno));
ami_cleanup(ami);
goto cleanup;
}
}

pthread_mutex_unlock(&ami->ami_read_lock);

/* establish the initial per-session debug fd and level */
ami->debugfd = -1;
ami->debug_level = 0;
return ami;

cleanup:
Expand Down Expand Up @@ -506,20 +575,28 @@ void ami_destroy(struct ami_session *ami)

void ami_set_debug(struct ami_session *ami, int fd)
{
ami->debugfd = fd;
if (ami) {
ami->debugfd = fd;
} else {
ami_initial_debugfd = fd;
}
}

int ami_set_debug_level(struct ami_session *ami, int level)
{
int old_level = ami->debug_level;
int old_level = ami ? ami->debug_level : ami_initial_debug_level;
if (level < 0 || level > 10) {
return -1;
}
ami->debug_level = level;
if (ami) {
ami->debug_level = level;
} else {
ami_initial_debug_level = level;
}
return old_level;
}

static int __attribute__ ((format (gnu_printf, 4, 5))) __ami_send(struct ami_session *ami, va_list ap, const char *fmt, const char *prefmt, ...)
static int __attribute__ ((format (printf, 3, 0))) __attribute__ ((format (printf, 4, 5))) __ami_send(struct ami_session *ami, va_list ap, const char *fmt, const char *prefmt, ...)
{
int res = 0;
int bytes = 0;
Expand Down Expand Up @@ -1040,7 +1117,7 @@ static int ami_wait_for_response(struct ami_session *ami, int msgid)
}
}

static int ami_send(struct ami_session *ami, const char *action, const char *fmt, ...)
static int __attribute__ ((format (printf, 3, 4))) ami_send(struct ami_session *ami, const char *action, const char *fmt, ...)
{
int res;

Expand All @@ -1053,7 +1130,7 @@ static int ami_send(struct ami_session *ami, const char *action, const char *fmt
return res;
}

struct ami_response *ami_action(struct ami_session *ami, const char *action, const char *fmt, ...)
struct ami_response * __attribute__ ((format (printf, 3, 4))) ami_action(struct ami_session *ami, const char *action, const char *fmt, ...)
{
struct ami_response *resp = NULL;
/* Remember: no trailing \r\n in fmt !*/
Expand Down
3 changes: 2 additions & 1 deletion include/cami.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ struct ami_response {

/*!
* \brief Enable debug logging
* \param ami
* \param ami The AMI session. If NULL, sets the file descriptor for debug logging prior to session creation (e.g. in ami_connect)
* \param fd File descriptor to which optional debug log messages should be delivered. Default is off (-1)
* \note This is not recommended for use in production, but may be helpful in a dev environment.
*/
Expand All @@ -59,6 +59,7 @@ void ami_set_debug(struct ami_session *ami, int fd);
/*!
* \brief Set debug logging level
* \param ami
* \param ami The AMI session. If NULL, sets the debug level prior to session creation (e.g. in ami_connect)
* \param level Level between 0 and 10. 0 will disable logging, 10 is the most granular. Default is 0.
* \note A log level of 1 is recommended for production use: this will log all errors and warnings. Use a greater log level for debugging.
* \retval -1 on failure, non-negative old log level otherwise
Expand Down
9 changes: 7 additions & 2 deletions simpleami.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
static void simple_callback(struct ami_session *ami, struct ami_event *event)
{
const char *eventname = ami_keyvalue(event, "Event");
(void) ami;

printf("(Callback) Event Received: %s\n", eventname);
#if 0
/* Or, you could print out the entire event contents for debugging, or to see what's there: */
Expand All @@ -57,8 +59,8 @@ static int simple_ami(const char *hostname, const char *username, const char *pa
struct ami_session *ami;
struct ami_response *resp = NULL;
#if 0
ami_set_debug(STDERR_FILENO); /* Not recommended for daemon programs */
ami_set_debug_level(1);
ami_set_debug(NULL, STDERR_FILENO); /* Not recommended for daemon programs */
ami_set_debug_level(NULL, 1);
#endif
ami = ami_connect(hostname, 0, simple_callback, simple_disconnect_callback);
if (!ami) {
Expand Down Expand Up @@ -95,6 +97,9 @@ static int simple_ami(const char *hostname, const char *username, const char *pa

int main(int argc,char *argv[])
{
(void) argc;
(void) argv;

if (simple_ami("127.0.0.1", "test", "test")) {
exit(EXIT_FAILURE);
}
Expand Down

0 comments on commit cd2a05a

Please sign in to comment.