/*

Copyright (C) 1995,1996,1997 RealNetworks, Inc.
All rights reserved.
 
http://www.real.com/

This program contains proprietary information of RealNetworks, Inc.,
and is licensed subject to restrictions on use and distribution.

Disclaimer:
You may freely distribute this code as long as this header stays intact.
RealNetworks is not responsible or liable for any damage this program
may cause.  Use at your own risk. This application is unsupported and is 
distributed only as an example of what could be implemented.

Notes: 

Producer Control Utility.
This is sample utilty provided as-is with no warnenties implied.
RealNetworks cannot offer technical support for this program.

  This utility allows signals to be sent to running producer instances
  for the purpose of stopping or canceling an encoding session.

  Two arguments are taken, action (stop or cancel) and the process ID (pid)
  The action can be abbreviated to a single character.

  Key methods:
  Windows:  PostMessage, RegisterWindowMessage and FindWindow (requires windows.h)
  UNIX: kill (requires signal.h)

  The argumetns to PostMessage are:
     A signal type either 1 (stop and merge files) or 2 (cancel and discard files)
     A Window ID (Obtained by combining the Window Class Name and the Process ID.
       * The PID may be obtained from the producer -pid file option (see producer command line help) or by viewing the PID listed in NT process monitor.
  The arguments to kill are:
     A process ID (again, obtained from cmd line -pid option or OS specific means)
   A signal type either SIGINT (stop) or SIGABRT (cancel)
       * Equivalent to the Ctrl-C and Ctrl-\ keyboard shortcuts.

More information about Windows Messages may be obtained from the MSDN website at:
http://msdn.microsoft.com

More information about UNIX Signals may be obtained from the the following references:
Introduction to UNIX Signal Programming
http://users.actcom.co.il/~choo/lupg/tutorials/signals/signals-programming.html
UNIX Processes
http://www.uwsg.iu.edu/usail/external/processes.html
Advanced Programming in the UNIX Environment by W. Richard Stevens
  Addison-Wesley Publishing  ISBN 0-201-56317-7


All other UNIX signals assume defaults.  Notably, SIGHUP takes the default action of
shutting down the producer.

Usage:

  signalproducer -a <stop|cancel> -p <PID> [-q]

  Options
  -a  Action <stop|cancel>
  -p  PID <int>
  -q  Quiet (no value)
  -l  List all Producer PIDs (not implemented)

  Return codes
  1 - No arguments passed
  2 - Unrecognized arguments passed
  3 - Feature not implemented
  4 - Must provide a value for -p <PID> or -P <PIDFILE>
  5 - Cannot read PID file. 
  6 - Error registering signal window handle.\n
  7 - Error in finding the signal window handle\n
  8 - Invalid action argument.  Must be either 'stop' or 'cancel'.
  9 - Error sending signal to producer.


Future Enhancements:
 - Modify to search for running producer instances and generate list with PID
 - If only one running producer instance then perform requested action on that 
   instance (so that user does not need to remember PID for only one running instance)


Compiling:

This program should compile on any C++ capable compiler.  With some minor
modifications (like changing bool to int) it would compile on any C compiler
as well.  Following are some basic instructions:

on Linux with gcc type the following
   gcc signalproducer.cpp  -o signalproducer

On Windows:
Open in Microsoft VisualStuido include in new project.  The make file 
and depedendency file is also included so that it can be compiled 
using nmake as follows:
  nmake /f "signalproducer.mak" CFG="signalproducer - Win32 Release"

You will need to update the file signalproducer.dep with the location 
for the installion of Microsoft VisualStuido.

Testing:

Start by running the producer.
  producer -ac 0 -vc 0 -o out.rm -pid c:\pid.txt

Notice that we specify that the PID should be written to a file called "pid.txt"

To stop the producer, we can use the -P option for signalproducer.exe as follows:

  signalproducer -P "C:\pid.txt"

  Note: the defualt action is 'stop'.

You can also use the process id (PID) (-p - lowercase) to reference the running instance of producer:

  signalproducer -p 342 -a stop
  
To cancel encode (e.g. don't save temporary files use the action (-a) argument.

  signalproducer -p 342 -a cancel

If you have problems, try using the -d option to print debug messages as follows:

  signalproducer -p 342 -a cancel -d

*/

#if defined(WIN32) || defined(WINDOWS)
#include <windows>
#include <cstdlib>
// Following 2 strings are defined by the producer.exe when it registers for window events.
const char ProducerSignalWndMsg[] = "Helix Producer signal catcher";
const char ProducerSignalClassName[] = "Helix Producer signal catcher";
#else
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <cstdlib>
#endif

