Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

278
Visualizações
Opening new pseudo-terminal device file in macOS with swift

I am trying to keep a shell (bash/zsh/etc) running by using Process from the Foundation library.

I know how to use Pipe for standard input/output/error for other command line executables but it seems like the shell programs require that the standard input/output/error file be a terminal device file.

It looks like these are the files called /dev/ttys<3 digit number> which are created for every new shell instance. How can I create these files myself and use them in swift?

over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

SOLUTION:

So this turned out to be a lot easier than I thought it would be. Here are the steps needed to create a pair of master and slave pseudo-terminal FileHandle objects:

  1. Import the Darwin module.
  2. Call on posix_openpt(int oflag) to get a file descriptor for an available master pseudo-terminal device.
  3. Call on grantpt(int filedes) to establish ownership of corresponding slave pseudo-terminal device.
  4. Call on unlockpt(int filedes) to unlock the slave pseudo-terminal device.
  5. Call on ptsname(int filedes) to get path to slave pseudo-terminal device.
  6. Create FileHandle objects using the file descriptor from step 2 and path from step 5.

In steps 3 - 5 filedes is the file descriptor returned by the call to posix_openpt(int oflag) from step 1. Use O_RDRW for the oflag parameter in step 1 for read-write permissions on the master pseudo-terminal device file.

Example code where we keep a bash session alive and have it run the tty command:

import Foundation
import Darwin

class A: NSObject {
    var task: Process?
    var slaveFile: FileHandle?
    var masterFile: FileHandle?

    override init() {
        self.task = Process()
        var masterFD: Int32 = 0
        masterFD = posix_openpt(O_RDWR)
        grantpt(masterFD)
        unlockpt(masterFD)
        self.masterFile = FileHandle.init(fileDescriptor: masterFD)
        let slavePath = String.init(cString: ptsname(masterFD))
        self.slaveFile = FileHandle.init(forUpdatingAtPath: slavePath)
        self.task!.executableURL = URL(fileURLWithPath: "/bin/bash")
        self.task!.arguments = ["-i"]
        self.task!.standardOutput = slaveFile
        self.task!.standardInput = slaveFile
        self.task!.standardError = slaveFile
    }

    func run() {
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            guard let self = self else {
                return
            }
            do {
                try self.task!.run()
            } catch {
                print("Something went wrong.\n")
            }
        }
        let data = self.masterFile!.availableData
        let strData = String(data: data, encoding: String.Encoding.utf8)!
        print("Output: "+strData)
        self.masterFile!.write("tty\u{0D}".data(using: String.Encoding.utf8)!)
        sleep(1)
        let data1 = self.masterFile!.availableData
        let strData1 = String(data: data1, encoding: String.Encoding.utf8)!
        print("Output: "+strData1)
    }
}

let a = A()
a.run()
over 4 years ago · Santiago Trujillo Relatório

0

An even simpler answer that is Playground friendly.

import PlaygroundSupport
import Foundation
import Darwin

// use socat to create psudo echo device
// socat -d -d -x pty,raw,nonblock,ispeed=115200,echo=0,link=/dev/ttys000 EXEC:/bin/cat

print("Started:\n")
var masterFile: FileHandle?

var masterFD: Int32 = 0
masterFD = open("/dev/ttys000",O_RDWR|O_NONBLOCK)
grantpt(masterFD)
unlockpt(masterFD)
masterFile = FileHandle.init(fileDescriptor: masterFD)

masterFile!.write("tty\u{0D}".data(using: String.Encoding.utf8)!)
sleep(1)

let data1 = masterFile!.availableData
let strData1 = String(data: data1, encoding: String.Encoding.utf8)!
print("Output: "+strData1)
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda