add a new command "ctdb scriptstatus"
[sahlberg/ctdb.git] / server / ctdb_logging.c
1 /* 
2    ctdb logging code
3
4    Copyright (C) Andrew Tridgell  2008
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 "../include/ctdb_private.h"
23 #include "system/syslog.h"
24 #include "system/time.h"
25 #include "system/filesys.h"
26
27 struct ctdb_log_state {
28         char *logfile;
29         int fd, pfd;
30         char buf[1024];
31         uint16_t buf_used;
32         bool use_syslog;
33 };
34
35 /* we need this global to eep the DEBUG() syntax */
36 static struct ctdb_log_state *log_state;
37
38 /*
39   syslog logging function
40  */
41 static void ctdb_syslog_log(const char *format, va_list ap)
42 {
43         int level = LOG_DEBUG;
44         switch (this_log_level) {
45         case DEBUG_EMERG: 
46                 level = LOG_EMERG; 
47                 break;
48         case DEBUG_ALERT: 
49                 level = LOG_ALERT; 
50                 break;
51         case DEBUG_CRIT: 
52                 level = LOG_CRIT; 
53                 break;
54         case DEBUG_ERR: 
55                 level = LOG_ERR; 
56                 break;
57         case DEBUG_WARNING: 
58                 level = LOG_WARNING; 
59                 break;
60         case DEBUG_NOTICE: 
61                 level = LOG_NOTICE;
62                 break;
63         case DEBUG_INFO: 
64                 level = LOG_INFO;
65                 break;
66         default:
67                 level = LOG_DEBUG;
68                 break;          
69         }
70         vsyslog(level, format, ap);
71 }
72
73
74 /*
75   log file logging function
76  */
77 static void ctdb_logfile_log(const char *format, va_list ap)
78 {
79         struct timeval t;
80         char *s = NULL;
81         struct tm *tm;
82         char tbuf[100];
83         char *s2 = NULL;
84
85         vasprintf(&s, format, ap);
86
87         t = timeval_current();
88         tm = localtime(&t.tv_sec);
89
90         strftime(tbuf,sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm);
91
92         asprintf(&s2, "%s.%06u [%5u]: %s", 
93                  tbuf, (unsigned)t.tv_usec, (unsigned)getpid(), s);
94         free(s);
95         if (s2) {
96                 write(log_state->fd, s2, strlen(s2));
97                 free(s2);       
98         }
99 }
100
101 /*
102   choose the logfile location
103 */
104 int ctdb_set_logfile(struct ctdb_context *ctdb, const char *logfile, bool use_syslog)
105 {
106         ctdb->log = talloc_zero(ctdb, struct ctdb_log_state);
107
108         log_state = ctdb->log;
109
110         if (use_syslog) {
111                 do_debug_v = ctdb_syslog_log;
112                 ctdb->log->use_syslog = true;
113         } else if (logfile == NULL || strcmp(logfile, "-") == 0) {
114                 do_debug_v = ctdb_logfile_log;
115                 ctdb->log->fd = 1;
116                 /* also catch stderr of subcommands to stdout */
117                 dup2(1, 2);
118         } else {
119                 do_debug_v = ctdb_logfile_log;
120                 ctdb->log->logfile = talloc_strdup(ctdb, logfile);
121
122                 ctdb->log->fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0666);
123                 if (ctdb->log->fd == -1) {
124                         printf("Failed to open logfile %s\n", ctdb->logfile);
125                         abort();
126                 }
127         }
128
129         return 0;
130 }
131
132
133
134 /*
135   called when log data comes in from a child process
136  */
137 static void ctdb_log_handler(struct event_context *ev, struct fd_event *fde, 
138                              uint16_t flags, void *private)
139 {
140         struct ctdb_context *ctdb = talloc_get_type(private, struct ctdb_context);
141         char *p;
142         int n;
143
144         if (!(flags & EVENT_FD_READ)) {
145                 return;
146         }
147         
148         n = read(ctdb->log->pfd, &ctdb->log->buf[ctdb->log->buf_used],
149                  sizeof(ctdb->log->buf) - ctdb->log->buf_used);
150         if (n > 0) {
151                 ctdb->log->buf_used += n;
152         }
153
154         this_log_level = script_log_level;
155
156         while (ctdb->log->buf_used > 0 &&
157                (p = memchr(ctdb->log->buf, '\n', ctdb->log->buf_used)) != NULL) {
158                 int n1 = (p - ctdb->log->buf)+1;
159                 int n2 = n1 - 1;
160                 /* swallow \r from child processes */
161                 if (n2 > 0 && ctdb->log->buf[n2-1] == '\r') {
162                         n2--;
163                 }
164                 if (script_log_level <= LogLevel) {
165                         do_debug("%*.*s\n", n2, n2, ctdb->log->buf);
166                         /* log it in the eventsystem as well */
167                         ctdb_log_event_script_output(ctdb, ctdb->log->buf, n2);
168                 }
169                 memmove(ctdb->log->buf, p+1, sizeof(ctdb->log->buf) - n1);
170                 ctdb->log->buf_used -= n1;
171         }
172
173         /* the buffer could have completely filled - unfortunately we have
174            no choice but to dump it out straight away */
175         if (ctdb->log->buf_used == sizeof(ctdb->log->buf)) {
176                 if (script_log_level <= LogLevel) {
177                         do_debug("%*.*s\n", 
178                                 (int)ctdb->log->buf_used, (int)ctdb->log->buf_used, ctdb->log->buf);
179                         /* log it in the eventsystem as well */
180                         ctdb_log_event_script_output(ctdb, ctdb->log->buf, ctdb->log->buf_used);
181                 }
182                 ctdb->log->buf_used = 0;
183         }
184 }
185
186
187
188 /*
189   setup for logging of child process stdout
190 */
191 int ctdb_set_child_logging(struct ctdb_context *ctdb)
192 {
193         int p[2];
194
195         if (ctdb->log->fd == 1) {
196                 /* not needed for stdout logging */
197                 return 0;
198         }
199
200         /* setup a pipe to catch IO from subprocesses */
201         if (pipe(p) != 0) {
202                 DEBUG(DEBUG_ERR,(__location__ " Failed to setup for child logging pipe\n"));
203                 return -1;
204         }
205
206         event_add_fd(ctdb->ev, ctdb, p[0], EVENT_FD_READ, 
207                      ctdb_log_handler, ctdb);
208         set_close_on_exec(p[0]);
209         ctdb->log->pfd = p[0];
210
211         close(1);
212         close(2);
213         if (p[1] != 1) {
214                 dup2(p[1], 1);
215                 close(p[1]);
216         }
217         /* also catch stderr of subcommands to the log */
218         dup2(1, 2);
219
220         return 0;
221 }