Add ctdb_fork(0 which will fork a child process and drop the real-time
[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/tevent/tevent.h"
22 #include "lib/tdb/include/tdb.h"
23 #include "system/time.h"
24 #include "../include/ctdb_private.h"
25 #include "../include/ctdb_client.h"
26
27 int log_ringbuf_size;
28
29 #define MAX_LOG_SIZE 128
30
31 static int first_entry;
32 static int last_entry;
33
34 struct ctdb_log_entry {
35         int32_t level;
36         struct timeval t;
37         char message[MAX_LOG_SIZE];
38 };
39
40
41 static struct ctdb_log_entry *log_entries;
42
43 /*
44  * this function logs all messages for all levels to a ringbuffer
45  */
46 static void log_ringbuffer_v(const char *format, va_list ap)
47 {
48         int ret;
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         log_entries[last_entry].message[0] = '\0';
59
60         ret = vsnprintf(&log_entries[last_entry].message[0], MAX_LOG_SIZE, format, ap);
61         if (ret == -1) {
62                 return;
63         }
64
65         log_entries[last_entry].level = this_log_level;
66         log_entries[last_entry].t = timeval_current();
67
68         last_entry++;
69         if (last_entry >= log_ringbuf_size) {
70                 last_entry = 0;
71         }
72         if (first_entry == last_entry) {
73                 first_entry++;
74         }
75         if (first_entry >= log_ringbuf_size) {
76                 first_entry = 0;
77         }
78 }
79
80 void log_ringbuffer(const char *format, ...)
81 {
82         va_list ap;
83
84         va_start(ap, format);
85         log_ringbuffer_v(format, ap);
86         va_end(ap);
87 }
88
89
90
91 static void ctdb_collect_log(struct ctdb_context *ctdb, struct ctdb_get_log_addr *log_addr)
92 {
93         TDB_DATA data;
94         FILE *f;
95         long fsize;
96         int tmp_entry;
97         int count = 0;
98         DEBUG(DEBUG_ERR,("Marshalling log entries  first:%d last:%d\n", first_entry, last_entry));
99
100         /* dump to a file, then send the file as a blob */
101         f = tmpfile();
102         if (f == NULL) {
103                 DEBUG(DEBUG_ERR,(__location__ " Unable to open tmpfile - %s\n", strerror(errno)));
104                 return;
105         }
106
107         tmp_entry = first_entry;
108         while (tmp_entry != last_entry) {
109                 struct tm *tm;
110                 char tbuf[100];
111
112                 if (log_entries == NULL) {
113                         break;
114                 }
115
116                 if (log_entries[tmp_entry].level > log_addr->level) {
117                         tmp_entry++;
118                         if (tmp_entry >= log_ringbuf_size) {
119                                 tmp_entry = 0;
120                         }
121                         continue;
122                 }
123
124                 tm = localtime(&log_entries[tmp_entry].t.tv_sec);
125                 strftime(tbuf, sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
126
127                 if (log_entries[tmp_entry].message) {
128                         count += fprintf(f, "%s:%s %s", tbuf, get_debug_by_level(log_entries[tmp_entry].level), log_entries[tmp_entry].message);
129                 }
130
131                 tmp_entry++;
132                 if (tmp_entry >= log_ringbuf_size) {
133                         tmp_entry = 0;
134                 }
135         }
136
137         fsize = ftell(f);
138         rewind(f);
139         data.dptr = talloc_size(NULL, fsize);
140         CTDB_NO_MEMORY_VOID(ctdb, data.dptr);
141         data.dsize = fread(data.dptr, 1, fsize, f);
142         fclose(f);
143
144         DEBUG(DEBUG_ERR,("Marshalling log entries into a blob of %d bytes\n", (int)data.dsize));
145
146         DEBUG(DEBUG_ERR,("Send log to %d:%d\n", (int)log_addr->pnn, (int)log_addr->srvid));
147         ctdb_client_send_message(ctdb, log_addr->pnn, log_addr->srvid, data);
148
149         talloc_free(data.dptr);
150 }
151
152 int32_t ctdb_control_get_log(struct ctdb_context *ctdb, TDB_DATA addr)
153 {
154         struct ctdb_get_log_addr *log_addr = (struct ctdb_get_log_addr *)addr.dptr;
155         pid_t child;
156
157         /* spawn a child process to marshall the huge log blob and send it back
158            to the ctdb tool using a MESSAGE
159         */
160         child = ctdb_fork(ctdb);
161         if (child == (pid_t)-1) {
162                 DEBUG(DEBUG_ERR,("Failed to fork a log collector child\n"));
163                 return -1;
164         }
165
166         if (child == 0) {
167                 if (switch_from_server_to_client(ctdb, "log-collector") != 0) {
168                         DEBUG(DEBUG_CRIT, (__location__ "ERROR: failed to switch log collector child into client mode.\n"));
169                         _exit(1);
170                 }
171                 ctdb_collect_log(ctdb, log_addr);
172                 _exit(0);
173         }
174
175         return 0;
176 }
177
178
179 int32_t ctdb_control_clear_log(struct ctdb_context *ctdb)
180 {
181         first_entry = 0;
182         last_entry  = 0;
183
184         return 0;
185 }
186