1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6 Copyright (C) 2017-2019 Olof Hagsand
7 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8
9 This file is part of CLIXON.
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22
23 Alternatively, the contents of this file may be used under the terms of
24 the GNU General Public License Version 3 or later (the "GPL"),
25 in which case the provisions of the GPL are applicable instead
26 of those above. If you wish to allow use of your version of this file only
27 under the terms of the GPL, and not to allow others to
28 use your version of this file under the terms of Apache License version 2,
29 indicate your decision by deleting the provisions above and replace them with
30 the notice and other provisions required by the GPL. If you do not delete
31 the provisions above, a recipient may use your version of this file under
32 the terms of any one of the Apache License version 2 or the GPL.
33
34 ***** END LICENSE BLOCK *****
35
36 *
37 * Errors may be syslogged using LOG_ERR, and printed to stderr, as controlled
38 * by clicon_log_init
39 * global error variables are set:
40 * clicon_errno, clicon_suberrno, clicon_err_reason.
41 */
42
43 #ifdef HAVE_CONFIG_H
44 #include "clixon_config.h" /* generated by config & autoconf */
45 #endif
46
47 #include <stdio.h>
48 #include <stdarg.h>
49 #include <stdint.h>
50 #include <stdlib.h>
51 #include <fcntl.h>
52 #include <errno.h>
53 #include <unistd.h>
54 #include <syslog.h>
55 #include <string.h>
56 #include <time.h>
57 #include <sys/time.h>
58 #include <sys/types.h>
59
60 #include "clixon_log.h"
61 #include "clixon_queue.h"
62 #include "clixon_err.h"
63
64 /*
65 * Types
66 */
67 struct errvec{
68 char *ev_str;
69 int ev_err;
70 };
71
72 struct err_state{
73 int es_errno;
74 int es_suberrno;
75 char es_reason[ERR_STRLEN];
76 };
77
78 /*
79 * Variables
80 */
81 int clicon_errno = 0; /* See enum clicon_err XXX: hide this and change to err_category */
82 int clicon_suberrno = 0; /* Corresponds to errno.h XXX: change to errno */
83 char clicon_err_reason[ERR_STRLEN] = {0, };
84
85 /*
86 * Error descriptions. Must stop with NULL element.
87 */
88 static struct errvec EV[] = {
89 {"Database error", OE_DB},
90 {"Daemon error", OE_DAEMON},
91 {"Event error", OE_EVENTS},
92 {"Config error", OE_CFG},
93 {"Netconf error", OE_NETCONF},
94 {"Protocol error", OE_PROTO},
95 {"Regexp error", OE_REGEX},
96 {"UNIX error", OE_UNIX},
97 {"Syslog error", OE_SYSLOG},
98 {"Routing demon error", OE_ROUTING},
99 {"XML error", OE_XML},
100 {"Plugins", OE_PLUGIN},
101 {"Yang error", OE_YANG},
102 {"FATAL", OE_FATAL},
103 {"Undefined", OE_UNDEF},
104 {NULL, -1}
105 };
106
107 static char *
clicon_strerror1(int err,struct errvec vec[])108 clicon_strerror1(int err,
109 struct errvec vec[])
110 {
111 struct errvec *ev;
112
113 for (ev=vec; ev->ev_err != -1; ev++)
114 if (ev->ev_err == err)
115 break;
116 return ev?(ev->ev_str?ev->ev_str:"unknown"):"CLICON unknown error";
117 }
118
119 /*! Clear error state and continue.
120 *
121 * Clear error state and get on with it, typically non-fatal error and you wish to continue.
122 */
123 int
clicon_err_reset(void)124 clicon_err_reset(void)
125 {
126 clicon_errno = 0;
127 clicon_suberrno = 0;
128 memset(clicon_err_reason, 0, ERR_STRLEN);
129 return 0;
130 }
131
132 /*! Report an error.
133 *
134 * Library routines should call this function when an error occurs.
135 * The function does he following:
136 * - Logs to syslog with LOG_ERR
137 * - Set global error variable name clicon_errno
138 * - Set global reason string clicon_err_reason
139 * @note: err direction (syslog and/or stderr) controlled by clicon_log_init()
140 *
141 * @param[in] fn Inline function name (when called from clicon_err() macro)
142 * @param[in] line Inline file line number (when called from clicon_err() macro)
143 * @param[in] category Clixon error category, See enum clicon_err
144 * @param[in] suberr Error number, typically errno
145 * @param[in] reason Error string, format with argv
146 * @see clicon_err_reset Reset the global error variables.
147 */
clicon_err_fn(const char * fn,const int line,int category,int suberr,const char * format,...)148 int clicon_err_fn(const char *fn,
149 const int line,
150 int category,
151 int suberr,
152 const char *format, ...)
153 {
154 va_list args;
155 int len;
156 char *msg = NULL;
157 int retval = -1;
158
159 /* Set the global variables */
160 clicon_errno = category;
161 clicon_suberrno = suberr;
162
163 /* first round: compute length of error message */
164 va_start(args, format);
165 len = vsnprintf(NULL, 0, format, args);
166 va_end(args);
167
168 /* allocate a message string exactly fitting the message length */
169 if ((msg = malloc(len+1)) == NULL){
170 fprintf(stderr, "malloc: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
171 goto done;
172 }
173
174 /* second round: compute write message from format and args */
175 va_start(args, format);
176 if (vsnprintf(msg, len+1, format, args) < 0){
177 va_end(args);
178 fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
179 goto done;
180 }
181 va_end(args);
182 strncpy(clicon_err_reason, msg, ERR_STRLEN-1);
183
184 /* Actually log it */
185 if (errno){
186 /* Here we could take care of specific errno, like application-defined errors */
187 clicon_log(LOG_ERR, "%s: %d: %s: %s: %s",
188 fn,
189 line,
190 clicon_strerror(category),
191 msg,
192 errno==XMLPARSE_ERRNO?"XML parse error":strerror(errno));
193 }
194 else
195 clicon_log(LOG_ERR, "%s: %d: %s: %s",
196 fn,
197 line,
198 clicon_strerror(category),
199 msg);
200
201 retval = 0;
202 done:
203 if (msg)
204 free(msg);
205 return retval;
206 }
207
208 /*! Translate from numeric error to string representation
209 */
210 char *
clicon_strerror(int err)211 clicon_strerror(int err)
212 {
213 return clicon_strerror1(err, EV);
214 }
215
216 /*! Push an error state, if recursive error handling
217 */
218 void*
clicon_err_save(void)219 clicon_err_save(void)
220 {
221 struct err_state *es;
222
223 if ((es = malloc(sizeof(*es))) == NULL)
224 return NULL;
225 es->es_errno = clicon_errno;
226 es->es_suberrno = clicon_suberrno;
227 strncpy(es->es_reason, clicon_err_reason, ERR_STRLEN-1);
228 return (void*)es;
229 }
230
231 /*! Pop an error state, if recursive error handling
232 */
233 int
clicon_err_restore(void * handle)234 clicon_err_restore(void* handle)
235 {
236 struct err_state *es;
237
238 if ((es = (struct err_state *)handle) != NULL){
239 clicon_errno = es->es_errno;
240 clicon_suberrno = es->es_suberrno;
241 strncpy(clicon_err_reason, es->es_reason, ERR_STRLEN-1);
242 free(es);
243 }
244 return 0;
245 }
246