I have created this function which acts as the Main Menu for a Terminal-based application:
bool wizard_run() {
char *command = NULL;
bool repeat = false;
bookmark:
terminal_prepare();
terminal_message(MESSAGE_INTRODUCTION);
loop: /* repeat until a valid command */ {
free(command); /* free any previous command */
command = terminal_command(PROMPT_COMMAND, COMMAND_LENGTH, repeat);
if (!strcmp(command, COMMAND_ENCRYPT)) wizard_encrypt();
else if (!strcmp(command, COMMAND_DECRYPT)) wizard_decrypt();
else if (!strcmp(command, COMMAND_CONCEAL)) wizard_conceal();
else if (!strcmp(command, COMMAND_REVEAL)) wizard_reveal();
else if (!strcmp(command, COMMAND_ERASE)) wizard_erase();
else if (!strcmp(command, COMMAND_GENERATE)) wizard_generate();
else if (!strcmp(command, COMMAND_NAVIGATE)) directory_navigate(RDS_HOME)
else if (!strcmp(command, COMMAND_SESSION)) wizard_session();
else if (!strcmp(command, COMMAND_SAFEMODE)) wizard_safemode();
else if (!strcmp(command, COMMAND_HELP)) wizard_help(HELP_MAINMENU)
else if (!strcmp(command, COMMAND_EXIT)) {free(command); return false;}
else {repeat = true; goto loop;}
}
free(command); /* free last command */
return true;
}
All capitalized Values are #defined
constants, all ...(...)
are functions except wizard_help(...)
and directory_navigate(...)
which are macros (that's why there is no semicolon ending those lines).
This is the main function of the application:
void main() {
// initialize any Components needed
if (!directory_rdsload()) return;
packager_reset();
// show the Splash Screen
SPLASH();
// continuously execute the Main Thread
while (wizard_run()); /* execution loop */
// perform any Clean-UP needed before exit
reset();
encryption_reset();
directory_reset(is_safemode);
}
Here, only SPLASH()
is a macro.
All boolean values (i.e. true
, false
) are predefined unsigned char values, while bool
is typedef unsigned char bool;
.
The normal behaviour for it would be to exit the wizard_run()
loop when the User inputs exit
(the current value of COMMAND_EXIT), execute reset(); encryption_reset(); directory_reset(is_safemode);
and then terminate the application normally. Instead, when I type exit
for the first time, it re-displays the menu and when I type exit
for a second time it terminates with the following error:
application: cxa_atexit.c:99: __new_exitfn: Assertion `l != ((void *)0)' failed.
Aborted (core dumped)
When the program is run under gdb
, I get this error:
application: cxa_atexit.c:99: __new_exitfn: Assertion `l != ((void *)0)' failed.
Program received signal SIGABRT, Aborted.
0xb7fdd424 in __kernel_vsyscall ()
No other function except main()
ever calls wizard_run()
, so this isn't the reason why the Menu is re-displayed instead of exiting. Any ideas?
Thanks in advance!!! :D
OK, eventually I found the problem! It was located within
SPLASH()
. This is a macro defined as:Due to the fact that my application is still at the stage of testing, I didn't put
splash
in directory/bin
. I never thought this could be a problem. However, there is a catch here: I usevfork
instead offork
, as I only want to execute a new program (vfork
is faster when you don't need the Child to be a copy of the Parent process). As no/bin/bash
existed, execlp didn't load the new application image for the Child Process. Thus, I can imagine that both processes were sharing a part of their Images? (look here)So, when
SPLASH()
is called, the Child process takes over and displays the Main Menu again. Everything looks fine. But when I callexit
, the Child terminates normally and the Parent takes control of the Terminal and re-displays the Main Menu (that's why it is displayed twice!). Whenexit
is called once again, it is the Parent process' turn to terminate. However, the Child process - which had been sharing the same process image as the Parent - has already terminated, thus altering some data. It must have been this altered data that caused the error on exit.As my functions clear the terminal and write over the previous messages, I couldn't notice the Child process displaying the Main Menu at the first place! ;)
I'm really excited about the nature of the whole problem. Although I couldn't explain it better (I must have made some mistakes in my terminology), I hope you understand what went wrong! Thanks everyone for your useful comments (especially WhozCraig)!!! :D