text: /*
  20140423
  Jan Mojzis
  Public domain.
  */
  
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <pwd.h>
  #include "crypto_uint32.h"
  #include "fail.h"
  #include "run.h"
  #include "byte.h"
  #include "channel.h"
  
  const char *user = "user";
  const char *termname = "xterm";
  crypto_uint32 id = 0;
  crypto_uint32 remotewindow = 100;
  crypto_uint32 maxpacket = 100;
  crypto_uint32 localwindow;
  unsigned char ch = 'x';
  
  static char *name(void) {
  
      struct passwd *pw;
  
      pw = getpwuid(geteuid());
      return pw->pw_name;
  }
  
  /* channel can't be opened 2x */
  static void testopen1(void) {
      int r;
      r = channel_open(user, id, remotewindow, maxpacket, &localwindow);
      if (r != 1) fail("first channel_open not returns 1");
      r = channel_open(user, id, remotewindow, maxpacket, &localwindow);
      if (r != 0) fail("first channel_open not returns 0");
      _exit(0);
  }
  
  /* *localwindow can't be 0 */
  static void testopen2(void) {
      channel_open(user, id, remotewindow, maxpacket, 0);
  }
  
  /* maxpacket can't be 0 */
  static void testopen3(void) {
      channel_open(user, id, remotewindow, 0, &localwindow);
  }
  
  /* remotewindow can't be 0 */
  static void testopen4(void) {
      channel_open(user, id, 0, maxpacket, &localwindow);
  }
  
  /* channel_openterminal() can't be called before channel_open() */
  static void testtermopen1(void) {
      channel_openterminal(termname, 0, 0, 0, 0);
  }
  
  /* channel_env() can't be called before channel_open() */
  static void testenv1(void) {
      channel_env("a", "b");
  }
  
  /* channel_env() can't be called after channel_exec() */
  static void testenv2(void) {
  
      channel_open(name(), id, remotewindow, maxpacket, &localwindow);
      channel_exec("exit 0");
      channel_env("a", "b");
  }
  
  /* channel_exec() can't be called before channel_open() */
  static void testexec1(void) {
      channel_exec("true");
  }
  
  /* channel_put() can't be called before channel_exec() */
  static void testput1(void) {
      channel_open(user, id, remotewindow, maxpacket, &localwindow);
      channel_put(&ch, 1);
  }
  
  /* channel_puteof() can't be called before channel_exec() */
  static void testputeof1(void) {
      channel_open(user, id, remotewindow, maxpacket, &localwindow);
      channel_puteof();
  }
  
  /* channel_read() can't be called before channel_exec() */
  static void testread1(void) {
      channel_open(user, id, remotewindow, maxpacket, &localwindow);
      channel_read(&ch, 1);
  }
  
  /* channel_extendedread() can't be called before channel_exec() */
  static void testextendedread1(void) {
      channel_open(user, id, remotewindow, maxpacket, &localwindow);
      channel_extendedread(&ch, 1);
  }
  
  /* channel_write() can't be called before channel_exec() */
  static void testwrite1(void) {
      channel_open(user, id, remotewindow, maxpacket, &localwindow);
      channel_write();
  }
  
  /* OK - tests */
  
  static void testok1(void) {
  
      int r, s, e;
  
      channel_open(name(), id, remotewindow, maxpacket, &localwindow);
      channel_exec("exit 0");
  
      do {
          r = channel_waitnohang(&s, &e);
      } while (r == 0);
  
      if (s != 0) fail("process killed");
      if (e != 0) fail("bad status");
      _exit(0);
  }
  
  static void testok2(void) {
  
      int r, s, e;
  
      channel_open(name(), id, remotewindow, maxpacket, &localwindow);
      channel_exec("exit 1");
  
      do {
          r = channel_waitnohang(&s, &e);
      } while (r == 0);
  
      if (s != 0) fail("process killed");
      if (e != 1) fail("bad status");
      _exit(0);
  }
  
  static void testok3(void) {
  
      int r, s, e;
  
      channel_open(name(), id, remotewindow, maxpacket, &localwindow);
      channel_exec("kill -9 $$");
  
      do {
          r = channel_waitnohang(&s, &e);
      } while (r == 0);
  
      if (s != 9) fail("bad exit signal");
      _exit(0);
  }
  
  
  static void testok4(void) {
  
      int r, s, e;
      unsigned char buf[10];
  
      channel_open(name(), id, remotewindow, maxpacket, &localwindow);
      channel_exec("cat");
  
      ch = 'a';
      channel_put(&ch, 1);
      do {
          r = channel_write();
      } while (r == 0);
  
      do {
          r = channel_read(buf, sizeof buf);
      } while (r == 0);
  
      if (r != 1) fail("channel_write()/channel_read() failure");
      if (buf[0] != 'a') fail("channel_write()/channel_read() failure");
  
      channel_puteof();
  
      do {
          r = channel_waitnohang(&s, &e);
      } while (r == 0);
  
      if (s != 0) fail("process killed");
      if (e != 0) fail("bad status");
      _exit(0);
  }
  
  #define warn_(a, b, x) do { fprintf(stderr, "%s:%llu: warning: %s\n", (a), (unsigned long long)(b), (x)); fflush(stderr); _exit(0); } while (0);
  #define warn(x) warn_(__FILE__, __LINE__, (x))
  
  
  static int homedir(void) {
      
      struct passwd *pw;
      struct stat st;
  
      pw = getpwuid(geteuid());
      if (!pw) return 0;
      
      return 1 + stat(pw->pw_dir, &st);
  }
  
  static int shell(void) {
  
      pid_t pid;
      int status;
  
      pid = fork();
      if (pid == -1) return 0;
      if (pid == 0) {
          struct passwd *pw;
          char *run[4];
          pw = getpwuid(geteuid());
          if (!pw) _exit(1);
          run[0] = pw->pw_shell;
          run[1] = (char *)"-c";
          run[2] = (char *)"exit 0";
          run[3] = (char *)0;
          execvp(run[0], run);
          _exit(1);
      }
  
      while (waitpid(pid, &status, 0) != pid) {};
      if (!WIFEXITED(status)) return 0;
      if (WEXITSTATUS(status)) return 0;
      return 1;
  }
  
  
  int main(void) {
  
      /* don't run check when homedir not exist */
      if (!homedir()) {
          warn("homedir not exist - skipping tests")
          _exit(0);
      }
  
      /* don't run check when shell returns non-zero */
      if (!shell()) {
          warn("shell returns non-zero - skipping tests")
          _exit(0);
      }
  
      run_mustpass(testopen1);
      run_mustfail(testopen2);
      run_mustfail(testopen3);
      run_mustfail(testopen4);
  
      run_mustfail(testtermopen1);
  
      run_mustfail(testenv1);
      run_mustfail(testenv2);
  
      run_mustfail(testexec1);
  
      run_mustfail(testput1);
  
      run_mustfail(testputeof1);
  
      run_mustfail(testread1);
  
      run_mustfail(testextendedread1);
  
      run_mustfail(testwrite1);
  
      /* temp. disable the rest */
      _exit(0);
  
      run_mustpass(testok1);
      run_mustpass(testok2);
      run_mustpass(testok3);
      run_mustpass(testok4);
  
      _exit(0);
  }
  