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