1 |
27 |
unneback |
//============================================================================
|
2 |
|
|
//
|
3 |
|
|
// ecosynth.c
|
4 |
|
|
//
|
5 |
|
|
// The eCos synthetic target I/O auxiliary
|
6 |
|
|
//
|
7 |
|
|
//============================================================================
|
8 |
|
|
//####COPYRIGHTBEGIN####
|
9 |
|
|
//
|
10 |
|
|
// ----------------------------------------------------------------------------
|
11 |
|
|
// Copyright (C) 2002 Bart Veer
|
12 |
|
|
//
|
13 |
|
|
// This file is part of the eCos host tools.
|
14 |
|
|
//
|
15 |
|
|
// This program is free software; you can redistribute it and/or modify it
|
16 |
|
|
// under the terms of the GNU General Public License as published by the Free
|
17 |
|
|
// Software Foundation; either version 2 of the License, or (at your option)
|
18 |
|
|
// any later version.
|
19 |
|
|
//
|
20 |
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
21 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
22 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
23 |
|
|
// more details.
|
24 |
|
|
//
|
25 |
|
|
// You should have received a copy of the GNU General Public License along with
|
26 |
|
|
// this program; if not, write to the Free Software Foundation, Inc.,
|
27 |
|
|
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
28 |
|
|
//
|
29 |
|
|
// ----------------------------------------------------------------------------
|
30 |
|
|
//
|
31 |
|
|
//####COPYRIGHTEND####
|
32 |
|
|
//============================================================================
|
33 |
|
|
//#####DESCRIPTIONBEGIN####
|
34 |
|
|
//
|
35 |
|
|
// Author(s): bartv
|
36 |
|
|
// Contact(s): bartv
|
37 |
|
|
// Date: 2002-08-05
|
38 |
|
|
// Version: 0.01
|
39 |
|
|
// Description:
|
40 |
|
|
//
|
41 |
|
|
// The main module for the eCos synthetic target auxiliary. This
|
42 |
|
|
// program is fork'ed and execve'd during the initialization phase of
|
43 |
|
|
// any synthetic target application, and is primarily responsible for
|
44 |
|
|
// I/O operations. This program should never be run directly by a
|
45 |
|
|
// user.
|
46 |
|
|
//
|
47 |
|
|
//####DESCRIPTIONEND####
|
48 |
|
|
//============================================================================
|
49 |
|
|
|
50 |
|
|
#include <stdio.h>
|
51 |
|
|
#include <stdlib.h>
|
52 |
|
|
#include <string.h>
|
53 |
|
|
#include <signal.h>
|
54 |
|
|
#include <limits.h>
|
55 |
|
|
#include <unistd.h>
|
56 |
|
|
#include <assert.h>
|
57 |
|
|
#include <fcntl.h>
|
58 |
|
|
#include <sys/param.h>
|
59 |
|
|
#include <sys/types.h>
|
60 |
|
|
// Avoid compatibility problems with Tcl 8.4 vs. earlier
|
61 |
|
|
#define USE_NON_CONST
|
62 |
|
|
#include <tcl.h>
|
63 |
|
|
#include <tk.h>
|
64 |
|
|
|
65 |
|
|
// The protocol between host and target is defined by a private
|
66 |
|
|
// target-side header.
|
67 |
|
|
#include "../src/synth_protocol.h"
|
68 |
|
|
|
69 |
|
|
// ----------------------------------------------------------------------------
|
70 |
|
|
// Statics etc.
|
71 |
|
|
#define PROGNAME "Synthetic target auxiliary"
|
72 |
|
|
|
73 |
|
|
static int no_windows = 0; // -nw arg
|
74 |
|
|
|
75 |
|
|
static pid_t parent_pid;
|
76 |
|
|
|
77 |
|
|
// ----------------------------------------------------------------------------
|
78 |
|
|
// Overview.
|
79 |
|
|
//
|
80 |
|
|
// When a synthetic target application is executed, whether directly
|
81 |
|
|
// or from inside gdb, it can fork() and execve() the synthetic
|
82 |
|
|
// target auxiliary: this program, ecosynth. For now this is disabled
|
83 |
|
|
// by default but can be enabled using a --io option on the command line.
|
84 |
|
|
// In future it may be enabled by default, but can be suppressed using
|
85 |
|
|
// --nio.
|
86 |
|
|
//
|
87 |
|
|
// stdin, stdout, and stderr will be inherited from the eCos application, so
|
88 |
|
|
// that attempts to use printf() or fprintf(stderr, ) from inside the
|
89 |
|
|
// auxiliary or its helper applications work as expected. In addition file
|
90 |
|
|
// descriptor 3 will be a pipe from the eCos application, and file descriptor
|
91 |
|
|
// 4 will be a pipe to the application. The protocol defined in
|
92 |
|
|
// ../src/synth_protocol.h runs over these pip
|
93 |
|
|
//
|
94 |
|
|
// argv[] and environ are also as per the application, with the exception
|
95 |
|
|
// of argv[0] which is the full path to this application.
|
96 |
|
|
//
|
97 |
|
|
// The bulk of the hard work is done inside Tcl. The C++ code is
|
98 |
|
|
// responsible only for initialization and for implementing some
|
99 |
|
|
// additional commands, for example to send a SIGIO signal to the
|
100 |
|
|
// parent when there is a new pending interrupt. The primary script is
|
101 |
|
|
// ecosynth.tcl which should be installed alongside the executable.
|
102 |
|
|
//
|
103 |
|
|
// This code makes use of the standard Tcl initialization facilities:
|
104 |
|
|
//
|
105 |
|
|
// 1) main() calls Tcl_Main() with the command-line arguments and an
|
106 |
|
|
// application-specific initialization routine ecosynth_appinit().
|
107 |
|
|
//
|
108 |
|
|
// 2) Tcl_Main() goes through the initialization sequence in generic/tclMain.c.
|
109 |
|
|
// There is one slight complication: Tcl_main() interprets arguments in
|
110 |
|
|
// a way that makes sense for tclsh, but not for ecosynth. Specially if
|
111 |
|
|
// argv[1] exists and does not begin with a - then it will be interpreted
|
112 |
|
|
// as a script to be executed. Hence argv[] is re-allocated and the name
|
113 |
|
|
// of a suitable script is inserted.
|
114 |
|
|
//
|
115 |
|
|
// 3) ecosynth_appinit() initializes the Tcl interpreter, and optionally
|
116 |
|
|
// the Tk interpreter as well. This is controlled by the presence of
|
117 |
|
|
// a -nw argument on the command line. This initialization routine
|
118 |
|
|
// also takes care of adding some commands to the Tcl interpreter.
|
119 |
|
|
//
|
120 |
|
|
// 4) Tcl_Main() will now proceed to execute the startup script, which
|
121 |
|
|
// is eccentric.tcl installed in the libexec directory.
|
122 |
|
|
|
123 |
|
|
static int ecosynth_appinit(Tcl_Interp*);
|
124 |
|
|
|
125 |
|
|
int
|
126 |
|
|
main(int argc, char** argv)
|
127 |
|
|
{
|
128 |
|
|
char ecosynth_tcl_path[_POSIX_PATH_MAX];
|
129 |
|
|
char** new_argv;
|
130 |
|
|
int i;
|
131 |
|
|
|
132 |
|
|
parent_pid = getppid();
|
133 |
|
|
|
134 |
|
|
// The various core Tcl scripts are installed in the same
|
135 |
|
|
// directory as ecosynth. The Tcl script itself will check whether
|
136 |
|
|
// there is a newer version of itself in the source tree and
|
137 |
|
|
// switch to that instead.
|
138 |
|
|
assert((strlen(LIBEXECDIR) + strlen(PACKAGE_INSTALL) + 20) < _POSIX_PATH_MAX);
|
139 |
|
|
strcpy(ecosynth_tcl_path, LIBEXECDIR);
|
140 |
|
|
strcat(ecosynth_tcl_path, "/ecos/");
|
141 |
|
|
strcat(ecosynth_tcl_path, PACKAGE_INSTALL);
|
142 |
|
|
strcat(ecosynth_tcl_path, "/ecosynth.tcl");
|
143 |
|
|
|
144 |
|
|
// Installation sanity checks.
|
145 |
|
|
if (0 != access(ecosynth_tcl_path, F_OK)) {
|
146 |
|
|
fprintf(stderr, PROGNAME ": error, a required Tcl script has not been installed.\n");
|
147 |
|
|
fprintf(stderr, " The script is \"%s\"\n", ecosynth_tcl_path);
|
148 |
|
|
exit(EXIT_FAILURE);
|
149 |
|
|
}
|
150 |
|
|
if (0 != access(ecosynth_tcl_path, R_OK)) {
|
151 |
|
|
fprintf(stderr, PROGNAME ": error, no read access to a required Tcl script.\n");
|
152 |
|
|
fprintf(stderr, " The script is \"%s\"\n", ecosynth_tcl_path);
|
153 |
|
|
exit(EXIT_FAILURE);
|
154 |
|
|
}
|
155 |
|
|
|
156 |
|
|
// Look for options -nw and -w. This information is needed by the appinit() routine.
|
157 |
|
|
no_windows = 0;
|
158 |
|
|
for (i = 1; i < argc; i++) {
|
159 |
|
|
if ((0 == strcmp("-nw", argv[i])) || (0 == strcmp("--nw", argv[i])) ||
|
160 |
|
|
(0 == strcmp("-no-windows", argv[i])) || (0 == strcmp("--no-windows", argv[i]))) {
|
161 |
|
|
no_windows = 1;
|
162 |
|
|
} else if ((0 == strcmp("-w", argv[i])) || (0 == strcmp("--w", argv[i])) ||
|
163 |
|
|
(0 == strcmp("-windows", argv[i])) || (0 == strcmp("--windows", argv[i]))) {
|
164 |
|
|
no_windows = 0;
|
165 |
|
|
}
|
166 |
|
|
}
|
167 |
|
|
|
168 |
|
|
new_argv = malloc((argc+2) * sizeof(char*));
|
169 |
|
|
if (NULL == new_argv) {
|
170 |
|
|
fprintf(stderr, PROGNAME ": internal error, out of memory.\n");
|
171 |
|
|
exit(EXIT_FAILURE);
|
172 |
|
|
}
|
173 |
|
|
new_argv[0] = argv[0];
|
174 |
|
|
new_argv[1] = ecosynth_tcl_path;
|
175 |
|
|
for (i = 1; i < argc; i++) {
|
176 |
|
|
new_argv[i+1] = argv[i];
|
177 |
|
|
}
|
178 |
|
|
new_argv[i+1] = NULL;
|
179 |
|
|
|
180 |
|
|
// Ignore SIGINT requests. Those can happen if e.g. the application is
|
181 |
|
|
// ctrl-C'd or if a gdb session is interrupted because this process is
|
182 |
|
|
// a child of the eCos application. Instead the normal code for handling
|
183 |
|
|
// application termination needs to run.
|
184 |
|
|
signal(SIGINT, SIG_IGN);
|
185 |
|
|
|
186 |
|
|
// Similarly ignore SIGTSTP if running in graphical mode, it would
|
187 |
|
|
// be inappropriate for the GUI to freeze if the eCos application is
|
188 |
|
|
// suspended. If running in text mode then it is better for both
|
189 |
|
|
// the application and the I/O auxiliary to freeze, halting any further
|
190 |
|
|
// output.
|
191 |
|
|
if (!no_windows) {
|
192 |
|
|
signal(SIGTSTP, SIG_IGN);
|
193 |
|
|
}
|
194 |
|
|
|
195 |
|
|
Tcl_Main(argc+1, new_argv, &ecosynth_appinit);
|
196 |
|
|
return EXIT_SUCCESS;
|
197 |
|
|
}
|
198 |
|
|
|
199 |
|
|
|
200 |
|
|
// ----------------------------------------------------------------------------
|
201 |
|
|
// Commands for the Tcl interpreter. These are few and far between because
|
202 |
|
|
// as much work as possible is done in Tcl.
|
203 |
|
|
|
204 |
|
|
// Send a SIGIO signal to the parent process. This is done whenever a new
|
205 |
|
|
// interrupt is pending. There is a possibility of strange behaviour if
|
206 |
|
|
// the synthetic target application is exiting at just the wrong moment
|
207 |
|
|
// and this process has become a zombie. An alternative approach would
|
208 |
|
|
// involve loading the Extended Tcl extension.
|
209 |
|
|
static int
|
210 |
|
|
ecosynth_send_SIGIO(ClientData clientData __attribute__ ((unused)),
|
211 |
|
|
Tcl_Interp* interp,
|
212 |
|
|
int argc,
|
213 |
|
|
char** argv __attribute__ ((unused)))
|
214 |
|
|
{
|
215 |
|
|
if (1 != argc) {
|
216 |
|
|
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_send_SIGIO\"" , TCL_STATIC);
|
217 |
|
|
return TCL_ERROR;
|
218 |
|
|
}
|
219 |
|
|
|
220 |
|
|
(void) kill(parent_pid, SIGIO);
|
221 |
|
|
return TCL_OK;
|
222 |
|
|
}
|
223 |
|
|
|
224 |
|
|
// Similarly send a SIGKILL (-9) to the parent process. This allows the GUI
|
225 |
|
|
// code to kill of the eCos application
|
226 |
|
|
static int
|
227 |
|
|
ecosynth_send_SIGKILL(ClientData clientData __attribute__ ((unused)),
|
228 |
|
|
Tcl_Interp* interp,
|
229 |
|
|
int argc,
|
230 |
|
|
char** argv __attribute__ ((unused)))
|
231 |
|
|
{
|
232 |
|
|
if (1 != argc) {
|
233 |
|
|
Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_send_SIGIO\"" , TCL_STATIC);
|
234 |
|
|
return TCL_ERROR;
|
235 |
|
|
}
|
236 |
|
|
|
237 |
|
|
(void) kill(parent_pid, SIGKILL);
|
238 |
|
|
return TCL_OK;
|
239 |
|
|
}
|
240 |
|
|
|
241 |
|
|
|
242 |
|
|
// ----------------------------------------------------------------------------
|
243 |
|
|
// Application-specific initialization.
|
244 |
|
|
|
245 |
|
|
static int
|
246 |
|
|
ecosynth_appinit(Tcl_Interp* interp)
|
247 |
|
|
{
|
248 |
|
|
Tcl_Channel from_app;
|
249 |
|
|
Tcl_Channel to_app;
|
250 |
|
|
|
251 |
|
|
// Tcl library initialization. This has the effect of executing init.tcl,
|
252 |
|
|
// thus setting up package load paths etc. Not all of that initialization
|
253 |
|
|
// is necessarily appropriate for ecosynth, but some devices may well want
|
254 |
|
|
// to load in additional packages or whatever.
|
255 |
|
|
if (Tcl_Init(interp) == TCL_ERROR) {
|
256 |
|
|
return TCL_ERROR;
|
257 |
|
|
}
|
258 |
|
|
// Optionally initialize Tk as well. This can be suppressed by an
|
259 |
|
|
// argument -nw. Possibly this could be done by the Tcl script
|
260 |
|
|
// instead using dynamic loading.
|
261 |
|
|
//
|
262 |
|
|
// There is a problem with the way that Tk does its argument processing.
|
263 |
|
|
// By default it will accept abbreviations for the standard wish arguments,
|
264 |
|
|
// so if the user specifies e.g. -v then the Tk code will interpret this
|
265 |
|
|
// as an abbreviation for -visual, and will probably complain because
|
266 |
|
|
// -visual takes an argument whereas ecosynth's -v is just a flag.
|
267 |
|
|
//
|
268 |
|
|
// The Tk argument processing can be suppressed simply by temporarily
|
269 |
|
|
// getting rid of the argv variable. The disadvantage is that some
|
270 |
|
|
// standard arguments now have to be processed explicitly:
|
271 |
|
|
//
|
272 |
|
|
// -colormap map ignored, of little interest these days
|
273 |
|
|
// -display display currently ignored. This would have to be
|
274 |
|
|
// implemented at the C level, not the Tcl level,
|
275 |
|
|
// since Tk_Init() will start interacting with the
|
276 |
|
|
// X server. Its implementation would involve a
|
277 |
|
|
// setenv() call.
|
278 |
|
|
// -geometry geom implemented in the Tcl code using "wm geometry"
|
279 |
|
|
// -name name ignored for now.
|
280 |
|
|
// -sync ignored, of little interest to typical users
|
281 |
|
|
// -use id ignored, probably of little interest for now
|
282 |
|
|
// -visual visual ignored, of little interest these days
|
283 |
|
|
//
|
284 |
|
|
// so actually the only extra work that is required is for the Tcl code
|
285 |
|
|
// to process -geometry.
|
286 |
|
|
if (!no_windows) {
|
287 |
|
|
Tcl_Obj* argv = Tcl_GetVar2Ex(interp, "argv", NULL, TCL_GLOBAL_ONLY);
|
288 |
|
|
Tcl_IncrRefCount(argv);
|
289 |
|
|
Tcl_UnsetVar(interp, "argv", TCL_GLOBAL_ONLY);
|
290 |
|
|
if (Tk_Init(interp) == TCL_ERROR) {
|
291 |
|
|
return TCL_ERROR;
|
292 |
|
|
}
|
293 |
|
|
Tcl_SetVar2Ex(interp, "argv", NULL, argv, TCL_GLOBAL_ONLY);
|
294 |
|
|
Tcl_DecrRefCount(argv);
|
295 |
|
|
}
|
296 |
|
|
|
297 |
|
|
// Create the synth:: namespace. Currently this does not seem to be possible from
|
298 |
|
|
// inside C.
|
299 |
|
|
if (TCL_OK != Tcl_Eval(interp,
|
300 |
|
|
"namespace eval synth {\n"
|
301 |
|
|
" variable channel_from_app 0\n"
|
302 |
|
|
" variable channel_to_app 0\n"
|
303 |
|
|
"}\n")) {
|
304 |
|
|
fprintf(stderr, PROGNAME ": internal error, failed to create Tcl synth:: namespace\n");
|
305 |
|
|
fprintf(stderr, " Please check Tcl version (8.3 or later required).\n");
|
306 |
|
|
exit(EXIT_FAILURE);
|
307 |
|
|
}
|
308 |
|
|
|
309 |
|
|
// The pipe to/from the application is exposed to Tcl, allowing
|
310 |
|
|
// the main communication to be handled at the Tcl level and
|
311 |
|
|
// specifically via the event loop. This requires two additional
|
312 |
|
|
// Tcl channels to be created for file descriptors 3 and 4.
|
313 |
|
|
from_app = Tcl_MakeFileChannel((ClientData) 3, TCL_READABLE);
|
314 |
|
|
if (NULL == from_app) {
|
315 |
|
|
fprintf(stderr, PROGNAME ": internal error, failed to create Tcl channel for pipe from application.\n");
|
316 |
|
|
exit(EXIT_FAILURE);
|
317 |
|
|
}
|
318 |
|
|
Tcl_RegisterChannel(interp, from_app);
|
319 |
|
|
|
320 |
|
|
// The channel name will be something like file0. Add a variable to the
|
321 |
|
|
// interpreter to store this name.
|
322 |
|
|
if (NULL == Tcl_SetVar(interp, "synth::_channel_from_app", Tcl_GetChannelName(from_app), TCL_GLOBAL_ONLY)) {
|
323 |
|
|
fprintf(stderr, PROGNAME ": internal error, failed to create Tcl variable from application channel 0\n");
|
324 |
|
|
exit(EXIT_FAILURE);
|
325 |
|
|
}
|
326 |
|
|
|
327 |
|
|
// Repeat for the channel to the application.
|
328 |
|
|
to_app = Tcl_MakeFileChannel((ClientData) 4, TCL_WRITABLE);
|
329 |
|
|
if (NULL == to_app) {
|
330 |
|
|
fprintf(stderr, PROGNAME ": internal error, failed to create Tcl channel for pipe to application.\n");
|
331 |
|
|
exit(EXIT_FAILURE);
|
332 |
|
|
}
|
333 |
|
|
Tcl_RegisterChannel(interp, to_app);
|
334 |
|
|
if (NULL == Tcl_SetVar(interp, "synth::_channel_to_app", Tcl_GetChannelName(to_app), TCL_GLOBAL_ONLY)) {
|
335 |
|
|
fprintf(stderr, PROGNAME ": internal error, failed to create Tcl variable from application channel 1\n");
|
336 |
|
|
exit(EXIT_FAILURE);
|
337 |
|
|
}
|
338 |
|
|
|
339 |
|
|
// The auxiliary may spawn additional programs, via
|
340 |
|
|
// device-specific scripts. Those programs should not have
|
341 |
|
|
// direct access to the pipe - that would just lead to
|
342 |
|
|
// confusion.
|
343 |
|
|
fcntl(3, F_SETFD, FD_CLOEXEC);
|
344 |
|
|
fcntl(4, F_SETFD, FD_CLOEXEC);
|
345 |
|
|
|
346 |
|
|
// Add synthetic target-specific commands to the Tcl interpreter.
|
347 |
|
|
Tcl_CreateCommand(interp, "synth::_send_SIGIO", &ecosynth_send_SIGIO, (ClientData)NULL, (Tcl_CmdDeleteProc*) NULL);
|
348 |
|
|
Tcl_CreateCommand(interp, "synth::_send_SIGKILL", &ecosynth_send_SIGKILL, (ClientData)NULL, (Tcl_CmdDeleteProc*) NULL);
|
349 |
|
|
|
350 |
|
|
// Define additional variables.
|
351 |
|
|
|
352 |
|
|
// The version, from the AM_INIT_AUTOMAKE() macro in configure.in
|
353 |
|
|
Tcl_SetVar(interp, "synth::_ecosynth_version", ECOSYNTH_VERSION, TCL_GLOBAL_ONLY);
|
354 |
|
|
|
355 |
|
|
// The parent process id, i.e. that for the eCos application itself.
|
356 |
|
|
Tcl_SetVar2Ex(interp, "synth::_ppid", NULL, Tcl_NewIntObj(getppid()), TCL_GLOBAL_ONLY);
|
357 |
|
|
|
358 |
|
|
// The various directories
|
359 |
|
|
Tcl_SetVar(interp, "synth::_ecosynth_repository", ECOS_REPOSITORY, TCL_GLOBAL_ONLY);
|
360 |
|
|
Tcl_SetVar(interp, "synth::_ecosynth_libexecdir", LIBEXECDIR, TCL_GLOBAL_ONLY);
|
361 |
|
|
Tcl_SetVar(interp, "synth::_ecosynth_package_dir", PACKAGE_DIR, TCL_GLOBAL_ONLY);
|
362 |
|
|
Tcl_SetVar(interp, "synth::_ecosynth_package_version", PACKAGE_VERSION, TCL_GLOBAL_ONLY);
|
363 |
|
|
Tcl_SetVar(interp, "synth::_ecosynth_package_install", PACKAGE_INSTALL, TCL_GLOBAL_ONLY);
|
364 |
|
|
|
365 |
|
|
return TCL_OK;
|
366 |
|
|
}
|