/*
 * W-NEWS       A simple NEWS processing package.
 *		syscall.c - wnews calls to real functions
 *
 *		For coherent users:
 *		Add checking of arguments received before doing system().
 *		Thinking about doing a stat and check permision/possiblity,
 *		existence etc..  - ET.
 *
 * Version:     @(#)syscall.c     4.50    03-Feb-1995
 *
 * Authors:     Miquel van Smoorenburg, <miquels@drinkel.ow.org>
 *              Edvard Tuinder, <ed@ow.org>
 *              Copyright 1988-1993 MicroWalt Corporation
 *		Copyright 1994,1995 Cistron Software C.V.
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 */
#include "libwnews.h"


/* Magic shell characters. */
#define SHELLMETA	"~`$&*()=|\\{};\"'<>"


/* Secure environment for wexec() function. */
static char tz[32];
static char *wenv[] = {
  "TERM=dumb",
  "HOME=/tmp",
  "PATH=/bin:/usr/bin:/usr/ucb:/usr/lib/news:/usr/local/bin",
  "LOGNAME=news",
  tz,
  NULL
};


/* Static variables for the pipe business. */
void (*p_sig)();
static int p_pid;


/* A secure "exec" function. */
void
wexec(cmd, sh_ok)
char *cmd;
int sh_ok;
{
  char buf[256];
  int f;
  char *p, *s;
  char *args[32];
  extern char **environ;

  log_close();

  /* Save the timezone. */
  if ((p = getenv("TZ")) != (char *)NULL) strcpy(tz, p);
    else strcpy(tz, "MIKE=");
  environ = wenv;

  if (! sh_ok || any(SHELLMETA, cmd) == 0) {
	/* Do it ourselves. */
	f = 0;
	s = strtok(cmd, " \t\n");
	do {
		args[f++] = s;
	} while((s = strtok(NULL, " \t\n")) != (char *)NULL && f < 31);
	args[f] = (char *)NULL;
	execvp(args[0], args);
	logmsg(LOGERR, "%s: not found\n", args[0]);
	exit(127);
  }

  /* Execute a shell to do the dirty work. */
  sprintf(buf, "exec %s", cmd);
  execl(SHELL, SHELL, "-c", buf, NULL);
  exit(127);
}


/* Our own "system" function. */
int
wsystem(cmd)
char *cmd;
{
  int pid, status;

  log_close();

  if ((pid = fork()) < 0) {
	logmsg(LOGERR, "cannot fork");
	return(ERROR);
  }

  if (pid == 0) {
	/* Child */
	wexec(cmd, 1);
  }

  /* We are the parent. Wait for child to terminate. */
  while(wait(&status) != pid)
	;
#ifdef WEXITSTATUS
  return(WEXITSTATUS(status));
#else
  return(status & 255);
#endif
}


/* Spawn a process. */
int
wspawn(cmd, inf, outf)
char *cmd;
char *inf;
char *outf;
{
  int pid, status;
  int ifd = -1, ofd = -1;

  log_close();

  if (inf && (ifd = open(inf, O_RDONLY)) < 0) {
	logmsg(LOGERR, "cannot open(%s)\n", inf);
	return(ERROR);
  }

  if (outf && (ofd = open(outf, O_CREAT|O_WRONLY, 600)) < 0) {
	logmsg(LOGERR, "cannot open(%s)\n", outf);
	return(ERROR);
  }

  if ((pid = fork()) < 0) {
	logmsg(LOGERR, "cannot fork");
	return(ERROR);
  }

  if (pid == 0) {
	if (ifd > 0) {
		close(0);
		dup(ifd);
		close(ifd);
	}
	if (ofd >= 0 && ofd != 1) {
		close(1);
		dup(ofd);
		close(ofd);
	}
	/* Child */
	wexec(cmd, 0);
  }
  if (inf) close(ifd);
  if (outf) close(ofd);

  /* We are the parent. Wait for child to terminate. */
  while(wait(&status) != pid)
	;
#ifdef WEXITSTATUS
  return(WEXITSTATUS(status));
#else
  return(status & 255);
#endif
}


