blob: dc77b69fbf57ae28545011d1eb47457c70287230 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * $Header$
3 * $Source$
4 * $Locker$
5 *
6 * Copyright 1987 by the Student Information Processing Board
7 * of the Massachusetts Institute of Technology
8 *
Theodore Ts'o06cefee1999-10-23 01:16:22 +00009 * 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.
Theodore Ts'o3839e651997-04-26 13:21:57 +000017 */
18
Theodore Ts'od1154eb2011-09-18 17:34:37 -040019#include "config.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000020#include <stdio.h>
Theodore Ts'o91835c12003-03-30 22:26:13 -050021#include <stdlib.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000022#include <string.h>
Theodore Ts'of3db3561997-04-26 13:34:30 +000023#include <errno.h>
Theodore Ts'oec84b742006-12-22 13:38:38 -050024#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
Theodore Ts'od7f45af2008-09-12 10:15:26 -040032#ifdef HAVE_SEMAPHORE_H
33#include <semaphore.h>
34#endif
Andreas Dilgerde8f3a72007-05-25 11:18:11 -040035#if HAVE_UNISTD_H
36#include <unistd.h>
37#endif
Theodore Ts'o902be4a2008-10-01 20:45:26 -040038#include <fcntl.h>
Andreas Dilgerde8f3a72007-05-25 11:18:11 -040039#if HAVE_SYS_TYPES_H
40#include <sys/types.h>
41#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +000042#include "com_err.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000043#include "error_table.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000044#include "internal.h"
45
Theodore Ts'o47526e32008-02-09 23:39:27 -050046#ifdef TLS
47#define THREAD_LOCAL static TLS
48#else
49#define THREAD_LOCAL static
50#endif
51
52THREAD_LOCAL char buffer[25];
Theodore Ts'o3839e651997-04-26 13:21:57 +000053
54struct et_list * _et_list = (struct et_list *) NULL;
Theodore Ts'od51b8192005-06-20 17:59:06 -040055struct et_list * _et_dynamic_list = (struct et_list *) NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000056
Theodore Ts'od7f45af2008-09-12 10:15:26 -040057#ifdef __GNUC__
58#define COMERR_ATTR(x) __attribute__(x)
59#else
60#define COMERR_ATTR(x)
61#endif
62
63#ifdef HAVE_SEM_INIT
64static sem_t _et_lock;
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040065static int _et_lock_initialized;
Theodore Ts'od7f45af2008-09-12 10:15:26 -040066
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040067static void COMERR_ATTR((constructor)) setup_et_lock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040068{
69 sem_init(&_et_lock, 0, 1);
70 _et_lock_initialized = 1;
71}
72
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040073static void COMERR_ATTR((destructor)) fini_et_lock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040074{
75 sem_destroy(&_et_lock);
76 _et_lock_initialized = 0;
77}
78#endif
79
80
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040081int et_list_lock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040082{
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
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040092int et_list_unlock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040093{
94#ifdef HAVE_SEM_INIT
95 if (_et_lock_initialized)
96 return sem_post(&_et_lock);
97#endif
98 return 0;
99}
Theodore Ts'of3db3561997-04-26 13:34:30 +0000100
Theodore Ts'o42590522011-10-05 00:58:40 -0400101typedef char *(*gettextf) (const char *);
102
Theodore Ts'o299a1e82011-10-05 03:03:44 -0400103static gettextf com_err_gettext = NULL;
Theodore Ts'o42590522011-10-05 00:58:40 -0400104
105gettextf set_com_err_gettext (new_proc)
106 gettextf new_proc;
107{
108 gettextf x = com_err_gettext;
109
110 com_err_gettext = new_proc;
111
112 return x;
113}
114
115
Theodore Ts'of3db3561997-04-26 13:34:30 +0000116const char * error_message (errcode_t code)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000117{
118 int offset;
119 struct et_list *et;
Theodore Ts'oa47b66e1997-08-10 23:02:21 +0000120 errcode_t table_num;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000121 int started = 0;
122 char *cp;
123
Theodore Ts'oa47b66e1997-08-10 23:02:21 +0000124 offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000125 table_num = code - offset;
126 if (!table_num) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000127#ifdef HAS_SYS_ERRLIST
Theodore Ts'o3839e651997-04-26 13:21:57 +0000128 if (offset < sys_nerr)
129 return(sys_errlist[offset]);
130 else
131 goto oops;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000132#else
133 cp = strerror(offset);
134 if (cp)
135 return(cp);
136 else
137 goto oops;
138#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000139 }
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400140 et_list_lock();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000141 for (et = _et_list; et; et = et->next) {
Theodore Ts'o6b6c27f2007-12-15 22:31:03 -0500142 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000143 /* This is the right table */
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400144 if (et->table->n_msgs <= offset) {
145 break;
146 } else {
147 const char *msg = et->table->msgs[offset];
148 et_list_unlock();
Theodore Ts'o42590522011-10-05 00:58:40 -0400149 if (com_err_gettext)
150 return (*com_err_gettext)(msg);
151 else
152 return msg;
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400153 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000154 }
155 }
Theodore Ts'od51b8192005-06-20 17:59:06 -0400156 for (et = _et_dynamic_list; et; et = et->next) {
Theodore Ts'o6b6c27f2007-12-15 22:31:03 -0500157 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
Theodore Ts'od51b8192005-06-20 17:59:06 -0400158 /* This is the right table */
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400159 if (et->table->n_msgs <= offset) {
160 break;
161 } else {
162 const char *msg = et->table->msgs[offset];
163 et_list_unlock();
Theodore Ts'o42590522011-10-05 00:58:40 -0400164 if (com_err_gettext)
165 return (*com_err_gettext)(msg);
166 else
167 return msg;
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400168 }
Theodore Ts'od51b8192005-06-20 17:59:06 -0400169 }
170 }
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400171 et_list_unlock();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000172oops:
173 strcpy (buffer, "Unknown code ");
174 if (table_num) {
175 strcat (buffer, error_table_name (table_num));
176 strcat (buffer, " ");
177 }
178 for (cp = buffer; *cp; cp++)
179 ;
180 if (offset >= 100) {
181 *cp++ = '0' + offset / 100;
182 offset %= 100;
183 started++;
184 }
185 if (started || offset >= 10) {
186 *cp++ = '0' + offset / 10;
187 offset %= 10;
188 }
189 *cp++ = '0' + offset;
190 *cp = '\0';
191 return(buffer);
192}
Theodore Ts'o00aba962003-03-19 19:46:02 -0500193
194/*
Theodore Ts'oec84b742006-12-22 13:38:38 -0500195 * This routine will only return a value if the we are not running as
196 * a privileged process.
197 */
198static char *safe_getenv(const char *arg)
199{
200 if ((getuid() != geteuid()) || (getgid() != getegid()))
201 return NULL;
202#if HAVE_PRCTL
203 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
204 return NULL;
205#else
206#if (defined(linux) && defined(SYS_prctl))
207 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
208 return NULL;
209#endif
210#endif
211
212#ifdef HAVE___SECURE_GETENV
213 return __secure_getenv(arg);
214#else
215 return getenv(arg);
216#endif
217}
218
219#define DEBUG_INIT 0x8000
220#define DEBUG_ADDREMOVE 0x0001
221
222static int debug_mask = 0;
223static FILE *debug_f = 0;
224
225static void init_debug(void)
226{
Theodore Ts'o902be4a2008-10-01 20:45:26 -0400227 char *dstr, *fn, *tmp;
228 int fd, flags;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500229
230 if (debug_mask & DEBUG_INIT)
231 return;
232
233 dstr = getenv("COMERR_DEBUG");
Theodore Ts'o902be4a2008-10-01 20:45:26 -0400234 if (dstr) {
235 debug_mask = strtoul(dstr, &tmp, 0);
236 if (*tmp || errno)
237 debug_mask = 0;
238 }
239
240 debug_mask |= DEBUG_INIT;
241 if (debug_mask == DEBUG_INIT)
242 return;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500243
244 fn = safe_getenv("COMERR_DEBUG_FILE");
245 if (fn)
246 debug_f = fopen(fn, "a");
247 if (!debug_f)
248 debug_f = fopen("/dev/tty", "a");
Theodore Ts'o902be4a2008-10-01 20:45:26 -0400249 if (debug_f) {
250 fd = fileno(debug_f);
251 if (fd >= 0) {
252 flags = fcntl(fd, F_GETFD);
253 if (flags >= 0)
254 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
255 }
256 } else
257 debug_mask = DEBUG_INIT;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500258
Theodore Ts'oec84b742006-12-22 13:38:38 -0500259}
260
261/*
Theodore Ts'o00aba962003-03-19 19:46:02 -0500262 * New interface provided by krb5's com_err library
263 */
Theodore Ts'o91835c12003-03-30 22:26:13 -0500264errcode_t add_error_table(const struct error_table * et)
Theodore Ts'o00aba962003-03-19 19:46:02 -0500265{
Theodore Ts'od51b8192005-06-20 17:59:06 -0400266 struct et_list *el;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500267
268 if (!(el = (struct et_list *) malloc(sizeof(struct et_list))))
269 return ENOMEM;
270
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400271 if (et_list_lock() != 0) {
272 free(el);
273 return errno;
274 }
275
Theodore Ts'o00aba962003-03-19 19:46:02 -0500276 el->table = et;
Theodore Ts'od51b8192005-06-20 17:59:06 -0400277 el->next = _et_dynamic_list;
278 _et_dynamic_list = el;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500279
Theodore Ts'oec84b742006-12-22 13:38:38 -0500280 init_debug();
281 if (debug_mask & DEBUG_ADDREMOVE)
282 fprintf(debug_f, "add_error_table: %s (0x%p)\n",
283 error_table_name(et->base),
284 (const void *) et);
285
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400286 et_list_unlock();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500287 return 0;
288}
289
290/*
291 * New interface provided by krb5's com_err library
292 */
Theodore Ts'o91835c12003-03-30 22:26:13 -0500293errcode_t remove_error_table(const struct error_table * et)
Theodore Ts'o00aba962003-03-19 19:46:02 -0500294{
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400295 struct et_list *el;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500296 struct et_list *el2 = 0;
297
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400298 if (et_list_lock() != 0)
299 return ENOENT;
300
301 el = _et_dynamic_list;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500302 init_debug();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500303 while (el) {
304 if (el->table->base == et->base) {
305 if (el2) /* Not the beginning of the list */
306 el2->next = el->next;
307 else
Theodore Ts'od51b8192005-06-20 17:59:06 -0400308 _et_dynamic_list = el->next;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500309 (void) free(el);
Theodore Ts'oec84b742006-12-22 13:38:38 -0500310 if (debug_mask & DEBUG_ADDREMOVE)
311 fprintf(debug_f,
312 "remove_error_table: %s (0x%p)\n",
313 error_table_name(et->base),
314 (const void *) et);
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400315 et_list_unlock();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500316 return 0;
317 }
318 el2 = el;
319 el = el->next;
320 }
Theodore Ts'oec84b742006-12-22 13:38:38 -0500321 if (debug_mask & DEBUG_ADDREMOVE)
322 fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n",
323 error_table_name(et->base),
324 (const void *) et);
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400325 et_list_unlock();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500326 return ENOENT;
327}
328
329/*
330 * Variant of the interface provided by Heimdal's com_err library
331 */
332void
333add_to_error_table(struct et_list *new_table)
334{
335 add_error_table(new_table->table);
336}