aio recvfrom was not null terminating the result
[jimtcl.git] / jim-signal.c
blob09a0f7a9b9d3ea99fa5493ea8ab0789963d3e82c
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 "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 CHECK_SIG(SIGIO);
71 CHECK_SIG(SIGKILL);
72 CHECK_SIG(SIGPIPE);
73 CHECK_SIG(SIGPROF);
74 CHECK_SIG(SIGQUIT);
75 CHECK_SIG(SIGSEGV);
76 CHECK_SIG(SIGSTOP);
77 CHECK_SIG(SIGSYS);
78 CHECK_SIG(SIGTERM);
79 CHECK_SIG(SIGTRAP);
80 CHECK_SIG(SIGTSTP);
81 CHECK_SIG(SIGTTIN);
82 CHECK_SIG(SIGTTOU);
83 CHECK_SIG(SIGURG);
84 CHECK_SIG(SIGUSR1);
85 CHECK_SIG(SIGUSR2);
86 CHECK_SIG(SIGVTALRM);
87 CHECK_SIG(SIGWINCH);
88 CHECK_SIG(SIGXCPU);
89 CHECK_SIG(SIGXFSZ);
90 #ifdef SIGPWR
91 CHECK_SIG(SIGPWR);
92 #endif
93 #ifdef SIGCLD
94 CHECK_SIG(SIGCLD);
95 #endif
96 #ifdef SIGEMT
97 CHECK_SIG(SIGEMT);
98 #endif
99 #ifdef SIGLOST
100 CHECK_SIG(SIGLOST);
101 #endif
102 #ifdef SIGPOLL
103 CHECK_SIG(SIGPOLL);
104 #endif
105 #ifdef SIGINFO
106 CHECK_SIG(SIGINFO);
107 #endif
108 return "unknown signal";
112 * Given the name of a signal, returns the signal value if found,
113 * or returns -1 (and sets an error) if not found.
114 * We accept -SIGINT, SIGINT, INT or any lowercase version or a number,
115 * either positive or negative.
117 static int find_signal_by_name(Jim_Interp *interp, const char *name)
119 int i;
120 const char *pt = name;
122 /* Remove optional - and SIG from the front of the name */
123 if (*pt == '-') {
124 pt++;
126 if (strncasecmp(name, "sig", 3) == 0) {
127 pt += 3;
129 if (isdigit(pt[0])) {
130 i = atoi(pt);
131 if (i > 0 && i < MAX_SIGNALS) {
132 return i;
135 else {
136 for (i = 1; i < MAX_SIGNALS; i++) {
137 /* Jim_SignalId() returns names such as SIGINT, and
138 * returns "unknown signal id" if unknown, so this will work
140 if (strcasecmp(Jim_SignalId(i) + 3, pt) == 0) {
141 return i;
145 Jim_SetResultString(interp, "unknown signal ", -1);
146 Jim_AppendString(interp, Jim_GetResult(interp), name, -1);
148 return -1;
151 #define SIGNAL_ACTION_HANDLE 1
152 #define SIGNAL_ACTION_IGNORE -1
153 #define SIGNAL_ACTION_DEFAULT 0
155 static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv)
157 struct sigaction sa;
158 int i;
160 if (argc == 0) {
161 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
162 for (i = 1; i < MAX_SIGNALS; i++) {
163 if (signal_handling[i] == action) {
164 /* Add signal name to the list */
165 Jim_ListAppendElement(interp, Jim_GetResult(interp),
166 Jim_NewStringObj(interp, Jim_SignalId(i), -1));
169 return JIM_OK;
172 /* Catch all the signals we care about */
173 if (action != SIGNAL_ACTION_DEFAULT) {
174 sa.sa_flags = 0;
175 sigemptyset(&sa.sa_mask);
176 if (action == SIGNAL_ACTION_HANDLE) {
177 sa.sa_handler = signal_handler;
179 else {
180 sa.sa_handler = signal_ignorer;
184 /* Iterate through the provided signals */
185 for (i = 0; i < argc; i++) {
186 int sig = find_signal_by_name(interp, Jim_GetString(argv[i], NULL));
188 if (sig < 0) {
189 return JIM_ERR;
191 if (action != signal_handling[sig]) {
192 /* Need to change the action for this signal */
193 switch (action) {
194 case SIGNAL_ACTION_HANDLE:
195 case SIGNAL_ACTION_IGNORE:
196 if (signal_handling[sig] == SIGNAL_ACTION_DEFAULT) {
197 if (!sa_old) {
198 /* Allocate the structure the first time through */
199 sa_old = Jim_Alloc(sizeof(*sa_old) * MAX_SIGNALS);
201 sigaction(sig, &sa, &sa_old[sig]);
203 else {
204 sigaction(sig, &sa, 0);
206 break;
208 case SIGNAL_ACTION_DEFAULT:
209 /* Restore old handler */
210 if (sa_old) {
211 sigaction(sig, &sa_old[sig], 0);
214 signal_handling[sig] = action;
218 return JIM_OK;
221 static int signal_cmd_handle(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
223 return do_signal_cmd(interp, SIGNAL_ACTION_HANDLE, argc, argv);
226 static int signal_cmd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
228 return do_signal_cmd(interp, SIGNAL_ACTION_IGNORE, argc, argv);
231 static int signal_cmd_default(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
233 return do_signal_cmd(interp, SIGNAL_ACTION_DEFAULT, argc, argv);
236 static int signal_set_sigmask_result(Jim_Interp *interp, jim_wide sigmask)
238 int i;
239 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
241 for (i = 0; i < MAX_SIGNALS; i++) {
242 if (sigmask & sig_to_bit(i)) {
243 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, Jim_SignalId(i), -1));
246 Jim_SetResult(interp, listObj);
247 return JIM_OK;
250 static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
252 int clear = 0;
253 jim_wide mask = 0;
254 jim_wide blocked;
256 if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) {
257 clear++;
259 if (argc > clear) {
260 int i;
262 /* Signals specified */
263 for (i = clear; i < argc; i++) {
264 int sig = find_signal_by_name(interp, Jim_GetString(argv[i], NULL));
266 if (sig < 0 || sig >= MAX_SIGNALS) {
267 return -1;
269 mask |= sig_to_bit(sig);
272 else {
273 /* No signals specified, so check/clear all */
274 mask = ~mask;
277 if ((sigsblocked & mask) == 0) {
278 /* No matching signals, so empty result and nothing to do */
279 return JIM_OK;
281 /* Be careful we don't have a race condition where signals are cleared but not returned */
282 blocked = sigsblocked & mask;
283 if (clear) {
284 sigsblocked &= ~blocked;
286 /* Set the result */
287 signal_set_sigmask_result(interp, blocked);
288 return JIM_OK;
291 static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
293 int sig = SIGINT;
295 if (argc == 1) {
296 if ((sig = find_signal_by_name(interp, Jim_GetString(argv[0], NULL))) < 0) {
297 return JIM_ERR;
301 /* If the signal is ignored (blocked) ... */
302 if (signal_handling[sig] == SIGNAL_ACTION_IGNORE) {
303 sigsblocked |= sig_to_bit(sig);
304 return JIM_OK;
307 /* Just set the signal */
308 interp->sigmask |= sig_to_bit(sig);
310 /* Set the canonical name of the signal as the result */
311 Jim_SetResultString(interp, Jim_SignalId(sig), -1);
313 /* And simply say we caught the signal */
314 return JIM_SIGNAL;
318 *-----------------------------------------------------------------------------
320 * Jim_SignalCmd --
321 * Implements the TCL signal command:
322 * signal handle|ignore|default|throw ?signals ...?
323 * signal throw signal
325 * Specifies which signals are handled by Tcl code.
326 * If the one of the given signals is caught, it causes a JIM_SIGNAL
327 * exception to be thrown which can be caught by catch.
329 * Use 'signal ignore' to ignore the signal(s)
330 * Use 'signal default' to go back to the default behaviour
331 * Use 'signal throw signal' to raise the given signal
333 * If no arguments are given, returns the list of signals which are being handled
335 * Results:
336 * Standard TCL results.
338 *-----------------------------------------------------------------------------
340 static const jim_subcmd_type signal_command_table[] = {
341 { .cmd = "handle",
342 .args = "?signals ...?",
343 .function = signal_cmd_handle,
344 .minargs = 0,
345 .maxargs = -1,
346 .description = "Lists handled signals, or adds to handled signals"
348 { .cmd = "ignore",
349 .args = "?signals ...?",
350 .function = signal_cmd_ignore,
351 .minargs = 0,
352 .maxargs = -1,
353 .description = "Lists ignored signals, or adds to ignored signals"
355 { .cmd = "default",
356 .args = "?signals ...?",
357 .function = signal_cmd_default,
358 .minargs = 0,
359 .maxargs = -1,
360 .description = "Lists defaulted signals, or adds to defaulted signals"
362 { .cmd = "check",
363 .args = "?-clear? ?signals ...?",
364 .function = signal_cmd_check,
365 .minargs = 0,
366 .maxargs = -1,
367 .description = "Returns ignored signals which have occurred, and optionally clearing them"
369 { .cmd = "throw",
370 .args = "?signal?",
371 .function = signal_cmd_throw,
372 .minargs = 0,
373 .maxargs = 1,
374 .description = "Raises the given signal (default SIGINT)"
376 { 0 }
379 static int Jim_AlarmCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
381 int ret;
383 if (argc != 2) {
384 Jim_WrongNumArgs(interp, 1, argv, "seconds");
385 return JIM_ERR;
387 else {
388 #ifdef HAVE_UALARM
389 double t;
391 ret = Jim_GetDouble(interp, argv[1], &t);
392 if (ret == JIM_OK) {
393 if (t < 1) {
394 ualarm(t * 1e6, 0);
396 else {
397 alarm(t);
400 #else
401 long t;
403 ret = Jim_GetLong(interp, argv[1], &t);
404 if (ret == JIM_OK) {
405 alarm(t);
407 #endif
410 return ret;
413 static int Jim_SleepCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
415 int ret;
417 if (argc != 2) {
418 Jim_WrongNumArgs(interp, 1, argv, "seconds");
419 return JIM_ERR;
421 else {
422 double t;
424 ret = Jim_GetDouble(interp, argv[1], &t);
425 if (ret == JIM_OK) {
426 if (t < 1) {
427 usleep(t * 1e6);
429 else {
430 sleep(t);
435 return ret;
438 static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
440 int sig;
441 long pid;
442 Jim_Obj *pidObj;
443 const char *signame;
445 if (argc != 2 && argc != 3) {
446 Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid");
447 return JIM_ERR;
450 if (argc == 2) {
451 signame = "SIGTERM";
452 pidObj = argv[1];
454 else {
455 signame = Jim_GetString(argv[1], NULL);
456 pidObj = argv[2];
459 /* Special 'kill -0 pid' to determine if a pid exists */
460 if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) {
461 sig = 0;
463 else {
464 sig = find_signal_by_name(interp, signame);
465 if (sig < 0) {
466 return JIM_ERR;
470 if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) {
471 return JIM_ERR;
474 if (kill(pid, sig) == 0) {
475 return JIM_OK;
478 Jim_SetResultString(interp, "kill: Failed to deliver signal", -1);
479 return JIM_ERR;
482 int Jim_signalInit(Jim_Interp *interp)
484 /* Teach the jim core how to set a result from a sigmask */
485 interp->signal_set_result = signal_set_sigmask_result;
487 /* Make sure we know where to store the signals which occur */
488 sigloc = &interp->sigmask;
490 Jim_CreateCommand(interp, "signal", Jim_SubCmdProc, (void *)signal_command_table, NULL);
491 Jim_CreateCommand(interp, "alarm", Jim_AlarmCmd, 0, 0);
492 Jim_CreateCommand(interp, "kill", Jim_KillCmd, 0, 0);
494 /* Sleep is slightly dubious here */
495 Jim_CreateCommand(interp, "sleep", Jim_SleepCmd, 0, 0);
496 return JIM_OK;