* This function forks a child process and drops the realtime
* scheduler for the child process.
*/
-pid_t ctdb_fork_no_free_ringbuffer(struct ctdb_context *ctdb)
+pid_t ctdb_fork(struct ctdb_context *ctdb)
{
pid_t pid;
return pid;
}
-pid_t ctdb_fork(struct ctdb_context *ctdb)
-{
- pid_t pid;
-
- pid = ctdb_fork_no_free_ringbuffer(ctdb);
- if (pid == 0) {
- ctdb_log_ringbuffer_free();
- }
-
- return pid;
-}
-
-
static void ctdb_sigchld_handler(struct tevent_context *ev,
struct tevent_signal *te, int signum, int count,
void *dont_care,
int this_log_level = 0;
const char *debug_extra = "";
-int log_ringbuf_size;
-
-#define MAX_LOG_SIZE 128
-
-static int first_entry = 0;
-static int ringbuf_count = 0;
-
-struct ctdb_log_entry {
- int32_t level;
- struct timeval t;
- char message[MAX_LOG_SIZE];
-};
-
-
-static struct ctdb_log_entry *log_entries;
-
-/*
- * this function logs all messages for all levels to a ringbuffer
- */
-static void log_ringbuffer_v(const char *format, va_list ap)
-{
- int ret;
- int next_entry;
-
- if (log_entries == NULL && log_ringbuf_size != 0) {
- /* Hope this works. We cant log anything if it doesnt anyway */
- log_entries = malloc(sizeof(struct ctdb_log_entry) * log_ringbuf_size);
- }
- if (log_entries == NULL) {
- return;
- }
-
- next_entry = (first_entry + ringbuf_count) % log_ringbuf_size;
-
- if (ringbuf_count > 0 && first_entry == next_entry) {
- first_entry = (first_entry + 1) % log_ringbuf_size;
- }
-
- log_entries[next_entry].message[0] = '\0';
-
- ret = vsnprintf(&log_entries[next_entry].message[0], MAX_LOG_SIZE, format, ap);
- if (ret == -1) {
- return;
- }
- /* Log messages longer than MAX_LOG_SIZE are truncated to MAX_LOG_SIZE-1
- * bytes. In that case, add a newline.
- */
- if (ret >= MAX_LOG_SIZE) {
- log_entries[next_entry].message[MAX_LOG_SIZE-2] = '\n';
- }
-
- log_entries[next_entry].level = this_log_level;
- log_entries[next_entry].t = timeval_current();
-
- if (ringbuf_count < log_ringbuf_size) {
- ringbuf_count++;
- }
-}
-
-void log_ringbuffer(const char *format, ...)
-{
- va_list ap;
-
- va_start(ap, format);
- log_ringbuffer_v(format, ap);
- va_end(ap);
-}
-
-void ctdb_log_ringbuffer_free(void)
-{
- if (log_entries != NULL) {
- free(log_entries);
- log_entries = NULL;
- }
- log_ringbuf_size = 0;
-}
-
-TDB_DATA ctdb_log_ringbuffer_collect_log(TALLOC_CTX *mem_ctx,
- enum debug_level max_level)
-{
- TDB_DATA data;
- FILE *f;
- long fsize;
- int tmp_entry;
- struct tm *tm;
- char tbuf[100];
- int i;
-
- DEBUG(DEBUG_ERR,("Marshalling %d log entries\n", ringbuf_count));
-
- /* dump to a file, then send the file as a blob */
- f = tmpfile();
- if (f == NULL) {
- DEBUG(DEBUG_ERR,(__location__ " Unable to open tmpfile - %s\n",
- strerror(errno)));
- return tdb_null;
- }
-
- for (i=0; i<ringbuf_count; i++) {
- tmp_entry = (first_entry + i) % log_ringbuf_size;
-
- if (log_entries[tmp_entry].level > max_level) {
- continue;
- }
-
- tm = localtime(&log_entries[tmp_entry].t.tv_sec);
- strftime(tbuf, sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
-
- if (log_entries[tmp_entry].message[0] != '\0') {
- fprintf(f, "%s:%s %s", tbuf,
- get_debug_by_level(log_entries[tmp_entry].level),
- log_entries[tmp_entry].message);
- }
- }
-
- fsize = ftell(f);
- if (fsize < 0) {
- fclose(f);
- DEBUG(DEBUG_ERR, ("Cannot get file size for log entries\n"));
- return tdb_null;
- }
- rewind(f);
- data.dptr = talloc_size(NULL, fsize);
- if (data.dptr == NULL) {
- fclose(f);
- DEBUG(DEBUG_ERR, (__location__ " Memory allocation error\n"));
- return tdb_null;
- }
- data.dsize = fread(data.dptr, 1, fsize, f);
- fclose(f);
-
- DEBUG(DEBUG_ERR,("Marshalling log entries into a blob of %d bytes\n", (int)data.dsize));
-
- return data;
-}
-
-void ctdb_clear_log(struct ctdb_context *ctdb)
-{
- first_entry = 0;
- ringbuf_count = 0;
-}
-
-int32_t ctdb_control_clear_log(struct ctdb_context *ctdb)
-{
- ctdb_clear_log(ctdb);
-
- return 0;
-}
-
struct debug_levels debug_levels[] = {
{DEBUG_EMERG, "EMERG"},
{DEBUG_ALERT, "ALERT"},
maybe_set "--no-lmaster" "$CTDB_CAPABILITY_LMASTER" "no"
maybe_set "--lvs --single-public-ip" "$CTDB_LVS_PUBLIC_IP"
maybe_set "--script-log-level" "$CTDB_SCRIPT_LOG_LEVEL"
- maybe_set "--log-ringbuf-size" "$CTDB_LOG_RINGBUF_SIZE"
maybe_set "--syslog" "$CTDB_SYSLOG" "yes"
maybe_set "--max-persistent-check-errors" "$CTDB_MAX_PERSISTENT_CHECK_ERRORS"
}
</para>
</refsect2>
- <refsect2>
- <title>getlog [<parameter>LEVEL</parameter>] [recoverd]</title>
- <para>
- In addition to the normal logging to a log file, CTDB also
- keeps a in-memory ringbuffer containing the most recent log
- entries for all log levels (except DEBUG).
- </para>
- <para>
- This is useful since it allows for keeping continuous logs to a file
- at a reasonable non-verbose level, but shortly after an incident has
- occured, a much more detailed log can be pulled from memory. This
- can allow you to avoid having to reproduce an issue due to the
- on-disk logs being of insufficient detail.
- </para>
- <para>
- This command extracts all messages of level or lower log level
- from memory and prints it to the screen. The level is not
- specified it defaults to NOTICE.
- </para>
- <para>
- By default, logs are extracted from the main CTDB daemon. If
- the recoverd option is given then logs are extracted from the
- recovery daemon.
- </para>
- </refsect2>
-
- <refsect2>
- <title>clearlog [recoverd]</title>
- <para>
- This command clears the in-memory logging ringbuffer.
- </para>
- <para>
- By default, logs are cleared in the main CTDB daemon. If the
- recoverd option is given then logs are cleared in the recovery
- daemon.
- </para>
- </refsect2>
-
<refsect2>
<title>setdbreadonly <parameter>DB</parameter></title>
<para>
</listitem>
</varlistentry>
- <varlistentry>
- <term>--log-ringbuf-size=<parameter>NUM</parameter></term>
- <listitem>
- <para>
- Set the size of the log ringbuffer to NUM entries.
- </para>
- <para>
- CTDB uses an in-memory ringbuffer containing NUM most
- recent log entries for all log levels (except DEBUG). The
- ringbugger can be useful for extracting detailed logs even
- if some entries are not logged to the regular logs.
- </para>
- <para>
- Use the <command>ctdb getlog</command> command to retrieve
- log entries from the ringbuffer.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term>--lvs</term>
<listitem>
</listitem>
</varlistentry>
- <varlistentry>
- <term>CTDB_LOG_RINGBUF_SIZE=<parameter>NUM</parameter></term>
- <listitem>
- <para>
- Default is 0. Corresponds to
- <option>--log-ringbuf-size</option>.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term>CTDB_LVS_PUBLIC_IP=<parameter>IPADDR</parameter></term>
<listitem>
};
#define DEBUGLVL(lvl) ((lvl) <= LogLevel)
-#define DEBUG(lvl, x) do { this_log_level = (lvl); if ((lvl) < DEBUG_DEBUG) { log_ringbuffer x; } if ((lvl) <= LogLevel) { do_debug x; }} while (0)
+#define DEBUG(lvl, x) do { this_log_level = (lvl); if ((lvl) <= LogLevel) { do_debug x; }} while (0)
#define DEBUGADD(lvl, x) do { if ((lvl) <= LogLevel) { this_log_level = (lvl); do_debug_add x; }} while (0)
#endif /* _CTDB_LOGGING_H_ */
struct tevent_signal *ctdb_init_sigchld(struct ctdb_context *ctdb);
void ctdb_track_child(struct ctdb_context *ctdb, pid_t pid);
pid_t ctdb_fork(struct ctdb_context *ctdb);
-pid_t ctdb_fork_no_free_ringbuffer(struct ctdb_context *ctdb);
void ctdb_set_child_info(TALLOC_CTX *mem_ctx, const char *child_name_fmt, ...);
bool ctdb_is_child_process(void);
int ctdb_kill(struct ctdb_context *ctdb, pid_t pid, int signum);
int32_t level;
};
-extern int log_ringbuf_size;
-
-enum debug_level;
-TDB_DATA ctdb_log_ringbuffer_collect_log(TALLOC_CTX *mem_ctx,
- enum debug_level max_level);
-void ctdb_collect_log(struct ctdb_context *ctdb, struct ctdb_get_log_addr *log_addr);
-void ctdb_clear_log(struct ctdb_context *ctdb);
-int32_t ctdb_control_get_log(struct ctdb_context *ctdb, TDB_DATA addr);
-int32_t ctdb_control_clear_log(struct ctdb_context *ctdb);
-void ctdb_log_ringbuffer_free(void);
-
struct ctdb_log_state *ctdb_vfork_with_logging(TALLOC_CTX *mem_ctx,
struct ctdb_context *ctdb,
const char *log_prefix,
CTDB_CONTROL_REGISTER_NOTIFY = 114,
CTDB_CONTROL_DEREGISTER_NOTIFY = 115,
CTDB_CONTROL_TRANS2_ACTIVE = 116, /* obsolete */
- CTDB_CONTROL_GET_LOG = 117,
- CTDB_CONTROL_CLEAR_LOG = 118,
+ CTDB_CONTROL_GET_LOG = 117, /* obsolete */
+ CTDB_CONTROL_CLEAR_LOG = 118, /* obsolete */
CTDB_CONTROL_TRANS3_COMMIT = 119,
CTDB_CONTROL_GET_DB_SEQNUM = 120,
CTDB_CONTROL_DB_SET_HEALTHY = 121,
extern void (*do_debug_v)(const char *, va_list ap);
extern void (*do_debug_add_v)(const char *, va_list ap);
-void log_ringbuffer(const char *format, ...);
void do_debug(const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
void do_debug_add(const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
void dump_data(int level, const uint8_t *buf1, size_t len);
return ctdb_control_deregister_notify(ctdb, client_id, indata);
case CTDB_CONTROL_GET_LOG:
- CHECK_CONTROL_DATA_SIZE(sizeof(struct ctdb_get_log_addr));
- return ctdb_control_get_log(ctdb, indata);
+ return control_not_implemented("GET_LOG", NULL);
case CTDB_CONTROL_CLEAR_LOG:
- return ctdb_control_clear_log(ctdb);
+ return control_not_implemented("CLEAR_LOG", NULL);
case CTDB_CONTROL_GET_DB_SEQNUM:
CHECK_CONTROL_DATA_SIZE(sizeof(uint64_t));
ctdb);
return ret;
}
-
-void ctdb_collect_log(struct ctdb_context *ctdb, struct ctdb_get_log_addr *log_addr)
-{
- TDB_DATA data;
-
- data = ctdb_log_ringbuffer_collect_log(ctdb, log_addr->level);
-
- DEBUG(DEBUG_ERR,("Send log to %d:%d\n", (int)log_addr->pnn, (int)log_addr->srvid));
- ctdb_client_send_message(ctdb, log_addr->pnn, log_addr->srvid, data);
-
- if (data.dptr) {
- talloc_free(data.dptr);
- }
-}
-
-int32_t ctdb_control_get_log(struct ctdb_context *ctdb, TDB_DATA addr)
-{
- struct ctdb_get_log_addr *log_addr = (struct ctdb_get_log_addr *)addr.dptr;
- pid_t child;
-
- /* spawn a child process to marshall the huge log blob and send it back
- to the ctdb tool using a MESSAGE
- */
- child = ctdb_fork_no_free_ringbuffer(ctdb);
- if (child == (pid_t)-1) {
- DEBUG(DEBUG_ERR,("Failed to fork a log collector child\n"));
- return -1;
- }
-
- if (child == 0) {
- ctdb_set_process_name("ctdb_log_collector");
- if (switch_from_server_to_client(ctdb, "log-collector") != 0) {
- DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch log collector child into client mode.\n"));
- _exit(1);
- }
- /* do logging here */
- ctdb_collect_log(ctdb, log_addr);
- _exit(0);
- }
-
- return 0;
-}
talloc_free(tmp_ctx);
}
-/*
- handler for getlog
-*/
-static void getlog_handler(struct ctdb_context *ctdb, uint64_t srvid,
- TDB_DATA data, void *private_data)
-{
- struct ctdb_get_log_addr *log_addr;
- pid_t child;
-
- if (data.dsize != sizeof(struct ctdb_get_log_addr)) {
- DEBUG(DEBUG_ERR, (__location__ " Wrong size of return address.\n"));
- return;
- }
- log_addr = (struct ctdb_get_log_addr *)data.dptr;
-
- child = ctdb_fork_no_free_ringbuffer(ctdb);
- if (child == (pid_t)-1) {
- DEBUG(DEBUG_ERR,("Failed to fork a log collector child\n"));
- return;
- }
-
- if (child == 0) {
- ctdb_set_process_name("ctdb_rec_log_collector");
- if (switch_from_server_to_client(ctdb, "recoverd-log-collector") != 0) {
- DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch log collector child into client mode.\n"));
- _exit(1);
- }
- ctdb_collect_log(ctdb, log_addr);
- _exit(0);
- }
-}
-
-/*
- handler for clearlog
-*/
-static void clearlog_handler(struct ctdb_context *ctdb, uint64_t srvid,
- TDB_DATA data, void *private_data)
-{
- ctdb_clear_log(ctdb);
-}
-
/*
handler for reload_nodes
*/
/* register a message port for sending memory dumps */
ctdb_client_set_message_handler(ctdb, CTDB_SRVID_MEM_DUMP, mem_dump_handler, rec);
- /* register a message port for requesting logs */
- ctdb_client_set_message_handler(ctdb, CTDB_SRVID_GETLOG, getlog_handler, rec);
-
- /* register a message port for clearing logs */
- ctdb_client_set_message_handler(ctdb, CTDB_SRVID_CLEARLOG, clearlog_handler, rec);
-
/* register a message port for recovery elections */
ctdb_client_set_message_handler(ctdb, CTDB_SRVID_RECOVERY, election_handler, rec);
return -1;
}
- ctdb->recoverd_pid = ctdb_fork_no_free_ringbuffer(ctdb);
+ ctdb->recoverd_pid = ctdb_fork(ctdb);
if (ctdb->recoverd_pid == -1) {
return -1;
}
srandom(getpid() ^ time(NULL));
- /* Clear the log ringbuffer */
- ctdb_clear_log(ctdb);
-
ctdb_set_process_name("ctdb_recovered");
if (switch_from_server_to_client(ctdb, "recoverd") != 0) {
DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch recovery daemon into client mode. shutting down.\n"));
{ "max-persistent-check-errors", 0, POPT_ARG_INT,
&options.max_persistent_check_errors, 0,
"max allowed persistent check errors (default 0)", NULL },
- { "log-ringbuf-size", 0, POPT_ARG_INT, &log_ringbuf_size, 0, "Number of log messages we can store in the memory ringbuffer", NULL },
{ "sloppy-start", 0, POPT_ARG_NONE, &fast_start, 0, "Do not perform full recovery on start", NULL },
POPT_TABLEEND
};
fi
local node_ip=$(sed -n -e "$(($pnn + 1))p" "$CTDB_NODES")
- local ctdb_options="--sloppy-start --reclock=${TEST_VAR_DIR}/rec.lock --nlist $CTDB_NODES --nopublicipcheck --listen=${node_ip} --event-script-dir=${TEST_VAR_DIR}/events.d --logfile=${TEST_VAR_DIR}/daemon.${pnn}.log -d 3 --log-ringbuf-size=10000 --dbdir=${TEST_VAR_DIR}/test.db --dbdir-persistent=${TEST_VAR_DIR}/test.db/persistent --dbdir-state=${TEST_VAR_DIR}/test.db/state --nosetsched"
+ local ctdb_options="--sloppy-start --reclock=${TEST_VAR_DIR}/rec.lock --nlist $CTDB_NODES --nopublicipcheck --listen=${node_ip} --event-script-dir=${TEST_VAR_DIR}/events.d --logfile=${TEST_VAR_DIR}/daemon.${pnn}.log -d 3 --dbdir=${TEST_VAR_DIR}/test.db --dbdir-persistent=${TEST_VAR_DIR}/test.db/persistent --dbdir-state=${TEST_VAR_DIR}/test.db/state --nosetsched"
if [ $pnn -eq $no_public_ips ] ; then
ctdb_options="$ctdb_options --public-addresses=/dev/null"
}
-
-static void log_handler(struct ctdb_context *ctdb, uint64_t srvid,
- TDB_DATA data, void *private_data)
-{
- DEBUG(DEBUG_ERR,("Log data received\n"));
- if (data.dsize > 0) {
- printf("%s", data.dptr);
- }
-
- exit(0);
-}
-
-/*
- display a list of log messages from the in memory ringbuffer
- */
-static int control_getlog(struct ctdb_context *ctdb, int argc, const char **argv)
-{
- int ret, i;
- bool main_daemon;
- struct ctdb_get_log_addr log_addr;
- TDB_DATA data;
- struct timeval tv;
-
- /* Process options */
- main_daemon = true;
- log_addr.pnn = ctdb_get_pnn(ctdb);
- log_addr.level = DEBUG_NOTICE;
- for (i = 0; i < argc; i++) {
- if (strcmp(argv[i], "recoverd") == 0) {
- main_daemon = false;
- } else {
- if (isalpha(argv[i][0]) || argv[i][0] == '-') {
- log_addr.level = get_debug_by_desc(argv[i]);
- } else {
- log_addr.level = strtol(argv[i], NULL, 0);
- }
- }
- }
-
- /* Our message port is our PID */
- log_addr.srvid = getpid();
-
- data.dptr = (unsigned char *)&log_addr;
- data.dsize = sizeof(log_addr);
-
- DEBUG(DEBUG_ERR, ("Pulling logs from node %u\n", options.pnn));
-
- ctdb_client_set_message_handler(ctdb, log_addr.srvid, log_handler, NULL);
- sleep(1);
-
- DEBUG(DEBUG_ERR,("Listen for response on %d\n", (int)log_addr.srvid));
-
- if (main_daemon) {
- int32_t res;
- char *errmsg;
- TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
-
- ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_GET_LOG,
- 0, data, tmp_ctx, NULL, &res, NULL, &errmsg);
- if (ret != 0 || res != 0) {
- DEBUG(DEBUG_ERR,("Failed to get logs - %s\n", errmsg));
- talloc_free(tmp_ctx);
- return -1;
- }
- talloc_free(tmp_ctx);
- } else {
- ret = ctdb_client_send_message(ctdb, options.pnn,
- CTDB_SRVID_GETLOG, data);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,("Failed to send getlog request message to %u\n", options.pnn));
- return -1;
- }
- }
-
- tv = timeval_current();
- /* this loop will terminate when we have received the reply */
- while (timeval_elapsed(&tv) < (double)options.timelimit) {
- event_loop_once(ctdb->ev);
- }
-
- DEBUG(DEBUG_INFO,("Timed out waiting for log data.\n"));
-
- return 0;
-}
-
-/*
- clear the in memory log area
- */
-static int control_clearlog(struct ctdb_context *ctdb, int argc, const char **argv)
-{
- int ret;
-
- if (argc == 0 || (argc >= 1 && strcmp(argv[0], "recoverd") != 0)) {
- /* "recoverd" not given - get logs from main daemon */
- int32_t res;
- char *errmsg;
- TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
-
- ret = ctdb_control(ctdb, options.pnn, 0, CTDB_CONTROL_CLEAR_LOG,
- 0, tdb_null, tmp_ctx, NULL, &res, NULL, &errmsg);
- if (ret != 0 || res != 0) {
- DEBUG(DEBUG_ERR,("Failed to clear logs\n"));
- talloc_free(tmp_ctx);
- return -1;
- }
-
- talloc_free(tmp_ctx);
- } else {
- TDB_DATA data; /* unused in recoverd... */
- data.dsize = 0;
-
- ret = ctdb_client_send_message(ctdb, options.pnn, CTDB_SRVID_CLEARLOG, data);
- if (ret != 0) {
- DEBUG(DEBUG_ERR,("Failed to send clearlog request message to %u\n", options.pnn));
- return -1;
- }
- }
-
- return 0;
-}
-
/* Reload public IPs on a specified nodes */
static int control_reloadips(struct ctdb_context *ctdb, int argc, const char **argv)
{
{ "enablemonitor", control_enable_monmode, true, false, "set monitoring mode to ACTIVE" },
{ "setdebug", control_setdebug, true, false, "set debug level", "<EMERG|ALERT|CRIT|ERR|WARNING|NOTICE|INFO|DEBUG>" },
{ "getdebug", control_getdebug, true, false, "get debug level" },
- { "getlog", control_getlog, true, false, "get the log data from the in memory ringbuffer", "[<level>] [recoverd]" },
- { "clearlog", control_clearlog, true, false, "clear the log data from the in memory ringbuffer", "[recoverd]" },
{ "attach", control_attach, true, false, "attach to a database", "<dbname> [persistent]" },
{ "detach", control_detach, false, false, "detach from a database", "<dbname|dbid> [<dbname|dbid> ...]" },
{ "dumpmemory", control_dumpmemory, true, false, "dump memory map to stdout" },