#include <stdio.h>

void error(int errcode, char* errmsg) {
  printf("%s\n",errmsg);
  exit(errcode);
}

int main(int argc, char* argv[])
{

  if( argc < 2 ) {
    printf("\nUsage: %s {-p <PID> | -P <PIDFILE>} [-a <stop|cancel>] [-q]\n",argv[0]);
    return 1;
  }

  // If -h or -help or --help or /? Print usage
  switch( argv[1][1] ) {
  case 'h':
  case '-':  // if user types --help
  case '?':
    printf("\nUsage: {-p <PID> | -P <PIDFILE>} %s [-a <stop|cancel>] [-q]\n",argv[0]);
    return 0;
  }

  bool quiet = false;
  bool debug = false;
  bool showpids = false;
  bool err = false;
  char action = 's';
  int pid = 0;
  char *pidfile = "0";

  // Parse the command line args.
  // Note that we are being case sensitive for flags
  for(int i = 1;i < argc; i++) {
    switch( (char) argv[i][1] ) {
    case 'a':
      action = argv[++i][0];
      break;
    case 'p':
      pid = atoi(argv[++i]);
      break;
    case 'P':
      pidfile = argv[++i];
      break;
    case 'q':
      quiet = true;
      break;
    case 'd': // undocumented option to help in debugging.
      debug = true;
      break;
    case 'l': // unimplemented option.
      showpids = true;
      break;
    default:
      printf("Unrecognized Flag: %s.  Please check input.\n", argv[i] );
      return 2;
      break;
    }
  }

  if(showpids) error(3, "Feature not implemented.");

  if(debug) printf("PID=%d, PIDFILE=%s\n", pid, pidfile);
  if(pid == 0 && pidfile == "0") error(4, "Must provide a value for -p <PID> or -P <PIDFILE>.");

  // Read PID file and write to pid
  if(pidfile != "0") {
    FILE *PIDFILE;
    PIDFILE = fopen(pidfile,"r");
    if(PIDFILE==NULL) error(5, "Cannot read PID file."); 
    char strPID[200];
    fgets(strPID,200,PIDFILE);
    pid = atoi(strPID);
  }

/*
  Alernately, this program can be simplified by using a usage of:
  Usage signalproducer <stop|cancel> <pid> [q]
  And then use the following for parsing input:
  char action = argv[1][0];
  int pid = atoi(argv[2]);
  bool quiet == false;
  if( strcmp(argv[3],"q") == 0 ) bool quiet = false;
*/

  int res=0; // Store result from function call
  #if defined(WIN32) || defined(WINDOWS)
    int WndID;
    char WndName[500];
    HWND hwnd;
    // Get the Window ID for the Helix Producer Signal Handler
    WndID = RegisterWindowMessage( ProducerSignalWndMsg );
    if (!WndID) {
      error(6, "Error registering signal window handle.");
    } 
    // Create unique ID for a specific instance producer.exe by combining the PID and Window Message
    sprintf(WndName,"%d %s",pid,ProducerSignalWndMsg);
    hwnd = FindWindow( ProducerSignalClassName, WndName);
    if (!hwnd) error(7, "Error in finding the signal window handle.");
  #endif

  switch(action) {
  case 's':
  case 'S':
    if(!quiet) printf("Stopping execution for PID=%d\n",pid);
    #if defined(WIN32) || defined(WINDOWS)
      if(debug) printf("Executing PostMessage( HWND, %d, 2, NULL)\n",WndID);
      res=PostMessage( hwnd, WndID, 2, NULL);
    #else
      if(debug) printf("Executing kill(pid,SIGINT)\n");
      res=kill(pid,SIGINT);
    #endif
    break;
  case 'c':
  case 'C':
    if(!quiet) printf("Canceling execution for PID=%d\n",pid);
    #if defined(WIN32) || defined(WINDOWS)
      if(debug) printf("Executing PostMessage( HWND, %d, 1, NULL)\n",WndID);
      res=PostMessage( hwnd, WndID, 1, NULL);
    #else
      if(debug) printf("Executing kill(pid,SIGABRT)\n");
      res=kill(pid,SIGABRT);
    #endif
    break;
  default:
    if(!quiet) error(8, "Invalid action argument.  Must be either 'stop' or 'cancel'.");
    break;
  }
  if(!quiet && res < 0) error(9, "Error sending signal to producer.");
  return 0;
}

