jimregexp: remove dead code
[jimtcl.git] / jim-signal.c
blob7fe60375250594298a0483de74f0ce9f33966558
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 (sizeof(jim_wide) * 8)
17 static jim_wide *sigloc;
18 static jim_wide sigsblocked;
19 static struct sigaction *sa_old;
20 static struct {
21 int status;
22 const char *name;
23 } siginfo[MAX_SIGNALS];
25 /* Make sure to do this as a wide, not int */
26 #define sig_to_bit(SIG) ((jim_wide)1 << (SIG))
28 static void signal_handler(int sig)
30 /* We just remember which signals occurred. Jim_Eval() will
31 * notice this as soon as it can and throw an error
33 *sigloc |= sig_to_bit(sig);
36 static void signal_ignorer(int sig)
38 /* We just remember which signals occurred */
39 sigsblocked |= sig_to_bit(sig);
42 static void signal_init_names(void)
44 #define SET_SIG_NAME(SIG) siginfo[SIG].name = #SIG
46 SET_SIG_NAME(SIGABRT);
47 SET_SIG_NAME(SIGALRM);
48 SET_SIG_NAME(SIGBUS);
49 SET_SIG_NAME(SIGCHLD);
50 SET_SIG_NAME(SIGCONT);
51 SET_SIG_NAME(SIGFPE);
52 SET_SIG_NAME(SIGHUP);
53 SET_SIG_NAME(SIGILL);
54 SET_SIG_NAME(SIGINT);
55 #ifdef SIGIO
56 SET_SIG_NAME(SIGIO);
57 #endif
58 SET_SIG_NAME(SIGKILL);
59 SET_SIG_NAME(SIGPIPE);
60 SET_SIG_NAME(SIGPROF);
61 SET_SIG_NAME(SIGQUIT);
62 SET_SIG_NAME(SIGSEGV);
63 SET_SIG_NAME(SIGSTOP);
64 SET_SIG_NAME(SIGSYS);
65 SET_SIG_NAME(SIGTERM);
66 SET_SIG_NAME(SIGTRAP);
67 SET_SIG_NAME(SIGTSTP);
68 SET_SIG_NAME(SIGTTIN);
69 SET_SIG_NAME(SIGTTOU);
70 SET_SIG_NAME(SIGURG);
71 SET_SIG_NAME(SIGUSR1);
72 SET_SIG_NAME(SIGUSR2);
73 SET_SIG_NAME(SIGVTALRM);
74 SET_SIG_NAME(SIGWINCH);
75 SET_SIG_NAME(SIGXCPU);
76 SET_SIG_NAME(SIGXFSZ);
77 #ifdef SIGPWR
78 SET_SIG_NAME(SIGPWR);
79 #endif
80 #ifdef SIGCLD
81 SET_SIG_NAME(SIGCLD);
82 #endif
83 #ifdef SIGEMT
84 SET_SIG_NAME(SIGEMT);
85 #endif
86 #ifdef SIGLOST
87 SET_SIG_NAME(SIGLOST);
88 #endif
89 #ifdef SIGPOLL
90 SET_SIG_NAME(SIGPOLL);
91 #endif
92 #ifdef SIGINFO
93 SET_SIG_NAME(SIGINFO);
94 #endif
98 *----------------------------------------------------------------------
100 * Tcl_SignalId --
102 * Return a textual identifier for a signal number.
104 * Results:
105 * This procedure returns a machine-readable textual identifier
106 * that corresponds to sig. The identifier is the same as the
107 * #define name in signal.h.
109 * Side effects:
110 * None.
112 *----------------------------------------------------------------------
114 const char *Jim_SignalId(int sig)
116 if (sig >=0 && sig < MAX_SIGNALS) {
117 if (siginfo[sig].name) {
118 return siginfo[sig].name;
121 return "unknown signal";
124 const char *Jim_SignalName(int sig)
126 #ifdef HAVE_SYS_SIGLIST
127 if (sig >= 0 && sig < NSIG) {
128 return sys_siglist[sig];
130 #endif
131 return Jim_SignalId(sig);
135 * Given the name of a signal, returns the signal value if found,
136 * or returns -1 (and sets an error) if not found.
137 * We accept -SIGINT, SIGINT, INT or any lowercase version or a number,
138 * either positive or negative.
140 static int find_signal_by_name(Jim_Interp *interp, const char *name)
142 int i;
143 const char *pt = name;
145 /* Remove optional - and SIG from the front of the name */
146 if (*pt == '-') {
147 pt++;
149 if (strncasecmp(name, "sig", 3) == 0) {
150 pt += 3;
152 if (isdigit(UCHAR(pt[0]))) {
153 i = atoi(pt);
154 if (i > 0 && i < MAX_SIGNALS) {
155 return i;
158 else {
159 for (i = 1; i < MAX_SIGNALS; i++) {
160 /* Jim_SignalId() returns names such as SIGINT, and
161 * returns "unknown signal" if unknown, so this will work
163 if (strcasecmp(Jim_SignalId(i) + 3, pt) == 0) {
164 return i;
168 Jim_SetResultFormatted(interp, "unknown signal %s", name);
170 return -1;
173 #define SIGNAL_ACTION_HANDLE 1
174 #define SIGNAL_ACTION_IGNORE -1
175 #define SIGNAL_ACTION_DEFAULT 0
177 static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv)
179 struct sigaction sa;
180 int i;
182 if (argc == 0) {
183 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
184 for (i = 1; i < MAX_SIGNALS; i++) {
185 if (siginfo[i].status == action) {
186 /* Add signal name to the list */
187 Jim_ListAppendElement(interp, Jim_GetResult(interp),
188 Jim_NewStringObj(interp, Jim_SignalId(i), -1));
191 return JIM_OK;
194 /* Catch all the signals we care about */
195 if (action != SIGNAL_ACTION_DEFAULT) {
196 sa.sa_flags = 0;
197 sigemptyset(&sa.sa_mask);
198 if (action == SIGNAL_ACTION_HANDLE) {
199 sa.sa_handler = signal_handler;
201 else {
202 sa.sa_handler = signal_ignorer;
206 /* Iterate through the provided signals */
207 for (i = 0; i < argc; i++) {
208 int sig = find_signal_by_name(interp, Jim_String(argv[i]));
210 if (sig < 0) {
211 return JIM_ERR;
213 if (action != siginfo[sig].status) {
214 /* Need to change the action for this signal */
215 switch (action) {
216 case SIGNAL_ACTION_HANDLE:
217 case SIGNAL_ACTION_IGNORE:
218 if (siginfo[sig].status == SIGNAL_ACTION_DEFAULT) {
219 if (!sa_old) {
220 /* Allocate the structure the first time through */
221 sa_old = Jim_Alloc(sizeof(*sa_old) * MAX_SIGNALS);
223 sigaction(sig, &sa, &sa_old[sig]);
225 else {
226 sigaction(sig, &sa, 0);
228 break;
230 case SIGNAL_ACTION_DEFAULT:
231 /* Restore old handler */
232 if (sa_old) {
233 sigaction(sig, &sa_old[sig], 0);
236 siginfo[sig].status = action;
240 return JIM_OK;
243 static int signal_cmd_handle(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
245 return do_signal_cmd(interp, SIGNAL_ACTION_HANDLE, argc, argv);
248 static int signal_cmd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
250 return do_signal_cmd(interp, SIGNAL_ACTION_IGNORE, argc, argv);
253 static int signal_cmd_default(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
255 return do_signal_cmd(interp, SIGNAL_ACTION_DEFAULT, argc, argv);
258 static int signal_set_sigmask_result(Jim_Interp *interp, jim_wide sigmask)
260 int i;
261 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
263 for (i = 0; i < MAX_SIGNALS; i++) {
264 if (sigmask & sig_to_bit(i)) {
265 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, Jim_SignalId(i), -1));
268 Jim_SetResult(interp, listObj);
269 return JIM_OK;
272 static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
274 int clear = 0;
275 jim_wide mask = 0;
276 jim_wide blocked;
278 if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) {
279 clear++;
281 if (argc > clear) {
282 int i;
284 /* Signals specified */
285 for (i = clear; i < argc; i++) {
286 int sig = find_signal_by_name(interp, Jim_String(argv[i]));
288 if (sig < 0 || sig >= MAX_SIGNALS) {
289 return -1;
291 mask |= sig_to_bit(sig);
294 else {
295 /* No signals specified, so check/clear all */
296 mask = ~mask;
299 if ((sigsblocked & mask) == 0) {
300 /* No matching signals, so empty result and nothing to do */
301 return JIM_OK;
303 /* Be careful we don't have a race condition where signals are cleared but not returned */
304 blocked = sigsblocked & mask;
305 if (clear) {
306 sigsblocked &= ~blocked;
308 /* Set the result */
309 signal_set_sigmask_result(interp, blocked);
310 return JIM_OK;
313 static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
315 int sig = SIGINT;
317 if (argc == 1) {
318 if ((sig = find_signal_by_name(interp, Jim_String(argv[0]))) < 0) {
319 return JIM_ERR;
323 /* If the signal is ignored (blocked) ... */
324 if (siginfo[sig].status == SIGNAL_ACTION_IGNORE) {
325 sigsblocked |= sig_to_bit(sig);
326 return JIM_OK;
329 /* Just set the signal */
330 interp->sigmask |= sig_to_bit(sig);
332 /* Set the canonical name of the signal as the result */
333 Jim_SetResultString(interp, Jim_SignalId(sig), -1);
335 /* And simply say we caught the signal */
336 return JIM_SIGNAL;
340 *-----------------------------------------------------------------------------
342 * Jim_SignalCmd --
343 * Implements the TCL signal command:
344 * signal handle|ignore|default|throw ?signals ...?
345 * signal throw signal
347 * Specifies which signals are handled by Tcl code.
348 * If the one of the given signals is caught, it causes a JIM_SIGNAL
349 * exception to be thrown which can be caught by catch.
351 * Use 'signal ignore' to ignore the signal(s)
352 * Use 'signal default' to go back to the default behaviour
353 * Use 'signal throw signal' to raise the given signal
355 * If no arguments are given, returns the list of signals which are being handled
357 * Results:
358 * Standard TCL results.
360 *-----------------------------------------------------------------------------
362 static const jim_subcmd_type signal_command_table[] = {
363 { "handle",
364 "?signals ...?",
365 signal_cmd_handle,
368 /* Description: Lists handled signals, or adds to handled signals */
370 { "ignore",
371 "?signals ...?",
372 signal_cmd_ignore,
375 /* Description: Lists ignored signals, or adds to ignored signals */
377 { "default",
378 "?signals ...?",
379 signal_cmd_default,
382 /* Description: Lists defaulted signals, or adds to defaulted signals */
384 { "check",
385 "?-clear? ?signals ...?",
386 signal_cmd_check,
389 /* Description: Returns ignored signals which have occurred, and optionally clearing them */
391 { "throw",
392 "?signal?",
393 signal_cmd_throw,
396 /* Description: Raises the given signal (default SIGINT) */
398 { NULL }
401 static int Jim_AlarmCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
403 int ret;
405 if (argc != 2) {
406 Jim_WrongNumArgs(interp, 1, argv, "seconds");
407 return JIM_ERR;
409 else {
410 #ifdef HAVE_UALARM
411 double t;
413 ret = Jim_GetDouble(interp, argv[1], &t);
414 if (ret == JIM_OK) {
415 if (t < 1) {
416 ualarm(t * 1e6, 0);
418 else {
419 alarm(t);
422 #else
423 long t;
425 ret = Jim_GetLong(interp, argv[1], &t);
426 if (ret == JIM_OK) {
427 alarm(t);
429 #endif
432 return ret;
435 static int Jim_SleepCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
437 int ret;
439 if (argc != 2) {
440 Jim_WrongNumArgs(interp, 1, argv, "seconds");
441 return JIM_ERR;
443 else {
444 double t;
446 ret = Jim_GetDouble(interp, argv[1], &t);
447 if (ret == JIM_OK) {
448 #ifdef HAVE_USLEEP
449 usleep((int)((t - (int)t) * 1e6));
450 #endif
451 sleep(t);
455 return ret;
458 static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
460 int sig;
461 long pid;
462 Jim_Obj *pidObj;
463 const char *signame;
465 if (argc != 2 && argc != 3) {
466 Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid");
467 return JIM_ERR;
470 if (argc == 2) {
471 sig = SIGTERM;
472 pidObj = argv[1];
474 else {
475 signame = Jim_String(argv[1]);
476 pidObj = argv[2];
478 /* Special 'kill -0 pid' to determine if a pid exists */
479 if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) {
480 sig = 0;
482 else {
483 sig = find_signal_by_name(interp, signame);
484 if (sig < 0) {
485 return JIM_ERR;
490 if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) {
491 return JIM_ERR;
494 if (kill(pid, sig) == 0) {
495 return JIM_OK;
498 Jim_SetResultString(interp, "kill: Failed to deliver signal", -1);
499 return JIM_ERR;
502 int Jim_signalInit(Jim_Interp *interp)
504 if (Jim_PackageProvide(interp, "signal", "1.0", JIM_ERRMSG))
505 return JIM_ERR;
507 signal_init_names();
509 /* Teach the jim core how to set a result from a sigmask */
510 interp->signal_set_result = signal_set_sigmask_result;
512 /* Make sure we know where to store the signals which occur */
513 sigloc = &interp->sigmask;
515 Jim_CreateCommand(interp, "signal", Jim_SubCmdProc, (void *)signal_command_table, NULL);
516 Jim_CreateCommand(interp, "alarm", Jim_AlarmCmd, 0, 0);
517 Jim_CreateCommand(interp, "kill", Jim_KillCmd, 0, 0);
519 /* Sleep is slightly dubious here */
520 Jim_CreateCommand(interp, "sleep", Jim_SleepCmd, 0, 0);
521 return JIM_OK;