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

Last change on this file since 607 was 503, checked in by nkissebe, 18 years ago

Converted to Tcl Extension Architecture (TEA) v3.5 for base Windows support. Added Windows ports of getrlimit, getrusage, gettimeofday, setrlimit functions.

File size: 10.1 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
45#ifdef _WIN32
46#define SIGHUP -1
47#define SIGQUIT -1
48#define SIGKILL -1
49#define SIGPIPE -1
50#define SIGALRM -1
51#define SIGUSR1 -1
52#define SIGUSR2 -1
53#define SIGCHLD -1
54#define SIGCONT -1
55#define SIGSTOP -1
56#define SIGTSTP -1
57#define SIGTTIN -1
58#define SIGTTOU -1
59#endif
60
61static RpSignalNames signalNames[] = {
62    {"SIGHUP",  SIGHUP},   /* Hangup detected on controlling terminal */
63    {"SIGINT",  SIGINT},   /* Interrupt from keyboard */
64    {"SIGQUIT", SIGQUIT},  /* Quit from keyboard */
65    {"SIGILL",  SIGILL},   /* Illegal Instruction */
66    {"SIGABRT", SIGABRT},  /* Abort signal from abort(3) */
67    {"SIGFPE",  SIGFPE},   /* Floating point exception */
68    {"SIGKILL", SIGKILL},  /* Kill signal */
69    {"SIGSEGV", SIGSEGV},  /* Invalid memory reference */
70    {"SIGPIPE", SIGPIPE},  /* Broken pipe: write to pipe with no readers */
71    {"SIGALRM", SIGALRM},  /* Timer signal from alarm(2) */
72    {"SIGTERM", SIGTERM},  /* Termination signal */
73    {"SIGUSR1", SIGUSR1},  /* User-defined signal 1 */
74    {"SIGUSR2", SIGUSR2},  /* User-defined signal 2 */
75    {"SIGCHLD", SIGCHLD},  /* Child stopped or terminated */
76    {"SIGCONT", SIGCONT},  /* Continue if stopped */
77    {"SIGSTOP", SIGSTOP},  /* Stop process */
78    {"SIGTSTP", SIGTSTP},  /* Stop typed at tty */
79    {"SIGTTIN", SIGTTIN},  /* tty input for background process */
80    {"SIGTTOU", SIGTTOU},  /* tty output for background process */
81    {(char*)NULL, 0}
82};
83
84/*
85 * ------------------------------------------------------------------------
86 *  RpSignal_Init()
87 *
88 *  Called in RapptureGUI_Init() to initialize the commands defined
89 *  in this file.
90 * ------------------------------------------------------------------------
91 */
92int
93RpSignal_Init(interp)
94    Tcl_Interp *interp;  /* interpreter being initialized */
95{
96    int i, isnew;
97    Tcl_HashTable *htPtr;
98    Tcl_HashEntry *entryPtr;
99
100    if (SigInfoPtr != NULL) {
101        Tcl_AppendResult(interp, "signals already being handled ",
102            "by another interpreter", (char*)NULL);
103        return TCL_ERROR;
104    }
105
106    /* create the table of signal handlers */
107    SigInfoPtr = (RpSignals*)ckalloc(sizeof(RpSignals));
108    SigInfoPtr->interp = interp;
109    SigInfoPtr->handlers = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
110    Tcl_InitHashTable(SigInfoPtr->handlers, TCL_STRING_KEYS);
111
112    for (i=0; signalNames[i].name != NULL; i++) {
113        entryPtr = Tcl_CreateHashEntry(SigInfoPtr->handlers,
114            signalNames[i].name, &isnew);
115
116        htPtr = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
117        Tcl_InitHashTable(htPtr, TCL_STRING_KEYS);
118
119        Tcl_SetHashValue(entryPtr, (char*)htPtr);
120    }
121
122    /* install the signal command */
123    Tcl_CreateCommand(interp, "::Rappture::signal", RpSignalCmd,
124        (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
125
126    return TCL_OK;
127}
128
129/*
130 * ------------------------------------------------------------------------
131 *  RpSignalCmd()
132 *
133 *  Invoked whenever someone uses the "signal" command to get/set
134 *  limits for child processes.  Handles the following syntax:
135 *
136 *      signal ?<signal>? ?<handler>? ?<callback>?
137 *
138 *  Returns TCL_OK on success, and TCL_ERROR (along with an error
139 *  message in the interpreter) if anything goes wrong.
140 * ------------------------------------------------------------------------
141 */
142static int
143RpSignalCmd(cdata, interp, argc, argv)
144    ClientData cdata;         /* not used */
145    Tcl_Interp *interp;       /* interpreter handling this request */
146    int argc;                 /* number of command line args */
147    CONST84 char *argv[];     /* strings for command line args */
148{
149    Tcl_HashTable *htPtr;
150    Tcl_HashEntry *entryPtr;
151    Tcl_HashSearch pos;
152    int i, isnew, signum;
153
154    /*
155     * HANDLE: signal
156     *   return a list of all signal names
157     */
158    if (argc < 2) {
159        for (i=0; signalNames[i].name != NULL; i++) {
160            Tcl_AppendElement(interp, signalNames[i].name);
161        }
162        return TCL_OK;
163    }
164
165    /*
166     * HANDLE: signal <signal>
167     *   return a list of handler names for one signal
168     */
169    if (argc < 3) {
170        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, argv[1]);
171        if (entryPtr == NULL) {
172            return RpSignalOptionError(interp, argv[1]);
173        }
174
175        htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
176        entryPtr = Tcl_FirstHashEntry(htPtr, &pos);
177        while (entryPtr != NULL) {
178            Tcl_AppendElement(interp, Tcl_GetHashKey(htPtr, entryPtr));
179            entryPtr = Tcl_NextHashEntry(&pos);
180        }
181        return TCL_OK;
182    }
183
184    /*
185     * HANDLE: signal <signal> <handler>
186     *   return the command for a certain handler on a signal
187     */
188    if (argc < 4) {
189        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, argv[1]);
190        if (entryPtr == NULL) {
191            return RpSignalOptionError(interp, argv[1]);
192        }
193
194        htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
195        entryPtr = Tcl_FindHashEntry(htPtr, argv[2]);
196        if (entryPtr == NULL) {
197            Tcl_AppendResult(interp, "handler \"", argv[2],
198                "\" not defined", (char*)NULL);
199            return TCL_ERROR;
200        }
201        Tcl_SetResult(interp, (char*)Tcl_GetHashValue(entryPtr), TCL_VOLATILE);
202        return TCL_OK;
203    }
204
205    /*
206     * HANDLE: signal <signal> <handler> <command>
207     *   define the command handler for a signal
208     */
209    if (argc < 5) {
210        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, argv[1]);
211        if (entryPtr == NULL) {
212            return RpSignalOptionError(interp, argv[1]);
213        }
214
215        htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
216        entryPtr = Tcl_CreateHashEntry(htPtr, argv[2], &isnew);
217        if (*argv[3] == '\0') {
218            if (!isnew) {
219                free((void*)Tcl_GetHashValue(entryPtr));
220            }
221            Tcl_DeleteHashEntry(entryPtr);
222        } else {
223            Tcl_SetHashValue(entryPtr, (ClientData)strdup(argv[3]));
224        }
225
226        /*
227         * If there are any handlers for this signal, then install
228         * a signal handler.  Otherwise, remove the signal handler.
229         */
230        signum = -1;
231        for (i=0; signalNames[i].name != NULL; i++) {
232            if (strcmp(argv[1], signalNames[i].name) == 0) {
233                signum = signalNames[i].signum;
234                break;
235            }
236        }
237        if (Tcl_FirstHashEntry(htPtr, &pos) != NULL) {
238            (void) signal(signum, RpSignalHandler);
239        } else {
240            (void) signal(signum, SIG_DFL);
241        }
242        return TCL_OK;
243    }
244
245    Tcl_AppendResult(interp, "wrong # args: should be \"",
246        argv[0], " ?signal? ?handler? ?command?\"", (char*)NULL);
247    return TCL_ERROR;
248}
249
250/*
251 * ------------------------------------------------------------------------
252 *  RpSignalHandler()
253 *
254 *  Invoked whenever the "signal" command is deleted, to clean up
255 *  all storage in the handler table.
256 * ------------------------------------------------------------------------
257 */
258static void
259RpSignalHandler(signum)
260    int signum;               /* signal being handled */
261{
262    int i, status;
263    Tcl_HashTable *htPtr;
264    Tcl_HashEntry *entryPtr;
265    Tcl_HashSearch pos;
266    char *cmd;
267
268    for (i=0; signalNames[i].name != NULL; i++) {
269        if (signalNames[i].signum == signum) {
270            break;
271        }
272    }
273    if (signalNames[i].name != NULL) {
274        entryPtr = Tcl_FindHashEntry(SigInfoPtr->handlers, signalNames[i].name);
275        if (entryPtr) {
276            htPtr = (Tcl_HashTable*)Tcl_GetHashValue(entryPtr);
277
278            /* execute all of the handlers associated with this signal */
279            entryPtr = Tcl_FirstHashEntry(htPtr, &pos);
280            while (entryPtr) {
281                cmd = (char*)Tcl_GetHashValue(entryPtr);
282
283                status = Tcl_Eval(SigInfoPtr->interp, cmd);
284                if (status != TCL_OK) {
285                    Tcl_BackgroundError(SigInfoPtr->interp);
286                }
287                entryPtr = Tcl_NextHashEntry(&pos);
288            }
289        }
290    }
291    Tcl_ResetResult(SigInfoPtr->interp);
292}
293
294/*
295 * ------------------------------------------------------------------------
296 *  RpSignalOptionError()
297 *
298 *  Used internally to set an error message in the interpreter
299 *  describing an unrecognized signal option.  Sets the result of
300 *  the Tcl interpreter and returns an error status code.
301 * ------------------------------------------------------------------------
302 */
303static int
304RpSignalOptionError(interp, option)
305    Tcl_Interp *interp;       /* interpreter handling this request */
306    CONST84 char *option;     /* bad option supplied by the user */
307{
308    int i;
309
310    Tcl_AppendResult(interp, "bad signal \"", option,
311        "\": should be one of ",(char*)NULL);
312
313    for (i=0; signalNames[i].name != NULL; i++) {
314        if (i > 0) {
315            Tcl_AppendResult(interp, ", ", (char*)NULL);
316        }
317        Tcl_AppendResult(interp, signalNames[i].name, (char*)NULL);
318    }
319    return TCL_ERROR;
320}
Note: See TracBrowser for help on using the repository browser.