diff options
Diffstat (limited to 'src/monkey/connect.c')
-rw-r--r-- | src/monkey/connect.c | 884 |
1 files changed, 884 insertions, 0 deletions
diff --git a/src/monkey/connect.c b/src/monkey/connect.c new file mode 100644 index 000000000..eaca967da --- /dev/null +++ b/src/monkey/connect.c | |||
@@ -0,0 +1,884 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004-2009 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Connect. | ||
7 | Comments: | ||
8 | This module handles the dialog with gdb, including starting and stopping | ||
9 | gdb.@p | ||
10 | |||
11 | GDB Bug workaround for "file -readnow": I tried to workaround a bug using | ||
12 | it but looks like this option also have bugs!!!! so I have to use the | ||
13 | command line option --readnow. | ||
14 | It also have a bug!!!! when the binary is changed and gdb must reload it | ||
15 | this option is ignored. So it looks like we have no solution but 3 gdb bugs | ||
16 | in a row. | ||
17 | |||
18 | ***************************************************************************/ | ||
19 | |||
20 | #define _GNU_SOURCE | ||
21 | #include <sys/types.h> | ||
22 | #include <unistd.h> | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <sys/wait.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <string.h> | ||
28 | #include <stdarg.h> | ||
29 | #include <limits.h> | ||
30 | #include <errno.h> | ||
31 | #include <signal.h> | ||
32 | #include <sys/stat.h> | ||
33 | #include <sys/time.h> | ||
34 | #include "mi_gdb.h" | ||
35 | |||
36 | #ifndef TEMP_FAILURE_RETRY | ||
37 | #define TEMP_FAILURE_RETRY(a) (a) | ||
38 | #endif | ||
39 | |||
40 | int mi_error=MI_OK; | ||
41 | char *mi_error_from_gdb=NULL; | ||
42 | static char *gdb_exe=NULL; | ||
43 | static char *xterm_exe=NULL; | ||
44 | static char *gdb_start=NULL; | ||
45 | static char *gdb_conn=NULL; | ||
46 | static char *main_func=NULL; | ||
47 | static char disable_psym_search_workaround=0; | ||
48 | |||
49 | mi_h *mi_alloc_h() | ||
50 | { | ||
51 | mi_h *h=(mi_h *)calloc(1,sizeof(mi_h)); | ||
52 | if (!h) | ||
53 | { | ||
54 | mi_error=MI_OUT_OF_MEMORY; | ||
55 | return NULL; | ||
56 | } | ||
57 | h->to_gdb[0]=h->to_gdb[1]=h->from_gdb[0]=h->from_gdb[1]=-1; | ||
58 | h->pid=-1; | ||
59 | return h; | ||
60 | } | ||
61 | |||
62 | int mi_check_running_pid(pid_t pid) | ||
63 | { | ||
64 | int status; | ||
65 | |||
66 | if (pid<=0) | ||
67 | return 0; | ||
68 | /* If waitpid returns the number of our child means it communicated | ||
69 | to as a termination status. */ | ||
70 | if (waitpid(pid,&status,WNOHANG)==pid) | ||
71 | { | ||
72 | pid=0; | ||
73 | return 0; | ||
74 | } | ||
75 | return 1; | ||
76 | } | ||
77 | |||
78 | int mi_check_running(mi_h *h) | ||
79 | { | ||
80 | return !h->died && mi_check_running_pid(h->pid); | ||
81 | } | ||
82 | |||
83 | void mi_kill_child(pid_t pid) | ||
84 | { | ||
85 | kill(pid,SIGTERM); | ||
86 | usleep(100000); | ||
87 | if (mi_check_running_pid(pid)) | ||
88 | { | ||
89 | int status; | ||
90 | kill(pid,SIGKILL); | ||
91 | waitpid(pid,&status,0); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | void mi_free_h(mi_h **handle) | ||
96 | { | ||
97 | mi_h *h=*handle; | ||
98 | if (h->to_gdb[0]>=0) | ||
99 | close(h->to_gdb[0]); | ||
100 | if (h->to) | ||
101 | fclose(h->to); | ||
102 | else if (h->to_gdb[1]>=0) | ||
103 | close(h->to_gdb[1]); | ||
104 | if (h->from) | ||
105 | fclose(h->from); | ||
106 | else if (h->from_gdb[0]>=0) | ||
107 | close(h->from_gdb[0]); | ||
108 | if (h->from_gdb[1]>=0) | ||
109 | close(h->from_gdb[1]); | ||
110 | if (mi_check_running(h)) | ||
111 | {/* GDB is running! */ | ||
112 | mi_kill_child(h->pid); | ||
113 | } | ||
114 | if (h->line) | ||
115 | free(h->line); | ||
116 | mi_free_output(h->po); | ||
117 | free(h->catched_console); | ||
118 | free(h); | ||
119 | *handle=NULL; | ||
120 | } | ||
121 | |||
122 | void mi_set_nonblk(int h) | ||
123 | { | ||
124 | int flf; | ||
125 | flf=fcntl(h,F_GETFL,0); | ||
126 | flf=flf | O_NONBLOCK; | ||
127 | fcntl(h,F_SETFL,flf); | ||
128 | } | ||
129 | |||
130 | int mi_getline(mi_h *h) | ||
131 | { | ||
132 | char c; | ||
133 | |||
134 | while (read(h->from_gdb[0],&c,1)==1) | ||
135 | { | ||
136 | if (h->lread>=h->llen) | ||
137 | { | ||
138 | h->llen=h->lread+128; | ||
139 | h->line=(char *)realloc(h->line,h->llen); | ||
140 | if (!h->line) | ||
141 | { | ||
142 | h->llen=0; | ||
143 | h->lread=0; | ||
144 | return -1; | ||
145 | } | ||
146 | } | ||
147 | if (c=='\n') | ||
148 | { | ||
149 | int ret=h->lread; | ||
150 | h->line[ret]=0; | ||
151 | h->lread=0; | ||
152 | return ret; | ||
153 | } | ||
154 | h->line[h->lread]=c; | ||
155 | h->lread++; | ||
156 | } | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | char *get_cstr(mi_output *o) | ||
161 | { | ||
162 | if (!o->c || o->c->type!=t_const) | ||
163 | return NULL; | ||
164 | return o->c->v.cstr; | ||
165 | } | ||
166 | |||
167 | int mi_get_response(mi_h *h) | ||
168 | { | ||
169 | int l=mi_getline(h); | ||
170 | if (!l) | ||
171 | return 0; | ||
172 | |||
173 | if (h->from_gdb_echo) | ||
174 | h->from_gdb_echo(h->line,h->from_gdb_echo_data); | ||
175 | if (strncmp(h->line,"(gdb)",5)==0) | ||
176 | {/* End of response. */ | ||
177 | return 1; | ||
178 | } | ||
179 | else | ||
180 | {/* Add to the response. */ | ||
181 | mi_output *o; | ||
182 | int add=1, is_exit=0; | ||
183 | o=mi_parse_gdb_output(h->line); | ||
184 | |||
185 | if (!o) | ||
186 | return 0; | ||
187 | /* Tunneled streams callbacks. */ | ||
188 | if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_STREAM) | ||
189 | { | ||
190 | char *aux; | ||
191 | add=0; | ||
192 | switch (o->sstype) | ||
193 | { | ||
194 | case MI_SST_CONSOLE: | ||
195 | aux=get_cstr(o); | ||
196 | if (h->console) | ||
197 | h->console(aux,h->console_data); | ||
198 | if (h->catch_console && aux) | ||
199 | { | ||
200 | h->catch_console--; | ||
201 | if (!h->catch_console) | ||
202 | { | ||
203 | free(h->catched_console); | ||
204 | h->catched_console=strdup(aux); | ||
205 | } | ||
206 | } | ||
207 | break; | ||
208 | case MI_SST_TARGET: | ||
209 | /* This one seems to be useless. */ | ||
210 | if (h->target) | ||
211 | h->target(get_cstr(o),h->target_data); | ||
212 | break; | ||
213 | case MI_SST_LOG: | ||
214 | if (h->log) | ||
215 | h->log(get_cstr(o),h->log_data); | ||
216 | break; | ||
217 | } | ||
218 | } | ||
219 | else if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_ASYNC) | ||
220 | { | ||
221 | if (h->async) | ||
222 | h->async(o,h->async_data); | ||
223 | } | ||
224 | else if (o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_ERROR) | ||
225 | {/* Error from gdb, record it. */ | ||
226 | mi_error=MI_FROM_GDB; | ||
227 | free(mi_error_from_gdb); | ||
228 | mi_error_from_gdb=NULL; | ||
229 | if (o->c && strcmp(o->c->var,"msg")==0 && o->c->type==t_const) | ||
230 | mi_error_from_gdb=strdup(o->c->v.cstr); | ||
231 | } | ||
232 | is_exit=(o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_EXIT); | ||
233 | /* Add to the list of responses. */ | ||
234 | if (add) | ||
235 | { | ||
236 | if (h->last) | ||
237 | h->last->next=o; | ||
238 | else | ||
239 | h->po=o; | ||
240 | h->last=o; | ||
241 | } | ||
242 | else | ||
243 | mi_free_output(o); | ||
244 | /* Exit RR means gdb exited, we won't get a new prompt ;-) */ | ||
245 | if (is_exit) | ||
246 | return 1; | ||
247 | } | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | mi_output *mi_retire_response(mi_h *h) | ||
253 | { | ||
254 | mi_output *ret=h->po; | ||
255 | h->po=h->last=NULL; | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | mi_output *mi_get_response_blk(mi_h *h) | ||
260 | { | ||
261 | int r; | ||
262 | /* Sometimes gdb dies. */ | ||
263 | if (!mi_check_running(h)) | ||
264 | { | ||
265 | h->died=1; | ||
266 | mi_error=MI_GDB_DIED; | ||
267 | return NULL; | ||
268 | } | ||
269 | do | ||
270 | { | ||
271 | if (1) | ||
272 | { | ||
273 | /* | ||
274 | That's a must. If we just keep trying to read and failing things | ||
275 | become really sloooowwww. Instead we try and if it fails we wait | ||
276 | until something is available. | ||
277 | TODO: Implement something with the time out, a callback to ask the | ||
278 | application is we have to wait or not could be a good thing. | ||
279 | */ | ||
280 | fd_set set; | ||
281 | struct timeval timeout; | ||
282 | int ret; | ||
283 | |||
284 | r=mi_get_response(h); | ||
285 | if (r) | ||
286 | return mi_retire_response(h); | ||
287 | |||
288 | FD_ZERO(&set); | ||
289 | FD_SET(h->from_gdb[0],&set); | ||
290 | timeout.tv_sec=h->time_out; | ||
291 | timeout.tv_usec=0; | ||
292 | ret=TEMP_FAILURE_RETRY(select(FD_SETSIZE,&set,NULL,NULL,&timeout)); | ||
293 | if (!ret) | ||
294 | { | ||
295 | if (!mi_check_running(h)) | ||
296 | { | ||
297 | h->died=1; | ||
298 | mi_error=MI_GDB_DIED; | ||
299 | return NULL; | ||
300 | } | ||
301 | if (h->time_out_cb) | ||
302 | ret=h->time_out_cb(h->time_out_cb_data); | ||
303 | if (!ret) | ||
304 | { | ||
305 | mi_error=MI_GDB_TIME_OUT; | ||
306 | return NULL; | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | else | ||
311 | { | ||
312 | r=mi_get_response(h); | ||
313 | if (r) | ||
314 | return mi_retire_response(h); | ||
315 | else | ||
316 | usleep(100); | ||
317 | } | ||
318 | } | ||
319 | while (!r); | ||
320 | |||
321 | return NULL; | ||
322 | } | ||
323 | |||
324 | void mi_send_commands(mi_h *h, const char *file) | ||
325 | { | ||
326 | FILE *f; | ||
327 | char b[PATH_MAX]; | ||
328 | |||
329 | //printf("File: %s\n",file); | ||
330 | if (!file) | ||
331 | return; | ||
332 | f=fopen(file,"rt"); | ||
333 | if (!f) | ||
334 | return; | ||
335 | while (!feof(f)) | ||
336 | { | ||
337 | if (fgets(b,PATH_MAX,f)) | ||
338 | { | ||
339 | //printf("Send: %s\n",b); | ||
340 | mi_send(h,b); | ||
341 | mi_res_simple_done(h); | ||
342 | } | ||
343 | } | ||
344 | fclose(f); | ||
345 | } | ||
346 | |||
347 | void mi_send_target_commands(mi_h *h) | ||
348 | { | ||
349 | mi_send_commands(h,gdb_conn); | ||
350 | } | ||
351 | |||
352 | /**[txh]******************************************************************** | ||
353 | |||
354 | Description: | ||
355 | Connect to a local copy of gdb. Note that the mi_h structure is something | ||
356 | similar to a "FILE *" for stdio. | ||
357 | |||
358 | Return: A new mi_h structure or NULL on error. | ||
359 | |||
360 | ***************************************************************************/ | ||
361 | |||
362 | mi_h *mi_connect_local() | ||
363 | { | ||
364 | mi_h *h; | ||
365 | const char *gdb=mi_get_gdb_exe(); | ||
366 | |||
367 | /* Start without error. */ | ||
368 | mi_error=MI_OK; | ||
369 | /* Verify we have a GDB binary. */ | ||
370 | if (access(gdb,X_OK)) | ||
371 | { | ||
372 | mi_error=MI_MISSING_GDB; | ||
373 | return NULL; | ||
374 | } | ||
375 | /* Alloc the handle structure. */ | ||
376 | h=mi_alloc_h(); | ||
377 | if (!h) | ||
378 | return h; | ||
379 | h->time_out=MI_DEFAULT_TIME_OUT; | ||
380 | /* Create the pipes to connect with the child. */ | ||
381 | if (pipe(h->to_gdb) || pipe(h->from_gdb)) | ||
382 | { | ||
383 | mi_error=MI_PIPE_CREATE; | ||
384 | mi_free_h(&h); | ||
385 | return NULL; | ||
386 | } | ||
387 | mi_set_nonblk(h->to_gdb[1]); | ||
388 | mi_set_nonblk(h->from_gdb[0]); | ||
389 | /* Associate streams to the file handles. */ | ||
390 | h->to=fdopen(h->to_gdb[1],"w"); | ||
391 | h->from=fdopen(h->from_gdb[0],"r"); | ||
392 | if (!h->to || !h->from) | ||
393 | { | ||
394 | mi_error=MI_PIPE_CREATE; | ||
395 | mi_free_h(&h); | ||
396 | return NULL; | ||
397 | } | ||
398 | /* Create the child. */ | ||
399 | h->pid=fork(); | ||
400 | if (h->pid==0) | ||
401 | {/* We are the child. */ | ||
402 | char *argv[5]; | ||
403 | /* Connect stdin/out to the pipes. */ | ||
404 | dup2(h->to_gdb[0],STDIN_FILENO); | ||
405 | dup2(h->from_gdb[1],STDOUT_FILENO); | ||
406 | /* Pass the control to gdb. */ | ||
407 | argv[0]=(char *)gdb; /* Is that OK? */ | ||
408 | argv[1]="--interpreter=mi"; | ||
409 | argv[2]="--quiet"; | ||
410 | argv[3]=disable_psym_search_workaround ? 0 : "--readnow"; | ||
411 | argv[4]=0; | ||
412 | execvp(argv[0],argv); | ||
413 | /* We get here only if exec failed. */ | ||
414 | _exit(127); | ||
415 | } | ||
416 | /* We are the parent. */ | ||
417 | if (h->pid==-1) | ||
418 | {/* Fork failed. */ | ||
419 | mi_error=MI_FORK; | ||
420 | mi_free_h(&h); | ||
421 | return NULL; | ||
422 | } | ||
423 | if (!mi_check_running(h)) | ||
424 | { | ||
425 | mi_error=MI_DEBUGGER_RUN; | ||
426 | mi_free_h(&h); | ||
427 | return NULL; | ||
428 | } | ||
429 | /* Wait for the prompt. */ | ||
430 | mi_get_response_blk(h); | ||
431 | /* Send the start-up commands */ | ||
432 | mi_send_commands(h,gdb_start); | ||
433 | |||
434 | return h; | ||
435 | } | ||
436 | |||
437 | /**[txh]******************************************************************** | ||
438 | |||
439 | Description: | ||
440 | Close connection. You should ask gdb to quit first @x{gmi_gdb_exit}. | ||
441 | |||
442 | ***************************************************************************/ | ||
443 | |||
444 | void mi_disconnect(mi_h *h) | ||
445 | { | ||
446 | mi_free_h(&h); | ||
447 | free(mi_error_from_gdb); | ||
448 | mi_error_from_gdb=NULL; | ||
449 | } | ||
450 | |||
451 | void mi_set_console_cb(mi_h *h, stream_cb cb, void *data) | ||
452 | { | ||
453 | h->console=cb; | ||
454 | h->console_data=data; | ||
455 | } | ||
456 | |||
457 | void mi_set_target_cb(mi_h *h, stream_cb cb, void *data) | ||
458 | { | ||
459 | h->target=cb; | ||
460 | h->target_data=data; | ||
461 | } | ||
462 | |||
463 | void mi_set_log_cb(mi_h *h, stream_cb cb, void *data) | ||
464 | { | ||
465 | h->log=cb; | ||
466 | h->log_data=data; | ||
467 | } | ||
468 | |||
469 | stream_cb mi_get_console_cb(mi_h *h, void **data) | ||
470 | { | ||
471 | if (data) | ||
472 | *data=h->console_data; | ||
473 | return h->console; | ||
474 | } | ||
475 | |||
476 | stream_cb mi_get_target_cb(mi_h *h, void **data) | ||
477 | { | ||
478 | if (data) | ||
479 | *data=h->target_data; | ||
480 | return h->target; | ||
481 | } | ||
482 | |||
483 | stream_cb mi_get_log_cb(mi_h *h, void **data) | ||
484 | { | ||
485 | if (data) | ||
486 | *data=h->log_data; | ||
487 | return h->log; | ||
488 | } | ||
489 | |||
490 | void mi_set_async_cb(mi_h *h, async_cb cb, void *data) | ||
491 | { | ||
492 | h->async=cb; | ||
493 | h->async_data=data; | ||
494 | } | ||
495 | |||
496 | async_cb mi_get_async_cb(mi_h *h, void **data) | ||
497 | { | ||
498 | if (data) | ||
499 | *data=h->async_data; | ||
500 | return h->async; | ||
501 | } | ||
502 | |||
503 | void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data) | ||
504 | { | ||
505 | h->to_gdb_echo=cb; | ||
506 | h->to_gdb_echo_data=data; | ||
507 | } | ||
508 | |||
509 | void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data) | ||
510 | { | ||
511 | h->from_gdb_echo=cb; | ||
512 | h->from_gdb_echo_data=data; | ||
513 | } | ||
514 | |||
515 | stream_cb mi_get_to_gdb_cb(mi_h *h, void **data) | ||
516 | { | ||
517 | if (data) | ||
518 | *data=h->to_gdb_echo_data; | ||
519 | return h->to_gdb_echo; | ||
520 | } | ||
521 | |||
522 | stream_cb mi_get_from_gdb_cb(mi_h *h, void **data) | ||
523 | { | ||
524 | if (data) | ||
525 | *data=h->from_gdb_echo_data; | ||
526 | return h->from_gdb_echo; | ||
527 | } | ||
528 | |||
529 | void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data) | ||
530 | { | ||
531 | h->time_out_cb=cb; | ||
532 | h->time_out_cb_data=data; | ||
533 | } | ||
534 | |||
535 | tm_cb mi_get_time_out_cb(mi_h *h, void **data) | ||
536 | { | ||
537 | if (data) | ||
538 | *data=h->time_out_cb_data; | ||
539 | return h->time_out_cb; | ||
540 | } | ||
541 | |||
542 | void mi_set_time_out(mi_h *h, int to) | ||
543 | { | ||
544 | h->time_out=to; | ||
545 | } | ||
546 | |||
547 | int mi_get_time_out(mi_h *h) | ||
548 | { | ||
549 | return h->time_out; | ||
550 | } | ||
551 | |||
552 | int mi_send(mi_h *h, const char *format, ...) | ||
553 | { | ||
554 | int ret; | ||
555 | char *str; | ||
556 | va_list argptr; | ||
557 | |||
558 | if (h->died) | ||
559 | return 0; | ||
560 | |||
561 | va_start(argptr,format); | ||
562 | ret=vasprintf(&str,format,argptr); | ||
563 | va_end(argptr); | ||
564 | fputs(str,h->to); | ||
565 | fflush(h->to); | ||
566 | if (h->to_gdb_echo) | ||
567 | h->to_gdb_echo(str,h->to_gdb_echo_data); | ||
568 | free(str); | ||
569 | |||
570 | return ret; | ||
571 | } | ||
572 | |||
573 | void mi_clean_up_globals() | ||
574 | { | ||
575 | free(gdb_exe); | ||
576 | gdb_exe=NULL; | ||
577 | free(xterm_exe); | ||
578 | xterm_exe=NULL; | ||
579 | free(gdb_start); | ||
580 | gdb_start=NULL; | ||
581 | free(gdb_conn); | ||
582 | gdb_conn=NULL; | ||
583 | free(main_func); | ||
584 | main_func=NULL; | ||
585 | } | ||
586 | |||
587 | void mi_register_exit() | ||
588 | { | ||
589 | static int registered=0; | ||
590 | if (!registered) | ||
591 | { | ||
592 | registered=1; | ||
593 | atexit(mi_clean_up_globals); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | void mi_set_gdb_exe(const char *name) | ||
598 | { | ||
599 | free(gdb_exe); | ||
600 | gdb_exe=name ? strdup(name) : NULL; | ||
601 | mi_register_exit(); | ||
602 | } | ||
603 | |||
604 | void mi_set_gdb_start(const char *name) | ||
605 | { | ||
606 | free(gdb_start); | ||
607 | gdb_start=name ? strdup(name) : NULL; | ||
608 | mi_register_exit(); | ||
609 | } | ||
610 | |||
611 | void mi_set_gdb_conn(const char *name) | ||
612 | { | ||
613 | free(gdb_conn); | ||
614 | gdb_conn=name ? strdup(name) : NULL; | ||
615 | mi_register_exit(); | ||
616 | } | ||
617 | |||
618 | static | ||
619 | char *mi_search_in_path(const char *file) | ||
620 | { | ||
621 | char *path, *pt, *r; | ||
622 | char test[PATH_MAX]; | ||
623 | struct stat st; | ||
624 | |||
625 | path=getenv("PATH"); | ||
626 | if (!path) | ||
627 | return NULL; | ||
628 | pt=strdup(path); | ||
629 | r=strtok(pt,":"); | ||
630 | while (r) | ||
631 | { | ||
632 | strcpy(test,r); | ||
633 | strcat(test,"/"); | ||
634 | strcat(test,file); | ||
635 | if (stat(test,&st)==0 && S_ISREG(st.st_mode)) | ||
636 | { | ||
637 | free(pt); | ||
638 | return strdup(test); | ||
639 | } | ||
640 | r=strtok(NULL,":"); | ||
641 | } | ||
642 | free(pt); | ||
643 | return NULL; | ||
644 | } | ||
645 | |||
646 | const char *mi_get_gdb_exe() | ||
647 | { | ||
648 | if (!gdb_exe) | ||
649 | {/* Look for gdb in path */ | ||
650 | gdb_exe=mi_search_in_path("gdb"); | ||
651 | if (!gdb_exe) | ||
652 | return "/usr/bin/gdb"; | ||
653 | } | ||
654 | return gdb_exe; | ||
655 | } | ||
656 | |||
657 | const char *mi_get_gdb_start() | ||
658 | { | ||
659 | return gdb_start; | ||
660 | } | ||
661 | |||
662 | const char *mi_get_gdb_conn() | ||
663 | { | ||
664 | return gdb_conn; | ||
665 | } | ||
666 | |||
667 | void mi_set_xterm_exe(const char *name) | ||
668 | { | ||
669 | free(xterm_exe); | ||
670 | xterm_exe=name ? strdup(name) : NULL; | ||
671 | mi_register_exit(); | ||
672 | } | ||
673 | |||
674 | const char *mi_get_xterm_exe() | ||
675 | { | ||
676 | if (!xterm_exe) | ||
677 | {/* Look for xterm in path */ | ||
678 | xterm_exe=mi_search_in_path("xterm"); | ||
679 | if (!xterm_exe) | ||
680 | return "/usr/bin/X11/xterm"; | ||
681 | } | ||
682 | return xterm_exe; | ||
683 | } | ||
684 | |||
685 | void mi_set_main_func(const char *name) | ||
686 | { | ||
687 | free(main_func); | ||
688 | main_func=name ? strdup(name) : NULL; | ||
689 | mi_register_exit(); | ||
690 | } | ||
691 | |||
692 | const char *mi_get_main_func() | ||
693 | { | ||
694 | if (main_func) | ||
695 | return main_func; | ||
696 | return "main"; | ||
697 | } | ||
698 | |||
699 | /**[txh]******************************************************************** | ||
700 | |||
701 | Description: | ||
702 | Opens a new xterm to be used by the child process to debug. | ||
703 | |||
704 | Return: A new mi_aux_term structure, you can use @x{gmi_end_aux_term} to | ||
705 | release it. | ||
706 | |||
707 | ***************************************************************************/ | ||
708 | |||
709 | mi_aux_term *gmi_start_xterm() | ||
710 | { | ||
711 | char nsh[14]="/tmp/shXXXXXX"; | ||
712 | char ntt[14]="/tmp/ttXXXXXX"; | ||
713 | const char *xterm; | ||
714 | struct stat st; | ||
715 | int hsh, htt=-1; | ||
716 | mi_aux_term *res=NULL; | ||
717 | FILE *f; | ||
718 | pid_t pid; | ||
719 | char buf[PATH_MAX]; | ||
720 | |||
721 | /* Verify we have an X terminal. */ | ||
722 | xterm=mi_get_xterm_exe(); | ||
723 | if (access(xterm,X_OK)) | ||
724 | { | ||
725 | mi_error=MI_MISSING_XTERM; | ||
726 | return NULL; | ||
727 | } | ||
728 | |||
729 | /* Create 2 temporals. */ | ||
730 | hsh=mkstemp(nsh); | ||
731 | if (hsh==-1) | ||
732 | { | ||
733 | mi_error=MI_CREATE_TEMPORAL; | ||
734 | return NULL; | ||
735 | } | ||
736 | htt=mkstemp(ntt); | ||
737 | if (htt==-1) | ||
738 | { | ||
739 | close(hsh); | ||
740 | unlink(nsh); | ||
741 | mi_error=MI_CREATE_TEMPORAL; | ||
742 | return NULL; | ||
743 | } | ||
744 | close(htt); | ||
745 | /* Create the script. */ | ||
746 | f=fdopen(hsh,"w"); | ||
747 | if (!f) | ||
748 | { | ||
749 | close(hsh); | ||
750 | unlink(nsh); | ||
751 | unlink(ntt); | ||
752 | mi_error=MI_CREATE_TEMPORAL; | ||
753 | return NULL; | ||
754 | } | ||
755 | fprintf(f,"#!/bin/sh\n"); | ||
756 | fprintf(f,"tty > %s\n",ntt); | ||
757 | fprintf(f,"rm %s\n",nsh); | ||
758 | fprintf(f,"sleep 365d\n"); | ||
759 | fclose(f); | ||
760 | /* Spawn xterm. */ | ||
761 | /* Create the child. */ | ||
762 | pid=fork(); | ||
763 | if (pid==0) | ||
764 | {/* We are the child. */ | ||
765 | char *argv[5]; | ||
766 | /* Pass the control to gdb. */ | ||
767 | argv[0]=(char *)mi_get_xterm_exe(); /* Is that ok? */ | ||
768 | argv[1]="-e"; | ||
769 | argv[2]="/bin/sh"; | ||
770 | argv[3]=nsh; | ||
771 | argv[4]=0; | ||
772 | execvp(argv[0],argv); | ||
773 | /* We get here only if exec failed. */ | ||
774 | unlink(nsh); | ||
775 | unlink(ntt); | ||
776 | _exit(127); | ||
777 | } | ||
778 | /* We are the parent. */ | ||
779 | if (pid==-1) | ||
780 | {/* Fork failed. */ | ||
781 | unlink(nsh); | ||
782 | unlink(ntt); | ||
783 | mi_error=MI_FORK; | ||
784 | return NULL; | ||
785 | } | ||
786 | /* Wait until the shell is deleted. */ | ||
787 | while (stat(nsh,&st)==0) | ||
788 | usleep(1000); | ||
789 | /* Try to read the tty name. */ | ||
790 | f=fopen(ntt,"rt"); | ||
791 | if (f) | ||
792 | { | ||
793 | if (fgets(buf,PATH_MAX,f)) | ||
794 | { | ||
795 | char *s; /* Strip the \n. */ | ||
796 | for (s=buf; *s && *s!='\n'; s++); | ||
797 | *s=0; | ||
798 | res=(mi_aux_term *)malloc(sizeof(mi_aux_term)); | ||
799 | if (res) | ||
800 | { | ||
801 | res->pid=pid; | ||
802 | res->tty=strdup(buf); | ||
803 | } | ||
804 | } | ||
805 | fclose(f); | ||
806 | } | ||
807 | unlink(ntt); | ||
808 | return res; | ||
809 | } | ||
810 | |||
811 | void mi_free_aux_term(mi_aux_term *t) | ||
812 | { | ||
813 | if (!t) | ||
814 | return; | ||
815 | free(t->tty); | ||
816 | free(t); | ||
817 | } | ||
818 | |||
819 | /**[txh]******************************************************************** | ||
820 | |||
821 | Description: | ||
822 | Closes the auxiliar terminal and releases the allocated memory. | ||
823 | |||
824 | ***************************************************************************/ | ||
825 | |||
826 | void gmi_end_aux_term(mi_aux_term *t) | ||
827 | { | ||
828 | if (!t) | ||
829 | return; | ||
830 | if (t->pid!=-1 && mi_check_running_pid(t->pid)) | ||
831 | mi_kill_child(t->pid); | ||
832 | mi_free_aux_term(t); | ||
833 | } | ||
834 | |||
835 | /**[txh]******************************************************************** | ||
836 | |||
837 | Description: | ||
838 | Forces the MI version. Currently the library can't detect it so you must | ||
839 | force it manually. GDB 5.x implemented MI v1 and 6.x v2. | ||
840 | |||
841 | ***************************************************************************/ | ||
842 | |||
843 | void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, | ||
844 | unsigned vMinor) | ||
845 | { | ||
846 | h->version=MI_VERSION2U(vMajor,vMiddle,vMinor); | ||
847 | } | ||
848 | |||
849 | /**[txh]******************************************************************** | ||
850 | |||
851 | Description: | ||
852 | Dis/Enables the @var{wa} workaround for a bug in gdb. | ||
853 | |||
854 | ***************************************************************************/ | ||
855 | |||
856 | void mi_set_workaround(unsigned wa, int enable) | ||
857 | { | ||
858 | switch (wa) | ||
859 | { | ||
860 | case MI_PSYM_SEARCH: | ||
861 | disable_psym_search_workaround=enable ? 0 : 1; | ||
862 | break; | ||
863 | } | ||
864 | } | ||
865 | |||
866 | /**[txh]******************************************************************** | ||
867 | |||
868 | Description: | ||
869 | Finds if the @var{wa} workaround for a bug in gdb is enabled. | ||
870 | |||
871 | Return: !=0 if enabled. | ||
872 | |||
873 | ***************************************************************************/ | ||
874 | |||
875 | int mi_get_workaround(unsigned wa) | ||
876 | { | ||
877 | switch (wa) | ||
878 | { | ||
879 | case MI_PSYM_SEARCH: | ||
880 | return disable_psym_search_workaround==0; | ||
881 | } | ||
882 | return 0; | ||
883 | } | ||
884 | |||