64507f4a20faa80299570678e20e422a96bac182
[sahlberg/ctdb.git] / common / ctdb_logging.c
1 /* 
2    ctdb logging code
3
4    Copyright (C) Ronnie Sahlberg 2009
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "lib/events/events.h"
22 #include "lib/tdb/include/tdb.h"
23 #include "system/time.h"
24 #include "../include/ctdb_private.h"
25 #include "../include/ctdb.h"
26
27 #define MAX_LOG_ENTRIES 500000
28 #define MAX_LOG_SIZE 128
29
30 static int first_entry;
31 static int last_entry;
32
33 struct ctdb_log_entry {
34         int32_t level;
35         struct timeval t;
36         char message[MAX_LOG_SIZE];
37 };
38
39
40 static struct ctdb_log_entry log_entries[MAX_LOG_ENTRIES];
41
42 /*
43  * this function logs all messages for all levels to a ringbuffer
44  */
45 static void log_ringbuffer_v(const char *format, va_list ap)
46 {
47         int ret;
48
49         log_entries[last_entry].message[0] = '\0';
50
51         ret = vsnprintf(&log_entries[last_entry].message[0], MAX_LOG_SIZE, format, ap);
52         if (ret == -1) {
53                 return;
54         }
55
56         log_entries[last_entry].level = this_log_level;
57         log_entries[last_entry].t = timeval_current();
58
59         last_entry++;
60         if (last_entry >= MAX_LOG_ENTRIES) {
61                 last_entry = 0;
62         }
63         if (first_entry == last_entry) {
64                 first_entry++;
65         }
66         if (first_entry >= MAX_LOG_ENTRIES) {
67                 first_entry = 0;
68         }
69 }
70
71 void log_ringbuffer(const char *format, ...)
72 {
73         va_list ap;
74
75         va_start(ap, format);
76         log_ringbuffer_v(format, ap);
77         va_end(ap);
78 }
79
80
81
82 static void ctdb_collect_log(struct ctdb_context *ctdb, struct ctdb_get_log_addr *log_addr)
83 {
84         TDB_DATA data;
85         FILE *f;
86         long fsize;
87         int tmp_entry;
88         int count = 0;
89         DEBUG(DEBUG_ERR,("Marshalling log entries  first:%d last:%d\n", first_entry, last_entry));
90
91         /* dump to a file, then send the file as a blob */
92         f = tmpfile();
93         if (f == NULL) {
94                 DEBUG(DEBUG_ERR,(__location__ " Unable to open tmpfile - %s\n", strerror(errno)));
95                 return;
96         }
97
98         tmp_entry = first_entry;
99         while (tmp_entry != last_entry) {
100                 struct tm *tm;
101                 char tbuf[100];
102
103                 if (log_entries[tmp_entry].level > log_addr->level) {
104                         tmp_entry++;
105                         if (tmp_entry >= MAX_LOG_ENTRIES) {
106                                 tmp_entry = 0;
107                         }
108                         continue;
109                 }
110
111                 tm = localtime(&log_entries[tmp_entry].t.tv_sec);
112                 strftime(tbuf, sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
113
114                 if (log_entries[tmp_entry].message) {
115                         count += fprintf(f, "%s:%s %s", tbuf, get_debug_by_level(log_entries[tmp_entry].level), log_entries[tmp_entry].message);
116                 }
117
118                 tmp_entry++;
119                 if (tmp_entry >= MAX_LOG_ENTRIES) {
120                         tmp_entry = 0;
121                 }
122         }
123
124         fsize = ftell(f);
125         rewind(f);
126         data.dptr = talloc_size(NULL, fsize);
127         CTDB_NO_MEMORY_VOID(ctdb, data.dptr);
128         data.dsize = fread(data.dptr, 1, fsize, f);
129         fclose(f);
130
131         DEBUG(DEBUG_ERR,("Marshalling log entries into a blob of %d bytes\n", (int)data.dsize));
132
133         DEBUG(DEBUG_ERR,("Send log to %d:%d\n", (int)log_addr->pnn, (int)log_addr->srvid));
134         ctdb_send_message(ctdb, log_addr->pnn, log_addr->srvid, data);
135
136         talloc_free(data.dptr);
137 }
138
139 int32_t ctdb_control_get_log(struct ctdb_context *ctdb, TDB_DATA addr)
140 {
141         struct ctdb_get_log_addr *log_addr = (struct ctdb_get_log_addr *)addr.dptr;
142         pid_t child;
143
144         /* spawn a child process to marshall the huge log blob and send it back
145            to the ctdb tool using a MESSAGE
146         */
147         child = fork();
148         if (child == (pid_t)-1) {
149                 DEBUG(DEBUG_ERR,("Failed to fork a log collector child\n"));
150                 return -1;
151         }
152
153         if (child == 0) {
154                 if (switch_from_server_to_client(ctdb) != 0) {
155                         DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch log collector child into client mode.\n"));
156                         _exit(1);
157                 }
158                 ctdb_collect_log(ctdb, log_addr);
159                 _exit(0);
160         }
161
162         return 0;
163 }
164
165
166 int32_t ctdb_control_clear_log(struct ctdb_context *ctdb)
167 {
168         first_entry = 0;
169         last_entry  = 0;
170
171         return 0;
172 }
173