1 /*
2  * SRT - Secure, Reliable, Transport
3  * Copyright (c) 2018 Haivision Systems Inc.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  */
10 
11 #include <map>
12 #include <vector>
13 #include <string>
14 #include <iterator>
15 #include <algorithm>
16 #include <cctype>
17 #include "logsupport.hpp"
18 #include "../srtcore/srt.h"
19 #include "../srtcore/utilities.h"
20 
21 using namespace std;
22 
23 // This is based on codes taken from <sys/syslog.h>
24 // This is POSIX standard, so it's not going to change.
25 // Haivision standard only adds one more severity below
26 // DEBUG named DEBUG_TRACE to satisfy all possible needs.
27 
28 map<string, int> srt_level_names
29 {
30     { "alert", LOG_ALERT },
31     { "crit", LOG_CRIT },
32     { "debug", LOG_DEBUG },
33     { "emerg", LOG_EMERG },
34     { "err", LOG_ERR },
35     { "error", LOG_ERR },		/* DEPRECATED */
36     { "fatal", LOG_CRIT }, // XXX Added for SRT
37     { "info", LOG_INFO },
38     // WTF? Undefined symbol? { "none", INTERNAL_NOPRI },		/* INTERNAL */
39     { "notice", LOG_NOTICE },
40     { "note", LOG_NOTICE }, // XXX Added for SRT
41     { "panic", LOG_EMERG },		/* DEPRECATED */
42     { "warn", LOG_WARNING },		/* DEPRECATED */
43     { "warning", LOG_WARNING },
44     //{ "", -1 }
45 };
46 
47 
48 
SrtParseLogLevel(string level)49 srt_logging::LogLevel::type SrtParseLogLevel(string level)
50 {
51     using namespace srt_logging;
52 
53     if ( level.empty() )
54         return LogLevel::fatal;
55 
56     if ( isdigit(level[0]) )
57     {
58         long lev = strtol(level.c_str(), 0, 10);
59         if ( lev >= SRT_LOG_LEVEL_MIN && lev <= SRT_LOG_LEVEL_MAX )
60             return LogLevel::type(lev);
61 
62         cerr << "ERROR: Invalid loglevel number: " << level << " - fallback to FATAL\n";
63         return LogLevel::fatal;
64     }
65 
66     int (*ToLower)(int) = &std::tolower; // manual overload resolution
67     transform(level.begin(), level.end(), level.begin(), ToLower);
68 
69     auto i = srt_level_names.find(level);
70     if ( i == srt_level_names.end() )
71     {
72         cerr << "ERROR: Invalid loglevel spec: " << level << " - fallback to FATAL\n";
73         return LogLevel::fatal;
74     }
75 
76     return LogLevel::type(i->second);
77 }
78 
79 struct ToLowerFormat
80 {
operator ()ToLowerFormat81     char operator()(char in)
82     {
83         if (islower(in))
84             return in;
85         if (isupper(in))
86             return tolower(in);
87         if (in == '_')
88             return '-';
89 
90         throw std::invalid_argument("Wrong FA name - please check the definition in scripts/generate-logging-defs.tcl file");
91     }
92 };
93 
Install(string upname,int value)94 void LogFANames::Install(string upname, int value)
95 {
96     string id;
97     transform(upname.begin(), upname.end(), back_inserter(id), ToLowerFormat());
98     namemap[id] = value;
99 }
100 
101 // See logsupport_appdefs.cpp for log FA definitions
102 LogFANames srt_transmit_logfa_names;
103 
SrtLogFAList()104 const map<string, int> SrtLogFAList()
105 {
106     return srt_transmit_logfa_names.namemap;
107 }
108 
SrtParseLogFA(string fa,set<string> * punknown)109 set<srt_logging::LogFA> SrtParseLogFA(string fa, set<string>* punknown)
110 {
111     using namespace srt_logging;
112 
113     set<LogFA> fas;
114 
115     // The split algo won't work on empty string.
116     if ( fa == "" )
117         return fas;
118 
119     auto& names = srt_transmit_logfa_names.namemap;
120 
121     if ( fa == "all" )
122     {
123         for (auto entry: names)
124         {
125             // Skip "general", it's always on
126             if (entry.first == "general")
127                 continue;
128             fas.insert(entry.second);
129         }
130         return fas;
131     }
132 
133     int (*ToLower)(int) = &std::tolower;
134     transform(fa.begin(), fa.end(), fa.begin(), ToLower);
135 
136     vector<string> xfas;
137     size_t pos = 0, ppos = 0;
138     for (;;)
139     {
140         if ( fa[pos] != ',' )
141         {
142             ++pos;
143             if ( pos < fa.size() )
144                 continue;
145         }
146         size_t n = pos - ppos;
147         if ( n != 0 )
148             xfas.push_back(fa.substr(ppos, n));
149         ++pos;
150         if ( pos >= fa.size() )
151             break;
152         ppos = pos;
153     }
154 
155     for (size_t i = 0; i < xfas.size(); ++i)
156     {
157         fa = xfas[i];
158         int* pfa = map_getp(names, fa);
159         if (!pfa)
160         {
161             if (punknown)
162                 punknown->insert(fa); // If requested, add it back silently
163             else
164                 cerr << "ERROR: Invalid log functional area spec: '" << fa << "' - skipping\n";
165             continue;
166         }
167 
168         fas.insert(*pfa);
169     }
170 
171     return fas;
172 }
173 
ParseLogFASpec(const vector<string> & speclist,string & w_on,string & w_off)174 void ParseLogFASpec(const vector<string>& speclist, string& w_on, string& w_off)
175 {
176     std::ostringstream son, soff;
177 
178     for (auto& s: speclist)
179     {
180         string name;
181         bool on = true;
182         if (s[0] == '+')
183             name = s.substr(1);
184         else if (s[0] == '~')
185         {
186             name = s.substr(1);
187             on = false;
188         }
189         else
190             name = s;
191 
192         if (on)
193             son << "," << name;
194         else
195             soff << "," << name;
196     }
197 
198     const string& sons = son.str();
199     const string& soffs = soff.str();
200 
201     w_on = sons.empty() ? string() : sons.substr(1);
202     w_off = soffs.empty() ? string() : soffs.substr(1);
203 }
204 
205 
206