signal: minor code cleanup
[jimtcl.git] / jim-signal.c
blob313784905172952abadf80dddf29f6db49b64e03
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 int signal_handling[MAX_SIGNALS];
22 /* Make sure to do this as a wide, not int */
23 #define sig_to_bit(SIG) ((jim_wide)1 << (SIG))
25 static void signal_handler(int sig)
27 /* We just remember which signals occurred. Jim_Eval() will
28 * notice this as soon as it can and throw an error
30 *sigloc |= sig_to_bit(sig);
33 static void signal_ignorer(int sig)
35 /* We just remember which signals occurred */
36 sigsblocked |= sig_to_bit(sig);
40 *----------------------------------------------------------------------
42 * Tcl_SignalId --
44 * Return a textual identifier for a signal number.
46 * Results:
47 * This procedure returns a machine-readable textual identifier
48 * that corresponds to sig. The identifier is the same as the
49 * #define name in signal.h.
51 * Side effects:
52 * None.
54 *----------------------------------------------------------------------
56 #define CHECK_SIG(NAME) if (sig == NAME) return #NAME
58 const char *Jim_SignalId(int sig)
60 CHECK_SIG(SIGABRT);
61 CHECK_SIG(SIGALRM);
62 CHECK_SIG(SIGBUS);
63 CHECK_SIG(SIGCHLD);
64 CHECK_SIG(SIGCONT);
65 CHECK_SIG(SIGFPE);
66 CHECK_SIG(SIGHUP);
67 CHECK_SIG(SIGILL);
68 CHECK_SIG(SIGINT);
69 #ifdef SIGIO
70 CHECK_SIG(SIGIO);
71 #endif
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 { "handle",
353 "?signals ...?",
354 signal_cmd_handle,
357 /* Description: Lists handled signals, or adds to handled signals */
359 { "ignore",
360 "?signals ...?",
361 signal_cmd_ignore,
364 /* Description: Lists ignored signals, or adds to ignored signals */
366 { "default",
367 "?signals ...?",
368 signal_cmd_default,
371 /* Description: Lists defaulted signals, or adds to defaulted signals */
373 { "check",
374 "?-clear? ?signals ...?",
375 signal_cmd_check,
378 /* Description: Returns ignored signals which have occurred, and optionally clearing them */
380 { "throw",
381 "?signal?",
382 signal_cmd_throw,
385 /* Description: Raises the given signal (default SIGINT) */
387 { NULL }
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 usleep((int)((t - (int)t) * 1e6));
439 #endif
440 sleep(t);
444 return ret;
447 static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
449 int sig;
450 long pid;
451 Jim_Obj *pidObj;
452 const char *signame;
454 if (argc != 2 && argc != 3) {
455 Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid");
456 return JIM_ERR;
459 if (argc == 2) {
460 sig = SIGTERM;
461 pidObj = argv[1];
463 else {
464 signame = Jim_String(argv[1]);
465 pidObj = argv[2];
467 /* Special 'kill -0 pid' to determine if a pid exists */
468 if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) {
469 sig = 0;
471 else {
472 sig = find_signal_by_name(interp, signame);
473 if (sig < 0) {
474 return JIM_ERR;
479 if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) {
480 return JIM_ERR;
483 if (kill(pid, sig) == 0) {
484 return JIM_OK;
487 Jim_SetResultString(interp, "kill: Failed to deliver signal", -1);
488 return JIM_ERR;
491 int Jim_signalInit(Jim_Interp *interp)
493 if (Jim_PackageProvide(interp, "signal", "1.0", JIM_ERRMSG))
494 return JIM_ERR;
496 /* Teach the jim core how to set a result from a sigmask */
497 interp->signal_set_result = signal_set_sigmask_result;
499 /* Make sure we know where to store the signals which occur */
500 sigloc = &interp->sigmask;
502 Jim_CreateCommand(interp, "signal", Jim_SubCmdProc, (void *)signal_command_table, NULL);
503 Jim_CreateCommand(interp, "alarm", Jim_AlarmCmd, 0, 0);
504 Jim_CreateCommand(interp, "kill", Jim_KillCmd, 0, 0);
506 /* Sleep is slightly dubious here */
507 Jim_CreateCommand(interp, "sleep", Jim_SleepCmd, 0, 0);
508 return JIM_OK;