2 * Copyright (c) 2004-2005, 2010-2015, 2017-2018
3 * Todd C. Miller <Todd.Miller@sudo.ws>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
20 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
25 #include <sys/types.h>
35 # include "compat/stdbool.h"
36 #endif /* HAVE_STDBOOL_H */
38 #define DEFAULT_TEXT_DOMAIN "sudo"
39 #include "sudo_gettext.h" /* must be included before sudo_compat.h */
41 #include "sudo_compat.h"
42 #include "sudo_fatal.h"
43 #include "sudo_queue.h"
44 #include "sudo_util.h"
45 #include "sudo_plugin.h"
47 #ifndef HAVE_GETADDRINFO
48 # include "compat/getaddrinfo.h"
51 struct sudo_fatal_callback {
52 SLIST_ENTRY(sudo_fatal_callback) entries;
55 SLIST_HEAD(sudo_fatal_callback_list, sudo_fatal_callback);
57 static struct sudo_fatal_callback_list callbacks = SLIST_HEAD_INITIALIZER(&callbacks);
58 static sudo_conv_t sudo_warn_conversation;
59 static bool (*sudo_warn_setlocale)(bool, int *);
60 static bool (*sudo_warn_setlocale_prev)(bool, int *);
62 static void warning(const char *errstr, const char *fmt, va_list ap);
67 struct sudo_fatal_callback *cb;
69 /* Run callbacks, removing them from the list as we go. */
70 while ((cb = SLIST_FIRST(&callbacks)) != NULL) {
71 SLIST_REMOVE_HEAD(&callbacks, entries);
78 sudo_fatal_nodebug_v1(const char *fmt, ...)
83 warning(strerror(errno), fmt, ap);
90 sudo_fatalx_nodebug_v1(const char *fmt, ...)
95 warning(NULL, fmt, ap);
102 sudo_vfatal_nodebug_v1(const char *fmt, va_list ap)
104 warning(strerror(errno), fmt, ap);
110 sudo_vfatalx_nodebug_v1(const char *fmt, va_list ap)
112 warning(NULL, fmt, ap);
118 sudo_warn_nodebug_v1(const char *fmt, ...)
123 warning(strerror(errno), fmt, ap);
128 sudo_warnx_nodebug_v1(const char *fmt, ...)
132 warning(NULL, fmt, ap);
137 sudo_vwarn_nodebug_v1(const char *fmt, va_list ap)
139 warning(strerror(errno), fmt, ap);
143 sudo_vwarnx_nodebug_v1(const char *fmt, va_list ap)
145 warning(NULL, fmt, ap);
149 sudo_gai_fatal_nodebug_v1(int errnum, const char *fmt, ...)
154 warning(gai_strerror(errnum), fmt, ap);
161 sudo_gai_vfatal_nodebug_v1(int errnum, const char *fmt, va_list ap)
163 warning(gai_strerror(errnum), fmt, ap);
169 sudo_gai_warn_nodebug_v1(int errnum, const char *fmt, ...)
174 warning(gai_strerror(errnum), fmt, ap);
179 sudo_gai_vwarn_nodebug_v1(int errnum, const char *fmt, va_list ap)
181 warning(gai_strerror(errnum), fmt, ap);
185 warning(const char *errstr, const char *fmt, va_list ap)
189 /* Set user locale if setter was specified. */
190 if (sudo_warn_setlocale != NULL)
191 sudo_warn_setlocale(false, &cookie);
193 if (sudo_warn_conversation != NULL) {
194 struct sudo_conv_message msgs[6];
195 char static_buf[1024], *buf = static_buf;
198 /* Use conversation function. */
199 msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
200 msgs[nmsgs++].msg = getprogname();
205 /* Use static buffer if possible, else dynamic. */
207 buflen = vsnprintf(static_buf, sizeof(static_buf), fmt, ap2);
209 if (buflen >= (int)sizeof(static_buf)) {
210 buf = malloc(++buflen);
212 (void)vsnprintf(buf, buflen, fmt, ap);
216 msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
217 msgs[nmsgs++].msg = ": ";
218 msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
219 msgs[nmsgs++].msg = buf;
221 if (errstr != NULL) {
222 msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
223 msgs[nmsgs++].msg = ": ";
224 msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
225 msgs[nmsgs++].msg = errstr;
227 msgs[nmsgs].msg_type = SUDO_CONV_ERROR_MSG;
228 msgs[nmsgs++].msg = "\n";
229 sudo_warn_conversation(nmsgs, msgs, NULL, NULL);
230 if (buf != static_buf)
233 /* Write to the standard error. */
234 fputs(getprogname(), stderr);
237 vfprintf(stderr, fmt, ap);
239 if (errstr != NULL) {
241 fputs(errstr, stderr);
246 /* Restore old locale as needed. */
247 if (sudo_warn_setlocale != NULL)
248 sudo_warn_setlocale(true, &cookie);
252 * Register a callback to be run when sudo_fatal()/sudo_fatalx() is called.
255 sudo_fatal_callback_register_v1(sudo_fatal_callback_t func)
257 struct sudo_fatal_callback *cb;
259 /* Do not register the same callback twice. */
260 SLIST_FOREACH(cb, &callbacks, entries) {
261 if (func == cb->func)
262 return -1; /* dupe! */
265 /* Allocate and insert new callback. */
266 cb = malloc(sizeof(*cb));
270 SLIST_INSERT_HEAD(&callbacks, cb, entries);
276 * Deregister a sudo_fatal()/sudo_fatalx() callback.
279 sudo_fatal_callback_deregister_v1(sudo_fatal_callback_t func)
281 struct sudo_fatal_callback *cb, **prev;
283 /* Search for callback and remove if found, dupes are not allowed. */
284 SLIST_FOREACH_PREVPTR(cb, prev, &callbacks, entries) {
285 if (cb->func == func) {
286 if (cb == SLIST_FIRST(&callbacks))
287 SLIST_REMOVE_HEAD(&callbacks, entries);
289 SLIST_REMOVE_AFTER(*prev, entries);
299 * Set the conversation function to use for output insteaf of the
300 * standard error. If conv is NULL, switch back to standard error.
303 sudo_warn_set_conversation_v1(sudo_conv_t conv)
305 sudo_warn_conversation = conv;
309 * Set the locale function so the plugin can use a non-default
310 * locale for user warnings.
313 sudo_warn_set_locale_func_v1(bool (*func)(bool, int *))
315 sudo_warn_setlocale_prev = sudo_warn_setlocale;
316 sudo_warn_setlocale = func;
319 #ifdef HAVE_LIBINTL_H
321 sudo_warn_gettext_v1(const char *domainname, const char *msgid)
326 /* Set user locale if setter was specified. */
327 if (sudo_warn_setlocale != NULL)
328 sudo_warn_setlocale(false, &cookie);
330 msg = dgettext(domainname, msgid);
332 /* Restore old locale as needed. */
333 if (sudo_warn_setlocale != NULL)
334 sudo_warn_setlocale(true, &cookie);
338 #endif /* HAVE_LIBINTL_H */