summaryrefslogtreecommitdiff
path: root/shared/nm-glib-aux/nm-errno.c
blob: d4ebb866f1f04c9163f8929a97c4bae6e505ec71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: LGPL-2.1+
/*
 * Copyright (C) 2018 Red Hat, Inc.
 */

#include "nm-default.h"

#include "nm-errno.h"

#include <pthread.h>

/*****************************************************************************/

static NM_UTILS_LOOKUP_STR_DEFINE(
    _geterror,
#if 0
	enum _NMErrno,
#else
    int,
#endif
    NM_UTILS_LOOKUP_DEFAULT(NULL),

    NM_UTILS_LOOKUP_STR_ITEM(NME_ERRNO_SUCCESS, "NME_ERRNO_SUCCESS"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_ERRNO_OUT_OF_RANGE, "NME_ERRNO_OUT_OF_RANGE"),

    NM_UTILS_LOOKUP_STR_ITEM(NME_UNSPEC, "NME_UNSPEC"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_BUG, "NME_BUG"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"),

    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_NL_NOADDR, "NME_NL_NOADDR"),

    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NOT_FOUND, "not-found"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_EXISTS, "exists"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_WRONG_TYPE, "wrong-type"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NOT_SLAVE, "not-slave"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NO_FIRMWARE, "no-firmware"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_OPNOTSUPP, "not-supported"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_NETLINK, "netlink"),
    NM_UTILS_LOOKUP_STR_ITEM(NME_PL_CANT_SET_MTU, "cant-set-mtu"),

    NM_UTILS_LOOKUP_ITEM_IGNORE(_NM_ERRNO_MININT),
    NM_UTILS_LOOKUP_ITEM_IGNORE(_NM_ERRNO_RESERVED_LAST_PLUS_1), );

/**
 * nm_strerror():
 * @nmerr: the NetworkManager specific errno to be converted
 *   to string.
 *
 * NetworkManager specific error numbers reserve a range in "errno.h" with
 * our own defines. For numbers that don't fall into this range, the numbers
 * are identical to the common error numbers.
 *
 * Identical to strerror(), g_strerror(), nm_strerror_native() for error numbers
 * that are not in the reserved range of NetworkManager specific errors.
 *
 * Returns: (transfer none): the string representation of the error number.
 */
const char *
nm_strerror(int nmerr)
{
    const char *s;

    nmerr = nm_errno(nmerr);

    if (nmerr >= _NM_ERRNO_RESERVED_FIRST) {
        s = _geterror(nmerr);
        if (s)
            return s;
    }
    return nm_strerror_native(nmerr);
}

/*****************************************************************************/

/**
 * nm_strerror_native_r:
 * @errsv: the errno to convert to string.
 * @buf: the output buffer where to write the string to.
 * @buf_size: the length of buffer.
 *
 * This is like strerror_r(), with one difference: depending on the
 * locale, the returned string is guaranteed to be valid UTF-8.
 * Also, there is some confusion as to whether to use glibc's
 * strerror_r() or the POXIX/XSI variant. This is abstracted
 * by the function.
 *
 * Note that the returned buffer may also be a statically allocated
 * buffer, and not the input buffer @buf. Consequently, the returned
 * string may be longer than @buf_size.
 *
 * Returns: (transfer none): a NUL terminated error message. This is either a static
 *   string (that is never freed), or the provided @buf argument.
 */
const char *
nm_strerror_native_r(int errsv, char *buf, gsize buf_size)
{
    char *buf2;

    nm_assert(buf);
    nm_assert(buf_size > 0);

#if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE
    /* XSI-compliant */
    {
        int errno_saved = errno;

        if (strerror_r(errsv, buf, buf_size) != 0) {
            g_snprintf(buf, buf_size, "Unspecified errno %d", errsv);
            errno = errno_saved;
        }
        buf2 = buf;
    }
#else
    /* GNU-specific */
    buf2 = strerror_r(errsv, buf, buf_size);
#endif

    /* like g_strerror(), ensure that the error message is UTF-8. */
    if (!g_get_charset(NULL) && !g_utf8_validate(buf2, -1, NULL)) {
        gs_free char *msg = NULL;

        msg = g_locale_to_utf8(buf2, -1, NULL, NULL, NULL);
        if (msg) {
            g_strlcpy(buf, msg, buf_size);
            buf2 = buf;
        }
    }

    return buf2;
}

/**
 * nm_strerror_native:
 * @errsv: the errno integer from <errno.h>
 *
 * Like strerror(), but strerror() is not thread-safe and not guaranteed
 * to be UTF-8.
 *
 * g_strerror() is a thread-safe variant of strerror(), however it caches
 * all returned strings in a dictionary. That means, using this on untrusted
 * error numbers can result in this cache to grow without limits.
 *
 * Instead, return a tread-local buffer. This way, it's thread-safe.
 *
 * There is a downside to this: subsequent calls of nm_strerror_native()
 * overwrite the error message.
 *
 * Returns: (transfer none): the text representation of the error number.
 */
const char *
nm_strerror_native(int errsv)
{
    static _nm_thread_local char *buf_static = NULL;
    char *                        buf;

    buf = buf_static;
    if (G_UNLIKELY(!buf)) {
        int           errno_saved = errno;
        pthread_key_t key;

        buf        = g_malloc(NM_STRERROR_BUFSIZE);
        buf_static = buf;

        if (pthread_key_create(&key, g_free) != 0 || pthread_setspecific(key, buf) != 0) {
            /* Failure. We will leak the buffer when the thread exits.
			 *
			 * Nothing we can do about it really. For Debug builds we fail with an assertion. */
            nm_assert_not_reached();
        }
        errno = errno_saved;
    }

    return nm_strerror_native_r(errsv, buf, NM_STRERROR_BUFSIZE);
}