blob: 92509e63abc2459668f5b85684ec036a356854ff [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'o1ad31742013-06-16 14:34:59 -040020#if HAVE_SECURE_GETENV
21#define _GNU_SOURCE
22#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000023#include <stdio.h>
Theodore Ts'o91835c12003-03-30 22:26:13 -050024#include <stdlib.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000025#include <string.h>
Theodore Ts'of3db3561997-04-26 13:34:30 +000026#include <errno.h>
Theodore Ts'oec84b742006-12-22 13:38:38 -050027#ifdef HAVE_SYS_PRCTL_H
28#include <sys/prctl.h>
29#else
30#define PR_GET_DUMPABLE 3
31#endif
32#if (!defined(HAVE_PRCTL) && defined(linux))
33#include <sys/syscall.h>
34#endif
Theodore Ts'od7f45af2008-09-12 10:15:26 -040035#ifdef HAVE_SEMAPHORE_H
36#include <semaphore.h>
37#endif
Andreas Dilgerde8f3a72007-05-25 11:18:11 -040038#if HAVE_UNISTD_H
39#include <unistd.h>
40#endif
Theodore Ts'o902be4a2008-10-01 20:45:26 -040041#include <fcntl.h>
Andreas Dilgerde8f3a72007-05-25 11:18:11 -040042#if HAVE_SYS_TYPES_H
43#include <sys/types.h>
44#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +000045#include "com_err.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000046#include "error_table.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000047#include "internal.h"
48
Theodore Ts'o47526e32008-02-09 23:39:27 -050049#ifdef TLS
50#define THREAD_LOCAL static TLS
51#else
52#define THREAD_LOCAL static
53#endif
54
55THREAD_LOCAL char buffer[25];
Theodore Ts'o3839e651997-04-26 13:21:57 +000056
57struct et_list * _et_list = (struct et_list *) NULL;
Theodore Ts'od51b8192005-06-20 17:59:06 -040058struct et_list * _et_dynamic_list = (struct et_list *) NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +000059
Theodore Ts'od7f45af2008-09-12 10:15:26 -040060#ifdef __GNUC__
61#define COMERR_ATTR(x) __attribute__(x)
62#else
63#define COMERR_ATTR(x)
64#endif
65
66#ifdef HAVE_SEM_INIT
67static sem_t _et_lock;
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040068static int _et_lock_initialized;
Theodore Ts'od7f45af2008-09-12 10:15:26 -040069
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040070static void COMERR_ATTR((constructor)) setup_et_lock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040071{
72 sem_init(&_et_lock, 0, 1);
73 _et_lock_initialized = 1;
74}
75
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040076static void COMERR_ATTR((destructor)) fini_et_lock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040077{
78 sem_destroy(&_et_lock);
79 _et_lock_initialized = 0;
80}
81#endif
82
83
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040084int et_list_lock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040085{
86#ifdef HAVE_SEM_INIT
87 if (!_et_lock_initialized)
88 setup_et_lock();
89 return sem_wait(&_et_lock);
90#else
91 return 0;
92#endif
93}
94
Theodore Ts'o98e9fb92008-10-02 08:51:27 -040095int et_list_unlock(void)
Theodore Ts'od7f45af2008-09-12 10:15:26 -040096{
97#ifdef HAVE_SEM_INIT
98 if (_et_lock_initialized)
99 return sem_post(&_et_lock);
100#endif
101 return 0;
102}
Theodore Ts'of3db3561997-04-26 13:34:30 +0000103
Theodore Ts'o42590522011-10-05 00:58:40 -0400104typedef char *(*gettextf) (const char *);
105
Theodore Ts'o299a1e82011-10-05 03:03:44 -0400106static gettextf com_err_gettext = NULL;
Theodore Ts'o42590522011-10-05 00:58:40 -0400107
108gettextf set_com_err_gettext (new_proc)
109 gettextf new_proc;
110{
111 gettextf x = com_err_gettext;
112
113 com_err_gettext = new_proc;
114
115 return x;
116}
117
118
Theodore Ts'of3db3561997-04-26 13:34:30 +0000119const char * error_message (errcode_t code)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000120{
121 int offset;
122 struct et_list *et;
Theodore Ts'oa47b66e1997-08-10 23:02:21 +0000123 errcode_t table_num;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000124 int started = 0;
125 char *cp;
126
Theodore Ts'oa47b66e1997-08-10 23:02:21 +0000127 offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000128 table_num = code - offset;
129 if (!table_num) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000130#ifdef HAS_SYS_ERRLIST
Theodore Ts'o3839e651997-04-26 13:21:57 +0000131 if (offset < sys_nerr)
132 return(sys_errlist[offset]);
133 else
134 goto oops;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000135#else
136 cp = strerror(offset);
137 if (cp)
138 return(cp);
139 else
140 goto oops;
141#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000142 }
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400143 et_list_lock();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000144 for (et = _et_list; et; et = et->next) {
Theodore Ts'o6b6c27f2007-12-15 22:31:03 -0500145 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000146 /* This is the right table */
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400147 if (et->table->n_msgs <= offset) {
148 break;
149 } else {
150 const char *msg = et->table->msgs[offset];
151 et_list_unlock();
Theodore Ts'o42590522011-10-05 00:58:40 -0400152 if (com_err_gettext)
153 return (*com_err_gettext)(msg);
154 else
155 return msg;
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400156 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000157 }
158 }
Theodore Ts'od51b8192005-06-20 17:59:06 -0400159 for (et = _et_dynamic_list; et; et = et->next) {
Theodore Ts'o6b6c27f2007-12-15 22:31:03 -0500160 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
Theodore Ts'od51b8192005-06-20 17:59:06 -0400161 /* This is the right table */
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400162 if (et->table->n_msgs <= offset) {
163 break;
164 } else {
165 const char *msg = et->table->msgs[offset];
166 et_list_unlock();
Theodore Ts'o42590522011-10-05 00:58:40 -0400167 if (com_err_gettext)
168 return (*com_err_gettext)(msg);
169 else
170 return msg;
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400171 }
Theodore Ts'od51b8192005-06-20 17:59:06 -0400172 }
173 }
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400174 et_list_unlock();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000175oops:
176 strcpy (buffer, "Unknown code ");
177 if (table_num) {
178 strcat (buffer, error_table_name (table_num));
179 strcat (buffer, " ");
180 }
181 for (cp = buffer; *cp; cp++)
182 ;
183 if (offset >= 100) {
184 *cp++ = '0' + offset / 100;
185 offset %= 100;
186 started++;
187 }
188 if (started || offset >= 10) {
189 *cp++ = '0' + offset / 10;
190 offset %= 10;
191 }
192 *cp++ = '0' + offset;
193 *cp = '\0';
194 return(buffer);
195}
Theodore Ts'o00aba962003-03-19 19:46:02 -0500196
197/*
Theodore Ts'oec84b742006-12-22 13:38:38 -0500198 * This routine will only return a value if the we are not running as
199 * a privileged process.
200 */
201static char *safe_getenv(const char *arg)
202{
203 if ((getuid() != geteuid()) || (getgid() != getegid()))
204 return NULL;
205#if HAVE_PRCTL
206 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
207 return NULL;
208#else
209#if (defined(linux) && defined(SYS_prctl))
210 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
211 return NULL;
212#endif
213#endif
214
Theodore Ts'o1ad31742013-06-16 14:34:59 -0400215#if defined(HAVE_SECURE_GETENV)
216 return secure_getenv(arg);
217#elif defined(HAVE___SECURE_GETENV)
Theodore Ts'oec84b742006-12-22 13:38:38 -0500218 return __secure_getenv(arg);
219#else
220 return getenv(arg);
221#endif
222}
223
224#define DEBUG_INIT 0x8000
225#define DEBUG_ADDREMOVE 0x0001
226
227static int debug_mask = 0;
228static FILE *debug_f = 0;
229
230static void init_debug(void)
231{
Theodore Ts'o902be4a2008-10-01 20:45:26 -0400232 char *dstr, *fn, *tmp;
233 int fd, flags;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500234
235 if (debug_mask & DEBUG_INIT)
236 return;
237
238 dstr = getenv("COMERR_DEBUG");
Theodore Ts'o902be4a2008-10-01 20:45:26 -0400239 if (dstr) {
240 debug_mask = strtoul(dstr, &tmp, 0);
241 if (*tmp || errno)
242 debug_mask = 0;
243 }
244
245 debug_mask |= DEBUG_INIT;
246 if (debug_mask == DEBUG_INIT)
247 return;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500248
249 fn = safe_getenv("COMERR_DEBUG_FILE");
250 if (fn)
251 debug_f = fopen(fn, "a");
252 if (!debug_f)
253 debug_f = fopen("/dev/tty", "a");
Theodore Ts'o902be4a2008-10-01 20:45:26 -0400254 if (debug_f) {
255 fd = fileno(debug_f);
256 if (fd >= 0) {
257 flags = fcntl(fd, F_GETFD);
258 if (flags >= 0)
259 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
260 }
261 } else
262 debug_mask = DEBUG_INIT;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500263
Theodore Ts'oec84b742006-12-22 13:38:38 -0500264}
265
266/*
Theodore Ts'o00aba962003-03-19 19:46:02 -0500267 * New interface provided by krb5's com_err library
268 */
Theodore Ts'o91835c12003-03-30 22:26:13 -0500269errcode_t add_error_table(const struct error_table * et)
Theodore Ts'o00aba962003-03-19 19:46:02 -0500270{
Theodore Ts'od51b8192005-06-20 17:59:06 -0400271 struct et_list *el;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500272
273 if (!(el = (struct et_list *) malloc(sizeof(struct et_list))))
274 return ENOMEM;
275
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400276 if (et_list_lock() != 0) {
277 free(el);
278 return errno;
279 }
280
Theodore Ts'o00aba962003-03-19 19:46:02 -0500281 el->table = et;
Theodore Ts'od51b8192005-06-20 17:59:06 -0400282 el->next = _et_dynamic_list;
283 _et_dynamic_list = el;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500284
Theodore Ts'oec84b742006-12-22 13:38:38 -0500285 init_debug();
286 if (debug_mask & DEBUG_ADDREMOVE)
287 fprintf(debug_f, "add_error_table: %s (0x%p)\n",
288 error_table_name(et->base),
289 (const void *) et);
290
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400291 et_list_unlock();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500292 return 0;
293}
294
295/*
296 * New interface provided by krb5's com_err library
297 */
Theodore Ts'o91835c12003-03-30 22:26:13 -0500298errcode_t remove_error_table(const struct error_table * et)
Theodore Ts'o00aba962003-03-19 19:46:02 -0500299{
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400300 struct et_list *el;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500301 struct et_list *el2 = 0;
302
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400303 if (et_list_lock() != 0)
304 return ENOENT;
305
306 el = _et_dynamic_list;
Theodore Ts'oec84b742006-12-22 13:38:38 -0500307 init_debug();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500308 while (el) {
309 if (el->table->base == et->base) {
310 if (el2) /* Not the beginning of the list */
311 el2->next = el->next;
312 else
Theodore Ts'od51b8192005-06-20 17:59:06 -0400313 _et_dynamic_list = el->next;
Theodore Ts'o00aba962003-03-19 19:46:02 -0500314 (void) free(el);
Theodore Ts'oec84b742006-12-22 13:38:38 -0500315 if (debug_mask & DEBUG_ADDREMOVE)
316 fprintf(debug_f,
317 "remove_error_table: %s (0x%p)\n",
318 error_table_name(et->base),
319 (const void *) et);
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400320 et_list_unlock();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500321 return 0;
322 }
323 el2 = el;
324 el = el->next;
325 }
Theodore Ts'oec84b742006-12-22 13:38:38 -0500326 if (debug_mask & DEBUG_ADDREMOVE)
327 fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n",
328 error_table_name(et->base),
329 (const void *) et);
Theodore Ts'od7f45af2008-09-12 10:15:26 -0400330 et_list_unlock();
Theodore Ts'o00aba962003-03-19 19:46:02 -0500331 return ENOENT;
332}
333
334/*
335 * Variant of the interface provided by Heimdal's com_err library
336 */
337void
338add_to_error_table(struct et_list *new_table)
339{
340 add_error_table(new_table->table);
341}