recoverd: Add CTDB_SRVID_GETLOG and CTDB_SRVID_CLEARLOG
[vlendec/samba-autobuild/.git] / ctdb / 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/tdb/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;
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;
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         if (log_entries == NULL && log_ringbuf_size != 0) {
50                 /* Hope this works. We cant log anything if it doesnt anyway */
51                 log_entries = malloc(sizeof(struct ctdb_log_entry) * log_ringbuf_size);
52         }
53         if (log_entries == NULL) {
54                 return;
55         }
56
57         log_entries[last_entry].message[0] = '\0';
58
59         ret = vsnprintf(&log_entries[last_entry].message[0], MAX_LOG_SIZE, format, ap);
60         if (ret == -1) {
61                 return;
62         }
63
64         log_entries[last_entry].level = this_log_level;
65         log_entries[last_entry].t = timeval_current();
66
67         last_entry++;
68         if (last_entry >= log_ringbuf_size) {
69                 last_entry = 0;
70         }
71         if (first_entry == last_entry) {
72                 first_entry++;
73         }
74         if (first_entry >= log_ringbuf_size) {
75                 first_entry = 0;
76         }
77 }
78
79 void log_ringbuffer(const char *format, ...)
80 {
81         va_list ap;
82
83         va_start(ap, format);
84         log_ringbuffer_v(format, ap);
85         va_end(ap);
86 }
87
88
89
90 void ctdb_collect_log(struct ctdb_context *ctdb, struct ctdb_get_log_addr *log_addr)
91 {
92         TDB_DATA data;
93         FILE *f;
94         long fsize;
95         int tmp_entry;
96         int count = 0;
97         DEBUG(DEBUG_ERR,("Marshalling log entries  first:%d last:%d\n", first_entry, last_entry));
98
99         /* dump to a file, then send the file as a blob */
100         f = tmpfile();
101         if (f == NULL) {
102                 DEBUG(DEBUG_ERR,(__location__ " Unable to open tmpfile - %s\n", strerror(errno)));
103                 return;
104         }
105
106         tmp_entry = first_entry;
107         while (tmp_entry != last_entry) {
108                 struct tm *tm;
109                 char tbuf[100];
110
111                 if (log_entries == NULL) {
112                         break;
113                 }
114
115                 if (log_entries[tmp_entry].level > log_addr->level) {
116                         tmp_entry++;
117                         if (tmp_entry >= log_ringbuf_size) {
118                                 tmp_entry = 0;
119                         }
120                         continue;
121                 }
122
123                 tm = localtime(&log_entries[tmp_entry].t.tv_sec);
124                 strftime(tbuf, sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
125
126                 if (log_entries[tmp_entry].message) {
127                         count += fprintf(f, "%s:%s %s", tbuf, get_debug_by_level(log_entries[tmp_entry].level), log_entries[tmp_entry].message);
128                 }
129
130                 tmp_entry++;
131                 if (tmp_entry >= log_ringbuf_size) {
132                         tmp_entry = 0;
133                 }
134         }
135
136         fsize = ftell(f);
137         rewind(f);
138         data.dptr = talloc_size(NULL, fsize);
139         CTDB_NO_MEMORY_VOID(ctdb, data.dptr);
140         data.dsize = fread(data.dptr, 1, fsize, f);
141         fclose(f);
142
143         DEBUG(DEBUG_ERR,("Marshalling log entries into a blob of %d bytes\n", (int)data.dsize));
144
145         DEBUG(DEBUG_ERR,("Send log to %d:%d\n", (int)log_addr->pnn, (int)log_addr->srvid));
146         ctdb_client_send_message(ctdb, log_addr->pnn, log_addr->srvid, data);
147
148         talloc_free(data.dptr);
149 }
150
151 int32_t ctdb_control_get_log(struct ctdb_context *ctdb, TDB_DATA addr)
152 {
153         struct ctdb_get_log_addr *log_addr = (struct ctdb_get_log_addr *)addr.dptr;
154         pid_t child;
155
156         /* spawn a child process to marshall the huge log blob and send it back
157            to the ctdb tool using a MESSAGE
158         */
159         child = ctdb_fork(ctdb);
160         if (child == (pid_t)-1) {
161                 DEBUG(DEBUG_ERR,("Failed to fork a log collector child\n"));
162                 return -1;
163         }
164
165         if (child == 0) {
166                 if (switch_from_server_to_client(ctdb, "log-collector") != 0) {
167                         DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch log collector child into client mode.\n"));
168                         _exit(1);
169                 }
170                 ctdb_collect_log(ctdb, log_addr);
171                 _exit(0);
172         }
173
174         return 0;
175 }
176
177 void ctdb_clear_log(struct ctdb_context *ctdb)
178 {
179         first_entry = 0;
180         last_entry  = 0;
181 }
182
183 int32_t ctdb_control_clear_log(struct ctdb_context *ctdb)
184 {
185         ctdb_clear_log(ctdb);
186
187         return 0;
188 }