1 2/* 3 * jim-signal.c 4 * 5 */ 6 7#include <signal.h> 8#include <string.h> 9#include <ctype.h> 10#include <unistd.h> 11 12#include "jim.h" 13#include "jimautoconf.h" 14#include "jim-subcmd.h" 15#include "jim-signal.h" 16 17#define MAX_SIGNALS (sizeof(jim_wide) * 8) 18 19static jim_wide *sigloc; 20static jim_wide sigsblocked; 21static struct sigaction *sa_old; 22static int signal_handling[MAX_SIGNALS]; 23 24/* Make sure to do this as a wide, not int */ 25#define sig_to_bit(SIG) ((jim_wide)1 << (SIG)) 26 27static void signal_handler(int sig) 28{ 29 /* We just remember which signals occurred. Jim_Eval() will 30 * notice this as soon as it can and throw an error 31 */ 32 *sigloc |= sig_to_bit(sig); 33} 34 35static void signal_ignorer(int sig) 36{ 37 /* We just remember which signals occurred */ 38 sigsblocked |= sig_to_bit(sig); 39} 40 41/* 42 *---------------------------------------------------------------------- 43 * 44 * Tcl_SignalId -- 45 * 46 * Return a textual identifier for a signal number. 47 * 48 * Results: 49 * This procedure returns a machine-readable textual identifier 50 * that corresponds to sig. The identifier is the same as the 51 * #define name in signal.h. 52 * 53 * Side effects: 54 * None. 55 * 56 *---------------------------------------------------------------------- 57 */ 58#define CHECK_SIG(NAME) if (sig == NAME) return #NAME 59 60const char *Jim_SignalId(int sig) 61{ 62 CHECK_SIG(SIGABRT); 63 CHECK_SIG(SIGALRM); 64 CHECK_SIG(SIGBUS); 65 CHECK_SIG(SIGCHLD); 66 CHECK_SIG(SIGCONT); 67 CHECK_SIG(SIGFPE); 68 CHECK_SIG(SIGHUP); 69 CHECK_SIG(SIGILL); 70 CHECK_SIG(SIGINT); 71#ifdef SIGIO 72 CHECK_SIG(SIGIO); 73#endif 74 CHECK_SIG(SIGKILL); 75 CHECK_SIG(SIGPIPE); 76 CHECK_SIG(SIGPROF); 77 CHECK_SIG(SIGQUIT); 78 CHECK_SIG(SIGSEGV); 79 CHECK_SIG(SIGSTOP); 80 CHECK_SIG(SIGSYS); 81 CHECK_SIG(SIGTERM); 82 CHECK_SIG(SIGTRAP); 83 CHECK_SIG(SIGTSTP); 84 CHECK_SIG(SIGTTIN); 85 CHECK_SIG(SIGTTOU); 86 CHECK_SIG(SIGURG); 87 CHECK_SIG(SIGUSR1); 88 CHECK_SIG(SIGUSR2); 89 CHECK_SIG(SIGVTALRM); 90 CHECK_SIG(SIGWINCH); 91 CHECK_SIG(SIGXCPU); 92 CHECK_SIG(SIGXFSZ); 93#ifdef SIGPWR 94 CHECK_SIG(SIGPWR); 95#endif 96#ifdef SIGCLD 97 CHECK_SIG(SIGCLD); 98#endif 99#ifdef SIGEMT 100 CHECK_SIG(SIGEMT); 101#endif 102#ifdef SIGLOST 103 CHECK_SIG(SIGLOST); 104#endif 105#ifdef SIGPOLL 106 CHECK_SIG(SIGPOLL); 107#endif 108#ifdef SIGINFO 109 CHECK_SIG(SIGINFO); 110#endif 111 return "unknown signal"; 112} 113 114const char *Jim_SignalName(int sig) 115{ 116#ifdef HAVE_SYS_SIGLIST 117 if (sig >= 0 && sig < NSIG) { 118 return sys_siglist[sig]; 119 } 120#endif 121 return Jim_SignalId(sig); 122} 123 124/** 125 * Given the name of a signal, returns the signal value if found, 126 * or returns -1 (and sets an error) if not found. 127 * We accept -SIGINT, SIGINT, INT or any lowercase version or a number, 128 * either positive or negative. 129 */ 130static int find_signal_by_name(Jim_Interp *interp, const char *name) 131{ 132 int i; 133 const char *pt = name; 134 135 /* Remove optional - and SIG from the front of the name */ 136 if (*pt == '-') { 137 pt++; 138 } 139 if (strncasecmp(name, "sig", 3) == 0) { 140 pt += 3; 141 } 142 if (isdigit(UCHAR(pt[0]))) { 143 i = atoi(pt); 144 if (i > 0 && i < MAX_SIGNALS) { 145 return i; 146 } 147 } 148 else { 149 for (i = 1; i < MAX_SIGNALS; i++) { 150 /* Jim_SignalId() returns names such as SIGINT, and 151 * returns "unknown signal id" if unknown, so this will work 152 */ 153 if (strcasecmp(Jim_SignalId(i) + 3, pt) == 0) { 154 return i; 155 } 156 } 157 } 158 Jim_SetResultString(interp, "unknown signal ", -1); 159 Jim_AppendString(interp, Jim_GetResult(interp), name, -1); 160 161 return -1; 162} 163 164#define SIGNAL_ACTION_HANDLE 1 165#define SIGNAL_ACTION_IGNORE -1 166#define SIGNAL_ACTION_DEFAULT 0 167 168static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv) 169{ 170 struct sigaction sa; 171 int i; 172 173 if (argc == 0) { 174 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); 175 for (i = 1; i < MAX_SIGNALS; i++) { 176 if (signal_handling[i] == action) { 177 /* Add signal name to the list */ 178 Jim_ListAppendElement(interp, Jim_GetResult(interp), 179 Jim_NewStringObj(interp, Jim_SignalId(i), -1)); 180 } 181 } 182 return JIM_OK; 183 } 184 185 /* Catch all the signals we care about */ 186 if (action != SIGNAL_ACTION_DEFAULT) { 187 sa.sa_flags = 0; 188 sigemptyset(&sa.sa_mask); 189 if (action == SIGNAL_ACTION_HANDLE) { 190 sa.sa_handler = signal_handler; 191 } 192 else { 193 sa.sa_handler = signal_ignorer; 194 } 195 } 196 197 /* Iterate through the provided signals */ 198 for (i = 0; i < argc; i++) { 199 int sig = find_signal_by_name(interp, Jim_String(argv[i])); 200 201 if (sig < 0) { 202 return JIM_ERR; 203 } 204 if (action != signal_handling[sig]) { 205 /* Need to change the action for this signal */ 206 switch (action) { 207 case SIGNAL_ACTION_HANDLE: 208 case SIGNAL_ACTION_IGNORE: 209 if (signal_handling[sig] == SIGNAL_ACTION_DEFAULT) { 210 if (!sa_old) { 211 /* Allocate the structure the first time through */ 212 sa_old = Jim_Alloc(sizeof(*sa_old) * MAX_SIGNALS); 213 } 214 sigaction(sig, &sa, &sa_old[sig]); 215 } 216 else { 217 sigaction(sig, &sa, 0); 218 } 219 break; 220 221 case SIGNAL_ACTION_DEFAULT: 222 /* Restore old handler */ 223 if (sa_old) { 224 sigaction(sig, &sa_old[sig], 0); 225 } 226 } 227 signal_handling[sig] = action; 228 } 229 } 230 231 return JIM_OK; 232} 233 234static int signal_cmd_handle(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 235{ 236 return do_signal_cmd(interp, SIGNAL_ACTION_HANDLE, argc, argv); 237} 238 239static int signal_cmd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 240{ 241 return do_signal_cmd(interp, SIGNAL_ACTION_IGNORE, argc, argv); 242} 243 244static int signal_cmd_default(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 245{ 246 return do_signal_cmd(interp, SIGNAL_ACTION_DEFAULT, argc, argv); 247} 248 249static int signal_set_sigmask_result(Jim_Interp *interp, jim_wide sigmask) 250{ 251 int i; 252 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 253 254 for (i = 0; i < MAX_SIGNALS; i++) { 255 if (sigmask & sig_to_bit(i)) { 256 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, Jim_SignalId(i), -1)); 257 } 258 } 259 Jim_SetResult(interp, listObj); 260 return JIM_OK; 261} 262 263static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 264{ 265 int clear = 0; 266 jim_wide mask = 0; 267 jim_wide blocked; 268 269 if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) { 270 clear++; 271 } 272 if (argc > clear) { 273 int i; 274 275 /* Signals specified */ 276 for (i = clear; i < argc; i++) { 277 int sig = find_signal_by_name(interp, Jim_String(argv[i])); 278 279 if (sig < 0 || sig >= MAX_SIGNALS) { 280 return -1; 281 } 282 mask |= sig_to_bit(sig); 283 } 284 } 285 else { 286 /* No signals specified, so check/clear all */ 287 mask = ~mask; 288 } 289 290 if ((sigsblocked & mask) == 0) { 291 /* No matching signals, so empty result and nothing to do */ 292 return JIM_OK; 293 } 294 /* Be careful we don't have a race condition where signals are cleared but not returned */ 295 blocked = sigsblocked & mask; 296 if (clear) { 297 sigsblocked &= ~blocked; 298 } 299 /* Set the result */ 300 signal_set_sigmask_result(interp, blocked); 301 return JIM_OK; 302} 303 304static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 305{ 306 int sig = SIGINT; 307 308 if (argc == 1) { 309 if ((sig = find_signal_by_name(interp, Jim_String(argv[0]))) < 0) { 310 return JIM_ERR; 311 } 312 } 313 314 /* If the signal is ignored (blocked) ... */ 315 if (signal_handling[sig] == SIGNAL_ACTION_IGNORE) { 316 sigsblocked |= sig_to_bit(sig); 317 return JIM_OK; 318 } 319 320 /* Just set the signal */ 321 interp->sigmask |= sig_to_bit(sig); 322 323 /* Set the canonical name of the signal as the result */ 324 Jim_SetResultString(interp, Jim_SignalId(sig), -1); 325 326 /* And simply say we caught the signal */ 327 return JIM_SIGNAL; 328} 329 330/* 331 *----------------------------------------------------------------------------- 332 * 333 * Jim_SignalCmd -- 334 * Implements the TCL signal command: 335 * signal handle|ignore|default|throw ?signals ...? 336 * signal throw signal 337 * 338 * Specifies which signals are handled by Tcl code. 339 * If the one of the given signals is caught, it causes a JIM_SIGNAL 340 * exception to be thrown which can be caught by catch. 341 * 342 * Use 'signal ignore' to ignore the signal(s) 343 * Use 'signal default' to go back to the default behaviour 344 * Use 'signal throw signal' to raise the given signal 345 * 346 * If no arguments are given, returns the list of signals which are being handled 347 * 348 * Results: 349 * Standard TCL results. 350 * 351 *----------------------------------------------------------------------------- 352 */ 353static const jim_subcmd_type signal_command_table[] = { 354 { .cmd = "handle", 355 .args = "?signals ...?", 356 .function = signal_cmd_handle, 357 .minargs = 0, 358 .maxargs = -1, 359 .description = "Lists handled signals, or adds to handled signals" 360 }, 361 { .cmd = "ignore", 362 .args = "?signals ...?", 363 .function = signal_cmd_ignore, 364 .minargs = 0, 365 .maxargs = -1, 366 .description = "Lists ignored signals, or adds to ignored signals" 367 }, 368 { .cmd = "default", 369 .args = "?signals ...?", 370 .function = signal_cmd_default, 371 .minargs = 0, 372 .maxargs = -1, 373 .description = "Lists defaulted signals, or adds to defaulted signals" 374 }, 375 { .cmd = "check", 376 .args = "?-clear? ?signals ...?", 377 .function = signal_cmd_check, 378 .minargs = 0, 379 .maxargs = -1, 380 .description = "Returns ignored signals which have occurred, and optionally clearing them" 381 }, 382 { .cmd = "throw", 383 .args = "?signal?", 384 .function = signal_cmd_throw, 385 .minargs = 0, 386 .maxargs = 1, 387 .description = "Raises the given signal (default SIGINT)" 388 }, 389 { 0 } 390}; 391 392static int Jim_AlarmCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 393{ 394 int ret; 395 396 if (argc != 2) { 397 Jim_WrongNumArgs(interp, 1, argv, "seconds"); 398 return JIM_ERR; 399 } 400 else { 401#ifdef HAVE_UALARM 402 double t; 403 404 ret = Jim_GetDouble(interp, argv[1], &t); 405 if (ret == JIM_OK) { 406 if (t < 1) { 407 ualarm(t * 1e6, 0); 408 } 409 else { 410 alarm(t); 411 } 412 } 413#else 414 long t; 415 416 ret = Jim_GetLong(interp, argv[1], &t); 417 if (ret == JIM_OK) { 418 alarm(t); 419 } 420#endif 421 } 422 423 return ret; 424} 425 426static int Jim_SleepCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 427{ 428 int ret; 429 430 if (argc != 2) { 431 Jim_WrongNumArgs(interp, 1, argv, "seconds"); 432 return JIM_ERR; 433 } 434 else { 435 double t; 436 437 ret = Jim_GetDouble(interp, argv[1], &t); 438 if (ret == JIM_OK) { 439#ifdef HAVE_USLEEP 440 if (t < 1) { 441 usleep(t * 1e6); 442 } 443 else 444#endif 445 sleep(t); 446 } 447 } 448 449 return ret; 450} 451 452static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 453{ 454 int sig; 455 long pid; 456 Jim_Obj *pidObj; 457 const char *signame; 458 459 if (argc != 2 && argc != 3) { 460 Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid"); 461 return JIM_ERR; 462 } 463 464 if (argc == 2) { 465 signame = "SIGTERM"; 466 pidObj = argv[1]; 467 } 468 else { 469 signame = Jim_String(argv[1]); 470 pidObj = argv[2]; 471 } 472 473 /* Special 'kill -0 pid' to determine if a pid exists */ 474 if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) { 475 sig = 0; 476 } 477 else { 478 sig = find_signal_by_name(interp, signame); 479 if (sig < 0) { 480 return JIM_ERR; 481 } 482 } 483 484 if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) { 485 return JIM_ERR; 486 } 487 488 if (kill(pid, sig) == 0) { 489 return JIM_OK; 490 } 491 492 Jim_SetResultString(interp, "kill: Failed to deliver signal", -1); 493 return JIM_ERR; 494} 495 496int Jim_signalInit(Jim_Interp *interp) 497{ 498 if (Jim_PackageProvide(interp, "signal", "1.0", JIM_ERRMSG)) 499 return JIM_ERR; 500 501 /* Teach the jim core how to set a result from a sigmask */ 502 interp->signal_set_result = signal_set_sigmask_result; 503 504 /* Make sure we know where to store the signals which occur */ 505 sigloc = &interp->sigmask; 506 507 Jim_CreateCommand(interp, "signal", Jim_SubCmdProc, (void *)signal_command_table, NULL); 508 Jim_CreateCommand(interp, "alarm", Jim_AlarmCmd, 0, 0); 509 Jim_CreateCommand(interp, "kill", Jim_KillCmd, 0, 0); 510 511 /* Sleep is slightly dubious here */ 512 Jim_CreateCommand(interp, "sleep", Jim_SleepCmd, 0, 0); 513 return JIM_OK; 514} 515