├── CMakeLists.txt
├── LICENSE
├── LICENSE2
├── NOTICE
├── README.md
├── api_for_osxiec_script.h
├── example
├── bin
│ └── hello
├── config.txt
├── container_config.txt
├── file.txt
├── run.sh
├── test.c
└── test
│ └── test.osxs
├── osxiec.c
├── osxiec.h
├── osxiec_script
├── osxiec_script.c
└── osxiec_script.h
├── plugin_manager
├── plugin.h
├── plugin_manager.c
└── plugin_manager.h
├── samples
├── Dockerfile
├── container1
├── container2
├── container_config.txt
├── deploy_config
├── deploy_multiple_config
└── sample_plugin.c
└── scripts
├── install.sh
└── osxiec_deploy_multiple.sh
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.27)
2 | project(osxiec C)
3 |
4 | set(CMAKE_C_STANDARD 11)
5 |
6 | # List shared sources
7 | set(OSXIEC_SOURCES
8 | osxiec.c
9 | plugin_manager/plugin.h
10 | plugin_manager/plugin_manager.h
11 | plugin_manager/plugin_manager.c
12 | osxiec_script/osxiec_script.h
13 | osxiec_script/osxiec_script.c
14 | api_for_osxiec_script.h
15 | )
16 |
17 | # Build shared library (.dylib)
18 | add_library(osxiec_shared SHARED
19 | ${OSXIEC_SOURCES}
20 | )
21 |
22 | set_target_properties(osxiec_shared PROPERTIES OUTPUT_NAME "osxiec")
23 |
24 | # Build executable
25 | add_executable(osxiec ${OSXIEC_SOURCES})
26 |
27 | # Find and link CURL
28 | find_package(CURL REQUIRED)
29 | target_link_libraries(osxiec PRIVATE CURL::libcurl)
30 | target_link_libraries(osxiec_shared PRIVATE CURL::libcurl)
31 |
32 | # Find and link Readline
33 | include_directories(/opt/homebrew/opt/readline/include)
34 | target_link_directories(osxiec PRIVATE /opt/homebrew/opt/readline/lib)
35 | target_link_directories(osxiec_shared PRIVATE /opt/homebrew/opt/readline/lib)
36 |
37 | target_link_libraries(osxiec PRIVATE readline)
38 | target_link_libraries(osxiec_shared PRIVATE readline)
39 |
40 | # Find and link json-c
41 | include_directories(/opt/homebrew/Cellar/json-c/0.18/include)
42 | target_link_directories(osxiec PRIVATE /opt/homebrew/Cellar/json-c/0.18/lib)
43 | target_link_directories(osxiec_shared PRIVATE /opt/homebrew/Cellar/json-c/0.18/lib)
44 |
45 | target_link_libraries(osxiec PRIVATE json-c)
46 | target_link_libraries(osxiec_shared PRIVATE json-c)
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Eclipse Public License - v 2.0
2 |
3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
6 |
7 | 1. DEFINITIONS
8 |
9 | "Contribution" means:
10 |
11 | a) in the case of the initial Contributor, the initial content
12 | Distributed under this Agreement, and
13 |
14 | b) in the case of each subsequent Contributor:
15 | i) changes to the Program, and
16 | ii) additions to the Program;
17 | where such changes and/or additions to the Program originate from
18 | and are Distributed by that particular Contributor. A Contribution
19 | "originates" from a Contributor if it was added to the Program by
20 | such Contributor itself or anyone acting on such Contributor's behalf.
21 | Contributions do not include changes or additions to the Program that
22 | are not Modified Works.
23 |
24 | "Contributor" means any person or entity that Distributes the Program.
25 |
26 | "Licensed Patents" mean patent claims licensable by a Contributor which
27 | are necessarily infringed by the use or sale of its Contribution alone
28 | or when combined with the Program.
29 |
30 | "Program" means the Contributions Distributed in accordance with this
31 | Agreement.
32 |
33 | "Recipient" means anyone who receives the Program under this Agreement
34 | or any Secondary License (as applicable), including Contributors.
35 |
36 | "Derivative Works" shall mean any work, whether in Source Code or other
37 | form, that is based on (or derived from) the Program and for which the
38 | editorial revisions, annotations, elaborations, or other modifications
39 | represent, as a whole, an original work of authorship.
40 |
41 | "Modified Works" shall mean any work in Source Code or other form that
42 | results from an addition to, deletion from, or modification of the
43 | contents of the Program, including, for purposes of clarity any new file
44 | in Source Code form that contains any contents of the Program. Modified
45 | Works shall not include works that contain only declarations,
46 | interfaces, types, classes, structures, or files of the Program solely
47 | in each case in order to link to, bind by name, or subclass the Program
48 | or Modified Works thereof.
49 |
50 | "Distribute" means the acts of a) distributing or b) making available
51 | in any manner that enables the transfer of a copy.
52 |
53 | "Source Code" means the form of a Program preferred for making
54 | modifications, including but not limited to software source code,
55 | documentation source, and configuration files.
56 |
57 | "Secondary License" means either the GNU General Public License,
58 | Version 2.0, or any later versions of that license, including any
59 | exceptions or additional permissions as identified by the initial
60 | Contributor.
61 |
62 | 2. GRANT OF RIGHTS
63 |
64 | a) Subject to the terms of this Agreement, each Contributor hereby
65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright
66 | license to reproduce, prepare Derivative Works of, publicly display,
67 | publicly perform, Distribute and sublicense the Contribution of such
68 | Contributor, if any, and such Derivative Works.
69 |
70 | b) Subject to the terms of this Agreement, each Contributor hereby
71 | grants Recipient a non-exclusive, worldwide, royalty-free patent
72 | license under Licensed Patents to make, use, sell, offer to sell,
73 | import and otherwise transfer the Contribution of such Contributor,
74 | if any, in Source Code or other form. This patent license shall
75 | apply to the combination of the Contribution and the Program if, at
76 | the time the Contribution is added by the Contributor, such addition
77 | of the Contribution causes such combination to be covered by the
78 | Licensed Patents. The patent license shall not apply to any other
79 | combinations which include the Contribution. No hardware per se is
80 | licensed hereunder.
81 |
82 | c) Recipient understands that although each Contributor grants the
83 | licenses to its Contributions set forth herein, no assurances are
84 | provided by any Contributor that the Program does not infringe the
85 | patent or other intellectual property rights of any other entity.
86 | Each Contributor disclaims any liability to Recipient for claims
87 | brought by any other entity based on infringement of intellectual
88 | property rights or otherwise. As a condition to exercising the
89 | rights and licenses granted hereunder, each Recipient hereby
90 | assumes sole responsibility to secure any other intellectual
91 | property rights needed, if any. For example, if a third party
92 | patent license is required to allow Recipient to Distribute the
93 | Program, it is Recipient's responsibility to acquire that license
94 | before distributing the Program.
95 |
96 | d) Each Contributor represents that to its knowledge it has
97 | sufficient copyright rights in its Contribution, if any, to grant
98 | the copyright license set forth in this Agreement.
99 |
100 | e) Notwithstanding the terms of any Secondary License, no
101 | Contributor makes additional grants to any Recipient (other than
102 | those set forth in this Agreement) as a result of such Recipient's
103 | receipt of the Program under the terms of a Secondary License
104 | (if permitted under the terms of Section 3).
105 |
106 | 3. REQUIREMENTS
107 |
108 | 3.1 If a Contributor Distributes the Program in any form, then:
109 |
110 | a) the Program must also be made available as Source Code, in
111 | accordance with section 3.2, and the Contributor must accompany
112 | the Program with a statement that the Source Code for the Program
113 | is available under this Agreement, and informs Recipients how to
114 | obtain it in a reasonable manner on or through a medium customarily
115 | used for software exchange; and
116 |
117 | b) the Contributor may Distribute the Program under a license
118 | different than this Agreement, provided that such license:
119 | i) effectively disclaims on behalf of all other Contributors all
120 | warranties and conditions, express and implied, including
121 | warranties or conditions of title and non-infringement, and
122 | implied warranties or conditions of merchantability and fitness
123 | for a particular purpose;
124 |
125 | ii) effectively excludes on behalf of all other Contributors all
126 | liability for damages, including direct, indirect, special,
127 | incidental and consequential damages, such as lost profits;
128 |
129 | iii) does not attempt to limit or alter the recipients' rights
130 | in the Source Code under section 3.2; and
131 |
132 | iv) requires any subsequent distribution of the Program by any
133 | party to be under a license that satisfies the requirements
134 | of this section 3.
135 |
136 | 3.2 When the Program is Distributed as Source Code:
137 |
138 | a) it must be made available under this Agreement, or if the
139 | Program (i) is combined with other material in a separate file or
140 | files made available under a Secondary License, and (ii) the initial
141 | Contributor attached to the Source Code the notice described in
142 | Exhibit A of this Agreement, then the Program may be made available
143 | under the terms of such Secondary Licenses, and
144 |
145 | b) a copy of this Agreement must be included with each copy of
146 | the Program.
147 |
148 | 3.3 Contributors may not remove or alter any copyright, patent,
149 | trademark, attribution notices, disclaimers of warranty, or limitations
150 | of liability ("notices") contained within the Program from any copy of
151 | the Program which they Distribute, provided that Contributors may add
152 | their own appropriate notices.
153 |
154 | 4. COMMERCIAL DISTRIBUTION
155 |
156 | Commercial distributors of software may accept certain responsibilities
157 | with respect to end users, business partners and the like. While this
158 | license is intended to facilitate the commercial use of the Program,
159 | the Contributor who includes the Program in a commercial product
160 | offering should do so in a manner which does not create potential
161 | liability for other Contributors. Therefore, if a Contributor includes
162 | the Program in a commercial product offering, such Contributor
163 | ("Commercial Contributor") hereby agrees to defend and indemnify every
164 | other Contributor ("Indemnified Contributor") against any losses,
165 | damages and costs (collectively "Losses") arising from claims, lawsuits
166 | and other legal actions brought by a third party against the Indemnified
167 | Contributor to the extent caused by the acts or omissions of such
168 | Commercial Contributor in connection with its distribution of the Program
169 | in a commercial product offering. The obligations in this section do not
170 | apply to any claims or Losses relating to any actual or alleged
171 | intellectual property infringement. In order to qualify, an Indemnified
172 | Contributor must: a) promptly notify the Commercial Contributor in
173 | writing of such claim, and b) allow the Commercial Contributor to control,
174 | and cooperate with the Commercial Contributor in, the defense and any
175 | related settlement negotiations. The Indemnified Contributor may
176 | participate in any such claim at its own expense.
177 |
178 | For example, a Contributor might include the Program in a commercial
179 | product offering, Product X. That Contributor is then a Commercial
180 | Contributor. If that Commercial Contributor then makes performance
181 | claims, or offers warranties related to Product X, those performance
182 | claims and warranties are such Commercial Contributor's responsibility
183 | alone. Under this section, the Commercial Contributor would have to
184 | defend claims against the other Contributors related to those performance
185 | claims and warranties, and if a court requires any other Contributor to
186 | pay any damages as a result, the Commercial Contributor must pay
187 | those damages.
188 |
189 | 5. NO WARRANTY
190 |
191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
196 | PURPOSE. Each Recipient is solely responsible for determining the
197 | appropriateness of using and distributing the Program and assumes all
198 | risks associated with its exercise of rights under this Agreement,
199 | including but not limited to the risks and costs of program errors,
200 | compliance with applicable laws, damage to or loss of data, programs
201 | or equipment, and unavailability or interruption of operations.
202 |
203 | 6. DISCLAIMER OF LIABILITY
204 |
205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
213 | POSSIBILITY OF SUCH DAMAGES.
214 |
215 | 7. GENERAL
216 |
217 | If any provision of this Agreement is invalid or unenforceable under
218 | applicable law, it shall not affect the validity or enforceability of
219 | the remainder of the terms of this Agreement, and without further
220 | action by the parties hereto, such provision shall be reformed to the
221 | minimum extent necessary to make such provision valid and enforceable.
222 |
223 | If Recipient institutes patent litigation against any entity
224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the
225 | Program itself (excluding combinations of the Program with other software
226 | or hardware) infringes such Recipient's patent(s), then such Recipient's
227 | rights granted under Section 2(b) shall terminate as of the date such
228 | litigation is filed.
229 |
230 | All Recipient's rights under this Agreement shall terminate if it
231 | fails to comply with any of the material terms or conditions of this
232 | Agreement and does not cure such failure in a reasonable period of
233 | time after becoming aware of such noncompliance. If all Recipient's
234 | rights under this Agreement terminate, Recipient agrees to cease use
235 | and distribution of the Program as soon as reasonably practicable.
236 | However, Recipient's obligations under this Agreement and any licenses
237 | granted by Recipient relating to the Program shall continue and survive.
238 |
239 | Everyone is permitted to copy and distribute copies of this Agreement,
240 | but in order to avoid inconsistency the Agreement is copyrighted and
241 | may only be modified in the following manner. The Agreement Steward
242 | reserves the right to publish new versions (including revisions) of
243 | this Agreement from time to time. No one other than the Agreement
244 | Steward has the right to modify this Agreement. The Eclipse Foundation
245 | is the initial Agreement Steward. The Eclipse Foundation may assign the
246 | responsibility to serve as the Agreement Steward to a suitable separate
247 | entity. Each new version of the Agreement will be given a distinguishing
248 | version number. The Program (including Contributions) may always be
249 | Distributed subject to the version of the Agreement under which it was
250 | received. In addition, after a new version of the Agreement is published,
251 | Contributor may elect to Distribute the Program (including its
252 | Contributions) under the new version.
253 |
254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
255 | receives no rights or licenses to the intellectual property of any
256 | Contributor under this Agreement, whether expressly, by implication,
257 | estoppel or otherwise. All rights in the Program not expressly granted
258 | under this Agreement are reserved. Nothing in this Agreement is intended
259 | to be enforceable by any entity that is not a Contributor or Recipient.
260 | No third-party beneficiary rights are created under this Agreement.
261 |
262 | Exhibit A - Form of Secondary Licenses Notice
263 |
264 | "This Source Code may also be made available under the following
265 | Secondary Licenses when the conditions for such availability set forth
266 | in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
267 | version(s), and exceptions or additional permissions here}."
268 |
269 | Simply including a copy of this Agreement, including this Exhibit A
270 | is not sufficient to license the Source Code under Secondary Licenses.
271 |
272 | If it is not possible or desirable to put the notice in a particular
273 | file, then You may include the notice in a location (such as a LICENSE
274 | file in a relevant directory) where a recipient would be likely to
275 | look for such a notice.
276 |
277 | You may add additional accurate notices of copyright ownership.
278 |
--------------------------------------------------------------------------------
/LICENSE2:
--------------------------------------------------------------------------------
1 | ------------------------------------------
2 | @Okerew, Okral Lesser License 2024
3 | ------------------------------------------
4 |
5 | This software is free and open-source and is released into the public domain.
6 |
7 | Anyone is free to copy, modify, publish, use, compile, or distribute this software, either in source code form or as a compiled binary.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
11 | WHEN CREATING A FORK OF THIS SOFTWARE, YOU MUST USE THE SAME LICENSE. MODIFICATIONS TO THIS LICENSE ARE NOT ALLOWED. YOU MUST CREDIT THE ORIGINAL AUTHOR OF THIS SOFTWARE.
12 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | NOTICE
2 | Copyright 2025 Witold Warchol
3 |
4 | This software is licensed under the Eclipse License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at:
7 |
8 | https://www.eclipse.org/legal/epl-2.0/
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Osxiec
2 |
3 | OSXIEC is a native docker-like solution for macOS developed by Okerew. It leverages native macOS features to provide containerization capabilities, albeit with some limitations compared to Docker.
4 |
5 |
6 |
7 |
8 | If it says that macOS can't identify if it is malware or not, close it go into settings and allow it to be executed.
9 |
10 | ____
11 |
12 | ## Maintaince mode
13 |
14 | With the realease of version 1.0 I won't update osxiec regularly anymore. I had done all the features I wanted in it, but I will fix bugs or any issues listed in the repo's Issues and if apple breaks stuff that osxiec uses I will update it.
15 | ____
16 |
17 |
18 | ## Dependencies
19 | **HomeBrew for installing dependencies**
20 | ```sh
21 | /arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
22 | ```
23 | **Curl**
24 | ```sh
25 | brew install curl
26 | ```
27 | **Readline**
28 | ```sh
29 | brew install readline
30 | ```
31 |
32 | **Json-c**
33 | ```sh
34 | brew install json-c@0.18
35 | ```
36 |
37 | **Xcode**
38 | https://apps.apple.com/us/app/xcode/id497799835?mt=12
39 |
40 | ### Build Dependencies
41 | **Ninja for building**
42 | ```sh
43 | brew install ninja
44 | ```
45 | **Cmake for building**
46 | ``` sh
47 | brew install cmake
48 | ```
49 | ## Installation
50 |
51 | **Quick way with command line**
52 |
53 | Arm architecture
54 | ```sh
55 | curl -L -o osxiec_cli.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli.tar.gz && tar -xvzf osxiec_cli.tar.gz && cd osxiec_cli && sudo sh install.sh
56 | ```
57 | replace %s with the latest version
58 | ______
59 |
60 | 86_64 architecture
61 | ```sh
62 | curl -L -o osxiec_cli_86_64.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_cli_86_64.tar.gz && tar -xvzf osxiec_cli_86_64.tar.gz && cd osxiec_cli_86_64 && sudo sh install.sh
63 | ```
64 | replace %s with the latest version
65 | ____
66 | Gui
67 | ```sh
68 | curl -L -o osxiec_gui.tar.gz https://github.com/Okerew/osxiec/releases/download/%s/osxiec_gui.tar.gz && sudo cp osxiec_gui.tar.gz /Applications
69 | ```
70 | replace %s with the latest version
71 |
72 | ______
73 | 1. **Download the Release**:
74 | Download the `osxiec_cli.tar.gz` and `osxiec_gui.tar.gz` if you want a gui app file from the releases section.
75 | 2. **Extract the Archive**:
76 | ```sh
77 | tar -xvzf osxiec_cli.tar.gz
78 | ```
79 | For gui version
80 | ```sh
81 | tar -xvzf osxiec_gui.tar.gz
82 | ```
83 |
84 | 3. **Run installation script in the extracted cli directory**:
85 | ```sh
86 | sudo sh install.sh
87 | ```
88 | 4. **If downloaded osxiec_gui**
89 | ```
90 | Copy app bundle from osxiec_gui.tar.gz to /Applications then run the app bundle or run osxiec.jar
91 | ```
92 |
93 | To update to a new release, repeat steps 1, 2, and 3 if using osxiec_gui also step 4.
94 | ## Usage
95 |
96 | **Containerize a Directory**: containerizes a directory
97 | ```sh
98 | sudo osxiec -contain {directory_path} {some_name}.bin {path_to_config_file_in_directory_path} {container_config_file}
99 | ```
100 |
101 | **Create a cluster ( virtualized network )** create a cluster
102 | ```sh
103 | sudo osxiec -network create {network_name} {vlan_id} {optional_ip_adress_to_only_connect_to}
104 | ```
105 | **Remove a vlan network** removes a vlan network
106 | ```sh
107 | sudo osxiec -network remove {network_name} {vlan_id}
108 | ```
109 | **Run with vlan config** run with vlan config
110 | ``` sh
111 | sudo osxiec -run {some_name.bin} {network_name} -port {PORT_NUMBER}
112 | ```
113 | port argument is optional
114 |
115 | **Version** checks the current version, updates
116 | ```sh
117 | osxiec --version
118 | ```
119 | **Pull** pulls an image from osxiec hub
120 | ```sh
121 | osxiec -pull {container_name}
122 | ```
123 | **Search** searches osxiec hub
124 | ```sh
125 | osxiec -search {search_term_for_osxiec_hub}
126 | ```
127 | **Upload** allows to upload a file to osxiec hub
128 | ```sh
129 | osxiec -upload {filename} {username} {password} {description}
130 | ```
131 | **Convert to Docker** converts to docker
132 | ```sh
133 | osxiec -convert-to-docker {bin_file} {output_directory} {base_image} [custom_dockerfile]
134 | ```
135 | **Clean** this will clean the container volume images from /tmp
136 | ```sh
137 | sudo osxiec -clean
138 | ```
139 |
140 | **Detach** detaches the container images
141 | ```sh
142 | osxiec -detach
143 | ```
144 |
145 | **Help** shows all the commands
146 | ```sh
147 | osxiec -help
148 | ```
149 |
150 | **Deploy** deploys a container
151 | ```sh
152 | sudo osxiec -deploy {path_to_config} -port {PORT_NUMBER}
153 | ```
154 |
155 | **Scan** scans a container for security vulnerabilities
156 | ```sh
157 | osxiec -scan {some_name.bin}
158 | ```
159 |
160 | **Deploym** deploys multiple containers, this is a work in progress, for now it works mostly fine with start config
161 | ```sh
162 | sudo osxiec -deploym {config_file}
163 | ```
164 |
165 | **Oexec** executes a container in offline mode without any networking or usage of ports
166 | ```sh
167 | sudo osxiec -oexec {bin_file_path}
168 | ```
169 | **Gexec** executes a container in gui mode
170 | ```sh
171 | sudo osxiec -gexec {bin_file_path}
172 | ```
173 | **Extract** extracts files and folders from a container
174 | ```sh
175 | sudo osxiec -extract {bin_file_path}
176 | ```
177 |
178 | **Convert to oci** converts to an oci container
179 | ```sh
180 | osxiec -convert-to-oci {bin_file_path} {output_path} {arch} {author}
181 | ```
182 |
183 | **Craft** crafts a container from a directory and a bin input file
184 | ```sh
185 | sudo osxiec -craft {directory_path} {bin_input_file} {output_file} {start_config_file} {container_config_file}
186 | ```
187 |
188 | **Start** starts a container a stopped container
189 | ```sh
190 | sudo osxiec -start {volume_name} {network} -port {PORT_NUMBER}
191 | ```
192 | port is optional
193 |
194 | **Ostart** starts a container a stopped container in offline mode
195 | ```sh
196 | sudo osxiec -ostart {volume_name}
197 | ```
198 | **Gstart** starts a container a stopped container in gui mode
199 | ```sh
200 | sudo osxiec -gstart {volume_name}
201 | ```
202 | **Api** an api that exposes some more functions of the cli
203 | ```sh
204 | osxiec -api {argument}
205 | ```
206 |
207 | **Update** updates configuration of a container
208 | ```sh
209 | sudo osxiec -update
210 | ```
211 |
212 | **Check for update** checks for update
213 | ```sh
214 | sudo osxiec -check-for-update
215 | ```
216 | **copy-volume** Copies files form a container volume to a directory
217 | ```sh
218 | sudo osxiec -copy-volume {volume_name} {target_directory}
219 | ```
220 |
221 | **bcn** Broadcasts a command to the container network
222 | ```sh
223 | sudo osxiec -bcn {network_name} {command}
224 | ```
225 |
226 | ## Creating a container
227 | Make sure to include any dependencies or executables you can obtain these by searching for where a dependency or executable is located and copying it along with it's dependencies.
228 |
229 |
230 | **Example structure**
231 | ```
232 | [
233 | "container_name",
234 | {
235 | "some_content",
236 | "subfolder1" [
237 | executable1
238 | "some_content"
239 | ]
240 | "start_config_file"
241 | "some_executable"
242 | "dependency"
243 | }
244 |
245 | ]
246 | ```
247 |
248 |
249 | **Script Example:**
250 | For example if you have a node project make sure to include somewhere the node executable in the directory you want to contain, then run the script with the node executable.
251 |
252 | ``` js
253 | console.log("Hello World")
254 | ```
255 |
256 | Do `path/to/node_executable_in_container/ script.js`
257 |
258 |
259 |
260 | **Containing**
261 |
262 | To contain a directory run the `osxiec -contain {directory_path} {container_name} {start_config_file}`
263 |
264 | This will also scan the container for security vulnerabilities.
265 | ## Executing a container
266 | After creating a container or downloading one.
267 | **You can execute one with**
268 |
269 | `osxiec -oexec {container_name}`
270 |
271 | This will execute it in offline mode. If you want to execute it in online mode see the next point.
272 |
273 | **Run with vlan**
274 |
275 | If you have created a vlan network like said in the next point you can run the container with
276 |
277 | ```osxiec -run {container_name} {network_name}```
278 |
279 | You can also add the port argument with `-port {PORT_NUMBER}`
280 |
281 | **When executing**
282 |
283 | Normally it will start a listener which will allow it to communicate with other containers.
284 | 
285 |
286 | If you want to access the container terminal just press enter.
287 | 
288 |
289 |
290 | ## Container commands
291 | 1. **help** shows all the commands
292 | 2. **debug** debugs the container
293 | 3. **scale** scales the resources of the container
294 | 4. **osxs** Execute an osxs script file
295 | 5. **xs** Execute an osxs script command
296 | 6. **autoscale** automatically scales the resources of the container
297 | 7. **status** shows the status of the container
298 | 8. **stop** stops the container.
299 | 9. **wait** waits for a command(background task) in a container to stop.
300 | 10. **ps** shows all the running commands(background tasks) in the container.
301 | 11. **pause** pauses all background tasks in the container
302 | 12. **unpause** unpauses all background tasks in the container
303 | 13. **br** runs a command in background
304 | 14 **network start** starts a network listener
305 | 15 **network stop** stops a network listener
306 | 16 **network status** shows the status of a network listener
307 | 17 **network restart** restarts a network listener
308 | 18 **trace** shows the trace of a command
309 | 19 **proctrace** Trace system calls of a running
310 | 20 **attach** Attach to a running background process
311 | 21 **schedule** Schedule a command to run at a specific time
312 | 22 **lschedule** List scheduled commands
313 |
314 | ## Creating a vlan network
315 | To create a network you can run `osxiec -network create {network_name} {vlan_id} {optional_ip_adress_to_only_connect_to}`
316 |
317 | The network_name can be any string like "test" for example.
318 |
319 | The vlan id can be any number from 1-4094.
320 |
321 | For example `osxiec -network create test 6`
322 | ## Converting to Docker
323 | **Create docker file**
324 | ``` dockerfile
325 | # Test Dockerfile for osxiec to Docker conversion
326 |
327 | # Use a lightweight base image
328 | FROM alpine:latest
329 |
330 | # Set the working directory in the container
331 | WORKDIR /app
332 |
333 | # Copy the application files from the osxiec container
334 | # Note: This will be handled by the conversion script, so we don't need COPY instructions here
335 |
336 | # Install any needed packages
337 | RUN apk add --no-cache python3 py3-pip
338 |
339 | # Set environment variables (these will be overwritten by the conversion script if not using a custom Dockerfile)
340 | ENV MEMORY_SOFT_LIMIT=256m
341 | ENV MEMORY_HARD_LIMIT=512m
342 | ENV CPU_PRIORITY=20
343 |
344 | # Make port 8080 available to the world outside this container
345 | EXPOSE 8080
346 |
347 | # Run a simple Python HTTP server when the container launches
348 | CMD ["python3", "-m", "http.server", "8080"]
349 | ```
350 | **Run convert-to-docker**
351 |
352 | For this example
353 | ```sh
354 | osxiec -convert-to-docker {container_name} {output_directory} alpine:latest samples/dockerfile
355 | ```
356 |
357 | ## Deploying
358 | **Create a config file**
359 | ```
360 | source_dir=/path/to/source/directory
361 | container_file=/path/to/output/container.bin
362 | network_name=my_network
363 | start_config=/path/to/start_config.sh
364 | ```
365 | **Deploy**
366 | ```sh
367 | sudo osxiec -deploy {path_to_config_file}
368 | ```
369 | You can also use `-port {PORT_NUMBER}`
370 |
371 | **Deploym**
372 | you can also deploy multiple containers, this is a work in progress though.
373 | ```sh
374 | sudo osxiec -deploym {config_file} {PORT_NUMBER1} {PORT_NUMBER2} etc.
375 | ```
376 | **Config**
377 |
378 | ```
379 | path_to_container1_config {network_name}
380 | path_to_container2_config {network_name}
381 | ```
382 |
383 | ## Osxiec Script
384 | Osxiec script is a scripting language created for managing osxiec containers.
385 | ### Syntax
386 | **Set Memory**
387 | ```sh
388 | SET_MEMORY {memory_soft} {memory_hard}
389 | ```
390 | **Set CPU Priority**
391 | ```sh
392 | SET_CPU {cpu_priority}
393 | ```
394 | **Execute**
395 | ```sh
396 | EXECUTE {command}
397 | ```
398 | **Conditional Execution**
399 | ```sh
400 | IF {condition} EXECUTE {command}
401 | ```
402 | **Sleep**
403 | ```sh
404 | SLEEP {seconds}
405 | ```
406 | **Log**
407 | ```sh
408 | LOG {message}
409 | ```
410 | **Execute File**
411 | ```sh
412 | EXECUTE_FILE {path_to_script}
413 | ```
414 | **Set Variable**
415 | ```sh
416 | SET {variable} {value}
417 | ```
418 | **While Loop**
419 | ```sh
420 | WHILE {condition} {commands} END
421 | ```
422 | **For Loop**
423 | ```sh
424 | FOR {variable} TO {2variable} STEP {value} {commands} END
425 | ```
426 |
427 | **ELSE**
428 | ```sh
429 | IF {condition} ELSE {commands} END
430 | ```
431 | Note ELSE statement for now doesn't work with LOG and is a work in progress
432 |
433 | **Example**
434 | ```
435 | # This is an example script for the OSXIEC scripting language
436 | SET counter 0
437 | SET limit 10
438 |
439 | # Loop from 0 to 10
440 | FOR counter=0 TO limit STEP 2
441 | IF counter==5
442 | LOG "Counter is 5"
443 | END
444 | SLEEP 1
445 | END
446 |
447 | IF $var==5 LOG Variable is 5 ELSE LOG Variable is not 5
448 |
449 | # Loop while counter is less than limit
450 | WHILE counter
487 | To start debugging `debug`
488 |
489 | Then you can use these commands
490 | ___
491 | ```
492 | step
493 | ```
494 | which steps to the next command
495 | ___
496 | ```
497 | break
498 | ```
499 | Creates a breakpoint at the specified command
500 | ___
501 | ```
502 | print
503 | ```
504 | Prints the value of the specified variable of the container
505 | ___
506 | ```
507 | print
508 | ```
509 | Prints the whole container state
510 | ___
511 | ```
512 | help
513 | ```
514 | Shows what you can do
515 | ___
516 | ```
517 | continue
518 | ```
519 | Continues execution of the container
520 | _____
521 | ### Osxiec Container Hub
522 | This is a place where you can upload your containers to.
523 | 
524 |
525 | ___
526 | ### Osxiec Gui
527 | This is the source code for the gui version of osxiec.
528 | 
529 |
530 | ___
531 | ### Osxiec Terminal
532 | A terminal emulator specifically created for osxiec
533 | ___
534 | ## Building
535 | **Git clone the repository**
536 | ``` sh
537 | git clone https://github.com/Okerew/osxiec.git
538 | ```
539 | **Go to the directory**
540 | ``` sh
541 | cd osxiec
542 | ```
543 | **Build the osxiec executable**
544 | ``` sh
545 | mkdir {build-directory}
546 | cd {build-directory}
547 | cmake -S .. -B . -G "Ninja"
548 | ninja
549 | ```
550 |
551 | **Give permissions to scripts if needed**
552 | ``` sh
553 | sudo chmod +x scripts/osxiec_deploy_multiple.sh
554 | sudo chmod +x scripts/install.sh
555 | ```
556 | **Finalize**
557 | to make it work put all executables in a one folder, copy there install.sh and run it
558 |
559 | ### Build java gui
560 | Git clone the gui
561 | ```sh
562 | git clone https://github.com/Okerew/osxiec_gui.git
563 | ```
564 |
565 | Go to the directory
566 | ```sh
567 | cd osxiec_gui
568 | ```
569 | Build the class
570 | ```sh
571 | javac OsxiecApp.java
572 | ```
573 | Build the jar
574 | ```sh
575 | jar -cvfe osxiec.jar OsxiecApp OsxiecApp.class
576 | ```
577 |
578 | Copy jar into app bundle, remove the previous one
579 | ```sh
580 | cp osxiec.jar osxiec.app/Contents/Resources
581 | ```
582 | If using the one from release, delete the previous one
583 |
584 | Copy the icon into Contents/Resources
585 |
586 | Finally, copy the run_app_bundle.sh into the bundle as osxiec_gui
587 | ```sh
588 | cp run_app_bundle.sh osxiec.app/Contents/MacOS/osxiec_gui
589 | ```
590 | ## Plugins
591 | **Example plugin** can be seen in samples/sample_plugin.c, this should make you understand how osxiec loads plugins.
592 |
593 | **Build plugin**
594 | ``` sh
595 | gcc -shared -fPIC -o {plugin_name}.so {plugin_name}.c
596 | ```
597 |
598 | **Install plugin**
599 |
600 | ``` sh
601 | sudo cp {plugin_name}.so ~/.osxiec/plugins
602 | ```
603 | After this on the execution of osxiec command the plugin will be loaded.
604 | ## Notes
605 |
606 | - **Not a Docker Replacement**:
607 | While OSXIEC offers similar functionality to Docker, it lacks some advanced features of Docker. It is more supposed to be a quicker testing tool than docker on macOS, it is not designed to replace it, just to test basic ideas and software, distribute macOS software.
608 | - **macOS Only**:
609 | OSXIEC uses native macOS features and is not compatible with other operating systems.
610 | - **Isolation Limitations**:
611 | Due to macOS limitations, complete isolation like in Linux is not possible. The contained directory will have some access to the outside environment, you can have a start config file if needed.
612 | - **Supported Features**:
613 | Despite its limitations, OSXIEC provides isolation using namespaces, setuid, image layers, basic user process control, memory and CPU control, and special permissions using user IDs and group IDs, unpacking the image into a disk image(APFS), vlans.
614 | - **Support**: Remember that not everything will work fully for example node won't work fully because it is making sys calls which spawn things outside the container, in this example local things that do not rely on the repl server will work.
615 | - **Temps**: If you need a lot of storage for the moment, and you used a container use the clean command.
616 | - **Why is chroot not used?**
617 | Chroot requires for SIP to be disabled, which causes many security risks, chroot can be easily exited by any process, using the normal macOS restrictions is way more secure, reliable,
618 | having it disabled causes many permission issues.
619 | - **Sandbox deprecation error** yes I know that sandbox innit is deprecated but there isn't really an alternative for it unless I would use xcode and there is no way I am using it to rebuild this.
620 |
621 | - **If you want to remove ips from the network do it by editing the file in private/etc/network_{network_name}.conf file**
622 |
--------------------------------------------------------------------------------
/api_for_osxiec_script.h:
--------------------------------------------------------------------------------
1 | #ifndef OSXIEC_H
2 | #define OSXIEC_H
3 |
4 | void execute_command(const char *command, const char *container_root);
5 |
6 | void scale_container_resources(int soft_limit, int hard_limit,
7 | int cpu_priority);
8 |
9 | #endif // OSXIEC_H
10 |
--------------------------------------------------------------------------------
/example/bin/hello:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Okerew/osxiec/bdfc395b014851802ecd66e17fbb0ea75dc947d3/example/bin/hello
--------------------------------------------------------------------------------
/example/config.txt:
--------------------------------------------------------------------------------
1 | echo test
2 |
--------------------------------------------------------------------------------
/example/container_config.txt:
--------------------------------------------------------------------------------
1 | name=my_custom_container
2 | memory_soft_limit=59870912
3 | memory_hard_limit=1073741824
4 | cpu_priority=10
5 | network_mode=bridge
6 | container_uid=1001
7 | container_gid=1001
8 | dependencies=neofetch
9 |
--------------------------------------------------------------------------------
/example/file.txt:
--------------------------------------------------------------------------------
1 | test
--------------------------------------------------------------------------------
/example/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | echo "Hello from inside the container!"
3 |
--------------------------------------------------------------------------------
/example/test.c:
--------------------------------------------------------------------------------
1 | #include
2 | int main() {
3 | // printf() displays the string inside quotation
4 | printf("Hello, World!");
5 | return 0;
6 | }
7 |
--------------------------------------------------------------------------------
/example/test/test.osxs:
--------------------------------------------------------------------------------
1 | # This is an example script for the OSXIEC scripting language
2 | SET counter 0
3 | SET limit 10
4 |
5 | # Loop from 0 to 10
6 | FOR counter=0 TO limit STEP 2
7 | IF counter==5
8 | LOG "Counter is 5"
9 | END
10 | SLEEP 1
11 | END
12 |
13 | IF $var==5 LOG Variable is 5 ELSE LOG Variable is not 5
14 |
15 | # Loop while counter is less than limit
16 | WHILE counter
4 | #include
5 | #include
6 |
7 | #define MAX_PATH_LEN 256
8 | #define MAX_SECRETS 64
9 | #define MAX_VAR_LEN 1024
10 | #define MAX_COMMAND_LEN 1024
11 | typedef struct {
12 | char name[MAX_PATH_LEN];
13 | size_t size;
14 | char *data;
15 | } File;
16 |
17 | #define MAX_CLIENTS 15
18 |
19 | typedef struct {
20 | char name[MAX_PATH_LEN];
21 | long memory_soft_limit;
22 | long memory_hard_limit;
23 | int cpu_priority;
24 | char network_mode[20];
25 | uid_t container_uid;
26 | gid_t container_gid;
27 | char network_name[MAX_PATH_LEN];
28 | int vlan_id;
29 | char start_config[MAX_PATH_LEN];
30 | } ContainerConfig;
31 |
32 | typedef struct {
33 | char name[MAX_PATH_LEN];
34 | int vlan_id;
35 | int num_containers;
36 | char container_names[MAX_CLIENTS][MAX_PATH_LEN];
37 | char container_ips[MAX_CLIENTS][16];
38 | } ContainerNetwork;
39 |
40 | typedef struct {
41 | char name[MAX_PATH_LEN];
42 | char data[4096];
43 | char audit_data[2048];
44 | char outdated_data[1024];
45 | } BrewInfo;
46 |
47 | typedef struct {
48 | char name[MAX_VAR_LEN];
49 | char *encrypted_value;
50 | size_t length;
51 | } SecretVariable;
52 |
53 | typedef struct {
54 | char current_directory[MAX_PATH_LEN];
55 | char last_executed_command[MAX_COMMAND_LEN];
56 | int num_processes;
57 | long memory_usage;
58 | char network_status[50];
59 | char **environment_variables;
60 | int num_env_vars;
61 | SecretVariable secrets[MAX_SECRETS];
62 | int num_secrets;
63 | } ContainerState;
64 |
65 | void execute_command(const char *command, const char *container_root);
66 | void containerize_directory_with_bin_file(const char *dir_path,
67 | const char *input_bin_file,
68 | const char *output_file,
69 | const char *start_config_file,
70 | const char *container_config_file);
71 | void containerize_directory(const char *dir_path, const char *output_file,
72 | const char *start_config_file,
73 | const char *container_config_file);
74 | void read_config_file(const char *filename, ContainerConfig *config);
75 | void extract_container(const char *osxiec_file, const char *output_dir);
76 | void security_scan(const char *bin_file);
77 | ContainerNetwork load_container_network(const char *name);
78 | void deploy_container(const char *config_file, int deploy_port);
79 | void apply_resource_limits(const ContainerConfig *config);
80 | void *monitor_memory_usage(void *arg);
81 | void setup_pf_rules(ContainerNetwork *network);
82 | void create_and_save_container_network(const char *name, int vlan_id);
83 | void remove_container_network(const char *name);
84 | void auto_scale_resources(const ContainerConfig *config);
85 | void start_auto_scaling(ContainerConfig *config);
86 | void handle_client(int client_socket, const char *container_root);
87 | void start_network_listener(const char *container_root);
88 | void create_isolated_environment(FILE *bin_file, const char *bin_file_path,
89 | ContainerNetwork *network);
90 | void ocreate_isolated_environment(FILE *bin_file, const char *bin_file_path);
91 | static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb,
92 | void *userp);
93 | void search(const char *term);
94 | void download_file(const char *file_name);
95 | void upload_file(const char *filename, const char *username,
96 | const char *password, const char *description);
97 | void convert_to_docker(const char *osxiec_file, const char *output_dir,
98 | const char *base_image, const char *custom_dockerfile);
99 | void clean_container_dmgs();
100 | void convert_to_oci(const char *osxiec_file, const char *output_dir,
101 | const char *arch, const char *author, const char *created);
102 | char *find_latest_bin_file(const char *volume_name);
103 | int copy_file(const char *source, const char *destination_folder,
104 | const char *new_file_name);
105 | size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp);
106 | char *fetch_latest_version(void);
107 | int compare_versions(const char *v1, const char *v2);
108 | int add_plugin(const char *plugin_source);
109 | int copy_file(const char *source, const char *destination_folder,
110 | const char *new_file_name);
111 | char *find_latest_bin_file(const char *volume_name);
112 | void create_directory_if_needed(const char *path);
113 | void handle_signal(int sig);
114 | void *logger_thread(void *arg);
115 | void signal_handler();
116 | void navigate_history(char *command, int *command_index, int *cursor_pos,
117 | int direction);
118 | void add_to_history(const char *command);
119 | void clear_line();
120 | void move_cursor_right(int n);
121 | void move_cursor_left(int n);
122 | void set_terminal_canonical_mode();
123 | void set_terminal_raw_mode();
124 | void detach_container_images(const char *volume_name);
125 | void print_current_resource_usage(ContainerConfig *config);
126 | void start_auto_scaling(ContainerConfig *config);
127 | double get_cpu_usage();
128 | void create_directories(const char *file_path);
129 | void create_shared_folder();
130 | void handle_script_file(const char *filename);
131 | void handle_script_command(const char *script_content);
132 | void scale_container_resources(long memory_soft_limit, long memory_hard_limit,
133 | int cpu_priority);
134 | void enable_container_communication(ContainerNetwork *network);
135 | void setup_network_isolation(ContainerConfig *config,
136 | ContainerNetwork *network);
137 | char *get_ip_address();
138 | void execute_start_config(const char *config_file, const char *container_root);
139 | int is_subpath(const char *path, const char *base);
140 | void handle_debug_command(char *command);
141 | void print_container_state();
142 | void update_container_state();
143 | void broadcast_command_to_network(const char *network_name, const char *command,
144 | int port);
145 | int copy_volume_to_directory(const char *volume_name, const char *target_dir);
146 | void update_container_config(const char *container_file,
147 | const char *new_config_file);
148 | int remove_plugin(const char *plugin_name);
149 | int link_system_directories(const char *container_root);
150 | int process_dependencies_recursive(const char *dep_name,
151 | const char *homebrew_prefix, File *files,
152 | int *file_count, int max_files,
153 | char processed[][MAX_PATH_LEN],
154 | int *processed_count);
155 | int process_dependency(const char *dep_name, const char *homebrew_prefix,
156 | File *files, int *file_count, int max_files);
157 | int get_brew_info(const char *package_name, BrewInfo *info);
158 | const char *get_homebrew_prefix();
159 | void analyze_security_findings(const BrewInfo *info);
160 | int parse_brew_dependencies(const char *brew_info_data,
161 | char deps[][MAX_PATH_LEN], int max_deps);
162 | int file_exists(const char *path);
163 | int copy_single_file(const char *src_path, const char *dest_path, File *files,
164 | int *file_count, int max_files);
165 |
166 | int copy_path(const char *src_path, const char *dest_path, File *files,
167 | int *file_count, int max_files);
168 | void handle_secret_command(const char *args);
169 | void handle_getsecret_command(const char *args);
170 | void save_container_state(FILE *state_file, const ContainerConfig *config,
171 | const ContainerState *state);
172 | void clean_container_plists();
173 | void cleanup_all_container_users(void);
174 | #endif // OSXIEC_H
175 |
--------------------------------------------------------------------------------
/osxiec_script/osxiec_script.c:
--------------------------------------------------------------------------------
1 | #include "osxiec_script.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "../api_for_osxiec_script.h"
9 |
10 | #define MAX_VARIABLES 100
11 | #define MAX_VAR_NAME 50
12 | #define MAX_VAR_VALUE 255
13 | #define SCRIPT_EXTENSION ".osxs"
14 |
15 | // Structure to hold variables
16 | typedef struct {
17 | char name[MAX_VAR_NAME];
18 | char value[MAX_VAR_VALUE];
19 | } Variable;
20 |
21 | Variable variables[MAX_VARIABLES];
22 | int variable_count = 0;
23 |
24 | void set_variable(const char *name, const char *value);
25 | char *get_variable(const char *name);
26 | void replace_variables(char *line);
27 | int evaluate_condition(const char *condition);
28 |
29 | void execute_script(const char *script) {
30 | char *line = strtok((char *)script, "\n");
31 | while (line != NULL) {
32 | // Skip comments and empty lines
33 | if (line[0] != '#' && line[0] != '\0') {
34 | replace_variables(line);
35 | execute_cs_command(line);
36 | }
37 | line = strtok(NULL, "\n");
38 | }
39 | }
40 |
41 | void execute_script_file(const char *filename) {
42 | // Check if the filename ends with .osxs
43 | size_t len = strlen(filename);
44 | size_t ext_len = strlen(SCRIPT_EXTENSION);
45 | if (len <= ext_len ||
46 | strcmp(filename + len - ext_len, SCRIPT_EXTENSION) != 0) {
47 | printf("Error: Invalid file extension. Expected %s\n", SCRIPT_EXTENSION);
48 | return;
49 | }
50 |
51 | FILE *file = fopen(filename, "r");
52 | if (file == NULL) {
53 | printf("Error: Unable to open file %s\n", filename);
54 | return;
55 | }
56 |
57 | char line[MAX_COMMAND_LEN];
58 | while (fgets(line, sizeof(line), file)) {
59 | // Remove newline character if present
60 | size_t line_len = strlen(line);
61 | if (line_len > 0 && line[line_len - 1] == '\n') {
62 | line[line_len - 1] = '\0';
63 | }
64 |
65 | // Skip comments and empty lines
66 | if (line[0] != '#' && line[0] != '\0') {
67 | execute_cs_command(line);
68 | }
69 | }
70 |
71 | fclose(file);
72 | }
73 |
74 | void execute_for_loop(char args[MAX_ARGS][MAX_COMMAND_LEN], int arg_count) {
75 | char var_name[MAX_VAR_NAME];
76 | int start, end, step;
77 | char loop_body[MAX_COMMAND_LEN] = "";
78 |
79 | sscanf(args[0], "%[^=]=", var_name);
80 | start = atoi(strchr(args[0], '=') + 1);
81 | end = atoi(args[2]);
82 | step = (arg_count > 5 && strcmp(args[3], "STEP") == 0) ? atoi(args[4]) : 1;
83 |
84 | int body_start = (arg_count > 5 && strcmp(args[3], "STEP") == 0) ? 5 : 3;
85 | for (int i = body_start; i < arg_count; i++) {
86 | strcat(loop_body, args[i]);
87 | if (i < arg_count - 1)
88 | strcat(loop_body, " ");
89 | }
90 |
91 | char value[MAX_VAR_VALUE];
92 | for (int i = start; i < end; i += step) {
93 | snprintf(value, sizeof(value), "%d", i);
94 | set_variable(var_name, value);
95 | execute_cs_command(loop_body);
96 | }
97 | }
98 |
99 | void execute_while_loop(char args[MAX_ARGS][MAX_COMMAND_LEN], int arg_count) {
100 | char condition[MAX_COMMAND_LEN];
101 | char loop_body[MAX_COMMAND_LEN] = "";
102 |
103 | strcpy(condition, args[0]);
104 | for (int i = 1; i < arg_count; i++) {
105 | strcat(loop_body, args[i]);
106 | if (i < arg_count - 1)
107 | strcat(loop_body, " ");
108 | }
109 |
110 | while (evaluate_condition(condition)) {
111 | execute_cs_command(loop_body);
112 | }
113 | }
114 |
115 | void execute_cs_command(const char *command) {
116 | char cmd[MAX_COMMAND_LEN];
117 | char args[MAX_ARGS][MAX_COMMAND_LEN];
118 | int arg_count = 0;
119 |
120 | // Parse command and arguments
121 | sscanf(command, "%s", cmd);
122 | char *arg_start = strchr(command, ' ');
123 | if (arg_start != NULL) {
124 | arg_start++; // Move past the space
125 | char *token = strtok((char *)arg_start, " ");
126 | while (token != NULL && arg_count < MAX_ARGS) {
127 | strcpy(args[arg_count++], token);
128 | token = strtok(NULL, " ");
129 | }
130 | }
131 |
132 | // Convert to uppercase for case-insensitive comparison
133 | for (int i = 0; cmd[i]; i++)
134 | cmd[i] = toupper(cmd[i]);
135 |
136 | // Execute appropriate function based on command
137 | if (strcmp(cmd, "SET_MEMORY") == 0 && arg_count == 2) {
138 | long soft_limit = atol(args[0]);
139 | long hard_limit = atol(args[1]);
140 | scale_container_resources(soft_limit, hard_limit,
141 | -1); // -1 for unchanged CPU priority
142 | } else if (strcmp(cmd, "SET_CPU") == 0 && arg_count == 1) {
143 | int cpu_priority = atoi(args[0]);
144 | scale_container_resources(-1, -1,
145 | cpu_priority); // -1 for unchanged memory limits
146 | } else if (strcmp(cmd, "EXECUTE") == 0 && arg_count >= 1) {
147 | char full_command[MAX_COMMAND_LEN] = "";
148 | for (int i = 0; i < arg_count; i++) {
149 | strcat(full_command, args[i]);
150 | if (i < arg_count - 1)
151 | strcat(full_command, " ");
152 | }
153 | execute_command(full_command, NULL);
154 | } else if (strcmp(cmd, "LOG") == 0 && arg_count >= 1) {
155 | char message[MAX_COMMAND_LEN] = "";
156 | for (int i = 0; i < arg_count; i++) {
157 | strcat(message, args[i]);
158 | if (i < arg_count - 1)
159 | strcat(message, " ");
160 | }
161 | printf("LOG: %s\n", message);
162 | } else if (strcmp(cmd, "EXECUTE_FILE") == 0 && arg_count == 1) {
163 | execute_script_file(args[0]);
164 | } else if (strcmp(cmd, "SET") == 0 && arg_count == 2) {
165 | set_variable(args[0], args[1]);
166 | } else if (strcmp(cmd, "IF") == 0 && arg_count >= 3) {
167 | char true_command[MAX_COMMAND_LEN] = "";
168 | char false_command[MAX_COMMAND_LEN] = "";
169 | int else_index = -1;
170 |
171 | // Find the ELSE keyword
172 | for (int i = 1; i < arg_count; i++) {
173 | if (strcmp(args[i], "ELSE") == 0) {
174 | else_index = i;
175 | break;
176 | }
177 | }
178 |
179 | // Construct true and false commands
180 | if (else_index != -1) {
181 | for (int i = 1; i < else_index; i++) {
182 | strcat(true_command, args[i]);
183 | if (i < else_index - 1)
184 | strcat(true_command, " ");
185 | }
186 | for (int i = else_index + 1; i < arg_count; i++) {
187 | strcat(false_command, args[i]);
188 | if (i < arg_count - 1)
189 | strcat(false_command, " ");
190 | }
191 | } else {
192 | for (int i = 1; i < arg_count; i++) {
193 | strcat(true_command, args[i]);
194 | if (i < arg_count - 1)
195 | strcat(true_command, " ");
196 | }
197 | }
198 |
199 | if (evaluate_condition(args[0])) {
200 | execute_cs_command(true_command);
201 | } else if (else_index != -1) {
202 | execute_cs_command(false_command);
203 | }
204 | }
205 |
206 | else if (strcmp(cmd, "SLEEP") == 0 && arg_count == 1) {
207 | int sleep_time = atoi(args[0]);
208 | sleep(sleep_time);
209 | } else if (strcmp(cmd, "FOR") == 0 && arg_count >= 5) {
210 | execute_for_loop(args, arg_count);
211 | } else if (strcmp(cmd, "WHILE") == 0 && arg_count >= 2) {
212 | execute_while_loop(args, arg_count);
213 | }
214 | }
215 |
216 | void set_variable(const char *name, const char *value) {
217 | for (int i = 0; i < variable_count; i++) {
218 | if (strcmp(variables[i].name, name) == 0) {
219 | strcpy(variables[i].value, value);
220 | return;
221 | }
222 | }
223 | if (variable_count < MAX_VARIABLES) {
224 | strcpy(variables[variable_count].name, name);
225 | strcpy(variables[variable_count].value, value);
226 | variable_count++;
227 | } else {
228 | printf("Error: Maximum number of variables reached\n");
229 | }
230 | }
231 |
232 | char *get_variable(const char *name) {
233 | for (int i = 0; i < variable_count; i++) {
234 | if (strcmp(variables[i].name, name) == 0) {
235 | return variables[i].value;
236 | }
237 | }
238 | return NULL;
239 | }
240 |
241 | void replace_variables(char *line) {
242 | char *var_start, *var_end;
243 | char var_name[MAX_VAR_NAME];
244 | char *var_value;
245 | char new_line[MAX_COMMAND_LEN];
246 |
247 | while ((var_start = strchr(line, '$')) != NULL) {
248 | var_end = var_start + 1;
249 | while (*var_end && (isalnum(*var_end) || *var_end == '_'))
250 | var_end++;
251 |
252 | strncpy(var_name, var_start + 1, var_end - var_start - 1);
253 | var_name[var_end - var_start - 1] = '\0';
254 |
255 | var_value = get_variable(var_name);
256 | if (var_value != NULL) {
257 | strncpy(new_line, line, var_start - line);
258 | new_line[var_start - line] = '\0';
259 | strcat(new_line, var_value);
260 | strcat(new_line, var_end);
261 | strcpy(line, new_line);
262 | } else {
263 | // Variable not found, move past this occurrence
264 | line = var_end;
265 | }
266 | }
267 | }
268 |
269 | int evaluate_condition(const char *condition) {
270 | char var_name[MAX_VAR_NAME];
271 | char operator[3];
272 | char expected_value[MAX_VAR_VALUE];
273 |
274 | sscanf(condition, "%s%2s%s", var_name, operator, expected_value);
275 |
276 | char *actual_value = get_variable(var_name);
277 | if (actual_value == NULL)
278 | return 0;
279 |
280 | int actual_int = atoi(actual_value);
281 | int expected_int = atoi(expected_value);
282 |
283 | if (strcmp(operator, "==") == 0)
284 | return strcmp(actual_value, expected_value) == 0;
285 | if (strcmp(operator, "!=") == 0)
286 | return strcmp(actual_value, expected_value) != 0;
287 | if (strcmp(operator, "<") == 0)
288 | return actual_int < expected_int;
289 | if (strcmp(operator, "<=") == 0)
290 | return actual_int <= expected_int;
291 | if (strcmp(operator, ">") == 0)
292 | return actual_int > expected_int;
293 | if (strcmp(operator, ">=") == 0)
294 | return actual_int >= expected_int;
295 |
296 | return 0; // Invalid operator
297 | }
298 |
--------------------------------------------------------------------------------
/osxiec_script/osxiec_script.h:
--------------------------------------------------------------------------------
1 | #ifndef CONTAINER_SCRIPT_H
2 | #define CONTAINER_SCRIPT_H
3 |
4 | #define MAX_COMMAND_LEN 1024
5 | #define MAX_ARGS 10
6 |
7 | void execute_script(const char *script);
8 | void execute_cs_command(const char *command);
9 | void execute_script_file(const char *filename);
10 | #endif // CONTAINER_SCRIPT_H
--------------------------------------------------------------------------------
/plugin_manager/plugin.h:
--------------------------------------------------------------------------------
1 | #ifndef PLUGIN_H
2 | #define PLUGIN_H
3 |
4 | typedef struct {
5 | const char* name;
6 | const char* version;
7 | int (*initialize)(void);
8 | int (*execute)(const char* command);
9 | int (*cleanup)(void);
10 | } OsxiecPlugin;
11 |
12 | #endif
--------------------------------------------------------------------------------
/plugin_manager/plugin_manager.c:
--------------------------------------------------------------------------------
1 | #include "plugin_manager.h"
2 | #include
3 | #include
4 | #include
5 |
6 | void plugin_manager_init(PluginManager* manager) {
7 | memset(manager, 0, sizeof(PluginManager));
8 | }
9 |
10 | int plugin_manager_load(PluginManager* manager, const char* plugin_path) {
11 | if (manager->plugin_count >= MAX_PLUGINS) {
12 | fprintf(stderr, "Maximum number of plugins reached\n");
13 | return -1;
14 | }
15 |
16 | void* handle = dlopen(plugin_path, RTLD_LAZY);
17 | if (!handle) {
18 | fprintf(stderr, "Cannot open plugin %s: %s\n", plugin_path, dlerror());
19 | return -1;
20 | }
21 |
22 | OsxiecPlugin* plugin = dlsym(handle, "osxiec_plugin");
23 | if (!plugin) {
24 | fprintf(stderr, "Cannot load symbol osxiec_plugin: %s\n", dlerror());
25 | dlclose(handle);
26 | return -1;
27 | }
28 |
29 | if (plugin->initialize && plugin->initialize() != 0) {
30 | fprintf(stderr, "Plugin initialization failed\n");
31 | dlclose(handle);
32 | return -1;
33 | }
34 |
35 | manager->plugins[manager->plugin_count++] = plugin;
36 | printf("Loaded plugin: %s (version %s)\n", plugin->name, plugin->version);
37 | return 0;
38 | }
39 |
40 | int plugin_manager_execute(PluginManager* manager, const char* command) {
41 | for (int i = 0; i < manager->plugin_count; i++) {
42 | if (manager->plugins[i]->execute(command) == 0) {
43 | return 0; // Command was handled by a plugin
44 | }
45 | }
46 | return -1; // No plugin handled the command
47 | }
48 |
49 | void plugin_manager_cleanup(PluginManager* manager) {
50 | for (int i = 0; i < manager->plugin_count; i++) {
51 | if (manager->plugins[i]->cleanup) {
52 | manager->plugins[i]->cleanup();
53 | }
54 | }
55 | manager->plugin_count = 0;
56 | }
--------------------------------------------------------------------------------
/plugin_manager/plugin_manager.h:
--------------------------------------------------------------------------------
1 | #ifndef PLUGIN_MANAGER_H
2 | #define PLUGIN_MANAGER_H
3 |
4 | #include "plugin.h"
5 |
6 | #define MAX_PLUGINS 50
7 |
8 | typedef struct {
9 | OsxiecPlugin* plugins[MAX_PLUGINS];
10 | int plugin_count;
11 | } PluginManager;
12 |
13 | void plugin_manager_init(PluginManager* manager);
14 | int plugin_manager_load(PluginManager* manager, const char* plugin_path);
15 | int plugin_manager_execute(PluginManager* manager, const char* command);
16 | void plugin_manager_cleanup(PluginManager* manager);
17 |
18 | #endif
--------------------------------------------------------------------------------
/samples/Dockerfile:
--------------------------------------------------------------------------------
1 | # Test Dockerfile for osxiec to Docker conversion
2 |
3 | # Use a lightweight base image
4 | FROM alpine:latest
5 |
6 | # Set the working directory in the container
7 | WORKDIR /app
8 |
9 | # Copy the application files from the osxiec container
10 | # Note: This will be handled by the conversion script, so we don't need COPY instructions here
11 |
12 | # Install any needed packages
13 | RUN apk add --no-cache python3 py3-pip
14 |
15 | # Set environment variables (these will be overwritten by the conversion script if not using a custom Dockerfile)
16 | ENV MEMORY_SOFT_LIMIT=256m
17 | ENV MEMORY_HARD_LIMIT=512m
18 | ENV CPU_PRIORITY=20
19 |
20 | # Make port 8080 available to the world outside this container
21 | EXPOSE 8080
22 |
23 | # Run a simple Python HTTP server when the container launches
24 | CMD ["python3", "-m", "http.server", "8080"]
25 |
--------------------------------------------------------------------------------
/samples/container1:
--------------------------------------------------------------------------------
1 | source_dir=example
2 | container_file=test.bin
3 | network_name=test
4 | start_config=config.txt
--------------------------------------------------------------------------------
/samples/container2:
--------------------------------------------------------------------------------
1 | source_dir=example
2 | container_file=example.bin
3 | network_name=test
4 | start_config=config.txt
--------------------------------------------------------------------------------
/samples/container_config.txt:
--------------------------------------------------------------------------------
1 | name=my_custom_container
2 | memory_soft_limit=59870912
3 | memory_hard_limit=1073741824
4 | cpu_priority=10
5 | network_mode=bridge
6 | container_uid=1001
7 | container_gid=1001
8 | dependencies=neofetch
9 |
--------------------------------------------------------------------------------
/samples/deploy_config:
--------------------------------------------------------------------------------
1 | source_dir=example
2 | container_file=test.bin
3 | network_name=test
4 | start_config=config.txt
--------------------------------------------------------------------------------
/samples/deploy_multiple_config:
--------------------------------------------------------------------------------
1 | container1 test
2 | container2 test
--------------------------------------------------------------------------------
/samples/sample_plugin.c:
--------------------------------------------------------------------------------
1 | // sample_plugin.c
2 | #include "plugin_manager/plugin.h"
3 | #include
4 | #include
5 | #include "osxiec.h"
6 |
7 | static int sample_initialize(void) {
8 | printf("Sample plugin initialized\n");
9 | execute_command("echo hello", "");
10 | return 0;
11 | }
12 |
13 | static int sample_execute(const char* command) {
14 | if (strcmp(command, "sample") == 0) {
15 | printf("Sample plugin executed\n");
16 | return 0;
17 | }
18 | return -1; // Command not handled
19 | }
20 |
21 | static int sample_cleanup(void) {
22 | printf("Sample plugin cleaned up\n");
23 | return 0;
24 | }
25 |
26 | OsxiecPlugin osxiec_plugin = {
27 | .name = "Sample Plugin",
28 | .version = "1.0",
29 | .initialize = sample_initialize,
30 | .execute = sample_execute,
31 | .cleanup = sample_cleanup
32 | };
33 |
--------------------------------------------------------------------------------
/scripts/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Define the version of the installation
4 | OSXIEC_VERSION="1.0"
5 |
6 | # Check if the script is run as root
7 | if [ "$EUID" -ne 0 ]
8 | then echo "Please run as root or use sudo"
9 | exit
10 | fi
11 |
12 | # Define the files and directories to copy
13 | files_to_copy=("osxiec" "libosxiec.dylib" "osxiec_deploy_multiple.sh" "include" "README.md" "LICENSE")
14 |
15 | # Define the destination directory
16 | install_dir="/usr/local/Cellar/osxiec/$OSXIEC_VERSION"
17 |
18 | # Create the installation directories
19 | mkdir -p "$install_dir/lib" "$install_dir/include" "$install_dir/bin"
20 |
21 | # Copy each file or directory
22 | for item in "${files_to_copy[@]}"
23 | do
24 | # Check if it's a directory or file
25 | if [ -d "$item" ]; then
26 | if [ "$item" == "include" ]; then
27 | if [ -d "$install_dir/include/osxiec" ]; then
28 | rm -rf "$install_dir/include/osxiec"
29 | echo "Deleted existing directory $install_dir/include/osxiec"
30 | fi
31 | cp -r "$item" "$install_dir/include/osxiec/"
32 |
33 | # Check if the copy was successful
34 | if [ $? -eq 0 ]; then
35 | echo "Directory $item copied successfully to $install_dir/include/osxiec"
36 | else
37 | echo "Failed to copy the directory $item"
38 | fi
39 | else
40 | # For any other directory, copy it directly to the version folder
41 | cp -r "$item" "$install_dir/"
42 |
43 | # Check if the copy was successful
44 | if [ $? -eq 0 ]; then
45 | echo "Directory $item copied successfully to $install_dir"
46 | else
47 | echo "Failed to copy the directory $item"
48 | fi
49 | fi
50 | elif [ -f "$item" ]; then
51 | if [ "$item" == "osxiec" ]; then
52 | # Check and delete existing symbolic link if present
53 | if [ -L "/usr/local/bin/osxiec" ]; then
54 | rm "/usr/local/bin/osxiec"
55 | echo "Deleted existing symbolic link /usr/local/bin/osxiec"
56 | fi
57 |
58 | # Copy the executable to the bin folder in the versioned directory
59 | if [ -f "$install_dir/bin/$item" ]; then
60 | rm "$install_dir/bin/$item"
61 | echo "Deleted existing file $install_dir/bin/$item"
62 | fi
63 | cp "$item" "$install_dir/bin/"
64 |
65 | # Check if the copy was successful
66 | if [ $? -eq 0 ]; then
67 | echo "Executable $item copied successfully to $install_dir/bin"
68 | else
69 | echo "Failed to copy the executable $item"
70 | fi
71 |
72 | # Create a new symbolic link in /usr/local/bin pointing to the executable in the bin folder
73 | ln -s "$install_dir/bin/osxiec" "/usr/local/bin/osxiec"
74 | if [ $? -eq 0 ]; then
75 | echo "Symbolic link created in /usr/local/bin pointing to $install_dir/bin/osxiec"
76 | else
77 | echo "Failed to create symbolic link in /usr/local/bin"
78 | fi
79 | elif [ "$item" == "libosxiec.dylib" ]; then
80 | # Check and delete existing symbolic link if present
81 | if [ -L "/usr/local/lib/libosxiec.dylib" ]; then
82 | rm "/usr/local/lib/libosxiec.dylib"
83 | echo "Deleted existing symbolic link /usr/local/lib/libosxiec.dylib"
84 | fi
85 |
86 | # For libosxiec.dylib, copy to the lib folder in the versioned directory
87 | if [ -f "$install_dir/lib/$item" ]; then
88 | rm "$install_dir/lib/$item"
89 | echo "Deleted existing file $install_dir/lib/$item"
90 | fi
91 | cp "$item" "$install_dir/lib/"
92 |
93 | # Check if the copy was successful
94 | if [ $? -eq 0 ]; then
95 | echo "File $item copied successfully to $install_dir/lib"
96 | else
97 | echo "Failed to copy the file $item"
98 | fi
99 |
100 | # Create a new symbolic link in /usr/local/lib to the library
101 | ln -s "$install_dir/lib/libosxiec.dylib" "/usr/local/lib/libosxiec.dylib"
102 | if [ $? -eq 0 ]; then
103 | echo "Symbolic link created in /usr/local/lib pointing to $install_dir/lib/libosxiec.dylib"
104 | else
105 | echo "Failed to create symbolic link in /usr/local/lib"
106 | fi
107 | elif [ "$item" == "osxiec_deploy_multiple.sh" ]; then
108 | if [ -f "/usr/local/bin/$item" ]; then
109 | rm "/usr/local/bin/$item"
110 | echo "Deleted existing file /usr/local/bin/$item"
111 | fi
112 |
113 | cp "$item" /usr/local/bin/
114 | if [ $? -eq 0 ]; then
115 | echo "File $item copied successfully to /usr/local/bin"
116 | else
117 | echo "Failed to copy the file $item"
118 | fi
119 | else
120 | # For any other file, copy it directly into the version folder
121 | cp "$item" "$install_dir/"
122 |
123 | # Check if the copy was successful
124 | if [ $? -eq 0 ]; then
125 | echo "File $item copied successfully to $install_dir"
126 | else
127 | echo "Failed to copy the file $item"
128 | fi
129 | fi
130 | else
131 | echo "$item does not exist or is not a valid file or directory"
132 | fi
133 | done
134 |
135 | echo "Installation complete!"
136 |
--------------------------------------------------------------------------------
/scripts/osxiec_deploy_multiple.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This is a work in progress.
3 |
4 | # Check for minimum number of arguments
5 | if [ "$#" -lt 1 ]; then
6 | echo "Usage: osxiec -deploym [port1 port2 ...]"
7 | exit 1
8 | fi
9 |
10 | multi_config_file="$1"
11 | shift
12 |
13 | # Array to store PIDs and ports
14 | pids=()
15 | ports=("$@")
16 |
17 | # Function to deploy and keep a container running
18 | deploy_container() {
19 | local config_file="$1"
20 | local network_name="$2"
21 | local port="$3"
22 | echo "Deploying container with config: $config_file on network: $network_name with port: $port"
23 | while true; do
24 | osxiec -deploy "$config_file" "$network_name" -port "$port"
25 | exit_status=$?
26 | if [ $exit_status -ne 0 ]; then
27 | echo "Container exited with status $exit_status, restarting in 5 seconds..."
28 | sleep 5
29 | else
30 | echo "Container exited normally. Not restarting."
31 | break
32 | fi
33 | done
34 | }
35 |
36 | # Read the multi_config_file and start each deployment in a separate thread
37 | i=0
38 | while IFS=' ' read -r config_file network_name || [[ -n "$config_file" ]]; do
39 | if [[ -n "$config_file" && -n "$network_name" ]]; then
40 | port=${ports[$i]:-$((8080 + i))} # Increment default port to avoid conflicts
41 | (deploy_container "$config_file" "$network_name" "$port") & # Run in background
42 | pids+=($!)
43 | echo "Started container process with PID: $! and port: $port"
44 | ((i++))
45 | fi
46 | done < "$multi_config_file"
47 |
48 | echo "All container processes started. PIDs: ${pids[*]}"
49 |
50 | # Function to handle script termination
51 | cleanup() {
52 | echo "Terminating all container processes..."
53 | for pid in "${pids[@]}"; do
54 | kill $pid 2>/dev/null
55 | done
56 | exit
57 | }
58 |
59 | # Set up trap to call cleanup function on script termination
60 | trap cleanup SIGINT SIGTERM
61 |
62 | # Wait for all processes to finish
63 | for pid in "${pids[@]}"; do
64 | wait $pid
65 | done
66 |
--------------------------------------------------------------------------------