1 /*
2  * Copyright (C) 2002-2003 Fhg Fokus
3  *
4  * This file is part of SEMS, a free SIP media server.
5  *
6  * SEMS 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 2 of the License, or
9  * (at your option) any later version. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include "sems.h"
32 
33 #ifndef DISABLE_SYSLOG_LOG
34 # include <syslog.h>
35 #endif
36 
37 #include <vector>
38 #include <string>
39 
40 #include "AmApi.h"	/* AmLoggingFacility */
41 #include "AmThread.h"   /* AmMutex */
42 #include "log.h"
43 
44 
45 int log_level  = AmConfig::LogLevel;	/**< log level */
46 int log_stderr = AmConfig::LogStderr;	/**< non-zero if logging to stderr */
47 
48 /** Map log levels to text labels */
49 const char* log_level2str[] = { "ERROR", "WARNING", "INFO", "DEBUG" };
50 
51 /** Registered logging hooks */
52 static vector<AmLoggingFacility*> log_hooks;
53 static AmMutex log_hooks_mutex;
54 
55 #ifndef DISABLE_SYSLOG_LOG
56 
57 /**
58  * Syslog Logging Facility (built-in plug-in)
59  */
60 class SyslogLogFac : public AmLoggingFacility {
61   int facility;		/**< syslog facility */
62 
init()63   void init() {
64     openlog(SEMS_APP_NAME, LOG_PID | LOG_CONS, facility);
65     setlogmask(-1);
66   }
67 
68  public:
SyslogLogFac()69   SyslogLogFac() : AmLoggingFacility("syslog"), facility(LOG_DAEMON) {
70     init();
71   }
72 
~SyslogLogFac()73   ~SyslogLogFac() {
74     closelog();
75   }
76 
onLoad()77   int onLoad() {
78     /* unused (because it is a built-in plug-in */
79     return 0;
80   }
81 
82   bool setFacility(const char* str);
83   void log(int, pid_t, pthread_t, const char*, const char*, int, char*);
84 };
85 
86 static SyslogLogFac syslog_log;
87 
88 /** Set syslog facility */
setFacility(const char * str)89 bool SyslogLogFac::setFacility(const char* str) {
90   static int local_fac[] = {
91     LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3,
92     LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7,
93   };
94 
95   int new_facility = -1;
96 
97   if (!strcmp(str, "DAEMON")){
98     new_facility = LOG_DAEMON;
99   }
100   else if (!strcmp(str, "USER")) {
101     new_facility = LOG_USER;
102   }
103   else if (strlen(str) == 6 && !strncmp(str, "LOCAL", 5) &&
104            isdigit(str[5]) && str[5] - '0' < 8) {
105     new_facility = local_fac[str[5] - '0'];
106   }
107   else {
108     ERROR("unknown syslog facility '%s'\n", str);
109     return false;
110   }
111 
112   if (new_facility != facility) {
113     facility = new_facility;
114     closelog();
115     init();
116   }
117 
118   return true;
119 }
120 
log(int level,pid_t pid,pthread_t tid,const char * func,const char * file,int line,char * msg)121 void SyslogLogFac::log(int level, pid_t pid, pthread_t tid, const char* func, const char* file, int line, char* msg)
122 {
123   static const int log2syslog_level[] = { LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG };
124 #ifdef _DEBUG
125 
126   // replace \r\n through a dot
127   for(char* c=msg; (*c); c++)
128     if(*c == '\r' || *c == '\n')
129       *c = '.';
130 
131 # ifndef NO_THREADID_LOG
132 #  ifdef LOG_LOC_DATA_ATEND
133   syslog(log2syslog_level[level], "%s: %s [#%lx] [%s %s:%d]",
134 	 log_level2str[level], msg, (unsigned long)tid, func, file, line);
135 #  else
136   syslog(log2syslog_level[level], "[#%lx] [%s, %s:%d] %s: %s",
137 	 (unsigned long)tid, func, file, line, log_level2str[level], msg);
138 #  endif
139 # else /* NO_THREADID_LOG */
140 #  ifdef LOG_LOC_DATA_ATEND
141   syslog(log2syslog_level[level], "%s: %s [%s] [%s:%d]",
142       log_level2str[level], msg, func, file, line);
143 #  else
144   syslog(log2syslog_level[level], "[%s, %s:%d] %s: %s",
145 	 func, file, line, log_level2str[level], msg);
146 #  endif
147 # endif /* NO_THREADID_LOG */
148 
149 #else /* !_DEBUG */
150 #  ifdef LOG_LOC_DATA_ATEND
151   syslog(log2syslog_level[level], "%s: %s [%s:%d]",
152       log_level2str[level], msg, file, line);
153 #  else
154   syslog(log2syslog_level[level], "[%s:%d] %s: %s",
155 	 file, line, log_level2str[level], msg);
156 #  endif
157 
158 #endif /* !_DEBUG */
159 }
160 
set_syslog_facility(const char * str)161 int set_syslog_facility(const char* str)
162 {
163   return (syslog_log.setFacility(str) == true);
164 }
165 
166 #endif /* !DISABLE_SYSLOG_LOG */
167 
168 
169 /**
170  * Initialize logging
171  */
init_logging()172 void init_logging()
173 {
174   log_hooks.clear();
175 
176 #ifndef DISABLE_SYSLOG_LOG
177   register_log_hook(&syslog_log);
178 #endif
179 
180   INFO("Logging initialized\n");
181 }
182 
183 /**
184  * Run log hooks
185  */
run_log_hooks(int level,pid_t pid,pthread_t tid,const char * func,const char * file,int line,char * msg)186 void run_log_hooks(int level, pid_t pid, pthread_t tid, const char* func, const char* file, int line, char* msg)
187 {
188   log_hooks_mutex.lock();
189 
190   if (!log_hooks.empty()) {
191     for (vector<AmLoggingFacility*>::iterator it = log_hooks.begin();
192          it != log_hooks.end(); ++it) {
193       (*it)->log(level, pid, tid, func, file, line, msg);
194     }
195   }
196 
197   log_hooks_mutex.unlock();
198 }
199 
200 /**
201  * Register the log hook
202  */
register_log_hook(AmLoggingFacility * fac)203 void register_log_hook(AmLoggingFacility* fac)
204 {
205   AmLock lock(log_hooks_mutex);
206   log_hooks.push_back(fac);
207 }
208 
209 /**
210  * Print stack-trace through logging function
211  */
log_stacktrace(int ll)212 void log_stacktrace(int ll)
213 {
214    void* callstack[128];
215    int i, frames = backtrace(callstack, 128);
216    char** strs = backtrace_symbols(callstack, frames);
217    for (i = 0; i < frames; ++i) {
218      _LOG(ll,"stack-trace(%i/[%p]): %s", i, callstack[i], strs[i]);
219    }
220    free(strs);
221 }
222 
223 /**
224  * Print a demangled stack backtrace of the caller function
225  */
__lds(int ll,unsigned int max_frames)226 void __lds(int ll, unsigned int max_frames)
227 {
228   // storage array for stack trace address data
229   void* addrlist[max_frames+1];
230 
231   // retrieve current stack addresses
232   int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
233 
234   if (addrlen == 0) {
235     _LOG(ll,"<empty, possibly corrupt>");
236     return;
237   }
238 
239   // resolve addresses into strings containing "filename(function+address)",
240   // this array must be free()-ed
241   char** symbollist = backtrace_symbols(addrlist, addrlen);
242 
243   // allocate string which will be filled with the demangled function name
244   size_t funcnamesize = 256;
245   char* funcname = (char*)malloc(funcnamesize);
246 
247   // iterate over the returned symbol lines. skip the first, it is the
248   // address of this function.
249   for (int i = 1; i < addrlen; i++)
250   {
251     char *begin_name = 0, *begin_offset = 0, *end_offset = 0;
252 
253     // find parentheses and +address offset surrounding the mangled name:
254     // ./module(function+0x15c) [0x8048a6d]
255     for (char *p = symbollist[i]; *p; ++p)
256     {
257         if (*p == '(')
258     	begin_name = p;
259         else if (*p == '+')
260     	begin_offset = p;
261         else if (*p == ')' && begin_offset) {
262     	end_offset = p;
263     	break;
264         }
265     }
266 
267     if (begin_name && begin_offset && end_offset
268         && begin_name < begin_offset)
269     {
270         *begin_name++ = '\0';
271         *begin_offset++ = '\0';
272         *end_offset = '\0';
273 
274         // mangled name is now in [begin_name, begin_offset) and caller
275         // offset in [begin_offset, end_offset). now apply
276         // __cxa_demangle():
277 
278         int status;
279         char* ret = abi::__cxa_demangle(begin_name,
280     				    funcname, &funcnamesize, &status);
281         if (status == 0) {
282     	funcname = ret; // use possibly realloc()-ed string
283     	_LOG(ll,"%s : %s+%s",
284     		symbollist[i], funcname, begin_offset);
285         }
286         else {
287     	// demangling failed. Output function name as a C function with
288     	// no arguments.
289     	_LOG(ll,"%s : %s()+%s",
290     		symbollist[i], begin_name, begin_offset);
291         }
292     }
293     else
294     {
295         // couldn't parse the line? print the whole line.
296         _LOG(ll,"%s", symbollist[i]);
297     }
298   }
299 
300   free(funcname);
301   free(symbollist);
302 }
303