Friday, March 2, 2012

Redirecting Output of already Running Process II

     We can redirect standard output of a running process with gdb. However, in this method application is stopped by gdb and continue again. In fact, we can redirect standard output to anywhere/any file without stopping process with
some extra code. At below, we will explain how to define signals to control redirecting operation.

     POSIX lets user to define their signals. You should use "SIGUSR1" to define your signal(man 7 signal). We define two new signals, add lines below to your source:

         /* user define signals */
         #define SIG_DIRECT_STDOUT_TO_FILE (SIGUSR1+51)
         #define SIG_REDIRECT_STDOUT (SIGUSR1+52)

     We should define new file where standard outputs will be redirected to it. Of course, we can get this file name as parameter, read from environment etc.

         #define CASE_LOG_FILE "/tmp/caselog.txt"

     We want to redirect output when first signal received and reassign original output file to original file. For that purpose we should get original output file info before redirection.

         #define STDOUT_FILE_SIZE 256
         static char stdoutFilePath[STDOUT_FILE_SIZE];

     We should define our signal handler which responsible with redirection:

         /* Function for signal directing stdout to case log file */
         static void appSigDirect(int sig)
         {
             fprintf(stderr, "SIG_DIRECT_STDOUT_TO_FILE entered, redirecting stdout to CASE_LOG_FILE!");
             freopen(CASE_LOG_FILE, "w", stdout);
         }


         /* Function for signal redirecting stdout to original file */
         static void appSigRedirect(int sig)
         {
             fprintf(stderr, "SIG_REDIRECT_STDOUT entered, redirecting stdout to original file: %s", stdoutFilePath);
             freopen(stdoutFilePath, "a", stdout);
         }

     In main function we should assign original output file to stdoutFilePath[] and assign handler to appropriate signals.

         /* get initial stdout direct file path */
         readlink("/proc/self/fd/1", stdoutFilePath, STDOUT_FILE_SIZE);


         if (signal(SIG_DIRECT_STDOUT_TO_FILE, appSigDirect) == SIG_ERR)
         {
             return -1;
         }


         if (signal(SIG_REDIRECT_STDOUT, appSigRedirect) == SIG_ERR)
         {
             return -1;
         }

     Compile your code and run as you wish. For example if you run your application as below and your application process id is "$APP_PID", you should see "/proc/$APP_PID/fd/1" linked to "/tmp/mylog.txt":

         myapp > /tmp/mylog.txt &

     Now you can tail "/tmp/mylog.txt" file to see outputs come to that file. When you want to redirect your logs to "/tmp/caselog.txt",
send SIG_DIRECT_STDOUT_TO_FILE signal to your application. Since SIGUSR1 is defined 10 in linux, if you are using linux our signal will be 61.

         kill -61 $APP_PID

     At that point your application will update outputfile to "/tmp/caselog.txt" (check "/proc/$APP_PID/fd/1") and output logs will be written to caselog.txt instead of mylog.txt file.

     When you want to reassing mylog.txt file as output file, you can use SIG_REDIRECT_STDOUT signal which number is 62 in linux.

         kill -62 $APP_PID