Whamcloud - gitweb
aclocal.m4: update to newer versions of autoconf macros
[tools/e2fsprogs.git] / lib / et / error_message.c
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 #include <fcntl.h>
39 #if HAVE_SYS_TYPES_H
40 #include <sys/types.h>
41 #endif
42 #include "com_err.h"
43 #include "error_table.h"
44 #include "internal.h"
45
46 #ifdef TLS
47 #define THREAD_LOCAL static TLS
48 #else
49 #define THREAD_LOCAL static
50 #endif
51
52 THREAD_LOCAL char buffer[25];
53
54 struct et_list * _et_list = (struct et_list *) NULL;
55 struct et_list * _et_dynamic_list = (struct et_list *) NULL;
56
57 #ifdef __GNUC__
58 #define COMERR_ATTR(x) __attribute__(x)
59 #else
60 #define COMERR_ATTR(x)
61 #endif
62
63 #ifdef HAVE_SEM_INIT
64 static sem_t _et_lock;
65 static int _et_lock_initialized;
66
67 static void COMERR_ATTR((constructor)) setup_et_lock(void)
68 {
69         sem_init(&_et_lock, 0, 1);
70         _et_lock_initialized = 1;
71 }
72
73 static void COMERR_ATTR((destructor)) fini_et_lock(void)
74 {
75         sem_destroy(&_et_lock);
76         _et_lock_initialized = 0;
77 }
78 #endif
79
80
81 int et_list_lock(void)
82 {
83 #ifdef HAVE_SEM_INIT
84         if (!_et_lock_initialized)
85                 setup_et_lock();
86         return sem_wait(&_et_lock);
87 #else
88         return 0;
89 #endif
90 }
91
92 int et_list_unlock(void)
93 {
94 #ifdef HAVE_SEM_INIT
95         if (_et_lock_initialized)
96                 return sem_post(&_et_lock);
97 #endif
98         return 0;
99 }
100
101 typedef char *(*gettextf) (const char *);
102
103 static gettextf com_err_gettext = NULL;
104
105 gettextf set_com_err_gettext(gettextf new_proc)
106 {
107     gettextf x = com_err_gettext;
108
109     com_err_gettext = new_proc;
110
111     return x;
112 }
113
114
115 const char * error_message (errcode_t code)
116 {
117     int offset;
118     struct et_list *et;
119     errcode_t table_num;
120     int started = 0;
121     char *cp;
122
123     offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
124     table_num = code - offset;
125     if (!table_num) {
126 #ifdef HAS_SYS_ERRLIST
127         if (offset < sys_nerr)
128             return(sys_errlist[offset]);
129         else
130             goto oops;
131 #else
132         cp = strerror(offset);
133         if (cp)
134             return(cp);
135         else
136             goto oops;
137 #endif
138     }
139     et_list_lock();
140     for (et = _et_list; et; et = et->next) {
141         if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
142             /* This is the right table */
143             if (et->table->n_msgs <= offset) {
144                 break;
145             } else {
146                 const char *msg = et->table->msgs[offset];
147                 et_list_unlock();
148                 if (com_err_gettext)
149                     return (*com_err_gettext)(msg);
150                 else
151                     return msg;
152             }
153         }
154     }
155     for (et = _et_dynamic_list; et; et = et->next) {
156         if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
157             /* This is the right table */
158             if (et->table->n_msgs <= offset) {
159                 break;
160             } else {
161                 const char *msg = et->table->msgs[offset];
162                 et_list_unlock();
163                 if (com_err_gettext)
164                     return (*com_err_gettext)(msg);
165                 else
166                     return msg;
167             }
168         }
169     }
170     et_list_unlock();
171 oops:
172     strcpy (buffer, "Unknown code ");
173     if (table_num) {
174         strcat (buffer, error_table_name (table_num));
175         strcat (buffer, " ");
176     }
177     for (cp = buffer; *cp; cp++)
178         ;
179     if (offset >= 100) {
180         *cp++ = '0' + offset / 100;
181         offset %= 100;
182         started++;
183     }
184     if (started || offset >= 10) {
185         *cp++ = '0' + offset / 10;
186         offset %= 10;
187     }
188     *cp++ = '0' + offset;
189     *cp = '\0';
190     return(buffer);
191 }
192
193 /*
194  * This routine will only return a value if the we are not running as
195  * a privileged process.
196  */
197 static char *safe_getenv(const char *arg)
198 {
199         if ((getuid() != geteuid()) || (getgid() != getegid()))
200                 return NULL;
201 #if HAVE_PRCTL
202         if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
203                 return NULL;
204 #else
205 #if (defined(linux) && defined(SYS_prctl))
206         if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
207                 return NULL;
208 #endif
209 #endif
210
211 #if defined(HAVE_SECURE_GETENV)
212         return secure_getenv(arg);
213 #elif defined(HAVE___SECURE_GETENV)
214         return __secure_getenv(arg);
215 #else
216         return getenv(arg);
217 #endif
218 }
219
220 #define DEBUG_INIT      0x8000
221 #define DEBUG_ADDREMOVE 0x0001
222
223 static int debug_mask = 0;
224 static FILE *debug_f = 0;
225
226 static void init_debug(void)
227 {
228         char    *dstr, *fn, *tmp;
229         int     fd, flags;
230
231         if (debug_mask & DEBUG_INIT)
232                 return;
233
234         dstr = getenv("COMERR_DEBUG");
235         if (dstr) {
236                 debug_mask = strtoul(dstr, &tmp, 0);
237                 if (*tmp || errno)
238                         debug_mask = 0;
239         }
240
241         debug_mask |= DEBUG_INIT;
242         if (debug_mask == DEBUG_INIT)
243                 return;
244
245         fn = safe_getenv("COMERR_DEBUG_FILE");
246         if (fn)
247                 debug_f = fopen(fn, "a");
248         if (!debug_f)
249                 debug_f = fopen("/dev/tty", "a");
250         if (debug_f) {
251                 fd = fileno(debug_f);
252                 if (fd >= 0) {
253                         flags = fcntl(fd, F_GETFD);
254                         if (flags >= 0)
255                                 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
256                 }
257         } else
258                 debug_mask = DEBUG_INIT;
259
260 }
261
262 /*
263  * New interface provided by krb5's com_err library
264  */
265 errcode_t add_error_table(const struct error_table * et)
266 {
267         struct et_list *el;
268
269         if (!(el = (struct et_list *) malloc(sizeof(struct et_list))))
270                 return ENOMEM;
271
272         if (et_list_lock() != 0) {
273                 free(el);
274                 return errno;
275         }
276
277         el->table = et;
278         el->next = _et_dynamic_list;
279         _et_dynamic_list = el;
280
281         init_debug();
282         if (debug_mask & DEBUG_ADDREMOVE)
283                 fprintf(debug_f, "add_error_table: %s (0x%p)\n",
284                         error_table_name(et->base),
285                         (const void *) et);
286
287         et_list_unlock();
288         return 0;
289 }
290
291 /*
292  * New interface provided by krb5's com_err library
293  */
294 errcode_t remove_error_table(const struct error_table * et)
295 {
296         struct et_list *el;
297         struct et_list *el2 = 0;
298
299         if (et_list_lock() != 0)
300                 return ENOENT;
301
302         el = _et_dynamic_list;
303         init_debug();
304         while (el) {
305                 if (el->table->base == et->base) {
306                         if (el2)        /* Not the beginning of the list */
307                                 el2->next = el->next;
308                         else
309                                 _et_dynamic_list = el->next;
310                         (void) free(el);
311                         if (debug_mask & DEBUG_ADDREMOVE)
312                                 fprintf(debug_f,
313                                         "remove_error_table: %s (0x%p)\n",
314                                         error_table_name(et->base),
315                                         (const void *) et);
316                         et_list_unlock();
317                         return 0;
318                 }
319                 el2 = el;
320                 el = el->next;
321         }
322         if (debug_mask & DEBUG_ADDREMOVE)
323                 fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n",
324                         error_table_name(et->base),
325                         (const void *) et);
326         et_list_unlock();
327         return ENOENT;
328 }
329
330 /*
331  * Variant of the interface provided by Heimdal's com_err library
332  */
333 void
334 add_to_error_table(struct et_list *new_table)
335 {
336         add_error_table(new_table->table);
337 }