aio: MinGW - Fix compilation error
[jimtcl.git] / jim-signal.c
blobfeb8e19b357a19cddedba6e8f2bc804102945665
1 /*
2 * jim-signal.c
4 */
6 #include <signal.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <unistd.h>
11 #include "jimautoconf.h"
12 #include <jim-subcmd.h>
13 #include <jim-signal.h>
15 #define MAX_SIGNALS_WIDE (sizeof(jim_wide) * 8)
16 #if defined(NSIG)
17 #define MAX_SIGNALS ((NSIG < MAX_SIGNALS_WIDE) ? NSIG : MAX_SIGNALS_WIDE)
18 #else
19 #define MAX_SIGNALS MAX_SIGNALS_WIDE
20 #endif
22 static jim_wide *sigloc;
23 static jim_wide sigsblocked;
24 static struct sigaction *sa_old;
25 static struct {
26 int status;
27 const char *name;
28 } siginfo[MAX_SIGNALS];
30 /* Make sure to do this as a wide, not int */
31 #define sig_to_bit(SIG) ((jim_wide)1 << (SIG))
33 static void signal_handler(int sig)
35 /* We just remember which signals occurred. Jim_Eval() will
36 * notice this as soon as it can and throw an error
38 *sigloc |= sig_to_bit(sig);
41 static void signal_ignorer(int sig)
43 /* We just remember which signals occurred */
44 sigsblocked |= sig_to_bit(sig);
47 static void signal_init_names(void)
49 #define SET_SIG_NAME(SIG) siginfo[SIG].name = #SIG
51 SET_SIG_NAME(SIGABRT);
52 SET_SIG_NAME(SIGALRM);
53 SET_SIG_NAME(SIGBUS);
54 SET_SIG_NAME(SIGCHLD);
55 SET_SIG_NAME(SIGCONT);
56 SET_SIG_NAME(SIGFPE);
57 SET_SIG_NAME(SIGHUP);
58 SET_SIG_NAME(SIGILL);
59 SET_SIG_NAME(SIGINT);
60 #ifdef SIGIO
61 SET_SIG_NAME(SIGIO);
62 #endif
63 SET_SIG_NAME(SIGKILL);
64 SET_SIG_NAME(SIGPIPE);
65 SET_SIG_NAME(SIGPROF);
66 SET_SIG_NAME(SIGQUIT);
67 SET_SIG_NAME(SIGSEGV);
68 SET_SIG_NAME(SIGSTOP);
69 SET_SIG_NAME(SIGSYS);
70 SET_SIG_NAME(SIGTERM);
71 SET_SIG_NAME(SIGTRAP);
72 SET_SIG_NAME(SIGTSTP);
73 SET_SIG_NAME(SIGTTIN);
74 SET_SIG_NAME(SIGTTOU);
75 SET_SIG_NAME(SIGURG);
76 SET_SIG_NAME(SIGUSR1);
77 SET_SIG_NAME(SIGUSR2);
78 SET_SIG_NAME(SIGVTALRM);
79 SET_SIG_NAME(SIGWINCH);
80 SET_SIG_NAME(SIGXCPU);
81 SET_SIG_NAME(SIGXFSZ);
82 #ifdef SIGPWR
83 SET_SIG_NAME(SIGPWR);
84 #endif
85 #ifdef SIGCLD
86 SET_SIG_NAME(SIGCLD);
87 #endif
88 #ifdef SIGEMT
89 SET_SIG_NAME(SIGEMT);
90 #endif
91 #ifdef SIGLOST
92 SET_SIG_NAME(SIGLOST);
93 #endif
94 #ifdef SIGPOLL
95 SET_SIG_NAME(SIGPOLL);
96 #endif
97 #ifdef SIGINFO
98 SET_SIG_NAME(SIGINFO);
99 #endif
103 *----------------------------------------------------------------------
105 * Tcl_SignalId --
107 * Return a textual identifier for a signal number.
109 * Results:
110 * This procedure returns a machine-readable textual identifier
111 * that corresponds to sig. The identifier is the same as the
112 * #define name in signal.h.
114 * Side effects:
115 * None.
117 *----------------------------------------------------------------------
119 const char *Jim_SignalId(int sig)
121 if (sig >=0 && sig < MAX_SIGNALS) {
122 if (siginfo[sig].name) {
123 return siginfo[sig].name;
126 return "unknown signal";
129 const char *Jim_SignalName(int sig)
131 #ifdef HAVE_SYS_SIGLIST
132 if (sig >= 0 && sig < NSIG) {
133 return sys_siglist[sig];
135 #endif
136 return Jim_SignalId(sig);
140 * Given the name of a signal, returns the signal value if found,
141 * or returns -1 (and sets an error) if not found.
142 * We accept -SIGINT, SIGINT, INT or any lowercase version or a number,
143 * either positive or negative.
145 static int find_signal_by_name(Jim_Interp *interp, const char *name)
147 int i;
148 const char *pt = name;
150 /* Remove optional - and SIG from the front of the name */
151 if (*pt == '-') {
152 pt++;
154 if (strncasecmp(name, "sig", 3) == 0) {
155 pt += 3;
157 if (isdigit(UCHAR(pt[0]))) {
158 i = atoi(pt);
159 if (i > 0 && i < MAX_SIGNALS) {
160 return i;
163 else {
164 for (i = 1; i < MAX_SIGNALS; i++) {
165 /* Jim_SignalId() returns names such as SIGINT, and
166 * returns "unknown signal" if unknown, so this will work
168 if (strcasecmp(Jim_SignalId(i) + 3, pt) == 0) {
169 return i;
173 Jim_SetResultFormatted(interp, "unknown signal %s", name);
175 return -1;
178 #define SIGNAL_ACTION_HANDLE 1
179 #define SIGNAL_ACTION_IGNORE -1
180 #define SIGNAL_ACTION_DEFAULT 0
182 static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv)
184 struct sigaction sa;
185 int i;
187 if (argc == 0) {
188 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
189 for (i = 1; i < MAX_SIGNALS; i++) {
190 if (siginfo[i].status == action) {
191 /* Add signal name to the list */
192 Jim_ListAppendElement(interp, Jim_GetResult(interp),
193 Jim_NewStringObj(interp, Jim_SignalId(i), -1));
196 return JIM_OK;
199 /* Catch all the signals we care about */
200 if (action != SIGNAL_ACTION_DEFAULT) {
201 memset(&sa, 0, sizeof(sa));
202 if (action == SIGNAL_ACTION_HANDLE) {
203 sa.sa_handler = signal_handler;
205 else {
206 sa.sa_handler = signal_ignorer;
210 /* Iterate through the provided signals */
211 for (i = 0; i < argc; i++) {
212 int sig = find_signal_by_name(interp, Jim_String(argv[i]));
214 if (sig < 0) {
215 return JIM_ERR;
217 if (action != siginfo[sig].status) {
218 /* Need to change the action for this signal */
219 switch (action) {
220 case SIGNAL_ACTION_HANDLE:
221 case SIGNAL_ACTION_IGNORE:
222 if (siginfo[sig].status == SIGNAL_ACTION_DEFAULT) {
223 if (!sa_old) {
224 /* Allocate the structure the first time through */
225 sa_old = Jim_Alloc(sizeof(*sa_old) * MAX_SIGNALS);
227 sigaction(sig, &sa, &sa_old[sig]);
229 else {
230 sigaction(sig, &sa, 0);
232 break;
234 case SIGNAL_ACTION_DEFAULT:
235 /* Restore old handler */
236 if (sa_old) {
237 sigaction(sig, &sa_old[sig], 0);
240 siginfo[sig].status = action;
244 return JIM_OK;
247 static int signal_cmd_handle(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
249 return do_signal_cmd(interp, SIGNAL_ACTION_HANDLE, argc, argv);
252 static int signal_cmd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
254 return do_signal_cmd(interp, SIGNAL_ACTION_IGNORE, argc, argv);
257 static int signal_cmd_default(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
259 return do_signal_cmd(interp, SIGNAL_ACTION_DEFAULT, argc, argv);
262 static int signal_set_sigmask_result(Jim_Interp *interp, jim_wide sigmask)
264 int i;
265 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
267 for (i = 0; i < MAX_SIGNALS; i++) {
268 if (sigmask & sig_to_bit(i)) {
269 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, Jim_SignalId(i), -1));
272 Jim_SetResult(interp, listObj);
273 return JIM_OK;
276 static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
278 int clear = 0;
279 jim_wide mask = 0;
280 jim_wide blocked;
282 if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) {
283 clear++;
285 if (argc > clear) {
286 int i;
288 /* Signals specified */
289 for (i = clear; i < argc; i++) {
290 int sig = find_signal_by_name(interp, Jim_String(argv[i]));
292 if (sig < 0 || sig >= MAX_SIGNALS) {
293 return -1;
295 mask |= sig_to_bit(sig);
298 else {
299 /* No signals specified, so check/clear all */
300 mask = ~mask;
303 if ((sigsblocked & mask) == 0) {
304 /* No matching signals, so empty result and nothing to do */
305 return JIM_OK;
307 /* Be careful we don't have a race condition where signals are cleared but not returned */
308 blocked = sigsblocked & mask;
309 if (clear) {
310 sigsblocked &= ~blocked;
312 /* Set the result */
313 signal_set_sigmask_result(interp, blocked);
314 return JIM_OK;
317 static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
319 int sig = SIGINT;
321 if (argc == 1) {
322 if ((sig = find_signal_by_name(interp, Jim_String(argv[0]))) < 0) {
323 return JIM_ERR;
327 /* If the signal is ignored (blocked) ... */
328 if (siginfo[sig].status == SIGNAL_ACTION_IGNORE) {
329 sigsblocked |= sig_to_bit(sig);
330 return JIM_OK;
333 /* Just set the signal */
334 interp->sigmask |= sig_to_bit(sig);
336 /* Set the canonical name of the signal as the result */
337 Jim_SetResultString(interp, Jim_SignalId(sig), -1);
339 /* And simply say we caught the signal */
340 return JIM_SIGNAL;
344 *-----------------------------------------------------------------------------
346 * Jim_SignalCmd --
347 * Implements the TCL signal command:
348 * signal handle|ignore|default|throw ?signals ...?
349 * signal throw signal
351 * Specifies which signals are handled by Tcl code.
352 * If the one of the given signals is caught, it causes a JIM_SIGNAL
353 * exception to be thrown which can be caught by catch.
355 * Use 'signal ignore' to ignore the signal(s)
356 * Use 'signal default' to go back to the default behaviour
357 * Use 'signal throw signal' to raise the given signal
359 * If no arguments are given, returns the list of signals which are being handled
361 * Results:
362 * Standard TCL results.
364 *-----------------------------------------------------------------------------
366 static const jim_subcmd_type signal_command_table[] = {
367 { "handle",
368 "?signals ...?",
369 signal_cmd_handle,
372 /* Description: Lists handled signals, or adds to handled signals */
374 { "ignore",
375 "?signals ...?",
376 signal_cmd_ignore,
379 /* Description: Lists ignored signals, or adds to ignored signals */
381 { "default",
382 "?signals ...?",
383 signal_cmd_default,
386 /* Description: Lists defaulted signals, or adds to defaulted signals */
388 { "check",
389 "?-clear? ?signals ...?",
390 signal_cmd_check,
393 /* Description: Returns ignored signals which have occurred, and optionally clearing them */
395 { "throw",
396 "?signal?",
397 signal_cmd_throw,
400 /* Description: Raises the given signal (default SIGINT) */
402 { NULL }
405 static int Jim_AlarmCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
407 int ret;
409 if (argc != 2) {
410 Jim_WrongNumArgs(interp, 1, argv, "seconds");
411 return JIM_ERR;
413 else {
414 #ifdef HAVE_UALARM
415 double t;
417 ret = Jim_GetDouble(interp, argv[1], &t);
418 if (ret == JIM_OK) {
419 if (t < 1) {
420 ualarm(t * 1e6, 0);
422 else {
423 alarm(t);
426 #else
427 long t;
429 ret = Jim_GetLong(interp, argv[1], &t);
430 if (ret == JIM_OK) {
431 alarm(t);
433 #endif
436 return ret;
439 static int Jim_SleepCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
441 int ret;
443 if (argc != 2) {
444 Jim_WrongNumArgs(interp, 1, argv, "seconds");
445 return JIM_ERR;
447 else {
448 double t;
450 ret = Jim_GetDouble(interp, argv[1], &t);
451 if (ret == JIM_OK) {
452 #ifdef HAVE_USLEEP
453 usleep((int)((t - (int)t) * 1e6));
454 #endif
455 sleep(t);
459 return ret;
462 static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
464 int sig;
465 long pid;
466 Jim_Obj *pidObj;
467 const char *signame;
469 if (argc != 2 && argc != 3) {
470 Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid");
471 return JIM_ERR;
474 if (argc == 2) {
475 sig = SIGTERM;
476 pidObj = argv[1];
478 else {
479 signame = Jim_String(argv[1]);
480 pidObj = argv[2];
482 /* Special 'kill -0 pid' to determine if a pid exists */
483 if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) {
484 sig = 0;
486 else {
487 sig = find_signal_by_name(interp, signame);
488 if (sig < 0) {
489 return JIM_ERR;
494 if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) {
495 return JIM_ERR;
498 if (kill(pid, sig) == 0) {
499 return JIM_OK;
502 Jim_SetResultString(interp, "kill: Failed to deliver signal", -1);
503 return JIM_ERR;
506 int Jim_signalInit(Jim_Interp *interp)
508 if (Jim_PackageProvide(interp, "signal", "1.0", JIM_ERRMSG))
509 return JIM_ERR;
511 signal_init_names();
513 /* Teach the jim core how to set a result from a sigmask */
514 interp->signal_set_result = signal_set_sigmask_result;
516 /* Make sure we know where to store the signals which occur */
517 sigloc = &interp->sigmask;
519 Jim_CreateCommand(interp, "signal", Jim_SubCmdProc, (void *)signal_command_table, NULL);
520 Jim_CreateCommand(interp, "alarm", Jim_AlarmCmd, 0, 0);
521 Jim_CreateCommand(interp, "kill", Jim_KillCmd, 0, 0);
523 /* Sleep is slightly dubious here */
524 Jim_CreateCommand(interp, "sleep", Jim_SleepCmd, 0, 0);
525 return JIM_OK;