1 /* @(#)checkerr.c	1.27 19/02/28 Copyright 2003-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)checkerr.c	1.27 19/02/28 Copyright 2003-2019 J. Schilling";
6 #endif
7 /*
8  *	Generic error control for programs that do file i/o.
9  *	The error control is usually used by archiving programs.
10  *
11  *	The current version does not provide a stable interface.
12  *	It does not support multi-threaded programs and it may call
13  *	comerr() from the parser. If you use the code before there is
14  *	an official stable and "library-compliant" interface, be careful
15  *	and watch for changes.
16  *
17  *	Copyright (c) 2003-2019 J. Schilling
18  */
19 /*
20  * The contents of this file are subject to the terms of the
21  * Common Development and Distribution License, Version 1.0 only
22  * (the "License").  You may not use this file except in compliance
23  * with the License.
24  *
25  * See the file CDDL.Schily.txt in this distribution for details.
26  * A copy of the CDDL is also available via the Internet at
27  * http://www.opensource.org/licenses/cddl1.txt
28  *
29  * When distributing Covered Code, include this CDDL HEADER in each
30  * file and include the License file CDDL.Schily.txt from this distribution.
31  */
32 
33 #include <schily/stdio.h>
34 #include <schily/standard.h>
35 #include <schily/patmatch.h>
36 #include <schily/string.h>
37 #include <schily/utypes.h>
38 #include <schily/schily.h>
39 #include <schily/checkerr.h>
40 
41 typedef struct errconf {
42 	struct errconf	*ec_next;	/* Next in list			    */
43 	const	Uchar	*ec_pat;	/* File name pattern		    */
44 	int		*ec_aux;	/* Aux array from pattern compiler  */
45 	int		ec_alt;		/* Alt return from pattern compiler */
46 	int		ec_plen;	/* String length of pattern	    */
47 	UInt32_t	ec_flags[E_NFL]; /* Error condition flags	    */
48 } ec_t;
49 
50 LOCAL	int	*ec_state;		/* State array for pattern compiler */
51 LOCAL	ec_t	*ec_root;		/* Root node of error config list   */
52 LOCAL	ec_t	**ec_last = &ec_root;	/* Last pointer in root node list   */
53 LOCAL	int	maxplen;
54 LOCAL	BOOL	_errflag = TRUE;	/* Abort on all errors		    */
55 
56 EXPORT	int	errconfig	__PR((char *name));
57 LOCAL	char	*_endword	__PR((char *p));
58 LOCAL	void	parse_errctl	__PR((char *line));
59 LOCAL	UInt32_t errflags	__PR((char *eflag, BOOL doexit, UInt32_t fla[E_NFL]));
60 LOCAL	ec_t	*_errptr	__PR((int etype, const char *fname));
61 EXPORT	BOOL	errhidden	__PR((int etype, const char *fname));
62 EXPORT	BOOL	errwarnonly	__PR((int etype, const char *fname));
63 EXPORT	BOOL	errabort	__PR((int etype, const char *fname, BOOL doexit));
64 
65 /*
66  * Read and parse error configuration file
67  */
68 EXPORT int
errconfig(name)69 errconfig(name)
70 	char	*name;
71 {
72 	char	line[8192];
73 	FILE	*f;
74 	int	omaxplen = maxplen;
75 
76 	if ((f = fileopen(name, "r")) == NULL) {
77 		if (errflags(name, FALSE, NULL) != 0)
78 			parse_errctl(name);
79 		else
80 			comerr("Cannot open '%s'.\n", name);
81 	} else {
82 		while (fgetline(f, line, sizeof (line)) >= 0) {
83 			parse_errctl(line);
84 		}
85 		fclose(f);
86 	}
87 	if (maxplen > omaxplen) {
88 		ec_state = ___realloc(ec_state, (maxplen+1)*sizeof (int),
89 							"pattern state");
90 	}
91 	return (1);
92 }
93 
94 LOCAL char *
_endword(p)95 _endword(p)
96 	char	*p;
97 {
98 	/*
99 	 * Find end of word.
100 	 */
101 	for (;  *p != '\0' &&
102 		*p != '\t' &&
103 		*p != ' ';
104 				p++) {
105 		;
106 		/* LINTED */
107 	}
108 	return (p);
109 }
110 
111 LOCAL void
parse_errctl(line)112 parse_errctl(line)
113 	char	*line;
114 {
115 	int	plen;
116 	char	*pattern;
117 	ec_t	*ep;
118 
119 	/*
120 	 * Find end of word.
121 	 */
122 	pattern = _endword(line);
123 
124 	if (pattern == line || *pattern == '\0') {
125 		comerrno(EX_BAD,
126 		"Bad error configuration line '%s'.\n", line);
127 	}
128 	/*
129 	 * Find end of white space after word.
130 	 */
131 	for (pattern++; *pattern != '\0' &&
132 	    (*pattern == '\t' || *pattern == ' ');
133 				pattern++) {
134 		;
135 		/* LINTED */
136 	}
137 	ep = ___malloc(sizeof (ec_t), "errcheck node");
138 	errflags(line, TRUE, ep->ec_flags);
139 	ep->ec_plen = plen = strlen(pattern);
140 	if (ep->ec_plen > maxplen)
141 		maxplen = ep->ec_plen;
142 	ep->ec_pat = (const Uchar *)___savestr(pattern);
143 	ep->ec_aux = ___malloc(plen*sizeof (int), "compiled pattern");
144 	if ((ep->ec_alt = patcompile((const Uchar *)pattern,
145 						plen, ep->ec_aux)) == 0)
146 		comerrno(EX_BAD, "Bad errctl pattern: '%s'.\n", pattern);
147 
148 	ep->ec_next = NULL;
149 	*ec_last = ep;
150 	ec_last = &ep->ec_next;
151 }
152 
153 LOCAL struct eflags {
154 	char	*fname;
155 	UInt32_t fval;
156 } eflags[] = {
157 	{ "STAT",		E_STAT },
158 	{ "GETACL",		E_GETACL },
159 	{ "OPEN",		E_OPEN },
160 	{ "READ",		E_READ },
161 	{ "WRITE",		E_WRITE },
162 	{ "GROW",		E_GROW },
163 	{ "SHRINK",		E_SHRINK },
164 	{ "MISSLINK",		E_MISSLINK },
165 	{ "NAMETOOLONG",	E_NAMETOOLONG },
166 	{ "FILETOOBIG",		E_FILETOOBIG },
167 	{ "SPECIALFILE",	E_SPECIALFILE },
168 	{ "READLINK",		E_READLINK },
169 	{ "GETXATTR",		E_GETXATTR },
170 	{ "CHDIR",		E_CHDIR },
171 	{ "ICONV",		E_ICONV },
172 	{ "ID",			E_ID },
173 	{ "TIME",		E_TIME },
174 
175 	{ "SETTIME",		E_SETTIME },
176 	{ "SETMODE",		E_SETMODE },
177 	{ "SECURITY",		E_SECURITY },
178 	{ "LSECURITY",		E_LSECURITY },
179 	{ "SAMEFILE",		E_SAMEFILE },
180 	{ "BADACL",		E_BADACL },
181 	{ "SETACL",		E_SETACL },
182 	{ "SETXATTR",		E_SETXATTR },
183 
184 	{ "ALL",		E_ALL },
185 
186 	{ "DIFF",		E_DIFF },
187 	{ "WARN",		E_WARN },
188 	{ "ABORT",		E_ABORT },
189 
190 	{ NULL,			0 }
191 };
192 
193 /*
194  * Convert error condition string into flag word
195  */
196 LOCAL UInt32_t
errflags(eflag,doexit,fla)197 errflags(eflag, doexit, fla)
198 	char	*eflag;
199 	BOOL	doexit;
200 	UInt32_t fla[E_NFL];
201 {
202 	register char		*p = eflag;
203 		char		*ef = _endword(eflag);
204 	register struct eflags	*ep;
205 	register int		slen;
206 	register UInt32_t	nflags = 0;
207 
208 	do {
209 		for (ep = eflags; ep->fname; ep++) {
210 			slen = strlen(ep->fname);
211 			if ((strncmp(ep->fname, p, slen) == 0) &&
212 			    (p[slen] == '|' || p[slen] == ' ' ||
213 			    p[slen] == '\0')) {
214 				UInt32_t nfl = ep->fval;
215 
216 				nflags |= nfl;
217 				if (fla) {
218 					int	idx = (nfl & E_EMASK) >> E_SHIFT;
219 
220 					fla[idx] |= nfl;
221 				}
222 				break;
223 			}
224 		}
225 		if (ep->fname == NULL) {
226 			if (doexit)
227 				comerrno(EX_BAD, "Bad flag '%s'\n", p);
228 			return (0);
229 		}
230 		p = strchr(p, '|');
231 	} while (p < ef && p && *p++ == '|');
232 
233 	if ((nflags & ~(UInt32_t)(E_ABORT|E_WARN)) == 0) {
234 		if (doexit)
235 			comerrno(EX_BAD, "Bad error condition '%s'.\n", eflag);
236 		return (0);
237 	}
238 	return (nflags);
239 }
240 
241 LOCAL ec_t *
_errptr(etype,fname)242 _errptr(etype, fname)
243 		int	etype;
244 	const	char	*fname;
245 {
246 	ec_t		*ep = ec_root;
247 	char		*ret;
248 	const Uchar	*name = (const Uchar *)fname;
249 	int		nlen;
250 	int		idx = (etype & E_EMASK) >> E_SHIFT;
251 
252 	if (fname == NULL) {
253 		errmsgno(EX_BAD,
254 			"Implementation botch for errhidden(0x%X, NULL)\n",
255 			etype);
256 		errmsgno(EX_BAD, "Please report this bug!\n");
257 		errmsgno(EX_BAD, "Error cannot be ignored.\n");
258 		return ((ec_t *)NULL);
259 	}
260 	nlen  = strlen(fname);
261 	etype &= ~E_EMASK;
262 	while (ep) {
263 		if ((ep->ec_flags[idx] & etype) != 0) {
264 			ret = (char *)patmatch(ep->ec_pat, ep->ec_aux,
265 					name, 0,
266 					nlen, ep->ec_alt, ec_state);
267 			if (ret != NULL && *ret == '\0')
268 				return (ep);
269 		}
270 		ep = ep->ec_next;
271 	}
272 	return ((ec_t *)NULL);
273 }
274 
275 /*
276  * Check whether error condition should be ignored for file name.
277  */
278 EXPORT BOOL
errhidden(etype,fname)279 errhidden(etype, fname)
280 		int	etype;
281 	const	char	*fname;
282 {
283 	ec_t		*ep;
284 
285 	if ((ep = _errptr(etype, fname)) != NULL) {
286 		if ((ep->ec_flags[0] & (E_ABORT|E_WARN)) != 0)
287 			return (FALSE);
288 		return (TRUE);
289 	}
290 	return (FALSE);
291 }
292 
293 /*
294  * Check whether error condition should not affect exit code for file name.
295  */
296 EXPORT BOOL
errwarnonly(etype,fname)297 errwarnonly(etype, fname)
298 		int	etype;
299 	const	char	*fname;
300 {
301 	ec_t		*ep;
302 
303 	if ((ep = _errptr(etype, fname)) != NULL) {
304 		if ((ep->ec_flags[0] & E_WARN) != 0)
305 			return (TRUE);
306 		return (FALSE);
307 	}
308 	return (FALSE);
309 }
310 
311 /*
312  * Check whether error condition should be fatal for file name.
313  */
314 EXPORT BOOL
errabort(etype,fname,doexit)315 errabort(etype, fname, doexit)
316 		int	etype;
317 	const	char	*fname;
318 		BOOL	doexit;
319 {
320 	ec_t	*ep;
321 
322 	if ((ep = _errptr(etype, fname)) == NULL) {
323 		if (!_errflag)
324 			return (FALSE);
325 	} else if ((ep->ec_flags[0] & E_ABORT) == 0)
326 		return (FALSE);
327 
328 	if (doexit) {
329 		errmsgno(EX_BAD, "Error is considered fatal, aborting.\n");
330 #ifdef	CALL_OTHER_FUNCTION
331 		if (other_func != NULL)
332 			(*other_func)();
333 		else
334 #endif
335 			comexit(-3);
336 	}
337 	return (TRUE);
338 }
339