1 /** 2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 */ 6 module proc.tty; 7 8 import std.stdio : File; 9 static import std.process; 10 11 import proc; 12 13 /// Spawn a process where stdin/stdout/stderr is pseudo-terminals. 14 PipeProcess ttyProcess(scope const(char[])[] args, const string[string] env = null, 15 std.process.Config config = std.process.Config.none, scope const(char)[] workDir = null) @trusted { 16 import proc.libc; 17 18 int master; 19 int slave; 20 auto res = openpty(&master, &slave, null, null, null); 21 if (res != 0) { 22 throw new Exception("failed to open a pseudo terminal"); 23 } 24 25 File ioSlave; 26 ioSlave.fdopen(slave, "rw"); 27 28 File ioMaster; 29 ioMaster.fdopen(master, "rw"); 30 31 auto p = std.process.spawnProcess(args, ioSlave, ioSlave, ioSlave, env, config, workDir); 32 33 return PipeProcess(p, ioMaster, ioMaster, ioMaster); 34 } 35 36 @("stdin, stdout and stderr shall be pseudo terminals") 37 unittest { 38 import core.sys.posix.unistd; 39 40 auto p = ttyProcess(["sleep", "1s"]); 41 auto res = p.wait; 42 assert(p.wait == 0); 43 44 assert(isatty(p.stdin.file.fileno) == 1); 45 assert(isatty(p.stdout.file.fileno) == 1); 46 assert(isatty(p.stderr.file.fileno) == 1); 47 } 48 49 @("shall open the command read in a fake tty") 50 unittest { 51 import std.stdio; 52 53 auto p = ttyProcess(["/bin/bash", "-c", "read && echo $REPLY"]); 54 p.stdin.write("foo\n"); 55 56 auto res = p.stdout.read(1024); 57 assert(res.length >= 4); 58 assert(res[0 .. 3] == cast(const(ubyte)[]) "foo"); 59 60 assert(p.wait == 0); 61 } 62 63 @("the execute command shall detect stdin as a fake tty") 64 unittest { 65 auto p = ttyProcess([ 66 "/bin/bash", "-c", "if [ -t 0 ]; then exit 0; fi; exit 1" 67 ]); 68 auto res = p.wait; 69 assert(p.wait == 0); 70 } 71 72 @("the execute command shall detect stdout as a fake tty") 73 unittest { 74 auto p = ttyProcess([ 75 "/bin/bash", "-c", "if [ -t 1 ]; then exit 0; fi; exit 1" 76 ]); 77 auto res = p.wait; 78 assert(p.wait == 0); 79 } 80 81 @("the execute command shall detect stderr as a fake tty") 82 unittest { 83 auto p = ttyProcess([ 84 "/bin/bash", "-c", "if [ -t 2 ]; then exit 0; fi; exit 1" 85 ]); 86 auto res = p.wait; 87 assert(p.wait == 0); 88 }