10 #include "jimautoconf.h"
14 #include <jim-subcmd.h>
15 #include <jim-signal.h>
17 #define MAX_SIGNALS_WIDE (sizeof(jim_wide) * 8)
19 #define MAX_SIGNALS (int)((NSIG < MAX_SIGNALS_WIDE) ? NSIG : MAX_SIGNALS_WIDE)
21 #define MAX_SIGNALS (int)MAX_SIGNALS_WIDE
24 static jim_wide
*sigloc
;
25 static jim_wide sigsignored
;
26 static struct sigaction
*sa_old
;
30 } siginfo
[MAX_SIGNALS
];
32 /* Make sure to do this as a wide, not int */
33 #define sig_to_bit(SIG) ((jim_wide)1 << (SIG))
35 static void signal_handler(int sig
)
37 /* Remember which signals occurred and store in *sigloc.
38 * Jim_Eval() will notice this as soon as it can and throw an error
40 *sigloc
|= sig_to_bit(sig
);
43 static void signal_ignorer(int sig
)
45 /* Remember which signals occurred for access by 'signal check' */
46 sigsignored
|= sig_to_bit(sig
);
49 static void signal_init_names(void)
51 #define SET_SIG_NAME(SIG) siginfo[SIG].name = #SIG
53 SET_SIG_NAME(SIGABRT
);
54 SET_SIG_NAME(SIGALRM
);
56 SET_SIG_NAME(SIGCHLD
);
57 SET_SIG_NAME(SIGCONT
);
65 SET_SIG_NAME(SIGKILL
);
66 SET_SIG_NAME(SIGPIPE
);
67 SET_SIG_NAME(SIGPROF
);
68 SET_SIG_NAME(SIGQUIT
);
69 SET_SIG_NAME(SIGSEGV
);
70 SET_SIG_NAME(SIGSTOP
);
72 SET_SIG_NAME(SIGTERM
);
73 SET_SIG_NAME(SIGTRAP
);
74 SET_SIG_NAME(SIGTSTP
);
75 SET_SIG_NAME(SIGTTIN
);
76 SET_SIG_NAME(SIGTTOU
);
78 SET_SIG_NAME(SIGUSR1
);
79 SET_SIG_NAME(SIGUSR2
);
80 SET_SIG_NAME(SIGVTALRM
);
81 SET_SIG_NAME(SIGWINCH
);
82 SET_SIG_NAME(SIGXCPU
);
83 SET_SIG_NAME(SIGXFSZ
);
94 SET_SIG_NAME(SIGLOST
);
97 SET_SIG_NAME(SIGPOLL
);
100 SET_SIG_NAME(SIGINFO
);
105 *----------------------------------------------------------------------
109 * Return a textual identifier for a signal number.
112 * This procedure returns a machine-readable textual identifier
113 * that corresponds to sig. The identifier is the same as the
114 * #define name in signal.h.
119 *----------------------------------------------------------------------
121 const char *Jim_SignalId(int sig
)
123 if (sig
>=0 && sig
< MAX_SIGNALS
) {
124 if (siginfo
[sig
].name
) {
125 return siginfo
[sig
].name
;
128 return "unknown signal";
132 * Given the name of a signal, returns the signal value if found,
133 * or returns -1 (and sets an error) if not found.
134 * We accept -SIGINT, SIGINT, INT or any lowercase version or a number,
135 * either positive or negative.
137 static int find_signal_by_name(Jim_Interp
*interp
, const char *name
)
140 const char *pt
= name
;
142 /* Remove optional - and SIG from the front of the name */
146 if (strncasecmp(name
, "sig", 3) == 0) {
149 if (isdigit(UCHAR(pt
[0]))) {
151 if (i
> 0 && i
< MAX_SIGNALS
) {
156 for (i
= 1; i
< MAX_SIGNALS
; i
++) {
157 /* Jim_SignalId() returns names such as SIGINT, and
158 * returns "unknown signal" if unknown, so this will work
160 if (strcasecmp(Jim_SignalId(i
) + 3, pt
) == 0) {
165 Jim_SetResultFormatted(interp
, "unknown signal %s", name
);
170 #define SIGNAL_ACTION_HANDLE 1
171 #define SIGNAL_ACTION_IGNORE -1
172 #define SIGNAL_ACTION_BLOCK -2
173 #define SIGNAL_ACTION_DEFAULT 0
175 static int do_signal_cmd(Jim_Interp
*interp
, int action
, int argc
, Jim_Obj
*const *argv
)
181 Jim_SetResult(interp
, Jim_NewListObj(interp
, NULL
, 0));
182 for (i
= 1; i
< MAX_SIGNALS
; i
++) {
183 if (siginfo
[i
].status
== action
) {
184 /* Add signal name to the list */
185 Jim_ListAppendElement(interp
, Jim_GetResult(interp
),
186 Jim_NewStringObj(interp
, Jim_SignalId(i
), -1));
192 /* Catch all the signals we care about */
193 if (action
!= SIGNAL_ACTION_DEFAULT
) {
194 memset(&sa
, 0, sizeof(sa
));
195 if (action
== SIGNAL_ACTION_HANDLE
) {
196 sa
.sa_handler
= signal_handler
;
198 else if (action
== SIGNAL_ACTION_IGNORE
) {
199 sa
.sa_handler
= signal_ignorer
;
202 /* SIGNAL_ACTION_BLOCK */
203 sa
.sa_handler
= SIG_IGN
;
207 /* Iterate through the provided signals */
208 for (i
= 0; i
< argc
; i
++) {
209 int sig
= find_signal_by_name(interp
, Jim_String(argv
[i
]));
214 if (action
!= siginfo
[sig
].status
) {
215 /* Need to change the action for this signal */
217 case SIGNAL_ACTION_BLOCK
:
218 case SIGNAL_ACTION_HANDLE
:
219 case SIGNAL_ACTION_IGNORE
:
220 if (siginfo
[sig
].status
== SIGNAL_ACTION_DEFAULT
) {
222 /* Allocate the structure the first time through */
223 sa_old
= Jim_Alloc(sizeof(*sa_old
) * MAX_SIGNALS
);
225 sigaction(sig
, &sa
, &sa_old
[sig
]);
228 sigaction(sig
, &sa
, 0);
232 case SIGNAL_ACTION_DEFAULT
:
233 /* Restore old handler */
235 sigaction(sig
, &sa_old
[sig
], 0);
238 siginfo
[sig
].status
= action
;
245 static int signal_cmd_handle(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
247 return do_signal_cmd(interp
, SIGNAL_ACTION_HANDLE
, argc
, argv
);
250 static int signal_cmd_ignore(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
252 return do_signal_cmd(interp
, SIGNAL_ACTION_IGNORE
, argc
, argv
);
255 static int signal_cmd_block(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
257 return do_signal_cmd(interp
, SIGNAL_ACTION_BLOCK
, argc
, argv
);
260 static int signal_cmd_default(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
262 return do_signal_cmd(interp
, SIGNAL_ACTION_DEFAULT
, argc
, argv
);
265 static int signal_set_sigmask_result(Jim_Interp
*interp
, jim_wide sigmask
)
268 Jim_Obj
*listObj
= Jim_NewListObj(interp
, NULL
, 0);
270 for (i
= 0; i
< MAX_SIGNALS
; i
++) {
271 if (sigmask
& sig_to_bit(i
)) {
272 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, Jim_SignalId(i
), -1));
275 Jim_SetResult(interp
, listObj
);
279 static int signal_cmd_check(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
285 if (argc
> 0 && Jim_CompareStringImmediate(interp
, argv
[0], "-clear")) {
291 /* Signals specified */
292 for (i
= clear
; i
< argc
; i
++) {
293 int sig
= find_signal_by_name(interp
, Jim_String(argv
[i
]));
295 if (sig
< 0 || sig
>= MAX_SIGNALS
) {
298 mask
|= sig_to_bit(sig
);
302 /* No signals specified, so check/clear all */
306 /* Be careful we don't have a race condition where signals are cleared but not returned */
307 ignored
= sigsignored
& mask
;
309 sigsignored
&= ~ignored
;
312 signal_set_sigmask_result(interp
, ignored
);
316 static int signal_cmd_throw(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
321 if ((sig
= find_signal_by_name(interp
, Jim_String(argv
[0]))) < 0) {
326 /* If the signal is ignored ... */
327 if (siginfo
[sig
].status
== SIGNAL_ACTION_IGNORE
) {
328 sigsignored
|= sig_to_bit(sig
);
332 /* Just set the signal */
333 interp
->sigmask
|= sig_to_bit(sig
);
335 /* Set the canonical name of the signal as the result */
336 Jim_SetResultString(interp
, Jim_SignalId(sig
), -1);
338 /* And simply say we caught the signal */
343 *-----------------------------------------------------------------------------
346 * Implements the TCL signal command:
347 * signal handle|ignore|default|throw ?signals ...?
348 * signal throw signal
350 * Specifies which signals are handled by Tcl code.
351 * If the one of the given signals is caught, it causes a JIM_SIGNAL
352 * exception to be thrown which can be caught by catch.
354 * Use 'signal ignore' to ignore the signal(s)
355 * Use 'signal default' to go back to the default behaviour
356 * Use 'signal throw signal' to raise the given signal
358 * If no arguments are given, returns the list of signals which are being handled
361 * Standard TCL results.
363 *-----------------------------------------------------------------------------
365 static const jim_subcmd_type signal_command_table
[] = {
371 /* Description: Lists handled signals, or adds to handled signals */
378 /* Description: Lists ignored signals, or adds to ignored signals */
385 /* Description: Lists blocked signals, or adds to blocked signals */
392 /* Description: Lists defaulted signals, or adds to defaulted signals */
395 "?-clear? ?signals ...?",
399 /* Description: Returns ignored signals which have occurred, and optionally clearing them */
406 /* Description: Raises the given signal (default SIGINT) */
412 * Restore default signal handling.
414 static void JimSignalCmdDelete(Jim_Interp
*interp
, void *privData
)
418 for (i
= 1; i
< MAX_SIGNALS
; i
++) {
419 if (siginfo
[i
].status
!= SIGNAL_ACTION_DEFAULT
) {
420 sigaction(i
, &sa_old
[i
], 0);
421 siginfo
[i
].status
= SIGNAL_ACTION_DEFAULT
;
430 static int Jim_AlarmCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
435 Jim_WrongNumArgs(interp
, 1, argv
, "seconds");
442 ret
= Jim_GetDouble(interp
, argv
[1], &t
);
454 ret
= Jim_GetLong(interp
, argv
[1], &t
);
464 static int Jim_SleepCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
469 Jim_WrongNumArgs(interp
, 1, argv
, "seconds");
475 ret
= Jim_GetDouble(interp
, argv
[1], &t
);
478 usleep((int)((t
- (int)t
) * 1e6
));
487 static int Jim_KillCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
494 if (argc
!= 2 && argc
!= 3) {
495 Jim_WrongNumArgs(interp
, 1, argv
, "?SIG|-0? pid");
504 signame
= Jim_String(argv
[1]);
507 /* Special 'kill -0 pid' to determine if a pid exists */
508 if (strcmp(signame
, "-0") == 0 || strcmp(signame
, "0") == 0) {
512 sig
= find_signal_by_name(interp
, signame
);
519 if (Jim_GetLong(interp
, pidObj
, &pid
) != JIM_OK
) {
523 if (kill(pid
, sig
) == 0) {
527 Jim_SetResultString(interp
, "kill: Failed to deliver signal", -1);
531 int Jim_signalInit(Jim_Interp
*interp
)
533 if (Jim_PackageProvide(interp
, "signal", "1.0", JIM_ERRMSG
))
536 Jim_CreateCommand(interp
, "alarm", Jim_AlarmCmd
, 0, 0);
537 Jim_CreateCommand(interp
, "kill", Jim_KillCmd
, 0, 0);
538 /* Sleep is slightly dubious here */
539 Jim_CreateCommand(interp
, "sleep", Jim_SleepCmd
, 0, 0);
541 /* Teach the jim core how to set a result from a sigmask */
542 interp
->signal_set_result
= signal_set_sigmask_result
;
544 /* Currently only the top level interp supports signals */
548 /* Make sure we know where to store the signals which occur */
549 sigloc
= &interp
->sigmask
;
551 Jim_CreateCommand(interp
, "signal", Jim_SubCmdProc
, (void *)signal_command_table
, JimSignalCmdDelete
);