13 #include "jimautoconf.h"
14 #include "jim-subcmd.h"
15 #include "jim-signal.h"
17 #define MAX_SIGNALS (sizeof(jim_wide) * 8)
19 static jim_wide
*sigloc
;
20 static jim_wide sigsblocked
;
21 static struct sigaction
*sa_old
;
22 static int signal_handling
[MAX_SIGNALS
];
24 /* Make sure to do this as a wide, not int */
25 #define sig_to_bit(SIG) ((jim_wide)1 << (SIG))
27 static void signal_handler(int sig
)
29 /* We just remember which signals occurred. Jim_Eval() will
30 * notice this as soon as it can and throw an error
32 *sigloc
|= sig_to_bit(sig
);
35 static void signal_ignorer(int sig
)
37 /* We just remember which signals occurred */
38 sigsblocked
|= sig_to_bit(sig
);
42 *----------------------------------------------------------------------
46 * Return a textual identifier for a signal number.
49 * This procedure returns a machine-readable textual identifier
50 * that corresponds to sig. The identifier is the same as the
51 * #define name in signal.h.
56 *----------------------------------------------------------------------
58 #define CHECK_SIG(NAME) if (sig == NAME) return #NAME
60 const char *Jim_SignalId(int sig
)
109 return "unknown signal";
112 const char *Jim_SignalName(int sig
)
114 #ifdef HAVE_SYS_SIGLIST
115 if (sig
>= 0 && sig
< NSIG
) {
116 return sys_siglist
[sig
];
119 return Jim_SignalId(sig
);
123 * Given the name of a signal, returns the signal value if found,
124 * or returns -1 (and sets an error) if not found.
125 * We accept -SIGINT, SIGINT, INT or any lowercase version or a number,
126 * either positive or negative.
128 static int find_signal_by_name(Jim_Interp
*interp
, const char *name
)
131 const char *pt
= name
;
133 /* Remove optional - and SIG from the front of the name */
137 if (strncasecmp(name
, "sig", 3) == 0) {
140 if (isdigit(UCHAR(pt
[0]))) {
142 if (i
> 0 && i
< MAX_SIGNALS
) {
147 for (i
= 1; i
< MAX_SIGNALS
; i
++) {
148 /* Jim_SignalId() returns names such as SIGINT, and
149 * returns "unknown signal id" if unknown, so this will work
151 if (strcasecmp(Jim_SignalId(i
) + 3, pt
) == 0) {
156 Jim_SetResultString(interp
, "unknown signal ", -1);
157 Jim_AppendString(interp
, Jim_GetResult(interp
), name
, -1);
162 #define SIGNAL_ACTION_HANDLE 1
163 #define SIGNAL_ACTION_IGNORE -1
164 #define SIGNAL_ACTION_DEFAULT 0
166 static int do_signal_cmd(Jim_Interp
*interp
, int action
, int argc
, Jim_Obj
*const *argv
)
172 Jim_SetResult(interp
, Jim_NewListObj(interp
, NULL
, 0));
173 for (i
= 1; i
< MAX_SIGNALS
; i
++) {
174 if (signal_handling
[i
] == action
) {
175 /* Add signal name to the list */
176 Jim_ListAppendElement(interp
, Jim_GetResult(interp
),
177 Jim_NewStringObj(interp
, Jim_SignalId(i
), -1));
183 /* Catch all the signals we care about */
184 if (action
!= SIGNAL_ACTION_DEFAULT
) {
186 sigemptyset(&sa
.sa_mask
);
187 if (action
== SIGNAL_ACTION_HANDLE
) {
188 sa
.sa_handler
= signal_handler
;
191 sa
.sa_handler
= signal_ignorer
;
195 /* Iterate through the provided signals */
196 for (i
= 0; i
< argc
; i
++) {
197 int sig
= find_signal_by_name(interp
, Jim_String(argv
[i
]));
202 if (action
!= signal_handling
[sig
]) {
203 /* Need to change the action for this signal */
205 case SIGNAL_ACTION_HANDLE
:
206 case SIGNAL_ACTION_IGNORE
:
207 if (signal_handling
[sig
] == SIGNAL_ACTION_DEFAULT
) {
209 /* Allocate the structure the first time through */
210 sa_old
= Jim_Alloc(sizeof(*sa_old
) * MAX_SIGNALS
);
212 sigaction(sig
, &sa
, &sa_old
[sig
]);
215 sigaction(sig
, &sa
, 0);
219 case SIGNAL_ACTION_DEFAULT
:
220 /* Restore old handler */
222 sigaction(sig
, &sa_old
[sig
], 0);
225 signal_handling
[sig
] = action
;
232 static int signal_cmd_handle(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
234 return do_signal_cmd(interp
, SIGNAL_ACTION_HANDLE
, argc
, argv
);
237 static int signal_cmd_ignore(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
239 return do_signal_cmd(interp
, SIGNAL_ACTION_IGNORE
, argc
, argv
);
242 static int signal_cmd_default(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
244 return do_signal_cmd(interp
, SIGNAL_ACTION_DEFAULT
, argc
, argv
);
247 static int signal_set_sigmask_result(Jim_Interp
*interp
, jim_wide sigmask
)
250 Jim_Obj
*listObj
= Jim_NewListObj(interp
, NULL
, 0);
252 for (i
= 0; i
< MAX_SIGNALS
; i
++) {
253 if (sigmask
& sig_to_bit(i
)) {
254 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, Jim_SignalId(i
), -1));
257 Jim_SetResult(interp
, listObj
);
261 static int signal_cmd_check(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
267 if (argc
> 0 && Jim_CompareStringImmediate(interp
, argv
[0], "-clear")) {
273 /* Signals specified */
274 for (i
= clear
; i
< argc
; i
++) {
275 int sig
= find_signal_by_name(interp
, Jim_String(argv
[i
]));
277 if (sig
< 0 || sig
>= MAX_SIGNALS
) {
280 mask
|= sig_to_bit(sig
);
284 /* No signals specified, so check/clear all */
288 if ((sigsblocked
& mask
) == 0) {
289 /* No matching signals, so empty result and nothing to do */
292 /* Be careful we don't have a race condition where signals are cleared but not returned */
293 blocked
= sigsblocked
& mask
;
295 sigsblocked
&= ~blocked
;
298 signal_set_sigmask_result(interp
, blocked
);
302 static int signal_cmd_throw(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
307 if ((sig
= find_signal_by_name(interp
, Jim_String(argv
[0]))) < 0) {
312 /* If the signal is ignored (blocked) ... */
313 if (signal_handling
[sig
] == SIGNAL_ACTION_IGNORE
) {
314 sigsblocked
|= sig_to_bit(sig
);
318 /* Just set the signal */
319 interp
->sigmask
|= sig_to_bit(sig
);
321 /* Set the canonical name of the signal as the result */
322 Jim_SetResultString(interp
, Jim_SignalId(sig
), -1);
324 /* And simply say we caught the signal */
329 *-----------------------------------------------------------------------------
332 * Implements the TCL signal command:
333 * signal handle|ignore|default|throw ?signals ...?
334 * signal throw signal
336 * Specifies which signals are handled by Tcl code.
337 * If the one of the given signals is caught, it causes a JIM_SIGNAL
338 * exception to be thrown which can be caught by catch.
340 * Use 'signal ignore' to ignore the signal(s)
341 * Use 'signal default' to go back to the default behaviour
342 * Use 'signal throw signal' to raise the given signal
344 * If no arguments are given, returns the list of signals which are being handled
347 * Standard TCL results.
349 *-----------------------------------------------------------------------------
351 static const jim_subcmd_type signal_command_table
[] = {
353 .args
= "?signals ...?",
354 .function
= signal_cmd_handle
,
357 .description
= "Lists handled signals, or adds to handled signals"
360 .args
= "?signals ...?",
361 .function
= signal_cmd_ignore
,
364 .description
= "Lists ignored signals, or adds to ignored signals"
367 .args
= "?signals ...?",
368 .function
= signal_cmd_default
,
371 .description
= "Lists defaulted signals, or adds to defaulted signals"
374 .args
= "?-clear? ?signals ...?",
375 .function
= signal_cmd_check
,
378 .description
= "Returns ignored signals which have occurred, and optionally clearing them"
382 .function
= signal_cmd_throw
,
385 .description
= "Raises the given signal (default SIGINT)"
390 static int Jim_AlarmCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
395 Jim_WrongNumArgs(interp
, 1, argv
, "seconds");
402 ret
= Jim_GetDouble(interp
, argv
[1], &t
);
414 ret
= Jim_GetLong(interp
, argv
[1], &t
);
424 static int Jim_SleepCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
429 Jim_WrongNumArgs(interp
, 1, argv
, "seconds");
435 ret
= Jim_GetDouble(interp
, argv
[1], &t
);
450 static int Jim_KillCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
457 if (argc
!= 2 && argc
!= 3) {
458 Jim_WrongNumArgs(interp
, 1, argv
, "?SIG|-0? pid");
467 signame
= Jim_String(argv
[1]);
471 /* Special 'kill -0 pid' to determine if a pid exists */
472 if (strcmp(signame
, "-0") == 0 || strcmp(signame
, "0") == 0) {
476 sig
= find_signal_by_name(interp
, signame
);
482 if (Jim_GetLong(interp
, pidObj
, &pid
) != JIM_OK
) {
486 if (kill(pid
, sig
) == 0) {
490 Jim_SetResultString(interp
, "kill: Failed to deliver signal", -1);
494 int Jim_signalInit(Jim_Interp
*interp
)
496 if (Jim_PackageProvide(interp
, "signal", "1.0", JIM_ERRMSG
))
499 /* Teach the jim core how to set a result from a sigmask */
500 interp
->signal_set_result
= signal_set_sigmask_result
;
502 /* Make sure we know where to store the signals which occur */
503 sigloc
= &interp
->sigmask
;
505 Jim_CreateCommand(interp
, "signal", Jim_SubCmdProc
, (void *)signal_command_table
, NULL
);
506 Jim_CreateCommand(interp
, "alarm", Jim_AlarmCmd
, 0, 0);
507 Jim_CreateCommand(interp
, "kill", Jim_KillCmd
, 0, 0);
509 /* Sleep is slightly dubious here */
510 Jim_CreateCommand(interp
, "sleep", Jim_SleepCmd
, 0, 0);