• Empleos
  • Sobre nosotros
  • profesionales
    • Inicio
    • Empleos
    • Cursos y retos
  • empresas
    • Inicio
    • Publicar vacante
    • Nuestro proceso
    • Precios
    • Evaluaciones
    • Nómina
    • Blog
    • Comercial
    • Calculadora de salario

0

207
Vistas
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?

about 3 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

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()
about 3 years ago · Santiago Trujillo Denunciar

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)
about 3 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Nuestro proceso Comercial
Legal
Términos y condiciones Política de privacidad
© 2025 PeakU Inc. All Rights Reserved.

Andres GPT

Recomiéndame algunas ofertas
Necesito ayuda