]> git.sur5r.net Git - openocd/blob - src/server/server.c
server: bind to IPv4 localhost by default
[openocd] / src / server / server.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   Copyright (C) 2007-2010 Ã˜yvind Harboe                                 *
6  *   oyvind.harboe@zylin.com                                               *
7  *                                                                         *
8  *   Copyright (C) 2008 by Spencer Oliver                                  *
9  *   spen@spen-soft.co.uk                                                  *
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  *   This program is distributed in the hope that it will be useful,       *
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
19  *   GNU General Public License for more details.                          *
20  *                                                                         *
21  *   You should have received a copy of the GNU General Public License     *
22  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
23  ***************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "server.h"
30 #include <target/target.h>
31 #include <target/target_request.h>
32 #include <target/openrisc/jsp_server.h>
33 #include "openocd.h"
34 #include "tcl_server.h"
35 #include "telnet_server.h"
36
37 #include <signal.h>
38
39 #ifdef HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42
43 #ifndef _WIN32
44 #include <netinet/tcp.h>
45 #endif
46
47 static struct service *services;
48
49 /* shutdown_openocd == 1: exit the main event loop, and quit the
50  * debugger; 2: quit with non-zero return code */
51 static int shutdown_openocd;
52
53 /* store received signal to exit application by killing ourselves */
54 static int last_signal;
55
56 /* set the polling period to 100ms */
57 static int polling_period = 100;
58
59 /* address by name on which to listen for incoming TCP/IP connections */
60 static char *bindto_name;
61
62 static int add_connection(struct service *service, struct command_context *cmd_ctx)
63 {
64         socklen_t address_size;
65         struct connection *c, **p;
66         int retval;
67         int flag = 1;
68
69         c = malloc(sizeof(struct connection));
70         c->fd = -1;
71         c->fd_out = -1;
72         memset(&c->sin, 0, sizeof(c->sin));
73         c->cmd_ctx = copy_command_context(cmd_ctx);
74         c->service = service;
75         c->input_pending = 0;
76         c->priv = NULL;
77         c->next = NULL;
78
79         if (service->type == CONNECTION_TCP) {
80                 address_size = sizeof(c->sin);
81
82                 c->fd = accept(service->fd, (struct sockaddr *)&service->sin, &address_size);
83                 c->fd_out = c->fd;
84
85                 /* This increases performance dramatically for e.g. GDB load which
86                  * does not have a sliding window protocol.
87                  *
88                  * Ignore errors from this fn as it probably just means less performance
89                  */
90                 setsockopt(c->fd,       /* socket affected */
91                         IPPROTO_TCP,                    /* set option at TCP level */
92                         TCP_NODELAY,                    /* name of option */
93                         (char *)&flag,                  /* the cast is historical cruft */
94                         sizeof(int));                   /* length of option value */
95
96                 LOG_INFO("accepting '%s' connection on tcp/%s", service->name, service->port);
97                 retval = service->new_connection(c);
98                 if (retval != ERROR_OK) {
99                         close_socket(c->fd);
100                         LOG_ERROR("attempted '%s' connection rejected", service->name);
101                         command_done(c->cmd_ctx);
102                         free(c);
103                         return retval;
104                 }
105         } else if (service->type == CONNECTION_STDINOUT) {
106                 c->fd = service->fd;
107                 c->fd_out = fileno(stdout);
108
109 #ifdef _WIN32
110                 /* we are using stdin/out so ignore ctrl-c under windoze */
111                 SetConsoleCtrlHandler(NULL, TRUE);
112 #endif
113
114                 /* do not check for new connections again on stdin */
115                 service->fd = -1;
116
117                 LOG_INFO("accepting '%s' connection from pipe", service->name);
118                 retval = service->new_connection(c);
119                 if (retval != ERROR_OK) {
120                         LOG_ERROR("attempted '%s' connection rejected", service->name);
121                         command_done(c->cmd_ctx);
122                         free(c);
123                         return retval;
124                 }
125         } else if (service->type == CONNECTION_PIPE) {
126                 c->fd = service->fd;
127                 /* do not check for new connections again on stdin */
128                 service->fd = -1;
129
130                 char *out_file = alloc_printf("%so", service->port);
131                 c->fd_out = open(out_file, O_WRONLY);
132                 free(out_file);
133                 if (c->fd_out == -1) {
134                         LOG_ERROR("could not open %s", service->port);
135                         command_done(c->cmd_ctx);
136                         free(c);
137                         return ERROR_FAIL;
138                 }
139
140                 LOG_INFO("accepting '%s' connection from pipe %s", service->name, service->port);
141                 retval = service->new_connection(c);
142                 if (retval != ERROR_OK) {
143                         LOG_ERROR("attempted '%s' connection rejected", service->name);
144                         command_done(c->cmd_ctx);
145                         free(c);
146                         return retval;
147                 }
148         }
149
150         /* add to the end of linked list */
151         for (p = &service->connections; *p; p = &(*p)->next)
152                 ;
153         *p = c;
154
155         if (service->max_connections != CONNECTION_LIMIT_UNLIMITED)
156                 service->max_connections--;
157
158         return ERROR_OK;
159 }
160
161 static int remove_connection(struct service *service, struct connection *connection)
162 {
163         struct connection **p = &service->connections;
164         struct connection *c;
165
166         /* find connection */
167         while ((c = *p)) {
168                 if (c->fd == connection->fd) {
169                         service->connection_closed(c);
170                         if (service->type == CONNECTION_TCP)
171                                 close_socket(c->fd);
172                         else if (service->type == CONNECTION_PIPE) {
173                                 /* The service will listen to the pipe again */
174                                 c->service->fd = c->fd;
175                         }
176
177                         command_done(c->cmd_ctx);
178
179                         /* delete connection */
180                         *p = c->next;
181                         free(c);
182
183                         if (service->max_connections != CONNECTION_LIMIT_UNLIMITED)
184                                 service->max_connections++;
185
186                         break;
187                 }
188
189                 /* redirect p to next list pointer */
190                 p = &(*p)->next;
191         }
192
193         return ERROR_OK;
194 }
195
196 static void free_service(struct service *c)
197 {
198         free(c->name);
199         free(c->port);
200         free(c);
201 }
202
203 int add_service(char *name,
204         const char *port,
205         int max_connections,
206         new_connection_handler_t new_connection_handler,
207         input_handler_t input_handler,
208         connection_closed_handler_t connection_closed_handler,
209         void *priv)
210 {
211         struct service *c, **p;
212         struct hostent *hp;
213         int so_reuseaddr_option = 1;
214
215         c = malloc(sizeof(struct service));
216
217         c->name = strdup(name);
218         c->port = strdup(port);
219         c->max_connections = 1; /* Only TCP/IP ports can support more than one connection */
220         c->fd = -1;
221         c->connections = NULL;
222         c->new_connection = new_connection_handler;
223         c->input = input_handler;
224         c->connection_closed = connection_closed_handler;
225         c->priv = priv;
226         c->next = NULL;
227         long portnumber;
228         if (strcmp(c->port, "pipe") == 0)
229                 c->type = CONNECTION_STDINOUT;
230         else {
231                 char *end;
232                 portnumber = strtol(c->port, &end, 0);
233                 if (!*end && (parse_long(c->port, &portnumber) == ERROR_OK)) {
234                         c->portnumber = portnumber;
235                         c->type = CONNECTION_TCP;
236                 } else
237                         c->type = CONNECTION_PIPE;
238         }
239
240         if (c->type == CONNECTION_TCP) {
241                 c->max_connections = max_connections;
242
243                 c->fd = socket(AF_INET, SOCK_STREAM, 0);
244                 if (c->fd == -1) {
245                         LOG_ERROR("error creating socket: %s", strerror(errno));
246                         free_service(c);
247                         return ERROR_FAIL;
248                 }
249
250                 setsockopt(c->fd,
251                         SOL_SOCKET,
252                         SO_REUSEADDR,
253                         (void *)&so_reuseaddr_option,
254                         sizeof(int));
255
256                 socket_nonblock(c->fd);
257
258                 memset(&c->sin, 0, sizeof(c->sin));
259                 c->sin.sin_family = AF_INET;
260
261                 if (bindto_name == NULL)
262                         c->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
263                 else {
264                         hp = gethostbyname(bindto_name);
265                         if (hp == NULL) {
266                                 LOG_ERROR("couldn't resolve bindto address: %s", bindto_name);
267                                 close_socket(c->fd);
268                                 free_service(c);
269                                 return ERROR_FAIL;
270                         }
271                         memcpy(&c->sin.sin_addr, hp->h_addr_list[0], hp->h_length);
272                 }
273                 c->sin.sin_port = htons(c->portnumber);
274
275                 if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1) {
276                         LOG_ERROR("couldn't bind %s to socket on port %d: %s", name, c->portnumber, strerror(errno));
277                         close_socket(c->fd);
278                         free_service(c);
279                         return ERROR_FAIL;
280                 }
281
282 #ifndef _WIN32
283                 int segsize = 65536;
284                 setsockopt(c->fd, IPPROTO_TCP, TCP_MAXSEG,  &segsize, sizeof(int));
285 #endif
286                 int window_size = 128 * 1024;
287
288                 /* These setsockopt()s must happen before the listen() */
289
290                 setsockopt(c->fd, SOL_SOCKET, SO_SNDBUF,
291                         (char *)&window_size, sizeof(window_size));
292                 setsockopt(c->fd, SOL_SOCKET, SO_RCVBUF,
293                         (char *)&window_size, sizeof(window_size));
294
295                 if (listen(c->fd, 1) == -1) {
296                         LOG_ERROR("couldn't listen on socket: %s", strerror(errno));
297                         close_socket(c->fd);
298                         free_service(c);
299                         return ERROR_FAIL;
300                 }
301
302                 struct sockaddr_in addr_in;
303                 addr_in.sin_port = 0;
304                 socklen_t addr_in_size = sizeof(addr_in);
305                 if (getsockname(c->fd, (struct sockaddr *)&addr_in, &addr_in_size) == 0)
306                         LOG_INFO("Listening on port %hu for %s connections",
307                                  ntohs(addr_in.sin_port), name);
308         } else if (c->type == CONNECTION_STDINOUT) {
309                 c->fd = fileno(stdin);
310
311 #ifdef _WIN32
312                 /* for win32 set stdin/stdout to binary mode */
313                 if (_setmode(_fileno(stdout), _O_BINARY) < 0)
314                         LOG_WARNING("cannot change stdout mode to binary");
315                 if (_setmode(_fileno(stdin), _O_BINARY) < 0)
316                         LOG_WARNING("cannot change stdin mode to binary");
317                 if (_setmode(_fileno(stderr), _O_BINARY) < 0)
318                         LOG_WARNING("cannot change stderr mode to binary");
319 #else
320                 socket_nonblock(c->fd);
321 #endif
322         } else if (c->type == CONNECTION_PIPE) {
323 #ifdef _WIN32
324                 /* we currenty do not support named pipes under win32
325                  * so exit openocd for now */
326                 LOG_ERROR("Named pipes currently not supported under this os");
327                 free_service(c);
328                 return ERROR_FAIL;
329 #else
330                 /* Pipe we're reading from */
331                 c->fd = open(c->port, O_RDONLY | O_NONBLOCK);
332                 if (c->fd == -1) {
333                         LOG_ERROR("could not open %s", c->port);
334                         free_service(c);
335                         return ERROR_FAIL;
336                 }
337 #endif
338         }
339
340         /* add to the end of linked list */
341         for (p = &services; *p; p = &(*p)->next)
342                 ;
343         *p = c;
344
345         return ERROR_OK;
346 }
347
348 static void remove_connections(struct service *service)
349 {
350         struct connection *connection;
351
352         connection = service->connections;
353
354         while (connection) {
355                 struct connection *tmp;
356
357                 tmp = connection->next;
358                 remove_connection(service, connection);
359                 connection = tmp;
360         }
361 }
362
363 static int remove_services(void)
364 {
365         struct service *c = services;
366
367         /* loop service */
368         while (c) {
369                 struct service *next = c->next;
370
371                 remove_connections(c);
372
373                 if (c->name)
374                         free(c->name);
375
376                 if (c->type == CONNECTION_PIPE) {
377                         if (c->fd != -1)
378                                 close(c->fd);
379                 }
380                 if (c->port)
381                         free(c->port);
382
383                 if (c->priv)
384                         free(c->priv);
385
386                 /* delete service */
387                 free(c);
388
389                 /* remember the last service for unlinking */
390                 c = next;
391         }
392
393         services = NULL;
394
395         return ERROR_OK;
396 }
397
398 int server_loop(struct command_context *command_context)
399 {
400         struct service *service;
401
402         bool poll_ok = true;
403
404         /* used in select() */
405         fd_set read_fds;
406         int fd_max;
407
408         /* used in accept() */
409         int retval;
410
411 #ifndef _WIN32
412         if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
413                 LOG_ERROR("couldn't set SIGPIPE to SIG_IGN");
414 #endif
415
416         while (!shutdown_openocd) {
417                 /* monitor sockets for activity */
418                 fd_max = 0;
419                 FD_ZERO(&read_fds);
420
421                 /* add service and connection fds to read_fds */
422                 for (service = services; service; service = service->next) {
423                         if (service->fd != -1) {
424                                 /* listen for new connections */
425                                 FD_SET(service->fd, &read_fds);
426
427                                 if (service->fd > fd_max)
428                                         fd_max = service->fd;
429                         }
430
431                         if (service->connections) {
432                                 struct connection *c;
433
434                                 for (c = service->connections; c; c = c->next) {
435                                         /* check for activity on the connection */
436                                         FD_SET(c->fd, &read_fds);
437                                         if (c->fd > fd_max)
438                                                 fd_max = c->fd;
439                                 }
440                         }
441                 }
442
443                 struct timeval tv;
444                 tv.tv_sec = 0;
445                 if (poll_ok) {
446                         /* we're just polling this iteration, this is faster on embedded
447                          * hosts */
448                         tv.tv_usec = 0;
449                         retval = socket_select(fd_max + 1, &read_fds, NULL, NULL, &tv);
450                 } else {
451                         /* Every 100ms, can be changed with "poll_period" command */
452                         tv.tv_usec = polling_period * 1000;
453                         /* Only while we're sleeping we'll let others run */
454                         openocd_sleep_prelude();
455                         kept_alive();
456                         retval = socket_select(fd_max + 1, &read_fds, NULL, NULL, &tv);
457                         openocd_sleep_postlude();
458                 }
459
460                 if (retval == -1) {
461 #ifdef _WIN32
462
463                         errno = WSAGetLastError();
464
465                         if (errno == WSAEINTR)
466                                 FD_ZERO(&read_fds);
467                         else {
468                                 LOG_ERROR("error during select: %s", strerror(errno));
469                                 return ERROR_FAIL;
470                         }
471 #else
472
473                         if (errno == EINTR)
474                                 FD_ZERO(&read_fds);
475                         else {
476                                 LOG_ERROR("error during select: %s", strerror(errno));
477                                 return ERROR_FAIL;
478                         }
479 #endif
480                 }
481
482                 if (retval == 0) {
483                         /* We only execute these callbacks when there was nothing to do or we timed
484                          *out */
485                         target_call_timer_callbacks();
486                         process_jim_events(command_context);
487
488                         FD_ZERO(&read_fds);     /* eCos leaves read_fds unchanged in this case!  */
489
490                         /* We timed out/there was nothing to do, timeout rather than poll next time
491                          **/
492                         poll_ok = false;
493                 } else {
494                         /* There was something to do, next time we'll just poll */
495                         poll_ok = true;
496                 }
497
498                 /* This is a simple back-off algorithm where we immediately
499                  * re-poll if we did something this time around.
500                  *
501                  * This greatly improves performance of DCC.
502                  */
503                 poll_ok = poll_ok || target_got_message();
504
505                 for (service = services; service; service = service->next) {
506                         /* handle new connections on listeners */
507                         if ((service->fd != -1)
508                             && (FD_ISSET(service->fd, &read_fds))) {
509                                 if (service->max_connections != 0)
510                                         add_connection(service, command_context);
511                                 else {
512                                         if (service->type == CONNECTION_TCP) {
513                                                 struct sockaddr_in sin;
514                                                 socklen_t address_size = sizeof(sin);
515                                                 int tmp_fd;
516                                                 tmp_fd = accept(service->fd,
517                                                                 (struct sockaddr *)&service->sin,
518                                                                 &address_size);
519                                                 close_socket(tmp_fd);
520                                         }
521                                         LOG_INFO(
522                                                 "rejected '%s' connection, no more connections allowed",
523                                                 service->name);
524                                 }
525                         }
526
527                         /* handle activity on connections */
528                         if (service->connections) {
529                                 struct connection *c;
530
531                                 for (c = service->connections; c; ) {
532                                         if ((FD_ISSET(c->fd, &read_fds)) || c->input_pending) {
533                                                 retval = service->input(c);
534                                                 if (retval != ERROR_OK) {
535                                                         struct connection *next = c->next;
536                                                         if (service->type == CONNECTION_PIPE ||
537                                                                         service->type == CONNECTION_STDINOUT) {
538                                                                 /* if connection uses a pipe then
539                                                                  * shutdown openocd on error */
540                                                                 shutdown_openocd = 1;
541                                                         }
542                                                         remove_connection(service, c);
543                                                         LOG_INFO("dropped '%s' connection",
544                                                                 service->name);
545                                                         c = next;
546                                                         continue;
547                                                 }
548                                         }
549                                         c = c->next;
550                                 }
551                         }
552                 }
553
554 #ifdef _WIN32
555                 MSG msg;
556                 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
557                         if (msg.message == WM_QUIT)
558                                 shutdown_openocd = 1;
559                 }
560 #endif
561         }
562
563         return shutdown_openocd != 2 ? ERROR_OK : ERROR_FAIL;
564 }
565
566 #ifdef _WIN32
567 BOOL WINAPI ControlHandler(DWORD dwCtrlType)
568 {
569         shutdown_openocd = 1;
570         return TRUE;
571 }
572 #endif
573
574 void sig_handler(int sig)
575 {
576         /* store only first signal that hits us */
577         if (!last_signal)
578                 last_signal = sig;
579         shutdown_openocd = 1;
580 }
581
582 int server_preinit(void)
583 {
584         /* this currently only calls WSAStartup on native win32 systems
585          * before any socket operations are performed.
586          * This is an issue if you call init in your config script */
587
588 #ifdef _WIN32
589         WORD wVersionRequested;
590         WSADATA wsaData;
591
592         wVersionRequested = MAKEWORD(2, 2);
593
594         if (WSAStartup(wVersionRequested, &wsaData) != 0) {
595                 LOG_ERROR("Failed to Open Winsock");
596                 return ERROR_FAIL;
597         }
598
599         /* register ctrl-c handler */
600         SetConsoleCtrlHandler(ControlHandler, TRUE);
601
602         signal(SIGBREAK, sig_handler);
603 #endif
604         signal(SIGINT, sig_handler);
605         signal(SIGTERM, sig_handler);
606         signal(SIGABRT, sig_handler);
607
608         return ERROR_OK;
609 }
610
611 int server_init(struct command_context *cmd_ctx)
612 {
613         int ret = tcl_init();
614
615         if (ret != ERROR_OK)
616                 return ret;
617
618         ret = telnet_init("Open On-Chip Debugger");
619
620         if (ret != ERROR_OK) {
621                 remove_services();
622                 return ret;
623         }
624
625         return ERROR_OK;
626 }
627
628 int server_quit(void)
629 {
630         remove_services();
631         target_quit();
632
633 #ifdef _WIN32
634         WSACleanup();
635         SetConsoleCtrlHandler(ControlHandler, FALSE);
636
637         return ERROR_OK;
638 #endif
639
640         /* return signal number so we can kill ourselves */
641         return last_signal;
642 }
643
644 void server_free(void)
645 {
646         tcl_service_free();
647         telnet_service_free();
648         jsp_service_free();
649 }
650
651 void exit_on_signal(int sig)
652 {
653 #ifndef _WIN32
654         /* bring back default system handler and kill yourself */
655         signal(sig, SIG_DFL);
656         kill(getpid(), sig);
657 #endif
658 }
659
660 int connection_write(struct connection *connection, const void *data, int len)
661 {
662         if (len == 0) {
663                 /* successful no-op. Sockets and pipes behave differently here... */
664                 return 0;
665         }
666         if (connection->service->type == CONNECTION_TCP)
667                 return write_socket(connection->fd_out, data, len);
668         else
669                 return write(connection->fd_out, data, len);
670 }
671
672 int connection_read(struct connection *connection, void *data, int len)
673 {
674         if (connection->service->type == CONNECTION_TCP)
675                 return read_socket(connection->fd, data, len);
676         else
677                 return read(connection->fd, data, len);
678 }
679
680 /* tell the server we want to shut down */
681 COMMAND_HANDLER(handle_shutdown_command)
682 {
683         LOG_USER("shutdown command invoked");
684
685         shutdown_openocd = 1;
686
687         if (CMD_ARGC == 1) {
688                 if (!strcmp(CMD_ARGV[0], "error")) {
689                         shutdown_openocd = 2;
690                         return ERROR_FAIL;
691                 }
692         }
693
694         return ERROR_COMMAND_CLOSE_CONNECTION;
695 }
696
697 COMMAND_HANDLER(handle_poll_period_command)
698 {
699         if (CMD_ARGC == 0)
700                 LOG_WARNING("You need to set a period value");
701         else
702                 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], polling_period);
703
704         LOG_INFO("set servers polling period to %ums", polling_period);
705
706         return ERROR_OK;
707 }
708
709 COMMAND_HANDLER(handle_bindto_command)
710 {
711         switch (CMD_ARGC) {
712                 case 0:
713                         command_print(CMD_CTX, "bindto name: %s", bindto_name);
714                         break;
715                 case 1:
716                         free(bindto_name);
717                         bindto_name = strdup(CMD_ARGV[0]);
718                         break;
719                 default:
720                         return ERROR_COMMAND_SYNTAX_ERROR;
721         }
722         return ERROR_OK;
723 }
724
725 static const struct command_registration server_command_handlers[] = {
726         {
727                 .name = "shutdown",
728                 .handler = &handle_shutdown_command,
729                 .mode = COMMAND_ANY,
730                 .usage = "",
731                 .help = "shut the server down",
732         },
733         {
734                 .name = "poll_period",
735                 .handler = &handle_poll_period_command,
736                 .mode = COMMAND_ANY,
737                 .usage = "",
738                 .help = "set the servers polling period",
739         },
740         {
741                 .name = "bindto",
742                 .handler = &handle_bindto_command,
743                 .mode = COMMAND_ANY,
744                 .usage = "[name]",
745                 .help = "Specify address by name on which to listen for "
746                     "incoming TCP/IP connections",
747         },
748         COMMAND_REGISTRATION_DONE
749 };
750
751 int server_register_commands(struct command_context *cmd_ctx)
752 {
753         int retval = telnet_register_commands(cmd_ctx);
754         if (ERROR_OK != retval)
755                 return retval;
756
757         retval = tcl_register_commands(cmd_ctx);
758         if (ERROR_OK != retval)
759                 return retval;
760
761         retval = jsp_register_commands(cmd_ctx);
762         if (ERROR_OK != retval)
763                 return retval;
764
765         return register_commands(cmd_ctx, NULL, server_command_handlers);
766 }
767
768 COMMAND_HELPER(server_port_command, unsigned short *out)
769 {
770         switch (CMD_ARGC) {
771                 case 0:
772                         command_print(CMD_CTX, "%d", *out);
773                         break;
774                 case 1:
775                 {
776                         uint16_t port;
777                         COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], port);
778                         *out = port;
779                         break;
780                 }
781                 default:
782                         return ERROR_COMMAND_SYNTAX_ERROR;
783         }
784         return ERROR_OK;
785 }
786
787 COMMAND_HELPER(server_pipe_command, char **out)
788 {
789         switch (CMD_ARGC) {
790                 case 0:
791                         command_print(CMD_CTX, "%s", *out);
792                         break;
793                 case 1:
794                 {
795                         if (CMD_CTX->mode == COMMAND_EXEC) {
796                                 LOG_WARNING("unable to change server port after init");
797                                 return ERROR_COMMAND_ARGUMENT_INVALID;
798                         }
799                         free(*out);
800                         *out = strdup(CMD_ARGV[0]);
801                         break;
802                 }
803                 default:
804                         return ERROR_COMMAND_SYNTAX_ERROR;
805         }
806         return ERROR_OK;
807 }