Merge "asterisk.c: When astcanary dies on linux, reset priority on all threads."
[asterisk/asterisk.git] / main / astfd.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, Digium, Inc.
5  *
6  * Tilghman Lesher <tlesher@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Debugging routines for file descriptor leaks
22  *
23  * \author Tilghman Lesher \verbatim <tlesher@digium.com> \endverbatim
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 #ifdef DEBUG_FD_LEAKS
33
34 ASTERISK_REGISTER_FILE()
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <stddef.h>
39 #include <time.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42
43 #include "asterisk/cli.h"
44 #include "asterisk/logger.h"
45 #include "asterisk/options.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/strings.h"
48 #include "asterisk/unaligned.h"
49 #include "asterisk/localtime.h"
50 #include "asterisk/time.h"
51
52 static struct fdleaks {
53         const char *callname;
54         int line;
55         unsigned int isopen:1;
56         char file[40];
57         char function[25];
58         char callargs[60];
59         struct timeval now;
60 } fdleaks[1024] = { { "", }, };
61
62 /* COPY does ast_copy_string(dst, src, sizeof(dst)), except:
63  * - if it doesn't fit, it copies the value after the slash
64  *   (possibly truncated)
65  * - if there is no slash, it copies the value with the head
66  *   truncated */
67 #define COPY(dst, src)                                             \
68         do {                                                           \
69                 int dlen = sizeof(dst), slen = strlen(src);                \
70                 if (slen + 1 > dlen) {                                     \
71                         const char *slash = strrchr(src, '/');                 \
72                         if (slash) {                                           \
73                                 ast_copy_string(dst, slash + 1, dlen);             \
74                         } else {                                               \
75                                 ast_copy_string(dst, src + slen - dlen + 1, dlen); \
76                         }                                                      \
77                 } else {                                                   \
78                         ast_copy_string(dst, src, dlen);                       \
79                 }                                                          \
80         } while (0)
81
82 #define STORE_COMMON(offset, name, ...)     \
83         do { \
84                 struct fdleaks *tmp = &fdleaks[offset]; \
85                 tmp->now = ast_tvnow(); \
86                 COPY(tmp->file, file);      \
87                 tmp->line = line;           \
88                 COPY(tmp->function, func);  \
89                 tmp->callname = name;       \
90                 snprintf(tmp->callargs, sizeof(tmp->callargs), __VA_ARGS__); \
91                 tmp->isopen = 1;            \
92         } while (0)
93
94 #undef open
95 int __ast_fdleak_open(const char *file, int line, const char *func, const char *path, int flags, ...)
96 {
97         int res;
98         va_list ap;
99         int mode;
100
101         if (flags & O_CREAT) {
102                 va_start(ap, flags);
103                 mode = va_arg(ap, int);
104                 va_end(ap);
105                 res = open(path, flags, mode);
106                 if (res > -1 && res < ARRAY_LEN(fdleaks)) {
107                         char sflags[80];
108                         snprintf(sflags, sizeof(sflags), "O_CREAT%s%s%s%s%s%s%s%s",
109                                 flags & O_APPEND ? "|O_APPEND" : "",
110                                 flags & O_EXCL ? "|O_EXCL" : "",
111                                 flags & O_NONBLOCK ? "|O_NONBLOCK" : "",
112                                 flags & O_TRUNC ? "|O_TRUNC" : "",
113                                 flags & O_RDWR ? "|O_RDWR" : "",
114 #if O_RDONLY == 0
115                                 !(flags & (O_WRONLY | O_RDWR)) ? "|O_RDONLY" : "",
116 #else
117                                 flags & O_RDONLY ? "|O_RDONLY" : "",
118 #endif
119                                 flags & O_WRONLY ? "|O_WRONLY" : "",
120                                 "");
121                         flags &= ~(O_CREAT | O_APPEND | O_EXCL | O_NONBLOCK | O_TRUNC | O_RDWR | O_RDONLY | O_WRONLY);
122                         if (flags) {
123                                 STORE_COMMON(res, "open", "\"%s\",%s|%d,%04o", path, sflags, flags, mode);
124                         } else {
125                                 STORE_COMMON(res, "open", "\"%s\",%s,%04o", path, sflags, mode);
126                         }
127                 }
128         } else {
129                 res = open(path, flags);
130                 if (res > -1 && res < ARRAY_LEN(fdleaks)) {
131                         STORE_COMMON(res, "open", "\"%s\",%d", path, flags);
132                 }
133         }
134         return res;
135 }
136
137 #undef pipe
138 int __ast_fdleak_pipe(int *fds, const char *file, int line, const char *func)
139 {
140         int i, res = pipe(fds);
141         if (res) {
142                 return res;
143         }
144         for (i = 0; i < 2; i++) {
145                 if (fds[i] > -1 && fds[i] < ARRAY_LEN(fdleaks)) {
146                         STORE_COMMON(fds[i], "pipe", "{%d,%d}", fds[0], fds[1]);
147                 }
148         }
149         return 0;
150 }
151
152 #undef socket
153 int __ast_fdleak_socket(int domain, int type, int protocol, const char *file, int line, const char *func)
154 {
155         char sdomain[20], stype[20], *sproto = NULL;
156         struct protoent *pe;
157         int res = socket(domain, type, protocol);
158         if (res < 0 || res >= ARRAY_LEN(fdleaks)) {
159                 return res;
160         }
161
162         if ((pe = getprotobynumber(protocol))) {
163                 sproto = pe->p_name;
164         }
165
166         if (domain == PF_UNIX) {
167                 ast_copy_string(sdomain, "PF_UNIX", sizeof(sdomain));
168         } else if (domain == PF_INET) {
169                 ast_copy_string(sdomain, "PF_INET", sizeof(sdomain));
170         } else {
171                 snprintf(sdomain, sizeof(sdomain), "%d", domain);
172         }
173
174         if (type == SOCK_DGRAM) {
175                 ast_copy_string(stype, "SOCK_DGRAM", sizeof(stype));
176                 if (protocol == 0) {
177                         sproto = "udp";
178                 }
179         } else if (type == SOCK_STREAM) {
180                 ast_copy_string(stype, "SOCK_STREAM", sizeof(stype));
181                 if (protocol == 0) {
182                         sproto = "tcp";
183                 }
184         } else {
185                 snprintf(stype, sizeof(stype), "%d", type);
186         }
187
188         if (sproto) {
189                 STORE_COMMON(res, "socket", "%s,%s,\"%s\"", sdomain, stype, sproto);
190         } else {
191                 STORE_COMMON(res, "socket", "%s,%s,\"%d\"", sdomain, stype, protocol);
192         }
193         return res;
194 }
195
196 #undef close
197 int __ast_fdleak_close(int fd)
198 {
199         int res = close(fd);
200         if (!res && fd > -1 && fd < ARRAY_LEN(fdleaks)) {
201                 fdleaks[fd].isopen = 0;
202         }
203         return res;
204 }
205
206 #undef fopen
207 FILE *__ast_fdleak_fopen(const char *path, const char *mode, const char *file, int line, const char *func)
208 {
209         FILE *res = fopen(path, mode);
210         int fd;
211         if (!res) {
212                 return res;
213         }
214         fd = fileno(res);
215         if (fd > -1 && fd < ARRAY_LEN(fdleaks)) {
216                 STORE_COMMON(fd, "fopen", "\"%s\",\"%s\"", path, mode);
217         }
218         return res;
219 }
220
221 #undef fclose
222 int __ast_fdleak_fclose(FILE *ptr)
223 {
224         int fd, res;
225         if (!ptr) {
226                 return fclose(ptr);
227         }
228
229         fd = fileno(ptr);
230         if ((res = fclose(ptr)) || fd < 0 || fd >= ARRAY_LEN(fdleaks)) {
231                 return res;
232         }
233         fdleaks[fd].isopen = 0;
234         return res;
235 }
236
237 #undef dup2
238 int __ast_fdleak_dup2(int oldfd, int newfd, const char *file, int line, const char *func)
239 {
240         int res = dup2(oldfd, newfd);
241         if (res < 0 || res >= ARRAY_LEN(fdleaks)) {
242                 return res;
243         }
244         /* On success, newfd will be closed automatically if it was already
245          * open. We don't need to mention anything about that, we're updating
246          * the value anway. */
247         STORE_COMMON(res, "dup2", "%d,%d", oldfd, newfd); /* res == newfd */
248         return res;
249 }
250
251 #undef dup
252 int __ast_fdleak_dup(int oldfd, const char *file, int line, const char *func)
253 {
254         int res = dup(oldfd);
255         if (res < 0 || res >= ARRAY_LEN(fdleaks)) {
256                 return res;
257         }
258         STORE_COMMON(res, "dup2", "%d", oldfd);
259         return res;
260 }
261
262 static char *handle_show_fd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
263 {
264         int i;
265         char line[24];
266         struct rlimit rl;
267         switch (cmd) {
268         case CLI_INIT:
269                 e->command = "core show fd";
270                 e->usage =
271                         "Usage: core show fd\n"
272                         "       List all file descriptors currently in use and where\n"
273                         "       each was opened, and with what command.\n";
274                 return NULL;
275         case CLI_GENERATE:
276                 return NULL;
277         }
278         getrlimit(RLIMIT_NOFILE, &rl);
279         if (rl.rlim_cur == RLIM_INFINITY) {
280                 ast_copy_string(line, "unlimited", sizeof(line));
281         } else {
282                 snprintf(line, sizeof(line), "%d", (int) rl.rlim_cur);
283         }
284         ast_cli(a->fd, "Current maxfiles: %s\n", line);
285         for (i = 0; i < ARRAY_LEN(fdleaks); i++) {
286                 if (fdleaks[i].isopen) {
287                         struct ast_tm tm = {0};
288                         char datestring[256];
289
290                         ast_localtime(&fdleaks[i].now, &tm, NULL);
291                         ast_strftime(datestring, sizeof(datestring), "%F %T", &tm);
292                         snprintf(line, sizeof(line), "%d", fdleaks[i].line);
293                         ast_cli(a->fd, "%5d [%s] %22s:%-7.7s (%-25s): %s(%s)\n", i, datestring, fdleaks[i].file, line, fdleaks[i].function, fdleaks[i].callname, fdleaks[i].callargs);
294                 }
295         }
296         return CLI_SUCCESS;
297 }
298
299 static struct ast_cli_entry cli_show_fd = AST_CLI_DEFINE(handle_show_fd, "Show open file descriptors");
300
301 static void fd_shutdown(void)
302 {
303         ast_cli_unregister(&cli_show_fd);
304 }
305
306 int ast_fd_init(void)
307 {
308         ast_register_cleanup(fd_shutdown);
309         return ast_cli_register(&cli_show_fd);
310 }
311
312 #else  /* !defined(DEBUG_FD_LEAKS) */
313 int ast_fd_init(void)
314 {
315         return 0;
316 }
317 #endif /* defined(DEBUG_FD_LEAKS) */
318