1 /***************************************************************************
2 * Copyright (C) 2010 Øyvind Harboe *
3 * oyvind.harboe@zylin.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
25 #include "tcl_server.h"
28 #define TCL_SERVER_VERSION "TCL Server 0.1"
29 #define TCL_MAX_LINE (4096)
31 struct tcl_connection
{
34 char tc_line
[TCL_MAX_LINE
];
35 int tc_outerror
; /* flag an output error */
38 static const char *tcl_port
;
41 static int tcl_new_connection(struct connection
*connection
);
42 static int tcl_input(struct connection
*connection
);
43 static int tcl_output(struct connection
*connection
, const void *buf
, ssize_t len
);
44 static int tcl_closed(struct connection
*connection
);
46 /* write data out to a socket.
48 * this is a blocking write, so the return value must equal the length, if
49 * that is not the case then flag the connection with an output error.
51 int tcl_output(struct connection
*connection
, const void *data
, ssize_t len
)
54 struct tcl_connection
*tclc
;
56 tclc
= connection
->priv
;
57 if (tclc
->tc_outerror
)
58 return ERROR_SERVER_REMOTE_CLOSED
;
60 wlen
= connection_write(connection
, data
, len
);
65 LOG_ERROR("error during write: %d != %d", (int)wlen
, (int)len
);
66 tclc
->tc_outerror
= 1;
67 return ERROR_SERVER_REMOTE_CLOSED
;
71 static int tcl_new_connection(struct connection
*connection
)
73 struct tcl_connection
*tclc
;
75 tclc
= malloc(sizeof(struct tcl_connection
));
77 return ERROR_CONNECTION_REJECTED
;
79 memset(tclc
, 0, sizeof(struct tcl_connection
));
80 connection
->priv
= tclc
;
84 static int tcl_input(struct connection
*connection
)
86 Jim_Interp
*interp
= (Jim_Interp
*)connection
->cmd_ctx
->interp
;
92 struct tcl_connection
*tclc
;
93 unsigned char in
[256];
95 rlen
= connection_read(connection
, &in
, sizeof(in
));
98 LOG_ERROR("error during read: %s", strerror(errno
));
99 return ERROR_SERVER_REMOTE_CLOSED
;
102 tclc
= connection
->priv
;
104 return ERROR_CONNECTION_REJECTED
;
106 /* push as much data into the line as possible */
107 for (i
= 0; i
< rlen
; i
++)
109 /* buffer the data */
110 tclc
->tc_line
[tclc
->tc_lineoffset
] = in
[i
];
111 if (tclc
->tc_lineoffset
< TCL_MAX_LINE
)
112 tclc
->tc_lineoffset
++;
114 tclc
->tc_linedrop
= 1;
116 /* ctrl-z is end of command. When testing from telnet, just
117 * press ctrl-z a couple of times first to put telnet into the
118 * mode where it will send 0x1a in response to pressing ctrl-z
123 /* process the line */
124 if (tclc
->tc_linedrop
) {
125 #define ESTR "line too long\n"
126 retval
= tcl_output(connection
, ESTR
, sizeof(ESTR
));
127 if (retval
!= ERROR_OK
)
132 tclc
->tc_line
[tclc
->tc_lineoffset
-1] = '\0';
133 LOG_DEBUG("Executing script:\n %s", tclc
->tc_line
);
134 retval
= Jim_Eval_Named(interp
, tclc
->tc_line
, "remote:connection",1);
135 LOG_DEBUG("Result: %d\n %s", retval
, Jim_GetString(Jim_GetResult(interp
), &reslen
));
136 result
= Jim_GetString(Jim_GetResult(interp
), &reslen
);
137 retval
= tcl_output(connection
, result
, reslen
);
138 if (retval
!= ERROR_OK
)
140 /* Always output ctrl-d as end of line to allow multiline results */
141 tcl_output(connection
, "\x1a", 1);
144 tclc
->tc_lineoffset
= 0;
145 tclc
->tc_linedrop
= 0;
151 static int tcl_closed(struct connection
*connection
)
153 /* cleanup connection context */
154 if (connection
->priv
) {
155 free(connection
->priv
);
156 connection
->priv
= NULL
;
163 if (strcmp(tcl_port
, "disabled") == 0)
165 LOG_INFO("tcl server disabled");
169 return add_service("tcl", tcl_port
, 1,
170 &tcl_new_connection
, &tcl_input
,
174 COMMAND_HANDLER(handle_tcl_port_command
)
176 return CALL_COMMAND_HANDLER(server_pipe_command
, &tcl_port
);
179 static const struct command_registration tcl_command_handlers
[] = {
182 .handler
= handle_tcl_port_command
,
183 .mode
= COMMAND_CONFIG
,
184 .help
= "Specify port on which to listen "
185 "for incoming Tcl syntax. "
186 "No arguments reports Tcl port; zero disables.",
187 .usage
= "[port_num]",
189 COMMAND_REGISTRATION_DONE
192 int tcl_register_commands(struct command_context
*cmd_ctx
)
194 tcl_port
= strdup("6666");
195 return register_commands(cmd_ctx
, NULL
, tcl_command_handlers
);