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