├── LICENSE
├── README.md
├── Ubuntu.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcuserdata
│ └── kanav.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
└── Ubuntu
├── AddDiskSheetController.swift
├── AppDelegate.swift
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ ├── Contents.json
│ ├── UbuntuCoF-1.png
│ └── UbuntuCoF.png
├── Contents.json
├── UbuntuCoF.imageset
│ ├── Contents.json
│ └── UbuntuCoF.png
└── ubuntu-white.imageset
│ ├── Contents.json
│ └── ubuntu-white.png
├── Authorization.m
├── Base.lproj
└── Main.storyboard
├── DiskManagerViewController.swift
├── Info.plist
├── MainWindowController.swift
├── ProgressSheetController.swift
├── Ubuntu-Bridging-Header.h
├── Ubuntu.entitlements
├── ViewController.swift
├── boot
├── initrd.img-4.4.0-131-generic
└── vmlinuz-4.4.0-131-generic
├── disk
├── DiskTools.swift
├── mkfs
└── qemu-img
├── hypervisor
├── hyperkit
└── xhyve
└── updates
└── 00-automount.sh
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Kanav Gupta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DSL
2 |
3 | Darwin subsytem for Linux. Not actually a subsystem like WSL, but runs an Ubuntu 16 VM on a hypervisor ([xhyve](https://github.com/machyve/xhyve) or [hyperkit](https://github.com/moby/hyperkit)) and adds a nice frontend to manage the VM.
4 | Goal is to make running Ubuntu or any flavour of linux as seamless as possible using existing technologies.
5 |
6 |
7 |
8 | ## Installation
9 |
10 | Head over to [releases page](https://github.com/sdslabs/DSL/releases) to download the latest version of Ubuntu.dmg and move the App to Applications folder. On the first run, it will make you download a base image, which is 3 Gb in size. You can later make and attach other disks to the VM. Login via SSH using username `default` and password `password`.
11 |
12 | ## Licensing
13 |
14 | We do not own the Ubuntu image - the hosted base image is derived from Ubuntu Server 16.04.5 and Canonical Ltd. is the rightful owner of Ubuntu. Please open an issue if this needs a proper licensing.
15 |
--------------------------------------------------------------------------------
/Ubuntu.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6F03EEAE25AE1706005ABD99 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 6F03EEAD25AE1706005ABD99 /* SwiftyJSON */; };
11 | 6F03EEB225AE26B5005ABD99 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F03EEB125AE26B5005ABD99 /* MainWindowController.swift */; };
12 | 6F155D2C25B064820050A2E8 /* hyperkit in Resources */ = {isa = PBXBuildFile; fileRef = 6F155D2B25B064820050A2E8 /* hyperkit */; };
13 | 6F155D3225B0742E0050A2E8 /* DiskManagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F155D3125B0742E0050A2E8 /* DiskManagerViewController.swift */; };
14 | 6F155D3625B075900050A2E8 /* AddDiskSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F155D3525B075900050A2E8 /* AddDiskSheetController.swift */; };
15 | 6F2EE07525A99FB300B4E169 /* Authorization.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F2EE07425A99FB300B4E169 /* Authorization.m */; };
16 | 6F2F8C6425ADE91B004C231D /* ProgressSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2F8C6325ADE91B004C231D /* ProgressSheetController.swift */; };
17 | 6F8923A025B2263C00D8118F /* DiskTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F89239F25B2263C00D8118F /* DiskTools.swift */; };
18 | 6F8FF14125B21C41003DF0A7 /* qemu-img in Resources */ = {isa = PBXBuildFile; fileRef = 6F8FF14025B21C41003DF0A7 /* qemu-img */; };
19 | 6F8FF14525B21C87003DF0A7 /* mkfs in Resources */ = {isa = PBXBuildFile; fileRef = 6F8FF14425B21C87003DF0A7 /* mkfs */; };
20 | 6FB349AF25A8C3F500666240 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB349AE25A8C3F500666240 /* AppDelegate.swift */; };
21 | 6FB349B125A8C3F500666240 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB349B025A8C3F500666240 /* ViewController.swift */; };
22 | 6FB349B325A8C3F700666240 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB349B225A8C3F700666240 /* Assets.xcassets */; };
23 | 6FB349B625A8C3F700666240 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FB349B425A8C3F700666240 /* Main.storyboard */; };
24 | 6FB349C625A8E4C400666240 /* xhyve in Resources */ = {isa = PBXBuildFile; fileRef = 6FB349C525A8E4C400666240 /* xhyve */; };
25 | 6FB349CB25A8ECF800666240 /* initrd.img-4.4.0-131-generic in Resources */ = {isa = PBXBuildFile; fileRef = 6FB349C925A8ECF800666240 /* initrd.img-4.4.0-131-generic */; };
26 | 6FB349CC25A8ECF800666240 /* vmlinuz-4.4.0-131-generic in Resources */ = {isa = PBXBuildFile; fileRef = 6FB349CA25A8ECF800666240 /* vmlinuz-4.4.0-131-generic */; };
27 | /* End PBXBuildFile section */
28 |
29 | /* Begin PBXFileReference section */
30 | 6F03EEB125AE26B5005ABD99 /* MainWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindowController.swift; sourceTree = ""; };
31 | 6F155D2B25B064820050A2E8 /* hyperkit */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = hyperkit; sourceTree = ""; };
32 | 6F155D3125B0742E0050A2E8 /* DiskManagerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskManagerViewController.swift; sourceTree = ""; };
33 | 6F155D3525B075900050A2E8 /* AddDiskSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddDiskSheetController.swift; sourceTree = ""; };
34 | 6F2EE07325A99FB300B4E169 /* Ubuntu-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Ubuntu-Bridging-Header.h"; sourceTree = ""; };
35 | 6F2EE07425A99FB300B4E169 /* Authorization.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Authorization.m; sourceTree = ""; };
36 | 6F2F8C6325ADE91B004C231D /* ProgressSheetController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressSheetController.swift; sourceTree = ""; };
37 | 6F89239F25B2263C00D8118F /* DiskTools.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskTools.swift; sourceTree = ""; };
38 | 6F8FF14025B21C41003DF0A7 /* qemu-img */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "qemu-img"; sourceTree = ""; };
39 | 6F8FF14425B21C87003DF0A7 /* mkfs */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = mkfs; path = ../../../../../ubuntu/mkfs; sourceTree = ""; };
40 | 6FB349AB25A8C3F500666240 /* Ubuntu.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ubuntu.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 6FB349AE25A8C3F500666240 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
42 | 6FB349B025A8C3F500666240 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
43 | 6FB349B225A8C3F700666240 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
44 | 6FB349B525A8C3F700666240 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
45 | 6FB349B725A8C3F700666240 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
46 | 6FB349B825A8C3F700666240 /* Ubuntu.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ubuntu.entitlements; sourceTree = ""; };
47 | 6FB349C525A8E4C400666240 /* xhyve */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = xhyve; sourceTree = ""; };
48 | 6FB349C925A8ECF800666240 /* initrd.img-4.4.0-131-generic */ = {isa = PBXFileReference; lastKnownFileType = file; path = "initrd.img-4.4.0-131-generic"; sourceTree = ""; };
49 | 6FB349CA25A8ECF800666240 /* vmlinuz-4.4.0-131-generic */ = {isa = PBXFileReference; lastKnownFileType = file; path = "vmlinuz-4.4.0-131-generic"; sourceTree = ""; };
50 | 6FD78FBB25B306BE00AD4942 /* 00-automount.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "00-automount.sh"; sourceTree = ""; };
51 | /* End PBXFileReference section */
52 |
53 | /* Begin PBXFrameworksBuildPhase section */
54 | 6FB349A825A8C3F500666240 /* Frameworks */ = {
55 | isa = PBXFrameworksBuildPhase;
56 | buildActionMask = 2147483647;
57 | files = (
58 | 6F03EEAE25AE1706005ABD99 /* SwiftyJSON in Frameworks */,
59 | );
60 | runOnlyForDeploymentPostprocessing = 0;
61 | };
62 | /* End PBXFrameworksBuildPhase section */
63 |
64 | /* Begin PBXGroup section */
65 | 6F155D3825B0D3090050A2E8 /* hypervisor */ = {
66 | isa = PBXGroup;
67 | children = (
68 | 6FB349C525A8E4C400666240 /* xhyve */,
69 | 6F155D2B25B064820050A2E8 /* hyperkit */,
70 | );
71 | path = hypervisor;
72 | sourceTree = "";
73 | };
74 | 6F8FF13C25B21B81003DF0A7 /* disk */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 6F8FF14425B21C87003DF0A7 /* mkfs */,
78 | 6F8FF14025B21C41003DF0A7 /* qemu-img */,
79 | 6F89239F25B2263C00D8118F /* DiskTools.swift */,
80 | );
81 | path = disk;
82 | sourceTree = "";
83 | };
84 | 6FB349A225A8C3F500666240 = {
85 | isa = PBXGroup;
86 | children = (
87 | 6FB349AD25A8C3F500666240 /* Ubuntu */,
88 | 6FB349AC25A8C3F500666240 /* Products */,
89 | 6FB349BF25A8E11100666240 /* Frameworks */,
90 | );
91 | sourceTree = "";
92 | };
93 | 6FB349AC25A8C3F500666240 /* Products */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 6FB349AB25A8C3F500666240 /* Ubuntu.app */,
97 | );
98 | name = Products;
99 | sourceTree = "";
100 | };
101 | 6FB349AD25A8C3F500666240 /* Ubuntu */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 6FD78FBA25B3069900AD4942 /* updates */,
105 | 6F8FF13C25B21B81003DF0A7 /* disk */,
106 | 6F155D3825B0D3090050A2E8 /* hypervisor */,
107 | 6FB349C825A8ECC600666240 /* boot */,
108 | 6FB349AE25A8C3F500666240 /* AppDelegate.swift */,
109 | 6F03EEB125AE26B5005ABD99 /* MainWindowController.swift */,
110 | 6FB349B025A8C3F500666240 /* ViewController.swift */,
111 | 6F155D3125B0742E0050A2E8 /* DiskManagerViewController.swift */,
112 | 6F155D3525B075900050A2E8 /* AddDiskSheetController.swift */,
113 | 6F2F8C6325ADE91B004C231D /* ProgressSheetController.swift */,
114 | 6F2EE07325A99FB300B4E169 /* Ubuntu-Bridging-Header.h */,
115 | 6F2EE07425A99FB300B4E169 /* Authorization.m */,
116 | 6FB349B425A8C3F700666240 /* Main.storyboard */,
117 | 6FB349B225A8C3F700666240 /* Assets.xcassets */,
118 | 6FB349B725A8C3F700666240 /* Info.plist */,
119 | 6FB349B825A8C3F700666240 /* Ubuntu.entitlements */,
120 | );
121 | path = Ubuntu;
122 | sourceTree = "";
123 | };
124 | 6FB349BF25A8E11100666240 /* Frameworks */ = {
125 | isa = PBXGroup;
126 | children = (
127 | );
128 | name = Frameworks;
129 | sourceTree = "";
130 | };
131 | 6FB349C825A8ECC600666240 /* boot */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 6FB349C925A8ECF800666240 /* initrd.img-4.4.0-131-generic */,
135 | 6FB349CA25A8ECF800666240 /* vmlinuz-4.4.0-131-generic */,
136 | );
137 | path = boot;
138 | sourceTree = "";
139 | };
140 | 6FD78FBA25B3069900AD4942 /* updates */ = {
141 | isa = PBXGroup;
142 | children = (
143 | 6FD78FBB25B306BE00AD4942 /* 00-automount.sh */,
144 | );
145 | path = updates;
146 | sourceTree = "";
147 | };
148 | /* End PBXGroup section */
149 |
150 | /* Begin PBXNativeTarget section */
151 | 6FB349AA25A8C3F500666240 /* Ubuntu */ = {
152 | isa = PBXNativeTarget;
153 | buildConfigurationList = 6FB349BB25A8C3F700666240 /* Build configuration list for PBXNativeTarget "Ubuntu" */;
154 | buildPhases = (
155 | 6FB349A725A8C3F500666240 /* Sources */,
156 | 6FB349A825A8C3F500666240 /* Frameworks */,
157 | 6FB349A925A8C3F500666240 /* Resources */,
158 | );
159 | buildRules = (
160 | );
161 | dependencies = (
162 | );
163 | name = Ubuntu;
164 | packageProductDependencies = (
165 | 6F03EEAD25AE1706005ABD99 /* SwiftyJSON */,
166 | );
167 | productName = Ubuntu;
168 | productReference = 6FB349AB25A8C3F500666240 /* Ubuntu.app */;
169 | productType = "com.apple.product-type.application";
170 | };
171 | /* End PBXNativeTarget section */
172 |
173 | /* Begin PBXProject section */
174 | 6FB349A325A8C3F500666240 /* Project object */ = {
175 | isa = PBXProject;
176 | attributes = {
177 | KnownAssetTags = (
178 | xhyve,
179 | );
180 | LastSwiftUpdateCheck = 1230;
181 | LastUpgradeCheck = 1230;
182 | TargetAttributes = {
183 | 6FB349AA25A8C3F500666240 = {
184 | CreatedOnToolsVersion = 12.3;
185 | LastSwiftMigration = 1230;
186 | };
187 | };
188 | };
189 | buildConfigurationList = 6FB349A625A8C3F500666240 /* Build configuration list for PBXProject "Ubuntu" */;
190 | compatibilityVersion = "Xcode 9.3";
191 | developmentRegion = en;
192 | hasScannedForEncodings = 0;
193 | knownRegions = (
194 | en,
195 | Base,
196 | );
197 | mainGroup = 6FB349A225A8C3F500666240;
198 | packageReferences = (
199 | 6F03EEAC25AE1706005ABD99 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
200 | );
201 | productRefGroup = 6FB349AC25A8C3F500666240 /* Products */;
202 | projectDirPath = "";
203 | projectRoot = "";
204 | targets = (
205 | 6FB349AA25A8C3F500666240 /* Ubuntu */,
206 | );
207 | };
208 | /* End PBXProject section */
209 |
210 | /* Begin PBXResourcesBuildPhase section */
211 | 6FB349A925A8C3F500666240 /* Resources */ = {
212 | isa = PBXResourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | 6F155D2C25B064820050A2E8 /* hyperkit in Resources */,
216 | 6FB349C625A8E4C400666240 /* xhyve in Resources */,
217 | 6F8FF14125B21C41003DF0A7 /* qemu-img in Resources */,
218 | 6FB349B325A8C3F700666240 /* Assets.xcassets in Resources */,
219 | 6FB349CB25A8ECF800666240 /* initrd.img-4.4.0-131-generic in Resources */,
220 | 6F8FF14525B21C87003DF0A7 /* mkfs in Resources */,
221 | 6FB349CC25A8ECF800666240 /* vmlinuz-4.4.0-131-generic in Resources */,
222 | 6FB349B625A8C3F700666240 /* Main.storyboard in Resources */,
223 | );
224 | runOnlyForDeploymentPostprocessing = 0;
225 | };
226 | /* End PBXResourcesBuildPhase section */
227 |
228 | /* Begin PBXSourcesBuildPhase section */
229 | 6FB349A725A8C3F500666240 /* Sources */ = {
230 | isa = PBXSourcesBuildPhase;
231 | buildActionMask = 2147483647;
232 | files = (
233 | 6F155D3225B0742E0050A2E8 /* DiskManagerViewController.swift in Sources */,
234 | 6FB349B125A8C3F500666240 /* ViewController.swift in Sources */,
235 | 6FB349AF25A8C3F500666240 /* AppDelegate.swift in Sources */,
236 | 6F2F8C6425ADE91B004C231D /* ProgressSheetController.swift in Sources */,
237 | 6F155D3625B075900050A2E8 /* AddDiskSheetController.swift in Sources */,
238 | 6F2EE07525A99FB300B4E169 /* Authorization.m in Sources */,
239 | 6F8923A025B2263C00D8118F /* DiskTools.swift in Sources */,
240 | 6F03EEB225AE26B5005ABD99 /* MainWindowController.swift in Sources */,
241 | );
242 | runOnlyForDeploymentPostprocessing = 0;
243 | };
244 | /* End PBXSourcesBuildPhase section */
245 |
246 | /* Begin PBXVariantGroup section */
247 | 6FB349B425A8C3F700666240 /* Main.storyboard */ = {
248 | isa = PBXVariantGroup;
249 | children = (
250 | 6FB349B525A8C3F700666240 /* Base */,
251 | );
252 | name = Main.storyboard;
253 | sourceTree = "";
254 | };
255 | /* End PBXVariantGroup section */
256 |
257 | /* Begin XCBuildConfiguration section */
258 | 6FB349B925A8C3F700666240 /* Debug */ = {
259 | isa = XCBuildConfiguration;
260 | buildSettings = {
261 | ALWAYS_SEARCH_USER_PATHS = NO;
262 | CLANG_ANALYZER_NONNULL = YES;
263 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
265 | CLANG_CXX_LIBRARY = "libc++";
266 | CLANG_ENABLE_MODULES = YES;
267 | CLANG_ENABLE_OBJC_ARC = YES;
268 | CLANG_ENABLE_OBJC_WEAK = YES;
269 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
270 | CLANG_WARN_BOOL_CONVERSION = YES;
271 | CLANG_WARN_COMMA = YES;
272 | CLANG_WARN_CONSTANT_CONVERSION = YES;
273 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
275 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
276 | CLANG_WARN_EMPTY_BODY = YES;
277 | CLANG_WARN_ENUM_CONVERSION = YES;
278 | CLANG_WARN_INFINITE_RECURSION = YES;
279 | CLANG_WARN_INT_CONVERSION = YES;
280 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
281 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
282 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
284 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
285 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
286 | CLANG_WARN_STRICT_PROTOTYPES = YES;
287 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
288 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
289 | CLANG_WARN_UNREACHABLE_CODE = YES;
290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
291 | COPY_PHASE_STRIP = NO;
292 | DEBUG_INFORMATION_FORMAT = dwarf;
293 | ENABLE_STRICT_OBJC_MSGSEND = YES;
294 | ENABLE_TESTABILITY = YES;
295 | GCC_C_LANGUAGE_STANDARD = gnu11;
296 | GCC_DYNAMIC_NO_PIC = NO;
297 | GCC_NO_COMMON_BLOCKS = YES;
298 | GCC_OPTIMIZATION_LEVEL = 0;
299 | GCC_PREPROCESSOR_DEFINITIONS = (
300 | "DEBUG=1",
301 | "$(inherited)",
302 | );
303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
304 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
305 | GCC_WARN_UNDECLARED_SELECTOR = YES;
306 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
307 | GCC_WARN_UNUSED_FUNCTION = YES;
308 | GCC_WARN_UNUSED_VARIABLE = YES;
309 | MACOSX_DEPLOYMENT_TARGET = 11.1;
310 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
311 | MTL_FAST_MATH = YES;
312 | ONLY_ACTIVE_ARCH = YES;
313 | SDKROOT = macosx;
314 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
315 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
316 | };
317 | name = Debug;
318 | };
319 | 6FB349BA25A8C3F700666240 /* Release */ = {
320 | isa = XCBuildConfiguration;
321 | buildSettings = {
322 | ALWAYS_SEARCH_USER_PATHS = NO;
323 | CLANG_ANALYZER_NONNULL = YES;
324 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
326 | CLANG_CXX_LIBRARY = "libc++";
327 | CLANG_ENABLE_MODULES = YES;
328 | CLANG_ENABLE_OBJC_ARC = YES;
329 | CLANG_ENABLE_OBJC_WEAK = YES;
330 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
331 | CLANG_WARN_BOOL_CONVERSION = YES;
332 | CLANG_WARN_COMMA = YES;
333 | CLANG_WARN_CONSTANT_CONVERSION = YES;
334 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
335 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
336 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
337 | CLANG_WARN_EMPTY_BODY = YES;
338 | CLANG_WARN_ENUM_CONVERSION = YES;
339 | CLANG_WARN_INFINITE_RECURSION = YES;
340 | CLANG_WARN_INT_CONVERSION = YES;
341 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
342 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
343 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
344 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
345 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
346 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
347 | CLANG_WARN_STRICT_PROTOTYPES = YES;
348 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
349 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
350 | CLANG_WARN_UNREACHABLE_CODE = YES;
351 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
352 | COPY_PHASE_STRIP = NO;
353 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
354 | ENABLE_NS_ASSERTIONS = NO;
355 | ENABLE_STRICT_OBJC_MSGSEND = YES;
356 | GCC_C_LANGUAGE_STANDARD = gnu11;
357 | GCC_NO_COMMON_BLOCKS = YES;
358 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
359 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
360 | GCC_WARN_UNDECLARED_SELECTOR = YES;
361 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
362 | GCC_WARN_UNUSED_FUNCTION = YES;
363 | GCC_WARN_UNUSED_VARIABLE = YES;
364 | MACOSX_DEPLOYMENT_TARGET = 11.1;
365 | MTL_ENABLE_DEBUG_INFO = NO;
366 | MTL_FAST_MATH = YES;
367 | SDKROOT = macosx;
368 | SWIFT_COMPILATION_MODE = wholemodule;
369 | SWIFT_OPTIMIZATION_LEVEL = "-O";
370 | };
371 | name = Release;
372 | };
373 | 6FB349BC25A8C3F700666240 /* Debug */ = {
374 | isa = XCBuildConfiguration;
375 | buildSettings = {
376 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
377 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
378 | CLANG_ENABLE_MODULES = YES;
379 | CODE_SIGN_ENTITLEMENTS = Ubuntu/Ubuntu.entitlements;
380 | CODE_SIGN_STYLE = Automatic;
381 | COMBINE_HIDPI_IMAGES = YES;
382 | INFOPLIST_FILE = Ubuntu/Info.plist;
383 | LD_RUNPATH_SEARCH_PATHS = (
384 | "$(inherited)",
385 | "@executable_path/../Frameworks",
386 | );
387 | MARKETING_VERSION = 0.2.0;
388 | PRODUCT_BUNDLE_IDENTIFIER = org.sdslabs.Ubuntu;
389 | PRODUCT_NAME = "$(TARGET_NAME)";
390 | SWIFT_OBJC_BRIDGING_HEADER = "Ubuntu/Ubuntu-Bridging-Header.h";
391 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
392 | SWIFT_VERSION = 5.0;
393 | };
394 | name = Debug;
395 | };
396 | 6FB349BD25A8C3F700666240 /* Release */ = {
397 | isa = XCBuildConfiguration;
398 | buildSettings = {
399 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
400 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
401 | CLANG_ENABLE_MODULES = YES;
402 | CODE_SIGN_ENTITLEMENTS = Ubuntu/Ubuntu.entitlements;
403 | CODE_SIGN_STYLE = Automatic;
404 | COMBINE_HIDPI_IMAGES = YES;
405 | INFOPLIST_FILE = Ubuntu/Info.plist;
406 | LD_RUNPATH_SEARCH_PATHS = (
407 | "$(inherited)",
408 | "@executable_path/../Frameworks",
409 | );
410 | MARKETING_VERSION = 0.2.0;
411 | PRODUCT_BUNDLE_IDENTIFIER = org.sdslabs.Ubuntu;
412 | PRODUCT_NAME = "$(TARGET_NAME)";
413 | SWIFT_OBJC_BRIDGING_HEADER = "Ubuntu/Ubuntu-Bridging-Header.h";
414 | SWIFT_VERSION = 5.0;
415 | };
416 | name = Release;
417 | };
418 | /* End XCBuildConfiguration section */
419 |
420 | /* Begin XCConfigurationList section */
421 | 6FB349A625A8C3F500666240 /* Build configuration list for PBXProject "Ubuntu" */ = {
422 | isa = XCConfigurationList;
423 | buildConfigurations = (
424 | 6FB349B925A8C3F700666240 /* Debug */,
425 | 6FB349BA25A8C3F700666240 /* Release */,
426 | );
427 | defaultConfigurationIsVisible = 0;
428 | defaultConfigurationName = Release;
429 | };
430 | 6FB349BB25A8C3F700666240 /* Build configuration list for PBXNativeTarget "Ubuntu" */ = {
431 | isa = XCConfigurationList;
432 | buildConfigurations = (
433 | 6FB349BC25A8C3F700666240 /* Debug */,
434 | 6FB349BD25A8C3F700666240 /* Release */,
435 | );
436 | defaultConfigurationIsVisible = 0;
437 | defaultConfigurationName = Release;
438 | };
439 | /* End XCConfigurationList section */
440 |
441 | /* Begin XCRemoteSwiftPackageReference section */
442 | 6F03EEAC25AE1706005ABD99 /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
443 | isa = XCRemoteSwiftPackageReference;
444 | repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON";
445 | requirement = {
446 | kind = upToNextMajorVersion;
447 | minimumVersion = 5.0.0;
448 | };
449 | };
450 | /* End XCRemoteSwiftPackageReference section */
451 |
452 | /* Begin XCSwiftPackageProductDependency section */
453 | 6F03EEAD25AE1706005ABD99 /* SwiftyJSON */ = {
454 | isa = XCSwiftPackageProductDependency;
455 | package = 6F03EEAC25AE1706005ABD99 /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
456 | productName = SwiftyJSON;
457 | };
458 | /* End XCSwiftPackageProductDependency section */
459 | };
460 | rootObject = 6FB349A325A8C3F500666240 /* Project object */;
461 | }
462 |
--------------------------------------------------------------------------------
/Ubuntu.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Ubuntu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Ubuntu.xcodeproj/xcuserdata/kanav.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Playground (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 2
13 |
14 | Playground (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 3
20 |
21 | Playground (Playground).xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 0
27 |
28 | Ubuntu.xcscheme_^#shared#^_
29 |
30 | orderHint
31 | 0
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/Ubuntu/AddDiskSheetController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddDiskSheetController.swift
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 14/01/21.
6 | //
7 |
8 | import Cocoa
9 |
10 | enum DiskFormat {
11 | case qcow2, raw
12 | }
13 |
14 | enum StorageUnit {
15 | case gb, mb
16 | }
17 |
18 | struct DiskImage {
19 | var name: String
20 | var format: DiskFormat
21 | var storage: Int32
22 | var storageUnit: StorageUnit
23 | var mounted: Bool
24 | }
25 |
26 | func formatDescription(_ format: DiskFormat) -> String {
27 | switch format {
28 | case .qcow2:
29 | return "QCoW2 Image"
30 | case .raw:
31 | return "Raw Image"
32 | }
33 | }
34 |
35 | func unitDescription(_ unit: StorageUnit) -> String {
36 | switch unit {
37 | case .gb:
38 | return "GB"
39 | case .mb:
40 | return "MB"
41 | }
42 | }
43 |
44 | class AddDiskSheetController: NSViewController {
45 | @IBOutlet weak var formatPopUpButton: NSPopUpButton!
46 | @IBOutlet weak var spaceSuffixPopUpButton: NSPopUpButton!
47 | @IBOutlet weak var spaceTextField: NSTextField!
48 | @IBOutlet weak var nameTextField: NSTextField!
49 |
50 | @IBOutlet weak var spinner: NSProgressIndicator!
51 | @IBOutlet weak var errorLabel: NSTextField!
52 |
53 | var parentController: DiskManagerViewController!
54 |
55 | override func viewDidAppear() {
56 | nameTextField.stringValue = ""
57 | spaceTextField.integerValue = 1
58 | formatPopUpButton.selectItem(at: 0)
59 | spaceSuffixPopUpButton.selectItem(at: 1)
60 | errorLabel.isHidden = true
61 | spinner.isHidden = true
62 | }
63 |
64 | @IBAction func onCreateClicked(_ sender: Any) {
65 | errorLabel.isHidden = true
66 | spinner.isHidden = false
67 | spinner.startAnimation(nil)
68 | let format = formatPopUpButton.indexOfSelectedItem == 1 ? DiskFormat.raw : .qcow2
69 | let storageUnit = spaceSuffixPopUpButton.indexOfSelectedItem == 1 ? StorageUnit.gb : .mb
70 | let disk = DiskImage(name: nameTextField.stringValue, format: format, storage: spaceTextField.intValue, storageUnit: storageUnit, mounted: true)
71 | // Validation and Creation
72 | DispatchQueue.global().async {
73 | for d in self.parentController.disks {
74 | if d.name == disk.name {
75 | // duplicate
76 | DispatchQueue.main.async {
77 | self.errorLabel.isHidden = false
78 | self.spinner.stopAnimation(nil)
79 | self.spinner.isHidden = true
80 | self.errorLabel.stringValue = "Disk by this name already exists"
81 | }
82 | return
83 | }
84 | }
85 |
86 | if DiskTools.diskExistWithName(d: disk.name) {
87 | DispatchQueue.main.async {
88 | self.errorLabel.isHidden = false
89 | self.spinner.stopAnimation(nil)
90 | self.spinner.isHidden = true
91 | self.errorLabel.stringValue = "File already exists by same name"
92 | }
93 | return
94 | }
95 |
96 | do {
97 | try DiskTools.createDisk(d: disk)
98 | }
99 | catch {
100 | DispatchQueue.main.async {
101 | self.errorLabel.isHidden = false
102 | self.spinner.stopAnimation(nil)
103 | self.spinner.isHidden = true
104 | self.errorLabel.stringValue = "Err something went wrong.."
105 | }
106 | return
107 | }
108 | DispatchQueue.main.async {
109 | self.parentController.addDisk(disk)
110 | self.dismiss(nil)
111 | }
112 | }
113 | }
114 |
115 | @IBAction func onCancelClicked(_ sender: Any) {
116 | self.dismiss(nil)
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/Ubuntu/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 08/01/21.
6 | //
7 |
8 | import Cocoa
9 | import SwiftyJSON
10 |
11 | enum State {
12 | case notInstalled, installing, off, on
13 | }
14 |
15 | @main
16 | class AppDelegate: NSObject, NSApplicationDelegate {
17 |
18 | var statusItem : NSStatusItem? = nil
19 |
20 | var state: State = .off
21 |
22 | var osThread : DispatchWorkItem? = nil
23 | var startStopMenuItem : NSMenuItem? = nil
24 | var preferencesViewController: ViewController?
25 | var mainWindow: NSWindow!
26 | var osTask: Process? = nil
27 | var macAddress: String = ""
28 | var ipAddress: String = ""
29 |
30 | var memory: Int = 1
31 | var vCPU: Int = 2
32 | var diskImagePath: URL!
33 | var configPath: URL!
34 | var disks: [DiskImage] = []
35 |
36 | var hypervisor: String = "hyperkit"
37 |
38 | func applicationDidFinishLaunching(_ aNotification: Notification) {
39 | // Insert code here to initialize your application
40 |
41 | initializeStatusItem()
42 | initializeConfiguration()
43 | initializePreferencesWindow() // Preferences window should always be initialized after config
44 | }
45 |
46 | func initializeStatusItem() {
47 | statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
48 | statusItem?.image = NSImage(named: "ubuntu-white")
49 |
50 | let statusMenu = NSMenu()
51 | startStopMenuItem = NSMenuItem(title: "Boot Ubuntu", action: #selector(AppDelegate.bootUbuntu), keyEquivalent: "")
52 | statusMenu.addItem(startStopMenuItem!)
53 | statusMenu.addItem(NSMenuItem(title: "Preferences", action: #selector(AppDelegate.showPreferencesWindow), keyEquivalent: ""))
54 | statusMenu.addItem(NSMenuItem(title: "Quit", action: #selector(AppDelegate.quitApplication), keyEquivalent: ""))
55 | statusItem?.menu = statusMenu
56 | }
57 |
58 | func initializePreferencesWindow() {
59 | mainWindow = NSApplication.shared.windows.first
60 | mainWindow.close()
61 | preferencesViewController = mainWindow.contentViewController as! ViewController
62 | preferencesViewController?.setApp(appDel: self)
63 | }
64 |
65 | func initializeConfiguration() {
66 | let appSupportDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
67 | let sdslabsPath = appSupportDirectory.appendingPathComponent("SDSLabs")
68 | configPath = sdslabsPath.appendingPathComponent("config.json")
69 | diskImagePath = sdslabsPath.appendingPathComponent("hdd.img")
70 |
71 | do
72 | {
73 | // Check if `~/Library/Application Support/SDSLabs` exist
74 | var isDirectory = ObjCBool(true)
75 | if FileManager.default.fileExists(atPath: sdslabsPath.path, isDirectory: &isDirectory) {
76 | if !isDirectory.boolValue {
77 | print("File with same path exists")
78 | quitApplication()
79 | }
80 | }
81 | else {
82 | try FileManager.default.createDirectory(at: sdslabsPath, withIntermediateDirectories: false, attributes: nil)
83 | }
84 |
85 | // Check if config.json exists
86 | if FileManager.default.fileExists(atPath: configPath.path) {
87 | let configData = try String(contentsOf: configPath, encoding: .utf8)
88 | let config = JSON(parseJSON: configData)
89 | if let jsonCPU = config["cpu"].int {
90 | vCPU = jsonCPU
91 | }
92 | if let jsonMem = config["memory"].int {
93 | memory = jsonMem
94 | }
95 | if let jsonDisks = config["disks"].array {
96 | for j in jsonDisks {
97 | disks.append(DiskImage(name: j["name"].string!, format: j["format"].string! == "qcow2" ? .qcow2 : .raw, storage: j["storage"].int32!, storageUnit: j["unit"].string! == "gb" ? .gb : .mb, mounted: j["mount"].bool!))
98 | }
99 | }
100 | }
101 | else {
102 | let config = JSON(dictionaryLiteral: ("cpu", vCPU), ("memory", memory), ("disks", JSON([])))
103 | print(config.description)
104 | print(configPath.path)
105 | try config.description.write(to: configPath, atomically: false, encoding: .utf8)
106 | }
107 |
108 | // Check if hdd.img exists
109 | if !FileManager.default.fileExists(atPath: diskImagePath.path) {
110 | setStateNotInstalled()
111 | }
112 | else {
113 | setStateTurnedOff()
114 | }
115 | }
116 | catch { quitApplication() }
117 | }
118 |
119 | func applicationWillTerminate(_ aNotification: Notification) {
120 | // Insert code here to tear down your application
121 | if state == .on {
122 | stopUbuntu()
123 | }
124 | }
125 |
126 | @objc func showPreferencesWindow() {
127 | NSApp.setActivationPolicy(.regular)
128 | NSApp.activate(ignoringOtherApps: true)
129 | mainWindow.makeKeyAndOrderFront(nil)
130 | }
131 |
132 | @objc func downloadUbuntuImage() {
133 | let imageDownloadURL = "https://s3.ap-south-1.amazonaws.com/ubuntu-server-16.04.5-preinstalled/base.img"
134 | showPreferencesWindow()
135 | preferencesViewController?.showProgressSheet()
136 | preferencesViewController?.progressSheet
137 | .download(from: URL(string: imageDownloadURL)!, to: diskImagePath.path)
138 | setStateInstalling()
139 | }
140 |
141 | func diskArgs() -> [String] {
142 | var i = 5
143 | let basePath: URL
144 | do {
145 | basePath = try DiskTools.getDisksDirectory()
146 | }
147 | catch {
148 | return []
149 | }
150 | var args: [String] = []
151 | for disk in disks {
152 | if disk.mounted {
153 | args.append("-s")
154 | var arg = "\(i),virtio-blk,"
155 | i += 1
156 | if disk.format == .qcow2 {
157 | let diskPath = basePath.appendingPathComponent("\(disk.name).qcow2")
158 | arg += "file://\(diskPath.path),format=qcow"
159 | }
160 | else if disk.format == .raw {
161 | let diskPath = basePath.appendingPathComponent("\(disk.name).img")
162 | arg += diskPath.path
163 | }
164 | args.append(arg)
165 | }
166 | }
167 | return args
168 | }
169 |
170 | @objc func bootUbuntu() {
171 |
172 | if let hypervisorExecutableURL = Bundle.main.url(forResource: hypervisor, withExtension: "") {
173 | if let vmlinuzURL = Bundle.main.url(forResource: "vmlinuz-4.4.0-131-generic", withExtension: "") {
174 | if let initrdURL = Bundle.main.url(forResource: "initrd.img-4.4.0-131-generic", withExtension: "") {
175 | // Step 1: Set setuid bit for xhyve
176 | setuid_file(hypervisorExecutableURL.path)
177 |
178 | let arguments = [
179 | "-c", String(vCPU),
180 | "-U", "8e7af180-c54d-4aa2-9bef-59d94a1ac572",
181 | "-m", "\(memory)G",
182 | "-s", "0:0,hostbridge",
183 | "-s", "31,lpc",
184 | "-s", "2:0,virtio-net",
185 | "-s", "4,virtio-blk,\(diskImagePath.path)",
186 | "-f", "kexec,\(vmlinuzURL.path),\(initrdURL.path),\"acpi=off root=/dev/vda1 ro quiet\""
187 | ] + diskArgs()
188 |
189 | // Step 2: Get MAC Address for the VM
190 | let macAddressPipe = Pipe()
191 | let macAddressTask = Process()
192 | macAddressTask.launchPath = hypervisorExecutableURL.path
193 | macAddressTask.standardOutput = macAddressPipe
194 | macAddressTask.arguments = arguments + ["-M"]
195 | macAddressTask.launch()
196 | macAddressTask.waitUntilExit()
197 | let macAddressHandle = macAddressPipe.fileHandleForReading
198 | macAddressHandle.readData(ofLength: 5) // Read out "MAC: "
199 | // Next 17 bytes contain the mac address
200 | macAddress = String(data: macAddressHandle.readData(ofLength: 17), encoding: String.Encoding.utf8)!
201 | print("Got MAC Address: \(macAddress)")
202 | preferencesViewController?.macAddressField.stringValue = macAddress
203 |
204 | // Step 3: Run OS
205 | let outputData = Pipe()
206 | osTask = Process()
207 | osTask!.launchPath = hypervisorExecutableURL.path
208 | osTask!.arguments = arguments
209 | osTask!.standardOutput = outputData
210 | osTask!.launch()
211 | DispatchQueue.global().async {
212 | self.osTask!.waitUntilExit()
213 | DispatchQueue.main.async {
214 | self.setStateTurnedOff()
215 | }
216 | }
217 | setStateTurnedOn()
218 |
219 | // Step 4: Get IP address from /var/db/dhcpd_leases
220 | DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
221 | self.fetchIP()
222 | }
223 | }
224 | }
225 | }
226 | }
227 |
228 | func fetchIP() {
229 | do {
230 | let data = try String(contentsOfFile: "/var/db/dhcpd_leases", encoding: .utf8)
231 | let lines = data.components(separatedBy: .newlines)
232 | var currIPAddress = ""
233 | var currHWAddress = ""
234 | for line in lines {
235 | let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines)
236 | if trimmedLine == "{" {
237 | currIPAddress = ""
238 | currHWAddress = ""
239 | }
240 | else if trimmedLine == "}" {
241 | if currHWAddress == macAddress {
242 | ipAddress = currIPAddress
243 | DispatchQueue.main.async {
244 | self.preferencesViewController?.ipAddressField.stringValue = currIPAddress
245 | }
246 | print("found IP address \(ipAddress)")
247 | break
248 | }
249 | }
250 | else {
251 | if trimmedLine.starts(with: "hw_address=") {
252 | currHWAddress = String(trimmedLine.split(separator: "=")[1].split(separator: ",")[1])
253 | }
254 | else if trimmedLine.starts(with: "ip_address=") {
255 | currIPAddress = String(trimmedLine.split(separator: "=")[1])
256 | }
257 | }
258 | }
259 | }
260 | catch {
261 | print("couldn't find leases file")
262 | }
263 | }
264 |
265 | @objc func stopUbuntu() {
266 | // let killer = Process()
267 | // killer.launchPath = "/usr/bin/ssh"
268 | // killer.arguments = [
269 | // "default@" + ipAddress,
270 | // "-C", "sudo poweroff"
271 | // ]
272 | // killer.launch()
273 | // TODO: This is not a graceful way to shut down,
274 | // TODO: Simulate ACPI shutdown
275 | osTask?.terminate()
276 | setStateTurnedOff()
277 | }
278 |
279 | @objc func quitApplication() {
280 | NSApplication.shared.terminate(self)
281 | }
282 |
283 | /*
284 | Update Configuration
285 | */
286 | func updateCPU(_ c: Int) {
287 | vCPU = c
288 | updateJSON()
289 | }
290 |
291 | func updateMemory(_ m: Int) {
292 | memory = m
293 | updateJSON()
294 | }
295 |
296 | func updateDisks(_ d: [DiskImage]) {
297 | disks = d
298 | updateJSON()
299 | }
300 |
301 | func updateJSON() {
302 | var jsonDiskArray: [JSON] = []
303 | for d in disks {
304 | jsonDiskArray.append(JSON(dictionaryLiteral: ("name", d.name), ("format", d.format == .qcow2 ? "qcow2" : "raw"), ("storage", d.storage), ("unit", d.storageUnit == .gb ? "gb" : "mb"), ("mount", d.mounted)))
305 | }
306 | let disksJSON = JSON(jsonDiskArray)
307 | let config = JSON(dictionaryLiteral: ("cpu", vCPU), ("memory", memory), ("disks", disksJSON))
308 | do { try config.description.write(to: configPath, atomically: false, encoding: .utf8) }
309 | catch {}
310 | }
311 |
312 | /*
313 | Set states of UI
314 | */
315 | func setStateNotInstalled() {
316 | DispatchQueue.main.async {
317 | self.state = .notInstalled
318 | self.startStopMenuItem?.title = "Download Ubuntu.."
319 | self.startStopMenuItem?.isEnabled = true
320 | self.startStopMenuItem?.action = #selector(AppDelegate.downloadUbuntuImage)
321 | self.preferencesViewController?.bootButton.title = "Download"
322 | self.preferencesViewController?.bootButton.isEnabled = true
323 | self.preferencesViewController?.terminalButton.isEnabled = false
324 | self.preferencesViewController?.manageDisksButton.isEnabled = false
325 | self.preferencesViewController?.cpuSlider.isEnabled = false
326 | self.preferencesViewController?.memorySlider.isEnabled = false
327 | }
328 | }
329 |
330 | func setStateInstalling() {
331 | DispatchQueue.main.async {
332 | self.state = .installing
333 | self.startStopMenuItem?.title = "Downloading..."
334 | self.startStopMenuItem?.isEnabled = false
335 | self.startStopMenuItem?.action = nil
336 | self.preferencesViewController?.bootButton.title = "Download"
337 | self.preferencesViewController?.bootButton.isEnabled = false
338 | self.preferencesViewController?.terminalButton.isEnabled = false
339 | self.preferencesViewController?.manageDisksButton.isEnabled = false
340 | self.preferencesViewController?.cpuSlider.isEnabled = false
341 | self.preferencesViewController?.memorySlider.isEnabled = false
342 | }
343 | }
344 |
345 | func setStateTurnedOff() {
346 | DispatchQueue.main.async {
347 | self.state = .off
348 | self.startStopMenuItem?.title = "Boot-up"
349 | self.startStopMenuItem?.isEnabled = true
350 | self.startStopMenuItem?.action = #selector(AppDelegate.bootUbuntu)
351 | self.preferencesViewController?.bootButton.title = "Boot"
352 | self.preferencesViewController?.bootButton.isEnabled = true
353 | self.preferencesViewController?.terminalButton.isEnabled = false
354 | self.preferencesViewController?.manageDisksButton.isEnabled = true
355 | self.preferencesViewController?.cpuSlider.isEnabled = true
356 | self.preferencesViewController?.memorySlider.isEnabled = true
357 | self.preferencesViewController?.ipAddressField.stringValue = ""
358 | self.preferencesViewController?.macAddressField.stringValue = ""
359 | }
360 | }
361 |
362 | func setStateTurnedOn() {
363 | DispatchQueue.main.async {
364 | self.state = .on
365 | self.startStopMenuItem?.title = "Shut Down"
366 | self.startStopMenuItem?.isEnabled = true
367 | self.startStopMenuItem?.action = #selector(AppDelegate.stopUbuntu)
368 | self.preferencesViewController?.bootButton.title = "Shut down"
369 | self.preferencesViewController?.bootButton.isEnabled = true
370 | self.preferencesViewController?.terminalButton.isEnabled = true
371 | self.preferencesViewController?.manageDisksButton.isEnabled = false
372 | self.preferencesViewController?.cpuSlider.isEnabled = false
373 | self.preferencesViewController?.memorySlider.isEnabled = false
374 | }
375 | }
376 | }
377 |
378 |
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "filename" : "UbuntuCoF.png",
40 | "idiom" : "mac",
41 | "scale" : "2x",
42 | "size" : "256x256"
43 | },
44 | {
45 | "filename" : "UbuntuCoF-1.png",
46 | "idiom" : "mac",
47 | "scale" : "1x",
48 | "size" : "512x512"
49 | },
50 | {
51 | "idiom" : "mac",
52 | "scale" : "2x",
53 | "size" : "512x512"
54 | }
55 | ],
56 | "info" : {
57 | "author" : "xcode",
58 | "version" : 1
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/AppIcon.appiconset/UbuntuCoF-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/Assets.xcassets/AppIcon.appiconset/UbuntuCoF-1.png
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/AppIcon.appiconset/UbuntuCoF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/Assets.xcassets/AppIcon.appiconset/UbuntuCoF.png
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/UbuntuCoF.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "UbuntuCoF.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/UbuntuCoF.imageset/UbuntuCoF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/Assets.xcassets/UbuntuCoF.imageset/UbuntuCoF.png
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/ubuntu-white.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x"
6 | },
7 | {
8 | "filename" : "ubuntu-white.png",
9 | "idiom" : "mac",
10 | "scale" : "2x"
11 | }
12 | ],
13 | "info" : {
14 | "author" : "xcode",
15 | "version" : 1
16 | },
17 | "properties" : {
18 | "template-rendering-intent" : "template"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Ubuntu/Assets.xcassets/ubuntu-white.imageset/ubuntu-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/Assets.xcassets/ubuntu-white.imageset/ubuntu-white.png
--------------------------------------------------------------------------------
/Ubuntu/Authorization.m:
--------------------------------------------------------------------------------
1 | //
2 | // Authorization.m
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 09/01/21.
6 | //
7 |
8 | #import
9 | #include
10 |
11 | int auth (NSString *command, NSMutableArray *args, NSPipe *outputBuf) {
12 | @autoreleasepool {
13 | // Create authorization reference
14 | AuthorizationRef authorizationRef;
15 | OSStatus status;
16 | unsigned long numArgs = [args count];
17 | NSFileHandle *writer = [outputBuf fileHandleForWriting];
18 |
19 | status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
20 |
21 | // Run the tool using the authorization reference
22 | char *argList[numArgs+1];
23 | for (int i = 0; i < numArgs; ++i) {
24 | argList[i] = [(NSString *) args[i] UTF8String];
25 | }
26 | argList[numArgs] = NULL;
27 | FILE *pipe = NULL;
28 |
29 | status = AuthorizationExecuteWithPrivileges(authorizationRef, (char *)[command UTF8String], kAuthorizationFlagDefaults, argList, &pipe);
30 |
31 | // Print to standard output
32 | char readBuffer[128];
33 | if (status == errAuthorizationSuccess) {
34 | for (;;) {
35 | ssize_t bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer));
36 | if (bytesRead < 1) break;
37 | [writer writeData: [NSData dataWithBytes:(const void *) readBuffer length: bytesRead]];
38 | }
39 | } else {
40 | NSLog(@"Authorization Result Code: %d", status);
41 | }
42 | }
43 | return 0;
44 | }
45 |
46 | int setuid_file (NSString *binaryPath) {
47 | @autoreleasepool {
48 | char* binary = (char *)[binaryPath UTF8String];
49 | struct stat info;
50 | stat(binary, &info);
51 | if (info.st_uid == 0 && info.st_mode & 1<<11)
52 | {
53 | return 0;
54 | }
55 |
56 | // Create authorization reference
57 | AuthorizationRef authorizationRef;
58 | OSStatus status;
59 | char readBuffer[128];
60 |
61 | status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
62 |
63 | // Run the tool using the authorization reference
64 | char *argListChown[] = { "root", binary, NULL };
65 | FILE *pipe = NULL;
66 |
67 | status = AuthorizationExecuteWithPrivileges(authorizationRef, "/usr/sbin/chown", kAuthorizationFlagDefaults, argListChown, &pipe);
68 |
69 | // Print to standard output
70 | if (status == errAuthorizationSuccess) {
71 | printf("success\n");
72 | for (;;) {
73 | ssize_t bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer));
74 | if (bytesRead < 1) break;
75 | write(fileno(stdout), readBuffer, bytesRead);
76 | }
77 | } else {
78 | NSLog(@"Authorization Result Code: %d", status);
79 | return -1;
80 | }
81 |
82 | char *argListChmod[] = { "u+s", binary, NULL };
83 | status = AuthorizationExecuteWithPrivileges(authorizationRef, "/bin/chmod", kAuthorizationFlagDefaults, argListChmod, &pipe);
84 | if (status == errAuthorizationSuccess) {
85 | for (;;) {
86 | ssize_t bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer));
87 | if (bytesRead < 1) break;
88 | write(fileno(stdout), readBuffer, bytesRead);
89 | }
90 | } else {
91 | NSLog(@"Authorization Result Code: %d", status);
92 | return -1;
93 | }
94 | }
95 | return 0;
96 | }
97 |
98 |
--------------------------------------------------------------------------------
/Ubuntu/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 | Default
530 |
531 |
532 |
533 |
534 |
535 |
536 | Left to Right
537 |
538 |
539 |
540 |
541 |
542 |
543 | Right to Left
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 | Default
555 |
556 |
557 |
558 |
559 |
560 |
561 | Left to Right
562 |
563 |
564 |
565 |
566 |
567 |
568 | Right to Left
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 | Gw
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
1066 |
1067 |
1068 |
1069 |
1070 |
1071 |
1072 |
1073 |
1074 |
1075 |
1076 |
1077 |
1078 |
1079 |
1080 |
1081 |
1082 |
1083 |
1084 |
1085 |
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1093 |
1094 |
1095 |
1096 |
1097 |
1098 |
1099 |
1100 |
1101 |
1102 |
1103 |
1104 |
1105 |
1106 |
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 |
1115 |
1116 |
1117 |
1118 |
1119 |
1120 |
1121 |
1122 |
1123 |
1124 |
1125 |
1126 |
1127 |
1128 |
1129 |
1130 |
1131 |
1132 |
1133 |
1134 |
1135 |
1136 |
1137 |
1138 |
1139 |
1140 |
1141 |
1142 |
1143 |
1144 |
1145 |
1146 |
1147 |
1148 |
1149 |
1150 |
1151 |
1152 |
1153 |
1154 |
1155 |
1156 |
1157 |
1158 |
1159 |
1160 |
1161 |
1162 |
1163 |
1164 |
1165 |
1166 |
1167 |
1168 |
1169 |
1170 |
1171 |
1172 |
1173 |
1174 |
1175 |
1176 |
1177 |
1178 |
1179 |
1180 |
1181 |
1182 |
1183 |
1184 |
1185 |
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 |
1194 |
1195 |
1196 |
1197 |
1198 |
1199 |
1200 |
1201 |
1202 |
1203 |
1204 |
1205 |
1206 |
1207 |
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 |
1216 |
1217 |
1218 |
1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 |
1229 |
1230 |
1231 |
1232 |
1233 |
1234 | Gw
1235 |
1236 |
1237 |
1238 |
1239 |
1240 |
1241 |
1242 |
1243 |
1244 |
1245 |
1246 |
1247 |
1248 |
1249 |
1250 |
1251 |
1252 |
1253 |
1254 |
1255 |
1256 |
1257 |
1258 |
1259 |
1260 |
1261 |
1262 |
1263 |
--------------------------------------------------------------------------------
/Ubuntu/DiskManagerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DiskManager.swift
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 14/01/21.
6 | //
7 |
8 | import Cocoa
9 |
10 | class MountCell: NSTableCellView {
11 | @IBOutlet weak var checkBox: NSButton!
12 | }
13 |
14 | class DiskManagerViewController: NSViewController, NSOutlineViewDataSource, NSOutlineViewDelegate {
15 | var app: AppDelegate!
16 |
17 | @IBOutlet weak var addImageButton: NSButton!
18 | @IBOutlet weak var saveButton: NSButton!
19 |
20 | @IBOutlet weak var outlineView: NSOutlineView!
21 |
22 | var disks: [DiskImage] = []
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 | outlineView.dataSource = self
27 | outlineView.delegate = self
28 | }
29 |
30 | override func viewDidAppear() {
31 | outlineView.reloadData()
32 | }
33 |
34 | lazy var addDiskSheet: AddDiskSheetController = {
35 | var sheet = NSStoryboard.main!.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("add-disk")) as! AddDiskSheetController
36 | sheet.parentController = self
37 | return sheet
38 | }()
39 |
40 | @IBAction func addImageButtonClicked(_ sender: Any) {
41 | self.presentAsSheet(addDiskSheet)
42 | }
43 |
44 | @IBAction func saveAndClose(_ sender: Any) {
45 | for i in 0.. Int {
55 | return disks.count
56 | }
57 |
58 | func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
59 | return disks[index]
60 | }
61 |
62 | func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
63 | return false
64 | }
65 |
66 | // Delegate Methods
67 | func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
68 | guard let colIdentifier = tableColumn?.identifier else { return nil }
69 | var cellIdentifier: NSUserInterfaceItemIdentifier
70 | var val: String = ""
71 |
72 | if let disk = item as? DiskImage {
73 | switch colIdentifier {
74 | case NSUserInterfaceItemIdentifier(rawValue: "imageColumn"):
75 | cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "imageCell")
76 | val = disk.name
77 | case NSUserInterfaceItemIdentifier(rawValue: "summaryColumn"):
78 | cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "summaryCell")
79 | val = "\(disk.storage) \(unitDescription(disk.storageUnit)) \(formatDescription(disk.format))"
80 | case NSUserInterfaceItemIdentifier(rawValue: "mountColumn"):
81 | cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "mountCell")
82 | guard let cell = outlineView.makeView(withIdentifier: cellIdentifier, owner: nil) as? MountCell else { return nil }
83 | cell.checkBox.state = disk.mounted ? .on : .off
84 | return cell
85 | default:
86 | return nil
87 | }
88 |
89 | guard let cell = outlineView.makeView(withIdentifier: cellIdentifier, owner: nil) as? NSTableCellView else { return nil }
90 | cell.textField?.stringValue = val
91 | return cell
92 | }
93 | return nil
94 | }
95 |
96 | func addDisk(_ disk: DiskImage) {
97 | disks.append(disk)
98 | app.updateDisks(disks)
99 | outlineView.reloadData()
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Ubuntu/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | 1
21 | LSMinimumSystemVersion
22 | $(MACOSX_DEPLOYMENT_TARGET)
23 | LSUIElement
24 |
25 | NSMainStoryboardFile
26 | Main
27 | NSPrincipalClass
28 | NSApplication
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Ubuntu/MainWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainWindowController.swift
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 13/01/21.
6 | //
7 |
8 | import Cocoa
9 |
10 | class MainWindowController: NSWindowController, NSWindowDelegate {
11 | required init?(coder: NSCoder) {
12 | super.init(coder: coder)
13 | }
14 |
15 | override func windowDidLoad() {
16 | super.windowDidLoad()
17 | self.window?.delegate = self
18 | }
19 |
20 | func windowShouldClose(_ sender: NSWindow) -> Bool {
21 | NSApp.setActivationPolicy(.accessory)
22 | return true
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/Ubuntu/ProgressSheetController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProgressSheetController.swift
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 12/01/21.
6 | //
7 |
8 | import Cocoa
9 |
10 |
11 | class ProgressSheetController: NSViewController, URLSessionDownloadDelegate {
12 |
13 | var mainViewController: ViewController?
14 |
15 | @IBOutlet weak var cancelButton: NSButton!
16 | @IBOutlet weak var progressBar: NSProgressIndicator!
17 | @IBOutlet weak var progressLabel: NSTextField!
18 |
19 | var task: URLSessionDownloadTask!
20 | var destPath: String!
21 |
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 |
25 | // Do any additional setup after loading the view.
26 | }
27 |
28 | override var representedObject: Any? {
29 | didSet {
30 | // Update the view, if already loaded.
31 | }
32 | }
33 |
34 | @IBAction func onCancel(_ sender: Any) {
35 | task.cancel()
36 | mainViewController?.dismiss(self)
37 | }
38 |
39 | func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
40 | {
41 | if downloadTask == task {
42 | let percentDownloaded = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
43 | let totalGigabytesWritten = String(format: "%.2f", Double(totalBytesWritten) / Double(1024 * 1024 * 1024))
44 | let totalGigabytesExpected = String(format: "%.2f", Double(totalBytesExpectedToWrite) / Double(1024 * 1024 * 1024))
45 | DispatchQueue.main.async {
46 | self.progressBar.doubleValue = percentDownloaded * 100
47 | self.progressLabel.stringValue = "\(totalGigabytesWritten) / \(totalGigabytesExpected) GB"
48 | }
49 | print(percentDownloaded * 100)
50 | }
51 | }
52 |
53 | func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
54 | {
55 | do {
56 | try FileManager.default.moveItem(atPath: location.path, toPath: destPath)
57 | mainViewController?.app.setStateTurnedOff()
58 | }
59 | catch {}
60 |
61 | DispatchQueue.main.async {
62 | self.progressBar.stopAnimation(nil)
63 | self.mainViewController?.app.setStateTurnedOff()
64 | self.mainViewController?.dismiss(self)
65 | }
66 | }
67 |
68 | func download(from url: URL, to path: String) {
69 | progressBar.startAnimation(nil)
70 | let configuration = URLSessionConfiguration.default
71 | let operationQueue = OperationQueue()
72 | let session = URLSession(configuration: configuration, delegate: self, delegateQueue: operationQueue)
73 |
74 | let downloadTask = session.downloadTask(with: url)
75 | downloadTask.resume()
76 | task = downloadTask
77 | destPath = path
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/Ubuntu/Ubuntu-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import
6 |
7 | int auth (NSString *command, NSMutableArray *args, NSPipe *outputBuf);
8 | int setuid_file (NSString *binaryPath);
9 |
10 |
--------------------------------------------------------------------------------
/Ubuntu/Ubuntu.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Ubuntu/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 08/01/21.
6 | //
7 |
8 | import Cocoa
9 |
10 | class ViewController: NSViewController {
11 |
12 | var app: AppDelegate!
13 |
14 | @IBOutlet weak var macAddressField: NSTextField!
15 | @IBOutlet weak var ipAddressField: NSTextField!
16 |
17 |
18 | @IBOutlet weak var copyMacAddressButton: NSButton!
19 | @IBOutlet weak var copyIpAddressButton: NSButton!
20 |
21 | @IBOutlet weak var cpuSlider: NSSlider!
22 | @IBOutlet weak var cpuLabel: NSTextField!
23 | @IBOutlet weak var memorySlider: NSSlider!
24 | @IBOutlet weak var memoryLabel: NSTextField!
25 | @IBOutlet weak var bootButton: NSButton!
26 | @IBOutlet weak var terminalButton: NSButton!
27 | @IBOutlet weak var manageDisksButton: NSButton!
28 |
29 | lazy var progressSheet: ProgressSheetController = {
30 | var sheet = self.storyboard!.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("progress")) as! ProgressSheetController
31 | sheet.mainViewController = self
32 | return sheet
33 | }()
34 |
35 | lazy var diskManagerViewController: DiskManagerViewController = {
36 | var vc = self.storyboard!.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("disk-manager")) as! DiskManagerViewController
37 | vc.app = app
38 | vc.disks = app.disks
39 | return vc
40 | }()
41 |
42 | override func viewDidLoad() {
43 | super.viewDidLoad()
44 |
45 | // Do any additional setup after loading the view.
46 | }
47 |
48 | override var representedObject: Any? {
49 | didSet {
50 | // Update the view, if already loaded.
51 | }
52 | }
53 |
54 | func setApp(appDel: AppDelegate) {
55 | app = appDel
56 | cpuSlider.integerValue = app.vCPU
57 | cpuLabel.integerValue = app.vCPU
58 | memorySlider.integerValue = app.memory
59 | memoryLabel.stringValue = "\(memorySlider.integerValue)G"
60 | }
61 |
62 | @IBAction func onCPUChange(_ sender: Any) {
63 | cpuLabel.integerValue = cpuSlider.integerValue
64 | app.updateCPU(cpuSlider.integerValue)
65 | }
66 |
67 | @IBAction func onMemoryChange(_ sender: Any) {
68 | memoryLabel.stringValue = "\(memorySlider.integerValue)G"
69 | app.updateMemory(memorySlider.integerValue)
70 | }
71 |
72 | func setControls(val: Bool) {
73 | cpuSlider.isEnabled = val
74 | memorySlider.isEnabled = val
75 | terminalButton.isEnabled = !val
76 | }
77 |
78 | @IBAction func bootUbuntu(_ sender: Any) {
79 | switch app.state {
80 | case .on:
81 | app.stopUbuntu()
82 | case .off:
83 | app.bootUbuntu()
84 | case .notInstalled:
85 | app.downloadUbuntuImage()
86 | case .installing:
87 | return
88 | }
89 | }
90 |
91 | @IBAction func openTerminal(_ sender: Any) {
92 | // let terminalTask = Process()
93 | // terminalTask.launchPath = "/usr/bin/open"
94 | // terminalTask.arguments = [
95 | // "-a", "Terminal", "ssh kanav@\(app.ipAddress)"
96 | // ]
97 | // terminalTask.launch()
98 |
99 | // let script = NSAppleScript.init(source: "tell application \"Terminal\" to do script \"cd ~/Desktop\"")
100 | // script?.executeAndReturnError(nil)
101 |
102 | }
103 |
104 | @IBAction func openDiskManager(_ sender: Any) {
105 | self.presentAsModalWindow(diskManagerViewController)
106 | }
107 |
108 |
109 | func showProgressSheet() {
110 | presentAsSheet(progressSheet)
111 | }
112 |
113 | @IBAction func copyMac(_ sender: Any) {
114 | let pasteboard = NSPasteboard.general
115 | pasteboard.declareTypes([.string], owner: nil)
116 | pasteboard.setString(macAddressField.stringValue, forType: .string)
117 | }
118 |
119 | @IBAction func copyIp(_ sender: Any) {
120 | let pasteboard = NSPasteboard.general
121 | pasteboard.declareTypes([.string], owner: nil)
122 | pasteboard.setString(ipAddressField.stringValue, forType: .string)
123 | }
124 | }
125 |
126 |
--------------------------------------------------------------------------------
/Ubuntu/boot/initrd.img-4.4.0-131-generic:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/boot/initrd.img-4.4.0-131-generic
--------------------------------------------------------------------------------
/Ubuntu/boot/vmlinuz-4.4.0-131-generic:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/boot/vmlinuz-4.4.0-131-generic
--------------------------------------------------------------------------------
/Ubuntu/disk/DiskTools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DiskTools.swift
3 | // Ubuntu
4 | //
5 | // Created by Kanav Gupta on 16/01/21.
6 | //
7 |
8 | import Foundation
9 |
10 | class DiskTools {
11 | static let qemuImg = Bundle.main.url(forResource: "qemu-img", withExtension: "")!.path
12 | static let mkfs = Bundle.main.url(forResource: "mkfs", withExtension: "")!.path
13 |
14 | static func removeExtension(path: String) -> String {
15 | var components = path.components(separatedBy: ".")
16 | if components.count > 1 {
17 | components.removeLast()
18 | return components.joined(separator: ".")
19 | }
20 | else {
21 | return path
22 | }
23 | }
24 |
25 | static func createQcowImage(path: URL, sizeInMegaBytes: Int) {
26 | let process = Process()
27 | process.launchPath = qemuImg
28 | process.arguments = [
29 | "create",
30 | "-f",
31 | "qcow2",
32 | path.path,
33 | "\(sizeInMegaBytes)M"
34 | ]
35 | process.launch()
36 | process.waitUntilExit()
37 | }
38 |
39 | static func createRawImage(path: URL, sizeInMegaBytes: Int) {
40 | let process = Process()
41 | process.launchPath = qemuImg
42 | process.arguments = [
43 | "create",
44 | "-f",
45 | "raw",
46 | path.path,
47 | "\(sizeInMegaBytes)M"
48 | ]
49 | process.launch()
50 | process.waitUntilExit()
51 | }
52 |
53 | static func raw2qcow(path: URL) throws {
54 | let process = Process()
55 | process.launchPath = qemuImg
56 | process.arguments = [
57 | "convert",
58 | "-f",
59 | "raw",
60 | "-O",
61 | "qcow2",
62 | path.path,
63 | "\(removeExtension(path: path.path)).qcow2"
64 | ]
65 | process.launch()
66 | process.waitUntilExit()
67 | try deleteFile(path: path)
68 | }
69 |
70 | static func deleteFile(path: URL) throws {
71 | try FileManager.default.removeItem(atPath: path.path)
72 | }
73 |
74 | static func mkfs(path: URL, label: String) {
75 | let process = Process()
76 | process.launchPath = mkfs
77 | process.arguments = [
78 | "-L",
79 | label,
80 | "-F",
81 | path.path
82 | ]
83 | process.launch()
84 | process.waitUntilExit()
85 | }
86 |
87 | static func createRawFilesystem(path: URL, sizeInMegaBytes: Int, label: String) {
88 | createRawImage(path: path, sizeInMegaBytes: sizeInMegaBytes)
89 | mkfs(path: path, label: label)
90 | }
91 |
92 | static func createQcowFileSystem(path: URL, sizeInMegaBytes: Int, label: String) throws {
93 | createRawFilesystem(path: path, sizeInMegaBytes: sizeInMegaBytes, label: label)
94 | try raw2qcow(path: path)
95 | }
96 |
97 | static func getDisksDirectory() throws -> URL {
98 | let appSupportDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
99 | let sdslabsPath = appSupportDirectory.appendingPathComponent("SDSLabs")
100 | let disksPath = sdslabsPath.appendingPathComponent("disks")
101 | if !FileManager.default.fileExists(atPath: disksPath.path) {
102 | try FileManager.default.createDirectory(at: disksPath, withIntermediateDirectories: false, attributes: nil)
103 | }
104 | return disksPath
105 | }
106 |
107 | static func diskExistWithName(d: String) -> Bool {
108 | let appSupportDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
109 | let sdslabsPath = appSupportDirectory.appendingPathComponent("SDSLabs")
110 | let disksPath = sdslabsPath.appendingPathComponent("disks")
111 | let qcowPath = disksPath.appendingPathComponent("\(d).qcow2")
112 | let rawPath = disksPath.appendingPathComponent("\(d).img")
113 | return FileManager.default.fileExists(atPath: qcowPath.path) || FileManager.default.fileExists(atPath: rawPath.path)
114 | }
115 |
116 | static func createDisk(d: DiskImage) throws {
117 | let sizeInMegaBytes = d.storageUnit == .gb ? d.storage * 1024 : d.storage
118 | let disksPath = try getDisksDirectory()
119 | switch d.format {
120 | case .qcow2:
121 | try createQcowFileSystem(path: disksPath.appendingPathComponent("\(d.name).img"), sizeInMegaBytes: Int(sizeInMegaBytes), label: d.name)
122 | case .raw:
123 | createRawFilesystem(path: disksPath.appendingPathComponent("\(d.name).img"), sizeInMegaBytes: Int(sizeInMegaBytes), label: d.name)
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/Ubuntu/disk/mkfs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/disk/mkfs
--------------------------------------------------------------------------------
/Ubuntu/disk/qemu-img:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/disk/qemu-img
--------------------------------------------------------------------------------
/Ubuntu/hypervisor/hyperkit:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/hypervisor/hyperkit
--------------------------------------------------------------------------------
/Ubuntu/hypervisor/xhyve:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdslabs/DSL/1b30210bf426d559491ab016401cf39f3e6b1cb8/Ubuntu/hypervisor/xhyve
--------------------------------------------------------------------------------
/Ubuntu/updates/00-automount.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 00-automount.sh
4 | # Ubuntu
5 | #
6 | # Created by Kanav Gupta on 16/01/21.
7 | #
8 |
9 | echo """\
10 | [Unit]
11 | Description=Automount disks according to disk labels
12 |
13 | [Service]
14 | ExecStart=/opt/sdslabs/automount.sh
15 |
16 | [Install]
17 | WantedBy=multi-user.target
18 | """ > /etc/systemd/system/automount.service
19 |
20 | mkdir -p /opt/sdslabs
21 |
22 | echo """\
23 | #!/bin/bash
24 |
25 | for disk in /dev/disk/by-label/*; do
26 | label=\"\$(basename \$disk)\"
27 | mkdir -p \"/mnt/\$label\"
28 | mount \"\$disk\" \"/mnt/\$label\"
29 | chown default \"/mnt/\$label\"
30 | chgrp default \"/mnt/\$label\"
31 | done
32 | """ > /opt/sdslabs/automount.sh
33 |
34 | chmod +x /opt/sdslabs/automount.sh
35 | systemctl enable automount.service
36 |
--------------------------------------------------------------------------------