dbt/cnf/
dbt/lib/
dbt/lib/house/
dbt/lib/text/help/
dbt/lib/world/
dbt/lib/world/qst/
dbt/src/
dbt/src/copyover/
/******************************************************************************
 *    File: prgrun.c                                 an addition to CircleMUD *
 *   Usage: Asynchronyously run unix programs from MUD                        *
 *  Author: Petr Vilim (Petr.Vilim@st.mff.cuni.cz)                            *
 *                                                                            *
 *****************************************************************************/

/*
 * WARNING 1: It works on Linux. It should work on all UNIXes but 
 *            I didn't try it.
 *
 * WARNING 2: Use on your own risk :-)
 *
 * WARNING 3: Playing with this you can easily make some security hole, 
 *            if you make some command modifying spefied file for example. 
 *            Mud password travels through net uncrypted and can be easily 
 *            caught (try tcpdump). So some hacker can log on mud with your 
 *            char and can destroy all your files...  (and can modify 
 *            file ~/.klogin and I don't know what more) 
 *            This is the reason why I didn't make command exec which 
 *            calls function system with given parametrs.
 */
 
 /*
  * You haven't to give me a credit. If you want you can mail me.
  */

#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "auction.h"
#include "db.h"

#define PQUEUE_LENGTH    20  /* Max. number of requests in queue */
#define MAX_ARGS         5   /* Max. number of arguments of program */

extern struct char_data *get_ch_by_id(long idnum);
extern struct descriptor_data *descriptor_list;
struct program_info {
  char *args[MAX_ARGS+1];/* args[0] is name of program, 
                            rest are arguments, after last argument
                            must be NULL  */
  char *input;           /* Input sended to process 		*/
  long char_id;          /* ID of character 			*/
  char *name;            /* Name used when printing output      */
  int timeout;           /* Timeout in seconds 			*/
};

struct program_info program_queue[PQUEUE_LENGTH];
int pqueue_counter=0;	 /* Number of requests in queue */

int pid=-1;		 /* PID of runnig command, -1 if none 		*/
int to[2], from[2];	 /* file descriptors of pipes between processes */
time_t start_time;	 /* Time when process started, for timeout 	*/
int had_output;		 /* Flag if running command already had an output */
FILE *process_in;        /* Standart input for runned program 		*/

/*
 * get_ch_by_id : given an ID number, searches every descriptor for a
 *              character with that number and returns a pointer to it.
 */
void program_fork() {	 /* Start new command */
  int i;
  long flags;
    
  pipe(to);
  pipe(from);
  start_time=time(0);
  had_output=0;
  pid=fork();
  if (pid < 0) {
    pid=-1;		 /* Some problem with fork */
    return;
  }
  if (pid==0) {		 /* Child process */
    dup2 (from[1], 1);	 /* Set from[1] as standart output */
    close (from[0]);
    close (from[1]);
    dup2 (to[0], 0);
    close (to[0]);
    close (to[1]);
    for (i=2; i<1000; i++)
      close(i);		 /* Close all opened file descriptors
			    1000 should be enough I hope :-) */
    execvp(program_queue[0].args[0], program_queue[0].args);	
    exit(0);
  }
  close(from[1]);
  close(to[0]);
  process_in=fdopen(to[1], "w");
  setbuf(process_in, NULL);
  if (program_queue[0].input)   /* Send input to program if any */
    fprintf(process_in, program_queue[0].input);
  flags=fcntl(from[0], F_GETFL);
  fcntl(from[0], F_SETFL, flags | O_NONBLOCK);
			 /* Set from[0] into no-blocking mode */
}

void program_done() {
  int i;
  
  if (pid!=-1) {
    close(from[0]);	   /* Close process standart output */
    fclose(process_in);	   /* Close process standart input  */
    waitpid(pid, NULL, 0); /* Wait for process termination  */
    for (i=0; i<MAX_ARGS+1; i++)
      if (program_queue[0].args[i]) free(program_queue[0].args[i]);
      else break;
    free(program_queue[0].name);
    if (program_queue[0].input) free(program_queue[0].input);
    for (i=0; i<pqueue_counter; i++)
      program_queue[i]=program_queue[i+1]; /* shift queue */
    pqueue_counter--;
    if (pqueue_counter)
      program_fork();	 /* Start next process */
    else
      pid=-1;  
  }
}

