Fix for segmentation fault.
[deliverable/binutils-gdb.git] / gdb / gdbserver / server.c
CommitLineData
c906108c 1/* Main code for remote server for GDB.
6f0f660e 2 Copyright (C) 1989, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2002, 2003, 2004,
dd24457d 3 2005, 2006
b6ba6518 4 Free Software Foundation, Inc.
c906108c 5
c5aa993b 6 This file is part of GDB.
c906108c 7
c5aa993b
JM
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
c906108c 12
c5aa993b
JM
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
c906108c 17
c5aa993b
JM
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
6f0f660e
EZ
20 Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA. */
c906108c
SS
22
23#include "server.h"
24
a9fa9f7d
DJ
25#include <unistd.h>
26#include <signal.h>
27#include <sys/wait.h>
28
a1928bad
DJ
29unsigned long cont_thread;
30unsigned long general_thread;
31unsigned long step_thread;
32unsigned long thread_from_wait;
33unsigned long old_thread_from_wait;
c906108c 34int extended_protocol;
0d62e5e8
DJ
35int server_waiting;
36
c906108c 37jmp_buf toplevel;
c906108c 38
a9fa9f7d
DJ
39/* The PID of the originally created or attached inferior. Used to
40 send signals to the process when GDB sends us an asynchronous interrupt
41 (user hitting Control-C in the client), and to wait for the child to exit
42 when no longer debugging it. */
43
a1928bad 44unsigned long signal_pid;
a9fa9f7d 45
fc620387 46static int
da85418c 47start_inferior (char *argv[], char *statusptr)
c906108c 48{
a9fa9f7d
DJ
49 signal (SIGTTOU, SIG_DFL);
50 signal (SIGTTIN, SIG_DFL);
51
52 signal_pid = create_inferior (argv[0], argv);
0d62e5e8 53
a1928bad 54 fprintf (stderr, "Process %s created; pid = %ld\n", argv[0],
a9fa9f7d
DJ
55 signal_pid);
56
57 signal (SIGTTOU, SIG_IGN);
58 signal (SIGTTIN, SIG_IGN);
59 tcsetpgrp (fileno (stderr), signal_pid);
c906108c
SS
60
61 /* Wait till we are at 1st instruction in program, return signal number. */
0d62e5e8 62 return mywait (statusptr, 0);
c906108c
SS
63}
64
45b7b345 65static int
fc620387 66attach_inferior (int pid, char *statusptr, int *sigptr)
45b7b345
DJ
67{
68 /* myattach should return -1 if attaching is unsupported,
69 0 if it succeeded, and call error() otherwise. */
a9fa9f7d 70
45b7b345
DJ
71 if (myattach (pid) != 0)
72 return -1;
73
6910d122
DJ
74 fprintf (stderr, "Attached; pid = %d\n", pid);
75
a9fa9f7d
DJ
76 /* FIXME - It may be that we should get the SIGNAL_PID from the
77 attach function, so that it can be the main thread instead of
78 whichever we were told to attach to. */
79 signal_pid = pid;
80
0d62e5e8 81 *sigptr = mywait (statusptr, 0);
45b7b345 82
9db87ebd
DJ
83 /* GDB knows to ignore the first SIGSTOP after attaching to a running
84 process using the "attach" command, but this is different; it's
85 just using "target remote". Pretend it's just starting up. */
86 if (*statusptr == 'T' && *sigptr == SIGSTOP)
87 *sigptr = SIGTRAP;
88
45b7b345
DJ
89 return 0;
90}
91
c906108c 92extern int remote_debug;
ce3a066d
DJ
93
94/* Handle all of the extended 'q' packets. */
95void
96handle_query (char *own_buf)
97{
0d62e5e8
DJ
98 static struct inferior_list_entry *thread_ptr;
99
ce3a066d
DJ
100 if (strcmp ("qSymbol::", own_buf) == 0)
101 {
2f2893d9
DJ
102 if (the_target->look_up_symbols != NULL)
103 (*the_target->look_up_symbols) ();
104
ce3a066d
DJ
105 strcpy (own_buf, "OK");
106 return;
107 }
108
0d62e5e8
DJ
109 if (strcmp ("qfThreadInfo", own_buf) == 0)
110 {
111 thread_ptr = all_threads.head;
a06660f7 112 sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
0d62e5e8
DJ
113 thread_ptr = thread_ptr->next;
114 return;
115 }
aa691b87 116
0d62e5e8
DJ
117 if (strcmp ("qsThreadInfo", own_buf) == 0)
118 {
119 if (thread_ptr != NULL)
120 {
a06660f7 121 sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
0d62e5e8
DJ
122 thread_ptr = thread_ptr->next;
123 return;
124 }
125 else
126 {
127 sprintf (own_buf, "l");
128 return;
129 }
130 }
aa691b87 131
52fb6437
NS
132 if (the_target->read_offsets != NULL
133 && strcmp ("qOffsets", own_buf) == 0)
134 {
135 CORE_ADDR text, data;
136
137 if (the_target->read_offsets (&text, &data))
138 sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX",
139 (long)text, (long)data, (long)data);
140 else
141 write_enn (own_buf);
142
143 return;
144 }
145
aa691b87
RM
146 if (the_target->read_auxv != NULL
147 && strncmp ("qPart:auxv:read::", own_buf, 17) == 0)
148 {
f450004a 149 unsigned char data[(PBUFSIZ - 1) / 2];
aa691b87
RM
150 CORE_ADDR ofs;
151 unsigned int len;
152 int n;
153 decode_m_packet (&own_buf[17], &ofs, &len); /* "OFS,LEN" */
154 if (len > sizeof data)
155 len = sizeof data;
156 n = (*the_target->read_auxv) (ofs, data, len);
157 if (n == 0)
158 write_ok (own_buf);
159 else if (n < 0)
160 write_enn (own_buf);
161 else
162 convert_int_to_ascii (data, own_buf, n);
163 return;
164 }
165
ce3a066d
DJ
166 /* Otherwise we didn't know what packet it was. Say we didn't
167 understand it. */
168 own_buf[0] = 0;
169}
170
64386c31
DJ
171/* Parse vCont packets. */
172void
fc620387 173handle_v_cont (char *own_buf, char *status, int *signal)
64386c31
DJ
174{
175 char *p, *q;
176 int n = 0, i = 0;
177 struct thread_resume *resume_info, default_action;
178
179 /* Count the number of semicolons in the packet. There should be one
180 for every action. */
181 p = &own_buf[5];
182 while (p)
183 {
184 n++;
185 p++;
186 p = strchr (p, ';');
187 }
188 /* Allocate room for one extra action, for the default remain-stopped
189 behavior; if no default action is in the list, we'll need the extra
190 slot. */
191 resume_info = malloc ((n + 1) * sizeof (resume_info[0]));
192
193 default_action.thread = -1;
194 default_action.leave_stopped = 1;
195 default_action.step = 0;
196 default_action.sig = 0;
197
198 p = &own_buf[5];
199 i = 0;
200 while (*p)
201 {
202 p++;
203
204 resume_info[i].leave_stopped = 0;
205
206 if (p[0] == 's' || p[0] == 'S')
207 resume_info[i].step = 1;
208 else if (p[0] == 'c' || p[0] == 'C')
209 resume_info[i].step = 0;
210 else
211 goto err;
212
213 if (p[0] == 'S' || p[0] == 'C')
214 {
215 int sig;
216 sig = strtol (p + 1, &q, 16);
217 if (p == q)
218 goto err;
219 p = q;
220
221 if (!target_signal_to_host_p (sig))
222 goto err;
223 resume_info[i].sig = target_signal_to_host (sig);
224 }
225 else
226 {
227 resume_info[i].sig = 0;
228 p = p + 1;
229 }
230
231 if (p[0] == 0)
232 {
233 resume_info[i].thread = -1;
234 default_action = resume_info[i];
235
236 /* Note: we don't increment i here, we'll overwrite this entry
237 the next time through. */
238 }
239 else if (p[0] == ':')
240 {
a06660f7
DJ
241 unsigned int gdb_id = strtoul (p + 1, &q, 16);
242 unsigned long thread_id;
243
64386c31
DJ
244 if (p == q)
245 goto err;
246 p = q;
247 if (p[0] != ';' && p[0] != 0)
248 goto err;
249
a06660f7
DJ
250 thread_id = gdb_id_to_thread_id (gdb_id);
251 if (thread_id)
252 resume_info[i].thread = thread_id;
253 else
254 goto err;
255
64386c31
DJ
256 i++;
257 }
258 }
259
260 resume_info[i] = default_action;
261
262 /* Still used in occasional places in the backend. */
263 if (n == 1 && resume_info[0].thread != -1)
264 cont_thread = resume_info[0].thread;
265 else
266 cont_thread = -1;
dc3f8883 267 set_desired_inferior (0);
64386c31
DJ
268
269 (*the_target->resume) (resume_info);
270
271 free (resume_info);
272
273 *signal = mywait (status, 1);
274 prepare_resume_reply (own_buf, *status, *signal);
275 return;
276
277err:
278 /* No other way to report an error... */
279 strcpy (own_buf, "");
280 free (resume_info);
281 return;
282}
283
284/* Handle all of the extended 'v' packets. */
285void
fc620387 286handle_v_requests (char *own_buf, char *status, int *signal)
64386c31
DJ
287{
288 if (strncmp (own_buf, "vCont;", 6) == 0)
289 {
290 handle_v_cont (own_buf, status, signal);
291 return;
292 }
293
294 if (strncmp (own_buf, "vCont?", 6) == 0)
295 {
296 strcpy (own_buf, "vCont;c;C;s;S");
297 return;
298 }
299
300 /* Otherwise we didn't know what packet it was. Say we didn't
301 understand it. */
302 own_buf[0] = 0;
303 return;
304}
305
306void
307myresume (int step, int sig)
308{
309 struct thread_resume resume_info[2];
310 int n = 0;
311
d592fa2f 312 if (step || sig || (cont_thread != 0 && cont_thread != -1))
64386c31
DJ
313 {
314 resume_info[0].thread
315 = ((struct inferior_list_entry *) current_inferior)->id;
316 resume_info[0].step = step;
317 resume_info[0].sig = sig;
318 resume_info[0].leave_stopped = 0;
319 n++;
320 }
321 resume_info[n].thread = -1;
322 resume_info[n].step = 0;
323 resume_info[n].sig = 0;
d592fa2f 324 resume_info[n].leave_stopped = (cont_thread != 0 && cont_thread != -1);
64386c31
DJ
325
326 (*the_target->resume) (resume_info);
327}
328
0729219d 329static int attached;
c906108c 330
dd24457d
DJ
331static void
332gdbserver_version (void)
333{
334 printf ("GNU gdbserver %s\n"
335 "Copyright (C) 2006 Free Software Foundation, Inc.\n"
336 "gdbserver is free software, covered by the GNU General Public License.\n"
337 "This gdbserver was configured as \"%s\"\n",
338 version, host_name);
339}
340
0bc68c49
DJ
341static void
342gdbserver_usage (void)
343{
dd24457d
DJ
344 printf ("Usage:\tgdbserver COMM PROG [ARGS ...]\n"
345 "\tgdbserver COMM --attach PID\n"
346 "\n"
347 "COMM may either be a tty device (for serial debugging), or \n"
348 "HOST:PORT to listen for a TCP connection.\n");
0bc68c49
DJ
349}
350
c906108c 351int
da85418c 352main (int argc, char *argv[])
c906108c 353{
f450004a 354 char ch, status, *own_buf;
7fb85e41 355 unsigned char *mem_buf;
c906108c 356 int i = 0;
fc620387 357 int signal;
c906108c
SS
358 unsigned int len;
359 CORE_ADDR mem_addr;
0729219d
DJ
360 int bad_attach;
361 int pid;
45b7b345 362 char *arg_end;
c906108c 363
dd24457d
DJ
364 if (argc >= 2 && strcmp (argv[1], "--version") == 0)
365 {
366 gdbserver_version ();
367 exit (0);
368 }
369
370 if (argc >= 2 && strcmp (argv[1], "--help") == 0)
371 {
372 gdbserver_usage ();
373 exit (0);
374 }
375
c5aa993b 376 if (setjmp (toplevel))
c906108c 377 {
c5aa993b
JM
378 fprintf (stderr, "Exiting\n");
379 exit (1);
c906108c
SS
380 }
381
0729219d
DJ
382 bad_attach = 0;
383 pid = 0;
384 attached = 0;
45b7b345
DJ
385 if (argc >= 3 && strcmp (argv[2], "--attach") == 0)
386 {
387 if (argc == 4
388 && argv[3] != '\0'
389 && (pid = strtoul (argv[3], &arg_end, 10)) != 0
390 && *arg_end == '\0')
391 {
392 ;
393 }
394 else
395 bad_attach = 1;
396 }
397
398 if (argc < 3 || bad_attach)
dd24457d
DJ
399 {
400 gdbserver_usage ();
401 exit (1);
402 }
c906108c 403
4ce44c66
JM
404 initialize_low ();
405
0a30fbc4 406 own_buf = malloc (PBUFSIZ);
7fb85e41 407 mem_buf = malloc (PBUFSIZ);
0a30fbc4 408
45b7b345
DJ
409 if (pid == 0)
410 {
411 /* Wait till we are at first instruction in program. */
412 signal = start_inferior (&argv[2], &status);
c906108c 413
45b7b345
DJ
414 /* We are now stopped at the first instruction of the target process */
415 }
416 else
417 {
418 switch (attach_inferior (pid, &status, &signal))
419 {
420 case -1:
421 error ("Attaching not supported on this target");
422 break;
423 default:
424 attached = 1;
425 break;
426 }
427 }
c906108c
SS
428
429 while (1)
430 {
431 remote_open (argv[1]);
432
c5aa993b
JM
433 restart:
434 setjmp (toplevel);
c906108c
SS
435 while (getpkt (own_buf) > 0)
436 {
437 unsigned char sig;
438 i = 0;
439 ch = own_buf[i++];
440 switch (ch)
441 {
ce3a066d
DJ
442 case 'q':
443 handle_query (own_buf);
444 break;
c906108c
SS
445 case 'd':
446 remote_debug = !remote_debug;
447 break;
6ad8ae5c
DJ
448 case 'D':
449 fprintf (stderr, "Detaching from inferior\n");
450 detach_inferior ();
451 write_ok (own_buf);
452 putpkt (own_buf);
aa691b87 453 remote_close ();
6ad8ae5c
DJ
454
455 /* If we are attached, then we can exit. Otherwise, we need to
456 hang around doing nothing, until the child is gone. */
457 if (!attached)
458 {
459 int status, ret;
460
461 do {
462 ret = waitpid (signal_pid, &status, 0);
463 if (WIFEXITED (status) || WIFSIGNALED (status))
464 break;
465 } while (ret != -1 || errno != ECHILD);
466 }
467
468 exit (0);
469
c906108c 470 case '!':
45b7b345
DJ
471 if (attached == 0)
472 {
473 extended_protocol = 1;
474 prepare_resume_reply (own_buf, status, signal);
475 }
476 else
477 {
478 /* We can not use the extended protocol if we are
479 attached, because we can not restart the running
480 program. So return unrecognized. */
481 own_buf[0] = '\0';
482 }
c906108c
SS
483 break;
484 case '?':
485 prepare_resume_reply (own_buf, status, signal);
486 break;
487 case 'H':
a06660f7 488 if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's')
c906108c 489 {
a06660f7
DJ
490 unsigned long gdb_id, thread_id;
491
492 gdb_id = strtoul (&own_buf[2], NULL, 16);
493 thread_id = gdb_id_to_thread_id (gdb_id);
494 if (thread_id == 0)
495 {
496 write_enn (own_buf);
497 break;
498 }
499
500 if (own_buf[1] == 'g')
501 {
502 general_thread = thread_id;
503 set_desired_inferior (1);
504 }
505 else if (own_buf[1] == 'c')
506 cont_thread = thread_id;
507 else if (own_buf[1] == 's')
508 step_thread = thread_id;
509
0d62e5e8 510 write_ok (own_buf);
a06660f7
DJ
511 }
512 else
513 {
c906108c
SS
514 /* Silently ignore it so that gdb can extend the protocol
515 without compatibility headaches. */
516 own_buf[0] = '\0';
c906108c
SS
517 }
518 break;
519 case 'g':
0d62e5e8 520 set_desired_inferior (1);
0a30fbc4 521 registers_to_string (own_buf);
c906108c
SS
522 break;
523 case 'G':
0d62e5e8 524 set_desired_inferior (1);
0a30fbc4 525 registers_from_string (&own_buf[1]);
c906108c
SS
526 write_ok (own_buf);
527 break;
528 case 'm':
529 decode_m_packet (&own_buf[1], &mem_addr, &len);
c3e735a6
DJ
530 if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
531 convert_int_to_ascii (mem_buf, own_buf, len);
532 else
533 write_enn (own_buf);
c906108c
SS
534 break;
535 case 'M':
536 decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
537 if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
538 write_ok (own_buf);
539 else
540 write_enn (own_buf);
541 break;
542 case 'C':
543 convert_ascii_to_int (own_buf + 1, &sig, 1);
0e98d0a7
DJ
544 if (target_signal_to_host_p (sig))
545 signal = target_signal_to_host (sig);
546 else
547 signal = 0;
0d62e5e8 548 set_desired_inferior (0);
0e98d0a7 549 myresume (0, signal);
0d62e5e8 550 signal = mywait (&status, 1);
c906108c
SS
551 prepare_resume_reply (own_buf, status, signal);
552 break;
553 case 'S':
554 convert_ascii_to_int (own_buf + 1, &sig, 1);
0e98d0a7
DJ
555 if (target_signal_to_host_p (sig))
556 signal = target_signal_to_host (sig);
557 else
558 signal = 0;
0d62e5e8 559 set_desired_inferior (0);
0e98d0a7 560 myresume (1, signal);
0d62e5e8 561 signal = mywait (&status, 1);
c906108c
SS
562 prepare_resume_reply (own_buf, status, signal);
563 break;
564 case 'c':
0d62e5e8 565 set_desired_inferior (0);
c906108c 566 myresume (0, 0);
0d62e5e8 567 signal = mywait (&status, 1);
c906108c
SS
568 prepare_resume_reply (own_buf, status, signal);
569 break;
570 case 's':
0d62e5e8 571 set_desired_inferior (0);
c906108c 572 myresume (1, 0);
0d62e5e8 573 signal = mywait (&status, 1);
c906108c
SS
574 prepare_resume_reply (own_buf, status, signal);
575 break;
e013ee27
OF
576 case 'Z':
577 {
578 char *lenptr;
579 char *dataptr;
580 CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16);
581 int len = strtol (lenptr + 1, &dataptr, 16);
582 char type = own_buf[1];
583
584 if (the_target->insert_watchpoint == NULL
585 || (type < '2' || type > '4'))
586 {
587 /* No watchpoint support or not a watchpoint command;
588 unrecognized either way. */
589 own_buf[0] = '\0';
590 }
591 else
592 {
593 int res;
594
595 res = (*the_target->insert_watchpoint) (type, addr, len);
596 if (res == 0)
597 write_ok (own_buf);
598 else if (res == 1)
599 /* Unsupported. */
600 own_buf[0] = '\0';
601 else
602 write_enn (own_buf);
603 }
604 break;
605 }
606 case 'z':
607 {
608 char *lenptr;
609 char *dataptr;
610 CORE_ADDR addr = strtoul (&own_buf[3], &lenptr, 16);
611 int len = strtol (lenptr + 1, &dataptr, 16);
612 char type = own_buf[1];
613
614 if (the_target->remove_watchpoint == NULL
615 || (type < '2' || type > '4'))
616 {
617 /* No watchpoint support or not a watchpoint command;
618 unrecognized either way. */
619 own_buf[0] = '\0';
620 }
621 else
622 {
623 int res;
624
625 res = (*the_target->remove_watchpoint) (type, addr, len);
626 if (res == 0)
627 write_ok (own_buf);
628 else if (res == 1)
629 /* Unsupported. */
630 own_buf[0] = '\0';
631 else
632 write_enn (own_buf);
633 }
634 break;
635 }
c906108c
SS
636 case 'k':
637 fprintf (stderr, "Killing inferior\n");
638 kill_inferior ();
639 /* When using the extended protocol, we start up a new
c5aa993b 640 debugging session. The traditional protocol will
c906108c
SS
641 exit instead. */
642 if (extended_protocol)
643 {
644 write_ok (own_buf);
645 fprintf (stderr, "GDBserver restarting\n");
646
647 /* Wait till we are at 1st instruction in prog. */
648 signal = start_inferior (&argv[2], &status);
649 goto restart;
650 break;
651 }
652 else
653 {
654 exit (0);
655 break;
656 }
657 case 'T':
a06660f7
DJ
658 {
659 unsigned long gdb_id, thread_id;
660
661 gdb_id = strtoul (&own_buf[1], NULL, 16);
662 thread_id = gdb_id_to_thread_id (gdb_id);
663 if (thread_id == 0)
664 {
665 write_enn (own_buf);
666 break;
667 }
668
669 if (mythread_alive (thread_id))
670 write_ok (own_buf);
671 else
672 write_enn (own_buf);
673 }
c906108c
SS
674 break;
675 case 'R':
676 /* Restarting the inferior is only supported in the
c5aa993b 677 extended protocol. */
c906108c
SS
678 if (extended_protocol)
679 {
680 kill_inferior ();
681 write_ok (own_buf);
682 fprintf (stderr, "GDBserver restarting\n");
683
684 /* Wait till we are at 1st instruction in prog. */
685 signal = start_inferior (&argv[2], &status);
686 goto restart;
687 break;
688 }
689 else
690 {
691 /* It is a request we don't understand. Respond with an
692 empty packet so that gdb knows that we don't support this
693 request. */
694 own_buf[0] = '\0';
695 break;
696 }
64386c31
DJ
697 case 'v':
698 /* Extended (long) request. */
699 handle_v_requests (own_buf, &status, &signal);
700 break;
c906108c
SS
701 default:
702 /* It is a request we don't understand. Respond with an
c5aa993b
JM
703 empty packet so that gdb knows that we don't support this
704 request. */
c906108c
SS
705 own_buf[0] = '\0';
706 break;
707 }
708
709 putpkt (own_buf);
710
711 if (status == 'W')
712 fprintf (stderr,
3a7fb99b 713 "\nChild exited with status %d\n", signal);
c906108c 714 if (status == 'X')
3a7fb99b
DJ
715 fprintf (stderr, "\nChild terminated with signal = 0x%x\n",
716 signal);
c906108c
SS
717 if (status == 'W' || status == 'X')
718 {
719 if (extended_protocol)
720 {
721 fprintf (stderr, "Killing inferior\n");
722 kill_inferior ();
723 write_ok (own_buf);
724 fprintf (stderr, "GDBserver restarting\n");
725
726 /* Wait till we are at 1st instruction in prog. */
727 signal = start_inferior (&argv[2], &status);
728 goto restart;
729 break;
730 }
731 else
732 {
733 fprintf (stderr, "GDBserver exiting\n");
734 exit (0);
735 }
736 }
737 }
738
739 /* We come here when getpkt fails.
740
c5aa993b
JM
741 For the extended remote protocol we exit (and this is the only
742 way we gracefully exit!).
c906108c 743
c5aa993b
JM
744 For the traditional remote protocol close the connection,
745 and re-open it at the top of the loop. */
c906108c
SS
746 if (extended_protocol)
747 {
748 remote_close ();
749 exit (0);
750 }
751 else
752 {
45b7b345
DJ
753 fprintf (stderr, "Remote side has terminated connection. "
754 "GDBserver will reopen the connection.\n");
c906108c
SS
755 remote_close ();
756 }
757 }
758}
This page took 0.538102 seconds and 4 git commands to generate.