OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [hal/] [synth/] [arch/] [v2_0/] [host/] [ecosynth.c] - Blame information for rev 597

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.