tests/tool: Fix some comment typos
[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 "tdb.h"
22 #include "system/time.h"
23 #include "../include/ctdb_private.h"
24 #include "../include/ctdb_client.h"
25
26 int log_ringbuf_size;
27
28 #define MAX_LOG_SIZE 128
29
30 static int first_entry = 0;
31 static int ringbuf_count = 0;
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;
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         int next_entry;
49
50         if (log_entries == NULL && log_ringbuf_size != 0) {
51                 /* Hope this works. We cant log anything if it doesnt anyway */
52                 log_entries = malloc(sizeof(struct ctdb_log_entry) * log_ringbuf_size);
53         }
54         if (log_entries == NULL) {
55                 return;
56         }
57
58         next_entry = (first_entry + ringbuf_count) % log_ringbuf_size;
59
60         if (ringbuf_count > 0 && first_entry == next_entry) {
61                 first_entry = (first_entry + 1) % log_ringbuf_size;
62         }
63
64         log_entries[next_entry].message[0] = '\0';
65
66         ret = vsnprintf(&log_entries[next_entry].message[0], MAX_LOG_SIZE, format, ap);
67         if (ret == -1) {
68                 return;
69         }
70         /* Log messages longer than MAX_LOG_SIZE are truncated to MAX_LOG_SIZE-1
71          * bytes.  In that case, add a newline.
72          */
73         if (ret >= MAX_LOG_SIZE) {
74                 log_entries[next_entry].message[MAX_LOG_SIZE-2] = '\n';
75         }
76
77         log_entries[next_entry].level = this_log_level;
78         log_entries[next_entry].t = timeval_current();
79
80         if (ringbuf_count < log_ringbuf_size) {
81                 ringbuf_count++;
82         }
83 }
84
85 void log_ringbuffer(const char *format, ...)
86 {
87         va_list ap;
88
89         va_start(ap, format);
90         log_ringbuffer_v(format, ap);
91         va_end(ap);
92 }
93
94 void ctdb_log_ringbuffer_free(void)
95 {
96         if (log_entries != NULL) {
97                 free(log_entries);
98                 log_entries = NULL;
99         }
100         log_ringbuf_size = 0;
101 }
102
103 void ctdb_collect_log(struct ctdb_context *ctdb, struct ctdb_get_log_addr *log_addr)
104 {
105         TDB_DATA data;
106         FILE *f;
107         long fsize;
108         int tmp_entry;
109         struct tm *tm;
110         char tbuf[100];
111         int i;
112
113         DEBUG(DEBUG_ERR,("Marshalling %d log entries\n", ringbuf_count));
114
115         /* dump to a file, then send the file as a blob */
116         f = tmpfile();
117         if (f == NULL) {
118                 DEBUG(DEBUG_ERR,(__location__ " Unable to open tmpfile - %s\n", strerror(errno)));
119                 return;
120         }
121
122         for (i=0; i<ringbuf_count; i++) {
123                 tmp_entry = (first_entry + i) % log_ringbuf_size;
124
125                 if (log_entries[tmp_entry].level > log_addr->level) {
126                         continue;
127                 }
128
129                 tm = localtime(&log_entries[tmp_entry].t.tv_sec);
130                 strftime(tbuf, sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
131
132                 if (log_entries[tmp_entry].message[0] != '\0') {
133                         fprintf(f, "%s:%s %s", tbuf,
134                                 get_debug_by_level(log_entries[tmp_entry].level),
135                                 log_entries[tmp_entry].message);
136                 }
137         }
138
139         fsize = ftell(f);
140         if (fsize < 0) {
141                 fclose(f);
142                 DEBUG(DEBUG_ERR, ("Cannot get file size for log entries\n"));
143                 return;
144         }
145         rewind(f);
146         data.dptr = talloc_size(NULL, fsize);
147         if (data.dptr == NULL) {
148                 fclose(f);
149                 CTDB_NO_MEMORY_VOID(ctdb, data.dptr);
150         }
151         data.dsize = fread(data.dptr, 1, fsize, f);
152         fclose(f);
153
154         DEBUG(DEBUG_ERR,("Marshalling log entries into a blob of %d bytes\n", (int)data.dsize));
155
156         DEBUG(DEBUG_ERR,("Send log to %d:%d\n", (int)log_addr->pnn, (int)log_addr->srvid));
157         ctdb_client_send_message(ctdb, log_addr->pnn, log_addr->srvid, data);
158
159         talloc_free(data.dptr);
160 }
161
162 int32_t ctdb_control_get_log(struct ctdb_context *ctdb, TDB_DATA addr)
163 {
164         struct ctdb_get_log_addr *log_addr = (struct ctdb_get_log_addr *)addr.dptr;
165         pid_t child;
166
167         /* spawn a child process to marshall the huge log blob and send it back
168            to the ctdb tool using a MESSAGE
169         */
170         child = ctdb_fork_no_free_ringbuffer(ctdb);
171         if (child == (pid_t)-1) {
172                 DEBUG(DEBUG_ERR,("Failed to fork a log collector child\n"));
173                 return -1;
174         }
175
176         if (child == 0) {
177                 ctdb_set_process_name("ctdb_log_collector");
178                 if (switch_from_server_to_client(ctdb, "log-collector") != 0) {
179                         DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch log collector child into client mode.\n"));
180                         _exit(1);
181                 }
182                 ctdb_collect_log(ctdb, log_addr);
183                 _exit(0);
184         }
185
186         return 0;
187 }
188
189 void ctdb_clear_log(struct ctdb_context *ctdb)
190 {
191         first_entry = 0;
192         ringbuf_count  = 0;
193 }
194
195 int32_t ctdb_control_clear_log(struct ctdb_context *ctdb)
196 {
197         ctdb_clear_log(ctdb);
198
199         return 0;
200 }