├── .github ├── CONTRIBUTING.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .swift-version ├── .swiftlint.yml ├── .travis.yml ├── BlueSignals.podspec ├── LICENSE ├── Package.swift ├── Package@swift-4.swift ├── README.md ├── Signals-Framework ├── Info.plist └── Signals_Framework.h ├── Signals.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── Signals-Framework.xcscheme │ ├── Signals.xcscheme │ └── xcschememanagement.plist └── Sources └── Signals ├── SignalWatch.swift ├── SignalWatchHandler.swift └── Signals.swift /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to IBM-Swift 2 | 3 | We welcome contributions, and request you follow these guidelines. 4 | 5 | - [Raising issues](#raising-issues) 6 | - [Contributor License Agreement](#contributor-license-agreement) 7 | - [Coding Standards](#coding-standards) 8 | 9 | 10 | ## Raising issues 11 | 12 | Please raise any bug reports on the issue tracker. Be sure to 13 | search the list to see if your issue has already been raised. 14 | 15 | A good bug report is one that make it easy for us to understand what you were 16 | trying to do and what went wrong. Provide as much context as possible so we can try to recreate the issue. 17 | 18 | ### Contributor License Agreement 19 | 20 | In order for us to accept pull-requests, the contributor must first complete 21 | a Contributor License Agreement (CLA). Please see our [CLA repo](http://github.com/IBM-Swift/CLA) for more information. 22 | 23 | This clarifies the intellectual property license granted with any contribution. It is for your protection as a 24 | Contributor as well as the protection of IBM and its customers; it does not 25 | change your rights to use your own Contributions for any other purpose. 26 | 27 | ### Coding standards 28 | 29 | Please ensure you follow the coding standards used throughout the existing 30 | code base. Some basic rules include: 31 | 32 | - all files must have the Apache license in the header. 33 | - all PRs must have passing builds for all operating systems. 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | ## Checklist: 16 | 17 | 18 | - [ ] I have submitted a [CLA form](https://github.com/IBM-Swift/CLA) 19 | - [ ] If applicable, I have updated the documentation accordingly. 20 | - [ ] If applicable, I have added tests to cover my changes. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | .swiftpm 40 | 41 | # CocoaPods 42 | # 43 | # We recommend against adding the Pods directory to your .gitignore. However 44 | # you should judge for yourself, the pros and cons are mentioned at: 45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 46 | # 47 | # Pods/ 48 | 49 | # Carthage 50 | # 51 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 52 | # Carthage/Checkouts 53 | 54 | Carthage/Build 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/screenshots 65 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | included: 2 | - Sources 3 | - Tests 4 | disabled_rules: 5 | - trailing_newline 6 | - force_cast 7 | - function_body_length 8 | - variable_name 9 | - line_length 10 | - trailing_whitespace 11 | - type_name 12 | - type_body_length 13 | - todo 14 | - file_length 15 | - leading_whitespace 16 | - mark 17 | - function_parameter_count 18 | - cyclomatic_complexity 19 | - colon 20 | - fallthrough 21 | - empty_enum_arguments 22 | - unused_optional_binding 23 | - empty_parentheses_with_trailing_closure 24 | - closure_parameter_position 25 | - vertical_whitespace 26 | - trailing_comma 27 | - xctfail_message 28 | - class_delegate_protocol 29 | - redundant_optional_initialization 30 | custom_rules: 31 | double_space: 32 | include: "*.swift" 33 | name: "Double space" 34 | regex: "([a-z,A-Z] \\s+)" 35 | message: "Double space between keywords" 36 | match_kinds: keyword 37 | severity: warning 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Travis CI build file. 2 | 3 | # whitelist (branches that should be built) 4 | branches: 5 | only: 6 | - master 7 | - /^issue.*$/ 8 | 9 | # the matrix of builds should cover each combination of Swift version 10 | # and platform that is supported. The version of Swift used is specified 11 | # by .swift-version, unless SWIFT_SNAPSHOT is specified. 12 | matrix: 13 | include: 14 | - os: linux 15 | dist: xenial 16 | sudo: required 17 | services: docker 18 | env: DOCKER_IMAGE=docker.kitura.net/kitura/swift-ci-ubuntu16.04:5.1.5 19 | - os: linux 20 | dist: bionic 21 | sudo: required 22 | services: docker 23 | env: DOCKER_IMAGE=docker.kitura.net/kitura/swift-ci-ubuntu18.04:5.4 24 | - os: linux 25 | dist: xenial 26 | sudo: required 27 | services: docker 28 | env: DOCKER_IMAGE=docker.kitura.net/kitura/swift-ci-ubuntu18.04:latest USE_SWIFT_DEVELOPMENT_SNAPSHOT=1 29 | - os: osx 30 | osx_image: xcode11 31 | sudo: required 32 | env: SWIFT_SNAPSHOT=5.1.5 JAZZY_ELIGIBLE=true 33 | - os: osx 34 | osx_image: xcode12.2 35 | sudo: required 36 | - os: osx 37 | osx_image: xcode12.5 38 | sudo: required 39 | env: USE_SWIFT_DEVELOPMENT_SNAPSHOT=1 40 | 41 | before_install: 42 | - git clone https://github.com/Kitura/Package-Builder.git 43 | 44 | script: 45 | - ./Package-Builder/build-package.sh -projectDir $TRAVIS_BUILD_DIR 46 | -------------------------------------------------------------------------------- /BlueSignals.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "BlueSignals" 3 | s.version = "2.0.0" 4 | s.summary = "Swift cross-platform OS signals handler." 5 | s.homepage = "https://github.com/Kitura/BlueSignals" 6 | s.license = { :type => "Apache License, Version 2.0" } 7 | s.author = "IBM" 8 | s.module_name = 'Signals' 9 | 10 | s.requires_arc = true 11 | s.osx.deployment_target = "10.11" 12 | s.ios.deployment_target = "10.0" 13 | s.tvos.deployment_target = "10.0" 14 | s.source = { :git => "https://github.com/Kitura/BlueSignals.git", :tag => s.version } 15 | s.source_files = "Sources/Signals/*.swift" 16 | s.pod_target_xcconfig = { 17 | 'SWIFT_VERSION' => '5.0', 18 | } 19 | end 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation and the Kitura project authors 2016-2020 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | #if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 23 | 24 | let package = Package( 25 | name: "Signals", 26 | platforms: [.macOS(.v10_15), .iOS(.v13), .watchOS(.v6), .tvOS(.v13)], 27 | products: [ 28 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 29 | .library( 30 | name: "Signals", 31 | targets: ["Signals"] 32 | ) 33 | ], 34 | targets: [ 35 | .target( 36 | name: "Signals" 37 | ), 38 | ] 39 | ) 40 | 41 | #else 42 | 43 | fatalError("Unsupported OS") 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Package@swift-4.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | /** 5 | * Copyright IBM Corporation and the Kitura project authors 2016-2020 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | **/ 19 | 20 | import PackageDescription 21 | 22 | #if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 23 | 24 | let package = Package( 25 | name: "Signals", 26 | products: [ 27 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 28 | .library( 29 | name: "Signals", 30 | targets: ["Signals"] 31 | ) 32 | ], 33 | targets: [ 34 | .target( 35 | name: "Signals", 36 | exclude: ["Signals.xcodeproj", "README.md", "Sources/Info.plist", "Sources/Signals.h", "Tests"] 37 | ), 38 | ] 39 | ) 40 | 41 | #else 42 | 43 | fatalError("Unsupported OS") 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | APIDoc 4 | 5 | 6 | Build Status - Master 7 | 8 | macOS 9 | iOS 10 | Linux 11 | Apache 2 12 | 13 | Slack Status 14 | 15 |

16 | 17 | # Signals 18 | 19 | Generic Cross Platform Signal Handler. 20 | 21 | ## Prerequisites 22 | 23 | ### Swift 24 | 25 | * Swift Open Source `swift-5.1-RELEASE` toolchain (**Minimum REQUIRED for latest release**) 26 | * Swift Open Source `swift-5.4-RELEASE` toolchain (**Recommended**) 27 | * Swift toolchain included in *Xcode Version 11.0 or higher*. 28 | 29 | BlueSignals version 2.0 and above supports Swift 5.1+. See older versions of BlueSSLService for older versions of Swift. 30 | 31 | ### macOS 32 | 33 | * macOS 10.14.6 (*Mojave*) or higher. 34 | * Xcode Version 11.0 or higher using one of the above toolchains. 35 | * Xcode Version 12.5 or higher using the included toolchain (*Recommended*). 36 | 37 | ### iOS 38 | 39 | * iOS 10.0 or higher 40 | * Xcode Version 11.0 or higher using one of the above toolchains. 41 | * Xcode Version 12.5 or higher using the included toolchain (*Recommended*). 42 | 43 | ### Linux 44 | 45 | * Ubuntu 16.04 (or 16.10 but only tested on 16.04) and 18.04. 46 | * One of the Swift Open Source toolchain listed above. 47 | 48 | 49 | ## Build 50 | 51 | To build Signals from the command line: 52 | 53 | ``` 54 | % cd 55 | % swift build 56 | ``` 57 | 58 | ## Using Signals 59 | 60 | ### Including in your project 61 | 62 | #### Swift Package Manager 63 | 64 | To include BlueSignals into a Swift Package Manager package, add it to the `dependencies` attribute defined in your `Package.swift` file. You can select the version using the `majorVersion` and `minor` parameters. For example: 65 | ``` 66 | dependencies: [ 67 | .Package(url: "https://github.com/Kitura/BlueSignals.git", majorVersion: , minor: ) 68 | ] 69 | ``` 70 | 71 | #### Carthage 72 | To include BlueSignals in a project using Carthage, add a line to your `Cartfile` with the GitHub organization and project names and version. For example: 73 | ``` 74 | github "Kitura/BlueSignals" ~> . 75 | ``` 76 | 77 | #### CocoaPods 78 | To include BlueSignals in a project using CocoaPods, you just add `BlueSignals` to your `Podfile`, for example: 79 | ``` 80 | platform :ios, '10.0' 81 | 82 | target 'MyApp' do 83 | use_frameworks! 84 | pod 'BlueSignals' 85 | end 86 | ``` 87 | 88 | ### Before starting 89 | 90 | The first thing you need to do is import the Signals framework. This is done by the following: 91 | ``` 92 | import Signals 93 | ``` 94 | 95 | ### Provided APIs 96 | 97 | Signals provides four (4) class level APIs. Three (3) are used for trapping and handling operating system signals. The other function allows for the raising of a signal. 98 | 99 | #### Watching a signal 100 | 101 | ``SignalWatch`` provides an interface that allows a trapped signal to notify multiple "signal watchers". In this way, signal traps can be shared across libraries in the same application. In most cases this can be used as a direct replacement for `trap()`. 102 | 103 | When a signal is added via ``SignalWatch``, it will install it's own handler on that signal via `trap()`. As such, it is important to not use `trap()` directly when using ``SignalWatch``. If all watchers of a signal are removed, ``SignalWatch`` will intelligently restore the handler that was installed before ``SignalWatch``. 104 | 105 | ```swift 106 | import Signals 107 | 108 | ... 109 | let server: SomeServer = ... 110 | 111 | 112 | SignalWatch.shared.on(signal: .int) { _ in 113 | server.shutdownServer() 114 | } 115 | 116 | server.run() 117 | 118 | ``` 119 | 120 | 121 | #### Trapping a signal 122 | - `trap(signal signal: Signal, action: SigActionHandler)` - This basic API allows you to set and specific handler for a specific signal. 123 | 124 | The example below shows how to add a trap handler to a server in order to perform and orderly shutdown in the event that user press `^C` which sends the process a `SIGINT`. 125 | ```swift 126 | import Signals 127 | 128 | ... 129 | 130 | let server: SomeServer = ... 131 | 132 | Signals.trap(signal: .int) { signal in 133 | 134 | server.shutdownServer() 135 | } 136 | 137 | server.run() 138 | ``` 139 | Additionally, convenience API's that build on the basic API specified above are provided that will allow for trapping multiple signals, each to a separate handler or to a single handler. 140 | - `trap(signals signals: [(signal: Signal, action: SigActionHandler)])` - This lets you trap multiple signals to separate handlers in a single function call. 141 | - `trap(signals signals: [Signal], action: SigActionHandler)` - This API lets you trap multiple signals to a common handler. 142 | 143 | #### Raising a signal 144 | - `raise(signal signal: Signal)` - This API is used to send an operating system signal to your application. 145 | 146 | This example illustrates how to use Signals to raise a signal with the OS, in this case `SIGABRT`. 147 | ```swift 148 | import Signals 149 | 150 | ... 151 | 152 | Signals.raise(signal: .abrt) 153 | ``` 154 | 155 | #### Ignoring a signal 156 | - `func ignore(signal: Signal)` - This API is used to ignore an operating system signal. 157 | 158 | This example illustrates how to use Signals to ignore a signal with the OS, in this case `SIGPIPE`. 159 | ```swift 160 | import Signals 161 | 162 | ... 163 | 164 | Signals.ignore(signal: .pipe) 165 | ``` 166 | 167 | #### Restoring a signals default handler 168 | - `func restore(signal: Signal)` - This API is used to restore an operating system signals default handler. 169 | 170 | This example illustrates how to use Signals to restore a signals default handler, in this case `SIGPIPE`. 171 | ```swift 172 | import Signals 173 | 174 | ... 175 | 176 | Signals.restore(signal: .pipe) 177 | ``` 178 | 179 | #### Adding a USER-DEFINED signal 180 | 181 | This example shows how to add a user defined signal, add a trap handler for it and then raise the signal. 182 | ```swift 183 | import Signals 184 | 185 | let mySignal = Signals.Signal.user(20) 186 | 187 | Signals.trap(signal: mySignal) { signal in 188 | 189 | print("Received signal \(signal)") 190 | } 191 | 192 | Signals.raise(signal: mySignal) 193 | 194 | ``` 195 | The output of the above snippet is: 196 | ``` 197 | Received signal 20 198 | ``` 199 | 200 | ## Community 201 | 202 | We love to talk server-side Swift and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team! 203 | 204 | ## License 205 | 206 | This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/Kitura/BlueSignals/blob/master/LICENSE). 207 | -------------------------------------------------------------------------------- /Signals-Framework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Signals-Framework/Signals_Framework.h: -------------------------------------------------------------------------------- 1 | // 2 | // Signals_Framework.h 3 | // Signals-Framework 4 | // 5 | // Created by Bill Abt on 10/19/18. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for Signals_Framework. 11 | FOUNDATION_EXPORT double Signals_FrameworkVersionNumber; 12 | 13 | //! Project version string for Signals_Framework. 14 | FOUNDATION_EXPORT const unsigned char Signals_FrameworkVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /Signals.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8C6EF7FA217A1D6D00D74FA1 /* Signals_Framework.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C6EF7F8217A1D6D00D74FA1 /* Signals_Framework.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | __src_cc_ref_Sources/Signals.swift /* Signals.swift in Sources */ = {isa = PBXBuildFile; fileRef = __PBXFileRef_Sources/Signals.swift /* Signals.swift */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | 8C2D91E1215EEEA1000E2D9C /* BlueSignals.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = BlueSignals.podspec; sourceTree = ""; }; 16 | 8C30FB321CAAE0E000113F9D /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 17 | 8C30FB331CAAE0E000113F9D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 18 | 8C6EF7F6217A1D6D00D74FA1 /* Signals_Framework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Signals_Framework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 19 | 8C6EF7F8217A1D6D00D74FA1 /* Signals_Framework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Signals_Framework.h; sourceTree = ""; }; 20 | 8C6EF7F9217A1D6D00D74FA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 21 | __PBXFileRef_Package.swift /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 22 | __PBXFileRef_Sources/Signals.swift /* Signals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Signals.swift; path = Sources/Signals/Signals.swift; sourceTree = ""; }; 23 | "_____Product_Signals" /* libSignals.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libSignals.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | /* End PBXFileReference section */ 25 | 26 | /* Begin PBXFrameworksBuildPhase section */ 27 | 8C6EF7F3217A1D6D00D74FA1 /* Frameworks */ = { 28 | isa = PBXFrameworksBuildPhase; 29 | buildActionMask = 2147483647; 30 | files = ( 31 | ); 32 | runOnlyForDeploymentPostprocessing = 0; 33 | }; 34 | "___LinkPhase_Signals" /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 0; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 8C6EF7F7217A1D6D00D74FA1 /* Signals-Framework */ = { 45 | isa = PBXGroup; 46 | children = ( 47 | 8C6EF7F8217A1D6D00D74FA1 /* Signals_Framework.h */, 48 | 8C6EF7F9217A1D6D00D74FA1 /* Info.plist */, 49 | ); 50 | path = "Signals-Framework"; 51 | sourceTree = ""; 52 | }; 53 | "___RootGroup_" = { 54 | isa = PBXGroup; 55 | children = ( 56 | 8C2D91E1215EEEA1000E2D9C /* BlueSignals.podspec */, 57 | 8C30FB321CAAE0E000113F9D /* LICENSE */, 58 | __PBXFileRef_Package.swift /* Package.swift */, 59 | 8C30FB331CAAE0E000113F9D /* README.md */, 60 | "_____Sources_" /* Sources */, 61 | "_______Tests_" /* Tests */, 62 | 8C6EF7F7217A1D6D00D74FA1 /* Signals-Framework */, 63 | "____Products_" /* Products */, 64 | ); 65 | sourceTree = ""; 66 | }; 67 | "____Products_" /* Products */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | "_____Product_Signals" /* libSignals.dylib */, 71 | 8C6EF7F6217A1D6D00D74FA1 /* Signals_Framework.framework */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | "_____Sources_" /* Sources */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | __PBXFileRef_Sources/Signals.swift /* Signals.swift */, 80 | ); 81 | name = Sources; 82 | sourceTree = ""; 83 | }; 84 | "_______Tests_" /* Tests */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | ); 88 | name = Tests; 89 | sourceTree = ""; 90 | }; 91 | /* End PBXGroup section */ 92 | 93 | /* Begin PBXHeadersBuildPhase section */ 94 | 8C6EF7F1217A1D6D00D74FA1 /* Headers */ = { 95 | isa = PBXHeadersBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | 8C6EF7FA217A1D6D00D74FA1 /* Signals_Framework.h in Headers */, 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | /* End PBXHeadersBuildPhase section */ 103 | 104 | /* Begin PBXNativeTarget section */ 105 | 8C6EF7F5217A1D6D00D74FA1 /* Signals-Framework */ = { 106 | isa = PBXNativeTarget; 107 | buildConfigurationList = 8C6EF7FD217A1D6D00D74FA1 /* Build configuration list for PBXNativeTarget "Signals-Framework" */; 108 | buildPhases = ( 109 | 8C6EF7F1217A1D6D00D74FA1 /* Headers */, 110 | 8C6EF7F2217A1D6D00D74FA1 /* Sources */, 111 | 8C6EF7F3217A1D6D00D74FA1 /* Frameworks */, 112 | 8C6EF7F4217A1D6D00D74FA1 /* Resources */, 113 | ); 114 | buildRules = ( 115 | ); 116 | dependencies = ( 117 | ); 118 | name = "Signals-Framework"; 119 | productName = "Signals-Framework"; 120 | productReference = 8C6EF7F6217A1D6D00D74FA1 /* Signals_Framework.framework */; 121 | productType = "com.apple.product-type.framework"; 122 | }; 123 | "______Target_Signals" /* Signals */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = "_______Confs_Signals" /* Build configuration list for PBXNativeTarget "Signals" */; 126 | buildPhases = ( 127 | CompilePhase_Signals /* Sources */, 128 | "___LinkPhase_Signals" /* Frameworks */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = Signals; 135 | productName = Signals; 136 | productReference = "_____Product_Signals" /* libSignals.dylib */; 137 | productType = "com.apple.product-type.library.dynamic"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | __RootObject_ /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastUpgradeCheck = 1020; 146 | TargetAttributes = { 147 | 8C6EF7F5217A1D6D00D74FA1 = { 148 | CreatedOnToolsVersion = 10.0; 149 | ProvisioningStyle = Automatic; 150 | }; 151 | "______Target_Signals" = { 152 | LastSwiftMigration = 1000; 153 | }; 154 | }; 155 | }; 156 | buildConfigurationList = "___RootConfs_" /* Build configuration list for PBXProject "Signals" */; 157 | compatibilityVersion = "Xcode 3.2"; 158 | developmentRegion = en; 159 | hasScannedForEncodings = 0; 160 | knownRegions = ( 161 | en, 162 | Base, 163 | ); 164 | mainGroup = "___RootGroup_"; 165 | productRefGroup = "____Products_" /* Products */; 166 | projectDirPath = ""; 167 | projectRoot = ""; 168 | targets = ( 169 | "______Target_Signals" /* Signals */, 170 | 8C6EF7F5217A1D6D00D74FA1 /* Signals-Framework */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | 8C6EF7F4217A1D6D00D74FA1 /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXResourcesBuildPhase section */ 184 | 185 | /* Begin PBXSourcesBuildPhase section */ 186 | 8C6EF7F2217A1D6D00D74FA1 /* Sources */ = { 187 | isa = PBXSourcesBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | CompilePhase_Signals /* Sources */ = { 194 | isa = PBXSourcesBuildPhase; 195 | buildActionMask = 0; 196 | files = ( 197 | __src_cc_ref_Sources/Signals.swift /* Signals.swift in Sources */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | /* End PBXSourcesBuildPhase section */ 202 | 203 | /* Begin XCBuildConfiguration section */ 204 | 8C6EF7FB217A1D6D00D74FA1 /* Debug */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | CLANG_ANALYZER_NONNULL = YES; 209 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 210 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 211 | CLANG_CXX_LIBRARY = "libc++"; 212 | CLANG_ENABLE_MODULES = YES; 213 | CLANG_ENABLE_OBJC_ARC = YES; 214 | CLANG_ENABLE_OBJC_WEAK = YES; 215 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 216 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 217 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 218 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 219 | CODE_SIGN_IDENTITY = "-"; 220 | CODE_SIGN_STYLE = Automatic; 221 | COMBINE_HIDPI_IMAGES = YES; 222 | COPY_PHASE_STRIP = NO; 223 | CURRENT_PROJECT_VERSION = 1; 224 | DEBUG_INFORMATION_FORMAT = dwarf; 225 | DEFINES_MODULE = YES; 226 | DYLIB_COMPATIBILITY_VERSION = 1; 227 | DYLIB_CURRENT_VERSION = 1; 228 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 229 | FRAMEWORK_VERSION = A; 230 | GCC_C_LANGUAGE_STANDARD = gnu11; 231 | GCC_DYNAMIC_NO_PIC = NO; 232 | GCC_OPTIMIZATION_LEVEL = 0; 233 | GCC_PREPROCESSOR_DEFINITIONS = ( 234 | "DEBUG=1", 235 | "$(inherited)", 236 | ); 237 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 238 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 239 | INFOPLIST_FILE = "Signals-Framework/Info.plist"; 240 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 241 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 242 | MACOSX_DEPLOYMENT_TARGET = 10.14; 243 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 244 | MTL_FAST_MATH = YES; 245 | PRODUCT_BUNDLE_IDENTIFIER = "com.ibm.oss.Signals-Framework"; 246 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 247 | SDKROOT = macosx; 248 | SKIP_INSTALL = YES; 249 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 250 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 251 | SWIFT_VERSION = 4.2; 252 | VERSIONING_SYSTEM = "apple-generic"; 253 | VERSION_INFO_PREFIX = ""; 254 | }; 255 | name = Debug; 256 | }; 257 | 8C6EF7FC217A1D6D00D74FA1 /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_ENABLE_OBJC_WEAK = YES; 268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 269 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 271 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 272 | CODE_SIGN_IDENTITY = "-"; 273 | CODE_SIGN_STYLE = Automatic; 274 | COMBINE_HIDPI_IMAGES = YES; 275 | COPY_PHASE_STRIP = NO; 276 | CURRENT_PROJECT_VERSION = 1; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | DEFINES_MODULE = YES; 279 | DYLIB_COMPATIBILITY_VERSION = 1; 280 | DYLIB_CURRENT_VERSION = 1; 281 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 282 | ENABLE_NS_ASSERTIONS = NO; 283 | FRAMEWORK_VERSION = A; 284 | GCC_C_LANGUAGE_STANDARD = gnu11; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 286 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 287 | INFOPLIST_FILE = "Signals-Framework/Info.plist"; 288 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 289 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 290 | MACOSX_DEPLOYMENT_TARGET = 10.14; 291 | MTL_ENABLE_DEBUG_INFO = NO; 292 | MTL_FAST_MATH = YES; 293 | PRODUCT_BUNDLE_IDENTIFIER = "com.ibm.oss.Signals-Framework"; 294 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 295 | SDKROOT = macosx; 296 | SKIP_INSTALL = YES; 297 | SWIFT_VERSION = 4.2; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | VERSION_INFO_PREFIX = ""; 300 | }; 301 | name = Release; 302 | }; 303 | _ReleaseConf_Signals /* Release */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ENABLE_OBJC_WEAK = YES; 308 | COMBINE_HIDPI_IMAGES = YES; 309 | DYLIB_INSTALL_NAME_BASE = "$(CONFIGURATION_BUILD_DIR)"; 310 | ENABLE_TESTABILITY = YES; 311 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 312 | MACOSX_DEPLOYMENT_TARGET = 10.10; 313 | OTHER_SWIFT_FLAGS = "-DXcode"; 314 | PRODUCT_MODULE_NAME = Signals; 315 | PRODUCT_NAME = "lib$(TARGET_NAME)"; 316 | SWIFT_VERSION = 5.0; 317 | }; 318 | name = Release; 319 | }; 320 | "___DebugConf_Signals" /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | CLANG_ENABLE_OBJC_WEAK = YES; 325 | COMBINE_HIDPI_IMAGES = YES; 326 | DYLIB_INSTALL_NAME_BASE = "$(CONFIGURATION_BUILD_DIR)"; 327 | ENABLE_TESTABILITY = YES; 328 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 329 | MACOSX_DEPLOYMENT_TARGET = 10.10; 330 | OTHER_SWIFT_FLAGS = "-DXcode"; 331 | PRODUCT_MODULE_NAME = Signals; 332 | PRODUCT_NAME = "lib$(TARGET_NAME)"; 333 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 334 | SWIFT_VERSION = 5.0; 335 | }; 336 | name = Debug; 337 | }; 338 | "_____Release_" /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 343 | CLANG_WARN_BOOL_CONVERSION = YES; 344 | CLANG_WARN_COMMA = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 347 | CLANG_WARN_EMPTY_BODY = YES; 348 | CLANG_WARN_ENUM_CONVERSION = YES; 349 | CLANG_WARN_INFINITE_RECURSION = YES; 350 | CLANG_WARN_INT_CONVERSION = YES; 351 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 352 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 354 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 355 | CLANG_WARN_STRICT_PROTOTYPES = YES; 356 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 357 | CLANG_WARN_UNREACHABLE_CODE = YES; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | ENABLE_STRICT_OBJC_MSGSEND = YES; 360 | GCC_NO_COMMON_BLOCKS = YES; 361 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 362 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 363 | GCC_WARN_UNDECLARED_SELECTOR = YES; 364 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 365 | GCC_WARN_UNUSED_FUNCTION = YES; 366 | GCC_WARN_UNUSED_VARIABLE = YES; 367 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 368 | SWIFT_VERSION = 3.0; 369 | }; 370 | name = Release; 371 | }; 372 | "_______Debug_" /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 376 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 377 | CLANG_WARN_BOOL_CONVERSION = YES; 378 | CLANG_WARN_COMMA = YES; 379 | CLANG_WARN_CONSTANT_CONVERSION = YES; 380 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 381 | CLANG_WARN_EMPTY_BODY = YES; 382 | CLANG_WARN_ENUM_CONVERSION = YES; 383 | CLANG_WARN_INFINITE_RECURSION = YES; 384 | CLANG_WARN_INT_CONVERSION = YES; 385 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 387 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 389 | CLANG_WARN_STRICT_PROTOTYPES = YES; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_UNREACHABLE_CODE = YES; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | ENABLE_STRICT_OBJC_MSGSEND = YES; 394 | ENABLE_TESTABILITY = YES; 395 | GCC_NO_COMMON_BLOCKS = YES; 396 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 397 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 398 | GCC_WARN_UNDECLARED_SELECTOR = YES; 399 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 400 | GCC_WARN_UNUSED_FUNCTION = YES; 401 | GCC_WARN_UNUSED_VARIABLE = YES; 402 | ONLY_ACTIVE_ARCH = YES; 403 | SWIFT_VERSION = 3.0; 404 | }; 405 | name = Debug; 406 | }; 407 | /* End XCBuildConfiguration section */ 408 | 409 | /* Begin XCConfigurationList section */ 410 | 8C6EF7FD217A1D6D00D74FA1 /* Build configuration list for PBXNativeTarget "Signals-Framework" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | 8C6EF7FB217A1D6D00D74FA1 /* Debug */, 414 | 8C6EF7FC217A1D6D00D74FA1 /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Debug; 418 | }; 419 | "___RootConfs_" /* Build configuration list for PBXProject "Signals" */ = { 420 | isa = XCConfigurationList; 421 | buildConfigurations = ( 422 | "_______Debug_" /* Debug */, 423 | "_____Release_" /* Release */, 424 | ); 425 | defaultConfigurationIsVisible = 0; 426 | defaultConfigurationName = Debug; 427 | }; 428 | "_______Confs_Signals" /* Build configuration list for PBXNativeTarget "Signals" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | "___DebugConf_Signals" /* Debug */, 432 | _ReleaseConf_Signals /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Debug; 436 | }; 437 | /* End XCConfigurationList section */ 438 | }; 439 | rootObject = __RootObject_ /* Project object */; 440 | } 441 | -------------------------------------------------------------------------------- /Signals.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Signals.xcodeproj/xcshareddata/xcschemes/Signals-Framework.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Signals.xcodeproj/xcshareddata/xcschemes/Signals.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Signals.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | Signals.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Sources/Signals/SignalWatch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignalWatch.swift 3 | // 4 | // 5 | // Created by Danny Sung on 05/14/2022. 6 | // 7 | 8 | import Foundation 9 | 10 | /// ``SignalWatch`` provides an interface around ``Signals`` that allows for multiple handlers to be registered for any given signal. 11 | public class SignalWatch { 12 | public static let shared = SignalWatch() 13 | 14 | typealias SignalNumber = Int32 15 | private var signalsWatched: [SignalNumber:[SignalWatchHandler]] 16 | private let queue: DispatchQueue 17 | private var currentSignalId = 0 18 | 19 | init() { 20 | self.signalsWatched = [:] 21 | self.queue = DispatchQueue(label: "SignalWatch Internal") 22 | } 23 | 24 | /// Add a handler for a signal 25 | /// - Parameters: 26 | /// - signal: Signal to watch 27 | /// - handler: Closure to call when the signal is recieved 28 | /// - Returns: A handle that can be used to remove the watcher 29 | @discardableResult 30 | public func on(signal: Signals.Signal, perform handler: @escaping (SignalWatchHandler)->Void) -> SignalWatchHandler { 31 | 32 | let signalWatchHandler = SignalWatchHandler(id: self.getNextSignalHandlerId(), signal: signal, userInfo: Void.self, handler: .noUserInfo(handler)) 33 | 34 | return self.addHandler(signal: signal, signalWatchHandler: signalWatchHandler) 35 | } 36 | 37 | /// Add a handler for a signal 38 | /// - Parameters: 39 | /// - signal: Signal to watch 40 | /// - handler: Closure to call when the signal is recieved 41 | /// - userInfo: Additional data that will be passed to the handler 42 | /// - Returns: A handle that can be used to remove the watcher 43 | @discardableResult 44 | public func on(signal: Signals.Signal, perform handler: @escaping (SignalWatchHandler, Any)->Void, userInfo: Any) -> SignalWatchHandler { 45 | 46 | let signalWatchHandler = SignalWatchHandler(id: self.getNextSignalHandlerId(), signal: signal, userInfo: userInfo, handler: .userInfo(handler)) 47 | 48 | return self.addHandler(signal: signal, signalWatchHandler: signalWatchHandler) 49 | } 50 | 51 | /// Remove the a signal handler 52 | /// - Parameter handler: ``SignalWatchHandler`` to remove 53 | public func remove(handler: SignalWatchHandler) { 54 | self.queue.sync { 55 | guard var handlerList = self.signalsWatched[handler.signal.rawValue] else { return } 56 | 57 | handlerList = handlerList.filter({ $0.id != handler.id }) 58 | if handlerList.isEmpty { 59 | Signals.restore(signal: handler.signal) 60 | self.signalsWatched.removeValue(forKey: handler.signal.rawValue) 61 | } else { 62 | self.signalsWatched[handler.signal.rawValue] = handlerList 63 | } 64 | } 65 | } 66 | 67 | // MARK: - Private methods 68 | 69 | private func addHandler(signal: Signals.Signal, signalWatchHandler: SignalWatchHandler) -> SignalWatchHandler { 70 | return self.queue.sync { 71 | 72 | var handlerList = self.signalsWatched[signal.rawValue] ?? [] 73 | handlerList.append(signalWatchHandler) 74 | 75 | self.signalsWatched[signal.rawValue] = handlerList 76 | 77 | Signals.trap(signal: signal) { signalValue in 78 | let signal = Signals.Signal(rawValue: signalValue) 79 | SignalWatch.shared.didRecieve(signal: signal) 80 | } 81 | 82 | return signalWatchHandler 83 | } 84 | 85 | } 86 | 87 | 88 | private func didRecieve(signal: Signals.Signal) { 89 | return self.queue.async { 90 | guard let signalWatchHandlerList = self.signalsWatched[signal.rawValue] else { return } 91 | 92 | for signalHandler in signalWatchHandlerList { 93 | signalHandler.callHandler() 94 | } 95 | } 96 | } 97 | 98 | private func getNextSignalHandlerId() -> Int { 99 | let currentSignalId = self.currentSignalId 100 | self.currentSignalId += 1 101 | return currentSignalId 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Sources/Signals/SignalWatchHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignalWatchHandler.swift 3 | // 4 | // 5 | // Created by Danny Sung on 05/14/2022. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct SignalWatchHandler: Identifiable { 11 | public let id: Int 12 | public let signal: Signals.Signal 13 | let userInfo: Any 14 | 15 | enum HandlerType { 16 | case noUserInfo((SignalWatchHandler) -> Void) 17 | case userInfo((SignalWatchHandler, Any) -> Void) 18 | } 19 | let handler: HandlerType 20 | 21 | internal func callHandler() { 22 | switch self.handler { 23 | case .noUserInfo(let handler): 24 | handler(self) 25 | case .userInfo(let handler): 26 | handler(self, self.userInfo) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Signals/Signals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Signals.swift 3 | // BlueSignals 4 | // 5 | // Created by Bill Abt on 3/29/16. 6 | // Copyright © 2016 IBM. All rights reserved. 7 | // 8 | // Licensed under the Apache License, Version 2.0 (the "License"); 9 | // you may not use this file except in compliance with the License. 10 | // You may obtain a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, software 15 | // distributed under the License is distributed on an "AS IS" BASIS, 16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | // See the License for the specific language governing permissions and 18 | // limitations under the License. 19 | // 20 | 21 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 22 | import Darwin 23 | #elseif os(Linux) 24 | import Glibc 25 | #endif 26 | 27 | import Foundation 28 | 29 | // MARK: Signals 30 | 31 | /// ``Signals`` provides a type-safe Swift interface around POSIX Signals via `sigaction`. As such, only one handler may be active per Signal. 32 | public class Signals { 33 | 34 | // MARK: Enums 35 | 36 | /// 37 | /// Common OS Signals 38 | /// 39 | public enum Signal { 40 | case hup 41 | case int 42 | case quit 43 | case abrt 44 | case kill 45 | case alrm 46 | case term 47 | case pipe 48 | case cont 49 | case chld 50 | case io 51 | case prof 52 | case winch 53 | case info 54 | case user(Int) 55 | 56 | @available(*, renamed: "rawValue") 57 | public var valueOf: Int32 { 58 | return self.rawValue 59 | } 60 | 61 | /// 62 | /// Obtain the OS dependent value of a Signal 63 | /// 64 | public var rawValue: Int32 { 65 | 66 | switch self { 67 | case .hup: 68 | return Int32(SIGHUP) 69 | case .int: 70 | return Int32(SIGINT) 71 | case .quit: 72 | return Int32(SIGQUIT) 73 | case .abrt: 74 | return Int32(SIGABRT) 75 | case .kill: 76 | return Int32(SIGKILL) 77 | case .alrm: 78 | return Int32(SIGALRM) 79 | case .term: 80 | return Int32(SIGTERM) 81 | case .pipe: 82 | return Int32(SIGPIPE) 83 | case .cont: 84 | return Int32(SIGCONT) 85 | case .chld: 86 | return Int32(SIGCHLD) 87 | case .io: 88 | return Int32(SIGIO) 89 | case .prof: 90 | return Int32(SIGPROF) 91 | case .winch: 92 | return Int32(SIGWINCH) 93 | case .info: 94 | return Int32(SIGINFO) 95 | case .user(let sig): 96 | return Int32(sig) 97 | 98 | } 99 | } 100 | 101 | init(rawValue: Int32) { 102 | self.init(rawValue: Int(rawValue)) 103 | } 104 | 105 | init(rawValue: Int) { 106 | switch rawValue { 107 | case Int(SIGHUP): self = .hup 108 | case Int(SIGINT): self = .int 109 | case Int(SIGQUIT): self = .quit 110 | case Int(SIGABRT): self = .abrt 111 | case Int(SIGKILL): self = .kill 112 | case Int(SIGALRM): self = .alrm 113 | case Int(SIGTERM): self = .term 114 | case Int(SIGPIPE): self = .pipe 115 | case Int(SIGCONT): self = .cont 116 | case Int(SIGCHLD): self = .chld 117 | case Int(SIGIO): self = .io 118 | case Int(SIGPROF): self = .prof 119 | case Int(SIGWINCH): self = .winch 120 | case Int(SIGINFO): self = .info 121 | 122 | default: 123 | self = .user(rawValue) 124 | } 125 | } 126 | } 127 | 128 | 129 | // MARK: Typealiases 130 | 131 | /// 132 | /// Action handler signature. 133 | /// 134 | public typealias SigActionHandler = @convention(c)(Int32) -> Void 135 | 136 | 137 | // MARK: Class Methods 138 | 139 | /// 140 | /// Trap an operating system signal. 141 | /// 142 | /// - Parameters: 143 | /// - signal: The signal to catch. 144 | /// - action: The action handler. 145 | /// 146 | public class func trap(signal: Signal, action: @escaping SigActionHandler) { 147 | 148 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 149 | 150 | var signalAction = sigaction(__sigaction_u: unsafeBitCast(action, to: __sigaction_u.self), sa_mask: 0, sa_flags: 0) 151 | 152 | _ = withUnsafePointer(to: &signalAction) { actionPointer in 153 | 154 | sigaction(signal.rawValue, actionPointer, nil) 155 | } 156 | 157 | #elseif os(Linux) 158 | 159 | var sigAction = sigaction() 160 | 161 | sigAction.__sigaction_handler = unsafeBitCast(action, to: sigaction.__Unnamed_union___sigaction_handler.self) 162 | 163 | _ = sigaction(signal.rawValue, &sigAction, nil) 164 | 165 | #endif 166 | } 167 | 168 | /// 169 | /// Trap multiple signals to individual handlers 170 | /// 171 | /// - Parameter signals: An array of tuples each containing a signal and signal handler. 172 | /// 173 | public class func trap(signals: [(signal: Signal, action: SigActionHandler)]) { 174 | 175 | for sighandler in signals { 176 | 177 | Signals.trap(signal: sighandler.signal, action: sighandler.action) 178 | } 179 | } 180 | 181 | /// 182 | /// Trap multiple signals to a single handler 183 | /// 184 | /// - Parameters: 185 | /// - signals: An array of signals to catch. 186 | /// - action: The action handler that will handle these signals. 187 | /// 188 | public class func trap(signals: [Signal], action: @escaping SigActionHandler) { 189 | 190 | for signal in signals { 191 | 192 | Signals.trap(signal: signal, action: action) 193 | } 194 | } 195 | 196 | /// 197 | /// Raise an operating system signal 198 | /// 199 | /// - Parameter signal: The signal to raise. 200 | /// 201 | public class func raise(signal: Signal) { 202 | 203 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 204 | 205 | _ = Darwin.raise(signal.rawValue) 206 | 207 | #elseif os(Linux) 208 | 209 | _ = Glibc.raise(signal.rawValue) 210 | 211 | #endif 212 | } 213 | 214 | /// 215 | /// Ignore a signal 216 | /// 217 | /// - Parameter signal: The signal to ignore. 218 | /// 219 | public class func ignore(signal: Signal) { 220 | 221 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 222 | 223 | _ = Darwin.signal(signal.rawValue, SIG_IGN) 224 | 225 | #elseif os(Linux) 226 | 227 | _ = Glibc.signal(signal.rawValue, SIG_IGN) 228 | 229 | #endif 230 | } 231 | 232 | /// 233 | /// Restore default signal handling 234 | /// 235 | /// - Parameter signal: The signal to restore. 236 | /// 237 | public class func restore(signal: Signal) { 238 | 239 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 240 | 241 | _ = Darwin.signal(signal.rawValue, SIG_DFL) 242 | 243 | #elseif os(Linux) 244 | 245 | _ = Glibc.signal(signal.rawValue, SIG_DFL) 246 | 247 | #endif 248 | } 249 | 250 | } 251 | --------------------------------------------------------------------------------