/* Our own "popen()" function. */
FILE *
wpopen(cmd, mode)
char *cmd;
char *mode;
{
  int pipefd[2];
  FILE *fp;

  log_close();

  /* Create a pipe. */
  (void) pipe(pipefd);

  /* Ignore SIGPIPE */
  p_sig = signal(SIGPIPE, SIG_IGN);

  /* Fork. */
  if ((p_pid = fork()) < 0) {
	logmsg(LOGSYS|LOGERR, "cannot fork\n");
	close(pipefd[0]);
	close(pipefd[1]);
	return((FILE *)NULL);
  }

  if (p_pid == 0) {
	/* Child. Fiddle around with file descriptors. */
	if (mode[0] == 'r') {
		dup2(pipefd[1], 1);
		if (pipefd[0] != 1) close(pipefd[0]);
		if (pipefd[1] != 1) close(pipefd[1]);
	} else {
		dup2(pipefd[0], 0);
		if (pipefd[0] != 0) close(pipefd[0]);
		if (pipefd[1] != 0) close(pipefd[1]);
	}
	/* Restore sigpipe. */
	(void) signal(SIGPIPE, p_sig);

	/* Execute the command. */
	wexec(cmd, 1);
  }

  /* Parent. Return one of the pipes. */
  if (mode[0] == 'r') {
	fp = fdopen(pipefd[0], "r");
	close(pipefd[1]);
  } else {
	fp = fdopen(pipefd[1], "w");
	close(pipefd[0]);
  }
  return(fp);
}


/* Close a pipe we opened. */
int
wpclose(fp)
FILE *fp;
{
  int status;
  int pid;

  /* Close the pipe to signal end-of-file. */
  (void) fclose(fp);

  /* Now wait for the process to terminate. */
  while((pid = wait(&status)) != p_pid)
	if (pid < 0 && errno != EINTR) break;

  /* Restore SIGPIPE */
  (void) signal(SIGPIPE, p_sig);

#ifdef WEXITSTATUS
  return(WEXITSTATUS(status));
#else
  return(status & 255);
#endif
}


#ifndef COH3


int wchmod(file, mode)
char *file;
mode_t mode;
{
  return(chmod(file, mode));
}


int
wmkdir(dir, mode)
char *dir;
mode_t mode;
{
  int mask, ret;

  mask = umask(022);
  ret = mkdir(dir, mode);
  (void) umask(mask);
  return(ret);
}


int
wrmdir(dir)
char *dir;
{
  return(rmdir(dir));
}


int
wunlink(file)
char *file;
{
  return(unlink(file));
}


#else


_PRO( static int w_system, (char *));


int
wunlink(file)
char *file;
{
  char buff[256];

  sprintf(buff, "%s %s", SYS_RM, file);
  return(w_system(buff));
}


int
wchmod(path, mode)
char *path;
mode_t mode;
{
  char buff[256];

  sprintf(buff, "%s %s %o", SYS_CHMOD, path, mode);
  return(w_system(buff));
}


int
wmkdir(dir, mode)
char *dir;
mode_t mode;
{
  char buff[256];
  int mask, ret;

  mask = umask(022);
  sprintf (buff, "%s %s", SYS_MKDIR, dir);
  ret = w_system(buff);
  (void) umask(mask);
  return(ret);
}


int
wrmdir(dir)
char *dir;
{
  char buff[256];

  sprintf(buff, "%s %s", SYS_RMDIR, dir);
  return(w_system(buff));
}


/* wsystem that logs failures. */
static int
w_system(cmd)
char *cmd;
{
  int ret;

  if (cmd == (char *)NULL) {
	logmsg(LOGERR, "received nill cmd in wsystem\n");
	return(ERROR);
  }

  if ((ret = wsystem(cmd)) != OK) {
	logmsg(LOGERR|LOGSYS, "couldn't execute `%s'\n", cmd);
	return(ret);
  }
  return(OK);
}


#endif /* COHERENT */
