package: simplification/code cleanup
[jimtcl.git] / jim-signal.c
blobfad8913c446fd70be20d779ea8790f0e8a10d27b
2 /*
3 * jim-signal.c
5 */
7 #include <signal.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <unistd.h>
12 #include "jimautoconf.h"
13 #include <jim-subcmd.h>
14 #include <jim-signal.h>
16 #define MAX_SIGNALS (sizeof(jim_wide) * 8)
18 static jim_wide *sigloc;
19 static jim_wide sigsblocked;
20 static struct sigaction *sa_old;
21 static int signal_handling[MAX_SIGNALS];
23 /* Make sure to do this as a wide, not int */
24 #define sig_to_bit(SIG) ((jim_wide)1 << (SIG))
26 static void signal_handler(int sig)
28 /* We just remember which signals occurred. Jim_Eval() will
29 * notice this as soon as it can and throw an error
31 *sigloc |= sig_to_bit(sig);
34 static void signal_ignorer(int sig)
36 /* We just remember which signals occurred */
37 sigsblocked |= sig_to_bit(sig);
41 *----------------------------------------------------------------------
43 * Tcl_SignalId --
45 * Return a textual identifier for a signal number.
47 * Results:
48 * This procedure returns a machine-readable textual identifier
49 * that corresponds to sig. The identifier is the same as the
50 * #define name in signal.h.
52 * Side effects:
53 * None.
55 *----------------------------------------------------------------------
57 #define CHECK_SIG(NAME) if (sig == NAME) return #NAME
59 const char *Jim_SignalId(int sig)
61 CHECK_SIG(SIGABRT);
62 CHECK_SIG(SIGALRM);
63 CHECK_SIG(SIGBUS);
64 CHECK_SIG(SIGCHLD);
65 CHECK_SIG(SIGCONT);
66 CHECK_SIG(SIGFPE);
67 CHECK_SIG(SIGHUP);
68 CHECK_SIG(SIGILL);
69 CHECK_SIG(SIGINT);
70 #ifdef SIGIO
71 CHECK_SIG(SIGIO);
72 #endif
73 CHECK_SIG(SIGKILL);
74 CHECK_SIG(SIGPIPE);
75 CHECK_SIG(SIGPROF);
76 CHECK_SIG(SIGQUIT);
77 CHECK_SIG(SIGSEGV);
78 CHECK_SIG(SIGSTOP);
79 CHECK_SIG(SIGSYS);
80 CHECK_SIG(SIGTERM);
81 CHECK_SIG(SIGTRAP);
82 CHECK_SIG(SIGTSTP);
83 CHECK_SIG(SIGTTIN);
84 CHECK_SIG(SIGTTOU);
85 CHECK_SIG(SIGURG);
86 CHECK_SIG(SIGUSR1);
87 CHECK_SIG(SIGUSR2);
88 CHECK_SIG(SIGVTALRM);
89 CHECK_SIG(SIGWINCH);
90 CHECK_SIG(SIGXCPU);
91 CHECK_SIG(SIGXFSZ);
92 #ifdef SIGPWR
93 CHECK_SIG(SIGPWR);
94 #endif
95 #ifdef SIGCLD
96 CHECK_SIG(SIGCLD);
97 #endif
98 #ifdef SIGEMT
99 CHECK_SIG(SIGEMT);
100 #endif
101 #ifdef SIGLOST
102 CHECK_SIG(SIGLOST);
103 #endif
104 #ifdef SIGPOLL
105 CHECK_SIG(SIGPOLL);
106 #endif
107 #ifdef SIGINFO
108 CHECK_SIG(SIGINFO);
109 #endif
110 return "unknown signal";
113 const char *Jim_SignalName(int sig)
115 #ifdef HAVE_SYS_SIGLIST
116 if (sig >= 0 && sig < NSIG) {
117 return sys_siglist[sig];
119 #endif
120 return Jim_SignalId(sig);
124 * Given the name of a signal, returns the signal value if found,
125 * or returns -1 (and sets an error) if not found.
126 * We accept -SIGINT, SIGINT, INT or any lowercase version or a number,
127 * either positive or negative.
129 static int find_signal_by_name(Jim_Interp *interp, const char *name)
131 int i;
132 const char *pt = name;
134 /* Remove optional - and SIG from the front of the name */
135 if (*pt == '-') {
136 pt++;
138 if (strncasecmp(name, "sig", 3) == 0) {
139 pt += 3;
141 if (isdigit(UCHAR(pt[0]))) {
142 i = atoi(pt);
143 if (i > 0 && i < MAX_SIGNALS) {
144 return i;
147 else {
148 for (i = 1; i < MAX_SIGNALS; i++) {
149 /* Jim_SignalId() returns names such as SIGINT, and
150 * returns "unknown signal id" if unknown, so this will work
152 if (strcasecmp(Jim_SignalId(i) + 3, pt) == 0) {
153 return i;
157 Jim_SetResultString(interp, "unknown signal ", -1);
158 Jim_AppendString(interp, Jim_GetResult(interp), name, -1);
160 return -1;
163 #define SIGNAL_ACTION_HANDLE 1
164 #define SIGNAL_ACTION_IGNORE -1
165 #define SIGNAL_ACTION_DEFAULT 0
167 static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv)
169 struct sigaction sa;
170 int i;
172 if (argc == 0) {
173 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
174 for (i = 1; i < MAX_SIGNALS; i++) {
175 if (signal_handling[i] == action) {
176 /* Add signal name to the list */
177 Jim_ListAppendElement(interp, Jim_GetResult(interp),
178 Jim_NewStringObj(interp, Jim_SignalId(i), -1));
181 return JIM_OK;
184 /* Catch all the signals we care about */
185 if (action != SIGNAL_ACTION_DEFAULT) {
186 sa.sa_flags = 0;
187 sigemptyset(&sa.sa_mask);
188 if (action == SIGNAL_ACTION_HANDLE) {
189 sa.sa_handler = signal_handler;
191 else {
192 sa.sa_handler = signal_ignorer;
196 /* Iterate through the provided signals */
197 for (i = 0; i < argc; i++) {
198 int sig = find_signal_by_name(interp, Jim_String(argv[i]));
200 if (sig < 0) {
201 return JIM_ERR;
203 if (action != signal_handling[sig]) {
204 /* Need to change the action for this signal */
205 switch (action) {
206 case SIGNAL_ACTION_HANDLE:
207 case SIGNAL_ACTION_IGNORE:
208 if (signal_handling[sig] == SIGNAL_ACTION_DEFAULT) {
209 if (!sa_old) {
210 /* Allocate the structure the first time through */
211 sa_old = Jim_Alloc(sizeof(*sa_old) * MAX_SIGNALS);
213 sigaction(sig, &sa, &sa_old[sig]);
215 else {
216 sigaction(sig, &sa, 0);
218 break;
220 case SIGNAL_ACTION_DEFAULT:
221 /* Restore old handler */
222 if (sa_old) {
223 sigaction(sig, &sa_old[sig], 0);
226 signal_handling[sig] = action;
230 return JIM_OK;
233 static int signal_cmd_handle(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
235 return do_signal_cmd(interp, SIGNAL_ACTION_HANDLE, argc, argv);
238 static int signal_cmd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
240 return do_signal_cmd(interp, SIGNAL_ACTION_IGNORE, argc, argv);
243 static int signal_cmd_default(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
245 return do_signal_cmd(interp, SIGNAL_ACTION_DEFAULT, argc, argv);
248 static int signal_set_sigmask_result(Jim_Interp *interp, jim_wide sigmask)
250 int i;
251 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
253 for (i = 0; i < MAX_SIGNALS; i++) {
254 if (sigmask & sig_to_bit(i)) {
255 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, Jim_SignalId(i), -1));
258 Jim_SetResult(interp, listObj);
259 return JIM_OK;
262 static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
264 int clear = 0;
265 jim_wide mask = 0;
266 jim_wide blocked;
268 if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) {
269 clear++;
271 if (argc > clear) {
272 int i;
274 /* Signals specified */
275 for (i = clear; i < argc; i++) {
276 int sig = find_signal_by_name(interp, Jim_String(argv[i]));
278 if (sig < 0 || sig >= MAX_SIGNALS) {
279 return -1;
281 mask |= sig_to_bit(sig);
284 else {
285 /* No signals specified, so check/clear all */
286 mask = ~mask;
289 if ((sigsblocked & mask) == 0) {
290 /* No matching signals, so empty result and nothing to do */
291 return JIM_OK;
293 /* Be careful we don't have a race condition where signals are cleared but not returned */
294 blocked = sigsblocked & mask;
295 if (clear) {
296 sigsblocked &= ~blocked;
298 /* Set the result */
299 signal_set_sigmask_result(interp, blocked);
300 return JIM_OK;
303 static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
305 int sig = SIGINT;
307 if (argc == 1) {
308 if ((sig = find_signal_by_name(interp, Jim_String(argv[0]))) < 0) {
309 return JIM_ERR;
313 /* If the signal is ignored (blocked) ... */
314 if (signal_handling[sig] == SIGNAL_ACTION_IGNORE) {
315 sigsblocked |= sig_to_bit(sig);
316 return JIM_OK;
319 /* Just set the signal */
320 interp->sigmask |= sig_to_bit(sig);
322 /* Set the canonical name of the signal as the result */
323 Jim_SetResultString(interp, Jim_SignalId(sig), -1);
325 /* And simply say we caught the signal */
326 return JIM_SIGNAL;
330 *-----------------------------------------------------------------------------
332 * Jim_SignalCmd --
333 * Implements the TCL signal command:
334 * signal handle|ignore|default|throw ?signals ...?
335 * signal throw signal
337 * Specifies which signals are handled by Tcl code.
338 * If the one of the given signals is caught, it causes a JIM_SIGNAL
339 * exception to be thrown which can be caught by catch.
341 * Use 'signal ignore' to ignore the signal(s)
342 * Use 'signal default' to go back to the default behaviour
343 * Use 'signal throw signal' to raise the given signal
345 * If no arguments are given, returns the list of signals which are being handled
347 * Results:
348 * Standard TCL results.
350 *-----------------------------------------------------------------------------
352 static const jim_subcmd_type signal_command_table[] = {
353 { "handle",
354 "?signals ...?",
355 signal_cmd_handle,
358 /* Description: Lists handled signals, or adds to handled signals */
360 { "ignore",
361 "?signals ...?",
362 signal_cmd_ignore,
365 /* Description: Lists ignored signals, or adds to ignored signals */
367 { "default",
368 "?signals ...?",
369 signal_cmd_default,
372 /* Description: Lists defaulted signals, or adds to defaulted signals */
374 { "check",
375 "?-clear? ?signals ...?",
376 signal_cmd_check,
379 /* Description: Returns ignored signals which have occurred, and optionally clearing them */
381 { "throw",
382 "?signal?",
383 signal_cmd_throw,
386 /* Description: Raises the given signal (default SIGINT) */
388 { NULL }
391 static int Jim_AlarmCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
393 int ret;
395 if (argc != 2) {
396 Jim_WrongNumArgs(interp, 1, argv, "seconds");
397 return JIM_ERR;
399 else {
400 #ifdef HAVE_UALARM
401 double t;
403 ret = Jim_GetDouble(interp, argv[1], &t);
404 if (ret == JIM_OK) {
405 if (t < 1) {
406 ualarm(t * 1e6, 0);
408 else {
409 alarm(t);
412 #else
413 long t;
415 ret = Jim_GetLong(interp, argv[1], &t);
416 if (ret == JIM_OK) {
417 alarm(t);
419 #endif
422 return ret;
425 static int Jim_SleepCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
427 int ret;
429 if (argc != 2) {
430 Jim_WrongNumArgs(interp, 1, argv, "seconds");
431 return JIM_ERR;
433 else {
434 double t;
436 ret = Jim_GetDouble(interp, argv[1], &t);
437 if (ret == JIM_OK) {
438 #ifdef HAVE_USLEEP
439 usleep((int)((t - (int)t) * 1e6));
440 #endif
441 sleep(t);
445 return ret;
448 static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
450 int sig;
451 long pid;
452 Jim_Obj *pidObj;
453 const char *signame;
455 if (argc != 2 && argc != 3) {
456 Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid");
457 return JIM_ERR;
460 if (argc == 2) {
461 signame = "SIGTERM";
462 pidObj = argv[1];
464 else {
465 signame = Jim_String(argv[1]);
466 pidObj = argv[2];
469 /* Special 'kill -0 pid' to determine if a pid exists */
470 if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) {
471 sig = 0;
473 else {
474 sig = find_signal_by_name(interp, signame);
475 if (sig < 0) {
476 return JIM_ERR;
480 if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) {
481 return JIM_ERR;
484 if (kill(pid, sig) == 0) {
485 return JIM_OK;
488 Jim_SetResultString(interp, "kill: Failed to deliver signal", -1);
489 return JIM_ERR;
492 int Jim_signalInit(Jim_Interp *interp)
494 if (Jim_PackageProvide(interp, "signal", "1.0", JIM_ERRMSG))
495 return JIM_ERR;
497 /* Teach the jim core how to set a result from a sigmask */
498 interp->signal_set_result = signal_set_sigmask_result;
500 /* Make sure we know where to store the signals which occur */
501 sigloc = &interp->sigmask;
503 Jim_CreateCommand(interp, "signal", Jim_SubCmdProc, (void *)signal_command_table, NULL);
504 Jim_CreateCommand(interp, "alarm", Jim_AlarmCmd, 0, 0);
505 Jim_CreateCommand(interp, "kill", Jim_KillCmd, 0, 0);
507 /* Sleep is slightly dubious here */
508 Jim_CreateCommand(interp, "sleep", Jim_SleepCmd, 0, 0);
509 return JIM_OK;