Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/examples/ipcserver.c b/examples/ipcserver.c
index 32ad58c..d6828ff 100644
--- a/examples/ipcserver.c
+++ b/examples/ipcserver.c
@@ -1,405 +1,405 @@
/*
* Copyright (c) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <signal.h>
#include <qb/qbarray.h>
#include <qb/qbdefs.h>
#include <qb/qbutil.h>
#include <qb/qblog.h>
#include <qb/qbloop.h>
#include <qb/qbipcs.h>
#ifdef HAVE_GLIB
#include <glib.h>
static GMainLoop *glib_loop;
static qb_array_t *gio_map;
#endif /* HAVE_GLIB */
#define ONE_MEG 1048576
static int32_t use_glib = QB_FALSE;
static int32_t use_events = QB_FALSE;
static qb_loop_t *bms_loop;
static qb_ipcs_service_t *s1;
static int32_t
s1_connection_accept_fn(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
{
#if 0
if (uid == 0 && gid == 0) {
qb_log(LOG_INFO, "Authenticated connection");
return 1;
}
qb_log(LOG_NOTICE, "BAD user!");
return 0;
#else
return 0;
#endif
}
static void
s1_connection_created_fn(qb_ipcs_connection_t * c)
{
struct qb_ipcs_stats srv_stats;
qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
qb_log(LOG_INFO, "Connection created (active:%d, closed:%d)",
srv_stats.active_connections, srv_stats.closed_connections);
}
static void
s1_connection_destroyed_fn(qb_ipcs_connection_t * c)
{
qb_log(LOG_INFO, "Connection about to be freed");
}
static int32_t
s1_connection_closed_fn(qb_ipcs_connection_t * c)
{
struct qb_ipcs_connection_stats stats;
struct qb_ipcs_stats srv_stats;
qb_ipcs_stats_get(s1, &srv_stats, QB_FALSE);
qb_ipcs_connection_stats_get(c, &stats, QB_FALSE);
qb_log(LOG_INFO,
"Connection to pid:%d destroyed (active:%d, closed:%d)",
stats.client_pid, srv_stats.active_connections,
srv_stats.closed_connections);
qb_log(LOG_DEBUG, " Requests %"PRIu64"", stats.requests);
qb_log(LOG_DEBUG, " Responses %"PRIu64"", stats.responses);
qb_log(LOG_DEBUG, " Events %"PRIu64"", stats.events);
qb_log(LOG_DEBUG, " Send retries %"PRIu64"", stats.send_retries);
qb_log(LOG_DEBUG, " Recv retries %"PRIu64"", stats.recv_retries);
qb_log(LOG_DEBUG, " FC state %d", stats.flow_control_state);
qb_log(LOG_DEBUG, " FC count %"PRIu64"", stats.flow_control_count);
return 0;
}
struct my_req {
struct qb_ipc_request_header hdr;
char message[256];
};
static int32_t
s1_msg_process_fn(qb_ipcs_connection_t * c, void *data, size_t size)
{
struct qb_ipc_request_header *hdr;
struct my_req *req_pt;
struct qb_ipc_response_header response;
ssize_t res;
struct iovec iov[2];
char resp[100];
int32_t sl;
int32_t send_ten_events = QB_FALSE;
hdr = (struct qb_ipc_request_header *)data;
if (hdr->id == (QB_IPC_MSG_USER_START + 1)) {
return 0;
}
req_pt = (struct my_req *)data;
qb_log(LOG_DEBUG, "msg received (id:%d, size:%d, data:%s)",
req_pt->hdr.id, req_pt->hdr.size, req_pt->message);
if (strcmp(req_pt->message, "kill") == 0) {
exit(0);
}
response.size = sizeof(struct qb_ipc_response_header);
response.id = 13;
response.error = 0;
- sl = snprintf(resp, 100, "ACK %zd bytes", size) + 1;
+ sl = snprintf(resp, 100, "ACK %zu bytes", size) + 1;
iov[0].iov_len = sizeof(response);
iov[0].iov_base = &response;
iov[1].iov_len = sl;
iov[1].iov_base = resp;
response.size += sl;
send_ten_events = (strcmp(req_pt->message, "events") == 0);
if (use_events && !send_ten_events) {
res = qb_ipcs_event_sendv(c, iov, 2);
} else {
res = qb_ipcs_response_sendv(c, iov, 2);
}
if (res < 0) {
errno = - res;
qb_perror(LOG_ERR, "qb_ipcs_response_send");
}
if (send_ten_events) {
int32_t i;
qb_log(LOG_INFO, "request to send 10 events");
for (i = 0; i < 10; i++) {
res = qb_ipcs_event_sendv(c, iov, 2);
qb_log(LOG_INFO, "sent event %d res:%d", i, res);
}
}
return 0;
}
static void
sigusr1_handler(int32_t num)
{
qb_log(LOG_DEBUG, "(%d)", num);
qb_ipcs_destroy(s1);
exit(0);
}
static void
show_usage(const char *name)
{
printf("usage: \n");
printf("%s <options>\n", name);
printf("\n");
printf(" options:\n");
printf("\n");
printf(" -h show this help text\n");
printf(" -m use shared memory\n");
printf(" -u use unix sockets\n");
printf(" -g use glib mainloop\n");
printf(" -e use events\n");
printf("\n");
}
#ifdef HAVE_GLIB
struct gio_to_qb_poll {
int32_t is_used;
int32_t events;
int32_t source;
int32_t fd;
void *data;
qb_ipcs_dispatch_fn_t fn;
enum qb_loop_priority p;
};
static gboolean
gio_read_socket(GIOChannel * gio, GIOCondition condition, gpointer data)
{
struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data;
gint fd = g_io_channel_unix_get_fd(gio);
return (adaptor->fn(fd, condition, adaptor->data) == 0);
}
static void
gio_poll_destroy(gpointer data)
{
struct gio_to_qb_poll *adaptor = (struct gio_to_qb_poll *)data;
adaptor->is_used--;
if (adaptor->is_used == 0) {
qb_log(LOG_DEBUG, "fd %d adaptor destroyed\n", adaptor->fd);
adaptor->fd = 0;
adaptor->source = 0;
}
}
static int32_t
my_g_dispatch_update(enum qb_loop_priority p, int32_t fd, int32_t evts,
void *data, qb_ipcs_dispatch_fn_t fn, gboolean is_new)
{
struct gio_to_qb_poll *adaptor;
GIOChannel *channel;
int32_t res = 0;
res = qb_array_index(gio_map, fd, (void **)&adaptor);
if (res < 0) {
return res;
}
if (adaptor->is_used && adaptor->source) {
if (is_new) {
return -EEXIST;
}
g_source_remove(adaptor->source);
adaptor->source = 0;
}
channel = g_io_channel_unix_new(fd);
if (!channel) {
return -ENOMEM;
}
adaptor->fn = fn;
adaptor->events = evts;
adaptor->data = data;
adaptor->p = p;
adaptor->is_used++;
adaptor->fd = fd;
adaptor->source = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, evts, gio_read_socket, adaptor, gio_poll_destroy);
/* we are handing the channel off to be managed by mainloop now.
* remove our reference. */
g_io_channel_unref(channel);
return 0;
}
static int32_t
my_g_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts,
void *data, qb_ipcs_dispatch_fn_t fn)
{
return my_g_dispatch_update(p, fd, evts, data, fn, TRUE);
}
static int32_t
my_g_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts,
void *data, qb_ipcs_dispatch_fn_t fn)
{
return my_g_dispatch_update(p, fd, evts, data, fn, FALSE);
}
static int32_t
my_g_dispatch_del(int32_t fd)
{
struct gio_to_qb_poll *adaptor;
if (qb_array_index(gio_map, fd, (void **)&adaptor) == 0) {
g_source_remove(adaptor->source);
adaptor->source = 0;
}
return 0;
}
#endif /* HAVE_GLIB */
static int32_t
my_job_add(enum qb_loop_priority p, void *data, qb_loop_job_dispatch_fn fn)
{
return qb_loop_job_add(bms_loop, p, data, fn);
}
static int32_t
my_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t evts,
void *data, qb_ipcs_dispatch_fn_t fn)
{
return qb_loop_poll_add(bms_loop, p, fd, evts, data, fn);
}
static int32_t
my_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t evts,
void *data, qb_ipcs_dispatch_fn_t fn)
{
return qb_loop_poll_mod(bms_loop, p, fd, evts, data, fn);
}
static int32_t
my_dispatch_del(int32_t fd)
{
return qb_loop_poll_del(bms_loop, fd);
}
int32_t
main(int32_t argc, char *argv[])
{
const char *options = "mpseugh";
int32_t opt;
int32_t rc;
enum qb_ipc_type ipc_type = QB_IPC_NATIVE;
struct qb_ipcs_service_handlers sh = {
.connection_accept = s1_connection_accept_fn,
.connection_created = s1_connection_created_fn,
.msg_process = s1_msg_process_fn,
.connection_destroyed = s1_connection_destroyed_fn,
.connection_closed = s1_connection_closed_fn,
};
struct qb_ipcs_poll_handlers ph = {
.job_add = my_job_add,
.dispatch_add = my_dispatch_add,
.dispatch_mod = my_dispatch_mod,
.dispatch_del = my_dispatch_del,
};
#ifdef HAVE_GLIB
struct qb_ipcs_poll_handlers glib_ph = {
.job_add = NULL, /* FIXME */
.dispatch_add = my_g_dispatch_add,
.dispatch_mod = my_g_dispatch_mod,
.dispatch_del = my_g_dispatch_del,
};
#endif /* HAVE_GLIB */
while ((opt = getopt(argc, argv, options)) != -1) {
switch (opt) {
case 'm':
ipc_type = QB_IPC_SHM;
break;
case 'u':
ipc_type = QB_IPC_SOCKET;
break;
case 'g':
use_glib = QB_TRUE;
break;
case 'e':
use_events = QB_TRUE;
break;
case 'h':
default:
show_usage(argv[0]);
exit(0);
break;
}
}
signal(SIGINT, sigusr1_handler);
qb_log_init("ipcserver", LOG_USER, LOG_TRACE);
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_format_set(QB_LOG_STDERR, "%f:%l [%p] %b");
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
s1 = qb_ipcs_create("ipcserver", 0, ipc_type, &sh);
if (s1 == 0) {
qb_perror(LOG_ERR, "qb_ipcs_create");
exit(1);
}
/* This forces the clients to use a minimum buffer size */
qb_ipcs_enforce_buffer_size(s1, ONE_MEG);
if (!use_glib) {
bms_loop = qb_loop_create();
qb_ipcs_poll_handlers_set(s1, &ph);
rc = qb_ipcs_run(s1);
if (rc != 0) {
errno = -rc;
qb_perror(LOG_ERR, "qb_ipcs_run");
exit(1);
}
qb_loop_run(bms_loop);
} else {
#ifdef HAVE_GLIB
glib_loop = g_main_loop_new(NULL, FALSE);
gio_map = qb_array_create_2(16, sizeof(struct gio_to_qb_poll), 1);
qb_ipcs_poll_handlers_set(s1, &glib_ph);
rc = qb_ipcs_run(s1);
if (rc != 0) {
errno = -rc;
qb_perror(LOG_ERR, "qb_ipcs_run");
exit(1);
}
g_main_loop_run(glib_loop);
#else
qb_log(LOG_ERR,
"You don't seem to have glib-devel installed.\n");
#endif
}
qb_log_fini();
return EXIT_SUCCESS;
}
diff --git a/examples/mapnotify.c b/examples/mapnotify.c
index daf5164..cc54f75 100644
--- a/examples/mapnotify.c
+++ b/examples/mapnotify.c
@@ -1,210 +1,210 @@
/*
* Copyright (c) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Jan Friesse <jfriesse@redhat.com>
* Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <qb/qbdefs.h>
#include <qb/qbutil.h>
#include <qb/qbmap.h>
static void
notify_fn(uint32_t event, char *key, void *old_value, void *value,
void *user_data)
{
if (event == QB_MAP_NOTIFY_FREE) {
fprintf(stderr, "Notify[FREE] %s [%d]\n",
key, *(int *)old_value);
free(old_value);
} else if (event == QB_MAP_NOTIFY_DELETED) {
fprintf(stderr, "Notify[DELETED] %s [%d]\n",
key, *(int *)old_value);
} else if (event == QB_MAP_NOTIFY_REPLACED) {
fprintf(stderr, "Notify[REPLACED] %s [%d] -> [%d]\n",
key, *(int *)old_value, *(int *)value);
} else {
- fprintf(stderr, "Notify[%d] %s \n", event, key);
+ fprintf(stderr, "Notify[%" PRIu32 "] %s \n", event, key);
if (value != NULL) {
fprintf(stderr, " value = [%d]\n", *(int *)value);
}
if (old_value != NULL) {
fprintf(stderr, " old value = [%d]\n",
*(int *)old_value);
}
}
}
static void
add_cs_keys(qb_map_t * m)
{
qb_map_put(m, "compatibility", strdup("none"));
qb_map_put(m, "totem.version", strdup("2"));
qb_map_put(m, "totem.secauth", strdup("off"));
qb_map_put(m, "totem.threads", strdup("0"));
qb_map_put(m, "totem.interface.ringnumber", strdup("0"));
qb_map_put(m, "totem.interface.bindnetaddr", strdup("192.168.122.1"));
qb_map_put(m, "totem.interface.mcastaddr", strdup("239.255.1.1"));
qb_map_put(m, "totem.interface.mcastport", strdup("5405"));
qb_map_put(m, "totem.interface.ttl", strdup("1"));
qb_map_put(m, "logging.to_stderr", strdup("yes"));
qb_map_put(m, "logging.to_logfile", strdup("no"));
qb_map_put(m, "logging.logfile", strdup("/var/log/cluster/corosync.log"));
qb_map_put(m, "logging.to_syslog", strdup("no"));
qb_map_put(m, "logging.debug", strdup("off"));
qb_map_put(m, "logging.timestamp", strdup("on"));
qb_map_put(m, "logging.logger_subsys.subsys", strdup("MAIN"));
qb_map_put(m, "logging.logger_subsys.debug", strdup("on"));
qb_map_put(m, "amf.mode", strdup("disabled"));
qb_map_put(m, "quorum.provider", strdup("corosync_quorum_ykd"));
qb_map_put(m, "runtime.services.evs.service_id", strdup("0"));
qb_map_put(m, "runtime.services.evs.0.tx", strdup("0"));
qb_map_put(m, "runtime.services.evs.0.rx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.service_id", strdup("7"));
qb_map_put(m, "runtime.services.cfg.0.tx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.0.rx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.1.tx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.1.rx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.2.tx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.2.rx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.3.tx", strdup("0"));
qb_map_put(m, "runtime.services.cfg.3.rx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.service_id", strdup("8"));
qb_map_put(m, "runtime.services.cpg.0.tx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.0.rx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.1.tx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.1.rx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.2.tx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.2.rx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.3.tx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.3.rx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.4.tx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.4.rx", strdup("0"));
qb_map_put(m, "runtime.services.cpg.5.tx", strdup("1"));
qb_map_put(m, "runtime.services.cpg.5.rx", strdup("1"));
qb_map_put(m, "runtime.services.confdb.service_id", strdup("11"));
qb_map_put(m, "runtime.services.pload.service_id", strdup("13"));
qb_map_put(m, "runtime.services.pload.0.tx", strdup("0"));
qb_map_put(m, "runtime.services.pload.0.rx", strdup("0"));
qb_map_put(m, "runtime.services.pload.1.tx", strdup("0"));
qb_map_put(m, "runtime.services.pload.1.rx", strdup("0"));
qb_map_put(m, "runtime.services.quorum.service_id", strdup("12"));
qb_map_put(m, "runtime.connections.active", strdup("1"));
qb_map_put(m, "runtime.connections.closed", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.service_id", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.client_pid", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.responses", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.dispatched", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.requests", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.send_retries", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.recv_retries", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.flow_control", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.flow_control_count", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.queue_size", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.invalid_request", strdup("0"));
qb_map_put(m, "runtime.connections.corosync-objctl:24175:0x17fd2b0.overload", strdup("0"));
qb_map_put(m, "runtime.totem.pg.msg_reserved", strdup("0"));
qb_map_put(m, "runtime.totem.pg.msg_queue_avail", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.orf_token_tx", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.orf_token_rx", strdup("100"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_merge_detect_tx", strdup("29"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_merge_detect_rx", strdup("29"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_join_tx", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_join_rx", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.mcast_tx", strdup("13"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.mcast_retx", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.mcast_rx", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_commit_token_tx", strdup("2"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.memb_commit_token_rx", strdup("2"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.token_hold_cancel_tx", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.token_hold_cancel_rx", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.operational_entered", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.operational_token_lost", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.gather_entered", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.gather_token_lost", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.commit_entered", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.commit_token_lost", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.recovery_entered", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.recovery_token_lost", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.consensus_timeouts", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.mtt_rx_token", strdup("106"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.avg_token_workload", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.avg_backlog_calc", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.rx_msg_dropped", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.continuous_gather", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.firewall_enabled_or_nic_failure", strdup("0"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.members.24815808.ip", strdup("r(0) ip(192.168.122.1) "));
qb_map_put(m, "runtime.totem.pg.mrp.srp.members.24815808.join_count", strdup("1"));
qb_map_put(m, "runtime.totem.pg.mrp.srp.members.24815808.status", strdup("joined"));
qb_map_put(m, "runtime.blackbox.dump_flight_data", strdup("no"));
qb_map_put(m, "runtime.blackbox.dump_state", strdup("no"));
}
int
main(void)
{
qb_map_t *trie;
int *i1, *i2, *i3;
qb_map_iter_t *iter;
const char *key;
void *val;
uint32_t revents = (QB_MAP_NOTIFY_DELETED |
QB_MAP_NOTIFY_REPLACED |
QB_MAP_NOTIFY_INSERTED |
QB_MAP_NOTIFY_RECURSIVE);
trie = qb_trie_create();
assert(trie != NULL);
qb_trie_dump(trie);
add_cs_keys(trie);
i1 = malloc(sizeof(int));
assert(i1 != NULL);
*i1 = 1;
i2 = malloc(sizeof(int));
assert(i2 != NULL);
*i2 = 2;
i3 = malloc(sizeof(int));
assert(i3 != NULL);
*i3 = 3;
qb_map_notify_add(trie, NULL, notify_fn, QB_MAP_NOTIFY_FREE, NULL);
qb_map_put(trie, "test.key1", i1);
qb_map_put(trie, "test.key2", i2);
qb_map_notify_add(trie, "test.", notify_fn, revents, NULL);
qb_trie_dump(trie);
qb_map_put(trie, "test.key1", i3);
iter = qb_map_pref_iter_create(trie, "test.");
while ((key = qb_map_iter_next(iter, &val)) != NULL) {
fprintf(stderr, "Iter %s [%d]\n", key, *(int *)val);
qb_map_rm(trie, key);
}
qb_map_iter_free(iter);
qb_map_notify_del_2(trie, "test.", notify_fn, revents, NULL);
qb_map_destroy(trie);
return (0);
}
diff --git a/lib/log.c b/lib/log.c
index 555c3a8..ad29db9 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -1,1211 +1,1215 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <ctype.h>
#ifdef HAVE_LINK_H
#include <link.h>
#endif /* HAVE_LINK_H */
#include <stdarg.h>
#include <pthread.h>
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif /* HAVE_DLFCN_H */
#include <stdarg.h>
#include <string.h>
#include <qb/qbdefs.h>
#include <qb/qblist.h>
#include <qb/qblog.h>
#include <qb/qbutil.h>
#include <qb/qbarray.h>
#include "log_int.h"
#include "util_int.h"
#include <regex.h>
#if defined(QB_NEED_ATTRIBUTE_SECTION_WORKAROUND) && !defined(S_SPLINT_S)
/* following only needed to force these symbols be global
with ld 2.29: https://bugzilla.redhat.com/1477354 */
struct qb_log_callsite __attribute__((weak)) QB_ATTR_SECTION_START[] = { 0 };
struct qb_log_callsite __attribute__((weak)) QB_ATTR_SECTION_STOP[] = { 0 };
#endif
static struct qb_log_target conf[QB_LOG_TARGET_MAX];
static uint32_t conf_active_max = 0;
static int32_t in_logger = QB_FALSE;
static int32_t logger_inited = QB_FALSE;
static pthread_rwlock_t _listlock;
static qb_log_filter_fn _custom_filter_fn = NULL;
static QB_LIST_DECLARE(dlnames);
static QB_LIST_DECLARE(tags_head);
static QB_LIST_DECLARE(callsite_sections);
struct dlname {
char *dln_name;
struct qb_list_head list;
};
struct callsite_section {
struct qb_log_callsite *start;
struct qb_log_callsite *stop;
struct qb_list_head list;
};
static int32_t _log_target_enable(struct qb_log_target *t);
static void _log_target_disable(struct qb_log_target *t);
static void _log_filter_apply(struct callsite_section *sect,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
regex_t *regex,
uint8_t high_priority, uint8_t low_priority);
static void _log_filter_apply_to_cs(struct qb_log_callsite *cs,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
regex_t *regex,
uint8_t high_priority, uint8_t low_priority);
/* deprecated method of getting internal log messages */
static qb_util_log_fn_t old_internal_log_fn = NULL;
void
qb_util_set_log_function(qb_util_log_fn_t fn)
{
old_internal_log_fn = fn;
}
static void
_log_free_filter(struct qb_log_filter *flt)
{
if (flt->regex) {
regfree(flt->regex);
}
free(flt->regex);
free(flt->text);
free(flt);
}
static int32_t
_cs_matches_filter_(struct qb_log_callsite *cs,
enum qb_log_filter_type type,
const char *text,
regex_t *regex,
uint8_t high_priority,
uint8_t low_priority)
{
int32_t match = QB_FALSE;
const char *offset = NULL;
const char *next = NULL;
if (cs->priority > low_priority ||
cs->priority < high_priority) {
return QB_FALSE;
}
if (strcmp(text, "*") == 0) {
return QB_TRUE;
}
switch (type) {
case QB_LOG_FILTER_FILE:
case QB_LOG_FILTER_FUNCTION:
next = text;
do {
char token[500];
offset = next;
next = strchrnul(offset, ',');
snprintf(token, 499, "%.*s", (int)(next - offset), offset);
if (type == QB_LOG_FILTER_FILE) {
match = (strcmp(cs->filename, token) == 0) ? 1 : 0;
} else {
match = (strcmp(cs->function, token) == 0) ? 1 : 0;
}
if (!match && next[0] != 0) {
next++;
}
} while (match == QB_FALSE && next != NULL && next[0] != 0);
break;
case QB_LOG_FILTER_FILE_REGEX:
next = next ? next : cs->filename;
/* fallthrough */
case QB_LOG_FILTER_FUNCTION_REGEX:
next = next ? next : cs->function;
/* fallthrough */
case QB_LOG_FILTER_FORMAT_REGEX:
next = next ? next : cs->format;
if (regex == NULL) {
return QB_FALSE;
}
match = regexec(regex, next, 0, NULL, 0);
if (match == 0) {
match = QB_TRUE;
} else {
match = QB_FALSE;
}
break;
case QB_LOG_FILTER_FORMAT:
if (strstr(cs->format, text)) {
match = QB_TRUE;
}
break;
}
return match;
}
/**
* @internal
* @brief Format a log message into a string buffer
*
* @param[out] str Destination buffer to contain formatted string
* @param[in] cs Callsite containing format to use
* @param[in] ap Variable arguments for format
*/
/* suppress suggestion that we currently can do nothing better about
as the format specification is hidden in cs argument */
#ifdef HAVE_GCC_FORMAT_COMPLAINTS
#pragma GCC diagnostic push
#ifdef HAVE_GCC_MISSING_FORMAT_ATTRIBUTE
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
#endif
#ifdef HAVE_GCC_SUGGEST_ATTRIBUTE_FORMAT
#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
#endif
#endif
static inline void
cs_format(char *str, struct qb_log_callsite *cs, va_list ap)
{
va_list ap_copy;
int len;
va_copy(ap_copy, ap);
len = vsnprintf(str, QB_LOG_MAX_LEN, cs->format, ap_copy);
va_end(ap_copy);
if (len > QB_LOG_MAX_LEN) {
len = QB_LOG_MAX_LEN;
}
if (str[len - 1] == '\n') {
str[len - 1] = '\0';
}
}
#ifdef HAVE_GCC_FORMAT_COMPLAINTS
#pragma GCC diagnostic pop
#endif
void
qb_log_real_va_(struct qb_log_callsite *cs, va_list ap)
{
int32_t found_threaded = QB_FALSE;
struct qb_log_target *t;
struct timespec tv;
enum qb_log_target_slot pos;
int32_t formatted = QB_FALSE;
char buf[QB_LOG_MAX_LEN];
char *str = buf;
va_list ap_copy;
if (in_logger || cs == NULL) {
return;
}
in_logger = QB_TRUE;
if (old_internal_log_fn &&
qb_bit_is_set(cs->tags, QB_LOG_TAG_LIBQB_MSG_BIT)) {
if (formatted == QB_FALSE) {
cs_format(str, cs, ap);
formatted = QB_TRUE;
}
qb_do_extended(str, QB_TRUE,
old_internal_log_fn(cs->filename, cs->lineno,
cs->priority, str));
}
qb_util_timespec_from_epoch_get(&tv);
/*
* 1 if we can find a threaded target that needs this log then post it
* 2 foreach non-threaded target call it's logger function
*/
for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
t = &conf[pos];
if ((t->state == QB_LOG_STATE_ENABLED)
&& qb_bit_is_set(cs->targets, pos)) {
if (t->threaded) {
if (!found_threaded) {
found_threaded = QB_TRUE;
if (formatted == QB_FALSE) {
cs_format(str, cs, ap);
formatted = QB_TRUE;
}
}
} else if (t->vlogger) {
va_copy(ap_copy, ap);
t->vlogger(t->pos, cs, tv.tv_sec, ap_copy);
va_end(ap_copy);
} else if (t->logger) {
if (formatted == QB_FALSE) {
cs_format(str, cs, ap);
formatted = QB_TRUE;
}
qb_do_extended(str, t->extended,
t->logger(t->pos, cs, tv.tv_sec, str));
}
}
}
if (found_threaded) {
qb_log_thread_log_post(cs, tv.tv_sec, str);
}
in_logger = QB_FALSE;
}
void
qb_log_real_(struct qb_log_callsite *cs, ...)
{
va_list ap;
va_start(ap, cs);
qb_log_real_va_(cs, ap);
va_end(ap);
}
void
qb_log_thread_log_write(struct qb_log_callsite *cs,
time_t timestamp, const char *buffer)
{
struct qb_log_target *t;
enum qb_log_target_slot pos;
for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
t = &conf[pos];
if ((t->state == QB_LOG_STATE_ENABLED) && t->threaded
&& qb_bit_is_set(cs->targets, t->pos)) {
qb_do_extended(buffer, t->extended,
t->logger(t->pos, cs, timestamp, buffer));
}
}
}
struct qb_log_callsite*
qb_log_callsite_get(const char *function,
const char *filename,
const char *format,
uint8_t priority,
uint32_t lineno,
uint32_t tags)
{
struct qb_log_target *t;
struct qb_log_filter *flt;
struct qb_log_callsite *cs;
int32_t new_dcs = QB_FALSE;
struct qb_list_head *f_item;
enum qb_log_target_slot pos;
if (!logger_inited) {
return NULL;
}
cs = qb_log_dcs_get(&new_dcs, function, filename,
format, priority, lineno, tags);
if (cs == NULL) {
return NULL;
}
if (new_dcs) {
pthread_rwlock_rdlock(&_listlock);
for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
t = &conf[pos];
if (t->state != QB_LOG_STATE_ENABLED) {
continue;
}
qb_list_for_each(f_item, &t->filter_head) {
flt = qb_list_entry(f_item, struct qb_log_filter, list);
_log_filter_apply_to_cs(cs, t->pos, flt->conf, flt->type,
flt->text, flt->regex, flt->high_priority,
flt->low_priority);
}
}
if (tags == 0) {
qb_list_for_each(f_item, &tags_head) {
flt = qb_list_entry(f_item, struct qb_log_filter, list);
_log_filter_apply_to_cs(cs, flt->new_value, flt->conf, flt->type,
flt->text, flt->regex, flt->high_priority,
flt->low_priority);
}
} else {
cs->tags = tags;
}
if (_custom_filter_fn) {
_custom_filter_fn(cs);
}
pthread_rwlock_unlock(&_listlock);
} else {
if (tags && cs->tags != tags) {
cs->tags = tags;
}
if (_custom_filter_fn) {
_custom_filter_fn(cs);
}
}
return cs;
}
void
qb_log_from_external_source_va(const char *function,
const char *filename,
const char *format,
uint8_t priority,
uint32_t lineno, uint32_t tags, va_list ap)
{
struct qb_log_callsite *cs;
if (!logger_inited) {
return;
}
cs = qb_log_callsite_get(function, filename,
format, priority, lineno, tags);
qb_log_real_va_(cs, ap);
}
void
qb_log_from_external_source(const char *function,
const char *filename,
const char *format,
uint8_t priority,
uint32_t lineno, uint32_t tags, ...)
{
struct qb_log_callsite *cs;
va_list ap;
if (!logger_inited) {
return;
}
cs = qb_log_callsite_get(function, filename,
format, priority, lineno, tags);
va_start(ap, tags);
qb_log_real_va_(cs, ap);
va_end(ap);
}
static void
qb_log_callsites_dump_sect(struct callsite_section *sect)
{
struct qb_log_callsite *cs;
-
printf(" start %p - stop %p\n", sect->start, sect->stop);
printf("filename lineno targets tags\n");
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno > 0) {
- printf("%12s %6d %16d %16d\n", cs->filename, cs->lineno,
- cs->targets, cs->tags);
+#ifndef S_SPLINT_S
+ printf("%12s %6" PRIu32 " %16" PRIu32 " %16u\n",
+ cs->filename, cs->lineno, cs->targets,
+ cs->tags);
+#endif /* S_SPLINT_S */
}
}
}
int32_t
qb_log_callsites_register(struct qb_log_callsite *_start,
struct qb_log_callsite *_stop)
{
struct callsite_section *sect;
struct qb_log_callsite *cs;
struct qb_log_target *t;
struct qb_log_filter *flt;
enum qb_log_target_slot pos;
if (_start == NULL || _stop == NULL) {
return -EINVAL;
}
pthread_rwlock_rdlock(&_listlock);
qb_list_for_each_entry(sect, &callsite_sections, list) {
if (sect->start == _start || sect->stop == _stop) {
pthread_rwlock_unlock(&_listlock);
return -EEXIST;
}
}
pthread_rwlock_unlock(&_listlock);
sect = calloc(1, sizeof(struct callsite_section));
if (sect == NULL) {
return -ENOMEM;
}
sect->start = _start;
sect->stop = _stop;
qb_list_init(&sect->list);
pthread_rwlock_wrlock(&_listlock);
qb_list_add(&sect->list, &callsite_sections);
/*
* Now apply the filters on these new callsites
*/
for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
t = &conf[pos];
if (t->state != QB_LOG_STATE_ENABLED) {
continue;
}
qb_list_for_each_entry(flt, &t->filter_head, list) {
_log_filter_apply(sect, t->pos, flt->conf,
flt->type, flt->text, flt->regex,
flt->high_priority, flt->low_priority);
}
}
qb_list_for_each_entry(flt, &tags_head, list) {
_log_filter_apply(sect, flt->new_value, flt->conf,
flt->type, flt->text, flt->regex,
flt->high_priority, flt->low_priority);
}
pthread_rwlock_unlock(&_listlock);
if (_custom_filter_fn) {
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno > 0) {
_custom_filter_fn(cs);
}
}
}
/* qb_log_callsites_dump_sect(sect); */
return 0;
}
void
qb_log_callsites_dump(void)
{
struct callsite_section *sect;
int32_t l;
pthread_rwlock_rdlock(&_listlock);
l = qb_list_length(&callsite_sections);
printf("Callsite Database [%d]\n", l);
printf("---------------------\n");
qb_list_for_each_entry(sect, &callsite_sections, list) {
qb_log_callsites_dump_sect(sect);
}
pthread_rwlock_unlock(&_listlock);
}
static int32_t
_log_filter_exists(struct qb_list_head *list_head,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority,
uint8_t low_priority,
uint32_t new_value)
{
struct qb_log_filter *flt;
qb_list_for_each_entry(flt, list_head, list) {
if (flt->type == type &&
flt->high_priority == high_priority &&
flt->low_priority == low_priority &&
flt->new_value == new_value &&
strcmp(flt->text, text) == 0) {
return QB_TRUE;
}
}
return QB_FALSE;
}
static int32_t
_log_filter_store(uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
uint8_t high_priority,
uint8_t low_priority,
struct qb_log_filter **new)
{
struct qb_log_filter *flt;
struct qb_list_head *iter;
struct qb_list_head *next;
struct qb_list_head *list_head;
switch (c) {
case QB_LOG_FILTER_ADD:
case QB_LOG_FILTER_REMOVE:
case QB_LOG_FILTER_CLEAR_ALL:
list_head = &conf[t].filter_head;
break;
case QB_LOG_TAG_SET:
case QB_LOG_TAG_CLEAR:
case QB_LOG_TAG_CLEAR_ALL:
list_head = &tags_head;
break;
default:
return -ENOSYS;
}
if (c == QB_LOG_FILTER_ADD || c == QB_LOG_TAG_SET) {
if (text == NULL) {
return -EINVAL;
}
if (_log_filter_exists(list_head, type, text,
high_priority, low_priority, t)) {
return -EEXIST;
}
flt = calloc(1, sizeof(struct qb_log_filter));
if (flt == NULL) {
return -ENOMEM;
}
qb_list_init(&flt->list);
flt->conf = c;
flt->type = type;
flt->text = strdup(text);
if (flt->text == NULL) {
_log_free_filter(flt);
return -ENOMEM;
}
if (type == QB_LOG_FILTER_FUNCTION_REGEX ||
type == QB_LOG_FILTER_FILE_REGEX ||
type == QB_LOG_FILTER_FORMAT_REGEX) {
int res;
flt->regex = calloc(1, sizeof(regex_t));
if (flt->regex == NULL) {
_log_free_filter(flt);
return -ENOMEM;
}
res = regcomp(flt->regex, flt->text, 0);
if (res) {
_log_free_filter(flt);
return -EINVAL;
}
}
flt->high_priority = high_priority;
flt->low_priority = low_priority;
flt->new_value = t;
qb_list_add_tail(&flt->list, list_head);
if (new) {
*new = flt;
}
} else if (c == QB_LOG_FILTER_REMOVE || c == QB_LOG_TAG_CLEAR) {
qb_list_for_each_safe(iter, next, list_head) {
flt = qb_list_entry(iter, struct qb_log_filter, list);
if (flt->type == type &&
flt->low_priority <= low_priority &&
flt->high_priority >= high_priority &&
(strcmp(flt->text, text) == 0 ||
strcmp("*", text) == 0)) {
qb_list_del(iter);
_log_free_filter(flt);
return 0;
}
}
} else if (c == QB_LOG_FILTER_CLEAR_ALL || c == QB_LOG_TAG_CLEAR_ALL) {
qb_list_for_each_safe(iter, next, list_head) {
flt = qb_list_entry(iter, struct qb_log_filter, list);
qb_list_del(iter);
_log_free_filter(flt);
}
}
return 0;
}
static void
_log_filter_apply(struct callsite_section *sect,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
regex_t *regex,
uint8_t high_priority, uint8_t low_priority)
{
struct qb_log_callsite *cs;
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno > 0) {
_log_filter_apply_to_cs(cs, t, c, type, text, regex,
high_priority, low_priority);
}
}
}
/* #define _QB_FILTER_DEBUGGING_ 1 */
static void
_log_filter_apply_to_cs(struct qb_log_callsite *cs,
uint32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text,
regex_t *regex,
uint8_t high_priority, uint8_t low_priority)
{
if (c == QB_LOG_FILTER_CLEAR_ALL) {
qb_bit_clear(cs->targets, t);
return;
} else if (c == QB_LOG_TAG_CLEAR_ALL) {
cs->tags = 0;
return;
}
if (_cs_matches_filter_(cs, type, text, regex, high_priority, low_priority)) {
#ifdef _QB_FILTER_DEBUGGING_
uint32_t old_targets = cs->targets;
uint32_t old_tags = cs->tags;
#endif /* _QB_FILTER_DEBUGGING_ */
if (c == QB_LOG_FILTER_ADD) {
qb_bit_set(cs->targets, t);
} else if (c == QB_LOG_FILTER_REMOVE) {
qb_bit_clear(cs->targets, t);
} else if (c == QB_LOG_TAG_SET) {
cs->tags = t;
} else if (c == QB_LOG_TAG_CLEAR) {
cs->tags = 0;
}
#ifdef _QB_FILTER_DEBUGGING_
if (old_targets != cs->targets) {
printf("targets: %s:%u value(%d) %d -> %d\n",
cs->filename, cs->lineno, t,
old_targets, cs->targets);
}
if (old_tags != cs->tags) {
printf("tags: %s:%u value(%d) %d -> %d\n",
cs->filename, cs->lineno, t, old_tags, cs->tags);
}
#endif /* _QB_FILTER_DEBUGGING_ */
}
}
int32_t
qb_log_filter_ctl2(int32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type, const char * text,
uint8_t high_priority, uint8_t low_priority)
{
struct qb_log_filter *new_flt = NULL;
regex_t *regex = NULL;
struct callsite_section *sect;
int32_t rc;
if (!logger_inited) {
return -EINVAL;
}
if (c == QB_LOG_FILTER_ADD ||
c == QB_LOG_FILTER_CLEAR_ALL ||
c == QB_LOG_FILTER_REMOVE) {
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return -EBADF;
}
}
if (text == NULL ||
low_priority < high_priority ||
type > QB_LOG_FILTER_FORMAT_REGEX ||
c > QB_LOG_TAG_CLEAR_ALL) {
return -EINVAL;
}
pthread_rwlock_rdlock(&_listlock);
rc = _log_filter_store(t, c, type, text, high_priority, low_priority, &new_flt);
if (rc < 0) {
pthread_rwlock_unlock(&_listlock);
return rc;
}
if (new_flt && new_flt->regex) {
regex = new_flt->regex;
}
qb_list_for_each_entry(sect, &callsite_sections, list) {
_log_filter_apply(sect, t, c, type, text, regex, high_priority, low_priority);
}
pthread_rwlock_unlock(&_listlock);
return 0;
}
int32_t
qb_log_filter_fn_set(qb_log_filter_fn fn)
{
struct callsite_section *sect;
struct qb_log_callsite *cs;
if (!logger_inited) {
return -EINVAL;
}
_custom_filter_fn = fn;
if (_custom_filter_fn == NULL) {
return 0;
}
qb_list_for_each_entry(sect, &callsite_sections, list) {
for (cs = sect->start; cs < sect->stop; cs++) {
if (cs->lineno > 0) {
_custom_filter_fn(cs);
}
}
}
return 0;
}
int32_t
qb_log_filter_ctl(int32_t t, enum qb_log_filter_conf c,
enum qb_log_filter_type type,
const char *text, uint8_t priority)
{
return qb_log_filter_ctl2(t, c, type, text, LOG_EMERG, priority);
}
#ifdef QB_HAVE_ATTRIBUTE_SECTION
/* Some platforms (eg. FreeBSD 10+) don't support calling dlopen() from
* within a dl_iterate_phdr() callback; so save all of the dlpi_names to
* a list and iterate over them afterwards. */
static int32_t
_log_so_walk_callback(struct dl_phdr_info *info, size_t size, void *data)
{
struct dlname *dlname;
if (strlen(info->dlpi_name) > 0) {
dlname = calloc(1, sizeof(struct dlname));
if (!dlname)
return 0;
dlname->dln_name = strdup(info->dlpi_name);
qb_list_add_tail(&dlname->list, &dlnames);
}
return 0;
}
static void
_log_so_walk_dlnames(void)
{
struct dlname *dlname;
struct qb_list_head *iter;
struct qb_list_head *next;
void *handle;
void *start;
void *stop;
const char *error;
qb_list_for_each_safe(iter, next, &dlnames) {
dlname = qb_list_entry(iter, struct dlname, list);
handle = dlopen(dlname->dln_name, RTLD_LAZY);
error = dlerror();
if (!handle || error) {
qb_log(LOG_ERR, "%s", error);
goto done;
}
start = dlsym(handle, QB_ATTR_SECTION_START_STR);
error = dlerror();
if (error) {
goto done;
}
stop = dlsym(handle, QB_ATTR_SECTION_STOP_STR);
error = dlerror();
if (error) {
goto done;
} else {
qb_log_callsites_register(start, stop);
}
done:
if (handle)
dlclose(handle);
qb_list_del(iter);
if (dlname->dln_name)
free(dlname->dln_name);
free(dlname);
}
}
#endif /* QB_HAVE_ATTRIBUTE_SECTION */
static void
_log_target_state_set(struct qb_log_target *t, enum qb_log_target_state s)
{
enum qb_log_target_slot i;
int32_t a_set = QB_FALSE;
int32_t u_set = QB_FALSE;
t->state = s;
for (i = QB_LOG_TARGET_MAX; i > QB_LOG_TARGET_START; i--) {
if (!a_set && conf[i-1].state == QB_LOG_STATE_ENABLED) {
a_set = QB_TRUE;
conf_active_max = i-1;
}
if (!u_set && conf[i-1].state != QB_LOG_STATE_UNUSED) {
u_set = QB_TRUE;
}
}
}
void
qb_log_init(const char *name, int32_t facility, uint8_t priority)
{
int32_t l;
enum qb_log_target_slot i;
#ifdef QB_HAVE_ATTRIBUTE_SECTION
void *work_handle; struct qb_log_callsite *work_s1, *work_s2;
Dl_info work_dli;
#endif /* QB_HAVE_ATTRIBUTE_SECTION */
/* cannot reuse single qb_log invocation in various contexts
through the variables (when section attribute in use),
hence this indirection */
enum {
preinit_err_none,
preinit_err_target_sec,
preinit_err_target_empty,
} preinit_err = preinit_err_none;
l = pthread_rwlock_init(&_listlock, NULL);
assert(l == 0);
qb_log_format_init();
for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
conf[i].pos = i;
conf[i].debug = QB_FALSE;
conf[i].file_sync = QB_FALSE;
conf[i].extended = QB_TRUE;
conf[i].state = QB_LOG_STATE_UNUSED;
(void)strlcpy(conf[i].name, name, PATH_MAX);
conf[i].facility = facility;
qb_list_init(&conf[i].filter_head);
}
qb_log_dcs_init();
#ifdef QB_HAVE_ATTRIBUTE_SECTION
/* sanity check that target chain supplied QB_ATTR_SECTION_ST{ART,OP}
symbols and hence the local references to them are not referencing
the proper libqb's ones (locating libqb by it's relatively unique
non-functional symbols -- the two are mutually exclusive, the
ordinarily latter was introduced by accident, the former is
intentional -- due to possible confusion otherwise) */
if ((dladdr(dlsym(RTLD_DEFAULT, "qb_ver_str"), &work_dli)
|| dladdr(dlsym(RTLD_DEFAULT, "facilitynames"), &work_dli))
&& (work_handle = dlopen(work_dli.dli_fname,
RTLD_LOCAL|RTLD_LAZY)) != NULL) {
work_s1 = (struct qb_log_callsite *)
dlsym(work_handle, QB_ATTR_SECTION_START_STR);
work_s2 = (struct qb_log_callsite *)
dlsym(work_handle, QB_ATTR_SECTION_STOP_STR);
if (work_s1 == QB_ATTR_SECTION_START
|| work_s2 == QB_ATTR_SECTION_STOP) {
preinit_err = preinit_err_target_sec;
} else if (work_s1 == work_s2) {
preinit_err = preinit_err_target_empty;
}
dlclose(work_handle); /* perhaps overly eager thing to do */
}
qb_log_callsites_register(QB_ATTR_SECTION_START, QB_ATTR_SECTION_STOP);
dl_iterate_phdr(_log_so_walk_callback, NULL);
_log_so_walk_dlnames();
#endif /* QB_HAVE_ATTRIBUTE_SECTION */
for (i = QB_LOG_TARGET_STATIC_START; i < QB_LOG_TARGET_STATIC_MAX; i++)
conf[i].state = QB_LOG_STATE_DISABLED;
logger_inited = QB_TRUE;
(void)qb_log_syslog_open(&conf[QB_LOG_SYSLOG]);
_log_target_state_set(&conf[QB_LOG_SYSLOG], QB_LOG_STATE_ENABLED);
(void)qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", priority);
if (preinit_err == preinit_err_target_sec)
qb_util_log(LOG_NOTICE, "(libqb) log module hasn't observed"
" target chain supplied callsite"
" section, target's and/or libqb's"
" build is at fault, preventing"
" reliable logging (unless qb_log_init"
" invoked in no-custom-logging context"
" unexpectedly, or target chain built"
" purposefully without these sections)");
else if (preinit_err == preinit_err_target_empty) {
qb_util_log(LOG_WARNING, "(libqb) log module has observed"
" target chain supplied section"
" unpopulated, target's and/or libqb's"
" build is at fault, preventing"
" reliable logging (unless qb_log_init"
" invoked in no-custom-logging context"
" unexpectedly)");
}
}
void
qb_log_fini(void)
{
struct qb_log_target *t;
struct qb_log_filter *flt;
struct callsite_section *s;
struct qb_list_head *iter;
struct qb_list_head *iter2;
struct qb_list_head *next;
struct qb_list_head *next2;
enum qb_log_target_slot pos;
if (!logger_inited) {
return;
}
logger_inited = QB_FALSE;
qb_log_thread_stop();
pthread_rwlock_destroy(&_listlock);
for (pos = QB_LOG_TARGET_START; pos <= conf_active_max; pos++) {
t = &conf[pos];
_log_target_disable(t);
qb_list_for_each_safe(iter2, next2, &t->filter_head) {
flt = qb_list_entry(iter2, struct qb_log_filter, list);
qb_list_del(iter2);
_log_free_filter(flt);
}
}
qb_log_format_fini();
qb_log_dcs_fini();
qb_list_for_each_safe(iter, next, &callsite_sections) {
s = qb_list_entry(iter, struct callsite_section, list);
qb_list_del(iter);
free(s);
}
qb_list_for_each_safe(iter, next, &tags_head) {
flt = qb_list_entry(iter, struct qb_log_filter, list);
qb_list_del(iter);
_log_free_filter(flt);
}
}
struct qb_log_target *
qb_log_target_alloc(void)
{
enum qb_log_target_slot i;
for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
if (conf[i].state == QB_LOG_STATE_UNUSED) {
_log_target_state_set(&conf[i], QB_LOG_STATE_DISABLED);
return &conf[i];
}
}
return NULL;
}
void
qb_log_target_free(struct qb_log_target *t)
{
(void)qb_log_filter_ctl(t->pos, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, NULL, 0);
t->debug = QB_FALSE;
t->filename[0] = '\0';
qb_log_format_set(t->pos, NULL);
_log_target_state_set(t, QB_LOG_STATE_UNUSED);
}
struct qb_log_target *
qb_log_target_get(int32_t pos)
{
return &conf[pos];
}
void *
qb_log_target_user_data_get(int32_t t)
{
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
errno = -EBADF;
return NULL;
}
return conf[t].instance;
}
int32_t
qb_log_target_user_data_set(int32_t t, void *user_data)
{
if (!logger_inited) {
return -EINVAL;
}
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return -EBADF;
}
conf[t].instance = user_data;
return 0;
}
int32_t
qb_log_custom_open(qb_log_logger_fn log_fn,
qb_log_close_fn close_fn,
qb_log_reload_fn reload_fn, void *user_data)
{
struct qb_log_target *t;
t = qb_log_target_alloc();
if (t == NULL) {
return -errno;
}
t->instance = user_data;
- snprintf(t->filename, PATH_MAX, "custom-%d", t->pos);
+#ifndef S_SPLINT_S
+ snprintf(t->filename, PATH_MAX, "custom-%" PRIu32, t->pos);
+#endif /* S_SPLINT_S */
t->logger = log_fn;
t->vlogger = NULL;
t->reload = reload_fn;
t->close = close_fn;
return t->pos;
}
void
qb_log_custom_close(int32_t t)
{
struct qb_log_target *target;
if (!logger_inited) {
return;
}
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return;
}
target = qb_log_target_get(t);
if (target->close) {
in_logger = QB_TRUE;
target->close(t);
in_logger = QB_FALSE;
}
qb_log_target_free(target);
}
static int32_t
_log_target_enable(struct qb_log_target *t)
{
int32_t rc = 0;
if (t->state == QB_LOG_STATE_ENABLED) {
return 0;
}
if (t->pos == QB_LOG_STDERR ||
t->pos == QB_LOG_STDOUT) {
rc = qb_log_stderr_open(t);
} else if (t->pos == QB_LOG_SYSLOG) {
rc = qb_log_syslog_open(t);
} else if (t->pos == QB_LOG_BLACKBOX) {
rc = qb_log_blackbox_open(t);
}
if (rc == 0) {
_log_target_state_set(t, QB_LOG_STATE_ENABLED);
}
return rc;
}
static void
_log_target_disable(struct qb_log_target *t)
{
if (t->state != QB_LOG_STATE_ENABLED) {
return;
}
_log_target_state_set(t, QB_LOG_STATE_DISABLED);
if (t->close) {
in_logger = QB_TRUE;
t->close(t->pos);
in_logger = QB_FALSE;
}
}
int32_t
qb_log_ctl2(int32_t t, enum qb_log_conf c, qb_log_ctl2_arg_t arg_not4directuse)
{
int32_t rc = 0;
int32_t need_reload = QB_FALSE;
/* extract the constants and do not touch the origin anymore */
const int32_t arg_i32 = arg_not4directuse.i32;
const char * const arg_s = arg_not4directuse.s;
if (!logger_inited) {
return -EINVAL;
}
if (t < 0 || t >= QB_LOG_TARGET_MAX ||
conf[t].state == QB_LOG_STATE_UNUSED) {
return -EBADF;
}
switch (c) {
case QB_LOG_CONF_ENABLED:
if (arg_i32) {
rc = _log_target_enable(&conf[t]);
} else {
_log_target_disable(&conf[t]);
}
break;
case QB_LOG_CONF_STATE_GET:
rc = conf[t].state;
break;
case QB_LOG_CONF_FACILITY:
conf[t].facility = arg_i32;
if (t == QB_LOG_SYSLOG) {
need_reload = QB_TRUE;
}
break;
case QB_LOG_CONF_IDENT:
(void)strlcpy(conf[t].name, arg_s, PATH_MAX);
if (t == QB_LOG_SYSLOG) {
need_reload = QB_TRUE;
}
break;
case QB_LOG_CONF_FILE_SYNC:
conf[t].file_sync = arg_i32;
break;
case QB_LOG_CONF_PRIORITY_BUMP:
conf[t].priority_bump = arg_i32;
break;
case QB_LOG_CONF_SIZE:
if (t == QB_LOG_BLACKBOX) {
if (arg_i32 <= 0) {
return -EINVAL;
}
conf[t].size = arg_i32;
need_reload = QB_TRUE;
} else {
return -ENOSYS;
}
break;
case QB_LOG_CONF_THREADED:
conf[t].threaded = arg_i32;
break;
case QB_LOG_CONF_EXTENDED:
conf[t].extended = arg_i32;
break;
default:
rc = -EINVAL;
}
if (rc == 0 && need_reload && conf[t].reload) {
in_logger = QB_TRUE;
conf[t].reload(t);
in_logger = QB_FALSE;
}
return rc;
}
int32_t
qb_log_ctl(int32_t t, enum qb_log_conf c, int32_t arg)
{
return qb_log_ctl2(t, c, QB_LOG_CTL2_I32(arg));
}
diff --git a/lib/log_blackbox.c b/lib/log_blackbox.c
index 5670687..64c30fe 100644
--- a/lib/log_blackbox.c
+++ b/lib/log_blackbox.c
@@ -1,298 +1,304 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <qb/qbrb.h>
#include "util_int.h"
#include "log_int.h"
#include "ringbuffer_int.h"
#define BB_MIN_ENTRY_SIZE (4 * sizeof(uint32_t) +\
sizeof(uint8_t) +\
2 * sizeof(char) + sizeof(time_t))
static void
_blackbox_reload(int32_t target)
{
struct qb_log_target *t = qb_log_target_get(target);
if (t->instance == NULL) {
return;
}
qb_rb_close(t->instance);
t->instance = qb_rb_open(t->filename, t->size,
QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
}
/* <u32> file lineno
* <u32> tags
* <u8> priority
* <u32> function name length
* <string> function name
* <u32> buffer length
* <string> buffer
*/
static void
_blackbox_vlogger(int32_t target,
struct qb_log_callsite *cs, time_t timestamp, va_list ap)
{
size_t max_size;
size_t actual_size;
uint32_t fn_size;
char *chunk;
char *msg_len_pt;
uint32_t msg_len;
struct qb_log_target *t = qb_log_target_get(target);
if (t->instance == NULL) {
return;
}
fn_size = strlen(cs->function) + 1;
actual_size = 4 * sizeof(uint32_t) + sizeof(uint8_t) + fn_size + sizeof(time_t);
max_size = actual_size + QB_LOG_MAX_LEN;
chunk = qb_rb_chunk_alloc(t->instance, max_size);
if (chunk == NULL) {
/* something bad has happened. abort blackbox logging */
qb_util_perror(LOG_ERR, "Blackbox allocation error, aborting blackbox log %s", t->filename);
qb_rb_close(qb_rb_lastref_and_ret(
(struct qb_ringbuffer_s **) &t->instance
));
return;
}
/* line number */
memcpy(chunk, &cs->lineno, sizeof(uint32_t));
chunk += sizeof(uint32_t);
/* tags */
memcpy(chunk, &cs->tags, sizeof(uint32_t));
chunk += sizeof(uint32_t);
/* log level/priority */
memcpy(chunk, &cs->priority, sizeof(uint8_t));
chunk += sizeof(uint8_t);
/* function name */
memcpy(chunk, &fn_size, sizeof(uint32_t));
chunk += sizeof(uint32_t);
memcpy(chunk, cs->function, fn_size);
chunk += fn_size;
/* timestamp */
memcpy(chunk, &timestamp, sizeof(time_t));
chunk += sizeof(time_t);
/* log message length */
msg_len_pt = chunk;
chunk += sizeof(uint32_t);
/* log message */
msg_len = qb_vsnprintf_serialize(chunk, QB_LOG_MAX_LEN, cs->format, ap);
if (msg_len >= QB_LOG_MAX_LEN) {
chunk = msg_len_pt + sizeof(uint32_t); /* Reset */
msg_len = qb_vsnprintf_serialize(chunk, QB_LOG_MAX_LEN,
"Log message too long to be stored in the blackbox. "\
"Maximum is QB_LOG_MAX_LEN" , ap);
actual_size += msg_len;
}
actual_size += msg_len;
/* now that we know the length, write it
*/
memcpy(msg_len_pt, &msg_len, sizeof(uint32_t));
(void)qb_rb_chunk_commit(t->instance, actual_size);
}
static void
_blackbox_close(int32_t target)
{
struct qb_log_target *t = qb_log_target_get(target);
qb_rb_close(qb_rb_lastref_and_ret(
(struct qb_ringbuffer_s **) &t->instance
));
}
int32_t
qb_log_blackbox_open(struct qb_log_target *t)
{
if (t->size < 1024) {
return -EINVAL;
}
snprintf(t->filename, PATH_MAX, "%s-%d-blackbox", t->name, getpid());
t->instance = qb_rb_open(t->filename, t->size,
QB_RB_FLAG_CREATE | QB_RB_FLAG_OVERWRITE, 0);
if (t->instance == NULL) {
return -errno;
}
t->logger = NULL;
t->vlogger = _blackbox_vlogger;
t->reload = _blackbox_reload;
t->close = _blackbox_close;
return 0;
}
ssize_t
qb_log_blackbox_write_to_file(const char *filename)
{
ssize_t written_size = 0;
struct qb_log_target *t;
int fd = open(filename, O_CREAT | O_RDWR, 0700);
if (fd < 0) {
return -errno;
}
t = qb_log_target_get(QB_LOG_BLACKBOX);
if (t->instance) {
written_size = qb_rb_write_to_file(t->instance, fd);
} else {
written_size = -ENOENT;
}
close(fd);
return written_size;
}
void
qb_log_blackbox_print_from_file(const char *bb_filename)
{
qb_ringbuffer_t *instance;
ssize_t bytes_read;
int max_size = 2 * QB_LOG_MAX_LEN;
char *chunk;
int fd;
char time_buf[64];
fd = open(bb_filename, 0);
if (fd < 0) {
qb_util_perror(LOG_ERR, "qb_log_blackbox_print_from_file");
return;
}
instance = qb_rb_create_from_file(fd, 0);
close(fd);
if (instance == NULL) {
return;
}
chunk = malloc(max_size);
do {
char *ptr;
uint32_t lineno;
uint32_t tags;
uint8_t priority;
uint32_t fn_size;
char *function;
uint32_t len;
time_t timestamp;
uint32_t msg_len;
struct tm *tm;
char message[QB_LOG_MAX_LEN];
bytes_read = qb_rb_chunk_read(instance, chunk, max_size, 0);
if (bytes_read >= 0 && bytes_read < BB_MIN_ENTRY_SIZE) {
printf("ERROR Corrupt file: blackbox header too small.\n");
goto cleanup;
} else if (bytes_read < 0) {
errno = -bytes_read;
perror("ERROR: qb_rb_chunk_read failed");
goto cleanup;
}
ptr = chunk;
/* lineno */
memcpy(&lineno, ptr, sizeof(uint32_t));
ptr += sizeof(uint32_t);
/* tags */
memcpy(&tags, ptr, sizeof(uint32_t));
ptr += sizeof(uint32_t);
/* priority */
memcpy(&priority, ptr, sizeof(uint8_t));
ptr += sizeof(uint8_t);
/* function size & name */
memcpy(&fn_size, ptr, sizeof(uint32_t));
if ((fn_size + BB_MIN_ENTRY_SIZE) > bytes_read) {
- printf("ERROR Corrupt file: fn_size way too big %d\n", fn_size);
+#ifndef S_SPLINT_S
+ printf("ERROR Corrupt file: fn_size way too big %" PRIu32 "\n", fn_size);
+#endif /* S_SPLINT_S */
goto cleanup;
}
if (fn_size <= 0) {
- printf("ERROR Corrupt file: fn_size negative %d\n", fn_size);
+#ifndef S_SPLINT_S
+ printf("ERROR Corrupt file: fn_size negative %" PRIu32 "\n", fn_size);
+#endif /* S_SPLINT_S */
goto cleanup;
}
ptr += sizeof(uint32_t);
function = ptr;
ptr += fn_size;
/* timestamp size & content */
memcpy(&timestamp, ptr, sizeof(time_t));
ptr += sizeof(time_t);
tm = localtime(&timestamp);
if (tm) {
(void)strftime(time_buf,
sizeof(time_buf), "%b %d %T",
tm);
} else {
snprintf(time_buf, sizeof(time_buf), "%ld",
(long int)timestamp);
}
/* message length */
memcpy(&msg_len, ptr, sizeof(uint32_t));
if (msg_len > QB_LOG_MAX_LEN || msg_len <= 0) {
- printf("ERROR Corrupt file: msg_len out of bounds %d\n", msg_len);
+#ifndef S_SPLINT_S
+ printf("ERROR Corrupt file: msg_len out of bounds %" PRIu32 "\n", msg_len);
+#endif /* S_SPLINT_S */
goto cleanup;
}
ptr += sizeof(uint32_t);
/* message content */
len = qb_vsnprintf_deserialize(message, QB_LOG_MAX_LEN, ptr);
assert(len > 0);
message[len] = '\0';
len--;
while (len > 0 && (message[len] == '\n' || message[len] == '\0')) {
message[len] = '\0';
len--;
}
printf("%-7s %s %s(%u):%u: %s\n",
qb_log_priority2str(priority),
time_buf, function, lineno, tags, message);
} while (bytes_read > BB_MIN_ENTRY_SIZE);
cleanup:
qb_rb_close(instance);
free(chunk);
}
diff --git a/lib/log_format.c b/lib/log_format.c
index 36305c9..888b393 100644
--- a/lib/log_format.c
+++ b/lib/log_format.c
@@ -1,908 +1,910 @@
/*
* Copyright (C) 2011,2016 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <ctype.h>
#include <qb/qbdefs.h>
#include "log_int.h"
static qb_log_tags_stringify_fn _user_tags_stringify_fn;
/*
* syslog prioritynames, facility names to value mapping
* Some C libraries build this in to their headers, but it is non-portable
* so logsys supplies its own version.
*/
struct syslog_names {
const char *c_name;
int32_t c_val;
};
static struct syslog_names prioritynames[] = {
{"emerg", LOG_EMERG},
{"alert", LOG_ALERT},
{"crit", LOG_CRIT},
{"error", LOG_ERR},
{"warning", LOG_WARNING},
{"notice", LOG_NOTICE},
{"info", LOG_INFO},
{"debug", LOG_DEBUG},
{"trace", LOG_TRACE},
{NULL, -1}
};
static struct syslog_names facilitynames[] = {
{"auth", LOG_AUTH},
#if defined(LOG_AUTHPRIV)
{"authpriv", LOG_AUTHPRIV},
#endif
{"cron", LOG_CRON},
{"daemon", LOG_DAEMON},
#if defined(LOG_FTP)
{"ftp", LOG_FTP},
#endif
{"kern", LOG_KERN},
{"lpr", LOG_LPR},
{"mail", LOG_MAIL},
{"news", LOG_NEWS},
{"syslog", LOG_SYSLOG},
{"user", LOG_USER},
{"uucp", LOG_UUCP},
{"local0", LOG_LOCAL0},
{"local1", LOG_LOCAL1},
{"local2", LOG_LOCAL2},
{"local3", LOG_LOCAL3},
{"local4", LOG_LOCAL4},
{"local5", LOG_LOCAL5},
{"local6", LOG_LOCAL6},
{"local7", LOG_LOCAL7},
{NULL, -1}
};
static const char log_month_name[][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static pthread_rwlock_t _formatlock;
void
qb_log_format_init(void)
{
int32_t l;
struct qb_log_target *t;
enum qb_log_target_slot i;
l = pthread_rwlock_init(&_formatlock, NULL);
assert(l == 0);
for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
t = qb_log_target_get(i);
t->format = strdup("[%p] %b");
}
}
void
qb_log_format_fini(void)
{
struct qb_log_target *t;
enum qb_log_target_slot i;
pthread_rwlock_destroy(&_formatlock);
for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
t = qb_log_target_get(i);
free(t->format);
}
}
void
qb_log_format_set(int32_t target, const char *format)
{
char modified_format[256];
struct qb_log_target *t = qb_log_target_get(target);
pthread_rwlock_wrlock(&_formatlock);
free(t->format);
if (format) {
qb_log_target_format_static(target, format, modified_format);
t->format = strdup(modified_format);
} else {
t->format = strdup("[%p] %b");
}
assert(t->format != NULL);
pthread_rwlock_unlock(&_formatlock);
}
/* Convert string "auth" to equivalent number "LOG_AUTH" etc. */
int32_t
qb_log_facility2int(const char *fname)
{
int32_t i;
if (fname == NULL) {
return -EINVAL;
}
for (i = 0; facilitynames[i].c_name != NULL; i++) {
if (strcmp(fname, facilitynames[i].c_name) == 0) {
return facilitynames[i].c_val;
}
}
return -EINVAL;
}
/* Convert number "LOG_AUTH" to equivalent string "auth" etc. */
const char *
qb_log_facility2str(int32_t fnum)
{
int32_t i;
for (i = 0; facilitynames[i].c_name != NULL; i++) {
if (facilitynames[i].c_val == fnum) {
return facilitynames[i].c_name;
}
}
return NULL;
}
const char *
qb_log_priority2str(uint8_t priority)
{
if (priority > LOG_TRACE) {
return prioritynames[LOG_TRACE].c_name;
}
return prioritynames[priority].c_name;
}
void
qb_log_tags_stringify_fn_set(qb_log_tags_stringify_fn fn)
{
_user_tags_stringify_fn = fn;
}
static int
_strcpy_cutoff(char *dest, const char *src, size_t cutoff, int ralign,
size_t buf_len)
{
size_t len = strlen(src);
if (buf_len <= 1) {
if (buf_len == 0)
dest[0] = 0;
return 0;
}
if (cutoff == 0) {
cutoff = len;
}
cutoff = QB_MIN(cutoff, buf_len - 1);
len = QB_MIN(len, cutoff);
if (ralign) {
memset(dest, ' ', cutoff - len);
memcpy(dest + cutoff - len, src, len);
} else {
memcpy(dest, src, len);
memset(dest + len, ' ', cutoff - len);
}
dest[cutoff] = '\0';
return cutoff;
}
/*
* This function will do static formatting (for things that don't
* change on each log message).
*
* %P PID
* %N name passed into qb_log_init
* %H hostname
*
* any number between % and character specify field length to pad or chop
*/
void
qb_log_target_format_static(int32_t target, const char * format,
char *output_buffer)
{
char tmp_buf[255];
unsigned int format_buffer_idx = 0;
unsigned int output_buffer_idx = 0;
size_t cutoff;
uint32_t len;
int ralign;
int c;
struct qb_log_target *t = qb_log_target_get(target);
if (format == NULL) {
return;
}
while ((c = format[format_buffer_idx])) {
cutoff = 0;
ralign = QB_FALSE;
if (c != '%') {
output_buffer[output_buffer_idx++] = c;
format_buffer_idx++;
} else {
const char *p;
unsigned int percent_buffer_idx = format_buffer_idx;
format_buffer_idx += 1;
if (format[format_buffer_idx] == '-') {
ralign = QB_TRUE;
format_buffer_idx += 1;
}
if (isdigit(format[format_buffer_idx])) {
cutoff = atoi(&format[format_buffer_idx]);
}
while (isdigit(format[format_buffer_idx])) {
format_buffer_idx += 1;
}
switch (format[format_buffer_idx]) {
case 'P':
snprintf(tmp_buf, 30, "%d", getpid());
p = tmp_buf;
break;
case 'N':
p = t->name;
break;
case 'H':
if (gethostname(tmp_buf, sizeof(tmp_buf)) == 0) {
tmp_buf[sizeof(tmp_buf) - 1] = '\0';
} else {
(void)strlcpy(tmp_buf, "localhost",
sizeof(tmp_buf));
}
p = tmp_buf;
break;
default:
p = &format[percent_buffer_idx];
cutoff = (format_buffer_idx - percent_buffer_idx + 1);
ralign = QB_FALSE;
break;
}
len = _strcpy_cutoff(output_buffer + output_buffer_idx,
p, cutoff, ralign,
(QB_LOG_MAX_LEN -
output_buffer_idx));
output_buffer_idx += len;
format_buffer_idx += 1;
}
if (output_buffer_idx >= QB_LOG_MAX_LEN - 1) {
break;
}
}
output_buffer[output_buffer_idx] = '\0';
}
/*
* %n FUNCTION NAME
* %f FILENAME
* %l FILELINE
* %p PRIORITY
* %t TIMESTAMP
* %b BUFFER
* %g SUBSYSTEM
*
* any number between % and character specify field length to pad or chop
*/
void
qb_log_target_format(int32_t target,
struct qb_log_callsite *cs,
time_t current_time,
const char *formatted_message, char *output_buffer)
{
char tmp_buf[128];
struct tm tm_res;
unsigned int format_buffer_idx = 0;
unsigned int output_buffer_idx = 0;
size_t cutoff;
uint32_t len;
int ralign;
int c;
struct qb_log_target *t = qb_log_target_get(target);
pthread_rwlock_rdlock(&_formatlock);
if (t->format == NULL) {
pthread_rwlock_unlock(&_formatlock);
return;
}
while ((c = t->format[format_buffer_idx])) {
cutoff = 0;
ralign = QB_FALSE;
if (c != '%') {
output_buffer[output_buffer_idx++] = c;
format_buffer_idx++;
} else {
const char *p;
format_buffer_idx += 1;
if (t->format[format_buffer_idx] == '-') {
ralign = QB_TRUE;
format_buffer_idx += 1;
}
if (isdigit(t->format[format_buffer_idx])) {
cutoff = atoi(&t->format[format_buffer_idx]);
}
while (isdigit(t->format[format_buffer_idx])) {
format_buffer_idx += 1;
}
switch (t->format[format_buffer_idx]) {
case 'g':
if (_user_tags_stringify_fn) {
p = _user_tags_stringify_fn(cs->tags);
} else {
p = "";
}
break;
case 'n':
p = cs->function;
break;
case 'f':
#ifdef BUILDING_IN_PLACE
p = cs->filename;
#else
p = strrchr(cs->filename, '/');
if (p == NULL) {
p = cs->filename;
} else {
p++; /* move past the "/" */
}
#endif /* BUILDING_IN_PLACE */
break;
case 'l':
- snprintf(tmp_buf, 30, "%d", cs->lineno);
+#ifndef S_SPLINT_S
+ snprintf(tmp_buf, 30, "%" PRIu32, cs->lineno);
+#endif /* S_SPLINT_S */
p = tmp_buf;
break;
case 't':
(void)localtime_r(&current_time, &tm_res);
snprintf(tmp_buf, TIME_STRING_SIZE,
"%s %02d %02d:%02d:%02d",
log_month_name[tm_res.tm_mon],
tm_res.tm_mday, tm_res.tm_hour,
tm_res.tm_min, tm_res.tm_sec);
p = tmp_buf;
break;
case 'b':
p = formatted_message;
break;
case 'p':
if (cs->priority > LOG_TRACE) {
p = prioritynames[LOG_TRACE].c_name;
} else {
p = prioritynames[cs->priority].c_name;
}
break;
default:
p = "";
break;
}
len = _strcpy_cutoff(output_buffer + output_buffer_idx,
p, cutoff, ralign,
(QB_LOG_MAX_LEN -
output_buffer_idx));
output_buffer_idx += len;
format_buffer_idx += 1;
}
if (output_buffer_idx >= QB_LOG_MAX_LEN - 1) {
break;
}
}
pthread_rwlock_unlock(&_formatlock);
if (output_buffer[output_buffer_idx - 1] == '\n') {
output_buffer[output_buffer_idx - 1] = '\0';
} else {
output_buffer[output_buffer_idx] = '\0';
}
}
/*
* These wrappers around strl* functions just return the
* number of characters written, not the number of characters
* requested to be written.
*/
static size_t
my_strlcpy(char *dest, const char * src, size_t maxlen)
{
size_t rc = strlcpy(dest, src, maxlen);
/* maxlen includes NUL, so -1 */
return QB_MIN(rc, maxlen-1);
}
static size_t
my_strlcat(char *dest, const char * src, size_t maxlen)
{
size_t rc = strlcat(dest, src, maxlen);
return QB_MIN(rc, maxlen-1);
}
size_t
qb_vsnprintf_serialize(char *serialize, size_t max_len,
const char *fmt, va_list ap)
{
char *format;
char *p;
char *qb_xc;
int type_long = QB_FALSE;
int type_longlong = QB_FALSE;
size_t sformat_length = 0;
int sformat_precision = QB_FALSE;
uint32_t location = my_strlcpy(serialize, fmt, max_len) + 1;
/* Assume serialized output always wants extended information
* (@todo: add variant of this function that takes argument for whether
* to print extended information, and make this a macro with that
* argument set to QB_TRUE, so callers can honor extended setting)
*/
if ((qb_xc = strchr(serialize, QB_XC)) != NULL) {
*qb_xc = *(qb_xc + 1)? '|' : '\0';
}
format = (char *)fmt;
for (;;) {
type_long = QB_FALSE;
type_longlong = QB_FALSE;
p = strchrnul((const char *)format, '%');
if (*p == '\0') {
break;
}
format = p + 1;
reprocess:
switch (format[0]) {
case '#': /* alternate form conversion, ignore */
case '-': /* left adjust, ignore */
case ' ': /* a space, ignore */
case '+': /* a sign should be used, ignore */
case '\'': /* group in thousands, ignore */
case 'I': /* glibc-ism locale alternative, ignore */
format++;
goto reprocess;
case '.': /* precision, ignore */
format++;
sformat_precision = QB_TRUE;
goto reprocess;
case '0': /* field width, ignore */
case '1': /* field width, ignore */
case '2': /* field width, ignore */
case '3': /* field width, ignore */
case '4': /* field width, ignore */
case '5': /* field width, ignore */
case '6': /* field width, ignore */
case '7': /* field width, ignore */
case '8': /* field width, ignore */
case '9': /* field width, ignore */
if (sformat_precision) {
sformat_length *= 10;
sformat_length += (format[0] - '0');
}
format++;
goto reprocess;
case '*': /* variable field width, save */ {
int arg_int = va_arg(ap, int);
if (location + sizeof (int) > max_len) {
return max_len;
}
memcpy(&serialize[location], &arg_int, sizeof (int));
location += sizeof(int);
format++;
goto reprocess;
}
case 'l':
format++;
type_long = QB_TRUE;
if (*format == 'l') {
type_long = QB_FALSE;
type_longlong = QB_TRUE;
format++;
}
goto reprocess;
case 'z':
format++;
if (sizeof(size_t) == sizeof(long long)) {
type_longlong = QB_TRUE;
} else {
type_long = QB_TRUE;
}
goto reprocess;
case 't':
format++;
if (sizeof(ptrdiff_t) == sizeof(long long)) {
type_longlong = QB_TRUE;
} else {
type_long = QB_TRUE;
}
goto reprocess;
case 'j':
format++;
if (sizeof(intmax_t) == sizeof(long long)) {
type_longlong = QB_TRUE;
} else {
type_long = QB_TRUE;
}
goto reprocess;
case 'd': /* int argument */
case 'i': /* int argument */
case 'o': /* unsigned int argument */
case 'u':
case 'x':
case 'X':
if (type_long) {
long int arg_int;
if (location + sizeof (long int) > max_len) {
return max_len;
}
arg_int = va_arg(ap, long int);
memcpy(&serialize[location], &arg_int,
sizeof(long int));
location += sizeof(long int);
format++;
break;
} else if (type_longlong) {
long long int arg_int;
if (location + sizeof (long long int) > max_len) {
return max_len;
}
arg_int = va_arg(ap, long long int);
memcpy(&serialize[location], &arg_int,
sizeof(long long int));
location += sizeof(long long int);
format++;
break;
} else {
int arg_int;
if (location + sizeof (int) > max_len) {
return max_len;
}
arg_int = va_arg(ap, int);
memcpy(&serialize[location], &arg_int,
sizeof(int));
location += sizeof(int);
format++;
break;
}
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
{
double arg_double;
if (location + sizeof (double) > max_len) {
return max_len;
}
arg_double = va_arg(ap, double);
memcpy (&serialize[location], &arg_double, sizeof (double));
location += sizeof(double);
format++;
break;
}
case 'c':
{
int arg_int;
unsigned char arg_char;
if (location + sizeof (unsigned char) > max_len) {
return max_len;
}
/* va_arg only takes fully promoted types */
arg_int = va_arg(ap, unsigned int);
arg_char = (unsigned char)arg_int;
memcpy (&serialize[location], &arg_char, sizeof (unsigned char));
location += sizeof(unsigned char);
break;
}
case 's':
{
char *arg_string;
arg_string = va_arg(ap, char *);
if (arg_string == NULL) {
location += my_strlcpy(&serialize[location],
"(null)",
QB_MIN(strlen("(null)") + 1,
max_len - location));
} else if (sformat_length) {
location += my_strlcpy(&serialize[location],
arg_string,
QB_MIN(sformat_length + 1,
(max_len - location)));
} else {
location += my_strlcpy(&serialize[location],
arg_string,
QB_MIN(strlen(arg_string) + 1,
max_len - location));
}
location++;
break;
}
case 'p':
{
ptrdiff_t arg_pointer = va_arg(ap, ptrdiff_t);
if (location + sizeof (ptrdiff_t) > max_len) {
return max_len;
}
memcpy(&serialize[location], &arg_pointer, sizeof(ptrdiff_t));
location += sizeof(ptrdiff_t);
break;
}
case '%':
if (location + 1 > max_len) {
return max_len;
}
serialize[location++] = '%';
sformat_length = 0;
sformat_precision = QB_FALSE;
break;
}
}
return (location);
}
#define MINI_FORMAT_STR_LEN 20
size_t
qb_vsnprintf_deserialize(char *string, size_t str_len, const char *buf)
{
char *p;
char *format;
char fmt[MINI_FORMAT_STR_LEN];
int fmt_pos;
uint32_t location = 0;
uint32_t data_pos = strlen(buf) + 1;
int type_long = QB_FALSE;
int type_longlong = QB_FALSE;
int len;
string[0] = '\0';
format = (char *)buf;
for (;;) {
type_long = QB_FALSE;
type_longlong = QB_FALSE;
p = strchrnul((const char *)format, '%');
if (*p == '\0') {
return my_strlcat(string, format, str_len) + 1;
}
/* copy from current to the next % */
len = p - format;
memcpy(&string[location], format, len);
location += len;
format = p;
/* start building up the format for snprintf */
fmt_pos = 0;
fmt[fmt_pos++] = *format;
format++;
reprocess:
switch (format[0]) {
case '#': /* alternate form conversion, ignore */
case '-': /* left adjust, ignore */
case ' ': /* a space, ignore */
case '+': /* a sign should be used, ignore */
case '\'': /* group in thousands, ignore */
case 'I': /* glibc-ism locale alternative, ignore */
case '.': /* precision, ignore */
case '0': /* field width, ignore */
case '1': /* field width, ignore */
case '2': /* field width, ignore */
case '3': /* field width, ignore */
case '4': /* field width, ignore */
case '5': /* field width, ignore */
case '6': /* field width, ignore */
case '7': /* field width, ignore */
case '8': /* field width, ignore */
case '9': /* field width, ignore */
fmt[fmt_pos++] = *format;
format++;
goto reprocess;
case '*': {
int arg_int;
memcpy(&arg_int, &buf[data_pos], sizeof(int));
data_pos += sizeof(int);
fmt_pos += snprintf(&fmt[fmt_pos],
MINI_FORMAT_STR_LEN - fmt_pos,
"%d", arg_int);
format++;
goto reprocess;
}
case 'l':
fmt[fmt_pos++] = *format;
format++;
type_long = QB_TRUE;
if (*format == 'l') {
type_long = QB_FALSE;
type_longlong = QB_TRUE;
}
goto reprocess;
case 'z':
fmt[fmt_pos++] = *format;
format++;
if (sizeof(size_t) == sizeof(long long)) {
type_long = QB_FALSE;
type_longlong = QB_TRUE;
} else {
type_longlong = QB_FALSE;
type_long = QB_TRUE;
}
goto reprocess;
case 't':
fmt[fmt_pos++] = *format;
format++;
if (sizeof(ptrdiff_t) == sizeof(long long)) {
type_longlong = QB_TRUE;
} else {
type_long = QB_TRUE;
}
goto reprocess;
case 'j':
fmt[fmt_pos++] = *format;
format++;
if (sizeof(intmax_t) == sizeof(long long)) {
type_longlong = QB_TRUE;
} else {
type_long = QB_TRUE;
}
goto reprocess;
case 'd': /* int argument */
case 'i': /* int argument */
case 'o': /* unsigned int argument */
case 'u':
case 'x':
case 'X':
if (type_long) {
long int arg_int;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_int, &buf[data_pos], sizeof(long int));
location += snprintf(&string[location],
str_len - location,
fmt, arg_int);
data_pos += sizeof(long int);
format++;
break;
} else if (type_longlong) {
long long int arg_int;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_int, &buf[data_pos], sizeof(long long int));
location += snprintf(&string[location],
str_len - location,
fmt, arg_int);
data_pos += sizeof(long long int);
format++;
break;
} else {
int arg_int;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_int, &buf[data_pos], sizeof(int));
location += snprintf(&string[location],
str_len - location,
fmt, arg_int);
data_pos += sizeof(int);
format++;
break;
}
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
{
double arg_double;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
memcpy(&arg_double, &buf[data_pos], sizeof(double));
location += snprintf(&string[location],
str_len - location,
fmt, arg_double);
data_pos += sizeof(double);
format++;
break;
}
case 'c':
{
unsigned char *arg_char;
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
arg_char = (unsigned char*)&buf[data_pos];
location += snprintf(&string[location],
str_len - location,
fmt, *arg_char);
data_pos += sizeof(unsigned char);
format++;
break;
}
case 's':
{
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
len = snprintf(&string[location],
str_len - location,
fmt, &buf[data_pos]);
location += len;
/* don't use len as there might be a len modifier */
data_pos += strlen(&buf[data_pos]) + 1;
format++;
break;
}
case 'p':
{
ptrdiff_t pt;
memcpy(&pt, &buf[data_pos],
sizeof(ptrdiff_t));
fmt[fmt_pos++] = *format;
fmt[fmt_pos++] = '\0';
location += snprintf(&string[location],
str_len - location,
fmt, pt);
data_pos += sizeof(void*);
format++;
break;
}
case '%':
string[location++] = '%';
format++;
break;
}
}
return location;
}
diff --git a/lib/ringbuffer.c b/lib/ringbuffer.c
index 3f401f0..81411cb 100644
--- a/lib/ringbuffer.c
+++ b/lib/ringbuffer.c
@@ -1,935 +1,935 @@
/*
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ringbuffer_int.h"
#include <qb/qbdefs.h>
#include "atomic_int.h"
#define QB_RB_FILE_HEADER_VERSION 1
/*
* #define CRAZY_DEBUG_PRINTFS 1
*/
#ifdef CRAZY_DEBUG_PRINTFS
#define DEBUG_PRINTF(format, args...) \
do { \
printf(format, ##args); \
} while(0)
#else
#define DEBUG_PRINTF(format, args...)
#endif /* CRAZY_DEBUG_PRINTFS */
/*
* move the write pointer to the next 128 byte boundary
* write_pt goes in 4 bytes (sizeof(uint32_t))
* #define USE_CACHE_LINE_ALIGNMENT 1
*/
#ifdef USE_CACHE_LINE_ALIGNMENT
#define QB_CACHE_LINE_SIZE 128
#define QB_CACHE_LINE_WORDS (QB_CACHE_LINE_SIZE/sizeof(uint32_t))
#define idx_cache_line_step(idx) \
do { \
if (idx % QB_CACHE_LINE_WORDS) { \
idx += (QB_CACHE_LINE_WORDS - (idx % QB_CACHE_LINE_WORDS)); \
} \
if (idx > (rb->shared_hdr->word_size - 1)) { \
idx = ((idx) % (rb->shared_hdr->word_size)); \
} \
} while (0)
#else
#define QB_CACHE_LINE_SIZE 0
#define QB_CACHE_LINE_WORDS 0
#define idx_cache_line_step(idx) \
do { \
if (idx > (rb->shared_hdr->word_size - 1)) { \
idx = ((idx) % (rb->shared_hdr->word_size)); \
} \
} while (0)
#endif
/* the chunk header is two words
* 1) the chunk data size
* 2) the magic number
*/
#define QB_RB_CHUNK_HEADER_WORDS 2
#define QB_RB_CHUNK_HEADER_SIZE (sizeof(uint32_t) * QB_RB_CHUNK_HEADER_WORDS)
/*
* margin is the gap we leave when checking to see if we have enough
* space for a new chunk.
* So:
* qb_rb_space_free() >= QB_RB_CHUNK_MARGIN + new data chunk
* The extra word size is to allow for non word sized data chunks.
* QB_CACHE_LINE_WORDS is to make sure we have space to align the
* chunk.
*/
#define QB_RB_WORD_ALIGN 1
#define QB_RB_CHUNK_MARGIN (sizeof(uint32_t) * (QB_RB_CHUNK_HEADER_WORDS +\
QB_RB_WORD_ALIGN +\
QB_CACHE_LINE_WORDS))
#define QB_RB_CHUNK_MAGIC 0xA1A1A1A1
#define QB_RB_CHUNK_MAGIC_DEAD 0xD0D0D0D0
#define QB_RB_CHUNK_MAGIC_ALLOC 0xA110CED0
#define QB_RB_CHUNK_SIZE_GET(rb, pointer) rb->shared_data[pointer]
#define QB_RB_CHUNK_MAGIC_GET(rb, pointer) \
qb_atomic_int_get_ex((int32_t*)&rb->shared_data[(pointer + 1) % rb->shared_hdr->word_size], \
QB_ATOMIC_ACQUIRE)
#define QB_RB_CHUNK_MAGIC_SET(rb, pointer, new_val) \
qb_atomic_int_set_ex((int32_t*)&rb->shared_data[(pointer + 1) % rb->shared_hdr->word_size], \
new_val, QB_ATOMIC_RELEASE)
#define QB_RB_CHUNK_DATA_GET(rb, pointer) \
&rb->shared_data[(pointer + QB_RB_CHUNK_HEADER_WORDS) % rb->shared_hdr->word_size]
#define QB_MAGIC_ASSERT(_ptr_) \
do { \
uint32_t chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, _ptr_); \
if (chunk_magic != QB_RB_CHUNK_MAGIC) print_header(rb); \
assert(chunk_magic == QB_RB_CHUNK_MAGIC); \
} while (0)
#define idx_step(idx) \
do { \
if (idx > (rb->shared_hdr->word_size - 1)) { \
idx = ((idx) % (rb->shared_hdr->word_size)); \
} \
} while (0)
static void print_header(struct qb_ringbuffer_s * rb);
static int _rb_chunk_reclaim(struct qb_ringbuffer_s * rb);
qb_ringbuffer_t *
qb_rb_open(const char *name, size_t size, uint32_t flags,
size_t shared_user_data_size)
{
return qb_rb_open_2(name, size, flags, shared_user_data_size, NULL);
}
qb_ringbuffer_t *
qb_rb_open_2(const char *name, size_t size, uint32_t flags,
size_t shared_user_data_size,
struct qb_rb_notifier *notifiers)
{
struct qb_ringbuffer_s *rb;
size_t real_size;
size_t shared_size;
char path[PATH_MAX];
int32_t fd_hdr;
int32_t fd_data;
uint32_t file_flags = O_RDWR;
char filename[PATH_MAX];
int32_t error = 0;
void *shm_addr;
long page_size = sysconf(_SC_PAGESIZE);
#ifdef QB_ARCH_HPPA
page_size = QB_MAX(page_size, 0x00400000); /* align to page colour */
#elif defined(QB_FORCE_SHM_ALIGN)
page_size = QB_MAX(page_size, 16 * 1024);
#endif /* QB_FORCE_SHM_ALIGN */
/* The user of this api expects the 'size' parameter passed into this function
* to be reflective of the max size single write we can do to the
* ringbuffer. This means we have to add both the 'margin' space used
* to calculate if there is enough space for a new chunk as well as the '+1' that
* prevents overlap of the read/write pointers */
size += QB_RB_CHUNK_MARGIN + 1;
real_size = QB_ROUNDUP(size, page_size);
shared_size =
sizeof(struct qb_ringbuffer_shared_s) + shared_user_data_size;
if (flags & QB_RB_FLAG_CREATE) {
file_flags |= O_CREAT | O_TRUNC;
}
rb = calloc(1, sizeof(struct qb_ringbuffer_s));
if (rb == NULL) {
return NULL;
}
/*
* Create a shared_hdr memory segment for the header.
*/
snprintf(filename, PATH_MAX, "qb-%s-header", name);
fd_hdr = qb_sys_mmap_file_open(path, filename,
shared_size, file_flags);
if (fd_hdr < 0) {
error = fd_hdr;
qb_util_log(LOG_ERR, "couldn't create file for mmap");
goto cleanup_hdr;
}
rb->shared_hdr = mmap(0,
shared_size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd_hdr, 0);
if (rb->shared_hdr == MAP_FAILED) {
error = -errno;
qb_util_log(LOG_ERR, "couldn't create mmap for header");
goto cleanup_hdr;
}
qb_atomic_init();
rb->flags = flags;
/*
* create the semaphore
*/
if (flags & QB_RB_FLAG_CREATE) {
rb->shared_data = NULL;
/* rb->shared_hdr->word_size tracks data by ints and not bytes/chars. */
rb->shared_hdr->word_size = real_size / sizeof(uint32_t);
rb->shared_hdr->write_pt = 0;
rb->shared_hdr->read_pt = 0;
(void)strlcpy(rb->shared_hdr->hdr_path, path, PATH_MAX);
}
if (notifiers && notifiers->post_fn) {
error = 0;
memcpy(&rb->notifier,
notifiers,
sizeof(struct qb_rb_notifier));
} else {
error = qb_rb_sem_create(rb, flags);
}
if (error < 0) {
errno = -error;
qb_util_perror(LOG_ERR, "couldn't create a semaphore");
goto cleanup_hdr;
}
/* Create the shared_data memory segment for the actual ringbuffer.
* They have to be separate.
*/
if (flags & QB_RB_FLAG_CREATE) {
snprintf(filename, PATH_MAX, "qb-%s-data", name);
fd_data = qb_sys_mmap_file_open(path,
filename,
real_size, file_flags);
(void)strlcpy(rb->shared_hdr->data_path, path, PATH_MAX);
} else {
fd_data = qb_sys_mmap_file_open(path,
rb->shared_hdr->data_path,
real_size, file_flags);
}
if (fd_data < 0) {
error = fd_data;
qb_util_log(LOG_ERR, "couldn't create file for mmap");
goto cleanup_hdr;
}
qb_util_log(LOG_DEBUG,
"shm size:%ld; real_size:%ld; rb->word_size:%d", size,
real_size, rb->shared_hdr->word_size);
/* this function closes fd_data */
error = qb_sys_circular_mmap(fd_data, &shm_addr, real_size);
rb->shared_data = shm_addr;
if (error != 0) {
qb_util_log(LOG_ERR, "couldn't create circular mmap on %s",
rb->shared_hdr->data_path);
goto cleanup_data;
}
if (flags & QB_RB_FLAG_CREATE) {
memset(rb->shared_data, 0, real_size);
rb->shared_data[rb->shared_hdr->word_size] = 5;
rb->shared_hdr->ref_count = 1;
} else {
qb_atomic_int_inc(&rb->shared_hdr->ref_count);
}
close(fd_hdr);
return rb;
cleanup_data:
if (flags & QB_RB_FLAG_CREATE) {
unlink(rb->shared_hdr->data_path);
}
cleanup_hdr:
if (fd_hdr >= 0) {
close(fd_hdr);
}
if (rb && (flags & QB_RB_FLAG_CREATE)) {
unlink(rb->shared_hdr->hdr_path);
if (rb->notifier.destroy_fn) {
(void)rb->notifier.destroy_fn(rb->notifier.instance);
}
}
if (rb && (rb->shared_hdr != MAP_FAILED && rb->shared_hdr != NULL)) {
munmap(rb->shared_hdr, sizeof(struct qb_ringbuffer_shared_s));
}
free(rb);
errno = -error;
return NULL;
}
void
qb_rb_close(struct qb_ringbuffer_s * rb)
{
if (rb == NULL) {
return;
}
qb_enter();
(void)qb_atomic_int_dec_and_test(&rb->shared_hdr->ref_count);
(void)qb_rb_close_helper(rb, rb->flags & QB_RB_FLAG_CREATE, QB_FALSE);
}
void
qb_rb_force_close(struct qb_ringbuffer_s * rb)
{
if (rb == NULL) {
return;
}
qb_enter();
qb_atomic_int_set(&rb->shared_hdr->ref_count, -1);
(void)qb_rb_close_helper(rb, QB_TRUE, QB_TRUE);
}
char *
qb_rb_name_get(struct qb_ringbuffer_s * rb)
{
if (rb == NULL) {
return NULL;
}
return rb->shared_hdr->hdr_path;
}
void *
qb_rb_shared_user_data_get(struct qb_ringbuffer_s * rb)
{
if (rb == NULL) {
return NULL;
}
return rb->shared_hdr->user_data;
}
int32_t
qb_rb_refcount_get(struct qb_ringbuffer_s * rb)
{
if (rb == NULL) {
return -EINVAL;
}
return qb_atomic_int_get(&rb->shared_hdr->ref_count);
}
ssize_t
qb_rb_space_free(struct qb_ringbuffer_s * rb)
{
uint32_t write_size;
uint32_t read_size;
size_t space_free = 0;
if (rb == NULL) {
return -EINVAL;
}
if (rb->notifier.space_used_fn) {
return (rb->shared_hdr->word_size * sizeof(uint32_t)) -
rb->notifier.space_used_fn(rb->notifier.instance);
}
write_size = rb->shared_hdr->write_pt;
read_size = rb->shared_hdr->read_pt;
if (write_size > read_size) {
space_free =
(read_size - write_size + rb->shared_hdr->word_size) - 1;
} else if (write_size < read_size) {
space_free = (read_size - write_size) - 1;
} else {
if (rb->notifier.q_len_fn && rb->notifier.q_len_fn(rb->notifier.instance) > 0) {
space_free = 0;
} else {
space_free = rb->shared_hdr->word_size;
}
}
/* word -> bytes */
return (space_free * sizeof(uint32_t));
}
ssize_t
qb_rb_space_used(struct qb_ringbuffer_s * rb)
{
uint32_t write_size;
uint32_t read_size;
size_t space_used;
if (rb == NULL) {
return -EINVAL;
}
if (rb->notifier.space_used_fn) {
return rb->notifier.space_used_fn(rb->notifier.instance);
}
write_size = rb->shared_hdr->write_pt;
read_size = rb->shared_hdr->read_pt;
if (write_size > read_size) {
space_used = write_size - read_size;
} else if (write_size < read_size) {
space_used =
(write_size - read_size + rb->shared_hdr->word_size) - 1;
} else {
space_used = 0;
}
/* word -> bytes */
return (space_used * sizeof(uint32_t));
}
ssize_t
qb_rb_chunks_used(struct qb_ringbuffer_s *rb)
{
if (rb == NULL) {
return -EINVAL;
}
if (rb->notifier.q_len_fn) {
return rb->notifier.q_len_fn(rb->notifier.instance);
}
return -ENOTSUP;
}
void *
qb_rb_chunk_alloc(struct qb_ringbuffer_s * rb, size_t len)
{
uint32_t write_pt;
if (rb == NULL) {
errno = EINVAL;
return NULL;
}
/*
* Reclaim data if we are over writing and we need space
*/
if (rb->flags & QB_RB_FLAG_OVERWRITE) {
while (qb_rb_space_free(rb) < (len + QB_RB_CHUNK_MARGIN)) {
int rc = _rb_chunk_reclaim(rb);
if (rc != 0) {
errno = rc;
return NULL;
}
}
} else {
if (qb_rb_space_free(rb) < (len + QB_RB_CHUNK_MARGIN)) {
errno = EAGAIN;
return NULL;
}
}
write_pt = rb->shared_hdr->write_pt;
/*
* insert the chunk header
*/
rb->shared_data[write_pt] = 0;
QB_RB_CHUNK_MAGIC_SET(rb, write_pt, QB_RB_CHUNK_MAGIC_ALLOC);
/*
* return a pointer to the beginning of the chunk data
*/
return (void *)QB_RB_CHUNK_DATA_GET(rb, write_pt);
}
static uint32_t
qb_rb_chunk_step(struct qb_ringbuffer_s * rb, uint32_t pointer)
{
uint32_t chunk_size = QB_RB_CHUNK_SIZE_GET(rb, pointer);
/*
* skip over the chunk header
*/
pointer += QB_RB_CHUNK_HEADER_WORDS;
/*
* skip over the user's data.
*/
pointer += (chunk_size / sizeof(uint32_t));
/* make allowance for non-word sizes */
if ((chunk_size % (sizeof(uint32_t) * QB_RB_WORD_ALIGN)) != 0) {
pointer++;
}
idx_cache_line_step(pointer);
return pointer;
}
int32_t
qb_rb_chunk_commit(struct qb_ringbuffer_s * rb, size_t len)
{
uint32_t old_write_pt;
if (rb == NULL) {
return -EINVAL;
}
/*
* commit the magic & chunk_size
*/
old_write_pt = rb->shared_hdr->write_pt;
rb->shared_data[old_write_pt] = len;
/*
* commit the new write pointer
*/
rb->shared_hdr->write_pt = qb_rb_chunk_step(rb, old_write_pt);
QB_RB_CHUNK_MAGIC_SET(rb, old_write_pt, QB_RB_CHUNK_MAGIC);
DEBUG_PRINTF("commit [%zd] read: %u, write: %u -> %u (%u)\n",
(rb->notifier.q_len_fn ?
rb->notifier.q_len_fn(rb->notifier.instance) : 0),
rb->shared_hdr->read_pt,
old_write_pt,
rb->shared_hdr->write_pt,
rb->shared_hdr->word_size);
/*
* post the notification to the reader
*/
if (rb->notifier.post_fn) {
return rb->notifier.post_fn(rb->notifier.instance, len);
}
return 0;
}
ssize_t
qb_rb_chunk_write(struct qb_ringbuffer_s * rb, const void *data, size_t len)
{
char *dest = qb_rb_chunk_alloc(rb, len);
int32_t res = 0;
if (rb == NULL) {
return -EINVAL;
}
if (dest == NULL) {
return -errno;
}
memcpy(dest, data, len);
res = qb_rb_chunk_commit(rb, len);
if (res < 0) {
return res;
}
return len;
}
static int
_rb_chunk_reclaim(struct qb_ringbuffer_s * rb)
{
uint32_t old_read_pt;
uint32_t new_read_pt;
uint32_t old_chunk_size;
uint32_t chunk_magic;
int rc = 0;
old_read_pt = rb->shared_hdr->read_pt;
chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, old_read_pt);
if (chunk_magic != QB_RB_CHUNK_MAGIC) {
return -EINVAL;
}
old_chunk_size = QB_RB_CHUNK_SIZE_GET(rb, old_read_pt);
new_read_pt = qb_rb_chunk_step(rb, old_read_pt);
/*
* clear the header
*/
rb->shared_data[old_read_pt] = 0;
QB_RB_CHUNK_MAGIC_SET(rb, old_read_pt, QB_RB_CHUNK_MAGIC_DEAD);
/*
* set the new read pointer after clearing the header
* to prevent a situation where a fast writer will write their
* new chunk between setting the new read pointer and clearing the
* header.
*/
rb->shared_hdr->read_pt = new_read_pt;
if (rb->notifier.reclaim_fn) {
rc = rb->notifier.reclaim_fn(rb->notifier.instance,
old_chunk_size);
if (rc < 0) {
errno = -rc;
qb_util_perror(LOG_WARNING, "reclaim_fn");
}
}
DEBUG_PRINTF("reclaim [%zd]: read: %u -> %u, write: %u\n",
(rb->notifier.q_len_fn ?
rb->notifier.q_len_fn(rb->notifier.instance) : 0),
old_read_pt,
rb->shared_hdr->read_pt,
rb->shared_hdr->write_pt);
return rc;
}
void
qb_rb_chunk_reclaim(struct qb_ringbuffer_s * rb)
{
if (rb == NULL) {
return;
}
_rb_chunk_reclaim(rb);
}
ssize_t
qb_rb_chunk_peek(struct qb_ringbuffer_s * rb, void **data_out, int32_t timeout)
{
uint32_t read_pt;
uint32_t chunk_size;
uint32_t chunk_magic;
int32_t res = 0;
if (rb == NULL) {
return -EINVAL;
}
if (rb->notifier.timedwait_fn) {
res = rb->notifier.timedwait_fn(rb->notifier.instance, timeout);
}
if (res < 0 && res != -EIDRM) {
if (res == -ETIMEDOUT) {
return 0;
} else {
errno = -res;
qb_util_perror(LOG_ERR, "sem_timedwait");
}
return res;
}
read_pt = rb->shared_hdr->read_pt;
chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, read_pt);
if (chunk_magic != QB_RB_CHUNK_MAGIC) {
if (rb->notifier.post_fn) {
(void)rb->notifier.post_fn(rb->notifier.instance, res);
}
#ifdef EBADMSG
return -EBADMSG;
#else
return -EINVAL;
#endif
}
chunk_size = QB_RB_CHUNK_SIZE_GET(rb, read_pt);
*data_out = QB_RB_CHUNK_DATA_GET(rb, read_pt);
return chunk_size;
}
ssize_t
qb_rb_chunk_read(struct qb_ringbuffer_s * rb, void *data_out, size_t len,
int32_t timeout)
{
uint32_t read_pt;
uint32_t chunk_size;
uint32_t chunk_magic;
int32_t res = 0;
if (rb == NULL) {
return -EINVAL;
}
if (rb->notifier.timedwait_fn) {
res = rb->notifier.timedwait_fn(rb->notifier.instance, timeout);
}
if (res < 0 && res != -EIDRM) {
if (res != -ETIMEDOUT) {
errno = -res;
qb_util_perror(LOG_ERR, "sem_timedwait");
}
return res;
}
read_pt = rb->shared_hdr->read_pt;
chunk_magic = QB_RB_CHUNK_MAGIC_GET(rb, read_pt);
if (chunk_magic != QB_RB_CHUNK_MAGIC) {
if (rb->notifier.timedwait_fn == NULL) {
return -ETIMEDOUT;
} else {
(void)rb->notifier.post_fn(rb->notifier.instance, res);
#ifdef EBADMSG
return -EBADMSG;
#else
return -EINVAL;
#endif
}
}
chunk_size = QB_RB_CHUNK_SIZE_GET(rb, read_pt);
if (len < chunk_size) {
qb_util_log(LOG_ERR,
"trying to recv chunk of size %d but %d available",
len, chunk_size);
if (rb->notifier.post_fn) {
(void)rb->notifier.post_fn(rb->notifier.instance, chunk_size);
}
return -ENOBUFS;
}
memcpy(data_out,
QB_RB_CHUNK_DATA_GET(rb, read_pt),
chunk_size);
_rb_chunk_reclaim(rb);
return chunk_size;
}
static void
print_header(struct qb_ringbuffer_s * rb)
{
printf("Ringbuffer: \n");
if (rb->flags & QB_RB_FLAG_OVERWRITE) {
printf(" ->OVERWRITE\n");
} else {
printf(" ->NORMAL\n");
}
- printf(" ->write_pt [%d]\n", rb->shared_hdr->write_pt);
- printf(" ->read_pt [%d]\n", rb->shared_hdr->read_pt);
- printf(" ->size [%d words]\n", rb->shared_hdr->word_size);
#ifndef S_SPLINT_S
- printf(" =>free [%zu bytes]\n", qb_rb_space_free(rb));
- printf(" =>used [%zu bytes]\n", qb_rb_space_used(rb));
+ printf(" ->write_pt [%" PRIu32 "]\n", rb->shared_hdr->write_pt);
+ printf(" ->read_pt [%" PRIu32 "]\n", rb->shared_hdr->read_pt);
+ printf(" ->size [%" PRIu32 " words]\n", rb->shared_hdr->word_size);
+ printf(" =>free [%zd bytes]\n", qb_rb_space_free(rb));
+ printf(" =>used [%zd bytes]\n", qb_rb_space_used(rb));
#endif /* S_SPLINT_S */
}
/*
* FILE HEADER ORDER
* 1. word_size
* 2. write_pt
* 3. read_pt
* 4. version
* 5. header_hash
*
* 6. data
*/
ssize_t
qb_rb_write_to_file(struct qb_ringbuffer_s * rb, int32_t fd)
{
ssize_t result;
ssize_t written_size = 0;
uint32_t hash = 0;
uint32_t version = QB_RB_FILE_HEADER_VERSION;
if (rb == NULL) {
return -EINVAL;
}
print_header(rb);
/*
* 1. word_size
*/
result = write(fd, &rb->shared_hdr->word_size, sizeof(uint32_t));
if (result != sizeof(uint32_t)) {
return -errno;
}
written_size += result;
/*
* 2. 3. store the read & write pointers
*/
result = write(fd, (void *)&rb->shared_hdr->write_pt, sizeof(uint32_t));
if (result != sizeof(uint32_t)) {
return -errno;
}
written_size += result;
result = write(fd, (void *)&rb->shared_hdr->read_pt, sizeof(uint32_t));
if (result != sizeof(uint32_t)) {
return -errno;
}
written_size += result;
/*
* 4. version used
*/
result = write(fd, &version, sizeof(uint32_t));
if (result != sizeof(uint32_t)) {
return -errno;
}
written_size += result;
/*
* 5. hash helps us verify header is not corrupted on file read
*/
hash = rb->shared_hdr->word_size + rb->shared_hdr->write_pt + rb->shared_hdr->read_pt + QB_RB_FILE_HEADER_VERSION;
result = write(fd, &hash, sizeof(uint32_t));
if (result != sizeof(uint32_t)) {
return -errno;
}
written_size += result;
result = write(fd, rb->shared_data,
rb->shared_hdr->word_size * sizeof(uint32_t));
if (result != rb->shared_hdr->word_size * sizeof(uint32_t)) {
return -errno;
}
written_size += result;
qb_util_log(LOG_DEBUG, " writing total of: %zd\n", written_size);
return written_size;
}
qb_ringbuffer_t *
qb_rb_create_from_file(int32_t fd, uint32_t flags)
{
ssize_t n_read;
size_t n_required;
size_t total_read = 0;
uint32_t read_pt;
uint32_t write_pt;
struct qb_ringbuffer_s *rb;
uint32_t word_size = 0;
uint32_t version = 0;
uint32_t hash = 0;
uint32_t calculated_hash = 0;
if (fd < 0) {
return NULL;
}
/*
* 1. word size
*/
n_required = sizeof(uint32_t);
n_read = read(fd, &word_size, n_required);
if (n_read != n_required) {
qb_util_perror(LOG_ERR, "Unable to read blackbox file header");
return NULL;
}
total_read += n_read;
/*
* 2. 3. read & write pointers
*/
n_read = read(fd, &write_pt, sizeof(uint32_t));
assert(n_read == sizeof(uint32_t));
total_read += n_read;
n_read = read(fd, &read_pt, sizeof(uint32_t));
assert(n_read == sizeof(uint32_t));
total_read += n_read;
/*
* 4. version
*/
n_required = sizeof(uint32_t);
n_read = read(fd, &version, n_required);
if (n_read != n_required) {
qb_util_perror(LOG_ERR, "Unable to read blackbox file header");
return NULL;
}
total_read += n_read;
/*
* 5. Hash
*/
n_required = sizeof(uint32_t);
n_read = read(fd, &hash, n_required);
if (n_read != n_required) {
qb_util_perror(LOG_ERR, "Unable to read blackbox file header");
return NULL;
}
total_read += n_read;
calculated_hash = word_size + write_pt + read_pt + version;
if (hash != calculated_hash) {
qb_util_log(LOG_ERR, "Corrupt blackbox: File header hash (%d) does not match calculated hash (%d)", hash, calculated_hash);
return NULL;
} else if (version != QB_RB_FILE_HEADER_VERSION) {
qb_util_log(LOG_ERR, "Wrong file header version. Expected %d got %d",
QB_RB_FILE_HEADER_VERSION, version);
return NULL;
}
/*
* 6. data
*/
n_required = (word_size * sizeof(uint32_t));
/*
* qb_rb_open adds QB_RB_CHUNK_MARGIN + 1 to the requested size.
*/
rb = qb_rb_open("create_from_file", n_required - (QB_RB_CHUNK_MARGIN + 1),
QB_RB_FLAG_CREATE | QB_RB_FLAG_NO_SEMAPHORE, 0);
if (rb == NULL) {
return NULL;
}
rb->shared_hdr->read_pt = read_pt;
rb->shared_hdr->write_pt = write_pt;
n_read = read(fd, rb->shared_data, n_required);
if (n_read < 0) {
qb_util_perror(LOG_ERR, "Unable to read blackbox file data");
goto cleanup_fail;
}
total_read += n_read;
if (n_read != n_required) {
qb_util_log(LOG_WARNING, "read %zd bytes, but expected %zu",
n_read, n_required);
goto cleanup_fail;
}
qb_util_log(LOG_DEBUG, "read total of: %zd", total_read);
print_header(rb);
return rb;
cleanup_fail:
qb_rb_close(rb);
return NULL;
}
int32_t
qb_rb_chown(struct qb_ringbuffer_s * rb, uid_t owner, gid_t group)
{
int32_t res;
if (rb == NULL) {
return -EINVAL;
}
res = chown(rb->shared_hdr->data_path, owner, group);
if (res < 0 && errno != EPERM) {
return -errno;
}
res = chown(rb->shared_hdr->hdr_path, owner, group);
if (res < 0 && errno != EPERM) {
return -errno;
}
return 0;
}
int32_t
qb_rb_chmod(qb_ringbuffer_t * rb, mode_t mode)
{
int32_t res;
if (rb == NULL) {
return -EINVAL;
}
res = chmod(rb->shared_hdr->data_path, mode);
if (res < 0) {
return -errno;
}
res = chmod(rb->shared_hdr->hdr_path, mode);
if (res < 0) {
return -errno;
}
return 0;
}
diff --git a/lib/trie.c b/lib/trie.c
index 43bed90..0e5dd6e 100644
--- a/lib/trie.c
+++ b/lib/trie.c
@@ -1,815 +1,819 @@
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include <os_base.h>
#include <assert.h>
#include <qb/qbdefs.h>
#include <qb/qblist.h>
#include <qb/qbmap.h>
#include "map_int.h"
struct trie_iter {
struct qb_map_iter i;
const char *prefix;
struct trie_node *n;
struct trie_node *root;
};
struct trie_node {
uint32_t idx;
char *segment;
uint32_t num_segments;
char *key;
void *value;
struct trie_node **children;
uint32_t num_children;
uint32_t refcount;
struct trie_node *parent;
struct qb_list_head *notifier_head;
};
struct trie {
struct qb_map map;
size_t length;
uint32_t num_nodes;
uint32_t mem_used;
struct trie_node *header;
};
static void trie_notify(struct trie_node *n, uint32_t event, const char *key,
void *old_value, void *value);
static struct trie_node *trie_new_node(struct trie *t, struct trie_node *parent);
static void trie_destroy_node(struct trie_node *node);
/*
* characters are stored in reverse to make accessing the
* more common case (non-control chars) more space efficient.
*/
#define TRIE_CHAR2INDEX(ch) (126 - ch)
#define TRIE_INDEX2CHAR(idx) (126 - idx)
static int32_t
trie_node_alive(struct trie_node *node)
{
if (node->value == NULL ||
node->refcount <= 0) {
return QB_FALSE;
}
return QB_TRUE;
}
static struct trie_node *
trie_node_next(struct trie_node *node, struct trie_node *root, int all)
{
struct trie_node *c = node;
struct trie_node *n;
struct trie_node *p;
int i;
keep_going:
n = NULL;
/* child/outward
*/
for (i = c->num_children - 1; i >= 0; i--) {
if (c->children[i]) {
n = c->children[i];
break;
}
}
if (n) {
if (all || trie_node_alive(n)) {
return n;
} else {
c = n;
goto keep_going;
}
}
/* sibling/parent
*/
if (c == root) {
return NULL;
}
p = c;
do {
for (i = p->idx - 1; i >= 0; i--) {
if (p->parent->children[i]) {
n = p->parent->children[i];
break;
}
}
if (n == NULL) {
p = p->parent;
}
} while (n == NULL && p != root);
if (n) {
if (all || trie_node_alive(n)) {
return n;
}
if (n == root) {
return NULL;
}
c = n;
goto keep_going;
}
return n;
}
static struct trie_node *
new_child_node(struct trie *t, struct trie_node * parent, char ch)
{
struct trie_node *new_node;
int old_max_idx;
int i;
int idx = TRIE_CHAR2INDEX(ch);
if (idx >= parent->num_children) {
old_max_idx = parent->num_children;
parent->num_children = QB_MAX(idx + 1, 30);
t->mem_used += (sizeof(struct trie_node*) * (parent->num_children - old_max_idx));
parent->children = realloc(parent->children,
(parent->num_children * sizeof(struct trie_node*)));
if (parent->children == NULL) {
return NULL;
}
for (i = old_max_idx; i < parent->num_children; i++) {
parent->children[i] = NULL;
}
}
new_node = trie_new_node(t, parent);
if (new_node == NULL) {
return NULL;
}
new_node->idx = idx;
parent->children[idx] = new_node;
return new_node;
}
static struct trie_node *
trie_node_split(struct trie *t, struct trie_node *cur_node, int seg_cnt)
{
struct trie_node *split_node;
struct trie_node ** children = cur_node->children;
uint32_t num_children = cur_node->num_children;
struct qb_list_head *tmp;
int i;
int s;
cur_node->children = NULL;
cur_node->num_children = 0;
split_node = new_child_node(t, cur_node, cur_node->segment[seg_cnt]);
if (split_node == NULL) {
return NULL;
}
split_node->children = children;
split_node->num_children = num_children;
for (i = 0; i < split_node->num_children; i++) {
if (split_node->children[i]) {
split_node->children[i]->parent = split_node;
}
}
split_node->value = cur_node->value;
split_node->key = cur_node->key;
split_node->refcount = cur_node->refcount;
cur_node->value = NULL;
cur_node->key = NULL;
cur_node->refcount = 0;
/* move notifier list to split */
tmp = split_node->notifier_head;
split_node->notifier_head = cur_node->notifier_head;
cur_node->notifier_head = tmp;
qb_list_init(cur_node->notifier_head);
if (seg_cnt < cur_node->num_segments) {
split_node->num_segments = cur_node->num_segments - seg_cnt - 1;
split_node->segment = malloc(split_node->num_segments * sizeof(char));
if (split_node->segment == NULL) {
trie_destroy_node(split_node);
return NULL;
}
for (i = (seg_cnt + 1); i < cur_node->num_segments; i++) {
s = i - seg_cnt - 1;
split_node->segment[s] = cur_node->segment[i];
cur_node->segment[i] = '\0';
}
cur_node->num_segments = seg_cnt;
}
return cur_node;
}
static struct trie_node *
trie_insert(struct trie *t, const char *key)
{
struct trie_node *cur_node = t->header;
struct trie_node *new_node;
char *cur = (char *)key;
int idx = TRIE_CHAR2INDEX(key[0]);
int seg_cnt = 0;
do {
new_node = NULL;
if (cur_node->num_segments > 0 &&
seg_cnt < cur_node->num_segments) {
if (cur_node->segment[seg_cnt] == *cur) {
/* we found the char in the segment */
seg_cnt++;
} else {
cur_node = trie_node_split(t, cur_node, seg_cnt);
if (cur_node == NULL) {
return NULL;
}
new_node = new_child_node(t, cur_node, *cur);
if (new_node == NULL) {
return NULL;
}
}
} else if (idx < cur_node->num_children &&
cur_node->children[idx]) {
/* the char can be found on the next node */
new_node = cur_node->children[idx];
} else if (cur_node == t->header) {
/* the root node is empty so make it on the next node */
new_node = new_child_node(t, cur_node, *cur);
if (new_node == NULL) {
return NULL;
}
} else if (cur_node->value == NULL &&
qb_list_empty(cur_node->notifier_head) &&
cur_node->num_children == 0 &&
seg_cnt == cur_node->num_segments) {
/* we are on a leaf (with no value) so just add it as a segment */
cur_node->segment = realloc(cur_node->segment, cur_node->num_segments + 1);
cur_node->segment[cur_node->num_segments] = *cur;
t->mem_used += sizeof(char);
cur_node->num_segments++;
seg_cnt++;
} else if (seg_cnt == cur_node->num_segments) {
/* on the last segment need to make a new node */
new_node = new_child_node(t, cur_node, *cur);
if (new_node == NULL) {
return NULL;
}
} else /* need_to_split */ {
cur_node = trie_node_split(t, cur_node, seg_cnt);
if (cur_node == NULL) {
return NULL;
}
new_node = new_child_node(t, cur_node, *cur);
if (new_node == NULL) {
return NULL;
}
}
if (new_node) {
seg_cnt = 0;
cur_node = new_node;
}
cur++;
idx = TRIE_CHAR2INDEX(*cur);
} while (*cur != '\0');
if (cur_node->num_segments > 0 &&
seg_cnt < cur_node->num_segments) {
/* we need to split */
cur_node = trie_node_split(t, cur_node, seg_cnt);
if (cur_node == NULL) {
return NULL;
}
new_node = new_child_node(t, cur_node, *cur);
if (new_node == NULL) {
return NULL;
}
}
return cur_node;
}
static struct trie_node *
trie_lookup(struct trie *t, const char *key, int exact_match)
{
struct trie_node *cur_node = t->header;
char *cur = (char *)key;
int idx = TRIE_CHAR2INDEX(key[0]);
int seg_cnt = 0;
do {
if (cur_node->num_segments > 0 &&
seg_cnt < cur_node->num_segments) {
if (cur_node->segment[seg_cnt] == *cur) {
/* we found the char in the segment */
seg_cnt++;
} else {
return NULL;
}
} else if (idx < cur_node->num_children &&
cur_node->children[idx]) {
/* the char can be found on the next node */
cur_node = cur_node->children[idx];
seg_cnt = 0;
} else {
return NULL;
}
cur++;
idx = TRIE_CHAR2INDEX(*cur);
} while (*cur != '\0');
if (exact_match &&
cur_node->num_segments > 0 &&
seg_cnt < cur_node->num_segments) {
return NULL;
}
return cur_node;
}
static void
trie_node_release(struct trie *t, struct trie_node *node)
{
int i;
int empty = QB_FALSE;
if (node->key == NULL &&
node->parent != NULL &&
qb_list_empty(node->notifier_head)) {
struct trie_node *p = node->parent;
if (node->num_children == 0) {
empty = QB_TRUE;
} else {
empty = QB_TRUE;
for (i = node->num_children - 1; i >= 0; i--) {
if (node->children[i]) {
empty = QB_FALSE;
break;
}
}
}
if (!empty) {
return;
}
/*
* unlink the node from the parent
*/
p->children[node->idx] = NULL;
trie_destroy_node(node);
t->num_nodes--;
t->mem_used -= sizeof(struct trie_node);
trie_node_release(t, p);
}
}
static void
trie_node_destroy(struct trie *t, struct trie_node *n)
{
if (n->value == NULL) {
return;
}
trie_notify(n, QB_MAP_NOTIFY_DELETED, n->key, n->value, NULL);
n->key = NULL;
n->value = NULL;
trie_node_release(t, n);
}
static void
trie_print_node(struct trie_node *n, struct trie_node *r, const char *suffix)
{
int i;
if (n->parent) {
trie_print_node(n->parent, n, suffix);
}
if (n->idx == 0) {
return;
}
- printf("[%c", TRIE_INDEX2CHAR(n->idx));
+ printf("[%c", (char) TRIE_INDEX2CHAR(n->idx));
for (i = 0; i < n->num_segments; i++) {
printf("%c", n->segment[i]);
}
if (n == r) {
- printf("] (%d) %s\n", n->refcount, suffix);
+#ifndef S_SPLINT_S
+ printf("] (%" PRIu32 ") %s\n", n->refcount, suffix);
+#endif /* S_SPLINT_S */
} else {
printf("] ");
}
}
static void
trie_node_ref(struct trie *t, struct trie_node *node)
{
if (t->header == node) {
return;
}
node->refcount++;
}
static void
trie_node_deref(struct trie *t, struct trie_node *node)
{
if (!trie_node_alive(node)) {
return;
}
node->refcount--;
if (node->refcount > 0) {
return;
}
trie_node_destroy(t, node);
}
static void
trie_destroy(struct qb_map *map)
{
struct trie *t = (struct trie *)map;
struct trie_node *cur_node = t->header;
struct trie_node *fwd_node;
do {
fwd_node = trie_node_next(cur_node, t->header, QB_FALSE);
trie_node_destroy(t, cur_node);
} while ((cur_node = fwd_node));
free(t);
}
static void
trie_destroy_node(struct trie_node *node)
{
free(node->segment);
free(node->children);
free(node->notifier_head);
free(node);
}
static struct trie_node *
trie_new_node(struct trie *t, struct trie_node *parent)
{
struct trie_node *new_node = calloc(1, sizeof(struct trie_node));
if (new_node == NULL) {
return NULL;
}
new_node->notifier_head = calloc(1, sizeof(struct qb_list_head));
if (new_node->notifier_head == NULL) {
free(new_node);
return NULL;
}
new_node->parent = parent;
new_node->num_children = 0;
new_node->children = NULL;
new_node->num_segments = 0;
new_node->segment = NULL;
t->num_nodes++;
t->mem_used += sizeof(struct trie_node);
qb_list_init(new_node->notifier_head);
return new_node;
}
void
qb_trie_dump(qb_map_t* m)
{
struct trie * t = (struct trie*)m;
struct trie_node *n;
if (t == NULL) {
return;
}
- printf("nodes: %d, bytes: %d\n", t->num_nodes, t->mem_used);
+#ifndef S_SPLINT_S
+ printf("nodes: %" PRIu32 ", bytes: %" PRIu32 "\n", t->num_nodes, t->mem_used);
+#endif /* S_SPLINT_S */
n = t->header;
do {
if (n->num_children == 0) {
trie_print_node(n, n, " ");
}
n = trie_node_next(n, t->header, QB_FALSE);
} while (n);
}
static void
trie_put(struct qb_map *map, const char *key, const void *value)
{
struct trie *t = (struct trie *)map;
struct trie_node *n = trie_insert(t, key);
if (n) {
const char *old_value = n->value;
const char *old_key = n->key;
n->key = (char *)key;
n->value = (void *)value;
if (old_value == NULL) {
trie_node_ref(t, n);
t->length++;
trie_notify(n, QB_MAP_NOTIFY_INSERTED,
n->key, NULL, n->value);
} else {
trie_notify(n, QB_MAP_NOTIFY_REPLACED,
(char *)old_key, (void *)old_value,
(void *)value);
}
}
}
static int32_t
trie_rm(struct qb_map *map, const char *key)
{
struct trie *t = (struct trie *)map;
struct trie_node *n = trie_lookup(t, key, QB_TRUE);
if (n) {
trie_node_deref(t, n);
t->length--;
return QB_TRUE;
} else {
return QB_FALSE;
}
}
static void *
trie_get(struct qb_map *map, const char *key)
{
struct trie *t = (struct trie *)map;
struct trie_node *n = trie_lookup(t, key, QB_TRUE);
if (n) {
return n->value;
}
return NULL;
}
static void
trie_notify_deref(struct qb_map_notifier *f)
{
f->refcount--;
if (f->refcount == 0) {
qb_list_del(&f->list);
free(f);
}
}
static void
trie_notify_ref(struct qb_map_notifier *f)
{
f->refcount++;
}
static void
trie_notify(struct trie_node *n,
uint32_t event, const char *key, void *old_value, void *value)
{
struct trie_node *c = n;
struct qb_list_head *list;
struct qb_list_head *next;
struct qb_list_head *head;
struct qb_map_notifier *tn;
do {
head = c->notifier_head;
qb_list_for_each_safe(list, next, head) {
tn = qb_list_entry(list, struct qb_map_notifier, list);
trie_notify_ref(tn);
if ((tn->events & event) &&
((tn->events & QB_MAP_NOTIFY_RECURSIVE) ||
(n == c))) {
tn->callback(event, (char *)key, old_value,
value, tn->user_data);
}
if (((event & QB_MAP_NOTIFY_DELETED) ||
(event & QB_MAP_NOTIFY_REPLACED)) &&
(tn->events & QB_MAP_NOTIFY_FREE)) {
tn->callback(QB_MAP_NOTIFY_FREE, (char *)key,
old_value, value, tn->user_data);
}
trie_notify_deref(tn);
}
c = c->parent;
} while (c);
}
static int32_t
trie_notify_add(qb_map_t * m, const char *key,
qb_map_notify_fn fn, int32_t events, void *user_data)
{
struct trie *t = (struct trie *)m;
struct qb_map_notifier *f;
struct trie_node *n;
struct qb_list_head *list;
int add_to_tail = QB_FALSE;
if (key) {
n = trie_lookup(t, key, QB_TRUE);
if (n == NULL) {
n = trie_insert(t, key);
}
} else {
n = t->header;
}
if (n) {
qb_list_for_each(list, n->notifier_head) {
f = qb_list_entry(list, struct qb_map_notifier, list);
if (events & QB_MAP_NOTIFY_FREE &&
f->events == events) {
/* only one free notifier */
return -EEXIST;
}
if (f->events == events &&
f->callback == fn &&
f->user_data == user_data) {
return -EEXIST;
}
}
f = malloc(sizeof(struct qb_map_notifier));
if (f == NULL) {
return -errno;
}
f->events = events;
f->user_data = user_data;
f->callback = fn;
f->refcount = 1;
qb_list_init(&f->list);
if (key) {
if (events & QB_MAP_NOTIFY_RECURSIVE) {
add_to_tail = QB_TRUE;
}
} else {
if (events & QB_MAP_NOTIFY_FREE) {
add_to_tail = QB_TRUE;
}
}
if (add_to_tail) {
qb_list_add_tail(&f->list, n->notifier_head);
} else {
qb_list_add(&f->list, n->notifier_head);
}
return 0;
}
return -EINVAL;
}
static int32_t
trie_notify_del(qb_map_t * m, const char *key,
qb_map_notify_fn fn, int32_t events,
int32_t cmp_userdata, void *user_data)
{
struct trie *t = (struct trie *)m;
struct trie_node *n;
struct qb_list_head *list;
struct qb_list_head *next;
int32_t found = QB_FALSE;
if (key) {
n = trie_lookup(t, key, QB_FALSE);
} else {
n = t->header;
}
if (n == NULL) {
return -ENOENT;
}
qb_list_for_each_safe(list, next, n->notifier_head) {
struct qb_map_notifier *f = qb_list_entry(list, struct qb_map_notifier, list);
if (f->events == events && f->callback == fn) {
if (cmp_userdata && (f->user_data == user_data)) {
trie_notify_deref(f);
found = QB_TRUE;
} else if (!cmp_userdata) {
trie_notify_deref(f);
found = QB_TRUE;
}
}
}
if (found) {
trie_node_release(t, n);
return 0;
} else {
return -ENOENT;
}
}
static qb_map_iter_t *
trie_iter_create(struct qb_map *map, const char *prefix)
{
struct trie_iter *i = malloc(sizeof(struct trie_iter));
struct trie *t = (struct trie *)map;
if (i == NULL) {
return NULL;
}
i->i.m = map;
i->prefix = prefix;
i->n = t->header;
i->root = t->header;
return (qb_map_iter_t *) i;
}
static const char *
trie_iter_next(qb_map_iter_t * i, void **value)
{
struct trie_iter *si = (struct trie_iter *)i;
struct trie_node *p = si->n;
struct trie *t = (struct trie *)(i->m);
if (p == NULL) {
return NULL;
}
if (p->parent == NULL && si->prefix) {
si->root = trie_lookup(t, si->prefix, QB_FALSE);
if (si->root == NULL) {
si->n = NULL;
} else if (si->root->value == NULL) {
si->n = trie_node_next(si->root, si->root, QB_FALSE);
} else {
si->n = si->root;
}
} else {
si->n = trie_node_next(p, si->root, QB_FALSE);
}
if (si->n == NULL) {
trie_node_deref(t, p);
return NULL;
}
trie_node_ref(t, si->n);
trie_node_deref(t, p);
*value = si->n->value;
return si->n->key;
}
static void
trie_iter_free(qb_map_iter_t * i)
{
struct trie_iter *si = (struct trie_iter *)i;
struct trie *t = (struct trie *)(i->m);
if (si->n != NULL) {
/* if free'ing the iterator before getting to the last
* node make sure we de-ref the current node.
*/
trie_node_deref(t, si->n);
}
free(i);
}
static size_t
trie_count_get(struct qb_map *map)
{
struct trie *list = (struct trie *)map;
return list->length;
}
qb_map_t *
qb_trie_create(void)
{
struct trie *t = malloc(sizeof(struct trie));
if (t == NULL) {
return NULL;
}
t->map.put = trie_put;
t->map.get = trie_get;
t->map.rm = trie_rm;
t->map.count_get = trie_count_get;
t->map.iter_create = trie_iter_create;
t->map.iter_next = trie_iter_next;
t->map.iter_free = trie_iter_free;
t->map.destroy = trie_destroy;
t->map.notify_add = trie_notify_add;
t->map.notify_del = trie_notify_del;
t->length = 0;
t->num_nodes = 0;
t->mem_used = sizeof(struct trie);
t->header = trie_new_node(t, NULL);
return (qb_map_t *) t;
}
diff --git a/tests/check_ipc.c b/tests/check_ipc.c
index 6b43068..d7b6bcb 100644
--- a/tests/check_ipc.c
+++ b/tests/check_ipc.c
@@ -1,1632 +1,1632 @@
/*
* Copyright (c) 2010 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <sys/wait.h>
#include <signal.h>
#include "check_common.h"
#include <qb/qbdefs.h>
#include <qb/qblog.h>
#include <qb/qbipcc.h>
#include <qb/qbipcs.h>
#include <qb/qbloop.h>
#ifdef HAVE_FAILURE_INJECTION
#include "_failure_injection.h"
#endif
static char ipc_name[256];
#define DEFAULT_MAX_MSG_SIZE (8192*16)
#ifndef __clang__
static int CALCULATED_DGRAM_MAX_MSG_SIZE = 0;
#define DGRAM_MAX_MSG_SIZE \
(CALCULATED_DGRAM_MAX_MSG_SIZE == 0 ? \
CALCULATED_DGRAM_MAX_MSG_SIZE = qb_ipcc_verify_dgram_max_msg_size(DEFAULT_MAX_MSG_SIZE) : \
CALCULATED_DGRAM_MAX_MSG_SIZE)
#define MAX_MSG_SIZE (ipc_type == QB_IPC_SOCKET ? DGRAM_MAX_MSG_SIZE : DEFAULT_MAX_MSG_SIZE)
#else
/* because of clang's
'variable length array in structure' extension will never be supported;
assign default for SHM as we'll skip test that would use run-time
established value (via qb_ipcc_verify_dgram_max_msg_size), anyway */
static const int MAX_MSG_SIZE = DEFAULT_MAX_MSG_SIZE;
#endif
/* The size the giant msg's data field needs to be to make
* this the largests msg we can successfully send. */
#define GIANT_MSG_DATA_SIZE MAX_MSG_SIZE - sizeof(struct qb_ipc_response_header) - 8
static int enforce_server_buffer=0;
static qb_ipcc_connection_t *conn;
static enum qb_ipc_type ipc_type;
enum my_msg_ids {
IPC_MSG_REQ_TX_RX,
IPC_MSG_RES_TX_RX,
IPC_MSG_REQ_DISPATCH,
IPC_MSG_RES_DISPATCH,
IPC_MSG_REQ_BULK_EVENTS,
IPC_MSG_RES_BULK_EVENTS,
IPC_MSG_REQ_STRESS_EVENT,
IPC_MSG_RES_STRESS_EVENT,
IPC_MSG_REQ_SERVER_FAIL,
IPC_MSG_RES_SERVER_FAIL,
IPC_MSG_REQ_SERVER_DISCONNECT,
IPC_MSG_RES_SERVER_DISCONNECT,
};
/* Test Cases
*
* 1) basic send & recv different message sizes
*
* 2) send message to start dispatch (confirm receipt)
*
* 3) flow control
*
* 4) authentication
*
* 5) thread safety
*
* 6) cleanup
*
* 7) service availability
*
* 8) multiple services
*/
static qb_loop_t *my_loop;
static qb_ipcs_service_t* s1;
static int32_t turn_on_fc = QB_FALSE;
static int32_t fc_enabled = 89;
static int32_t send_event_on_created = QB_FALSE;
static int32_t disconnect_after_created = QB_FALSE;
static int32_t num_bulk_events = 10;
static int32_t num_stress_events = 30000;
static int32_t reference_count_test = QB_FALSE;
static int32_t multiple_connections = QB_FALSE;
static int32_t
exit_handler(int32_t rsignal, void *data)
{
qb_log(LOG_DEBUG, "caught signal %d", rsignal);
qb_ipcs_destroy(s1);
exit(0);
}
static void
set_ipc_name(const char *prefix)
{
/* We have to make the server name as unique as possible given
* the build- (seconds part of preprocessor's timestamp) and
* run-time (pid + lower 16 bits of the current timestamp)
* circumstances, because some build systems attempt to generate
* packages for libqb in parallel. These unit tests are run
* during the package build process. 2+ builds executing on
* the same machine (whether containerized or not because of
* abstract unix sockets namespace sharing) can stomp on each
* other's unit tests if the ipc server names aren't unique... */
/* single-shot grab of seconds part of preprocessor's timestamp */
static char t_sec[3] = "";
if (t_sec[0] == '\0') {
const char *const found = strrchr(__TIME__, ':');
strncpy(t_sec, found ? found + 1 : "-", sizeof(t_sec) - 1);
t_sec[sizeof(t_sec) - 1] = '\0';
}
snprintf(ipc_name, sizeof(ipc_name), "%s%s%lX%.4x", prefix, t_sec,
- (long)getpid(), (int) ((long) time(NULL) % (0x10000)));
+ (unsigned long)getpid(), (unsigned) ((long) time(NULL) % (0x10000)));
}
static int32_t
s1_msg_process_fn(qb_ipcs_connection_t *c,
void *data, size_t size)
{
struct qb_ipc_request_header *req_pt = (struct qb_ipc_request_header *)data;
struct qb_ipc_response_header response = { 0, };
ssize_t res;
if (req_pt->id == IPC_MSG_REQ_TX_RX) {
response.size = sizeof(struct qb_ipc_response_header);
response.id = IPC_MSG_RES_TX_RX;
response.error = 0;
res = qb_ipcs_response_send(c, &response, response.size);
if (res < 0) {
qb_perror(LOG_INFO, "qb_ipcs_response_send");
} else if (res != response.size) {
qb_log(LOG_DEBUG, "qb_ipcs_response_send %zd != %d",
res, response.size);
}
if (turn_on_fc) {
qb_ipcs_request_rate_limit(s1, QB_IPCS_RATE_OFF);
}
} else if (req_pt->id == IPC_MSG_REQ_DISPATCH) {
response.size = sizeof(struct qb_ipc_response_header);
response.id = IPC_MSG_RES_DISPATCH;
response.error = 0;
res = qb_ipcs_event_send(c, &response,
sizeof(response));
if (res < 0) {
qb_perror(LOG_INFO, "qb_ipcs_event_send");
}
} else if (req_pt->id == IPC_MSG_REQ_BULK_EVENTS) {
int32_t m;
int32_t num;
struct qb_ipcs_connection_stats_2 *stats;
uint32_t max_size = MAX_MSG_SIZE;
response.size = sizeof(struct qb_ipc_response_header);
response.error = 0;
stats = qb_ipcs_connection_stats_get_2(c, QB_FALSE);
num = stats->event_q_length;
free(stats);
/* crazy large message */
res = qb_ipcs_event_send(c, &response, max_size*10);
ck_assert_int_eq(res, -EMSGSIZE);
/* send one event before responding */
res = qb_ipcs_event_send(c, &response, sizeof(response));
ck_assert_int_eq(res, sizeof(response));
response.id++;
/* There should be one more item in the event queue now. */
stats = qb_ipcs_connection_stats_get_2(c, QB_FALSE);
ck_assert_int_eq(stats->event_q_length - num, 1);
free(stats);
/* send response */
response.id = IPC_MSG_RES_BULK_EVENTS;
res = qb_ipcs_response_send(c, &response, response.size);
ck_assert_int_eq(res, sizeof(response));
/* send the rest of the events after the response */
for (m = 1; m < num_bulk_events; m++) {
res = qb_ipcs_event_send(c, &response, sizeof(response));
if (res == -EAGAIN || res == -ENOBUFS) {
/* retry */
usleep(1000);
m--;
continue;
}
ck_assert_int_eq(res, sizeof(response));
response.id++;
}
} else if (req_pt->id == IPC_MSG_REQ_STRESS_EVENT) {
struct {
struct qb_ipc_response_header hdr __attribute__ ((aligned(8)));
char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8)));
uint32_t sent_msgs __attribute__ ((aligned(8)));
} __attribute__ ((aligned(8))) giant_event_send;
int32_t m;
response.size = sizeof(struct qb_ipc_response_header);
response.error = 0;
response.id = IPC_MSG_RES_STRESS_EVENT;
res = qb_ipcs_response_send(c, &response, response.size);
ck_assert_int_eq(res, sizeof(response));
giant_event_send.hdr.error = 0;
giant_event_send.hdr.id = IPC_MSG_RES_STRESS_EVENT;
for (m = 0; m < num_stress_events; m++) {
size_t sent_len = sizeof(struct qb_ipc_response_header);
if (((m+1) % 1000) == 0) {
sent_len = sizeof(giant_event_send);
giant_event_send.sent_msgs = m + 1;
}
giant_event_send.hdr.size = sent_len;
res = qb_ipcs_event_send(c, &giant_event_send, sent_len);
if (res < 0) {
if (res == -EAGAIN || res == -ENOBUFS) {
/* yield to the receive process */
usleep(1000);
m--;
continue;
} else {
qb_perror(LOG_DEBUG, "sending stress events");
ck_assert_int_eq(res, sent_len);
}
} else if (((m+1) % 1000) == 0) {
qb_log(LOG_DEBUG, "SENT: %d stress events sent", m+1);
}
giant_event_send.hdr.id++;
}
} else if (req_pt->id == IPC_MSG_REQ_SERVER_FAIL) {
exit(0);
} else if (req_pt->id == IPC_MSG_REQ_SERVER_DISCONNECT) {
multiple_connections = QB_FALSE;
qb_ipcs_disconnect(c);
}
return 0;
}
static int32_t
my_job_add(enum qb_loop_priority p,
void *data,
qb_loop_job_dispatch_fn fn)
{
return qb_loop_job_add(my_loop, p, data, fn);
}
static int32_t
my_dispatch_add(enum qb_loop_priority p, int32_t fd, int32_t events,
void *data, qb_ipcs_dispatch_fn_t fn)
{
return qb_loop_poll_add(my_loop, p, fd, events, data, fn);
}
static int32_t
my_dispatch_mod(enum qb_loop_priority p, int32_t fd, int32_t events,
void *data, qb_ipcs_dispatch_fn_t fn)
{
return qb_loop_poll_mod(my_loop, p, fd, events, data, fn);
}
static int32_t
my_dispatch_del(int32_t fd)
{
return qb_loop_poll_del(my_loop, fd);
}
static int32_t
s1_connection_closed(qb_ipcs_connection_t *c)
{
if (multiple_connections) {
return 0;
}
qb_enter();
qb_leave();
return 0;
}
static void
outq_flush (void *data)
{
static int i = 0;
struct cs_ipcs_conn_context *cnx;
cnx = qb_ipcs_context_get(data);
qb_log(LOG_DEBUG,"iter %u\n", i);
i++;
if (i == 2) {
qb_ipcs_destroy(s1);
s1 = NULL;
}
/* if the reference counting is not working, this should fail
* for i > 1.
*/
qb_ipcs_event_send(data, "test", 4);
assert(memcmp(cnx, "test", 4) == 0);
if (i < 5) {
qb_loop_job_add(my_loop, QB_LOOP_HIGH, data, outq_flush);
} else {
/* this single unref should clean everything up.
*/
qb_ipcs_connection_unref(data);
qb_log(LOG_INFO, "end of test, stopping loop");
qb_loop_stop(my_loop);
}
}
static void
s1_connection_destroyed(qb_ipcs_connection_t *c)
{
if (multiple_connections) {
return;
}
qb_enter();
if (reference_count_test) {
struct cs_ipcs_conn_context *cnx;
cnx = qb_ipcs_context_get(c);
free(cnx);
} else {
qb_loop_stop(my_loop);
}
qb_leave();
}
static void
s1_connection_created(qb_ipcs_connection_t *c)
{
uint32_t max = MAX_MSG_SIZE;
if (multiple_connections) {
return;
}
if (send_event_on_created) {
struct qb_ipc_response_header response;
int32_t res;
response.size = sizeof(struct qb_ipc_response_header);
response.id = IPC_MSG_RES_DISPATCH;
response.error = 0;
res = qb_ipcs_event_send(c, &response,
sizeof(response));
ck_assert_int_eq(res, response.size);
}
if (reference_count_test) {
struct cs_ipcs_conn_context *context;
qb_ipcs_connection_ref(c);
qb_loop_job_add(my_loop, QB_LOOP_HIGH, c, outq_flush);
context = calloc(1, 20);
memcpy(context, "test", 4);
qb_ipcs_context_set(c, context);
}
ck_assert_int_eq(max, qb_ipcs_connection_get_buffer_size(c));
}
static void
run_ipc_server(void)
{
int32_t res;
qb_loop_signal_handle handle;
struct qb_ipcs_service_handlers sh = {
.connection_accept = NULL,
.connection_created = s1_connection_created,
.msg_process = s1_msg_process_fn,
.connection_destroyed = s1_connection_destroyed,
.connection_closed = s1_connection_closed,
};
struct qb_ipcs_poll_handlers ph = {
.job_add = my_job_add,
.dispatch_add = my_dispatch_add,
.dispatch_mod = my_dispatch_mod,
.dispatch_del = my_dispatch_del,
};
uint32_t max_size = MAX_MSG_SIZE;
my_loop = qb_loop_create();
qb_loop_signal_add(my_loop, QB_LOOP_HIGH, SIGTERM,
NULL, exit_handler, &handle);
s1 = qb_ipcs_create(ipc_name, 4, ipc_type, &sh);
fail_if(s1 == 0);
if (enforce_server_buffer) {
qb_ipcs_enforce_buffer_size(s1, max_size);
}
qb_ipcs_poll_handlers_set(s1, &ph);
res = qb_ipcs_run(s1);
ck_assert_int_eq(res, 0);
qb_loop_run(my_loop);
qb_log(LOG_DEBUG, "loop finished - done ...");
}
static pid_t
run_function_in_new_process(void (*run_ipc_server_fn)(void))
{
pid_t pid = fork ();
if (pid == -1) {
fprintf (stderr, "Can't fork\n");
return -1;
}
if (pid == 0) {
run_ipc_server_fn();
exit(0);
}
return pid;
}
static void
request_server_exit(void)
{
struct qb_ipc_request_header req_header;
struct qb_ipc_response_header res_header;
struct iovec iov[1];
int32_t res;
/*
* tell the server to exit
*/
req_header.id = IPC_MSG_REQ_SERVER_FAIL;
req_header.size = sizeof(struct qb_ipc_request_header);
iov[0].iov_len = req_header.size;
iov[0].iov_base = &req_header;
ck_assert_int_eq(QB_TRUE, qb_ipcc_is_connected(conn));
res = qb_ipcc_sendv_recv(conn, iov, 1,
&res_header,
sizeof(struct qb_ipc_response_header), -1);
/*
* confirm we get -ENOTCONN or ECONNRESET
*/
if (res != -ECONNRESET && res != -ENOTCONN) {
qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size);
ck_assert_int_eq(res, -ENOTCONN);
}
}
static void
kill_server(pid_t pid)
{
kill(pid, SIGTERM);
waitpid(pid, NULL, 0);
}
static int32_t
verify_graceful_stop(pid_t pid)
{
int wait_rc = 0;
int status = 0;
int rc = 0;
int tries;
/* We need the server to be able to exit by itself */
for (tries = 10; tries >= 0; tries--) {
sleep(1);
wait_rc = waitpid(pid, &status, WNOHANG);
if (wait_rc > 0) {
break;
}
}
ck_assert_int_eq(wait_rc, pid);
rc = WIFEXITED(status);
if (rc) {
rc = WEXITSTATUS(status);
ck_assert_int_eq(rc, 0);
} else {
fail_if(rc == 0);
}
return 0;
}
struct my_req {
struct qb_ipc_request_header hdr;
char message[1024 * 1024];
};
static struct my_req request;
static int32_t
send_and_check(int32_t req_id, uint32_t size,
int32_t ms_timeout, int32_t expect_perfection)
{
struct qb_ipc_response_header res_header;
int32_t res;
int32_t try_times = 0;
uint32_t max_size = MAX_MSG_SIZE;
request.hdr.id = req_id;
request.hdr.size = sizeof(struct qb_ipc_request_header) + size;
/* check that we can't send a message that is too big
* and we get the right return code.
*/
res = qb_ipcc_send(conn, &request, max_size*2);
ck_assert_int_eq(res, -EMSGSIZE);
repeat_send:
res = qb_ipcc_send(conn, &request, request.hdr.size);
try_times++;
if (res < 0) {
if (res == -EAGAIN && try_times < 10) {
goto repeat_send;
} else {
if (res == -EAGAIN && try_times >= 10) {
fc_enabled = QB_TRUE;
}
errno = -res;
qb_perror(LOG_INFO, "qb_ipcc_send");
return res;
}
}
if (req_id == IPC_MSG_REQ_DISPATCH) {
res = qb_ipcc_event_recv(conn, &res_header,
sizeof(struct qb_ipc_response_header),
ms_timeout);
} else {
res = qb_ipcc_recv(conn, &res_header,
sizeof(struct qb_ipc_response_header),
ms_timeout);
}
if (res == -EINTR) {
return -1;
}
if (res == -EAGAIN || res == -ETIMEDOUT) {
fc_enabled = QB_TRUE;
qb_perror(LOG_DEBUG, "qb_ipcc_recv");
return res;
}
if (expect_perfection) {
ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
ck_assert_int_eq(res_header.id, req_id + 1);
ck_assert_int_eq(res_header.size, sizeof(struct qb_ipc_response_header));
}
return res;
}
static void
test_ipc_txrx_timeout(void)
{
struct qb_ipc_request_header req_header;
struct qb_ipc_response_header res_header;
struct iovec iov[1];
int32_t res;
int32_t c = 0;
int32_t j = 0;
pid_t pid;
uint32_t max_size = MAX_MSG_SIZE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
/* The dispatch response will only come over
* the event channel, we want to verify the receive times
* out when an event is returned with no response */
req_header.id = IPC_MSG_REQ_DISPATCH;
req_header.size = sizeof(struct qb_ipc_request_header);
iov[0].iov_len = req_header.size;
iov[0].iov_base = &req_header;
res = qb_ipcc_sendv_recv(conn, iov, 1,
&res_header,
sizeof(struct qb_ipc_response_header), 5000);
ck_assert_int_eq(res, -ETIMEDOUT);
request_server_exit();
verify_graceful_stop(pid);
/*
* wait a bit for the server to die.
*/
sleep(1);
/*
* this needs to free up the shared mem
*/
qb_ipcc_disconnect(conn);
}
static int32_t recv_timeout = -1;
static void
test_ipc_txrx(void)
{
int32_t j;
int32_t c = 0;
size_t size;
pid_t pid;
uint32_t max_size = MAX_MSG_SIZE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
size = QB_MIN(sizeof(struct qb_ipc_request_header), 64);
for (j = 1; j < 19; j++) {
size *= 2;
if (size >= max_size)
break;
if (send_and_check(IPC_MSG_REQ_TX_RX, size,
recv_timeout, QB_TRUE) < 0) {
break;
}
}
if (turn_on_fc) {
/* can't signal server to shutdown if flow control is on */
ck_assert_int_eq(fc_enabled, QB_TRUE);
qb_ipcc_disconnect(conn);
/* TODO - figure out why this sleep is necessary */
sleep(1);
kill_server(pid);
} else {
request_server_exit();
qb_ipcc_disconnect(conn);
verify_graceful_stop(pid);
}
}
static void
test_ipc_exit(void)
{
struct qb_ipc_request_header req_header;
struct qb_ipc_response_header res_header;
struct iovec iov[1];
int32_t res;
int32_t c = 0;
int32_t j = 0;
pid_t pid;
uint32_t max_size = MAX_MSG_SIZE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
req_header.id = IPC_MSG_REQ_TX_RX;
req_header.size = sizeof(struct qb_ipc_request_header);
iov[0].iov_len = req_header.size;
iov[0].iov_base = &req_header;
res = qb_ipcc_sendv_recv(conn, iov, 1,
&res_header,
sizeof(struct qb_ipc_response_header), -1);
ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
request_server_exit();
verify_graceful_stop(pid);
/*
* wait a bit for the server to die.
*/
sleep(1);
/*
* this needs to free up the shared mem
*/
qb_ipcc_disconnect(conn);
}
START_TEST(test_ipc_exit_us)
{
qb_enter();
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
recv_timeout = 5000;
test_ipc_exit();
qb_leave();
}
END_TEST
START_TEST(test_ipc_exit_shm)
{
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
recv_timeout = 1000;
test_ipc_exit();
qb_leave();
}
END_TEST
START_TEST(test_ipc_txrx_shm_timeout)
{
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_txrx_timeout();
qb_leave();
}
END_TEST
START_TEST(test_ipc_txrx_us_timeout)
{
qb_enter();
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_txrx_timeout();
qb_leave();
}
END_TEST
START_TEST(test_ipc_txrx_shm_tmo)
{
qb_enter();
turn_on_fc = QB_FALSE;
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
recv_timeout = 1000;
test_ipc_txrx();
qb_leave();
}
END_TEST
START_TEST(test_ipc_txrx_shm_block)
{
qb_enter();
turn_on_fc = QB_FALSE;
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
recv_timeout = -1;
test_ipc_txrx();
qb_leave();
}
END_TEST
START_TEST(test_ipc_fc_shm)
{
qb_enter();
turn_on_fc = QB_TRUE;
ipc_type = QB_IPC_SHM;
recv_timeout = 500;
set_ipc_name(__func__);
test_ipc_txrx();
qb_leave();
}
END_TEST
START_TEST(test_ipc_txrx_us_block)
{
qb_enter();
turn_on_fc = QB_FALSE;
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
recv_timeout = -1;
test_ipc_txrx();
qb_leave();
}
END_TEST
START_TEST(test_ipc_txrx_us_tmo)
{
qb_enter();
turn_on_fc = QB_FALSE;
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
recv_timeout = 1000;
test_ipc_txrx();
qb_leave();
}
END_TEST
START_TEST(test_ipc_fc_us)
{
qb_enter();
turn_on_fc = QB_TRUE;
ipc_type = QB_IPC_SOCKET;
recv_timeout = 500;
set_ipc_name(__func__);
test_ipc_txrx();
qb_leave();
}
END_TEST
struct my_res {
struct qb_ipc_response_header hdr;
char message[1024 * 1024];
};
static void
test_ipc_dispatch(void)
{
int32_t j;
int32_t c = 0;
pid_t pid;
int32_t size;
uint32_t max_size = MAX_MSG_SIZE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
size = QB_MIN(sizeof(struct qb_ipc_request_header), 64);
for (j = 1; j < 19; j++) {
size *= 2;
if (size >= max_size)
break;
if (send_and_check(IPC_MSG_REQ_DISPATCH, size,
recv_timeout, QB_TRUE) < 0) {
break;
}
}
request_server_exit();
qb_ipcc_disconnect(conn);
verify_graceful_stop(pid);
}
START_TEST(test_ipc_dispatch_us)
{
qb_enter();
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_dispatch();
qb_leave();
}
END_TEST
static int32_t events_received;
static int32_t
count_stress_events(int32_t fd, int32_t revents, void *data)
{
struct {
struct qb_ipc_response_header hdr __attribute__ ((aligned(8)));
char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8)));
uint32_t sent_msgs __attribute__ ((aligned(8)));
} __attribute__ ((aligned(8))) giant_event_recv;
qb_loop_t *cl = (qb_loop_t*)data;
int32_t res;
res = qb_ipcc_event_recv(conn, &giant_event_recv,
sizeof(giant_event_recv),
-1);
if (res > 0) {
events_received++;
if ((events_received % 1000) == 0) {
qb_log(LOG_DEBUG, "RECV: %d stress events processed", events_received);
if (res != sizeof(giant_event_recv)) {
qb_log(LOG_DEBUG, "Unexpected recv size, expected %d got %d",
res, sizeof(giant_event_recv));
ck_assert_int_eq(res, sizeof(giant_event_recv));
} else if (giant_event_recv.sent_msgs != events_received) {
qb_log(LOG_DEBUG, "Server event mismatch. Server thinks we got %d msgs, but we only received %d",
giant_event_recv.sent_msgs, events_received);
/* This indicates that data corruption is occurring. Since the events
* received is placed at the end of the giant msg, it is possible
* that buffers were not allocated correctly resulting in us
* reading/writing to uninitialized memeory at some point. */
ck_assert_int_eq(giant_event_recv.sent_msgs, events_received);
}
}
} else if (res != -EAGAIN) {
qb_perror(LOG_DEBUG, "count_stress_events");
qb_loop_stop(cl);
return -1;
}
if (events_received >= num_stress_events) {
qb_loop_stop(cl);
return -1;
}
return 0;
}
static int32_t
count_bulk_events(int32_t fd, int32_t revents, void *data)
{
qb_loop_t *cl = (qb_loop_t*)data;
struct qb_ipc_response_header res_header;
int32_t res;
res = qb_ipcc_event_recv(conn, &res_header,
sizeof(struct qb_ipc_response_header),
-1);
if (res > 0) {
events_received++;
}
if (events_received >= num_bulk_events) {
qb_loop_stop(cl);
return -1;
}
return 0;
}
static void
test_ipc_stress_connections(void)
{
int32_t c = 0;
int32_t j = 0;
uint32_t max_size = MAX_MSG_SIZE;
int32_t connections = 0;
pid_t pid;
multiple_connections = QB_TRUE;
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_INFO);
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
for (connections = 1; connections < 70000; connections++) {
if (conn) {
qb_ipcc_disconnect(conn);
conn = NULL;
}
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
if (((connections+1) % 1000) == 0) {
qb_log(LOG_INFO, "%d ipc connections made", connections+1);
}
}
multiple_connections = QB_FALSE;
request_server_exit();
verify_graceful_stop(pid);
qb_ipcc_disconnect(conn);
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
}
static void
test_ipc_bulk_events(void)
{
int32_t c = 0;
int32_t j = 0;
pid_t pid;
int32_t res;
qb_loop_t *cl;
int32_t fd;
uint32_t max_size = MAX_MSG_SIZE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
events_received = 0;
cl = qb_loop_create();
res = qb_ipcc_fd_get(conn, &fd);
ck_assert_int_eq(res, 0);
res = qb_loop_poll_add(cl, QB_LOOP_MED,
fd, POLLIN,
cl, count_bulk_events);
ck_assert_int_eq(res, 0);
res = send_and_check(IPC_MSG_REQ_BULK_EVENTS,
0,
recv_timeout, QB_TRUE);
ck_assert_int_eq(res, sizeof(struct qb_ipc_response_header));
qb_loop_run(cl);
ck_assert_int_eq(events_received, num_bulk_events);
request_server_exit();
qb_ipcc_disconnect(conn);
verify_graceful_stop(pid);
}
static void
test_ipc_stress_test(void)
{
struct {
struct qb_ipc_request_header hdr __attribute__ ((aligned(8)));
char data[GIANT_MSG_DATA_SIZE] __attribute__ ((aligned(8)));
uint32_t sent_msgs __attribute__ ((aligned(8)));
} __attribute__ ((aligned(8))) giant_req;
struct qb_ipc_response_header res_header;
struct iovec iov[1];
int32_t c = 0;
int32_t j = 0;
pid_t pid;
int32_t res;
qb_loop_t *cl;
int32_t fd;
uint32_t max_size = MAX_MSG_SIZE;
/* This looks strange, but it serves an important purpose.
* This test forces the server to enforce the MAX_MSG_SIZE
* limit from the server side, which overrides the client's
* buffer limit. To verify this functionality is working
* we set the client limit lower than what the server
* is enforcing. */
int32_t client_buf_size = max_size - 1024;
int32_t real_buf_size;
enforce_server_buffer = 1;
pid = run_function_in_new_process(run_ipc_server);
enforce_server_buffer = 0;
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, client_buf_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
real_buf_size = qb_ipcc_get_buffer_size(conn);
ck_assert_int_eq(real_buf_size, max_size);
qb_log(LOG_DEBUG, "Testing %d iterations of EVENT msg passing.", num_stress_events);
events_received = 0;
cl = qb_loop_create();
res = qb_ipcc_fd_get(conn, &fd);
ck_assert_int_eq(res, 0);
res = qb_loop_poll_add(cl, QB_LOOP_MED,
fd, POLLIN,
cl, count_stress_events);
ck_assert_int_eq(res, 0);
res = send_and_check(IPC_MSG_REQ_STRESS_EVENT, 0, recv_timeout, QB_TRUE);
qb_loop_run(cl);
ck_assert_int_eq(events_received, num_stress_events);
giant_req.hdr.id = IPC_MSG_REQ_SERVER_FAIL;
giant_req.hdr.size = sizeof(giant_req);
if (giant_req.hdr.size <= client_buf_size) {
ck_assert_int_eq(1, 0);
}
iov[0].iov_len = giant_req.hdr.size;
iov[0].iov_base = &giant_req;
res = qb_ipcc_sendv_recv(conn, iov, 1,
&res_header,
sizeof(struct qb_ipc_response_header), -1);
if (res != -ECONNRESET && res != -ENOTCONN) {
qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size);
ck_assert_int_eq(res, -ENOTCONN);
}
qb_ipcc_disconnect(conn);
verify_graceful_stop(pid);
}
#ifndef __clang__ /* see variable length array in structure' at the top */
START_TEST(test_ipc_stress_test_us)
{
qb_enter();
send_event_on_created = QB_FALSE;
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_stress_test();
qb_leave();
}
END_TEST
#endif
START_TEST(test_ipc_stress_connections_us)
{
qb_enter();
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_stress_connections();
qb_leave();
}
END_TEST
START_TEST(test_ipc_bulk_events_us)
{
qb_enter();
send_event_on_created = QB_FALSE;
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_bulk_events();
qb_leave();
}
END_TEST
static void
test_ipc_event_on_created(void)
{
int32_t c = 0;
int32_t j = 0;
pid_t pid;
int32_t res;
qb_loop_t *cl;
int32_t fd;
uint32_t max_size = MAX_MSG_SIZE;
num_bulk_events = 1;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
events_received = 0;
cl = qb_loop_create();
res = qb_ipcc_fd_get(conn, &fd);
ck_assert_int_eq(res, 0);
res = qb_loop_poll_add(cl, QB_LOOP_MED,
fd, POLLIN,
cl, count_bulk_events);
ck_assert_int_eq(res, 0);
qb_loop_run(cl);
ck_assert_int_eq(events_received, num_bulk_events);
request_server_exit();
qb_ipcc_disconnect(conn);
verify_graceful_stop(pid);
}
START_TEST(test_ipc_event_on_created_us)
{
qb_enter();
send_event_on_created = QB_TRUE;
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_event_on_created();
qb_leave();
}
END_TEST
static void
test_ipc_disconnect_after_created(void)
{
struct qb_ipc_request_header req_header;
struct qb_ipc_response_header res_header;
struct iovec iov[1];
int32_t c = 0;
int32_t j = 0;
pid_t pid;
int32_t res;
uint32_t max_size = MAX_MSG_SIZE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
ck_assert_int_eq(QB_TRUE, qb_ipcc_is_connected(conn));
req_header.id = IPC_MSG_REQ_SERVER_DISCONNECT;
req_header.size = sizeof(struct qb_ipc_request_header);
iov[0].iov_len = req_header.size;
iov[0].iov_base = &req_header;
res = qb_ipcc_sendv_recv(conn, iov, 1,
&res_header,
sizeof(struct qb_ipc_response_header), -1);
/*
* confirm we get -ENOTCONN or -ECONNRESET
*/
if (res != -ECONNRESET && res != -ENOTCONN) {
qb_log(LOG_ERR, "id:%d size:%d", res_header.id, res_header.size);
ck_assert_int_eq(res, -ENOTCONN);
}
ck_assert_int_eq(QB_FALSE, qb_ipcc_is_connected(conn));
qb_ipcc_disconnect(conn);
kill_server(pid);
}
START_TEST(test_ipc_disconnect_after_created_us)
{
qb_enter();
disconnect_after_created = QB_TRUE;
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_disconnect_after_created();
qb_leave();
}
END_TEST
static void
test_ipc_server_fail(void)
{
int32_t j;
int32_t c = 0;
pid_t pid;
uint32_t max_size = MAX_MSG_SIZE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
request_server_exit();
if (_fi_unlink_inject_failure == QB_TRUE) {
_fi_truncate_called = _fi_openat_called = 0;
}
ck_assert_int_eq(QB_FALSE, qb_ipcc_is_connected(conn));
qb_ipcc_disconnect(conn);
if (_fi_unlink_inject_failure == QB_TRUE) {
ck_assert_int_ne(_fi_truncate_called + _fi_openat_called, 0);
}
verify_graceful_stop(pid);
}
START_TEST(test_ipc_server_fail_soc)
{
qb_enter();
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_server_fail();
qb_leave();
}
END_TEST
START_TEST(test_ipc_dispatch_shm)
{
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_dispatch();
qb_leave();
}
END_TEST
START_TEST(test_ipc_stress_test_shm)
{
qb_enter();
send_event_on_created = QB_FALSE;
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_stress_test();
qb_leave();
}
END_TEST
START_TEST(test_ipc_stress_connections_shm)
{
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_stress_connections();
qb_leave();
}
END_TEST
START_TEST(test_ipc_bulk_events_shm)
{
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_bulk_events();
qb_leave();
}
END_TEST
START_TEST(test_ipc_event_on_created_shm)
{
qb_enter();
send_event_on_created = QB_TRUE;
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_event_on_created();
qb_leave();
}
END_TEST
START_TEST(test_ipc_server_fail_shm)
{
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_server_fail();
qb_leave();
}
END_TEST
#ifdef HAVE_FAILURE_INJECTION
START_TEST(test_ipcc_truncate_when_unlink_fails_shm)
{
char sock_file[PATH_MAX];
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
sprintf(sock_file, "%s/%s", SOCKETDIR, ipc_name);
/* If there's an old socket left from a previous run this test will fail
unexpectedly, so try to remove it first */
unlink(sock_file);
_fi_unlink_inject_failure = QB_TRUE;
test_ipc_server_fail();
_fi_unlink_inject_failure = QB_FALSE;
qb_leave();
}
END_TEST
#endif
static void
test_ipc_service_ref_count(void)
{
int32_t c = 0;
int32_t j = 0;
pid_t pid;
uint32_t max_size = MAX_MSG_SIZE;
reference_count_test = QB_TRUE;
pid = run_function_in_new_process(run_ipc_server);
fail_if(pid == -1);
sleep(1);
do {
conn = qb_ipcc_connect(ipc_name, max_size);
if (conn == NULL) {
j = waitpid(pid, NULL, WNOHANG);
ck_assert_int_eq(j, 0);
sleep(1);
c++;
}
} while (conn == NULL && c < 5);
fail_if(conn == NULL);
sleep(5);
kill_server(pid);
}
START_TEST(test_ipc_service_ref_count_shm)
{
qb_enter();
ipc_type = QB_IPC_SHM;
set_ipc_name(__func__);
test_ipc_service_ref_count();
qb_leave();
}
END_TEST
START_TEST(test_ipc_service_ref_count_us)
{
qb_enter();
ipc_type = QB_IPC_SOCKET;
set_ipc_name(__func__);
test_ipc_service_ref_count();
qb_leave();
}
END_TEST
static void test_max_dgram_size(void)
{
/* most implementations will not let you set a dgram buffer
* of 1 million bytes. This test verifies that the we can detect
* the max dgram buffersize regardless, and that the value we detect
* is consistent. */
int32_t init;
int32_t i;
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_REMOVE,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
init = qb_ipcc_verify_dgram_max_msg_size(1000000);
fail_if(init <= 0);
for (i = 0; i < 100; i++) {
int try = qb_ipcc_verify_dgram_max_msg_size(1000000);
#if 0
ck_assert_int_eq(init, try);
#else
/* extra troubleshooting, report also on i and errno variables;
related: https://github.com/ClusterLabs/libqb/issues/234 */
if (init != try) {
#ifdef ci_dump_shm_usage
system("df -h | grep -e /shm >/tmp/_shm_usage");
#endif
ck_abort_msg("Assertion 'init==try' failed:"
" init==%#x, try==%#x, i=%d, errno=%d",
init, try, i, errno);
}
#endif
}
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
}
START_TEST(test_ipc_max_dgram_size)
{
qb_enter();
test_max_dgram_size();
qb_leave();
}
END_TEST
static Suite *
make_shm_suite(void)
{
TCase *tc;
Suite *s = suite_create("shm");
add_tcase(s, tc, test_ipc_txrx_shm_timeout, 30);
add_tcase(s, tc, test_ipc_server_fail_shm, 8);
add_tcase(s, tc, test_ipc_txrx_shm_block, 8);
add_tcase(s, tc, test_ipc_txrx_shm_tmo, 8);
add_tcase(s, tc, test_ipc_fc_shm, 8);
add_tcase(s, tc, test_ipc_dispatch_shm, 16);
add_tcase(s, tc, test_ipc_stress_test_shm, 16);
add_tcase(s, tc, test_ipc_bulk_events_shm, 16);
add_tcase(s, tc, test_ipc_exit_shm, 8);
add_tcase(s, tc, test_ipc_event_on_created_shm, 10);
add_tcase(s, tc, test_ipc_service_ref_count_shm, 10);
add_tcase(s, tc, test_ipc_stress_connections_shm, 3600);
#ifdef HAVE_FAILURE_INJECTION
add_tcase(s, tc, test_ipcc_truncate_when_unlink_fails_shm, 8);
#endif
return s;
}
static Suite *
make_soc_suite(void)
{
Suite *s = suite_create("socket");
TCase *tc;
add_tcase(s, tc, test_ipc_txrx_us_timeout, 30);
/* Commented out for the moment as space in /dev/shm on the CI machines
causes random failures */
/* add_tcase(s, tc, test_ipc_max_dgram_size, 30); */
add_tcase(s, tc, test_ipc_server_fail_soc, 8);
add_tcase(s, tc, test_ipc_txrx_us_block, 8);
add_tcase(s, tc, test_ipc_txrx_us_tmo, 8);
add_tcase(s, tc, test_ipc_fc_us, 8);
add_tcase(s, tc, test_ipc_exit_us, 8);
add_tcase(s, tc, test_ipc_dispatch_us, 16);
#ifndef __clang__ /* see variable length array in structure' at the top */
add_tcase(s, tc, test_ipc_stress_test_us, 60);
#endif
add_tcase(s, tc, test_ipc_bulk_events_us, 16);
add_tcase(s, tc, test_ipc_event_on_created_us, 10);
add_tcase(s, tc, test_ipc_disconnect_after_created_us, 10);
add_tcase(s, tc, test_ipc_service_ref_count_us, 10);
add_tcase(s, tc, test_ipc_stress_connections_us, 3600);
return s;
}
int32_t
main(void)
{
int32_t number_failed;
SRunner *sr;
Suite *s;
int32_t do_shm_tests = QB_TRUE;
set_ipc_name("ipc_test");
#ifdef DISABLE_IPC_SHM
do_shm_tests = QB_FALSE;
#endif /* DISABLE_IPC_SHM */
s = make_soc_suite();
sr = srunner_create(s);
if (do_shm_tests) {
srunner_add_suite(sr, make_shm_suite());
}
qb_log_init("check", LOG_USER, LOG_EMERG);
atexit(qb_log_fini);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log_format_set(QB_LOG_STDERR, "lib/%f|%l| %b");
srunner_run_all(sr, CK_VERBOSE);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/tests/check_log.c b/tests/check_log.c
index bc050b4..0e28ef7 100644
--- a/tests/check_log.c
+++ b/tests/check_log.c
@@ -1,914 +1,914 @@
/*
* Copyright (c) 2011-2015 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <pthread.h>
#include "check_common.h"
#include <qb/qbdefs.h>
#include <qb/qbutil.h>
#include <qb/qblog.h>
#include "_syslog_override.h"
extern size_t qb_vsnprintf_serialize(char *serialize, size_t max_len, const char *fmt, va_list ap);
extern size_t qb_vsnprintf_deserialize(char *string, size_t strlen, const char *buf);
static void
format_this(char *out, const char *fmt, ...)
{
char buf[QB_LOG_MAX_LEN];
va_list ap;
va_start(ap, fmt);
qb_vsnprintf_serialize(buf, QB_LOG_MAX_LEN, fmt, ap);
qb_vsnprintf_deserialize(out, QB_LOG_MAX_LEN, buf);
va_end(ap);
}
static void
format_this_up_to(char *out, size_t max_len, const char *fmt, ...)
{
char buf[QB_LOG_MAX_LEN];
va_list ap;
va_start(ap, fmt);
qb_vsnprintf_serialize(buf, max_len, fmt, ap);
qb_vsnprintf_deserialize(out, QB_LOG_MAX_LEN, buf);
va_end(ap);
}
START_TEST(test_va_serialize)
{
char buf[QB_LOG_MAX_LEN];
char cmp_buf[QB_LOG_MAX_LEN];
format_this(buf, "one line");
ck_assert_str_eq(buf, "one line");
format_this(buf, "p1:%p, p2:%p", format_this, buf);
snprintf(cmp_buf, QB_LOG_MAX_LEN, "p1:%p, p2:%p", format_this, buf);
ck_assert_str_eq(buf, cmp_buf);
format_this(buf, "s1:%s, s2:%s", "Yes", "Never");
ck_assert_str_eq(buf, "s1:Yes, s2:Never");
format_this(buf, "s1:%s, s2:%s", "Yes", "Never");
ck_assert_str_eq(buf, "s1:Yes, s2:Never");
format_this(buf, "d1:%d, d2:%5i, d3:%04i", 23, 37, 84);
ck_assert_str_eq(buf, "d1:23, d2: 37, d3:0084");
format_this(buf, "f1:%.5f, f2:%.2f", 23.34109, 23.34109);
ck_assert_str_eq(buf, "f1:23.34109, f2:23.34");
format_this(buf, "%zd", (size_t)13140964);
ck_assert_str_eq(buf, "13140964");
format_this(buf, "%jd", (intmax_t)30627823);
ck_assert_str_eq(buf, "30627823");
format_this(buf, "%td", buf-cmp_buf);
snprintf(cmp_buf, QB_LOG_MAX_LEN, "%td", buf-cmp_buf);
ck_assert_str_eq(buf, cmp_buf);
format_this(buf, ":%s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world!:");
format_this(buf, ":%15s:", "Hello, world!");
ck_assert_str_eq(buf, ": Hello, world!:");
format_this(buf, ":%.10s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, wor:");
format_this(buf, ":%-10s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world!:");
format_this(buf, ":%-15s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world! :");
format_this(buf, ":%.15s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, world!:");
format_this(buf, ":%15.10s:", "Hello, world!");
ck_assert_str_eq(buf, ": Hello, wor:");
format_this(buf, ":%-15.10s:", "Hello, world!");
ck_assert_str_eq(buf, ":Hello, wor :");
format_this(buf, ":%*d:", 8, 96);
ck_assert_str_eq(buf, ": 96:");
format_this_up_to(buf, 11, "123456789____");
ck_assert_str_eq(buf, "123456789_");
format_this(buf, "Client %s.%.9s wants to fence (%s) '%s' with device '%s'",
"bla", "foooooooooooooooooo",
"action", "target", "hoop");
ck_assert_str_eq(buf,
"Client bla.foooooooo wants to fence (action) 'target' with device 'hoop'");
format_this(buf, "Node %s now has process list: %.32x (was %.32x)",
"18builder", 2, 0);
ck_assert_str_eq(buf, "Node 18builder now has process list: 00000000000000000000000000000002 (was 00000000000000000000000000000000)");
}
END_TEST
START_TEST(test_log_stupid_inputs)
{
int32_t rc;
/* shouldn't crash with out an init() */
qb_log_fini();
/* not init'ed */
rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 2000);
ck_assert_int_eq(rc, -EINVAL);
qb_log(LOG_INFO, "not init'd");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "also not init'd");
qb_log_init("test", LOG_USER, LOG_DEBUG);
/* non-opened log file */
rc = qb_log_filter_ctl(21, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
ck_assert_int_eq(rc, -EBADF);
rc = qb_log_ctl(21, QB_LOG_CONF_PRIORITY_BUMP, -1);
ck_assert_int_eq(rc, -EBADF);
/* target < 0 or >= 32 */
rc = qb_log_filter_ctl(41, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "bla", LOG_TRACE);
ck_assert_int_eq(rc, -EBADF);
rc = qb_log_ctl(-1, QB_LOG_CONF_PRIORITY_BUMP, -1);
ck_assert_int_eq(rc, -EBADF);
/* crap values to filter_ctl() */
rc = qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, NULL, LOG_INFO);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_filter_ctl(QB_LOG_SYSLOG, 56,
QB_LOG_FILTER_FILE, "boja", LOG_INFO);
ck_assert_int_eq(rc, -EINVAL);
/* crap values to ctl() */
rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, -2000);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_ctl(QB_LOG_BLACKBOX, 67, 2000);
ck_assert_int_eq(rc, -EINVAL);
rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_SIZE, 2000);
ck_assert_int_eq(rc, -ENOSYS);
}
END_TEST
static char test_buf[QB_LOG_MAX_LEN];
static uint8_t test_priority;
static int32_t num_msgs;
/*
* to test that we get what we expect.
*/
static void
_test_logger(int32_t t,
struct qb_log_callsite *cs,
time_t timestamp, const char *msg)
{
test_buf[0] = '\0';
qb_log_target_format(t, cs, timestamp, msg, test_buf);
test_priority = cs->priority;
num_msgs++;
}
static void log_also(void)
{
qb_log(LOG_INFO, "yes please");
}
static void log_and_this_too(void)
{
qb_log(LOG_INFO, "this too please");
}
static void log_it_please(void)
{
qb_enter();
qb_log(LOG_TRACE, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_DEBUG, "A:%d B:%d C:%d", 1, 2, 3);
errno = EEXIST;
qb_perror(LOG_WARNING, "bogus error");
errno = 0;
qb_log(LOG_INFO, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_NOTICE, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_WARNING, "A:%d B:%d C:%d", 1, 2, 3);
qb_log(LOG_ERR, "A:%d B:%d C:%d", 1, 2, 3);
qb_leave();
}
static int32_t _cust_t = -1;
static void
m_filter(struct qb_log_callsite *cs)
{
if ((cs->priority >= LOG_ALERT &&
cs->priority <= LOG_INFO) ||
cs->tags > 0) {
qb_bit_set(cs->targets, _cust_t);
} else {
qb_bit_clear(cs->targets, _cust_t);
}
}
START_TEST(test_log_filter_fn)
{
int32_t rc;
qb_log_init("test", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
_cust_t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
_ck_assert_int(_cust_t, >, QB_LOG_BLACKBOX);
rc = qb_log_ctl(_cust_t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
/*
* test the custom filter function.
* make sure qb_log, and qb_log_from_external_source are filtered.
*/
qb_log_filter_fn_set(m_filter);
num_msgs = 0;
qb_log(LOG_NOTICE, "qb_log_filter_fn_set good");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "qb_log_filter_fn_set good");
qb_log(LOG_TRACE, "qb_log_filter_fn_set bad");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG,
__LINE__, 44, "qb_log_filter_fn_set woot");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_DEBUG,
__LINE__, 0, "qb_log_filter_fn_set bad");
ck_assert_int_eq(num_msgs, 3);
}
END_TEST
START_TEST(test_log_basic)
{
int32_t t;
int32_t rc;
qb_log_init("test", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FORMAT, "Angus", LOG_WARNING);
ck_assert_int_eq(rc, 0);
qb_log_format_set(t, "%b");
rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
/* captures last log */
memset(test_buf, 0, sizeof(test_buf));
test_priority = 0;
num_msgs = 0;
/*
* test filtering by format
*/
qb_log(LOG_INFO, "Hello Angus, how are you?");
qb_log(LOG_WARNING, "Hello Steven, how are you?");
qb_log(LOG_ERR, "Hello Andrew, how are you?");
qb_log(LOG_ERR, "Hello Angus, how are you?");
qb_log(LOG_EMERG, "Hello Anna, how are you?");
ck_assert_int_eq(test_priority, LOG_ERR);
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "Hello Angus, how are you?");
/*
* test filtering by file regex
*/
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FORMAT, "*", LOG_TRACE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE_REGEX, "^fakefile*", LOG_DEBUG);
num_msgs = 0;
qb_log_from_external_source(__func__, "fakefile_logging", "%s bla", LOG_INFO,
56, 0, "filename/lineno");
qb_log_from_external_source(__func__, "do_not_log_fakefile_logging", "%s bla", LOG_INFO,
56, 0, "filename/lineno");
ck_assert_int_eq(num_msgs, 1);
/*
* test filtering by format regex
*/
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FORMAT, "*", LOG_TRACE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FORMAT_REGEX, "^one", LOG_WARNING);
num_msgs = 0;
qb_log(LOG_INFO, "one two three");
qb_log(LOG_ERR, "testing one two three");
qb_log(LOG_WARNING, "one two three");
qb_log(LOG_ERR, "one two three");
qb_log(LOG_EMERG, "one two three");
ck_assert_int_eq(num_msgs, 3);
/*
* test filtering by function and regex
*/
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FUNCTION_REGEX, "^log_.*please", LOG_WARNING);
num_msgs = 0;
qb_log(LOG_ERR, "try if you: log_it_please()");
log_it_please();
ck_assert_int_eq(num_msgs, 3);
qb_log_filter_ctl(t, QB_LOG_FILTER_REMOVE,
QB_LOG_FILTER_FUNCTION_REGEX, "log_it_please", LOG_WARNING);
/*
* test filtering by function
*/
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING);
num_msgs = 0;
qb_log(LOG_ERR, "try if you: log_it_please()");
log_it_please();
ck_assert_int_eq(num_msgs, 3);
qb_log_filter_ctl(t, QB_LOG_FILTER_REMOVE,
QB_LOG_FILTER_FUNCTION, "log_it_please", LOG_WARNING);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FUNCTION, __func__, LOG_DEBUG);
num_msgs = 0;
log_it_please();
ck_assert_int_eq(num_msgs, 0);
qb_log(LOG_DEBUG, "try if you: log_it_please()");
ck_assert_int_eq(num_msgs, 1);
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FUNCTION,
"log_also,log_and_this_too",
LOG_DEBUG);
num_msgs = 0;
log_also();
log_and_this_too();
ck_assert_int_eq(num_msgs, 2);
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "fakefile.c,"__FILE__",otherfakefile", LOG_DEBUG);
/*
* make sure we can pass in a null filename or function name.
*/
qb_log_from_external_source(__func__, NULL, "%s", LOG_INFO,
__LINE__, 0, "null filename");
qb_log_from_external_source(NULL, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "null function");
/* check same file/lineno logs with different formats work
*/
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__, "%s bla", LOG_INFO,
56, 0, "filename/lineno");
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "filename/lineno bla");
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
56, 0, "same filename/lineno");
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "same filename/lineno");
/* check filtering works on same file/lineno but different
* log level.
*/
qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, __FILE__, LOG_INFO);
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__,
"same filename/lineno, this level %d",
LOG_INFO, 56, 0, LOG_INFO);
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "same filename/lineno, this level 6");
num_msgs = 0;
qb_log_from_external_source(__func__, __FILE__,
"same filename/lineno, this level %d",
LOG_DEBUG, 56, 0, LOG_DEBUG);
ck_assert_int_eq(num_msgs, 0);
}
END_TEST
static const char *_test_tags_stringify(uint32_t tags)
{
if (tags == 1) {
return "ONE";
} else if (tags == 8) {
return "ATE";
} else {
return "ANY";
}
}
START_TEST(test_log_format)
{
int32_t t;
/* following size/length related equation holds in the context of use:
strlen(cmp_str) = strlen(host_str) + X; X ~ 20 < sizeof(host_str) */
char host_str[256];
char cmp_str[2 * sizeof(host_str)];
qb_log_init("test", LOG_USER, LOG_DEBUG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_format_set(t, "%p %f %b");
qb_log(LOG_DEBUG, "Angus");
ck_assert_str_eq(test_buf, "debug check_log.c Angus");
qb_log(LOG_INFO, "Angus");
ck_assert_str_eq(test_buf, "info check_log.c Angus");
qb_log(LOG_NOTICE, "Angus");
ck_assert_str_eq(test_buf, "notice check_log.c Angus");
qb_log(LOG_WARNING, "Angus");
ck_assert_str_eq(test_buf, "warning check_log.c Angus");
qb_log(LOG_ERR, "Angus");
ck_assert_str_eq(test_buf, "error check_log.c Angus");
qb_log(LOG_CRIT, "Angus");
ck_assert_str_eq(test_buf, "crit check_log.c Angus");
qb_log(LOG_ALERT, "Angus");
ck_assert_str_eq(test_buf, "alert check_log.c Angus");
qb_log(LOG_EMERG, "Angus");
ck_assert_str_eq(test_buf, "emerg check_log.c Angus");
qb_log_tags_stringify_fn_set(_test_tags_stringify);
qb_log_format_set(t, "%g %b");
qb_logt(LOG_INFO, 0, "Angus");
ck_assert_str_eq(test_buf, "ANY Angus");
qb_logt(LOG_INFO, 1, "Angus");
ck_assert_str_eq(test_buf, "ONE Angus");
qb_logt(LOG_INFO, 5, "Angus");
ck_assert_str_eq(test_buf, "ANY Angus");
qb_logt(LOG_INFO, 8, "Angus");
ck_assert_str_eq(test_buf, "ATE Angus");
qb_log_format_set(t, "%-15f %b");
qb_log(LOG_WARNING, "Andrew");
ck_assert_str_eq(test_buf, " check_log.c Andrew");
qb_log_tags_stringify_fn_set(NULL);
gethostname(host_str, sizeof(host_str));
host_str[sizeof(host_str) - 1] = '\0';
qb_log_format_set(t, "%P %H %N %b");
qb_log(LOG_INFO, "Angus");
snprintf(cmp_str, sizeof(cmp_str), "%d %s test Angus", getpid(),
host_str);
ck_assert_str_eq(test_buf, cmp_str);
qb_log_format_set(t, "%3N %H %P %b");
qb_log(LOG_INFO, "Angus");
snprintf(cmp_str, sizeof(cmp_str), "tes %s %d Angus", host_str,
getpid());
ck_assert_str_eq(test_buf, cmp_str);
}
END_TEST
START_TEST(test_log_enable)
{
int32_t t;
int32_t state;
qb_log_init("test", LOG_USER, LOG_DEBUG);
state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_ENABLED);
state = qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
state = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
state = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_STATE_GET, 0);
ck_assert_int_eq(state, QB_LOG_STATE_DISABLED);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
qb_log_format_set(t, "%b");
qb_log(LOG_DEBUG, "Hello");
ck_assert_str_eq(test_buf, "Hello");
num_msgs = 0;
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_FALSE);
qb_log(LOG_DEBUG, "Goodbye");
ck_assert_int_eq(num_msgs, 0);
qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log(LOG_DEBUG, "Hello again");
ck_assert_int_eq(num_msgs, 1);
ck_assert_str_eq(test_buf, "Hello again");
}
END_TEST
#define ITERATIONS 100000
static void *thr_send_logs_2(void *ctx)
{
int32_t i;
printf("%s\n", __func__);
for (i = 0; i < ITERATIONS; i++) {
qb_log(LOG_INFO, "bla bla");
qb_log(LOG_INFO, "blue blue");
qb_log(LOG_INFO, "bra bra");
qb_log(LOG_INFO, "bro bro");
qb_log(LOG_INFO, "brown brown");
qb_log(LOG_INFO, "booo booo");
qb_log(LOG_INFO, "bogus bogus");
qb_log(LOG_INFO, "bungu bungu");
}
return (NULL);
}
static void *thr_send_logs_1(void *ctx)
{
int32_t i;
printf("%s\n", __func__);
for (i = 0; i < ITERATIONS; i++) {
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "foo soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fungus soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fruity soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "free soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "frot soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fresh soup");
qb_log_from_external_source(__func__, __FILE__, "%s", LOG_INFO,
__LINE__, 0, "fattening soup");
}
return (NULL);
}
#define THREADS 4
START_TEST(test_log_threads)
{
pthread_t threads[THREADS];
pthread_attr_t thread_attr[THREADS];
int32_t i;
int32_t rc;
int32_t lf;
void *retval;
qb_log_init("test", LOG_USER, LOG_DEBUG);
lf = qb_log_file_open("threads.log");
rc = qb_log_filter_ctl(lf, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE,
__FILE__, LOG_DEBUG);
ck_assert_int_eq(rc, 0);
qb_log_format_set(lf, "[%p] [%l] %b");
rc = qb_log_ctl(lf, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
ck_assert_int_eq(rc, 0);
for (i = 0; i < THREADS/2; i++) {
pthread_attr_init(&thread_attr[i]);
pthread_attr_setdetachstate(&thread_attr[i],
PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[i], &thread_attr[i],
thr_send_logs_1, NULL);
}
for (i = THREADS/2; i < THREADS; i++) {
pthread_attr_init(&thread_attr[i]);
pthread_attr_setdetachstate(&thread_attr[i],
PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[i], &thread_attr[i],
thr_send_logs_2, NULL);
}
for (i = 0; i < THREADS; i++) {
pthread_join(threads[i], &retval);
}
}
END_TEST
START_TEST(test_log_long_msg)
{
int lpc;
int rc;
int i, max = 1000;
char *buffer = calloc(1, max);
qb_log_init("test", LOG_USER, LOG_DEBUG);
rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
ck_assert_int_eq(rc, 0);
rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024);
ck_assert_int_eq(rc, 0);
rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
rc = qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
ck_assert_int_eq(rc, 0);
for (lpc = 500; lpc < max; lpc++) {
lpc++;
for(i = 0; i < max; i++) {
buffer[i] = 'a' + (i % 10);
}
buffer[lpc%600] = 0;
qb_log(LOG_INFO, "Message %d %d - %s", lpc, lpc%600, buffer);
}
qb_log_blackbox_write_to_file("blackbox.dump");
qb_log_blackbox_print_from_file("blackbox.dump");
unlink("blackbox.dump");
qb_log_fini();
}
END_TEST
START_TEST(test_threaded_logging)
{
int32_t t;
int32_t rc;
qb_log_init("test", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_INFO);
ck_assert_int_eq(rc, 0);
qb_log_format_set(t, "%b");
rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
rc = qb_log_ctl(t, QB_LOG_CONF_THREADED, QB_TRUE);
ck_assert_int_eq(rc, 0);
qb_log_thread_start();
memset(test_buf, 0, sizeof(test_buf));
test_priority = 0;
num_msgs = 0;
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log(LOG_INFO, "Yoda how old are you? - %d", __LINE__);
qb_log_fini();
ck_assert_int_eq(num_msgs, 10);
}
END_TEST
#ifdef HAVE_PTHREAD_SETSCHEDPARAM
START_TEST(test_threaded_logging_bad_sched_params)
{
int32_t t;
int32_t rc;
qb_log_init("test", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_INFO);
ck_assert_int_eq(rc, 0);
qb_log_format_set(t, "%b");
rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
rc = qb_log_ctl(t, QB_LOG_CONF_THREADED, QB_TRUE);
ck_assert_int_eq(rc, 0);
#if defined(SCHED_RR)
#define QB_SCHED SCHED_RR
#elif defined(SCHED_FIFO)
#define QB_SCHED SCHED_FIFO
#else
#define QB_SCHED (-1)
#endif
rc = qb_log_thread_priority_set(QB_SCHED, -1);
ck_assert_int_eq(rc, 0);
rc = qb_log_thread_start();
ck_assert_int_ne(rc, 0);
qb_log_fini();
}
END_TEST
#endif
START_TEST(test_extended_information)
{
int32_t t;
int32_t rc;
int extended;
qb_log_init("test", LOG_USER, LOG_DEBUG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
_ck_assert_int(t, >, QB_LOG_STDOUT);
qb_log_format_set(t, "%b");
rc = qb_log_filter_fn_set(NULL);
ck_assert_int_eq(rc, 0);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE,
"*", LOG_TRACE);
ck_assert_int_eq(rc, 0);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FORMAT,
"*", LOG_TRACE);
ck_assert_int_eq(rc, 0);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FORMAT,
"*", LOG_TRACE);
ck_assert_int_eq(rc, 0);
rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
for (extended = QB_FALSE; extended <= QB_TRUE; ++extended) {
rc = qb_log_ctl(t, QB_LOG_CONF_EXTENDED, extended);
ck_assert_int_eq(rc, 0);
num_msgs = 0;
memset(test_buf, 0, sizeof(test_buf));
qb_log(LOG_ERR, "message with no extended information");
ck_assert_str_eq(test_buf, "message with no extended information");
memset(test_buf, 0, sizeof(test_buf));
qb_log(LOG_ERR, "message with empty extended information "QB_XS);
ck_assert_str_eq(test_buf, "message with empty extended information ");
memset(test_buf, 0, sizeof(test_buf));
qb_log(LOG_ERR, QB_XS" message with only extended information");
ck_assert_str_eq(test_buf, extended?
"| message with only extended information" : "");
memset(test_buf, 0, sizeof(test_buf));
qb_log(LOG_ERR, "message with extended information "QB_XS" (namely this)");
ck_assert_str_eq(test_buf, extended?
"message with extended information | (namely this)"
: "message with extended information ");
ck_assert_int_eq(num_msgs, (extended? 4 : 3));
}
qb_log_fini();
}
END_TEST
static const char *tagtest_stringify_tag(uint32_t tag)
{
static char buf[32];
- sprintf(buf, "%5d", tag);
+ sprintf(buf, "%5" PRIu32, tag);
return buf;
}
START_TEST(test_zero_tags)
{
int32_t rc;
int32_t t;
qb_log_init("test", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
t = qb_log_custom_open(_test_logger, NULL, NULL, NULL);
rc = qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_INFO);
ck_assert_int_eq(rc, 0);
qb_log_format_set(t, "[%g] %b");
qb_log_tags_stringify_fn_set(tagtest_stringify_tag);
rc = qb_log_ctl(t, QB_LOG_CONF_ENABLED, QB_TRUE);
ck_assert_int_eq(rc, 0);
qb_log_filter_ctl(t, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_TRACE);
qb_log_from_external_source("function", "filename", "%s: %d", LOG_DEBUG, 356, 2, "testlog", 2);
ck_assert_str_eq(test_buf, "[ 2] testlog: 2");
qb_log_from_external_source("function", "filename", "%s: %d", LOG_DEBUG, 356, 0, "testlog", 0);
ck_assert_str_eq(test_buf, "[ 2] testlog: 0");
qb_log_fini();
}
END_TEST
START_TEST(test_syslog)
{
qb_log_init("flip", LOG_USER, LOG_INFO);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
qb_log(LOG_ERR, "first as flip");
ck_assert_int_eq(_syslog_opened, 1);
ck_assert_str_eq(_syslog_ident, "flip");
qb_log_ctl2(QB_LOG_SYSLOG, QB_LOG_CONF_IDENT, QB_LOG_CTL2_S("flop"));
qb_log(LOG_ERR, "second as flop");
ck_assert_str_eq(_syslog_ident, "flop");
qb_log_fini();
}
END_TEST
static Suite *
log_suite(void)
{
TCase *tc;
Suite *s = suite_create("logging");
add_tcase(s, tc, test_va_serialize);
add_tcase(s, tc, test_log_stupid_inputs);
add_tcase(s, tc, test_log_basic);
add_tcase(s, tc, test_log_format);
add_tcase(s, tc, test_log_enable);
add_tcase(s, tc, test_log_threads, 360);
add_tcase(s, tc, test_log_long_msg);
add_tcase(s, tc, test_log_filter_fn);
add_tcase(s, tc, test_threaded_logging);
#ifdef HAVE_PTHREAD_SETSCHEDPARAM
add_tcase(s, tc, test_threaded_logging_bad_sched_params);
#endif
add_tcase(s, tc, test_extended_information);
add_tcase(s, tc, test_zero_tags);
add_tcase(s, tc, test_syslog);
return s;
}
int32_t
main(void)
{
int32_t number_failed;
Suite *s = log_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_VERBOSE);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/tests/format_compare_speed.c b/tests/format_compare_speed.c
index 1b04bd7..12b547c 100644
--- a/tests/format_compare_speed.c
+++ b/tests/format_compare_speed.c
@@ -1,97 +1,97 @@
/*
* Copyright (c) 2013 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Angus Salkeld <asalkeld@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <qb/qbdefs.h>
#include <qb/qbutil.h>
#include <qb/qblog.h>
extern size_t qb_vsnprintf_serialize(char *serialize, size_t max_len, const char *fmt, va_list ap);
static void
store_this_qb(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
static void
store_this_snprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
typedef void (*snprintf_like_func)(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
static void
store_this_qb(const char *fmt, ...)
{
char buf[QB_LOG_MAX_LEN];
va_list ap;
va_start(ap, fmt);
qb_vsnprintf_serialize(buf, QB_LOG_MAX_LEN, fmt, ap);
va_end(ap);
}
static void
store_this_snprintf(const char *fmt, ...)
{
char buf[QB_LOG_MAX_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, QB_LOG_MAX_LEN, fmt, ap);
va_end(ap);
}
#define ITERATIONS 10000000
static void
test_this_one(const char *name, snprintf_like_func func)
{
- int i;
+ unsigned i;
qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
float elapsed = 452.245252343;
float ops_per_sec = 0.345624523;
qb_util_stopwatch_start(sw);
for (i = 0; i < ITERATIONS; i++) {
- func("%d %s %llu %9.3f", i, "hello", 3425ULL, elapsed);
- func("[%10s] %.32x -> %p", "hello", i, func);
+ func("%u %s %llu %9.3f", i, "hello", 3425ULL, elapsed);
+ func("[%10s] %.32xd -> %p", "hello", i, func);
func("Client %s.%.9s wants to fence (%s) '%s' with device '%3.5f'",
"bla", "foooooooooooooooooo",
name, "target", ops_per_sec);
func("Node %s now has process list: %.32x (was %.32x)",
- "18builder", 2, 0);
+ "18builder", 2U, 0U);
}
qb_util_stopwatch_stop(sw);
elapsed = qb_util_stopwatch_sec_elapsed_get(sw);
ops_per_sec = ((float)ITERATIONS) / elapsed;
printf("%s] Duration: %9.3f OPs/sec: %9.3f\n", name, elapsed, ops_per_sec);
qb_util_stopwatch_free(sw);
}
int
main(void)
{
test_this_one("qb store", store_this_qb);
test_this_one("snprintf", store_this_snprintf);
return 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 8, 5:53 PM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002393
Default Alt Text
(189 KB)

Event Timeline