Support and ignore fconfigure -translation ...
[jimtcl.git] / jim-signal.c
blobc539aa36e0b8625949e8c9ce12dc87e85133ebbc
2 /*
3 * jim-signal.c
5 */
7 #include <signal.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <unistd.h>
12 #include "jim.h"
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 *----------------------------------------------------------------------
44 * Tcl_SignalId --
46 * Return a textual identifier for a signal number.
48 * Results:
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.
53 * Side effects:
54 * None.
56 *----------------------------------------------------------------------
58 #define CHECK_SIG(NAME) if (sig == NAME) return #NAME
60 const char *Jim_SignalId(int sig)
62 CHECK_SIG(SIGABRT);
63 CHECK_SIG(SIGALRM);
64 CHECK_SIG(SIGBUS);
65 CHECK_SIG(SIGCHLD);
66 CHECK_SIG(SIGCONT);
67 CHECK_SIG(SIGFPE);
68 CHECK_SIG(SIGHUP);
69 CHECK_SIG(SIGILL);
70 CHECK_SIG(SIGINT);
71 CHECK_SIG(SIGIO);
72 CHECK_SIG(SIGKILL);
73 CHECK_SIG(SIGPIPE);
74 CHECK_SIG(SIGPROF);
75 CHECK_SIG(SIGQUIT);
76 CHECK_SIG(SIGSEGV);
77 CHECK_SIG(SIGSTOP);
78 CHECK_SIG(SIGSYS);
79 CHECK_SIG(SIGTERM);
80 CHECK_SIG(SIGTRAP);
81 CHECK_SIG(SIGTSTP);
82 CHECK_SIG(SIGTTIN);
83 CHECK_SIG(SIGTTOU);
84 CHECK_SIG(SIGURG);
85 CHECK_SIG(SIGUSR1);
86 CHECK_SIG(SIGUSR2);
87 CHECK_SIG(SIGVTALRM);
88 CHECK_SIG(SIGWINCH);
89 CHECK_SIG(SIGXCPU);
90 CHECK_SIG(SIGXFSZ);
91 #ifdef SIGPWR
92 CHECK_SIG(SIGPWR);
93 #endif
94 #ifdef SIGCLD
95 CHECK_SIG(SIGCLD);
96 #endif
97 #ifdef SIGEMT
98 CHECK_SIG(SIGEMT);
99 #endif
100 #ifdef SIGLOST
101 CHECK_SIG(SIGLOST);
102 #endif
103 #ifdef SIGPOLL
104 CHECK_SIG(SIGPOLL);
105 #endif
106 #ifdef SIGINFO
107 CHECK_SIG(SIGINFO);
108 #endif
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];
118 #endif
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)
130 int i;
131 const char *pt = name;
133 /* Remove optional - and SIG from the front of the name */
134 if (*pt == '-') {
135 pt++;
137 if (strncasecmp(name, "sig", 3) == 0) {
138 pt += 3;
140 if (isdigit(UCHAR(pt[0]))) {
141 i = atoi(pt);
142 if (i > 0 && i < MAX_SIGNALS) {
143 return i;
146 else {
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) {
152 return i;
156 Jim_SetResultString(interp, "unknown signal ", -1);
157 Jim_AppendString(interp, Jim_GetResult(interp), name, -1);
159 return -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)
168 struct sigaction sa;
169 int i;
171 if (argc == 0) {
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));
180 return JIM_OK;
183 /* Catch all the signals we care about */
184 if (action != SIGNAL_ACTION_DEFAULT) {
185 sa.sa_flags = 0;
186 sigemptyset(&sa.sa_mask);
187 if (action == SIGNAL_ACTION_HANDLE) {
188 sa.sa_handler = signal_handler;
190 else {
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]));
199 if (sig < 0) {
200 return JIM_ERR;
202 if (action != signal_handling[sig]) {
203 /* Need to change the action for this signal */
204 switch (action) {
205 case SIGNAL_ACTION_HANDLE:
206 case SIGNAL_ACTION_IGNORE:
207 if (signal_handling[sig] == SIGNAL_ACTION_DEFAULT) {
208 if (!sa_old) {
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]);
214 else {
215 sigaction(sig, &sa, 0);
217 break;
219 case SIGNAL_ACTION_DEFAULT:
220 /* Restore old handler */
221 if (sa_old) {
222 sigaction(sig, &sa_old[sig], 0);
225 signal_handling[sig] = action;
229 return JIM_OK;
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)
249 int i;
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);
258 return JIM_OK;
261 static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
263 int clear = 0;
264 jim_wide mask = 0;
265 jim_wide blocked;
267 if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) {
268 clear++;
270 if (argc > clear) {
271 int i;
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) {
278 return -1;
280 mask |= sig_to_bit(sig);
283 else {
284 /* No signals specified, so check/clear all */
285 mask = ~mask;
288 if ((sigsblocked & mask) == 0) {
289 /* No matching signals, so empty result and nothing to do */
290 return JIM_OK;
292 /* Be careful we don't have a race condition where signals are cleared but not returned */
293 blocked = sigsblocked & mask;
294 if (clear) {
295 sigsblocked &= ~blocked;
297 /* Set the result */
298 signal_set_sigmask_result(interp, blocked);
299 return JIM_OK;
302 static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
304 int sig = SIGINT;
306 if (argc == 1) {
307 if ((sig = find_signal_by_name(interp, Jim_String(argv[0]))) < 0) {
308 return JIM_ERR;
312 /* If the signal is ignored (blocked) ... */
313 if (signal_handling[sig] == SIGNAL_ACTION_IGNORE) {
314 sigsblocked |= sig_to_bit(sig);
315 return JIM_OK;
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 */
325 return JIM_SIGNAL;
329 *-----------------------------------------------------------------------------
331 * Jim_SignalCmd --
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
346 * Results:
347 * Standard TCL results.
349 *-----------------------------------------------------------------------------
351 static const jim_subcmd_type signal_command_table[] = {
352 { .cmd = "handle",
353 .args = "?signals ...?",
354 .function = signal_cmd_handle,
355 .minargs = 0,
356 .maxargs = -1,
357 .description = "Lists handled signals, or adds to handled signals"
359 { .cmd = "ignore",
360 .args = "?signals ...?",
361 .function = signal_cmd_ignore,
362 .minargs = 0,
363 .maxargs = -1,
364 .description = "Lists ignored signals, or adds to ignored signals"
366 { .cmd = "default",
367 .args = "?signals ...?",
368 .function = signal_cmd_default,
369 .minargs = 0,
370 .maxargs = -1,
371 .description = "Lists defaulted signals, or adds to defaulted signals"
373 { .cmd = "check",
374 .args = "?-clear? ?signals ...?",
375 .function = signal_cmd_check,
376 .minargs = 0,
377 .maxargs = -1,
378 .description = "Returns ignored signals which have occurred, and optionally clearing them"
380 { .cmd = "throw",
381 .args = "?signal?",
382 .function = signal_cmd_throw,
383 .minargs = 0,
384 .maxargs = 1,
385 .description = "Raises the given signal (default SIGINT)"
387 { 0 }
390 static int Jim_AlarmCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
392 int ret;
394 if (argc != 2) {
395 Jim_WrongNumArgs(interp, 1, argv, "seconds");
396 return JIM_ERR;
398 else {
399 #ifdef HAVE_UALARM
400 double t;
402 ret = Jim_GetDouble(interp, argv[1], &t);
403 if (ret == JIM_OK) {
404 if (t < 1) {
405 ualarm(t * 1e6, 0);
407 else {
408 alarm(t);
411 #else
412 long t;
414 ret = Jim_GetLong(interp, argv[1], &t);
415 if (ret == JIM_OK) {
416 alarm(t);
418 #endif
421 return ret;
424 static int Jim_SleepCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
426 int ret;
428 if (argc != 2) {
429 Jim_WrongNumArgs(interp, 1, argv, "seconds");
430 return JIM_ERR;
432 else {
433 double t;
435 ret = Jim_GetDouble(interp, argv[1], &t);
436 if (ret == JIM_OK) {
437 #ifdef HAVE_USLEEP
438 if (t < 1) {
439 usleep(t * 1e6);
441 else
442 #endif
443 sleep(t);
447 return ret;
450 static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
452 int sig;
453 long pid;
454 Jim_Obj *pidObj;
455 const char *signame;
457 if (argc != 2 && argc != 3) {
458 Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid");
459 return JIM_ERR;
462 if (argc == 2) {
463 signame = "SIGTERM";
464 pidObj = argv[1];
466 else {
467 signame = Jim_String(argv[1]);
468 pidObj = argv[2];
471 /* Special 'kill -0 pid' to determine if a pid exists */
472 if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) {
473 sig = 0;
475 else {
476 sig = find_signal_by_name(interp, signame);
477 if (sig < 0) {
478 return JIM_ERR;
482 if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) {
483 return JIM_ERR;
486 if (kill(pid, sig) == 0) {
487 return JIM_OK;
490 Jim_SetResultString(interp, "kill: Failed to deliver signal", -1);
491 return JIM_ERR;
494 int Jim_signalInit(Jim_Interp *interp)
496 if (Jim_PackageProvide(interp, "signal", "1.0", JIM_ERRMSG))
497 return JIM_ERR;
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);
511 return JIM_OK;