1 /* sigsegv.c -- sigsegv handlers
2  *
3  * Copyright (c) 2003 Juan F. Codagnone <juam@users.sourceforge.net>
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 
24 #ifdef HAVE_CONFIG_H_
25   #include <config.h>
26 #endif
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <errno.h>
33 
34 #include <unistd.h>
35 
36 #ifdef HAVE_PTHREADS_H
37 #	include <pthread.h>
38 #endif
39 
40 /*
41  * http://www.gnu.org/manual/glibc-2.2.3/html_chapter/libc_33.html
42  */
43 #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1
44 #	define HAVE_BACKTRACE
45 #	include <execinfo.h>
46 #endif
47 #include <sys/wait.h>
48 
49 #include "crash_sigsegv.h"
50 
51 namespace Crash {
52 
53 static int (* print)(const char *format, ...) = NULL;
54 static int needs_cr = 1;
55 
sigsegv_set_print(int (* fnc)(const char * format,...),int _needs_cr)56 void *sigsegv_set_print( int (* fnc)(const char *format, ...), int _needs_cr)
57 {
58 	void *ret;
59 
60 	ret = &print;
61 	print = fnc;
62 	needs_cr = _needs_cr;
63 
64 	return ret;
65 }
66 
67 /**
68  * launchs gdb, and feeds myprint with the backtrace
69  */
dump_pid_son(pid_t pid,const char * binary,int full_bt,int (* myprint)(const char * format,...))70 static int dump_pid_son(pid_t pid, const char *binary, int full_bt,
71              int (* myprint)(const char *format, ...))
72 {
73 	char tmp[]="/tmp/mrbug-crash-XXXXXX";
74 	int ret = 0;
75 	int fd;
76 
77 	fd = mkstemp(tmp);
78 	if( fd == -1 )
79 	{
80 		(*myprint)("opening gdb command (tempory) file `%s'%s", tmp,
81 			   needs_cr ? "\n" : "");
82 		ret = -1;
83 	}
84 	else
85 	{
86 		char gdb_cmd[]="bt\nquit";
87 		char gdb_cmd_full[]="bt full\nquit";
88 		char cmd[128];
89 		FILE *fp;
90 
91 		ssize_t w = 0;
92 		if( full_bt )
93 			w = write(fd, gdb_cmd_full, strlen(gdb_cmd_full));
94 		else
95 			w = write(fd, gdb_cmd, strlen(gdb_cmd));
96 		if(w == -1) {
97 			(*myprint)("Write failed at crash_sigsegv.cpp:92(94)");
98 		}
99 		close(fd);
100 
101 		sprintf(cmd, "gdb -nw -n -batch -x \"%s\" %s %d", tmp, binary,
102 			pid);
103 		(*myprint)("trying to dump pid: %d (%s)...%s", pid, binary,
104 				   needs_cr ? "\n" : "");
105 
106 		fflush(NULL);
107 		fp = popen(cmd, "r");
108 		if( fp == NULL )
109 		{
110 			(*myprint)("err. couldn't exec `%s'%s", cmd,
111 				   needs_cr ? "\n" : "");
112 			ret = -1;
113 		}
114 		else
115 		{
116 			char buff[4096];
117 			size_t len;
118 
119 			while(fgets(buff, sizeof(buff), fp))
120 			{
121 				len = strlen(buff);
122 				if( buff[len-1] == '\n')
123 					buff[len-1]=0;
124 
125 				(*myprint)("%s%s", buff,needs_cr ? "\n" : "");
126 			}
127 			pclose(fp);
128 		}
129 		if( remove(tmp) == -1 )
130 			(*myprint)("removing `%s` (@;@)%s", tmp,
131 				   needs_cr ? "\n" : "");
132 	}
133 
134 	return ret;
135 }
136 
dump_pid(pid_t pid,const char * binary,int full_bt)137 static int dump_pid(pid_t pid, const char *binary, int full_bt )
138 {
139 	pid_t mpid;
140 	int (* myprint)(const char *format, ...);
141 
142 	myprint = print ? (int(*)(const char *format, ...))print : (int(*)(const char *format, ...))printf;
143 
144 	/*
145 	 * clone the process, so we don't make the bt bigger.
146 	 */
147 	mpid = fork();
148 	if( mpid == 0 )
149 	{
150 		dump_pid_son(pid, binary, full_bt,  myprint);
151 		exit(0);
152 	}
153 	else if( mpid == -1 )
154 		(*myprint)("lunching son: `%s' %s", strerror(errno),
155 			   needs_cr ? "\n" : "");
156 	else
157 	{
158 		/* father */
159 		int status;
160 
161 		alarm(0);
162 		waitpid(0, &status, 0);
163 		if( WIFEXITED(status) && WEXITSTATUS(status)==0 ) {
164 			//
165 		}
166 	}
167 
168 	return 0;
169 }
170 
171 /**
172  * get `pid`'s real path
173  *
174  * \param buff     buffer for the output
175  * \param nbuff    size of the buffer
176  * \param pid      pid processes id to use
177  *
178  * \note this function works only in linux
179  *
180  * \return the buffer
181  */
get_path_from_pid(char * buff,size_t nbuff,pid_t pid)182 static char *get_path_from_pid(char *buff, size_t nbuff, pid_t pid)
183 {
184 	char proc[256];
185 	char *ret = NULL;
186 	int n;
187 
188 	sprintf(proc, "/proc/%d/exe", pid);
189 	if( (n=readlink(proc, buff, nbuff)) == -1 )
190 		ret = NULL;
191 	else
192 	{
193 		buff[n]=0;
194 		ret = buff;
195 	}
196 
197 	return ret;
198 }
199 
sigsegv_libc_dump(int (* myprint)(const char * format,...))200 static void sigsegv_libc_dump( int (* myprint)(const char *format, ...) )
201 {
202 	void *array[48] = {0};
203 	unsigned short i;
204 	int n;
205 	char **res;
206 
207 #ifdef HAVE_BACKTRACE
208 	(*myprint)("Backtrace:%c", needs_cr ? "\n" : "");
209 	n  = backtrace(array, sizeof(array)/(sizeof(*array)));
210 	res =  backtrace_symbols(array, n);
211 	for (i = 0; i < n; i++)
212 		(*myprint)("%s%s", res[i], needs_cr ? "\n" : "");
213 
214 	(*myprint)("Attempting to generate core file%s",
215 		   needs_cr ? "" : "");
216 #endif
217 }
218 
sigsegv_handler_generic(int signal,int full_bt)219 static void sigsegv_handler_generic(int signal, int full_bt)
220 {
221 	(void)signal;
222 	char binary[2048];
223 	int pid = getpid();
224 	int (* myprint)(const char *format, ...);
225 
226 	myprint = print ? print : printf;
227 	if( get_path_from_pid(binary, sizeof(binary), pid) == NULL) {
228 		(*myprint)("pid %d does not seems to exist", pid);
229 	}
230 	else {
231 		(*myprint)("Segmentation Violation Detected.%s",
232 			   needs_cr ? "\n" : "");
233 		dump_pid(pid, binary, full_bt);
234 		sigsegv_libc_dump(myprint);
235 	}
236 
237 #ifdef HAVE_PTHREAD_H
238 	pthread_kill_other_threads_np();
239 #endif
240 	fflush(NULL);
241 	abort();
242 }
243 
sigsegv_handler_fnc(int signal)244 void sigsegv_handler_fnc(int signal)
245 {
246 	sigsegv_handler_generic(signal, 0);
247 }
248 
sigsegv_handler_bt_full_fnc(int signal)249 void sigsegv_handler_bt_full_fnc(int signal)
250 {
251 	sigsegv_handler_generic(signal, 1);
252 }
253 
254 }; // namespace Crash
255