source: trunk/lang/tcl/src/RpSignal.c @ 1264

Last change on this file since 1264 was 1018, checked in by gah, 16 years ago

Massive changes: New directory/file layout

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