1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 /*
11  * The strerror() messages are copied because:
12  *
13  * 1) strerror() and strerror_r() functions are not Async-Signal-Safe,
14  *    therefore, they can not be used in signal handlers;
15  *
16  * 2) a direct sys_errlist[] array may be used instead of these functions,
17  *    but Linux linker warns about this usage:
18  *
19  * warning: `sys_errlist' is deprecated; use `strerror' or `strerror_r' instead
20  * warning: `sys_nerr' is deprecated; use `strerror' or `strerror_r' instead
21  *
22  *    causing false bug reports.
23  */
24 
25 static u_char *nxt_bootstrap_strerror(nxt_err_t err, u_char *errstr,
26     size_t size);
27 static u_char *nxt_runtime_strerror(nxt_err_t err, u_char *errstr, size_t size);
28 
29 
30 nxt_strerror_t     nxt_strerror = nxt_bootstrap_strerror;
31 static nxt_str_t   *nxt_sys_errlist;
32 static nxt_uint_t  nxt_sys_nerr;
33 
34 
35 nxt_int_t
nxt_strerror_start(void)36 nxt_strerror_start(void)
37 {
38     char        *msg;
39     u_char      *p;
40     size_t      size, length, n;
41     nxt_uint_t  err, invalid;
42 
43     /* The last entry. */
44     size = nxt_length("Unknown error");
45 
46     /*
47      * Linux has holes for error codes 41 and 58, so the loop
48      * stops only after 100 invalid codes in succession.
49      */
50 
51     for (invalid = 0; invalid < 100 && nxt_sys_nerr < 65536; nxt_sys_nerr++) {
52 
53         nxt_set_errno(0);
54         msg = strerror((int) nxt_sys_nerr);
55 
56         /*
57          * strerror() behaviour on passing invalid error code depends
58          * on OS and version:
59          *   Linux returns "Unknown error NN";
60          *   FreeBSD, NetBSD and OpenBSD return "Unknown error: NN"
61          *     and set errno to EINVAL;
62          *   Solaris 10 returns "Unknown error" and sets errno to EINVAL;
63          *   Solaris 9 returns "Unknown error";
64          *   Solaris 2 returns NULL;
65          *   MacOSX returns "Unknown error: NN";
66          *   AIX returns "Error NNN occurred.";
67          *   HP-UX returns "Unknown error" for invalid codes lesser than 250
68          *     or empty string for larger codes.
69          */
70 
71         if (msg == NULL) {
72             invalid++;
73             continue;
74         }
75 
76         length = nxt_strlen(msg);
77         size += length;
78 
79         if (length == 0  /* HP-UX empty strings. */
80             || nxt_errno == NXT_EINVAL
81             || nxt_memcmp(msg, "Unknown error", 13) == 0)
82         {
83             invalid++;
84             continue;
85         }
86 
87 #if (NXT_AIX)
88 
89         if (nxt_memcmp(msg, "Error ", 6) == 0
90             && nxt_memcmp(msg + length - 10, " occurred.", 9) == 0)
91         {
92             invalid++;
93             continue;
94         }
95 
96 #endif
97     }
98 
99     nxt_sys_nerr -= invalid;
100 
101     nxt_main_log_debug("sys_nerr: %d", nxt_sys_nerr);
102 
103     n = (nxt_sys_nerr + 1) * sizeof(nxt_str_t);
104 
105     nxt_sys_errlist = nxt_malloc(n + size);
106     if (nxt_sys_errlist == NULL) {
107         return NXT_ERROR;
108     }
109 
110     p = nxt_pointer_to(nxt_sys_errlist, n);
111 
112     for (err = 0; err < nxt_sys_nerr; err++) {
113         msg = strerror((int) err);
114         length = nxt_strlen(msg);
115 
116         nxt_sys_errlist[err].length = length;
117         nxt_sys_errlist[err].start = p;
118 
119         p = nxt_cpymem(p, msg, length);
120     }
121 
122     nxt_sys_errlist[err].length = 13;
123     nxt_sys_errlist[err].start = p;
124     nxt_memcpy(p, "Unknown error", 13);
125 
126     nxt_strerror = nxt_runtime_strerror;
127 
128     return NXT_OK;
129 }
130 
131 
132 static u_char *
nxt_bootstrap_strerror(nxt_err_t err,u_char * errstr,size_t size)133 nxt_bootstrap_strerror(nxt_err_t err, u_char *errstr, size_t size)
134 {
135     return nxt_cpystrn(errstr, (u_char *) strerror(err), size);
136 }
137 
138 
139 static u_char *
nxt_runtime_strerror(nxt_err_t err,u_char * errstr,size_t size)140 nxt_runtime_strerror(nxt_err_t err, u_char *errstr, size_t size)
141 {
142     nxt_str_t   *msg;
143     nxt_uint_t  n;
144 
145     n = nxt_min((nxt_uint_t) err, nxt_sys_nerr);
146 
147     msg = &nxt_sys_errlist[n];
148 
149     size = nxt_min(size, msg->length);
150 
151     return nxt_cpymem(errstr, msg->start, size);
152 }
153