From 215775da59d0774d1d045839444a0a23d7633cec Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 22 Jun 2016 18:15:58 -0700 Subject: [PATCH 01/13] librb: Add smart-pointer macro for RAII resource management. A good practice, especially for large community projects, preventing leaks and error handling as clean possible without exceptions. --- librb/include/rb_memory.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/librb/include/rb_memory.h b/librb/include/rb_memory.h index 222b5c8d9..8c9f4a0bf 100644 --- a/librb/include/rb_memory.h +++ b/librb/include/rb_memory.h @@ -32,6 +32,8 @@ #include +#define RB_UNIQUE_PTR(deleter) __attribute__((cleanup(deleter))) +#define RB_AUTO_PTR RB_UNIQUE_PTR(rb_raii_free) void rb_outofmemory(void) __attribute__((noreturn)); @@ -82,4 +84,18 @@ rb_free(void *ptr) free(ptr); } + +static inline void +rb_raii_free(const void *const ptr) +{ + if(!ptr) + return; + + const void *const _ptr = *(const void **)ptr; + if(!_ptr) + return; + + rb_free((void *)_ptr); +} + #endif /* _I_MEMORY_H */ From 3073c2fa3ac47919f83fdf4465a443d9e4716f0a Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 22 Jun 2016 18:23:24 -0700 Subject: [PATCH 02/13] ircd: Add slog(), a log function that takes both an L_ argument and a SNO_ argument to smash the pattern of having an ilog() followed by sendto_realops_snomask() with the same message. --- include/logger.h | 3 +++ ircd/logger.c | 56 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/include/logger.h b/include/logger.h index faa892705..6a0f7dc07 100644 --- a/include/logger.h +++ b/include/logger.h @@ -63,4 +63,7 @@ extern void report_operspy(struct Client *, const char *, const char *); extern const char *smalldate(time_t); extern void ilog_error(const char *); +void vslog(ilogfile dest, unsigned int snomask, const char *fmt, va_list ap); +void slog(ilogfile dest, unsigned int snomask, const char *fmt, ...) AFP(3, 4); + #endif diff --git a/ircd/logger.c b/ircd/logger.c index 3b81acd71..b8c0816bc 100644 --- a/ircd/logger.c +++ b/ircd/logger.c @@ -163,34 +163,39 @@ close_logfiles(void) } } -void -ilog(ilogfile dest, const char *format, ...) -{ - FILE *logfile = *log_table[dest].logfile; - char buf[BUFSIZE]; - char buf2[BUFSIZE]; - va_list args; - if(logfile == NULL) +static void +log_va(const ilogfile dest, const unsigned int snomask, const char *const fmt, va_list ap) +{ + FILE *const logfile = *log_table[dest].logfile; + if(!logfile && !snomask) return; - va_start(args, format); - vsnprintf(buf, sizeof(buf), format, args); - va_end(args); + char buf[BUFSIZE]; + vsnprintf(buf, sizeof(buf), fmt, ap); - snprintf(buf2, sizeof(buf2), "%s %s\n", - smalldate(rb_current_time()), buf); + if(snomask) + sendto_realops_snomask(snomask, L_ALL, "%s", buf); - if(fputs(buf2, logfile) < 0) + if(fprintf(logfile, "%s %s\n", smalldate(rb_current_time()), buf) < 0) { fclose(logfile); *log_table[dest].logfile = NULL; - return; + } else { + fflush(logfile); } - - fflush(logfile); } +void +ilog(ilogfile dest, const char *format, ...) +{ + va_list args; + va_start(args, format); + log_va(dest, 0, format, args); + va_end(args); +} + + static void _iprint(const char *domain, const char *buf) { @@ -309,3 +314,20 @@ ilog_error(const char *error) sendto_realops_snomask(SNO_DEBUG, L_ALL, "%s: %d (%s)", error, e, errstr); } + + +void +vslog(const ilogfile dest, const unsigned int snomask, const char *const fmt, va_list ap) +{ + log_va(dest, snomask, fmt, ap); +} + + +void +slog(const ilogfile dest, const unsigned int snomask, const char *const fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vslog(dest, snomask, fmt, ap); + va_end(ap); +} From 4ad6696403d77ac6a6051dcca256ae7b6e5b82c8 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Thu, 23 Jun 2016 15:27:50 -0700 Subject: [PATCH 03/13] librb: fork an rb_dlink.h out of rb_tools.h --- librb/include/rb_dlink.h | 338 +++++++++++++++++++++++++++++++++++++++ librb/include/rb_lib.h | 1 + librb/include/rb_tools.h | 305 ----------------------------------- 3 files changed, 339 insertions(+), 305 deletions(-) create mode 100644 librb/include/rb_dlink.h diff --git a/librb/include/rb_dlink.h b/librb/include/rb_dlink.h new file mode 100644 index 000000000..99118fdd1 --- /dev/null +++ b/librb/include/rb_dlink.h @@ -0,0 +1,338 @@ +/* + * ircd-ratbox: A slightly useful ircd. + * tools.h: Header for the various tool functions. + * + * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center + * Copyright (C) 1996-2002 Hybrid Development Team + * Copyright (C) 2002-2005 ircd-ratbox development team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#ifndef RB_LIB_H +# error "Do not use tools.h directly" +#endif + +#ifndef __DLINK_H__ +#define __DLINK_H__ + +/* + * double-linked-list stuff + */ +typedef struct _rb_dlink_node rb_dlink_node; +typedef struct _rb_dlink_list rb_dlink_list; + +struct _rb_dlink_node +{ + void *data; + rb_dlink_node *prev; + rb_dlink_node *next; + +}; + +struct _rb_dlink_list +{ + rb_dlink_node *head; + rb_dlink_node *tail; + unsigned long length; +}; + +rb_dlink_node *rb_make_rb_dlink_node(void); +void rb_free_rb_dlink_node(rb_dlink_node *lp); +void rb_init_rb_dlink_nodes(size_t dh_size); + +/* This macros are basically swiped from the linux kernel + * they are simple yet effective + */ + +/* + * Walks forward of a list. + * pos is your node + * head is your list head + */ +#define RB_DLINK_FOREACH(pos, head) for (pos = (head); pos != NULL; pos = pos->next) + +/* + * Walks forward of a list safely while removing nodes + * pos is your node + * n is another list head for temporary storage + * head is your list head + */ +#define RB_DLINK_FOREACH_SAFE(pos, n, head) for (pos = (head), n = pos ? pos->next : NULL; pos != NULL; pos = n, n = pos ? pos->next : NULL) + +#define RB_DLINK_FOREACH_PREV(pos, head) for (pos = (head); pos != NULL; pos = pos->prev) + + +/* Returns the list length */ +#define rb_dlink_list_length(list) (list)->length + +#define rb_dlinkAddAlloc(data, list) rb_dlinkAdd(data, rb_make_rb_dlink_node(), list) +#define rb_dlinkAddTailAlloc(data, list) rb_dlinkAddTail(data, rb_make_rb_dlink_node(), list) +#define rb_dlinkDestroy(node, list) do { rb_dlinkDelete(node, list); rb_free_rb_dlink_node(node); } while(0) + + +/* + * dlink_ routines are stolen from squid, except for rb_dlinkAddBefore, + * which is mine. + * -- adrian + */ + +static inline void +rb_dlinkMoveNode(rb_dlink_node *m, rb_dlink_list *oldlist, rb_dlink_list *newlist) +{ + /* Assumption: If m->next == NULL, then list->tail == m + * and: If m->prev == NULL, then list->head == m + */ + assert(m != NULL); + assert(oldlist != NULL); + assert(newlist != NULL); + + if(m->next) + m->next->prev = m->prev; + else + oldlist->tail = m->prev; + + if(m->prev) + m->prev->next = m->next; + else + oldlist->head = m->next; + + m->prev = NULL; + m->next = newlist->head; + if(newlist->head != NULL) + newlist->head->prev = m; + else if(newlist->tail == NULL) + newlist->tail = m; + newlist->head = m; + + oldlist->length--; + newlist->length++; +} + +static inline void +rb_dlinkAdd(void *data, rb_dlink_node *m, rb_dlink_list *list) +{ + assert(data != NULL); + assert(m != NULL); + assert(list != NULL); + + m->data = data; + m->prev = NULL; + m->next = list->head; + + /* Assumption: If list->tail != NULL, list->head != NULL */ + if(list->head != NULL) + list->head->prev = m; + else if(list->tail == NULL) + list->tail = m; + + list->head = m; + list->length++; +} + +static inline void +rb_dlinkAddBefore(rb_dlink_node *b, void *data, rb_dlink_node *m, rb_dlink_list *list) +{ + assert(b != NULL); + assert(data != NULL); + assert(m != NULL); + assert(list != NULL); + + /* Shortcut - if its the first one, call rb_dlinkAdd only */ + if(b == list->head) + { + rb_dlinkAdd(data, m, list); + } + else + { + m->data = data; + b->prev->next = m; + m->prev = b->prev; + b->prev = m; + m->next = b; + list->length++; + } +} + +static inline void +rb_dlinkMoveTail(rb_dlink_node *m, rb_dlink_list *list) +{ + if(list->tail == m) + return; + + /* From here assume that m->next != NULL as that can only + * be at the tail and assume that the node is on the list + */ + + m->next->prev = m->prev; + + if(m->prev != NULL) + m->prev->next = m->next; + else + list->head = m->next; + + list->tail->next = m; + m->prev = list->tail; + m->next = NULL; + list->tail = m; +} + +static inline void +rb_dlinkAddTail(void *data, rb_dlink_node *m, rb_dlink_list *list) +{ + assert(m != NULL); + assert(list != NULL); + assert(data != NULL); + + m->data = data; + m->next = NULL; + m->prev = list->tail; + + /* Assumption: If list->tail != NULL, list->head != NULL */ + if(list->tail != NULL) + list->tail->next = m; + else if(list->head == NULL) + list->head = m; + + list->tail = m; + list->length++; +} + +/* Execution profiles show that this function is called the most + * often of all non-spontaneous functions. So it had better be + * efficient. */ +static inline void +rb_dlinkDelete(rb_dlink_node *m, rb_dlink_list *list) +{ + assert(m != NULL); + assert(list != NULL); + /* Assumption: If m->next == NULL, then list->tail == m + * and: If m->prev == NULL, then list->head == m + */ + if(m->next) + m->next->prev = m->prev; + else + list->tail = m->prev; + + if(m->prev) + m->prev->next = m->next; + else + list->head = m->next; + + m->next = m->prev = NULL; + list->length--; +} + +static inline rb_dlink_node * +rb_dlinkFindDelete(void *data, rb_dlink_list *list) +{ + rb_dlink_node *m; + assert(list != NULL); + assert(data != NULL); + RB_DLINK_FOREACH(m, list->head) + { + if(m->data != data) + continue; + + if(m->next) + m->next->prev = m->prev; + else + list->tail = m->prev; + + if(m->prev) + m->prev->next = m->next; + else + list->head = m->next; + + m->next = m->prev = NULL; + list->length--; + return m; + } + return NULL; +} + +static inline int +rb_dlinkFindDestroy(void *data, rb_dlink_list *list) +{ + void *ptr; + + assert(list != NULL); + assert(data != NULL); + ptr = rb_dlinkFindDelete(data, list); + + if(ptr != NULL) + { + rb_free_rb_dlink_node(ptr); + return 1; + } + return 0; +} + +/* + * rb_dlinkFind + * inputs - list to search + * - data + * output - pointer to link or NULL if not found + * side effects - Look for ptr in the linked listed pointed to by link. + */ +static inline rb_dlink_node * +rb_dlinkFind(void *data, rb_dlink_list *list) +{ + rb_dlink_node *ptr; + assert(list != NULL); + assert(data != NULL); + + RB_DLINK_FOREACH(ptr, list->head) + { + if(ptr->data == data) + return (ptr); + } + return (NULL); +} + +static inline void +rb_dlinkMoveList(rb_dlink_list *from, rb_dlink_list *to) +{ + assert(from != NULL); + assert(to != NULL); + + /* There are three cases */ + /* case one, nothing in from list */ + if(from->head == NULL) + return; + + /* case two, nothing in to list */ + if(to->head == NULL) + { + to->head = from->head; + to->tail = from->tail; + from->head = from->tail = NULL; + to->length = from->length; + from->length = 0; + return; + } + + /* third case play with the links */ + from->tail->next = to->head; + to->head->prev = from->tail; + to->head = from->head; + from->head = from->tail = NULL; + to->length += from->length; + from->length = 0; +} + +#endif /* __DLINK_H__ */ diff --git a/librb/include/rb_lib.h b/librb/include/rb_lib.h index 2a817408b..699ef2723 100644 --- a/librb/include/rb_lib.h +++ b/librb/include/rb_lib.h @@ -248,6 +248,7 @@ pid_t rb_getpid(void); #include +#include #include #include #include diff --git a/librb/include/rb_tools.h b/librb/include/rb_tools.h index 26186763d..14231555a 100644 --- a/librb/include/rb_tools.h +++ b/librb/include/rb_tools.h @@ -49,311 +49,6 @@ char *rb_dirname(const char *); int rb_string_to_array(char *string, char **parv, int maxpara); -/* - * double-linked-list stuff - */ -typedef struct _rb_dlink_node rb_dlink_node; -typedef struct _rb_dlink_list rb_dlink_list; - -struct _rb_dlink_node -{ - void *data; - rb_dlink_node *prev; - rb_dlink_node *next; - -}; - -struct _rb_dlink_list -{ - rb_dlink_node *head; - rb_dlink_node *tail; - unsigned long length; -}; - -rb_dlink_node *rb_make_rb_dlink_node(void); -void rb_free_rb_dlink_node(rb_dlink_node *lp); -void rb_init_rb_dlink_nodes(size_t dh_size); - -/* This macros are basically swiped from the linux kernel - * they are simple yet effective - */ - -/* - * Walks forward of a list. - * pos is your node - * head is your list head - */ -#define RB_DLINK_FOREACH(pos, head) for (pos = (head); pos != NULL; pos = pos->next) - -/* - * Walks forward of a list safely while removing nodes - * pos is your node - * n is another list head for temporary storage - * head is your list head - */ -#define RB_DLINK_FOREACH_SAFE(pos, n, head) for (pos = (head), n = pos ? pos->next : NULL; pos != NULL; pos = n, n = pos ? pos->next : NULL) - -#define RB_DLINK_FOREACH_PREV(pos, head) for (pos = (head); pos != NULL; pos = pos->prev) - - -/* Returns the list length */ -#define rb_dlink_list_length(list) (list)->length - -#define rb_dlinkAddAlloc(data, list) rb_dlinkAdd(data, rb_make_rb_dlink_node(), list) -#define rb_dlinkAddTailAlloc(data, list) rb_dlinkAddTail(data, rb_make_rb_dlink_node(), list) -#define rb_dlinkDestroy(node, list) do { rb_dlinkDelete(node, list); rb_free_rb_dlink_node(node); } while(0) - - -/* - * dlink_ routines are stolen from squid, except for rb_dlinkAddBefore, - * which is mine. - * -- adrian - */ - -static inline void -rb_dlinkMoveNode(rb_dlink_node *m, rb_dlink_list *oldlist, rb_dlink_list *newlist) -{ - /* Assumption: If m->next == NULL, then list->tail == m - * and: If m->prev == NULL, then list->head == m - */ - assert(m != NULL); - assert(oldlist != NULL); - assert(newlist != NULL); - - if(m->next) - m->next->prev = m->prev; - else - oldlist->tail = m->prev; - - if(m->prev) - m->prev->next = m->next; - else - oldlist->head = m->next; - - m->prev = NULL; - m->next = newlist->head; - if(newlist->head != NULL) - newlist->head->prev = m; - else if(newlist->tail == NULL) - newlist->tail = m; - newlist->head = m; - - oldlist->length--; - newlist->length++; -} - -static inline void -rb_dlinkAdd(void *data, rb_dlink_node *m, rb_dlink_list *list) -{ - assert(data != NULL); - assert(m != NULL); - assert(list != NULL); - - m->data = data; - m->prev = NULL; - m->next = list->head; - - /* Assumption: If list->tail != NULL, list->head != NULL */ - if(list->head != NULL) - list->head->prev = m; - else if(list->tail == NULL) - list->tail = m; - - list->head = m; - list->length++; -} - -static inline void -rb_dlinkAddBefore(rb_dlink_node *b, void *data, rb_dlink_node *m, rb_dlink_list *list) -{ - assert(b != NULL); - assert(data != NULL); - assert(m != NULL); - assert(list != NULL); - - /* Shortcut - if its the first one, call rb_dlinkAdd only */ - if(b == list->head) - { - rb_dlinkAdd(data, m, list); - } - else - { - m->data = data; - b->prev->next = m; - m->prev = b->prev; - b->prev = m; - m->next = b; - list->length++; - } -} - -static inline void -rb_dlinkMoveTail(rb_dlink_node *m, rb_dlink_list *list) -{ - if(list->tail == m) - return; - - /* From here assume that m->next != NULL as that can only - * be at the tail and assume that the node is on the list - */ - - m->next->prev = m->prev; - - if(m->prev != NULL) - m->prev->next = m->next; - else - list->head = m->next; - - list->tail->next = m; - m->prev = list->tail; - m->next = NULL; - list->tail = m; -} - -static inline void -rb_dlinkAddTail(void *data, rb_dlink_node *m, rb_dlink_list *list) -{ - assert(m != NULL); - assert(list != NULL); - assert(data != NULL); - - m->data = data; - m->next = NULL; - m->prev = list->tail; - - /* Assumption: If list->tail != NULL, list->head != NULL */ - if(list->tail != NULL) - list->tail->next = m; - else if(list->head == NULL) - list->head = m; - - list->tail = m; - list->length++; -} - -/* Execution profiles show that this function is called the most - * often of all non-spontaneous functions. So it had better be - * efficient. */ -static inline void -rb_dlinkDelete(rb_dlink_node *m, rb_dlink_list *list) -{ - assert(m != NULL); - assert(list != NULL); - /* Assumption: If m->next == NULL, then list->tail == m - * and: If m->prev == NULL, then list->head == m - */ - if(m->next) - m->next->prev = m->prev; - else - list->tail = m->prev; - - if(m->prev) - m->prev->next = m->next; - else - list->head = m->next; - - m->next = m->prev = NULL; - list->length--; -} - -static inline rb_dlink_node * -rb_dlinkFindDelete(void *data, rb_dlink_list *list) -{ - rb_dlink_node *m; - assert(list != NULL); - assert(data != NULL); - RB_DLINK_FOREACH(m, list->head) - { - if(m->data != data) - continue; - - if(m->next) - m->next->prev = m->prev; - else - list->tail = m->prev; - - if(m->prev) - m->prev->next = m->next; - else - list->head = m->next; - - m->next = m->prev = NULL; - list->length--; - return m; - } - return NULL; -} - -static inline int -rb_dlinkFindDestroy(void *data, rb_dlink_list *list) -{ - void *ptr; - - assert(list != NULL); - assert(data != NULL); - ptr = rb_dlinkFindDelete(data, list); - - if(ptr != NULL) - { - rb_free_rb_dlink_node(ptr); - return 1; - } - return 0; -} - -/* - * rb_dlinkFind - * inputs - list to search - * - data - * output - pointer to link or NULL if not found - * side effects - Look for ptr in the linked listed pointed to by link. - */ -static inline rb_dlink_node * -rb_dlinkFind(void *data, rb_dlink_list *list) -{ - rb_dlink_node *ptr; - assert(list != NULL); - assert(data != NULL); - - RB_DLINK_FOREACH(ptr, list->head) - { - if(ptr->data == data) - return (ptr); - } - return (NULL); -} - -static inline void -rb_dlinkMoveList(rb_dlink_list *from, rb_dlink_list *to) -{ - assert(from != NULL); - assert(to != NULL); - - /* There are three cases */ - /* case one, nothing in from list */ - if(from->head == NULL) - return; - - /* case two, nothing in to list */ - if(to->head == NULL) - { - to->head = from->head; - to->tail = from->tail; - from->head = from->tail = NULL; - to->length = from->length; - from->length = 0; - return; - } - - /* third case play with the links */ - from->tail->next = to->head; - to->head->prev = from->tail; - to->head = from->head; - from->head = from->tail = NULL; - to->length += from->length; - from->length = 0; -} - typedef struct _rb_zstring { uint16_t len; /* big enough */ From 050e36b369a6f880d3590517e0c1fe047a9d5e4c Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Thu, 23 Jun 2016 16:18:05 -0700 Subject: [PATCH 04/13] librb: Dress some memory functions with attributes. --- librb/include/rb_memory.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/librb/include/rb_memory.h b/librb/include/rb_memory.h index 8c9f4a0bf..303365d7b 100644 --- a/librb/include/rb_memory.h +++ b/librb/include/rb_memory.h @@ -37,6 +37,11 @@ void rb_outofmemory(void) __attribute__((noreturn)); +#ifdef __clang__ +__attribute__((malloc, returns_nonnull)) +#else +__attribute__((malloc, alloc_size(1), returns_nonnull)) +#endif static inline void * rb_malloc(size_t size) { @@ -46,6 +51,11 @@ rb_malloc(size_t size) return (ret); } +#ifdef __clang__ +__attribute__((returns_nonnull)) +#else +__attribute__((alloc_size(2), returns_nonnull)) +#endif static inline void * rb_realloc(void *x, size_t y) { @@ -56,6 +66,7 @@ rb_realloc(void *x, size_t y) return (ret); } +__attribute__((returns_nonnull)) static inline char * rb_strndup(const char *x, size_t y) { @@ -66,6 +77,7 @@ rb_strndup(const char *x, size_t y) return (ret); } +__attribute__((returns_nonnull)) static inline char * rb_strdup(const char *x) { From c7529ba5bf3286548c95b3465fe11ca717be8869 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Thu, 23 Jun 2016 16:28:36 -0700 Subject: [PATCH 05/13] librb: Relax rb_free() to allow const pointers. --- librb/include/rb_memory.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/librb/include/rb_memory.h b/librb/include/rb_memory.h index 303365d7b..7c0ac969a 100644 --- a/librb/include/rb_memory.h +++ b/librb/include/rb_memory.h @@ -88,14 +88,17 @@ rb_strdup(const char *x) return (ret); } - +// overrule libc's prototype and ignore the cast warning, allowing proper const +// propagation without casting everywhere in the real code. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" static inline void -rb_free(void *ptr) +rb_free(const void *const ptr) { if(rb_likely(ptr != NULL)) - free(ptr); + free((void *)ptr); } - +#pragma GCC diagnostic pop static inline void rb_raii_free(const void *const ptr) @@ -103,11 +106,11 @@ rb_raii_free(const void *const ptr) if(!ptr) return; - const void *const _ptr = *(const void **)ptr; + const void *const _ptr = *(const void *const *)ptr; if(!_ptr) return; - rb_free((void *)_ptr); + rb_free(_ptr); } #endif /* _I_MEMORY_H */ From 14cd76b4359d9568cd20f231876984ab85a10191 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Thu, 23 Jun 2016 16:33:35 -0700 Subject: [PATCH 06/13] librb: Suppress a ubiquitous strict-prototypes warning with anecdote. --- librb/include/rb_dictionary.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/librb/include/rb_dictionary.h b/librb/include/rb_dictionary.h index 2baf027d4..9a84ceed4 100644 --- a/librb/include/rb_dictionary.h +++ b/librb/include/rb_dictionary.h @@ -33,7 +33,12 @@ typedef struct rb_dictionary_iter rb_dictionary_iter; struct rb_dictionary; +// This comparator could be based on a union of function types emulating a +// quasi-template, shutting up a lot of warnings. For now it gets The Treatment. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" typedef int (*DCF)(/* const void *a, const void *b */); +#pragma GCC diagnostic pop struct rb_dictionary_element { From c9f0354dbcff5f1c8455343b65dce64670ad62c5 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Fri, 24 Jun 2016 22:27:33 -0700 Subject: [PATCH 07/13] librb: Add array_to_string() to invert string_to_array(). --- librb/include/rb_tools.h | 1 + librb/src/export-syms.txt | 1 + librb/src/tools.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/librb/include/rb_tools.h b/librb/include/rb_tools.h index 14231555a..bc6757307 100644 --- a/librb/include/rb_tools.h +++ b/librb/include/rb_tools.h @@ -48,6 +48,7 @@ char *rb_basename(const char *); char *rb_dirname(const char *); int rb_string_to_array(char *string, char **parv, int maxpara); +size_t rb_array_to_string(int parc, const char **parv, char *buf, size_t max); typedef struct _rb_zstring { diff --git a/librb/src/export-syms.txt b/librb/src/export-syms.txt index 5e3253f8f..062d782c0 100644 --- a/librb/src/export-syms.txt +++ b/librb/src/export-syms.txt @@ -174,6 +174,7 @@ rb_strcasecmp rb_strcasestr rb_strerror rb_string_to_array +rb_array_to_string rb_strlcat rb_strlcpy rb_strncasecmp diff --git a/librb/src/tools.c b/librb/src/tools.c index 17a9b45c8..42dcb9e9e 100644 --- a/librb/src/tools.c +++ b/librb/src/tools.c @@ -138,6 +138,36 @@ rb_string_to_array(char *string, char **parv, int maxpara) return x; } + +/* rb_array_to_string() + * Inverse of rb_string_to_array() + * - space for delim + * - ':' prefixed at last parameter + */ +size_t +rb_array_to_string(int parc, const char **parv, char *buf, size_t max) +{ + size_t ret = 0; + if(rb_unlikely(!max)) + return ret; + + buf[0] = '\0'; + for(int i = 0; i < parc - 1; ++i) + { + ret = rb_strlcat(buf, parv[i], max); + ret = rb_strlcat(buf, " ", max); + } + + if(parc) + { + ret = rb_strlcat(buf, ":", max); + ret = rb_strlcat(buf, parv[parc - 1], max); + } + + return ret; +} + + #ifndef HAVE_STRCASECMP #ifndef _WIN32 /* Fallback taken from FreeBSD. --Elizafox */ From 1490dd36b58a359040ded16330fff7bc41662896 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sun, 26 Jun 2016 18:31:04 -0700 Subject: [PATCH 08/13] librb: Add missing rb_dictionary.h to rb_lib.h list. --- librb/include/rb_lib.h | 1 + 1 file changed, 1 insertion(+) diff --git a/librb/include/rb_lib.h b/librb/include/rb_lib.h index 699ef2723..3d9a7147b 100644 --- a/librb/include/rb_lib.h +++ b/librb/include/rb_lib.h @@ -257,5 +257,6 @@ pid_t rb_getpid(void); #include #include #include +#include #endif From c5698b02769f1efbf163d0fcc9e150fcf42bac82 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Mon, 27 Jun 2016 15:21:23 -0700 Subject: [PATCH 09/13] librb: Move bool type stuff from ircd stdinc to rb. --- include/stdinc.h | 17 ----------------- librb/include/rb_lib.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/stdinc.h b/include/stdinc.h index d620f7799..ccae3f813 100644 --- a/include/stdinc.h +++ b/include/stdinc.h @@ -70,23 +70,6 @@ char *alloca (); #endif -#ifdef HAVE_STDBOOL_H -# include -#else -# ifndef HAVE__BOOL -# ifdef __cplusplus -typedef bool _Bool; -# else -# define _Bool signed char -# endif -# endif -# define bool _Bool -# define false 0 -# define true 1 -# define __bool_true_false_are_defined 1 -#endif - - #include #include #include diff --git a/librb/include/rb_lib.h b/librb/include/rb_lib.h index 3d9a7147b..e8d9ac5b0 100644 --- a/librb/include/rb_lib.h +++ b/librb/include/rb_lib.h @@ -10,6 +10,24 @@ #include #include + +#ifdef HAVE_STDBOOL_H + #include +#else + #ifndef HAVE__BOOL + #ifdef __cplusplus + typedef bool _Bool; + #else + #define _Bool signed char + #endif + #endif + #define bool _Bool + #define false 0 + #define true 1 + #define __bool_true_false_are_defined 1 +#endif + + #ifdef __GNUC__ #undef alloca #define alloca __builtin_alloca From 5a0016ccbd505b3aa778217f6d6e93a9f6fb7c38 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Mon, 27 Jun 2016 15:23:34 -0700 Subject: [PATCH 10/13] librb: Minor cleanup of rb_dlink.h Add prototypes; some macro cleanup. --- librb/include/rb_dlink.h | 74 +++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/librb/include/rb_dlink.h b/librb/include/rb_dlink.h index 99118fdd1..e793ae6d9 100644 --- a/librb/include/rb_dlink.h +++ b/librb/include/rb_dlink.h @@ -24,65 +24,67 @@ */ #ifndef RB_LIB_H -# error "Do not use tools.h directly" + #error "Do not use rb_dlink.h directly" #endif #ifndef __DLINK_H__ #define __DLINK_H__ -/* - * double-linked-list stuff - */ -typedef struct _rb_dlink_node rb_dlink_node; -typedef struct _rb_dlink_list rb_dlink_list; -struct _rb_dlink_node +typedef struct rb_dlink_node { void *data; - rb_dlink_node *prev; - rb_dlink_node *next; + struct rb_dlink_node *prev; + struct rb_dlink_node *next; +} +rb_dlink_node; -}; - -struct _rb_dlink_list +typedef struct rb_dlink_list { rb_dlink_node *head; rb_dlink_node *tail; unsigned long length; -}; +} +rb_dlink_list; + +// Utils +#define rb_dlink_list_length(list) (list)->length + +// XXX I'd like to remove this interface, it's not safe. rb_dlink_node *rb_make_rb_dlink_node(void); void rb_free_rb_dlink_node(rb_dlink_node *lp); void rb_init_rb_dlink_nodes(size_t dh_size); +#define rb_dlinkAddAlloc(data, list) rb_dlinkAdd(data, rb_make_rb_dlink_node(), list) +#define rb_dlinkAddTailAlloc(data, list) rb_dlinkAddTail(data, rb_make_rb_dlink_node(), list) +#define rb_dlinkDestroy(node, list) do { rb_dlinkDelete(node, list); rb_free_rb_dlink_node(node); } while(0) + +// Vintage +static void rb_dlinkAdd(void *data, rb_dlink_node *m, rb_dlink_list *list); +static void rb_dlinkAddBefore(rb_dlink_node *b, void *data, rb_dlink_node *m, rb_dlink_list *list); +static void rb_dlinkAddTail(void *data, rb_dlink_node *m, rb_dlink_list *list); +static rb_dlink_node *rb_dlinkFindDelete(void *data, rb_dlink_list *list); +static int rb_dlinkFindDestroy(void *data, rb_dlink_list *list); +static rb_dlink_node *rb_dlinkFind(void *data, rb_dlink_list *list); +static void rb_dlinkMoveNode(rb_dlink_node *m, rb_dlink_list *oldlist, rb_dlink_list *newlist); +static void rb_dlinkMoveTail(rb_dlink_node *m, rb_dlink_list *list); +static void rb_dlinkDelete(rb_dlink_node *m, rb_dlink_list *list); +static void rb_dlinkMoveList(rb_dlink_list *from, rb_dlink_list *to); /* This macros are basically swiped from the linux kernel * they are simple yet effective */ +// Usage: rb_dlink_node *n; RB_DLINK_FOREACH(n, list.head) { ... } +#define RB_DLINK_FOREACH(node, head) for(node = (head); node != NULL; node = node->next) +#define RB_DLINK_FOREACH_PREV(node, head) for(node = (head); node != NULL; node = node->prev) -/* - * Walks forward of a list. - * pos is your node - * head is your list head - */ -#define RB_DLINK_FOREACH(pos, head) for (pos = (head); pos != NULL; pos = pos->next) +// Allows for modifying the list during iteration. +// Usage: rb_dlink_node *cur, *nxt; RB_DLINK_FOREACH_SAFE(cur, nxt, list.head) { ... } +#define RB_DLINK_FOREACH_SAFE(cur, nxt, head) \ + for(cur = (head), nxt = cur? cur->next : NULL; cur != NULL; cur = nxt, nxt = cur? cur->next : NULL) -/* - * Walks forward of a list safely while removing nodes - * pos is your node - * n is another list head for temporary storage - * head is your list head - */ -#define RB_DLINK_FOREACH_SAFE(pos, n, head) for (pos = (head), n = pos ? pos->next : NULL; pos != NULL; pos = n, n = pos ? pos->next : NULL) - -#define RB_DLINK_FOREACH_PREV(pos, head) for (pos = (head); pos != NULL; pos = pos->prev) - - -/* Returns the list length */ -#define rb_dlink_list_length(list) (list)->length - -#define rb_dlinkAddAlloc(data, list) rb_dlinkAdd(data, rb_make_rb_dlink_node(), list) -#define rb_dlinkAddTailAlloc(data, list) rb_dlinkAddTail(data, rb_make_rb_dlink_node(), list) -#define rb_dlinkDestroy(node, list) do { rb_dlinkDelete(node, list); rb_free_rb_dlink_node(node); } while(0) +#define RB_DLINK_FOREACH_PREV_SAFE(cur, pre, head) \ + for(cur = (head), pre = cur? cur->prev : NULL; cur != NULL; cur = pre, pre = cur? cur->prev : NULL) /* From ba7cb82ab3d123b23ca5bf10056d22b3f6881a02 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Tue, 28 Jun 2016 15:45:24 -0700 Subject: [PATCH 11/13] librb: Add backtrace support and trace on assertions. --- librb/configure.ac | 2 +- librb/include/rb_lib.h | 21 ++++++++---- librb/src/export-syms.txt | 3 ++ librb/src/rb_lib.c | 70 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/librb/configure.ac b/librb/configure.ac index 0f5f3f593..2556a9644 100644 --- a/librb/configure.ac +++ b/librb/configure.ac @@ -104,7 +104,7 @@ AC_TYPE_UID_T dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([crypt.h unistd.h sys/socket.h sys/stat.h sys/time.h time.h netinet/in.h netinet/tcp.h arpa/inet.h errno.h sys/uio.h spawn.h sys/poll.h sys/epoll.h sys/select.h sys/devpoll.h sys/event.h port.h signal.h sys/signalfd.h sys/timerfd.h]) +AC_CHECK_HEADERS([crypt.h unistd.h sys/socket.h sys/stat.h sys/time.h time.h netinet/in.h netinet/tcp.h arpa/inet.h errno.h sys/uio.h spawn.h sys/poll.h sys/epoll.h sys/select.h sys/devpoll.h sys/event.h port.h signal.h sys/signalfd.h sys/timerfd.h execinfo.h]) AC_HEADER_TIME dnl Networking Functions diff --git a/librb/include/rb_lib.h b/librb/include/rb_lib.h index e8d9ac5b0..dcf2edabe 100644 --- a/librb/include/rb_lib.h +++ b/librb/include/rb_lib.h @@ -146,13 +146,16 @@ char *rb_strerror(int error); #endif #ifdef __GNUC__ -#define slrb_assert(expr) do \ - if(rb_unlikely(!(expr))) { \ - rb_lib_log( \ - "file: %s line: %d (%s): Assertion failed: (%s)", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ - } \ - while(0) +#define slrb_assert(expr) do \ +{ \ + if(rb_unlikely(!(expr))) \ + { \ + rb_lib_log("file: %s line: %d (%s): Assertion failed: (%s)", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \ + rb_backtrace_log_symbols(); \ + } \ +} \ +while(0) #else #define slrb_assert(expr) do \ if(rb_unlikely(!(expr))) { \ @@ -264,6 +267,10 @@ pid_t rb_waitpid(pid_t pid, int *status, int options); pid_t rb_getpid(void); //unsigned int rb_geteuid(void); +void *const *rb_backtrace(int *len); // writes to and returns static vector (*len indicates element count) +const char *const *rb_backtrace_symbols(int *len); // translates rb_backtrace(), all static +void rb_backtrace_log_symbols(void); // rb_backtrace_symbols piped to rb_lib_log() + #include #include diff --git a/librb/src/export-syms.txt b/librb/src/export-syms.txt index 062d782c0..562d2d8ee 100644 --- a/librb/src/export-syms.txt +++ b/librb/src/export-syms.txt @@ -1,6 +1,9 @@ make_and_lookup make_and_lookup_ip rb_accept_tcp +rb_backtrace +rb_backtrace_symbols +rb_backtrace_log_symbols rb_base64_decode rb_base64_encode rb_basename diff --git a/librb/src/rb_lib.c b/librb/src/rb_lib.c index 041200f91..3d5b7b7e8 100644 --- a/librb/src/rb_lib.c +++ b/librb/src/rb_lib.c @@ -23,6 +23,11 @@ */ #include + +#ifdef HAVE_EXECINFO_H +#include +#endif + #include #include #include @@ -423,3 +428,68 @@ rb_base64_decode(const unsigned char *str, int length, int *ret) *ret = j; return result; } + + +void *bt_stack[64]; +char bt_symbuf[64][256]; +const char *bt_symbol[64]; + +#ifdef HAVE_EXECINFO_H +void *const *rb_backtrace(int *const usrlen) +{ + int len = backtrace(bt_stack, 64); + if(usrlen) *usrlen = len; + return bt_stack; +} +#else +void *const *rb_backtrace(int *const usrlen) +{ + if(usrlen) *usrlen = 0; + return bt_stack; +} +#endif + + +#ifdef HAVE_EXECINFO_H +const char *const *rb_backtrace_symbols(int *const usrlen) +{ + int len; + void *const *const stack = rb_backtrace(&len); + char *const *const symbol = backtrace_symbols(stack, len); + if(!symbol) + len = 0; + + if(usrlen) + *usrlen = len; + + for(int i = 0; i < len; i++) + { + rb_strlcpy(bt_symbuf[i], symbol[i], sizeof(bt_symbuf[i])); + bt_symbol[i] = bt_symbuf[i]; + } + + free((char **)symbol); + return bt_symbol; +} +#else +const char *const *rb_backtrace_symbols(int *const usrlen) +{ + if(usrlen) *usrlen = 0; + return bt_symbol; +} +#endif + + +#ifdef HAVE_EXECINFO_H +void rb_backtrace_log_symbols(void) +{ + int len; + const char *const *const symbol = rb_backtrace_symbols(&len); + for(int i = 0; i < len; i++) + rb_lib_log("%2d: %s", i, symbol[i]); +} +#else +void rb_backtrace_log_symbols(void) +{ +} +#endif From 97a4adae40d43fc122de7f49f5a841773eaae606 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Tue, 28 Jun 2016 01:38:58 -0700 Subject: [PATCH 12/13] ircd/librb: epoch needs a john hancock --- include/ircd.h | 2 +- include/modules.h | 2 +- ircd/version.c.SH | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ircd.h b/include/ircd.h index 28df4d5b3..f01510c7c 100644 --- a/include/ircd.h +++ b/include/ircd.h @@ -61,7 +61,7 @@ extern const char *creation; extern const char *generation; extern const char *infotext[]; extern const char *serno; -extern const unsigned long int datecode; +extern const time_t datecode; extern const char *ircd_version; extern const char *logFileName; extern const char *pidFileName; diff --git a/include/modules.h b/include/modules.h index 141d78e06..141a13aa5 100644 --- a/include/modules.h +++ b/include/modules.h @@ -109,7 +109,7 @@ struct mapi_mheader_av2 mapi_cap_list_av2 *mapi_cap_list; /* List of CAPs to add */ const char *mapi_module_version; /* Module's version (freeform), replaced with ircd version if NULL */ const char *mapi_module_description; /* Module's description (freeform) */ - unsigned long int mapi_datecode; /* Unix timestamp of module's build */ + time_t mapi_datecode; /* Unix timestamp of module's build */ }; #define DECLARE_MODULE_AV1(name, reg, unreg, cl, hl, hfnlist, v) \ diff --git a/ircd/version.c.SH b/ircd/version.c.SH index fcaad4b8d..1eccf4501 100644 --- a/ircd/version.c.SH +++ b/ircd/version.c.SH @@ -57,7 +57,7 @@ const char *generation = "$generation"; const char *creation = "$creation"; const char *ircd_version = PATCHLEVEL; const char *serno = SERNO; -const unsigned long int datecode = DATECODE; +const time_t datecode = DATECODE; const char *infotext[] = { From e8355cb7beced1291ccacd2add58e0898071b24d Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 22 Jun 2016 18:30:05 -0700 Subject: [PATCH 13/13] MAPI Version 3 This version leverages a flexible, cleaner key-value strategy reducing the need to design entire new headers for every feature addition, change, etc. * A friendly declaration for the module authors, with minimal requirements to fill in, and explicit labels of what the fields are. * Repetition of keys, removing references to (and the requirement to build) a clist, hlist and hfnlist and caplist and whatever the future holds. * Safe deterministic loading and unloading. Keys are evaluated in order, errors can be recognized, and unloading occurs in reverse order. ircd: Refactor internal half of modules.c, with some V3 additions. Provides better delegation for versions, a cleaner stack with better error handling, and some functionality deduping. V1 and V2 handlers are still somewhat unaltered, just factored in. --- include/modules.h | 60 ++- ircd/modules.c | 922 ++++++++++++++++++++++++++++------------------ 2 files changed, 629 insertions(+), 353 deletions(-) diff --git a/include/modules.h b/include/modules.h index 141a13aa5..60fad5183 100644 --- a/include/modules.h +++ b/include/modules.h @@ -38,7 +38,8 @@ struct module { - char *name; + const char *name; + const char *path; const char *version; const char *description; lt_dlhandle address; @@ -53,6 +54,7 @@ struct module #define MAPI_V1 (MAPI_MAGIC_HDR | 0x1) #define MAPI_V2 (MAPI_MAGIC_HDR | 0x2) +#define MAPI_V3 (MAPI_MAGIC_HDR | 0x3) #define MAPI_MAGIC(x) ((x) & 0xffff0000) #define MAPI_VERSION(x) ((x) & 0x0000ffff) @@ -118,6 +120,62 @@ struct mapi_mheader_av2 #define DECLARE_MODULE_AV2(name, reg, unreg, cl, hl, hfnlist, caplist, v, desc) \ struct mapi_mheader_av2 _mheader = { MAPI_V2, reg, unreg, cl, hl, hfnlist, caplist, v, desc, DATECODE} + +/*** +Version 3 modules utilize a flexible key/value vector. + +Example: +DECLARE_MODULE_AV3 +( + MOD_ATTR { "name", "mymodule" }, + MOD_ATTR { "mtab", &message_foo }, + MOD_ATTR { "mtab", &message_unfoo }, + MOD_ATTR { "init", modinitfunc }, + MOD_ATTR { "hook", MOD_HOOK { "myhook", &id } }, + MOD_ATTR { "hookfn", MOD_HOOKFN { "myhook", hookfun } }, +) + +Notes: +- Multiple keys with the same name will have different behavior depending on the logic for that key. +- On load, the order in which keys are specified is the order they will be evaluated (top to bottom). +- On unload, the evaluation is the REVERSE order (bottom to top). +- If an init function returns false, or other error occurs, no further keys are evaluated and the + unload rolls back from that point. +***/ + +struct mapi_av3_attr +{ + #define MAPI_V3_KEY_MAXLEN 16 /* Maximum length for a key string */ + + const char *key; + union { const void *cvalue; void *value; int (*init)(void); void (*fini)(void); }; +}; + +struct mapi_mheader_av3 +{ + int mapi_version; // Module API version + struct mapi_av3_attr **attrs; // A vector of attributes, NULL terminated +}; + +#define MOD_ATTR &(struct mapi_av3_attr) +#define MOD_HOOK &(mapi_hlist_av1) +#define MOD_HOOKFN &(mapi_hfn_list_av1) + +#define DECLARE_MODULE_AV3(...) \ +struct mapi_mheader_av3 _mheader = \ +{ \ + MAPI_V3, (struct mapi_av3_attr *[]) \ + { \ + MOD_ATTR { "time", DATECODE }, \ + __VA_ARGS__, \ + NULL \ + } \ +}; + + +// Prefixes your slog() message with module info +void module_log(struct module *mod, const char *fmt, ...) AFP(2, 3); + /* add a path */ void mod_add_path(const char *path); void mod_clear_paths(void); diff --git a/ircd/modules.c b/ircd/modules.c index 1d969af73..6e4b9e4fb 100644 --- a/ircd/modules.c +++ b/ircd/modules.c @@ -276,8 +276,564 @@ load_one_module(const char *path, int origin, bool coremodule) return false; } -static char unknown_ver[] = ""; -static char unknown_description[] = ""; + +void module_log(struct module *const mod, + const char *const fmt, + ...) +{ + va_list ap; + va_start(ap, fmt); + + char buf[BUFSIZE]; + vsnprintf(buf, sizeof(buf), fmt, ap), + slog(L_MAIN, SNO_GENERAL, "Module %s: %s", mod->name, buf); + + va_end(ap); +} + + + +// ****************************************************************************** +// INTERNAL API STACK +// driven by load_a_module() / unload_one_module() (bottom) + +static +bool init_module_v1(struct module *const mod) +{ + struct mapi_mheader_av1 *mheader = (struct mapi_mheader_av1 *)mod->mapi_header; + if(mheader->mapi_register && (mheader->mapi_register() == -1)) + return false; + + if(mheader->mapi_command_list) + { + struct Message **m; + for (m = mheader->mapi_command_list; *m; ++m) + mod_add_cmd(*m); + } + + if(mheader->mapi_hook_list) + { + mapi_hlist_av1 *m; + for (m = mheader->mapi_hook_list; m->hapi_name; ++m) + *m->hapi_id = register_hook(m->hapi_name); + } + + if(mheader->mapi_hfn_list) + { + mapi_hfn_list_av1 *m; + for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) + add_hook(m->hapi_name, m->fn); + } + + mod->version = mheader->mapi_module_version; + return true; +} + + +static +void fini_module_v1(struct module *const mod) +{ + struct mapi_mheader_av1 *mheader = mod->mapi_header; + if(mheader->mapi_command_list) + { + struct Message **m; + for (m = mheader->mapi_command_list; *m; ++m) + mod_del_cmd(*m); + } + + /* hook events are never removed, we simply lose the + * ability to call them --fl + */ + if(mheader->mapi_hfn_list) + { + mapi_hfn_list_av1 *m; + for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) + remove_hook(m->hapi_name, m->fn); + } + + if(mheader->mapi_unregister) + mheader->mapi_unregister(); +} + + +static +bool init_module__cap(struct module *const mod, + mapi_cap_list_av2 *const m) +{ + struct CapabilityIndex *idx; + switch(m->cap_index) + { + case MAPI_CAP_CLIENT: idx = cli_capindex; break; + case MAPI_CAP_SERVER: idx = serv_capindex; break; + default: + slog(L_MAIN, SNO_GENERAL, + "Unknown/unsupported CAP index found of type %d on capability %s when loading %s", + m->cap_index, + m->cap_name, + mod->name); + return false; + } + + if(m->cap_id) + { + *(m->cap_id) = capability_put(idx, m->cap_name, m->cap_ownerdata); + sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY, ":%s CAP * ADD :%s", me.name, m->cap_name); + } + + return true; +} + + +static +void fini_module__cap(struct module *const mod, + mapi_cap_list_av2 *const m) +{ + struct CapabilityIndex *idx; + switch(m->cap_index) + { + case MAPI_CAP_CLIENT: idx = cli_capindex; break; + case MAPI_CAP_SERVER: idx = serv_capindex; break; + default: + slog(L_MAIN, SNO_GENERAL, + "Unknown/unsupported CAP index found of type %d on capability %s when unloading %s", + m->cap_index, + m->cap_name, + mod->name); + return; + } + + if(m->cap_id) + { + capability_orphan(idx, m->cap_name); + sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY, ":%s CAP * DEL :%s", me.name, m->cap_name); + } +} + + +static +bool init_module_v2(struct module *const mod) +{ + struct mapi_mheader_av2 *mheader = (struct mapi_mheader_av2 *)mod->mapi_header; + if(mheader->mapi_register && (mheader->mapi_register() == -1)) + return false; + + /* Basic date code checks + * + * Don't make them fatal, but do complain about differences within a certain time frame. + * Later on if there are major API changes we can add fatal checks. + * -- Elizafox + */ + if(mheader->mapi_datecode != datecode && mheader->mapi_datecode > 0) + { + long int delta = datecode - mheader->mapi_datecode; + if (delta > MOD_WARN_DELTA) + { + delta /= 86400; + iwarn("Module %s build date is out of sync with ircd build date by %ld days, expect problems", + mod->name, delta); + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "Module %s build date is out of sync with ircd build date by %ld days, expect problems", + mod->name, delta); + } + } + + if(mheader->mapi_command_list) + { + struct Message **m; + for (m = mheader->mapi_command_list; *m; ++m) + mod_add_cmd(*m); + } + + if(mheader->mapi_hook_list) + { + mapi_hlist_av1 *m; + for (m = mheader->mapi_hook_list; m->hapi_name; ++m) + *m->hapi_id = register_hook(m->hapi_name); + } + + if(mheader->mapi_hfn_list) + { + mapi_hfn_list_av1 *m; + for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) + add_hook(m->hapi_name, m->fn); + } + + /* New in MAPI v2 - version replacement */ + mod->version = mheader->mapi_module_version? mheader->mapi_module_version : ircd_version; + mod->description = mheader->mapi_module_description; + + if(mheader->mapi_cap_list) + { + mapi_cap_list_av2 *m; + for (m = mheader->mapi_cap_list; m->cap_name; ++m) + if(!init_module__cap(mod, m)) + return false; + } + + return true; +} + + +static +void fini_module_v2(struct module *const mod) +{ + struct mapi_mheader_av2 *mheader = (struct mapi_mheader_av2 *)mod->mapi_header; + + if(mheader->mapi_command_list) + { + struct Message **m; + for (m = mheader->mapi_command_list; *m; ++m) + mod_del_cmd(*m); + } + + /* hook events are never removed, we simply lose the + * ability to call them --fl + */ + if(mheader->mapi_hfn_list) + { + mapi_hfn_list_av1 *m; + for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) + remove_hook(m->hapi_name, m->fn); + } + + if(mheader->mapi_unregister) + mheader->mapi_unregister(); + + if(mheader->mapi_cap_list) + { + mapi_cap_list_av2 *m; + for (m = mheader->mapi_cap_list; m->cap_name; ++m) + fini_module__cap(mod, m); + } +} + + +static +bool require_value(struct module *const mod, + struct mapi_av3_attr *const attr) +{ + if(!attr->value) + { + module_log(mod, "key[%s] requires non-null value", attr->key); + return false; + } + + return true; +} + +static +bool init_v3_module_attr(struct module *const mod, + struct mapi_mheader_av3 *const h, + struct mapi_av3_attr *const attr) +{ + if(EmptyString(attr->key)) + { + module_log(mod, "Skipping a NULL or empty key (ignoring)"); + return true; + } + + if(strncmp(attr->key, "time", MAPI_V3_KEY_MAXLEN) == 0) + { + //TODO: elizafox's warning + return true; + } + + if(strncmp(attr->key, "name", MAPI_V3_KEY_MAXLEN) == 0) + { + module_log(mod, "Changing the display unsupported (ignoring)"); + return true; + } + + if(strncmp(attr->key, "mtab", MAPI_V3_KEY_MAXLEN) == 0) + { + if(!require_value(mod, attr)) + return false; + + struct Message *const v = attr->value; + mod_add_cmd(v); + return true; + } + + if(strncmp(attr->key, "hook", MAPI_V3_KEY_MAXLEN) == 0) + { + if(!require_value(mod, attr)) + return false; + + mapi_hlist_av1 *const v = attr->value; + *v->hapi_id = register_hook(v->hapi_name); + return true; + } + + if(strncmp(attr->key, "hookfn", MAPI_V3_KEY_MAXLEN) == 0) + { + if(!require_value(mod, attr)) + return false; + + mapi_hfn_list_av1 *const v = attr->value; + add_hook(v->hapi_name, v->fn); + return true; + } + + if(strncmp(attr->key, "cap", MAPI_V3_KEY_MAXLEN) == 0) + { + if(!require_value(mod, attr)) + return false; + + mapi_cap_list_av2 *const v = attr->value; + return init_module__cap(mod, v); + } + + if(strncmp(attr->key, "description", MAPI_V3_KEY_MAXLEN) == 0) + { + mod->description = (const char *)attr->value; + return true; + } + + if(strncmp(attr->key, "version", MAPI_V3_KEY_MAXLEN) == 0) + { + mod->version = (const char *)attr->value; + return true; + } + + if(strncmp(attr->key, "init", MAPI_V3_KEY_MAXLEN) == 0) + return attr->init(); + + // Ignore fini on load + if(strncmp(attr->key, "fini", MAPI_V3_KEY_MAXLEN) == 0) + return true; + + // TODO: analysis. + module_log(mod, "Unknown key [%s]. Host version does not yet support unknown keys.", attr->key); + return false; +} + + +static +void fini_v3_module_attr(struct module *const mod, + struct mapi_mheader_av3 *const h, + struct mapi_av3_attr *const attr) +{ + if(EmptyString(attr->key)) + { + module_log(mod, "Skipping a NULL or empty key (ignoring)"); + return; + } + + if(strncmp(attr->key, "mtab", MAPI_V3_KEY_MAXLEN) == 0) + { + if(attr->value) + mod_del_cmd(attr->value); + + return; + } + + if(strncmp(attr->key, "hook", MAPI_V3_KEY_MAXLEN) == 0) + { + // ??? + return; + } + + if(strncmp(attr->key, "hookfn", MAPI_V3_KEY_MAXLEN) == 0) + { + if(!attr->value) + return; + + mapi_hfn_list_av1 *const v = attr->value; + remove_hook(v->hapi_name, v->fn); + return; + } + + if(strncmp(attr->key, "cap", MAPI_V3_KEY_MAXLEN) == 0) + { + if(attr->value) + fini_module__cap(mod, attr->value); + + return; + } + + if(strncmp(attr->key, "fini", MAPI_V3_KEY_MAXLEN) == 0) + { + if(attr->value) + attr->fini(); + + return; + } +} + + +static +void fini_module_v3(struct module *const mod) +{ + struct mapi_mheader_av3 *const h = (struct mapi_mheader_av3 *)mod->mapi_header; + if(!h->attrs) + { + module_log(mod, "(unload) has no attribute vector!"); + return; + } + + ssize_t i = -1; + struct mapi_av3_attr *attr; + for(attr = h->attrs[++i]; attr; attr = h->attrs[++i]); + for(attr = h->attrs[--i]; i > -1; attr = h->attrs[--i]) + fini_v3_module_attr(mod, h, attr); +} + + +static +bool init_module_v3(struct module *const mod) +{ + struct mapi_mheader_av3 *const h = (struct mapi_mheader_av3 *)mod->mapi_header; + if(!h->attrs) + { + module_log(mod, "has no attribute vector!"); + return false; + } + + size_t i = 0; + for(struct mapi_av3_attr *attr = h->attrs[i]; attr; attr = h->attrs[++i]) + { + if(!init_v3_module_attr(mod, h, attr)) + { + h->attrs[i] = NULL; + fini_module_v3(mod); + return false; + } + } + + return true; +} + + +static +bool init_module(struct module *const mod) +{ + mod->mapi_header = lt_dlsym(mod->address, "_mheader"); + if(!mod->mapi_header) + { + module_log(mod, "has no MAPI header. (%s)", lt_dlerror()); + return false; + } + + const int version_magic = *(const int *)mod->mapi_header; + if(MAPI_MAGIC(version_magic) != MAPI_MAGIC_HDR) + { + module_log(mod, "has an invalid header (magic is [%x] mismatches [%x]).", + MAPI_MAGIC(version_magic), + MAPI_MAGIC_HDR); + return false; + } + + mod->mapi_version = MAPI_VERSION(version_magic); + switch(mod->mapi_version) + { + case 1: return init_module_v1(mod); + case 2: return init_module_v2(mod); + case 3: return init_module_v3(mod); + default: + module_log(mod, "has unknown/unsupported MAPI version %d.", mod->mapi_version); + return false; + } +} + + +static +const char *reflect_origin(const int origin) +{ + switch(origin) + { + case MAPI_ORIGIN_EXTENSION: return "extension"; + case MAPI_ORIGIN_CORE: return "core"; + default: return "unknown"; + } +} + + +static +void free_module(struct module **ptr) +{ + if(!ptr || !*ptr) + return; + + struct module *mod = *ptr; + if(mod->name) + rb_free(mod->name); + + if(mod->path) + rb_free(mod->path); + + rb_free(mod); +} + + +static +void close_handle(lt_dlhandle *handle) +{ + if(handle && *handle) + lt_dlclose(*handle); +} + + +/* + * load_a_module() + * + * inputs - path name of module, bool to notice, int of origin, bool if core + * output - false if error true if success + * side effects - loads a module if successful + */ +bool +load_a_module(const char *path, bool warn, int origin, bool core) +{ + char *const name RB_AUTO_PTR = rb_basename(path); + + /* Trim off the ending for the display name if we have to */ + char *c; + if((c = rb_strcasestr(name, LT_MODULE_EXT)) != NULL) + *c = '\0'; + + lt_dlhandle handle RB_UNIQUE_PTR(close_handle) = lt_dlopenext(path); + if(handle == NULL) + { + slog(L_MAIN, SNO_GENERAL, "Error loading module %s: %s", name, lt_dlerror()); + return false; + } + + struct module *mod RB_UNIQUE_PTR(free_module) = rb_malloc(sizeof(struct module)); + mod->name = rb_strdup(name); + mod->path = rb_strdup(path); + mod->address = handle; + mod->origin = origin; + mod->core = core; + + if(!init_module(mod)) + { + slog(L_MAIN, SNO_GENERAL, "Loading module %s aborted.", name); + return false; + } + + if(!mod->version) + mod->version = ""; + + if(!mod->description) + mod->description = ""; + + if(warn) + slog(L_MAIN, SNO_GENERAL, + "Module %s [version: %s; MAPI version: %d; origin: %s; description: \"%s\"] loaded from [%s] to %p", + name, + mod->version, + mod->mapi_version, + reflect_origin(mod->origin), + mod->description, + mod->path, + (const void *)mod->address); + + // NULL the acquired resources after commitment to list ownership + rb_dlinkAdd(mod, &mod->node, &module_list); + mod = NULL; + handle = NULL; + return true; +} + /* unload_one_module() * @@ -290,7 +846,6 @@ bool unload_one_module(const char *name, bool warn) { struct module *mod; - if((mod = findmodule_byname(name)) == NULL) return false; @@ -310,361 +865,24 @@ unload_one_module(const char *name, bool warn) /* Left the comment in but the code isn't here any more -larne */ switch (mod->mapi_version) { - case 1: - { - struct mapi_mheader_av1 *mheader = mod->mapi_header; - if(mheader->mapi_command_list) - { - struct Message **m; - for (m = mheader->mapi_command_list; *m; ++m) - mod_del_cmd(*m); - } - - /* hook events are never removed, we simply lose the - * ability to call them --fl - */ - if(mheader->mapi_hfn_list) - { - mapi_hfn_list_av1 *m; - for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) - remove_hook(m->hapi_name, m->fn); - } - - if(mheader->mapi_unregister) - mheader->mapi_unregister(); - break; - } - case 2: - { - struct mapi_mheader_av2 *mheader = mod->mapi_header; - - /* XXX duplicate code :( */ - if(mheader->mapi_command_list) - { - struct Message **m; - for (m = mheader->mapi_command_list; *m; ++m) - mod_del_cmd(*m); - } - - /* hook events are never removed, we simply lose the - * ability to call them --fl - */ - if(mheader->mapi_hfn_list) - { - mapi_hfn_list_av1 *m; - for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) - remove_hook(m->hapi_name, m->fn); - } - - if(mheader->mapi_unregister) - mheader->mapi_unregister(); - - if(mheader->mapi_cap_list) - { - mapi_cap_list_av2 *m; - for (m = mheader->mapi_cap_list; m->cap_name; ++m) - { - struct CapabilityIndex *idx; - - switch (m->cap_index) - { - case MAPI_CAP_CLIENT: - idx = cli_capindex; - break; - case MAPI_CAP_SERVER: - idx = serv_capindex; - break; - default: - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Unknown/unsupported CAP index found of type %d on capability %s when unloading %s", - m->cap_index, m->cap_name, mod->name); - ilog(L_MAIN, - "Unknown/unsupported CAP index found of type %d on capability %s when unloading %s", - m->cap_index, m->cap_name, mod->name); - continue; - } - - if (m->cap_id != NULL) - { - capability_orphan(idx, m->cap_name); - sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY, ":%s CAP * DEL :%s", me.name, m->cap_name); - } - } - } - break; - } - default: - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Unknown/unsupported MAPI version %d when unloading %s!", - mod->mapi_version, mod->name); - ilog(L_MAIN, "Unknown/unsupported MAPI version %d when unloading %s!", - mod->mapi_version, mod->name); + case 1: fini_module_v1(mod); break; + case 2: fini_module_v2(mod); break; + case 3: fini_module_v3(mod); break; + default: + slog(L_MAIN, SNO_GENERAL, + "Unknown/unsupported MAPI version %d when unloading %s!", + mod->mapi_version, + mod->name); break; } - lt_dlclose(mod->address); - rb_dlinkDelete(&mod->node, &module_list); - rb_free(mod->name); - rb_free(mod); + close_handle(&mod->address); if(warn) - { - ilog(L_MAIN, "Module %s unloaded", name); - sendto_realops_snomask(SNO_GENERAL, L_ALL, "Module %s unloaded", name); - } + slog(L_MAIN, SNO_GENERAL, "Module %s unloaded", name); - return true; -} - -/* - * load_a_module() - * - * inputs - path name of module, bool to notice, int of origin, bool if core - * output - false if error true if success - * side effects - loads a module if successful - */ -bool -load_a_module(const char *path, bool warn, int origin, bool core) -{ - struct module *mod; - lt_dlhandle tmpptr; - char *mod_displayname, *c; - const char *ver, *description = NULL; - size_t module_ext_len = strlen(LT_MODULE_EXT); - - int *mapi_version; - - mod_displayname = rb_basename(path); - - /* Trim off the ending for the display name if we have to */ - if((c = rb_strcasestr(mod_displayname, LT_MODULE_EXT)) != NULL) - *c = '\0'; - - tmpptr = lt_dlopenext(path); - - if(tmpptr == NULL) - { - const char *err = lt_dlerror(); - - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Error loading module %s: %s", mod_displayname, err); - ilog(L_MAIN, "Error loading module %s: %s", mod_displayname, err); - rb_free(mod_displayname); - return false; - } - - /* - * _mheader is actually a struct mapi_mheader_*, but mapi_version - * is always the first member of this structure, so we treate it - * as a single int in order to determine the API version. - * -larne. - */ - mapi_version = (int *) (uintptr_t) lt_dlsym(tmpptr, "_mheader"); - if((mapi_version == NULL - && (mapi_version = (int *) (uintptr_t) lt_dlsym(tmpptr, "__mheader")) == NULL) - || MAPI_MAGIC(*mapi_version) != MAPI_MAGIC_HDR) - { - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Data format error: module %s has no MAPI header.", - mod_displayname); - ilog(L_MAIN, "Data format error: module %s has no MAPI header.", mod_displayname); - (void) lt_dlclose(tmpptr); - rb_free(mod_displayname); - return false; - } - - switch (MAPI_VERSION(*mapi_version)) - { - case 1: - { - struct mapi_mheader_av1 *mheader = (struct mapi_mheader_av1 *)(void *)mapi_version; /* see above */ - if(mheader->mapi_register && (mheader->mapi_register() == -1)) - { - ilog(L_MAIN, "Module %s indicated failure during load.", - mod_displayname); - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Module %s indicated failure during load.", - mod_displayname); - lt_dlclose(tmpptr); - rb_free(mod_displayname); - return false; - } - if(mheader->mapi_command_list) - { - struct Message **m; - for (m = mheader->mapi_command_list; *m; ++m) - mod_add_cmd(*m); - } - - if(mheader->mapi_hook_list) - { - mapi_hlist_av1 *m; - for (m = mheader->mapi_hook_list; m->hapi_name; ++m) - *m->hapi_id = register_hook(m->hapi_name); - } - - if(mheader->mapi_hfn_list) - { - mapi_hfn_list_av1 *m; - for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) - add_hook(m->hapi_name, m->fn); - } - - ver = mheader->mapi_module_version; - break; - } - case 2: - { - struct mapi_mheader_av2 *mheader = (struct mapi_mheader_av2 *)(void *)mapi_version; /* see above */ - - /* XXX duplicated code :( */ - if(mheader->mapi_register && (mheader->mapi_register() == -1)) - { - ilog(L_MAIN, "Module %s indicated failure during load.", - mod_displayname); - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Module %s indicated failure during load.", - mod_displayname); - lt_dlclose(tmpptr); - rb_free(mod_displayname); - return false; - } - - /* Basic date code checks - * - * Don't make them fatal, but do complain about differences within a certain time frame. - * Later on if there are major API changes we can add fatal checks. - * -- Elizafox - */ - if(mheader->mapi_datecode != datecode && mheader->mapi_datecode > 0) - { - long int delta = datecode - mheader->mapi_datecode; - if (delta > MOD_WARN_DELTA) - { - delta /= 86400; - iwarn("Module %s build date is out of sync with ircd build date by %ld days, expect problems", - mod_displayname, delta); - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Module %s build date is out of sync with ircd build date by %ld days, expect problems", - mod_displayname, delta); - } - } - - if(mheader->mapi_command_list) - { - struct Message **m; - for (m = mheader->mapi_command_list; *m; ++m) - mod_add_cmd(*m); - } - - if(mheader->mapi_hook_list) - { - mapi_hlist_av1 *m; - for (m = mheader->mapi_hook_list; m->hapi_name; ++m) - *m->hapi_id = register_hook(m->hapi_name); - } - - if(mheader->mapi_hfn_list) - { - mapi_hfn_list_av1 *m; - for (m = mheader->mapi_hfn_list; m->hapi_name; ++m) - add_hook(m->hapi_name, m->fn); - } - - /* New in MAPI v2 - version replacement */ - ver = mheader->mapi_module_version ? mheader->mapi_module_version : ircd_version; - description = mheader->mapi_module_description; - - if(mheader->mapi_cap_list) - { - mapi_cap_list_av2 *m; - for (m = mheader->mapi_cap_list; m->cap_name; ++m) - { - struct CapabilityIndex *idx; - int result; - - switch (m->cap_index) - { - case MAPI_CAP_CLIENT: - idx = cli_capindex; - break; - case MAPI_CAP_SERVER: - idx = serv_capindex; - break; - default: - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Unknown/unsupported CAP index found of type %d on capability %s when loading %s", - m->cap_index, m->cap_name, mod_displayname); - ilog(L_MAIN, - "Unknown/unsupported CAP index found of type %d on capability %s when loading %s", - m->cap_index, m->cap_name, mod_displayname); - continue; - } - - result = capability_put(idx, m->cap_name, m->cap_ownerdata); - if (m->cap_id != NULL) - { - *(m->cap_id) = result; - sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY, ":%s CAP * ADD :%s", me.name, m->cap_name); - } - } - } - } - - break; - default: - ilog(L_MAIN, "Module %s has unknown/unsupported MAPI version %d.", - mod_displayname, MAPI_VERSION(*mapi_version)); - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Module %s has unknown/unsupported MAPI version %d.", - mod_displayname, *mapi_version); - lt_dlclose(tmpptr); - rb_free(mod_displayname); - return false; - } - - if(ver == NULL) - ver = unknown_ver; - - if(description == NULL) - description = unknown_description; - - mod = rb_malloc(sizeof(struct module)); - mod->address = tmpptr; - mod->version = ver; - mod->description = description; - mod->core = core; - mod->name = rb_strdup(mod_displayname); - mod->mapi_header = mapi_version; - mod->mapi_version = MAPI_VERSION(*mapi_version); - mod->origin = origin; - rb_dlinkAdd(mod, &mod->node, &module_list); - - if(warn) - { - const char *o; - - switch (origin) - { - case MAPI_ORIGIN_EXTENSION: - o = "extension"; - break; - case MAPI_ORIGIN_CORE: - o = "core"; - break; - default: - o = "unknown"; - break; - } - - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Module %s [version: %s; MAPI version: %d; origin: %s; description: \"%s\"] loaded at %p", - mod_displayname, ver, MAPI_VERSION(*mapi_version), o, description, - (void *) tmpptr); - ilog(L_MAIN, "Module %s [version: %s; MAPI version: %d; origin: %s; description: \"%s\"] loaded at %p", - mod_displayname, ver, MAPI_VERSION(*mapi_version), o, description, (void *) tmpptr); - } - rb_free(mod_displayname); + // free after the unload message in case *name came from the mod struct. + free_module(&mod); return true; }