Sources/SourceParsingFramework/Utilities/ProcessUtilities.swift (36 lines of code) (raw):
//
// Copyright (c) 2018. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// A set of utility functions for running processes.
public protocol ProcessUtilities {
/// The current list of processes without controlling ttys in jobs
/// format.
var currentNonControllingTTYSProcesses: String { get }
/// A convinient method for killing all the processes with given name.
/// This executes `/usr/bin/killall -9` with the given process name.
///
/// - parameter processName: The name of the process to kill.
/// - returns: `true` if succeeded. `false` otherwise.
func killAll(_ processName: String) -> Bool
/// Execute the given process with given arguments and return the
/// standard output as a `String`.
///
/// - parameter path: The path to the process to execute.
/// - parameter process: The name of the process to execute.
/// - parameter arguments: The list of arguments to supply to the
/// process.
/// - returns: The standard output content as a single `String` and
/// the standard error content as a single `String`.
func execute(path: String, processName: String, withArguments arguments: [String]) -> (output: String, error: String)
}
/// A set of utility functions for running processes.
public class ProcessUtilitiesImpl: ProcessUtilities {
/// Initializer.
public init() {}
/// The current list of processes without controlling ttys in jobs
/// format.
public var currentNonControllingTTYSProcesses: String {
// Select processes without controlling ttys in jobs format.
return execute(processName: "ps", withArguments: ["-xj"]).output.lowercased()
}
/// A convinient method for killing all the processes with given name.
/// This executes `/usr/bin/killall -9` with the given process name.
///
/// - parameter processName: The name of the process to kill.
/// - returns: `true` if succeeded. `false` otherwise.
public func killAll(_ processName: String) -> Bool {
let result = execute(path: "/usr/bin/", processName: "killall", withArguments: ["-9", processName])
return result.error.isEmpty
}
/// Execute the given process with given arguments and return the
/// standard output as a `String`.
///
/// - parameter path: The path to the process to execute.
/// - parameter process: The name of the process to execute.
/// - parameter arguments: The list of arguments to supply to the
/// process.
/// - returns: The standard output content as a single `String` and
/// the standard error content as a single `String`.
public func execute(path: String = "/bin", processName: String, withArguments arguments: [String] = []) -> (output: String, error: String) {
let justName = processName.starts(with: "/") ? String(processName.suffix(from: processName.index(processName.startIndex, offsetBy: 1))) : processName
let justPath = path.hasSuffix("/") ? path : path + "/"
let task = Process()
task.launchPath = justPath + justName
task.arguments = arguments
let outputPipe = Pipe()
task.standardOutput = outputPipe
let errorPipe = Pipe()
task.standardError = errorPipe
task.launch()
let output = read(pipe: outputPipe) ?? ""
let error = read(pipe: errorPipe) ?? ""
return (output, error)
}
// MARK: - Private
private func read(pipe: Pipe) -> String? {
let handle = pipe.fileHandleForReading
let data = handle.readDataToEndOfFile()
return String(data: data, encoding: .utf8)
}
}