I'm writing a tiny web server in C, and I want to ignore SIGPIPE signal so that my server won't terminate by trying to write to closed connection.
Signal(SIGPIPE, SIG_IGN);
I create thread to handle each request, and I set the signal ignore at main thread. But I think signal is process-level, so I believe this should work for any child thread created.(i.e, I don't have to call this signal ignore again in my child thread.) But still, my server will terminated for some reason. I still debugging on that, but I just want to make sure that it's not the SIGPIPE problem.....
Technically, the C standard says that using the signal function in a multithreaded environment results in undefined behavior (which is crazy, IMHO, especially if your calling it with SIG_IGN).
Practically, signal dispositions are process-wide, and it is recommended that new POSIX programs use sigaction instead of signal, as sigaction has better defined semantic (not that it matters for SIG_IGN).
In C99 and newer, you could call sigaction like so:
sigaction(SIGPIPE, &(struct sigaction){ .sa_handler = SIG_IGN }, 0);
(Note that if you call it correctly, you shouldn't have to check the return status since the only possible errors a, which arere EINVAL and EFAULT, which you'll only get if you provide ill-formed arguments.)
Related
I am wondering if there is ever a good reason to exit a watchdog thread in the manner depicted, versus exiting with exit(). In the code I came across that brought this question to mind, a segfault was caused by de-referencing a null pointer with the strange line *(char **)0 = "watchdog timeout";.
Unless I'm mistaken, a thread calling exit() terminates the entire process. I interpret a segfault as an error, and not intended behavior, but perhaps there are times when it is desired.
void *watchdog_loop(void *arg) {
time_t now;
while(foo) {
sleep(1);
now = current_time();
if (watchdog_timeout && now - bar > watchdog_timeout) {
raise(SIGSEGV); //something went wrong
}
}
return NULL;
}
Is there ever a time that it would be more desirable to have a watchdog loop segfault intentionally, versus exiting nonzero?
It is never desirable to elicit undefined behavior, which is what the example code does. In particular, note well that that code is not required to cause a segfault to be delivered to the process, though it might reliably do so on certain systems.
However, one might indeed prefer to kill a process via a signal instead of by calling exit(), so as to achieve termination without executing any application or library cleanup code. This is a plausible goal for a watchdog. Even in that event, however,
Either the raise() or the abort() function would definedly cause a signal to be delivered to the process.
SIGSEGV seems an odd choice of signal. Any of SIGABRT, SIGTERM, or SIGKILL would make more sense to me. Of those,
SIGKILL is not specified by the C language spec, but rather by POSIX (and maybe others). On a POSIX system, SIGKILL cannot be blocked or caught, so it is a very good candidate for a signal to terminate the process as quickly and surely as possible.
SIGABRT is used by the abort() function, which also goes to some pains to try to overcome program resistance to being terminated that way. This is the most natural standard function to use to trigger an intentional abnormal program termination.
SIGTERM can be caught and / or blocked, but unlike SIGKILL, it is defined by the C language specification, and therefore is more portable. But I don't really see any advantage over SIGABRT, unless you intend to allow it to be handled.
Another alternative would be _exit() (POSIX) or _Exit() (C99 or later). These perform a cleaner shutdown than you can expect from termination via a signal, but without executing most cleanup code. Open files will be closed, and the parent process will observe the process to terminate normally with a failure status instead of terminating by being killed by a signal.
I was about to add an extra signal handler to an app we have here and I noticed that the author had used sigaction() to set up the other signal handlers. I was going to use signal(). To follow convention I should use sigaction() but if I was writing from scratch, which should I choose?
Use sigaction() unless you've got very compelling reasons not to do so.
The signal() interface has antiquity (and hence availability) in its favour, and it is defined in the C standard. Nevertheless, it has a number of undesirable characteristics that sigaction() avoids - unless you use the flags explicitly added to sigaction() to allow it to faithfully simulate the old signal() behaviour.
The signal() function does not (necessarily) block other signals from arriving while the current handler is executing; sigaction() can block other signals until the current handler returns.
The signal() function (usually) resets the signal action back to SIG_DFL (default) for almost all signals. This means that the signal() handler must reinstall itself as its first action. It also opens up a window of vulnerability between the time when the signal is detected and the handler is reinstalled during which if a second instance of the signal arrives, the default behaviour (usually terminate, sometimes with prejudice - aka core dump) occurs.
The exact behaviour of signal() varies between systems — and the standards permit those variations.
These are generally good reasons for using sigaction() instead of signal(). However, the interface of sigaction() is undeniably more fiddly.
Whichever of the two you use, do not be tempted by the alternative signal interfaces such as
sighold(),
sigignore(),
sigpause() and
sigrelse().
They are nominally alternatives to sigaction(), but they are only barely standardized and are present in POSIX for backwards compatibility rather than for serious use. Note that the POSIX standard says their behaviour in multi-threaded programs is undefined.
Multi-threaded programs and signals is a whole other complicated story. AFAIK, both signal() and sigaction() are OK in multi-threaded applications.
Cornstalks observes:
The Linux man page for signal() says:
The effects of signal() in a multi-threaded process are unspecified.
Thus, I think sigaction() is the only that can be used safely in a multi-threaded process.
That's interesting. The Linux manual page is more restrictive than POSIX in this case. POSIX specifies for signal():
If the process is multi-threaded, or if the process is single-threaded and a signal handler is executed other than as the result of:
The process calling abort(), raise(), kill(), pthread_kill(), or sigqueue() to generate a signal that is not blocked
A pending signal being unblocked and being delivered before the call that unblocked it returns
the behavior is undefined if the signal handler refers to any object other than errno with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or if the signal handler calls any function defined in this standard other than one of the functions listed in Signal Concepts.
So POSIX clearly specifies the behaviour of signal() in a multi-threaded application.
Nevertheless, sigaction() is to be preferred in essentially all circumstances — and portable multi-threaded code should use sigaction() unless there's an overwhelming reason why it can't (such as "only use functions defined by Standard C" — and yes, C11 code can be multi-threaded). Which is basically what the opening paragraph of this answer also says.
In short:
sigaction() (see here and here) is good and well-defined, but is a POSIX function and so it works only on Linux or POSIX systems. signal() (see here and here) is bad and poorly-defined, but is a C standard function and so it works on anything.
What do the Linux man pages have to say about it?
man 2 signal (see it online here) states:
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. See Portability below.
Portability
The only portable use of signal() is to set a signal's disposition to
SIG_DFL or SIG_IGN. The semantics when using signal() to establish a
signal handler vary across systems (and POSIX.1 explicitly permits
this variation); do not use it for this purpose.
In other words: do not use signal(). Use sigaction() instead!
This position is reiterated in the next line which states (emphasis added):
POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a signal handler is invoked; use that interface instead of signal().
What does GCC think?
From https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling (emphasis added):
Compatibility Note: As said above for signal, this function should be avoided when possible. sigaction is the preferred method.
So, if both Linux and GCC say not to use signal(), but to use sigaction() instead, that begs the question: how the heck do we use this confusing sigaction() thing!?
Usage Examples:
Read GCC's EXCELLENT signal() example here: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
And their EXCELLENT sigaction() example here: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
After reading those pages, I came up with the following technique for sigaction():
1. sigaction(), since it's the right way to attach a signal handler, as described above:
#include <errno.h> // errno
#include <signal.h> // sigaction()
#include <stdio.h> // printf()
#include <string.h> // strerror()
// Format: const char *, unsigned int, const char *
#define LOG_LOCATION __FILE__, __LINE__, __func__
#define LOG_FORMAT_STR "file: %s, line: %u, func: %s: "
/// #brief Callback function to handle termination signals, such as
/// Ctrl + C
/// #param[in] signal Signal number of the signal being handled by this
/// callback function
/// #return None
static void termination_handler(const int signal)
{
switch (signal)
{
case SIGINT:
printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal);
break;
case SIGTERM:
printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n",
signal);
break;
case SIGHUP:
printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal);
break;
default:
printf("\nUnk signal (%i) caught.\n", signal);
break;
}
// DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc.
exit(signal);
}
/// #brief Set a new signal handler action for a given signal
/// #details Only update the signals with our custom handler if they are NOT
/// set to "signal ignore" (`SIG_IGN`), which means they are currently
/// intentionally ignored. GCC recommends this "because non-job-control
/// shells often ignore certain signals when starting children, and it is
/// important for children to respect this." See
/// https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
/// and
/// https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html.
/// Note that termination signals can be found here:
/// https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
/// #param[in] signal Signal to set to this action
/// #param[in] action Pointer to sigaction struct, including the callback
/// function inside it, to attach to this signal
/// #return None
static inline void set_sigaction(int signal, const struct sigaction *action)
{
struct sigaction old_action;
// check current signal handler action to see if it's set to SIGNAL IGNORE
sigaction(signal, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
{
// set new signal handler action to what we want
int ret_code = sigaction(signal, action, NULL);
if (ret_code == -1)
{
printf(LOG_FORMAT_STR "sigaction failed when setting signal to "
"%i; errno = %i: %s\n",
LOG_LOCATION, signal, errno, strerror(errno));
}
}
}
int main(int argc, char *argv[])
{
//...
// Register callbacks to handle kill signals; prefer the Linux function
// `sigaction()` over the C function `signal()`: "It is better to use
// sigaction if it is available since the results are much more reliable."
// Source:
// https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
// and
// https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711.
// See here for official gcc `sigaction()` demo, which this code is modeled
// after:
// https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
// Set up the structure to specify the new action, per GCC's demo.
struct sigaction new_action;
new_action.sa_handler = termination_handler; // set callback function
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
// SIGINT: ie: Ctrl + C kill signal
set_sigaction(SIGINT, &new_action);
// SIGTERM: termination signal--the default generated by `kill` and
// `killall`
set_sigaction(SIGTERM, &new_action);
// SIGHUP: "hang-up" signal due to lost connection
set_sigaction(SIGHUP, &new_action);
//...
}
2. And for signal(), even though its not a good way to attach a signal handler, as described above, it's still good to know how to use it.
Here's the GCC demonstration code copy-pasted, as it's about as good as it's going to get:
#include <signal.h>
void
termination_handler (int signum)
{
struct temp_file *p;
for (p = temp_file_list; p; p = p->next)
unlink (p->name);
}
int
main (void)
{
…
if (signal (SIGINT, termination_handler) == SIG_IGN)
signal (SIGINT, SIG_IGN);
if (signal (SIGHUP, termination_handler) == SIG_IGN)
signal (SIGHUP, SIG_IGN);
if (signal (SIGTERM, termination_handler) == SIG_IGN)
signal (SIGTERM, SIG_IGN);
…
}
The main links to be aware of:
Standard Signals: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
Termination Signals: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
Basic Signal Handling, including official GCC signal() usage example: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
Official GCC sigaction() usage example: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
Signal sets, including sigemptyset() and sigfillset(); I still don't understand these exactly, but know they are important: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html
See also:
My answer on "How to manually send any signal to any running process" and "How to trap any signal in your program" (ex: in Bash).
TutorialsPoint C++ Signal Handling [with excellent demo code]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
https://www.tutorialspoint.com/c_standard_library/signal_h.htm
To me, this below line was enough to decide:
The sigaction() function provides a
more comprehensive and reliable
mechanism for controlling signals; new
applications should use sigaction()
rather than signal()
http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07
Whether you're starting from scratch or modifying an old program, sigaction should be the right option.
signal() is standard C, sigaction() is not.
If you're able to use either (that is, you're on a POSIX system), then use sigaction(); it's unspecified whether signal() resets the handler, meaning that to be portable you have to call signal() again inside the handler. What's worse is that there's a race: if you get two signals in quick succession, and the second is delivered before you reinstall the handler, you'll have the default action, which is probably going to be to kill your process.
sigaction(), on the other hand, is guaranteed to use “reliable” signal semantics. You need not reinstall the handler, because it will never be reset. With SA_RESTART, you can also get some system calls to automatically restart (so you don't have to manually check for EINTR).
sigaction() has more options and is reliable, so its use is encouraged.
Psst... don't tell anyone I told you this, but POSIX currently has a function bsd_signal() which acts like signal() but gives BSD semantics, which means it's reliable. Its main use is for porting old applications that assumed reliable signals, and POSIX does not recommend using it.
They're different interfaces for OS's signal facilities. One should prefer using sigaction to signal if possible as the signal() has implementation-defined (often race prone) behavior and behaves differently on Windows, OS X, Linux and other UNIX systems.
See this security note for details.
From the signal(3) man page:
DESCRIPTION
This signal() facility is a simplified interface to the more
general sigaction(2) facility.
Both invoke the same underlying facility. You should presumably not manipulate the response the a single signal with both, but mixing them shouldn't cause anything to break...
I would also suggest using sigaction() over signal() and would like to add one more point.
sigaction() gives you more options such as pid of the process that died (possible using the siginfo_t struct).
I'd use signal() since it's more portable, in theory at least. I'll vote up any commenter who can come up with a modern system that doesn't have a POSIX compatibility layer and supports signal().
Quoting from the GLIBC documentation:
It's possible to use both the signal and sigaction functions within
a single program, but you have to be careful because they can
interact in slightly strange ways.
The sigaction function specifies more information than the signal
function, so the return value from signal cannot express the full
range of sigaction possibilities. Therefore, if you use signal to
save and later reestablish an action, it may not be able to
reestablish properly a handler that was established with sigaction.
To avoid having problems as a result, always use sigaction to save
and restore a handler if your program uses sigaction at all. Since
sigaction is more general, it can properly save and reestablish any
action, regardless of whether it was established originally with
signal or sigaction.
On some systems if you establish an action with signal and then
examine it with sigaction, the handler address that you get may not
be the same as what you specified with signal. It may not even be
suitable for use as an action argument with signal. But you can rely
on using it as an argument to sigaction. This problem never happens
on the GNU system.
So, you're better off using one or the other of the mechanisms
consistently within a single program.
Portability Note: The basic signal function is a feature of ISO C,
while sigaction is part of the POSIX.1 standard. If you are
concerned about portability to non-POSIX systems, then you should
use the signal function instead.
Copyright (C) 1996-2008 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.2 or any later version published by the Free Software
Foundation; with no Invariant Sections, with no Front-Cover Texts,
and with no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
From man page signal(7)
A process-directed signal may be delivered to any
one of the threads that does not currently have the signal blocked.
If more than one of the threads has the signal unblocked, then the
kernel chooses an arbitrary thread to which to deliver the signal.
And I would say this "issue" exists for signal(2) and sigaction(2).
So be careful with signals and pthreads.
... and signal(2) seems to call sigaction(2) underneath in Linux with glibc.
Consider the following chunk of C code:
void TERMHandler(int sig){
signal (sig, SIG_DFL);
}
main() {
pid_t pid;
pid = fork()
if (pid == 0) {
signal(SIGTERM,TERMHandler);
while(1);
}
else
{
sleep(3);
kill(pid,SIGTERM);
sleep(3);
kill(pid,SIGTERM);
}
}
We create a new process and distinguish between child (pid = 0) and parent.
Can a custom handler be used for every type of signals? If so, assuming we create a custom handler, is it right that there wouldn't be any difference between all signals if I only use the signal once (or never reset the signal handler), since it would just execute my handler without considering the signal in the function?
What I'm trying to say is, is it right that:
signal(SIGTERM,CustomHandler);
signal(SIGTSTP,CustomHandler);
signal(SIGHUP,CustomHandler);
...
will execute the same code when the parent runs kill(pid, SomeSignal)?
Can a custom handler be used for every type of signals?
Yes, the same custom signal-handler function can be registered to handle different types of signals, up to and including all the signals that can be caught on the system in question. Note, however, that there may be defined signals that cannot be caught. On POSIX-conforming systems, for example, SIGKILL and SIGSTOP have this property.
If so,
assuming we create a custom handler, is it right that there wouldn't
be any difference between all signals if I only use the signal once
(or never reset the signal handler), since it would just execute my
handler without considering the signal in the function?
The signal handler function is not obligated to consider the signal number in determining what to do. It can perform the same action no matter what, or, as in your example function, it can simply pass the signal number on to some other function. You may or may not consider the latter to be a special case of the former.
Do note, however, that on a POSIX system, the sigaction() function is preferable to signal() for modifying signal dispositions. Its behavior is both more flexible and more consistent than signal()'s over various operating systems.
Can a custom handler be used for every type of signals?
Yes. You can install a custom "signal-catching" function for all signals which can be caught. (For example, SIGKILL and SIGSTOP may not be caught.)
[I]s it right that there wouldn't be any difference between all signals if I only use the signal once (or never reset the signal handler), since it would just execute my handler without considering the signal in the function?
That depends on how you code your signal catching function. The system will pass the caught signal to the function, so the same function could do something different upon catching a SIGTERM rather than a SIGHUP, for instance. If your handler ignores its sig argument and ignores the signal environment generally (masks, stacks, dispositions), then, yes, each invocation would be like any other.
In a single threaded program, does a race condition is possible in a signal handler?
void signal_handler(...)
{
static int i = 0;
i = i + 10 * 10;
}
Imagine that two very close signals are thrown, so close that they enter the function at the same time.
I can't find informations about how recent Linux OS handle this. I just know that both signals are correctly handled but I don't know how. Does race conditions are possible ?
Any helps appreciated, thanks!
There is no race condition in the sense that you mean (between two signals). Multiple signals of the same signal are not delivered simultaneously. Unless precautions are taken, multiple signals for different signal numbers may be delivered simultaneously, as described in torek's answer.
Whenever you involve variables of static duration (or global variables), your function may no longer reentrant. This is typically not important for the signal handler function itself. However, if it calls some other function that accesses global or static data, then that function will see an access pattern that is similar to two threads racing through a critical section. That is, your program is calling such a function to do its normal processing, but the signal arrives in the middle of that function, and then your signal handler calls into that same function. The global/static variables may be in an inconsistent state, and might cause your program to have non-deterministic behavior.
POSIX defines a set of APIs that are safe to be called from within a signal handler. Your code should take similar precautions when you plan to let your signal handler call functions that you implement.
Single threaded means only one app touching the static at a time yes? If there are 2 apps, there are 2 statics and no race condition.
If this is an interrupt handler and i += 100 is not atomic (which it may be depending on the platform/CPU), then it would race.
One additional, important note: if you're using "reliable signals" (POSIX sigaction with the corresponding sa_mask field), you get control over how signals behave in a single-thread-single-process situation.
Consider the case of single process P1, with a signal-handler like the one you show above. Suppose that you are catching signal SIGUSR1 and having that enter the function signal_handler. While you are inside signal_handler, some other process P2 sends another SIGUSR1 to P1 (e.g., via kill). This signal is "blocked" (temporarily) via sa_mask until signal_handler returns in P1. This is true even if you don't set any bits in sa_mask (as long as you don't set SA_NODEFER in sa_flags, see below).
But, suppose you've also decided to catch SIGUSR2 with function signal_handler. Suppose that P2 also sends a SIGUSR2. In this case, the SIGUSR2 is (or may be) caught, starting another instance of signal_handler running, this time on behalf of the SIGUSR2 signal.
You can prevent this by making sure that when SIGUSR1 is being handled, SIGUSR2 is temporarily blocked as well. In general you'd probably want to make SIGUSR1 blocked while SIGUSR2 is being handled. To do this, set both corresponding bits in sa_mask:
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_flags = SA_RESTART | SA_SIGINFO; /* (decide for yourself which flags) */
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_sigaction = signal_handler;
error = sigaction(SIGUSR1, &sa, NULL);
if (error) ... handle error ...
error = sigaction(SIGUSR2, &sa, NULL);
if (error) ... handle error ...
The two sigaddset calls make sure that both SIGUSR1 and SIGUSR2 are held-off (blocked, temporarily) for the duration of the function.
If you're only catching one signal, there is no need for this extra complexity, because as long as SA_NODEFER is not set, the OS automatically adds whatever signal triggered entry to your signal handler to the "currently blocked signals" set at entry.
(Note that the OS's automatic blocking and unblocking of signals at entry and exit to your signal handler is done with sigprocmask, using SIG_BLOCK and SIG_SETMASK—not SIG_UNBLOCK—with the mask for the SIG_SETMASK at exit set by saving the previous mask filled in via SIG_BLOCK. Well, it's normally done inside kernel code, rather than actually calling sigprocmask, but the effect is the same, just more efficient.)
I am trying to make my program ignore Ctrl+C in unix which seems to work, the issue is that it keep writing "Syntax error". Here is the code
extern "C" void ignore( int sig )
{
fprintf( stderr, "\n"); // Print a new line
// This function does nothing except ignore ctrl-c
}
int main()
{
// For ctrl-c
sigset( SIGINT, ignore );
while (1) {
getUserInput();
}
return 0;
}
Everytime I hit Ctrl+C it runs through getUserInput again, which is the expected behavior, but it writes "Syntax error" as well. I checked and the "ignore" function gets executed, and once it has been executed, then it prints the error message, I am not sure why.
Does anyone have any clues please?
Thank you very much,
Jary
Do not use sigset(). Although it is in POSIX 2008, it is marked obsolescent - and also unsafe in threaded programs.
You then have a choice between signal(), which is blessed by ISO C but has some undesirable characteristics, and sigaction() which is the preferred solution in POSIX systems.
One key point with signal handling is to ensure you do not trap any signals that are ignored when you enter the program - unless you know something that the caller can't (such as you need to trap SIGCHLD signals for dead children).
This leads to the standard formulations, preached since time immemorial, for signal():
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, ignore);
Or, for sigaction():
struct sigaction new_sa;
struct sigaction old_sa;
sigfillset(&new_sa.sa_mask);
new_sa.sa_handler = SIG_IGN;
new_sa.sa_flags = 0;
if (sigaction(SIGINT, &new_sa, &old_sa) == 0 && old_sa.sa_handler != SIG_IGN)
{
new_sa.sa_handler = ignore;
sigaction(SIGINT, &new_sa, 0);
}
Since you do not show us the getUserInput() function, there is no way we can prognosticate on why you see 'Syntax Error'. However, if you have a grammar at work, it may well be that your read is not returning valid data, and the parser is unhappy with what is left in the buffer for it to process.
(Note: for more portability, you should probably use signal() (C standard library) or sigaction() (POSIX) instead of sigset(), but I don't think that's the problem here.)
You're not really ignoring the signal here; you're catching it and taking your own action - but it's likely that a system call has returned an error as a result of the interruption.
e.g. maybe you've caused a read system call to fail with EINTR. I suspect the real problem is that some code inside getUserInput() is not handling this error case.
You can ignore the signal completely by setting the handler to the special value SIG_IGN, which should work with any of sigset(), signal() or sigaction().
The "Syntax Error" message must be coming from the getUserInput function (or something it calls, of course) -- you could investigate by having that function print out what it is receiving and why it is complaining.
Note that the canonical way to ignore a signal is to use the pre-defined SIG_IGN as the signal handler. e.g.
sigset(SIGINT, SIG_IGN)
ignore() does a lot more than nothing.
When you run this code on UNIX, SIGINT is delivered asynchronously on the same thread that is executing getUserInput(). If a SIGINT arrives while getUserInput() is accessing stderr, the behavior is undefined because fprintf() usually isn't designed to be reentrant. (However, the behavior is likely to include garbled and/or repeated output. Did your program print "Syntax Error" before the SIGINT was delivered?) CERT and GNU libc have some more info about this.
If you were running this code on Win32, SIGINT would be delivered asynchronously in a separate thread.