void process_program_output() {
  int len;
  struct char_data *ch;
  char *c, *d;

  if (pid==-1) 
    return;
  len=read(from[0], buf, MAX_STRING_LENGTH);
  if ((len==-1) && (errno==EAGAIN)) { /* No datas are available now */
    if (time(0)<start_time+program_queue[0].timeout)
      return;
    else
      sprintf(buf, "&15Killing &c%s&15 becouse of timeout.\r\n",
	      program_queue[0].name);
  } else if (len < 0) {	 /* Error with read or timeout */
    sprintf(buf, "&15Error with reading from &c%s&15, killed.",
	    program_queue[0].name);
  } else if (len == 0) {  /* EOF readed  */
    if (had_output)
      buf[0]=0;
    else
      sprintf(buf, "&15No output from &13%s&15.\r\n", program_queue[0].name);    
  };
  ch=get_ch_by_id(program_queue[0].char_id);
  if (len<=0) {
    kill(pid, 9);         /* kill process with signal 9 if is still running */
    program_done();
    if ((ch) && (buf[0]))
      send_to_char(buf, ch);
    return;
  }
  had_output=1;
  buf[len]='\0';
  for (c=buf, d=buf1; *c; *d++=*c++)
    if (*c=='\n') *d++='\r';
  *d=0;
  if (!ch) return;	 /* Player quited the game? */
  send_to_char("&15Output from &13", ch);
  send_to_char(program_queue[0].name, ch);
  send_to_char("&15:&12\r\n", ch);
  send_to_char(buf1, ch);
}

/* Following function add program into queue */
void add_program(struct program_info prg, struct char_data *ch)
{
  prg.char_id=GET_IDNUM(ch);
  if (pqueue_counter==PQUEUE_LENGTH) {
    send_to_char("Sorry, there are too many requests now, try later.\r\n", ch);
    return;
  }
  program_queue[pqueue_counter]=prg;
  pqueue_counter++;
  if (pqueue_counter==1)	 /* No process is running now so start new process */
    program_fork();
}
   /*
    * get_ch_by_id : given an ID number, searches every descriptor for a
    *              character with that number and returns a pointer to it.
    */
 
/* 
 * swho calls program w. 
 * It shows who is logged on (not in mud, in unix) 
 * and what they are doing
 */
 
ACMD(do_swho) {
  struct program_info swho;
 
  swho.args[0]=strdup("w");
  swho.args[1]=NULL;
  swho.input=NULL;
  swho.timeout=10;
  swho.name=strdup("swho");
  add_program(swho, ch); 
}

/* 
 * slast calls program last.
 * It shows last 10 login of specified user or last 10 logins
 * if no user is specified.
 */

ACMD(do_slast) {
  struct program_info slast;
  
  slast.args[0]=strdup("last");
  slast.args[1]=strdup("-10");
  one_argument(argument, arg);
  if (!*arg) 			/* No user specified */
    slast.args[2]=NULL;
  else
    slast.args[2]=strdup(arg);
  slast.args[3]=NULL;
  slast.input=NULL;
  slast.timeout=5;
  slast.name=strdup("slast");
  add_program(slast, ch);
}

/* 
 * grep is normal grep <pattern> <file>
 * Useful is for example:
 * 	grep ../syslog <player> 
 *  or  grep ../log/syslog.2 <player>
 */
 
ACMD(do_grep) {
  struct program_info grep;
  
  argument=one_argument(argument, arg);
  skip_spaces(&argument);
  grep.args[0]=strdup("grep");
  /* Ignore cas, becouse one_argument function makes all characters lower. */
  grep.args[1]=strdup("-i");     
  grep.args[2]=strdup(arg);
  grep.args[3]=strdup(argument);
  grep.args[4]=NULL;
  grep.input=NULL;
  grep.timeout=10;
  grep.name=strdup("grep");
  add_program(grep, ch);
}

/*
 * sps calls program ps.
 * I use for example sps aux, sps ux on Linux, but on other
 * platforms it can have another parameters
 */

ACMD(do_sps) {
  struct program_info sps;
  
  skip_spaces(&argument);
  sps.args[0]=strdup("ps");
  sps.args[1]=strdup(argument);
  sps.args[2]=NULL;
  sps.input=NULL;
  sps.timeout=1;
  sps.name=strdup("sps");
  add_program(sps, ch);
}
ACMD(do_showadd){
  struct program_info showadd;

  skip_spaces(&argument);
  showadd.args[0]=strdup("showadd");
  showadd.args[1]=strdup(argument);
  showadd.args[2]=NULL;
  showadd.input=NULL;
  showadd.timeout=100;
  showadd.name=strdup("showadd");
  add_program(showadd, ch);
}
   
/*
void show(char *filename)
{
  FILE *fl;
  struct char_file_u player;
 struct descriptor_data d; 
  int num = 0;

  if (!(fl = fopen(filename, "r+"))) {
    perror("error opening playerfile");
    exit(1);
  }
  for (;;) {
    fread(&player, sizeof(struct char_file_u), 1, fl);
    if (feof(fl)) {
      fclose(fl);
      exit(1);
    }
    printf("%5d. ID: %5ld %-20s %-40s\n", ++num,

   player.char_specials_saved.idnum, player.name,

   player.host);
  }
}

*/