source: trunk/gui/src/RpSignal.c @ 193

Last change on this file since 193 was 193, checked in by mmc, 19 years ago
  • Added "Upload..." capability to the <loader> widget. You can now add an <upload> directive containing the path of the control that will receive data. A file is uploaded from the desktop and saved in the specified control.
  • Fixed download capability to work properly for all result types, including contour plots and molecules.
  • Added rappture::signal so we can catch SIGHUP and clean up the upload/download spool directory.
File size: 9.8 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  Rappture::signal
4 *
5 *  This is an interface to the system signal() function, which allows
6 *  you to catch and handle system signals.  We use this in Rappture
7 *  to catch a SIGHUP and clean up scratch files.
8 * ======================================================================
9 *  AUTHOR:  Michael McLennan, Purdue University
10 *  Copyright (c) 2004-2006  Purdue Research Foundation
11 *
12 *  See the file "license.terms" for information on usage and
13 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 * ======================================================================
15 */
16#include <tcl.h>
17#include <stdlib.h>
18#include <string.h>
19#include <signal.h>
20
21static int RpSignalCmd _ANSI_ARGS_((ClientData cdata,
22    Tcl_Interp *interp, int argc, CONST84 char *argv[]));
23static void RpSignalHandler _ANSI_ARGS_((int signum));
24static int RpSignalOptionError _ANSI_ARGS_((Tcl_Interp *interp,
25    CONST84 char *option));
26
27/*
28 * Global table of all known signal handlers:
29 */
30typedef struct RpSignals {
31    Tcl_Interp *interp;
32    Tcl_HashTable *handlers;
33} RpSignals;
34
35static RpSignals *SigInfoPtr = NULL;
36
37/*
38 * signal configuration options:
39 */
40typedef struct RpSignalNames {
41    char *name;
42    int signum;
43} RpSignalNames;
44
45static RpSignalNames signalNames[] = {
46    {"SIGHUP",  SIGHUP},   /* Hangup detected on controlling terminal */
47    {"SIGINT",  SIGINT},   /* Interrupt from keyboard */
48    {"SIGQUIT", SIGQUIT},  /* Quit from keyboard */
49    {"SIGILL",  SIGILL},   /* Illegal Instruction */
50    {"SIGABRT", SIGABRT},  /* Abort signal from abort(3) */
51    {"SIGFPE",  SIGFPE},   /* Floating point exception */
52    {"SIGKILL", SIGKILL},  /* Kill signal */
53    {"SIGSEGV", SIGSEGV},  /* Invalid memory reference */
54    {"SIGPIPE", SIGPIPE},  /* Broken pipe: write to pipe with no readers */
55    {"SIGALRM", SIGALRM},  /* Timer signal from alarm(2) */
56    {"SIGTERM", SIGTERM},  /* Termination signal */
57    {"SIGUSR1", SIGUSR1},  /* User-defined signal 1 */
58    {"SIGUSR2", SIGUSR2},  /* User-defined signal 2 */
59    {"SIGCHLD", SIGCHLD},  /* Child stopped or terminated */
60    {"SIGCONT", SIGCONT},  /* Continue if stopped */
61    {"SIGSTOP", SIGSTOP},  /* Stop process */
62    {"SIGTSTP", SIGTSTP},  /* Stop typed at tty */
63    {"SIGTTIN", SIGTTIN},  /* tty input for background process */
64    {"SIGTTOU", SIGTTOU},  /* tty output for background process */
65    {(char*)NULL, 0}
66};
67
68/*
69 * ------------------------------------------------------------------------
70 *  RpSignal_Init()
71 *
72 *  Called in RapptureGUI_Init() to initialize the commands defined
73 *  in this file.
74 * ------------------------------------------------------------------------
75 */
76int
77RpSignal_Init(interp)
78    Tcl_Interp *interp;  /* interpreter being initialized */
79{
80    int i, isnew;
81    Tcl_HashTable *htPtr;
82    Tcl_HashEntry *entryPtr;
83
84    if (SigInfoPtr != NULL) {
85        Tcl_AppendResult(interp, "signals already being handled ",
86            "by another interpreter", (char*)NULL);
87        return TCL_ERROR;
88    }
89
90    /* create the table of signal handlers */
91    SigInfoPtr = (RpSignals*)ckalloc(sizeof(RpSignals));
92    SigInfoPtr->interp = interp;
93    SigInfoPtr->handlers = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
94    Tcl_InitHashTable(SigInfoPtr->handlers, TCL_STRING_KEYS);
95
96    for (i=0; signalNames[i].name != NULL; i++) {
97        entryPtr = Tcl_CreateHashEntry(SigInfoPtr->handlers,
98            signalNames[i].name, &isnew);
99
100        htPtr = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
101        Tcl_InitHashTable(htPtr, TCL_STRING_KEYS);
102
103        Tcl_SetHashValue(entryPtr, (char*)htPtr);
104    }
105
106    /* install the signal command */
107    Tcl_CreateCommand(interp, "::Rappture::signal", RpSignalCmd,
108        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
109
110    return TCL_OK;
111}
112
113/*
114 * ------------------------------------------------------------------------
115 *  RpSignalCmd()
116 *
117 *  Invoked whenever someone uses the "signal" command to get/set
118 *  limits for child processes.  Handles the following syntax:
119 *
120 *      signal ?<signal>? ?<handler>? ?<callback>?
121 *
122 *  Returns TCL_OK on success, and TCL_ERROR (along with an error
123 *  message in the interpreter) if anything goes wrong.
124 * ------------------------------------------------------------------------
125 */
126static int
127RpSignalCmd(cdata, interp, argc, argv)
128    ClientData cdata;         /* not used */
129    Tcl_Interp *interp;       /* interpreter handling this request */
130    int argc;                 /* number of command line args */
131    CONST84 char *argv[];     /* strings for command line args */
132{
133    Tcl_HashTable *htPtr;
134    Tcl_HashEntry *entryPtr;
135    Tcl_HashSearch pos;
136    int i, isnew, signum;
137
138    /*
139     * HANDLE: signal
140     *   return a list of all signal names
141     */
142    if (argc < 2) {
143        for (i=0; signalNames[i].name != NULL; i++) {
144            Tcl_AppendElement(interp, signalNames[i].name);
145        }
146        return TCL_OK;
147    }
148
149    /*
150     * HANDLE: signal <signal>
151     *   return a list of handler names for one signal
152     */
153    if (argc < 3) {
154        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, argv[1]);
155        if (entryPtr == NULL) {
156            return RpSignalOptionError(interp, argv[1]);
157        }
158
159        htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
160        entryPtr = Tcl_FirstHashEntry(htPtr, &pos);
161        while (entryPtr != NULL) {
162            Tcl_AppendElement(interp, Tcl_GetHashKey(htPtr, entryPtr));
163            entryPtr = Tcl_NextHashEntry(&pos);
164        }
165        return TCL_OK;
166    }
167
168    /*
169     * HANDLE: signal <signal> <handler>
170     *   return the command for a certain handler on a signal
171     */
172    if (argc < 4) {
173        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, argv[1]);
174        if (entryPtr == NULL) {
175            return RpSignalOptionError(interp, argv[1]);
176        }
177
178        htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
179        entryPtr = Tcl_FindHashEntry(htPtr, argv[2]);
180        if (entryPtr == NULL) {
181            Tcl_AppendResult(interp, "handler \"", argv[2],
182                "\" not defined", (char*)NULL);
183            return TCL_ERROR;
184        }
185        Tcl_SetResult(interp, (char*)Tcl_GetHashValue(entryPtr), TCL_VOLATILE);
186        return TCL_OK;
187    }
188
189    /*
190     * HANDLE: signal <signal> <handler> <command>
191     *   define the command handler for a signal
192     */
193    if (argc < 5) {
194        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, argv[1]);
195        if (entryPtr == NULL) {
196            return RpSignalOptionError(interp, argv[1]);
197        }
198
199        htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
200        entryPtr = Tcl_CreateHashEntry(htPtr, argv[2], &isnew);
201        if (*argv[3] == '\0') {
202            if (!isnew) {
203                free((void*)Tcl_GetHashValue(entryPtr));
204            }
205            Tcl_DeleteHashEntry(entryPtr);
206        } else {
207            Tcl_SetHashValue(entryPtr, (ClientData)strdup(argv[3]));
208        }
209
210        /*
211         * If there are any handlers for this signal, then install
212         * a signal handler.  Otherwise, remove the signal handler.
213         */
214        signum = -1;
215        for (i=0; signalNames[i].name != NULL; i++) {
216            if (strcmp(argv[1], signalNames[i].name) == 0) {
217                signum = signalNames[i].signum;
218                break;
219            }
220        }
221        if (Tcl_FirstHashEntry(htPtr, &pos) != NULL) {
222            (void) signal(signum, RpSignalHandler);
223        } else {
224            (void) signal(signum, SIG_DFL);
225        }
226        return TCL_OK;
227    }
228
229    Tcl_AppendResult(interp, "wrong # args: should be \"",
230        argv[0], " ?signal? ?handler? ?command?\"", (char*)NULL);
231    return TCL_ERROR;
232}
233
234/*
235 * ------------------------------------------------------------------------
236 *  RpSignalHandler()
237 *
238 *  Invoked whenever the "signal" command is deleted, to clean up
239 *  all storage in the handler table.
240 * ------------------------------------------------------------------------
241 */
242static void
243RpSignalHandler(signum)
244    int signum;               /* signal being handled */
245{
246    int i, status;
247    Tcl_HashTable *htPtr;
248    Tcl_HashEntry *entryPtr;
249    Tcl_HashSearch pos;
250    char *cmd;
251
252    for (i=0; signalNames[i].name != NULL; i++) {
253        if (signalNames[i].signum == signum) {
254            break;
255        }
256    }
257    if (signalNames[i].name != NULL) {
258        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, signalNames[i].name);
259        if (entryPtr) {
260            htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
261
262            /* execute all of the handlers associated with this signal */
263            entryPtr = Tcl_FirstHashEntry(htPtr, &pos);
264            while (entryPtr) {
265                cmd = (char*)Tcl_GetHashValue(entryPtr);
266
267                status = Tcl_Eval(SigInfoPtr->interp, cmd);
268                if (status != TCL_OK) {
269                    Tcl_BackgroundError(SigInfoPtr->interp);
270                }
271                entryPtr = Tcl_NextHashEntry(&pos);
272            }
273        }
274    }
275    Tcl_ResetResult(SigInfoPtr->interp);
276}
277
278/*
279 * ------------------------------------------------------------------------
280 *  RpSignalOptionError()
281 *
282 *  Used internally to set an error message in the interpreter
283 *  describing an unrecognized signal option.  Sets the result of
284 *  the Tcl interpreter and returns an error status code.
285 * ------------------------------------------------------------------------
286 */
287static int
288RpSignalOptionError(interp, option)
289    Tcl_Interp *interp;       /* interpreter handling this request */
290    CONST84 char *option;     /* bad option supplied by the user */
291{
292    int i;
293
294    Tcl_AppendResult(interp, "bad signal \"", option,
295        "\": should be one of ",(char*)NULL);
296
297    for (i=0; signalNames[i].name != NULL; i++) {
298        if (i > 0) {
299            Tcl_AppendResult(interp, ", ", (char*)NULL);
300        }
301        Tcl_AppendResult(interp, signalNames[i].name, (char*)NULL);
302    }
303    return TCL_ERROR;
304}
Note: See TracBrowser for help on using the repository browser.