aboutsummaryrefslogtreecommitdiffstats
path: root/include/osmocom/core
diff options
context:
space:
mode:
Diffstat (limited to 'include/osmocom/core')
-rw-r--r--include/osmocom/core/defs.h2
-rw-r--r--include/osmocom/core/exec.h28
-rw-r--r--include/osmocom/core/fsm.h2
-rw-r--r--include/osmocom/core/logging.h38
-rw-r--r--include/osmocom/core/logging_internal.h3
-rw-r--r--include/osmocom/core/msgb.h19
-rw-r--r--include/osmocom/core/select.h2
-rw-r--r--include/osmocom/core/sockaddr_str.h20
-rw-r--r--include/osmocom/core/socket.h7
-rw-r--r--include/osmocom/core/talloc.h28
-rw-r--r--include/osmocom/core/tdef.h7
-rw-r--r--include/osmocom/core/utils.h64
12 files changed, 205 insertions, 15 deletions
diff --git a/include/osmocom/core/defs.h b/include/osmocom/core/defs.h
index 5e5aa90f..33ffb7bb 100644
--- a/include/osmocom/core/defs.h
+++ b/include/osmocom/core/defs.h
@@ -43,8 +43,10 @@
#if BUILDING_LIBOSMOCORE
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE
+# define OSMO_DEPRECATED_OUTSIDE(text)
#else
# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE OSMO_DEPRECATED("For internal use inside libosmocore only.")
+# define OSMO_DEPRECATED_OUTSIDE(text) OSMO_DEPRECATED(text)
#endif
#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
diff --git a/include/osmocom/core/exec.h b/include/osmocom/core/exec.h
new file mode 100644
index 00000000..6bbd352c
--- /dev/null
+++ b/include/osmocom/core/exec.h
@@ -0,0 +1,28 @@
+#pragma once
+/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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.
+ *
+ */
+extern const char *osmo_environment_whitelist[];
+
+int osmo_environment_filter(char **out, size_t out_len, char **in, const char **whitelist);
+int osmo_environment_append(char **out, size_t out_len, char **in);
+int osmo_close_all_fds_above(int last_fd_to_keep);
+int osmo_system_nowait(const char *command, const char **env_whitelist, char **addl_env);
diff --git a/include/osmocom/core/fsm.h b/include/osmocom/core/fsm.h
index 1701c45e..7b262c71 100644
--- a/include/osmocom/core/fsm.h
+++ b/include/osmocom/core/fsm.h
@@ -10,6 +10,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
/*! \defgroup fsm Finite State Machine abstraction
* @{
@@ -122,6 +123,7 @@ struct osmo_fsm_inst {
void osmo_fsm_log_addr(bool log_addr);
void osmo_fsm_log_timeouts(bool log_timeouts);
void osmo_fsm_term_safely(bool term_safely);
+void osmo_fsm_set_dealloc_ctx(void *ctx);
/*! Log using FSM instance's context, on explicit logging subsystem and level.
* \param fi An osmo_fsm_inst.
diff --git a/include/osmocom/core/logging.h b/include/osmocom/core/logging.h
index 803b4a94..79eec10d 100644
--- a/include/osmocom/core/logging.h
+++ b/include/osmocom/core/logging.h
@@ -105,7 +105,7 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define LOGL_ERROR 7 /*!< error condition, requires user action */
#define LOGL_FATAL 8 /*!< fatal, program aborted */
-/* logging levels defined by the library itself */
+/* logging subsystems defined by the library itself */
#define DLGLOBAL -1 /*!< global logging */
#define DLLAPD -2 /*!< LAPD implementation */
#define DLINP -3 /*!< (A-bis) Input sub-system */
@@ -127,6 +127,25 @@ void logp(int subsys, const char *file, int line, int cont, const char *format,
#define DLRSPRO -19 /*!< Osmocom Remote SIM Protocol */
#define OSMO_NUM_DLIB 19 /*!< Number of logging sub-systems in libraries */
+/* Colors that can be used in log_info_cat.color */
+#define OSMO_LOGCOLOR_NORMAL NULL
+#define OSMO_LOGCOLOR_RED "\033[1;31m"
+#define OSMO_LOGCOLOR_GREEN "\033[1;32m"
+#define OSMO_LOGCOLOR_YELLOW "\033[1;33m"
+#define OSMO_LOGCOLOR_BLUE "\033[1;34m"
+#define OSMO_LOGCOLOR_PURPLE "\033[1;35m"
+#define OSMO_LOGCOLOR_CYAN "\033[1;36m"
+#define OSMO_LOGCOLOR_DARKRED "\033[31m"
+#define OSMO_LOGCOLOR_DARKGREEN "\033[32m"
+#define OSMO_LOGCOLOR_DARKYELLOW "\033[33m"
+#define OSMO_LOGCOLOR_DARKBLUE "\033[34m"
+#define OSMO_LOGCOLOR_DARKPURPLE "\033[35m"
+#define OSMO_LOGCOLOR_DARKCYAN "\033[36m"
+#define OSMO_LOGCOLOR_DARKGREY "\033[1;30m"
+#define OSMO_LOGCOLOR_GREY "\033[37m"
+#define OSMO_LOGCOLOR_BRIGHTWHITE "\033[1;37m"
+#define OSMO_LOGCOLOR_END "\033[0;m"
+
/*! Configuration of single log category / sub-system */
struct log_category {
uint8_t loglevel; /*!< configured log-level */
@@ -154,6 +173,7 @@ enum log_ctx_index {
LOG_CTX_GB_BVC,
LOG_CTX_BSC_SUBSCR,
LOG_CTX_VLR_SUBSCR,
+ LOG_CTX_L1_SAPI,
_LOG_CTX_COUNT
};
@@ -166,6 +186,7 @@ enum log_filter_index {
LOG_FLT_GB_BVC,
LOG_FLT_BSC_SUBSCR,
LOG_FLT_VLR_SUBSCR,
+ LOG_FLT_L1_SAPI,
_LOG_FLT_COUNT
};
@@ -377,6 +398,19 @@ void log_add_target(struct log_target *target);
void log_del_target(struct log_target *target);
struct log_target *log_target_find(int type, const char *fname);
-extern struct llist_head osmo_log_target_list;
+
+void log_enable_multithread(void);
+
+void log_tgt_mutex_lock_impl(void);
+void log_tgt_mutex_unlock_impl(void);
+#define LOG_MTX_DEBUG 0
+#if LOG_MTX_DEBUG
+ #include <pthread.h>
+ #define log_tgt_mutex_lock() do { fprintf(stderr, "[%lu] %s:%d [%s] lock\n", pthread_self(), __FILE__, __LINE__, __func__); log_tgt_mutex_lock_impl(); } while (0)
+ #define log_tgt_mutex_unlock() do { fprintf(stderr, "[%lu] %s:%d [%s] unlock\n", pthread_self(), __FILE__, __LINE__, __func__); log_tgt_mutex_unlock_impl(); } while (0)
+#else
+ #define log_tgt_mutex_lock() log_tgt_mutex_lock_impl()
+ #define log_tgt_mutex_unlock() log_tgt_mutex_unlock_impl()
+#endif
/*! @} */
diff --git a/include/osmocom/core/logging_internal.h b/include/osmocom/core/logging_internal.h
index a510f83e..2e656603 100644
--- a/include/osmocom/core/logging_internal.h
+++ b/include/osmocom/core/logging_internal.h
@@ -7,8 +7,9 @@
#include <osmocom/core/utils.h>
extern void *tall_log_ctx;
-extern const struct log_info *osmo_log_info;
+extern struct log_info *osmo_log_info;
extern const struct value_string loglevel_strs[];
+extern struct llist_head osmo_log_target_list;
void assert_loginfo(const char *src);
diff --git a/include/osmocom/core/msgb.h b/include/osmocom/core/msgb.h
index e05d37f0..cc76e3ad 100644
--- a/include/osmocom/core/msgb.h
+++ b/include/osmocom/core/msgb.h
@@ -239,7 +239,11 @@ static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
{
unsigned char *tmp = msgb->tail;
if (msgb_tailroom(msgb) < (int) len)
- MSGB_ABORT(msgb, "Not enough tailroom msgb_put (%u < %u)\n",
+ MSGB_ABORT(msgb, "Not enough tailroom msgb_put"
+ " (allocated %u, head at %u, len %u, tailroom %u < want tailroom %u)\n",
+ msgb->data_len - sizeof(struct msgb),
+ msgb->head - msgb->_data,
+ msgb->len,
msgb_tailroom(msgb), len);
msgb->tail += len;
msgb->len += len;
@@ -335,8 +339,13 @@ static inline uint32_t msgb_get_u32(struct msgb *msgb)
static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
{
if (msgb_headroom(msgb) < (int) len)
- MSGB_ABORT(msgb, "Not enough headroom msgb_push (%u < %u)\n",
- msgb_headroom(msgb), len);
+ MSGB_ABORT(msgb, "Not enough headroom msgb_push"
+ " (allocated %u, head at %u < want headroom %u, len %u, tailroom %u)\n",
+ msgb->data_len - sizeof(struct msgb),
+ msgb->head - msgb->_data,
+ len,
+ msgb->len,
+ msgb_tailroom(msgb));
msgb->data -= len;
msgb->len += len;
return msgb->data;
@@ -518,7 +527,7 @@ static inline int msgb_l3trim(struct msgb *msg, int l3len)
static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int headroom,
const char *name)
{
- osmo_static_assert(size > headroom, headroom_bigger);
+ osmo_static_assert(size >= headroom, headroom_bigger);
struct msgb *msg = msgb_alloc_c(ctx, size, name);
if (msg)
@@ -540,7 +549,7 @@ static inline struct msgb *msgb_alloc_headroom_c(const void *ctx, int size, int
static inline struct msgb *msgb_alloc_headroom(int size, int headroom,
const char *name)
{
- osmo_static_assert(size > headroom, headroom_bigger);
+ osmo_static_assert(size >= headroom, headroom_bigger);
struct msgb *msg = msgb_alloc(size, name);
if (msg)
diff --git a/include/osmocom/core/select.h b/include/osmocom/core/select.h
index e4787b09..92904e2f 100644
--- a/include/osmocom/core/select.h
+++ b/include/osmocom/core/select.h
@@ -51,6 +51,8 @@ int osmo_fd_register(struct osmo_fd *fd);
void osmo_fd_unregister(struct osmo_fd *fd);
void osmo_fd_close(struct osmo_fd *fd);
int osmo_select_main(int polling);
+int osmo_select_main_ctx(int polling);
+void osmo_select_init(void);
struct osmo_fd *osmo_fd_get_by_fd(int fd);
diff --git a/include/osmocom/core/sockaddr_str.h b/include/osmocom/core/sockaddr_str.h
index 253b755f..e42216a4 100644
--- a/include/osmocom/core/sockaddr_str.h
+++ b/include/osmocom/core/sockaddr_str.h
@@ -31,6 +31,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <arpa/inet.h>
+#include <osmocom/core/defs.h>
struct in_addr;
struct in6_addr;
@@ -61,17 +62,23 @@ struct osmo_sockaddr_str {
* struct osmo_sockaddr_str *my_sockaddr_str = ...;
* printf("got " OSMO_SOCKADDR_STR_FMT "\n", OSMO_SOCKADDR_STR_FMT_ARGS(my_sockaddr_str));
*/
-#define OSMO_SOCKADDR_STR_FMT "%s:%u"
-#define OSMO_SOCKADDR_STR_FMT_ARGS(R) ((R)->ip ? : ""), (R)->port
+#define OSMO_SOCKADDR_STR_FMT "%s%s%s:%u"
+#define OSMO_SOCKADDR_STR_FMT_ARGS(R) \
+ ((R) && (R)->af == AF_INET6)? "[" : "", \
+ (R)? (R)->ip : "NULL", \
+ ((R) && (R)->af == AF_INET6)? "]" : "", \
+ (R)? (R)->port : 0
bool osmo_sockaddr_str_is_set(const struct osmo_sockaddr_str *sockaddr_str);
+bool osmo_sockaddr_str_is_nonzero(const struct osmo_sockaddr_str *sockaddr_str);
+int osmo_sockaddr_str_cmp(const struct osmo_sockaddr_str *a, const struct osmo_sockaddr_str *b);
int osmo_sockaddr_str_from_str(struct osmo_sockaddr_str *sockaddr_str, const char *ip, uint16_t port);
int osmo_sockaddr_str_from_in_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in_addr *addr, uint16_t port);
int osmo_sockaddr_str_from_in6_addr(struct osmo_sockaddr_str *sockaddr_str, const struct in6_addr *addr, uint16_t port);
int osmo_sockaddr_str_from_32(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port);
-int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port);
+int osmo_sockaddr_str_from_32h(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port);
int osmo_sockaddr_str_from_sockaddr_in(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in *src);
int osmo_sockaddr_str_from_sockaddr_in6(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_in6 *src);
int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, const struct sockaddr_storage *src);
@@ -79,9 +86,14 @@ int osmo_sockaddr_str_from_sockaddr(struct osmo_sockaddr_str *sockaddr_str, cons
int osmo_sockaddr_str_to_in_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in_addr *dst);
int osmo_sockaddr_str_to_in6_addr(const struct osmo_sockaddr_str *sockaddr_str, struct in6_addr *dst);
int osmo_sockaddr_str_to_32(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip);
-int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip);
+int osmo_sockaddr_str_to_32h(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip);
int osmo_sockaddr_str_to_sockaddr_in(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in *dst);
int osmo_sockaddr_str_to_sockaddr_in6(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_in6 *dst);
int osmo_sockaddr_str_to_sockaddr(const struct osmo_sockaddr_str *sockaddr_str, struct sockaddr_storage *dst);
+int osmo_sockaddr_str_from_32n(struct osmo_sockaddr_str *sockaddr_str, uint32_t ip, uint16_t port)
+ OSMO_DEPRECATED("osmo_sockaddr_str_from_32n() actually uses *host* byte order. Use osmo_sockaddr_str_from_32h() instead");
+int osmo_sockaddr_str_to_32n(const struct osmo_sockaddr_str *sockaddr_str, uint32_t *ip)
+ OSMO_DEPRECATED("osmo_sockaddr_str_to_32n() actually uses *host* byte order. Use osmo_sockaddr_str_to_32h() instead");
+
/*! @} */
diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h
index 37b1eaef..e26ca0d3 100644
--- a/include/osmocom/core/socket.h
+++ b/include/osmocom/core/socket.h
@@ -36,6 +36,9 @@ struct osmo_fd;
/*! use SO_REUSEADDR on UDP ports (required for multicast) */
#define OSMO_SOCK_F_UDP_REUSEADDR (1 << 5)
+/*! maximum number of local or remote addresses supported by an osmo_sock instance */
+#define OSMO_SOCK_MAX_ADDRS 32
+
int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
const char *host, uint16_t port, unsigned int flags);
@@ -43,6 +46,10 @@ int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
const char *local_host, uint16_t local_port,
const char *remote_host, uint16_t remote_port, unsigned int flags);
+int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto,
+ const char **local_hosts, size_t local_hosts_cnt, uint16_t local_port,
+ const char **remote_hosts, size_t remote_hosts_cnt, uint16_t remote_port, unsigned int flags);
+
int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
const char *host, uint16_t port, unsigned int flags);
diff --git a/include/osmocom/core/talloc.h b/include/osmocom/core/talloc.h
index 191a463f..c68a56cf 100644
--- a/include/osmocom/core/talloc.h
+++ b/include/osmocom/core/talloc.h
@@ -1,5 +1,27 @@
-/*! \file talloc.h
- * Convenience wrapper. libosmocore used to ship its own internal copy of
- * talloc, before libtalloc became a standard component on most systems */
+/*! \file talloc.h */
#pragma once
#include <talloc.h>
+
+/*! per-thread talloc contexts. This works around the problem that talloc is not
+ * thread-safe. However, one can simply have a different set of talloc contexts for each
+ * thread, and ensure that allocations made on one thread are always only free'd on that
+ * very same thread.
+ * WARNING: Users must make sure they free() on the same thread as they allocate!! */
+struct osmo_talloc_contexts {
+ /*! global per-thread talloc context. */
+ void *global;
+ /*! volatile select-dispatch context. This context is completely free'd and
+ * re-created every time the main select loop in osmo_select_main() returns from
+ * select(2) and calls per-fd callback functions. This allows users of this
+ * facility to allocate temporary objects like string buffers, message buffers
+ * and the like which are automatically free'd when going into the next select()
+ * system call */
+ void *select;
+};
+
+extern __thread struct osmo_talloc_contexts *osmo_ctx;
+
+/* short-hand #defines for the osmo talloc contexts (OTC) that can be used to pass
+ * to the various _c functions like msgb_alloc_c() */
+#define OTC_GLOBAL (osmo_ctx->global)
+#define OTC_SELECT (osmo_ctx->select)
diff --git a/include/osmocom/core/tdef.h b/include/osmocom/core/tdef.h
index 566f5dd3..54819d95 100644
--- a/include/osmocom/core/tdef.h
+++ b/include/osmocom/core/tdef.h
@@ -77,6 +77,10 @@ struct osmo_tdef {
/*! Currently active timeout value, e.g. set by user config. This is the only mutable member: a user may
* configure the timeout value, but neither unit nor any other field. */
unsigned long val;
+ /*! Minimum timer value (in this tdef unit), checked if set (not zero). */
+ unsigned long min_val;
+ /*! Maximum timer value (in this tdef unit), checked if set (not zero). */
+ unsigned long max_val;
};
/*! Iterate an array of struct osmo_tdef, the last item should be fully zero, i.e. "{}".
@@ -97,6 +101,9 @@ void osmo_tdefs_reset(struct osmo_tdef *tdefs);
unsigned long osmo_tdef_get(const struct osmo_tdef *tdefs, int T, enum osmo_tdef_unit as_unit,
long val_if_not_present);
struct osmo_tdef *osmo_tdef_get_entry(struct osmo_tdef *tdefs, int T);
+int osmo_tdef_set(struct osmo_tdef *tdefs, int T, unsigned long val, enum osmo_tdef_unit val_unit);
+bool osmo_tdef_val_in_range(struct osmo_tdef *tdef, unsigned long new_val);
+int osmo_tdef_range_str_buf(char *buf, size_t buf_len, struct osmo_tdef *t);
/*! Using osmo_tdef for osmo_fsm_inst: array entry for a mapping of state numbers to timeout definitions.
* For a usage example, see osmo_tdef_get_state_timeout() and test_tdef_state_timeout() in tdef_test.c. */
diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h
index 601bb565..01c4de6e 100644
--- a/include/osmocom/core/utils.h
+++ b/include/osmocom/core/utils.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include <string.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/core/talloc.h>
@@ -21,6 +22,8 @@
#define OSMO_MAX(a, b) ((a) >= (b) ? (a) : (b))
/*! Return the minimum of two specified values */
#define OSMO_MIN(a, b) ((a) >= (b) ? (b) : (a))
+/*! Return a typical cmp result for comparable entities a and b. */
+#define OSMO_CMP(a, b) ((a) < (b)? -1 : ((a) > (b)? 1 : 0))
/*! Stringify the name of a macro x, e.g. an FSM event name.
* Note: if nested within another preprocessor macro, this will
* stringify the value of x instead of its name. */
@@ -136,6 +139,7 @@ uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len);
uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len);
size_t osmo_strlcpy(char *dst, const char *src, size_t siz);
+const char *osmo_strnchr(const char *str, size_t str_size, char c);
bool osmo_is_hexstr(const char *str, int min_digits, int max_digits,
bool require_even);
@@ -144,6 +148,11 @@ bool osmo_identifier_valid(const char *str);
bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars);
void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with);
+size_t osmo_escape_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len);
+char *osmo_escape_cstr_c(void *ctx, const char *str, int in_len);
+size_t osmo_quote_cstr_buf(char *buf, size_t bufsize, const char *str, int in_len);
+char *osmo_quote_cstr_c(void *ctx, const char *str, int in_len);
+
const char *osmo_escape_str(const char *str, int len);
char *osmo_escape_str_buf2(char *buf, size_t bufsize, const char *str, int in_len);
const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize);
@@ -269,4 +278,59 @@ struct osmo_strbuf {
bool osmo_str_startswith(const char *str, const char *startswith_str);
+/*! Translate a buffer function to a talloc context function.
+ * This is the full function body of a char *foo_name_c(void *ctx, val...) function, implemented by an
+ * int foo_name_buf(buf, buflen, val...) function:
+ *
+ * char *foo_name_c(void *ctx, example_t arg)
+ * {
+ * OSMO_NAME_C_IMPL(ctx, 64, "ERROR", foo_name_buf, arg)
+ * }
+ *
+ * Return a talloc'd string containing the result of the given foo_name_buf() function, or ON_ERROR on error in the called
+ * foo_name_buf() function.
+ *
+ * If ON_ERROR is NULL, the function returns NULL on error rc from FUNC_BUF. Take care: returning NULL in printf() like
+ * formats (LOGP()) makes the program crash. If ON_ERROR is non-NULL, it must be a string constant, which is not
+ * returned directly, but written to an allocated string buffer first.
+ *
+ * \param[in] INITIAL_BUFSIZE Which size to first talloc from ctx -- a larger size makes a reallocation less likely, a
+ * smaller size allocates less unused bytes, zero allocates once but still runs the string composition twice.
+ * \param[in] ON_ERROR String constant to copy on error rc returned by FUNC_BUF, or NULL to return NULL.
+ * \param[in] FUNC_BUF Name of a function with signature foo_buf(char *buf, size_t buflen, ...).
+ * \param[in] FUNC_BUF_ARGS Additional arguments to pass to FUNC_BUF after the buf and buflen.
+ */
+#define OSMO_NAME_C_IMPL(CTX, INITIAL_BUFSIZE, ON_ERROR, FUNC_BUF, FUNC_BUF_ARGS...) \
+ size_t _len = INITIAL_BUFSIZE; \
+ int _needed; \
+ char *_str = NULL; \
+ if ((INITIAL_BUFSIZE) > 0) { \
+ _str = (char*)talloc_named_const(CTX, _len, __func__); \
+ OSMO_ASSERT(_str); \
+ } \
+ _needed = FUNC_BUF(_str, _len, ## FUNC_BUF_ARGS); \
+ if (_needed < 0) \
+ goto OSMO_NAME_C_on_error; \
+ if (_needed < _len) \
+ return _str; \
+ _len = _needed + 1; \
+ if (_str) \
+ talloc_free(_str); \
+ _str = (char*)talloc_named_const(CTX, _len, __func__); \
+ OSMO_ASSERT(_str); \
+ _needed = FUNC_BUF(_str, _len, ## FUNC_BUF_ARGS); \
+ if (_needed < 0) \
+ goto OSMO_NAME_C_on_error; \
+ return _str; \
+OSMO_NAME_C_on_error: \
+ /* Re-using and re-sizing above allocated buf ends up in very complex code. Just free and strdup. */ \
+ if (_str) \
+ talloc_free(_str); \
+ if (!(ON_ERROR)) \
+ return NULL; \
+ _str = talloc_strdup(CTX, ON_ERROR); \
+ OSMO_ASSERT(_str); \
+ talloc_set_name_const(_str, __func__); \
+ return _str;
+
/*! @} */