3

I'm attempting to interrupt readline with signals (SIGUSR1), but obviously if the signal isn't handled, the program exits, when handling, it readline proceeds as though nothing has happened. Is readline supposed to be able to be interrupted using signals.

I got the idea from this other question: force exit from readline() function

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <readline/readline.h>
#include <pthread.h>

pthread_t main_id;

void *thread_main(void* arg)
{
  sleep(10);
  pthread_kill(main_id, SIGUSR1);
}

void signal_handler(int sig)
{
  puts("got signal");
  (void) sig;
}

int main(int argc, char** argv)
{
  struct sigaction sa;
  sa.sa_handler = signal_handler;
  sa.sa_flags = 0;
  sigemptyset(&sa.sa_mask);
  sigaction(SIGUSR1, &sa, NULL);

  main_id = pthread_self();

  pthread_t id;
  pthread_create(&id, NULL, thread_main, NULL);

  char *input = readline("prompt> ");

  puts("main thread done");
  return 0;
}

The output:

$ ./test
prompt> got signal
enter something
main thread done
$

Thanks.

Community
  • 1
  • 1
goji
  • 6,911
  • 3
  • 42
  • 59
  • NOTE: puts() is unsafe in a signal handler (it might call malloc, which is not reentrant). – wildplasser Sep 05 '12 at 22:27
  • This is purely an example I created to test the problem. So puts being unsafe doesn't really matter. – goji Sep 06 '12 at 02:23
  • 2
    You might need to use the [alternate interface](http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC41). You should also read more about readlines [signal handling](http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC43). – Some programmer dude Sep 06 '12 at 06:13
  • Joachim: Using the alternative interface has worked perfect. If you want to claim the reputation points, do an answer for me to accept, otherwise I'll do one tomorrow. – goji Sep 06 '12 at 21:53

3 Answers3

2

Joachim Pileborg answered this question in a comment.

The best solution seems to be using the readline alternate interface. The docs are at http://www.delorie.com/gnu/docs/readline/rlman_41.html

Also an extremely basic example at http://www.mcld.co.uk/blog/blog.php?274 that just needs to be adapted to use select instead of polling with sleep.

Much better than using signals!

goji
  • 6,911
  • 3
  • 42
  • 59
1

Change your signal_handler function:

void signal_handler(int sig)
{
        puts("got signal");
        //(void) sig;
        exit(sig);
}
TOC
  • 4,326
  • 18
  • 21
  • I don't want to just straight up exit the prorgam uncleanly. The code example is purely for testing the problem. I intend to interrupt readline in a much larger program that requires a lot of cleanup, not just exit(). – goji Sep 06 '12 at 02:24
1

libreadline's default implementation of reading a character (int rl_getc(FILE *)) does handle EINTR (returned by read() if signalled) in a way of simply re-read()ing. Due to this receiving a signal does not cancel the readline().

To work around this you might set the function pointer rl_getc_function to your own implementation to read a character (rl_getc_function points to rl_getc() by default).

alk
  • 69,737
  • 10
  • 105
  • 255
  • I gave this a test in my example code i posted, and it works perfectly, unfortunately I tried the same thing in my other code and it dosen't work. I'm guessing that is because the other code uses an idle hook, so instead of blocking on rl_getc_function it blocks in select and the accept isn't interrupting. I think I'll have to try the alternate interface Joachim mentioned. – goji Sep 06 '12 at 20:23
  • `select()` as well as `accept()` should return on a received signal and set `errno` to `EINTR` both. So you might consider sending the signal to the thread blocking in `select()` (or `accept()`? I'm not sure what it is actually doing) as well. @TroyHeron – alk Sep 07 '12 at 10:41