Main Loop doesn't exit Normally

280 views Asked by At

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

1

There are 1 answers

0
someone On

OK, eventually I found the problem! It was located within SPLASH(). This is a macro defined as:

#define SPLASH() { \
   if (!vfork()) execlp("/bin/splash", "/bin/splash", NULL); \
   else wait(); \
}

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 use vfork instead of fork, 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 call exit, 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!). When exit 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