/src/e2fsprogs/lib/et/error_message.c
Line | Count | Source |
1 | | /* |
2 | | * $Header$ |
3 | | * $Source$ |
4 | | * $Locker$ |
5 | | * |
6 | | * Copyright 1987 by the Student Information Processing Board |
7 | | * of the Massachusetts Institute of Technology |
8 | | * |
9 | | * Permission to use, copy, modify, and distribute this software and |
10 | | * its documentation for any purpose is hereby granted, provided that |
11 | | * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in |
12 | | * advertising or publicity pertaining to distribution of the software |
13 | | * without specific, written prior permission. M.I.T. and the |
14 | | * M.I.T. S.I.P.B. make no representations about the suitability of |
15 | | * this software for any purpose. It is provided "as is" without |
16 | | * express or implied warranty. |
17 | | */ |
18 | | |
19 | | #include "config.h" |
20 | | #include <stdio.h> |
21 | | #include <stdlib.h> |
22 | | #include <string.h> |
23 | | #include <errno.h> |
24 | | #ifdef HAVE_SYS_PRCTL_H |
25 | | #include <sys/prctl.h> |
26 | | #else |
27 | | #define PR_GET_DUMPABLE 3 |
28 | | #endif |
29 | | #if (!defined(HAVE_PRCTL) && defined(linux)) |
30 | | #include <sys/syscall.h> |
31 | | #endif |
32 | | #ifdef HAVE_SEMAPHORE_H |
33 | | #include <semaphore.h> |
34 | | #endif |
35 | | #if HAVE_UNISTD_H |
36 | | #include <unistd.h> |
37 | | #endif |
38 | | #if HAVE_FCNTL |
39 | | #include <fcntl.h> |
40 | | #endif |
41 | | #if HAVE_SYS_TYPES_H |
42 | | #include <sys/types.h> |
43 | | #endif |
44 | | #include "com_err.h" |
45 | | #include "error_table.h" |
46 | | #include "internal.h" |
47 | | |
48 | | #ifdef TLS |
49 | | #define THREAD_LOCAL static TLS |
50 | | #else |
51 | | #define THREAD_LOCAL static |
52 | | #endif |
53 | | |
54 | | THREAD_LOCAL char buffer[25]; |
55 | | |
56 | | struct et_list * _et_list = (struct et_list *) NULL; |
57 | | struct et_list * _et_dynamic_list = (struct et_list *) NULL; |
58 | | |
59 | | #ifdef __GNUC__ |
60 | | #define COMERR_ATTR(x) __attribute__(x) |
61 | | #else |
62 | | #define COMERR_ATTR(x) |
63 | | #endif |
64 | | |
65 | | #ifdef HAVE_SEM_INIT |
66 | | static sem_t _et_lock; |
67 | | static int _et_lock_initialized; |
68 | | |
69 | | static void COMERR_ATTR((constructor)) setup_et_lock(void) |
70 | | { |
71 | | sem_init(&_et_lock, 0, 1); |
72 | | _et_lock_initialized = 1; |
73 | | } |
74 | | |
75 | | static void COMERR_ATTR((destructor)) fini_et_lock(void) |
76 | | { |
77 | | sem_destroy(&_et_lock); |
78 | | _et_lock_initialized = 0; |
79 | | } |
80 | | #endif |
81 | | |
82 | | |
83 | | int et_list_lock(void) |
84 | 255k | { |
85 | | #ifdef HAVE_SEM_INIT |
86 | | if (!_et_lock_initialized) |
87 | | setup_et_lock(); |
88 | | return sem_wait(&_et_lock); |
89 | | #else |
90 | 255k | return 0; |
91 | 255k | #endif |
92 | 255k | } |
93 | | |
94 | | int et_list_unlock(void) |
95 | 255k | { |
96 | | #ifdef HAVE_SEM_INIT |
97 | | if (_et_lock_initialized) |
98 | | return sem_post(&_et_lock); |
99 | | #endif |
100 | 255k | return 0; |
101 | 255k | } |
102 | | |
103 | | typedef char *(*gettextf) (const char *); |
104 | | |
105 | | static gettextf com_err_gettext = NULL; |
106 | | |
107 | | gettextf set_com_err_gettext(gettextf new_proc) |
108 | 0 | { |
109 | 0 | gettextf x = com_err_gettext; |
110 | |
|
111 | 0 | com_err_gettext = new_proc; |
112 | |
|
113 | 0 | return x; |
114 | 0 | } |
115 | | |
116 | | #ifdef __GNU__ |
117 | | #define SYS_ERR_BASE 0x40000000 |
118 | | #else |
119 | 255k | #define SYS_ERR_BASE 0 |
120 | | #endif |
121 | | |
122 | | const char * error_message (errcode_t code) |
123 | 255k | { |
124 | 255k | int offset; |
125 | 255k | struct et_list *et; |
126 | 255k | errcode_t table_num; |
127 | 255k | int started = 0; |
128 | 255k | char *cp; |
129 | | |
130 | 255k | offset = (int) (code & ((1<<ERRCODE_RANGE)-1)); |
131 | 255k | table_num = code - offset; |
132 | 255k | if (table_num == SYS_ERR_BASE) { |
133 | | #ifdef HAS_SYS_ERRLIST |
134 | | if (code < sys_nerr) |
135 | | return(sys_errlist[code]); |
136 | | else |
137 | | goto oops; |
138 | | #else |
139 | 0 | cp = strerror(code); |
140 | 0 | if (cp) |
141 | 0 | return(cp); |
142 | 0 | else |
143 | 0 | goto oops; |
144 | 0 | #endif |
145 | 0 | } |
146 | 255k | et_list_lock(); |
147 | 255k | for (et = _et_list; et; et = et->next) { |
148 | 0 | if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { |
149 | | /* This is the right table */ |
150 | 0 | if (et->table->n_msgs <= offset) { |
151 | 0 | break; |
152 | 0 | } else { |
153 | 0 | const char *msg = et->table->msgs[offset]; |
154 | 0 | et_list_unlock(); |
155 | 0 | if (com_err_gettext) |
156 | 0 | return (*com_err_gettext)(msg); |
157 | 0 | else |
158 | 0 | return msg; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | } |
162 | 255k | for (et = _et_dynamic_list; et; et = et->next) { |
163 | 0 | if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { |
164 | | /* This is the right table */ |
165 | 0 | if (et->table->n_msgs <= offset) { |
166 | 0 | break; |
167 | 0 | } else { |
168 | 0 | const char *msg = et->table->msgs[offset]; |
169 | 0 | et_list_unlock(); |
170 | 0 | if (com_err_gettext) |
171 | 0 | return (*com_err_gettext)(msg); |
172 | 0 | else |
173 | 0 | return msg; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | } |
177 | 255k | et_list_unlock(); |
178 | 255k | oops: |
179 | 255k | strcpy (buffer, "Unknown code "); |
180 | 255k | if (table_num) { |
181 | 255k | strcat (buffer, error_table_name (table_num)); |
182 | 255k | strcat (buffer, " "); |
183 | 255k | } |
184 | 4.86M | for (cp = buffer; *cp; cp++) |
185 | 4.60M | ; |
186 | 255k | if (offset >= 100) { |
187 | 0 | *cp++ = '0' + offset / 100; |
188 | 0 | offset %= 100; |
189 | 0 | started++; |
190 | 0 | } |
191 | 255k | if (started || offset >= 10) { |
192 | 255k | *cp++ = '0' + offset / 10; |
193 | 255k | offset %= 10; |
194 | 255k | } |
195 | 255k | *cp++ = '0' + offset; |
196 | 255k | *cp = '\0'; |
197 | 255k | return(buffer); |
198 | 255k | } |
199 | | |
200 | | /* |
201 | | * This routine will only return a value if the we are not running as |
202 | | * a privileged process. |
203 | | */ |
204 | | static char *safe_getenv(const char *arg) |
205 | 0 | { |
206 | 0 | #if !defined(_WIN32) |
207 | 0 | if ((getuid() != geteuid()) || (getgid() != getegid())) |
208 | 0 | return NULL; |
209 | 0 | #endif |
210 | 0 | #if HAVE_PRCTL |
211 | 0 | if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) |
212 | 0 | return NULL; |
213 | | #else |
214 | | #if (defined(linux) && defined(SYS_prctl)) |
215 | | if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) |
216 | | return NULL; |
217 | | #endif |
218 | | #endif |
219 | | |
220 | 0 | #if defined(HAVE_SECURE_GETENV) |
221 | 0 | return secure_getenv(arg); |
222 | | #elif defined(HAVE___SECURE_GETENV) |
223 | | return __secure_getenv(arg); |
224 | | #else |
225 | | return getenv(arg); |
226 | | #endif |
227 | 0 | } |
228 | | |
229 | 0 | #define DEBUG_INIT 0x8000 |
230 | 0 | #define DEBUG_ADDREMOVE 0x0001 |
231 | | |
232 | | static int debug_mask = 0; |
233 | | static FILE *debug_f = 0; |
234 | | |
235 | | static void init_debug(void) |
236 | 0 | { |
237 | 0 | char *dstr, *fn, *tmp; |
238 | |
|
239 | 0 | if (debug_mask & DEBUG_INIT) |
240 | 0 | return; |
241 | | |
242 | 0 | dstr = getenv("COMERR_DEBUG"); |
243 | 0 | if (dstr) { |
244 | 0 | debug_mask = strtoul(dstr, &tmp, 0); |
245 | 0 | if (*tmp || errno) |
246 | 0 | debug_mask = 0; |
247 | 0 | } |
248 | |
|
249 | 0 | debug_mask |= DEBUG_INIT; |
250 | 0 | if (debug_mask == DEBUG_INIT) |
251 | 0 | return; |
252 | | |
253 | 0 | fn = safe_getenv("COMERR_DEBUG_FILE"); |
254 | 0 | if (fn) |
255 | 0 | debug_f = fopen(fn, "a"); |
256 | 0 | if (!debug_f) |
257 | 0 | debug_f = fopen("/dev/tty", "a"); |
258 | 0 | if (debug_f) { |
259 | 0 | #ifdef HAVE_FCNTL |
260 | 0 | int fd = fileno(debug_f); |
261 | |
|
262 | 0 | if (fd >= 0) { |
263 | 0 | int flags = fcntl(fd, F_GETFD); |
264 | |
|
265 | 0 | if (flags >= 0) |
266 | 0 | flags = fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
267 | 0 | if (flags < 0) { |
268 | 0 | fprintf(debug_f, "Couldn't set FD_CLOEXEC " |
269 | 0 | "on debug FILE: %s\n", strerror(errno)); |
270 | 0 | fclose(debug_f); |
271 | 0 | debug_f = NULL; |
272 | 0 | debug_mask = DEBUG_INIT; |
273 | 0 | } |
274 | 0 | } |
275 | 0 | #endif |
276 | 0 | } else |
277 | 0 | debug_mask = DEBUG_INIT; |
278 | 0 | } |
279 | | |
280 | | /* |
281 | | * New interface provided by krb5's com_err library |
282 | | */ |
283 | | errcode_t add_error_table(const struct error_table * et) |
284 | 0 | { |
285 | 0 | struct et_list *el; |
286 | |
|
287 | 0 | if (!(el = (struct et_list *) malloc(sizeof(struct et_list)))) |
288 | 0 | return ENOMEM; |
289 | | |
290 | 0 | if (et_list_lock() != 0) { |
291 | 0 | free(el); |
292 | 0 | return errno; |
293 | 0 | } |
294 | | |
295 | 0 | el->table = et; |
296 | 0 | el->next = _et_dynamic_list; |
297 | 0 | _et_dynamic_list = el; |
298 | |
|
299 | 0 | init_debug(); |
300 | 0 | if (debug_mask & DEBUG_ADDREMOVE) |
301 | 0 | fprintf(debug_f, "add_error_table: %s (0x%p)\n", |
302 | 0 | error_table_name(et->base), |
303 | 0 | (const void *) et); |
304 | |
|
305 | 0 | et_list_unlock(); |
306 | 0 | return 0; |
307 | 0 | } |
308 | | |
309 | | /* |
310 | | * New interface provided by krb5's com_err library |
311 | | */ |
312 | | errcode_t remove_error_table(const struct error_table * et) |
313 | 0 | { |
314 | 0 | struct et_list *el; |
315 | 0 | struct et_list *el2 = 0; |
316 | |
|
317 | 0 | if (et_list_lock() != 0) |
318 | 0 | return ENOENT; |
319 | | |
320 | 0 | el = _et_dynamic_list; |
321 | 0 | init_debug(); |
322 | 0 | while (el) { |
323 | 0 | if (el->table->base == et->base) { |
324 | 0 | if (el2) /* Not the beginning of the list */ |
325 | 0 | el2->next = el->next; |
326 | 0 | else |
327 | 0 | _et_dynamic_list = el->next; |
328 | 0 | (void) free(el); |
329 | 0 | if (debug_mask & DEBUG_ADDREMOVE) |
330 | 0 | fprintf(debug_f, |
331 | 0 | "remove_error_table: %s (0x%p)\n", |
332 | 0 | error_table_name(et->base), |
333 | 0 | (const void *) et); |
334 | 0 | et_list_unlock(); |
335 | 0 | return 0; |
336 | 0 | } |
337 | 0 | el2 = el; |
338 | 0 | el = el->next; |
339 | 0 | } |
340 | 0 | if (debug_mask & DEBUG_ADDREMOVE) |
341 | 0 | fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n", |
342 | 0 | error_table_name(et->base), |
343 | 0 | (const void *) et); |
344 | 0 | et_list_unlock(); |
345 | 0 | return ENOENT; |
346 | 0 | } |
347 | | |
348 | | /* |
349 | | * Variant of the interface provided by Heimdal's com_err library |
350 | | */ |
351 | | void |
352 | | add_to_error_table(struct et_list *new_table) |
353 | 0 | { |
354 | 0 | add_error_table(new_table->table); |
355 | 0 | } |