├── .editorconfig ├── .github ├── FUNDING.yml └── renovate.json ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ └── maven-wrapper.properties ├── .settings ├── org.eclipse.core.resources.prefs └── org.eclipse.jdt.ui.prefs ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── config ├── OSHIJavaFormatConventions.xml └── license-header.txt ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ ├── module-info.java │ └── ooo │ └── oshi │ ├── PlatformEnum.java │ ├── SystemInfo.java │ ├── annotation │ └── concurrent │ │ ├── GuardedBy.java │ │ ├── Immutable.java │ │ ├── NotThreadSafe.java │ │ ├── ThreadSafe.java │ │ └── package-info.java │ ├── foreign │ ├── mac │ │ └── SystemLibrary.java │ └── windows │ │ ├── Kernel32Library.java │ │ └── WinBase.java │ ├── hardware │ ├── HardwareAbstractionLayer.java │ ├── linux │ │ └── LinuxHardwareAbstractionLayer.java │ ├── mac │ │ └── MacHardwareAbstractionLayer.java │ ├── package-info.java │ └── windows │ │ └── WindowsHardwareAbstractionLayer.java │ ├── package-info.java │ ├── software │ └── os │ │ ├── OSProcess.java │ │ ├── OSThread.java │ │ ├── OperatingSystem.java │ │ ├── common │ │ ├── AbstractOSProcess.java │ │ ├── AbstractOperatingSystem.java │ │ └── package-info.java │ │ ├── linux │ │ └── LinuxOperatingSystem.java │ │ ├── mac │ │ ├── MacOSProcess.java │ │ ├── MacOperatingSystem.java │ │ └── package-info.java │ │ ├── package-info.java │ │ └── windows │ │ └── WindowsOperatingSystem.java │ └── util │ ├── Constants.java │ ├── ExecutingCommand.java │ ├── FormatUtil.java │ ├── Memoizer.java │ ├── ParseUtil.java │ ├── Tuples.java │ ├── package-info.java │ └── platform │ └── mac │ ├── SysctlUtil.java │ └── package-info.java └── test └── java └── ooo └── oshi ├── Kernel32Test.java └── SystemInfoTest.java /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Global editorconfig settings 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true # Insert a newline at the end of a file 10 | charset = utf-8 # Set default charset 11 | indent_style = space # Use spaces for indents 12 | indent_size = 4 13 | trim_trailing_whitespace = true # delete whitespace at the end of each line 14 | 15 | # 2 space indentation for yaml 16 | [*.{yml, yaml}] 17 | indent_size = 2 18 | 19 | # IntelliJ specific settings 20 | ij_java_class_count_to_use_import_on_demand = 1000 # Do not group imports with an asterisk 21 | ij_java_names_count_to_use_import_on_demand = 1000 # Do not group imports with an asterisk 22 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [dbwiddis] 2 | tidelift: maven/com.github.oshi:oshi-core 3 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", "schedule:daily" 5 | ], 6 | "packageRules": [ 7 | { 8 | "description": "Batch and auto-merge plugin updates", 9 | "matchPackagePatterns": ["-plugin$"], 10 | "matchUpdateTypes": ["patch", "minor"], 11 | "groupName": "plugins", 12 | "automerge": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Output 2 | target 3 | .project 4 | .settings 5 | .classpath 6 | .idea 7 | .DS_Store 8 | *.iml 9 | .mvn/wrapper/maven-wrapper.jar 10 | .github/keys 11 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.Authenticator; 23 | import java.net.PasswordAuthentication; 24 | import java.net.URI; 25 | import java.net.URL; 26 | import java.nio.file.Files; 27 | import java.nio.file.Path; 28 | import java.nio.file.StandardCopyOption; 29 | import java.util.concurrent.ThreadLocalRandom; 30 | 31 | public final class MavenWrapperDownloader { 32 | private static final String WRAPPER_VERSION = "3.3.2"; 33 | 34 | private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); 35 | 36 | public static void main(String[] args) { 37 | log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); 38 | 39 | if (args.length != 2) { 40 | System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); 41 | System.exit(1); 42 | } 43 | 44 | try { 45 | log(" - Downloader started"); 46 | final URL wrapperUrl = URI.create(args[0]).toURL(); 47 | final String jarPath = args[1].replace("..", ""); // Sanitize path 48 | final Path wrapperJarPath = Path.of(jarPath).toAbsolutePath().normalize(); 49 | downloadFileFromURL(wrapperUrl, wrapperJarPath); 50 | log("Done"); 51 | } catch (IOException e) { 52 | System.err.println("- Error downloading: " + e.getMessage()); 53 | if (VERBOSE) { 54 | e.printStackTrace(); 55 | } 56 | System.exit(1); 57 | } 58 | } 59 | 60 | private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) 61 | throws IOException { 62 | log(" - Downloading to: " + wrapperJarPath); 63 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 64 | final String username = System.getenv("MVNW_USERNAME"); 65 | final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 66 | Authenticator.setDefault(new Authenticator() { 67 | @Override 68 | protected PasswordAuthentication getPasswordAuthentication() { 69 | return new PasswordAuthentication(username, password); 70 | } 71 | }); 72 | } 73 | Path temp = wrapperJarPath 74 | .getParent() 75 | .resolve(wrapperJarPath.getFileName() + "." 76 | + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); 77 | try (InputStream inStream = wrapperUrl.openStream()) { 78 | Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); 79 | Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); 80 | } finally { 81 | Files.deleteIfExists(temp); 82 | } 83 | log(" - Downloader complete"); 84 | } 85 | 86 | private static void log(String msg) { 87 | if (VERBOSE) { 88 | System.out.println(msg); 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=source 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar 21 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | cleanup.add_all=false 2 | cleanup.add_default_serial_version_id=true 3 | cleanup.add_generated_serial_version_id=false 4 | cleanup.add_missing_annotations=true 5 | cleanup.add_missing_deprecated_annotations=true 6 | cleanup.add_missing_methods=false 7 | cleanup.add_missing_nls_tags=false 8 | cleanup.add_missing_override_annotations=true 9 | cleanup.add_missing_override_annotations_interface_methods=true 10 | cleanup.add_serial_version_id=false 11 | cleanup.always_use_blocks=true 12 | cleanup.always_use_parentheses_in_expressions=false 13 | cleanup.always_use_this_for_non_static_field_access=false 14 | cleanup.always_use_this_for_non_static_method_access=false 15 | cleanup.array_with_curly=false 16 | cleanup.arrays_fill=false 17 | cleanup.bitwise_conditional_expression=false 18 | cleanup.boolean_literal=false 19 | cleanup.boolean_value_rather_than_comparison=true 20 | cleanup.break_loop=false 21 | cleanup.collection_cloning=false 22 | cleanup.comparing_on_criteria=false 23 | cleanup.comparison_statement=false 24 | cleanup.controlflow_merge=false 25 | cleanup.convert_functional_interfaces=false 26 | cleanup.convert_to_enhanced_for_loop=true 27 | cleanup.convert_to_enhanced_for_loop_if_loop_var_used=true 28 | cleanup.convert_to_switch_expressions=false 29 | cleanup.correct_indentation=false 30 | cleanup.do_while_rather_than_while=true 31 | cleanup.double_negation=false 32 | cleanup.else_if=false 33 | cleanup.embedded_if=false 34 | cleanup.evaluate_nullable=false 35 | cleanup.extract_increment=false 36 | cleanup.format_source_code=false 37 | cleanup.format_source_code_changes_only=false 38 | cleanup.hash=false 39 | cleanup.if_condition=false 40 | cleanup.insert_inferred_type_arguments=false 41 | cleanup.instanceof=false 42 | cleanup.instanceof_keyword=false 43 | cleanup.invert_equals=false 44 | cleanup.join=false 45 | cleanup.lazy_logical_operator=false 46 | cleanup.make_local_variable_final=true 47 | cleanup.make_parameters_final=false 48 | cleanup.make_private_fields_final=true 49 | cleanup.make_type_abstract_if_missing_method=false 50 | cleanup.make_variable_declarations_final=false 51 | cleanup.map_cloning=false 52 | cleanup.merge_conditional_blocks=false 53 | cleanup.multi_catch=false 54 | cleanup.never_use_blocks=false 55 | cleanup.never_use_parentheses_in_expressions=true 56 | cleanup.no_string_creation=false 57 | cleanup.no_super=false 58 | cleanup.number_suffix=false 59 | cleanup.objects_equals=false 60 | cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=true 61 | cleanup.operand_factorization=false 62 | cleanup.organize_imports=true 63 | cleanup.overridden_assignment=false 64 | cleanup.plain_replacement=false 65 | cleanup.precompile_regex=false 66 | cleanup.primitive_comparison=false 67 | cleanup.primitive_parsing=false 68 | cleanup.primitive_rather_than_wrapper=true 69 | cleanup.primitive_serialization=false 70 | cleanup.pull_out_if_from_if_else=false 71 | cleanup.pull_up_assignment=false 72 | cleanup.push_down_negation=false 73 | cleanup.qualify_static_field_accesses_with_declaring_class=false 74 | cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true 75 | cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true 76 | cleanup.qualify_static_member_accesses_with_declaring_class=true 77 | cleanup.qualify_static_method_accesses_with_declaring_class=false 78 | cleanup.reduce_indentation=false 79 | cleanup.redundant_comparator=false 80 | cleanup.redundant_falling_through_block_end=false 81 | cleanup.remove_private_constructors=true 82 | cleanup.remove_redundant_modifiers=false 83 | cleanup.remove_redundant_semicolons=true 84 | cleanup.remove_redundant_type_arguments=true 85 | cleanup.remove_trailing_whitespaces=true 86 | cleanup.remove_trailing_whitespaces_all=true 87 | cleanup.remove_trailing_whitespaces_ignore_empty=false 88 | cleanup.remove_unnecessary_array_creation=false 89 | cleanup.remove_unnecessary_casts=true 90 | cleanup.remove_unnecessary_nls_tags=true 91 | cleanup.remove_unused_imports=true 92 | cleanup.remove_unused_local_variables=false 93 | cleanup.remove_unused_private_fields=true 94 | cleanup.remove_unused_private_members=false 95 | cleanup.remove_unused_private_methods=true 96 | cleanup.remove_unused_private_types=true 97 | cleanup.return_expression=false 98 | cleanup.simplify_lambda_expression_and_method_ref=false 99 | cleanup.single_used_field=false 100 | cleanup.sort_members=false 101 | cleanup.sort_members_all=false 102 | cleanup.standard_comparison=false 103 | cleanup.static_inner_class=false 104 | cleanup.strictly_equal_or_different=false 105 | cleanup.stringbuffer_to_stringbuilder=false 106 | cleanup.stringbuilder=false 107 | cleanup.stringbuilder_for_local_vars=true 108 | cleanup.stringconcat_to_textblock=false 109 | cleanup.substring=false 110 | cleanup.switch=false 111 | cleanup.system_property=false 112 | cleanup.system_property_boolean=false 113 | cleanup.system_property_file_encoding=false 114 | cleanup.system_property_file_separator=false 115 | cleanup.system_property_line_separator=false 116 | cleanup.system_property_path_separator=false 117 | cleanup.ternary_operator=false 118 | cleanup.try_with_resource=false 119 | cleanup.unlooped_while=false 120 | cleanup.unreachable_block=false 121 | cleanup.use_anonymous_class_creation=false 122 | cleanup.use_autoboxing=false 123 | cleanup.use_blocks=false 124 | cleanup.use_blocks_only_for_return_and_throw=false 125 | cleanup.use_directly_map_method=false 126 | cleanup.use_lambda=true 127 | cleanup.use_parentheses_in_expressions=false 128 | cleanup.use_string_is_blank=false 129 | cleanup.use_this_for_non_static_field_access=false 130 | cleanup.use_this_for_non_static_field_access_only_if_necessary=true 131 | cleanup.use_this_for_non_static_method_access=false 132 | cleanup.use_this_for_non_static_method_access_only_if_necessary=true 133 | cleanup.use_unboxing=false 134 | cleanup.use_var=false 135 | cleanup.useless_continue=false 136 | cleanup.useless_return=false 137 | cleanup.valueof_rather_than_instantiation=false 138 | cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile 139 | cleanup_settings_version=2 140 | eclipse.preferences.version=1 141 | formatter_profile=_OSHI Java Conventions 142 | formatter_settings_version=22 143 | org.eclipse.jdt.ui.exception.name=e 144 | org.eclipse.jdt.ui.gettersetter.use.is=true 145 | org.eclipse.jdt.ui.ignorelowercasenames=true 146 | org.eclipse.jdt.ui.importorder=java;javax;org;com; 147 | org.eclipse.jdt.ui.keywordthis=false 148 | org.eclipse.jdt.ui.ondemandthreshold=99 149 | org.eclipse.jdt.ui.overrideannotation=true 150 | org.eclipse.jdt.ui.staticondemandthreshold=99 151 | org.eclipse.jdt.ui.text.custom_code_templates= 152 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to OSHI-FFM 2 | 3 | Welcome! This is a community maintained project and we need YOU to join our community! 4 | 5 | ## First Things First 6 | 7 | Respect the license of any code you submit. You may only submit code that is compatible with the Apache 2.0 license. If you have copied any code from any other source, ensure you comply with the restrictions of its license, and do not impose any additional restrictions on this project. 8 | 9 | ## Code Format 10 | 11 | This section will eventually contain a lot more detail, but for now: 12 | - There's an Eclipse formatting convention file in the config directory 13 | - If you don't have eclipse or an IDE that can import its configs, don't worry, just run `./mvnw spotless:apply` before committing 14 | 15 | ## This is a new project with an old history. 16 | 17 | The intent of this project is to eventually contain all the same features of the OSHI project, but using core Java features only, without JNA. The OSHI project has plenty of examples to get you started, but: 18 | - Do not feel constrained to use the same API. If the community desires to change the API, that can be done. 19 | - Do not feel constrained to use the same implementations. If you know a better way to do things, do it! 20 | 21 | ## Developer Certificate of Origin 22 | 23 | OSHI-FFM is an open source product released under the Apache 2.0 license (see either [the Apache site](https://www.apache.org/licenses/LICENSE-2.0) or the [LICENSE file](./LICENSE)). The Apache 2.0 license allows you to freely use, modify, distribute, and sell your own products that include Apache 2.0 licensed software. 24 | 25 | We respect intellectual property rights of others and we want to make sure all incoming contributions are correctly attributed and licensed. A Developer Certificate of Origin (DCO) is a lightweight mechanism to do that. 26 | 27 | The DCO is a declaration attached to every contribution made by every developer. In the commit message of the contribution, the developer simply adds a `Signed-off-by` statement and thereby agrees to the DCO, which you can find below or at [DeveloperCertificate.org](http://developercertificate.org/). 28 | 29 | Each commit must include a DCO which looks like this 30 | 31 | ``` 32 | Signed-off-by: Charles Babbage 33 | ``` 34 | You may type this line on your own when writing your commit messages. However, if your user.name and user.email are set in your git configs, you can use `-s` or `--signoff` to add the `Signed-off-by` line to the end of the commit message. 35 | 36 | Please use your real name. The git history is documentation of your copyright in the work. Anonymous contributions are not permitted. 37 | 38 | Please use an email address uniquely identifying you. A [GitHub noreply address](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address) associated with your username is acceptable but discouraged. 39 | 40 | ``` 41 | Developer's Certificate of Origin 1.1 42 | By making a contribution to this project, I certify that: 43 | (a) The contribution was created in whole or in part by me and I 44 | have the right to submit it under the open source license 45 | indicated in the file; or 46 | (b) The contribution is based upon previous work that, to the 47 | best of my knowledge, is covered under an appropriate open 48 | source license and I have the right under that license to 49 | submit that work with modifications, whether created in whole 50 | or in part by me, under the same open source license (unless 51 | I am permitted to submit under a different license), as 52 | Indicated in the file; or 53 | (c) The contribution was provided directly to me by some other 54 | person who certified (a), (b) or (c) and I have not modified 55 | it. 56 | (d) I understand and agree that this project and the contribution 57 | are public and that a record of the contribution (including 58 | all personal information I submit with it, including my 59 | sign-off) is maintained indefinitely and may be redistributed 60 | consistent with this project or the open source license(s) 61 | involved. 62 | ``` 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![OSHI](https://dl.dropboxusercontent.com/s/c82qboyvvudpvdp/oshilogo.png) 2 | 3 | [![MIT License](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) 4 | 5 | OSHI is a free Operating System and Hardware Information library for Java. 6 | 7 | OSHI-FFM intends to leverage the efficiency and performance improvements of [JEP 424](https://openjdk.org/jeps/424) 8 | (Foreign Function & Memory API) to provide the same capabilities as the original [OSHI](https://github.com/oshi/oshi) 9 | project, without the overhead of JNA. 10 | 11 | It does not require the installation of any additional native libraries and aims to provide a 12 | cross-platform implementation to retrieve system information, such as OS version, processes, 13 | memory and CPU usage, disks and partitions, devices, sensors, etc. 14 | 15 | ## Contributors are (more than) welcome! 16 | 17 | This project is soliciting community involvement with the release of JDK 19, with JEP 424 as a Preview feature. 18 | The goal is for a 1.0.0 release in about one year, with the release of JDK 21 LTS. 19 | 20 | Recreating (and modifying as needed) the OSHI API is a Herculean task that will require a community effort. 21 | 22 | Contributors who just want to learn about the new Foreign Function and Memory API are welcome to use this as a practical training ground! What better way to sharpen your skills than to solve an actual, practical problem instead of repeating a Hello World tutorial! 23 | 24 | Long-time OSHI users who want to have a voice in the future of this project are encouraged to contribute more and join the maintainer team. 25 | 26 | Ideally this will become a community-maintained project with a team of maintainers, with project direction determined by consensus rather than a benevolent dictator. 27 | 28 | ## Non-code contributions are (more than) welcome! 29 | 30 | The initial commits have brought over a minimum number of features and minimal documentation. There are tons of non-code things you can do to help: 31 | - Help define and implement a CI workflow 32 | - Add more documentation 33 | - Organize and triage issues and "where can I help next" guidance 34 | - Create cool graphics and branding 35 | - Solicit contributions from your friends, colleagues, and corporations 36 | 37 | ## This is a new project with an old history. 38 | 39 | The intent of this project is to eventually contain all the same features of the OSHI project, but using core Java features only, without JNA. The OSHI project has plenty of examples to get you started, but: 40 | - Do not feel constrained to use the same API. If the community desires to change the API, that can be done. 41 | - Do not feel constrained to use the same implementations. If you know a better way to do things, do it! 42 | 43 | ## FAQ 44 | 45 | **Q: Why all this effort? What's wrong with JNA-based OSHI?** 46 | 47 | **A:** Nothing is wrong with JNA! It's a capable program that many projects have used to access native functions in a standard Java-based format. However, [benchmark](https://github.com/zakgof/java-native-benchmark#results) have shown that JEP 424 (Project Panama)-based implementations are about 12 times faster. An order of magnitude improvement is worth the effort. 48 | 49 | **Q: Will this keep the same API as OSHI?** 50 | 51 | **A:** OSHI's API has evolved over 12 years and seems reasonably consistent, so the general structure will likely stay the same. There will possibly be changes in how unavailable or unsupported data is handled, however, including custom exceptions, Optional return types, and possibly better leverage of JPMS modules. 52 | 53 | To disambiguate the packages, this project will prepend `ooo` to exsiting `oshi` package names (the reverse dns for the oshi.ooo domain). 54 | 55 | **Q: What does OOO stand for? Why is that OSHI's domain extension?** 56 | 57 | **A:** All the good extensions were taken. :-) OOO seemed the most neutral, non-localized generic domain. 58 | 59 | There is an opportunity to use the triple-O in some sort of branding: 60 | - Zero additional software required 61 | - Zero dependencies beyond the JDK 62 | - Zero restrictions with a permissive license 63 | 64 | Perhaps you've got better ideas? 65 | -------------------------------------------------------------------------------- /config/license-header.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright $YEAR the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.3.2 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ]; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ]; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ]; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ]; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false 54 | darwin=false 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true ;; 59 | Darwin*) 60 | darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | JAVA_HOME="$(/usr/libexec/java_home)" 66 | export JAVA_HOME 67 | else 68 | JAVA_HOME="/Library/Java/Home" 69 | export JAVA_HOME 70 | fi 71 | fi 72 | ;; 73 | esac 74 | 75 | if [ -z "$JAVA_HOME" ]; then 76 | if [ -r /etc/gentoo-release ]; then 77 | JAVA_HOME=$(java-config --jre-home) 78 | fi 79 | fi 80 | 81 | # For Cygwin, ensure paths are in UNIX format before anything is touched 82 | if $cygwin; then 83 | [ -n "$JAVA_HOME" ] \ 84 | && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 85 | [ -n "$CLASSPATH" ] \ 86 | && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 87 | fi 88 | 89 | # For Mingw, ensure paths are in UNIX format before anything is touched 90 | if $mingw; then 91 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ 92 | && JAVA_HOME="$( 93 | cd "$JAVA_HOME" || ( 94 | echo "cannot cd into $JAVA_HOME." >&2 95 | exit 1 96 | ) 97 | pwd 98 | )" 99 | fi 100 | 101 | if [ -z "$JAVA_HOME" ]; then 102 | javaExecutable="$(which javac)" 103 | if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then 104 | # readlink(1) is not available as standard on Solaris 10. 105 | readLink=$(which readlink) 106 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 107 | if $darwin; then 108 | javaHome="$(dirname "$javaExecutable")" 109 | javaExecutable="$(cd "$javaHome" && pwd -P)/javac" 110 | else 111 | javaExecutable="$(readlink -f "$javaExecutable")" 112 | fi 113 | javaHome="$(dirname "$javaExecutable")" 114 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 115 | JAVA_HOME="$javaHome" 116 | export JAVA_HOME 117 | fi 118 | fi 119 | fi 120 | 121 | if [ -z "$JAVACMD" ]; then 122 | if [ -n "$JAVA_HOME" ]; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD="$JAVA_HOME/jre/sh/java" 126 | else 127 | JAVACMD="$JAVA_HOME/bin/java" 128 | fi 129 | else 130 | JAVACMD="$( 131 | \unset -f command 2>/dev/null 132 | \command -v java 133 | )" 134 | fi 135 | fi 136 | 137 | if [ ! -x "$JAVACMD" ]; then 138 | echo "Error: JAVA_HOME is not defined correctly." >&2 139 | echo " We cannot execute $JAVACMD" >&2 140 | exit 1 141 | fi 142 | 143 | if [ -z "$JAVA_HOME" ]; then 144 | echo "Warning: JAVA_HOME environment variable is not set." >&2 145 | fi 146 | 147 | # traverses directory structure from process work directory to filesystem root 148 | # first directory with .mvn subdirectory is considered project base directory 149 | find_maven_basedir() { 150 | if [ -z "$1" ]; then 151 | echo "Path not specified to find_maven_basedir" >&2 152 | return 1 153 | fi 154 | 155 | basedir="$1" 156 | wdir="$1" 157 | while [ "$wdir" != '/' ]; do 158 | if [ -d "$wdir"/.mvn ]; then 159 | basedir=$wdir 160 | break 161 | fi 162 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 163 | if [ -d "${wdir}" ]; then 164 | wdir=$( 165 | cd "$wdir/.." || exit 1 166 | pwd 167 | ) 168 | fi 169 | # end of workaround 170 | done 171 | printf '%s' "$( 172 | cd "$basedir" || exit 1 173 | pwd 174 | )" 175 | } 176 | 177 | # concatenates all lines of a file 178 | concat_lines() { 179 | if [ -f "$1" ]; then 180 | # Remove \r in case we run on Windows within Git Bash 181 | # and check out the repository with auto CRLF management 182 | # enabled. Otherwise, we may read lines that are delimited with 183 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 184 | # splitting rules. 185 | tr -s '\r\n' ' ' <"$1" 186 | fi 187 | } 188 | 189 | log() { 190 | if [ "$MVNW_VERBOSE" = true ]; then 191 | printf '%s\n' "$1" 192 | fi 193 | } 194 | 195 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 196 | if [ -z "$BASE_DIR" ]; then 197 | exit 1 198 | fi 199 | 200 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 201 | export MAVEN_PROJECTBASEDIR 202 | log "$MAVEN_PROJECTBASEDIR" 203 | 204 | ########################################################################################## 205 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 206 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 207 | ########################################################################################## 208 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 209 | if [ -r "$wrapperJarPath" ]; then 210 | log "Found $wrapperJarPath" 211 | else 212 | log "Couldn't find $wrapperJarPath, downloading it ..." 213 | 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 216 | else 217 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 218 | fi 219 | while IFS="=" read -r key value; do 220 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 221 | safeValue=$(echo "$value" | tr -d '\r') 222 | case "$key" in wrapperUrl) 223 | wrapperUrl="$safeValue" 224 | break 225 | ;; 226 | esac 227 | done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 228 | log "Downloading from: $wrapperUrl" 229 | 230 | if $cygwin; then 231 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 232 | fi 233 | 234 | if command -v wget >/dev/null; then 235 | log "Found wget ... using wget" 236 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 237 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 238 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 239 | else 240 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | fi 242 | elif command -v curl >/dev/null; then 243 | log "Found curl ... using curl" 244 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 245 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 246 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 247 | else 248 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 249 | fi 250 | else 251 | log "Falling back to using Java to download" 252 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 253 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 254 | # For Cygwin, switch paths to Windows format before running javac 255 | if $cygwin; then 256 | javaSource=$(cygpath --path --windows "$javaSource") 257 | javaClass=$(cygpath --path --windows "$javaClass") 258 | fi 259 | if [ -e "$javaSource" ]; then 260 | if [ ! -e "$javaClass" ]; then 261 | log " - Compiling MavenWrapperDownloader.java ..." 262 | ("$JAVA_HOME/bin/javac" "$javaSource") 263 | fi 264 | if [ -e "$javaClass" ]; then 265 | log " - Running MavenWrapperDownloader.java ..." 266 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 267 | fi 268 | fi 269 | fi 270 | fi 271 | ########################################################################################## 272 | # End of extension 273 | ########################################################################################## 274 | 275 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 276 | wrapperSha256Sum="" 277 | while IFS="=" read -r key value; do 278 | case "$key" in wrapperSha256Sum) 279 | wrapperSha256Sum=$value 280 | break 281 | ;; 282 | esac 283 | done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 284 | if [ -n "$wrapperSha256Sum" ]; then 285 | wrapperSha256Result=false 286 | if command -v sha256sum >/dev/null; then 287 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then 288 | wrapperSha256Result=true 289 | fi 290 | elif command -v shasum >/dev/null; then 291 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then 292 | wrapperSha256Result=true 293 | fi 294 | else 295 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 296 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 297 | exit 1 298 | fi 299 | if [ $wrapperSha256Result = false ]; then 300 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 301 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 302 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 303 | exit 1 304 | fi 305 | fi 306 | 307 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 308 | 309 | # For Cygwin, switch paths to Windows format before running java 310 | if $cygwin; then 311 | [ -n "$JAVA_HOME" ] \ 312 | && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 313 | [ -n "$CLASSPATH" ] \ 314 | && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 315 | [ -n "$MAVEN_PROJECTBASEDIR" ] \ 316 | && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 317 | fi 318 | 319 | # Provide a "standardized" way to retrieve the CLI args that will 320 | # work with both Windows and non-Windows executions. 321 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 322 | export MAVEN_CMD_LINE_ARGS 323 | 324 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 325 | 326 | # shellcheck disable=SC2086 # safe args 327 | exec "$JAVACMD" \ 328 | $MAVEN_OPTS \ 329 | $MAVEN_DEBUG_OPTS \ 330 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 331 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 332 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 333 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. >&2 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. >&2 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. >&2 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. >&2 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ 164 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 165 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 166 | " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 167 | " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 168 | " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 169 | " exit 1;"^ 170 | "}"^ 171 | "}" 172 | if ERRORLEVEL 1 goto error 173 | ) 174 | 175 | @REM Provide a "standardized" way to retrieve the CLI args that will 176 | @REM work with both Windows and non-Windows executions. 177 | set MAVEN_CMD_LINE_ARGS=%* 178 | 179 | %MAVEN_JAVA_EXE% ^ 180 | %JVM_CONFIG_MAVEN_PROPS% ^ 181 | %MAVEN_OPTS% ^ 182 | %MAVEN_DEBUG_OPTS% ^ 183 | -classpath %WRAPPER_JAR% ^ 184 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 185 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 186 | if ERRORLEVEL 1 goto error 187 | goto end 188 | 189 | :error 190 | set ERROR_CODE=1 191 | 192 | :end 193 | @endlocal & set ERROR_CODE=%ERROR_CODE% 194 | 195 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 196 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 197 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 198 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 199 | :skipRcPost 200 | 201 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 202 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 203 | 204 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 205 | 206 | cmd /C exit /B %ERROR_CODE% 207 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.github.oshi 5 | oshi-ffm 6 | 0.0.1-SNAPSHOT 7 | 8 | 9 | 2022 10 | yyyy-MM-dd HH:mm:ss 11 | 3.3.9 12 | UTF-8 13 | UTF-8 14 | UTF-8 15 | 16 | 2.0.17 17 | 5.12.2 18 | 3.0 19 | 20 | 3.14.0 21 | 3.5.3 22 | 23 | 4.0.0 24 | 2.44.4 25 | 26 | 27 | 28 | 29 | org.slf4j 30 | slf4j-api 31 | ${slf4j.version} 32 | 33 | 34 | org.slf4j 35 | slf4j-simple 36 | ${slf4j.version} 37 | test 38 | 39 | 40 | org.junit.jupiter 41 | junit-jupiter-api 42 | ${junit.version} 43 | test 44 | 45 | 46 | org.hamcrest 47 | hamcrest 48 | ${hamcrest.version} 49 | test 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | com.github.ekryd.sortpom 58 | sortpom-maven-plugin 59 | ${sortpom-plugin.version} 60 | 61 | false 62 | \n 63 | ${project.build.sourceEncoding} 64 | scope 65 | 4 66 | false 67 | true 68 | 69 | 70 | 71 | 72 | sort 73 | 74 | verify 75 | 76 | 77 | 78 | 79 | com.diffplug.spotless 80 | spotless-maven-plugin 81 | ${spotless-plugin.version} 82 | 83 | 84 | 85 | 86 | **/*.md 87 | **/*.yml 88 | **/*.yaml 89 | 90 | 91 | 92 | 93 | true 94 | 2 95 | 96 | 97 | 98 | 99 | **/*.xml 100 | 101 | 102 | 103 | 104 | true 105 | 4 106 | 107 | 108 | 109 | 110 | origin/main 111 | 112 | 113 | 114 | 115 | ${project.basedir}/config/license-header.txt 116 | 117 | 118 | ${project.basedir}/config/OSHIJavaFormatConventions.xml 119 | 120 | 121 | 122 | 123 | 124 | 125 | check 126 | 127 | compile 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | maven-compiler-plugin 137 | ${maven-compiler-plugin.version} 138 | 139 | 19 140 | 19 141 | 19 142 | --enable-preview 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-surefire-plugin 148 | ${maven-surefire-plugin.version} 149 | 150 | --enable-preview 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module ooo.oshi { 2 | // API 3 | exports ooo.oshi; 4 | exports ooo.oshi.hardware; 5 | exports ooo.oshi.software.os; 6 | exports ooo.oshi.util; 7 | 8 | // dependencies 9 | requires org.slf4j; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/PlatformEnum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi; 6 | 7 | /** 8 | * An enumeration of supported operating systems. 9 | */ 10 | public enum PlatformEnum { 11 | /** 12 | * macOS 13 | */ 14 | MACOS("macOS"), 15 | /** 16 | * A flavor of Linux 17 | */ 18 | LINUX("Linux"), 19 | /** 20 | * Microsoft Windows 21 | */ 22 | WINDOWS("Windows"), 23 | /** 24 | * Unsupported OS 25 | */ 26 | UNSUPPORTED("Unsupported Operating System"); 27 | 28 | private final String name; 29 | 30 | PlatformEnum(String name) { 31 | this.name = name; 32 | } 33 | 34 | /** 35 | * Gets the friendly name of the platform 36 | * 37 | * @return the friendly name of the platform 38 | */ 39 | public String getName() { 40 | return this.name; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/SystemInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi; 6 | 7 | import static ooo.oshi.PlatformEnum.LINUX; 8 | import static ooo.oshi.PlatformEnum.MACOS; 9 | import static ooo.oshi.PlatformEnum.UNSUPPORTED; 10 | import static ooo.oshi.PlatformEnum.WINDOWS; 11 | import static ooo.oshi.util.Memoizer.memoize; 12 | 13 | import java.util.function.Supplier; 14 | 15 | import ooo.oshi.hardware.HardwareAbstractionLayer; 16 | import ooo.oshi.hardware.linux.LinuxHardwareAbstractionLayer; 17 | import ooo.oshi.hardware.mac.MacHardwareAbstractionLayer; 18 | import ooo.oshi.hardware.windows.WindowsHardwareAbstractionLayer; 19 | import ooo.oshi.software.os.OperatingSystem; 20 | import ooo.oshi.software.os.linux.LinuxOperatingSystem; 21 | import ooo.oshi.software.os.mac.MacOperatingSystem; 22 | import ooo.oshi.software.os.windows.WindowsOperatingSystem; 23 | 24 | /** 25 | * System information. This is the main entry point to OSHI. 26 | *

27 | * This object provides getters which instantiate the appropriate platform-specific implementations of 28 | * {@link oshi.software.os.OperatingSystem} (software) and {@link oshi.hardware.HardwareAbstractionLayer} (hardware). 29 | */ 30 | public class SystemInfo { 31 | 32 | private static final PlatformEnum CURRENT_PLATFORM; 33 | 34 | static { 35 | CURRENT_PLATFORM = switch (System.getProperty("os.name")) { 36 | case String name when name.startsWith("Linux") -> LINUX; 37 | case String name when name.startsWith("Mac") || name.startsWith("Darwin") -> MACOS; 38 | case String name when name.startsWith("Windows") -> WINDOWS; 39 | default -> UNSUPPORTED; 40 | }; 41 | } 42 | 43 | private static final String NOT_SUPPORTED = "Operating system not supported: "; 44 | 45 | private final Supplier os = memoize(SystemInfo::createOperatingSystem); 46 | 47 | private final Supplier hardware = memoize(SystemInfo::createHardware); 48 | 49 | /** 50 | * Create a new instance of {@link SystemInfo}. This is the main entry point to OSHI and provides access to 51 | * cross-platform code. 52 | *

53 | * Platform-specific Hardware and Software objects are retrieved via memoized suppliers. To conserve memory at the 54 | * cost of additional processing time, create a new instance of SystemInfo for subsequent calls. To conserve 55 | * processing time at the cost of additional memory usage, re-use the same {@link SystemInfo} object for future 56 | * queries. 57 | */ 58 | public SystemInfo() { 59 | // Intentionally empty, here to enable the constructor javadoc. 60 | } 61 | 62 | /** 63 | * Gets the {@link PlatformEnum} value representing this system. 64 | * 65 | * @return Returns the current platform 66 | */ 67 | public static PlatformEnum getCurrentPlatform() { 68 | return CURRENT_PLATFORM; 69 | } 70 | 71 | /** 72 | * Creates a new instance of the appropriate platform-specific {@link OperatingSystem}. 73 | * 74 | * @return A new platform-specific instance implementing {@link OperatingSystem}. 75 | */ 76 | public OperatingSystem getOperatingSystem() { 77 | return os.get(); 78 | } 79 | 80 | private static OperatingSystem createOperatingSystem() { 81 | return switch (CURRENT_PLATFORM) { 82 | case WINDOWS -> new WindowsOperatingSystem(); 83 | case LINUX -> new LinuxOperatingSystem(); 84 | case MACOS -> new MacOperatingSystem(); 85 | default -> throw new UnsupportedOperationException(NOT_SUPPORTED); 86 | }; 87 | } 88 | 89 | /** 90 | * Creates a new instance of the appropriate platform-specific {@link HardwareAbstractionLayer}. 91 | * 92 | * @return A new platform-specific instance implementing {@link HardwareAbstractionLayer}. 93 | */ 94 | public HardwareAbstractionLayer getHardware() { 95 | return hardware.get(); 96 | } 97 | 98 | private static HardwareAbstractionLayer createHardware() { 99 | return switch (CURRENT_PLATFORM) { 100 | case WINDOWS -> new WindowsHardwareAbstractionLayer(); 101 | case LINUX -> new LinuxHardwareAbstractionLayer(); 102 | case MACOS -> new MacHardwareAbstractionLayer(); 103 | default -> throw new UnsupportedOperationException(NOT_SUPPORTED); 104 | }; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/annotation/concurrent/GuardedBy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.annotation.concurrent; 6 | 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | /** 13 | * The field or method to which this annotation is applied can only be accessed when holding a particular lock, which 14 | * may be a built-in (synchronization) lock, or may be an explicit {@code java.util.concurrent.Lock}. 15 | *

16 | * The argument determines which lock guards the annotated field or method: 17 | *

28 | *

29 | * This annotation is intended for internal use in OSHI as a temporary workaround until it is available in 30 | * {@code jakarta.annotations}. 31 | */ 32 | @Target({ ElementType.FIELD, ElementType.METHOD }) 33 | @Retention(RetentionPolicy.CLASS) 34 | public @interface GuardedBy { 35 | String value(); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/annotation/concurrent/Immutable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.annotation.concurrent; 6 | 7 | import java.lang.annotation.Documented; 8 | 9 | /** 10 | * The presence of this annotation indicates that the author believes the class to be immutable and hence inherently 11 | * thread-safe. An immutable class is one where the state of an instance cannot be seen to change. As a result 12 | *

17 | * Performance optimization may mean that instances of an immutable class may have mutable internal state. The critical 18 | * point is that callers cannot tell the difference. For example {@link String} is an immutable class, despite having an 19 | * internal int that is non-final but used as a cache for {@link String#hashCode()}. 20 | *

21 | * Immutable objects are inherently thread-safe; they may be passed between threads or published without 22 | * synchronization. 23 | *

24 | * This annotation is intended for internal use in OSHI as a temporary workaround until it is available in 25 | * {@code jakarta.annotations}. 26 | */ 27 | @Documented 28 | public @interface Immutable { 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/annotation/concurrent/NotThreadSafe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.annotation.concurrent; 6 | 7 | import java.lang.annotation.Documented; 8 | 9 | /** 10 | * The presence of this annotation indicates that the author believes the class is not thread-safe. The absence of this 11 | * annotation does not indicate that the class is thread-safe, instead this annotation is for cases where a naïve 12 | * assumption could be easily made that the class is thread-safe. In general, it is a bad plan to assume a class is 13 | * thread safe without good reason. 14 | *

15 | * This annotation is intended for internal use in OSHI as a temporary workaround until it is available in 16 | * {@code jakarta.annotations}. 17 | */ 18 | @Documented 19 | public @interface NotThreadSafe { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/annotation/concurrent/ThreadSafe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.annotation.concurrent; 6 | 7 | import java.lang.annotation.Documented; 8 | 9 | /** 10 | * The presence of this annotation indicates that the author believes the class to be thread-safe. As such, there should 11 | * be no sequence of accessing the public methods or fields that could put an instance of this class into an invalid 12 | * state, irrespective of any rearrangement of those operations by the Java Runtime and without introducing any 13 | * requirements for synchronization or coordination by the caller/accessor. 14 | *

15 | * This annotation is intended for internal use in OSHI as a temporary workaround until it is available in 16 | * {@code jakarta.annotations}. 17 | */ 18 | @Documented 19 | public @interface ThreadSafe { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/annotation/concurrent/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * Provides annotations to document thread safety 7 | */ 8 | package ooo.oshi.annotation.concurrent; 9 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/foreign/mac/SystemLibrary.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.foreign.mac; 6 | 7 | import static java.lang.foreign.MemoryLayout.paddingLayout; 8 | import static java.lang.foreign.MemoryLayout.sequenceLayout; 9 | import static java.lang.foreign.ValueLayout.ADDRESS; 10 | import static java.lang.foreign.ValueLayout.JAVA_BYTE; 11 | import static java.lang.foreign.ValueLayout.JAVA_INT; 12 | import static java.lang.foreign.ValueLayout.JAVA_LONG; 13 | 14 | import java.lang.foreign.Addressable; 15 | import java.lang.foreign.FunctionDescriptor; 16 | import java.lang.foreign.GroupLayout; 17 | import java.lang.foreign.Linker; 18 | import java.lang.foreign.MemoryAddress; 19 | import java.lang.foreign.MemoryLayout; 20 | import java.lang.foreign.SymbolLookup; 21 | import java.lang.invoke.MethodHandle; 22 | 23 | public class SystemLibrary { 24 | 25 | private static final SymbolLookup SYS; 26 | private static final Linker LINKER; 27 | 28 | static { 29 | LINKER = Linker.nativeLinker(); 30 | SYS = LINKER.defaultLookup(); 31 | } 32 | 33 | // Data size 34 | public static final int BYTE_SIZE = (int) JAVA_BYTE.byteSize(); 35 | public static final int INT_SIZE = (int) JAVA_INT.byteSize(); 36 | public static final int LONG_SIZE = (int) JAVA_LONG.byteSize(); 37 | 38 | // params.h 39 | public static final int MAXCOMLEN = 16; 40 | public static final int MAXPATHLEN = 1024; 41 | public static final int PROC_PIDPATHINFO_MAXSIZE = MAXPATHLEN * INT_SIZE; 42 | 43 | // proc_info.h 44 | public static final int PROC_ALL_PIDS = 1; 45 | public static final int PROC_PIDTASKALLINFO = 2; 46 | public static final int PROC_PIDVNODEPATHINFO = 9; 47 | 48 | // resource.h 49 | public static final int RUSAGE_INFO_V2 = 2; 50 | 51 | private static final MethodHandle methodHandle(String methodName, FunctionDescriptor fd) { 52 | return LINKER.downcallHandle(SYS.lookup(methodName).orElseThrow(), fd); 53 | } 54 | 55 | /** 56 | * Gets the last error value ({@code errno}). 57 | * 58 | * @return the value of the native errno variable. 59 | */ 60 | public static int errno() { 61 | try { 62 | MemoryAddress addr = (MemoryAddress) errno.invokeExact(); 63 | return addr.get(JAVA_INT, 0); 64 | } catch (Throwable e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | 69 | private static final MethodHandle errno = methodHandle("__error", FunctionDescriptor.of(ADDRESS)); 70 | 71 | /** 72 | * Returns the process ID of the calling process. The ID is guaranteed to be unique and is useful for constructing 73 | * temporary file names. 74 | * 75 | * @return the process ID of the calling process. 76 | */ 77 | public static int getpid() { 78 | try { 79 | return (int) getpid.invokeExact(); 80 | } catch (Throwable e) { 81 | throw new RuntimeException(e); 82 | } 83 | } 84 | 85 | private static final MethodHandle getpid = methodHandle("getpid", FunctionDescriptor.of(JAVA_INT)); 86 | 87 | /** 88 | * Search through the current processes 89 | * 90 | * @param type types of processes to be searched 91 | * @param typeinfo adjunct information for type 92 | * @param pids a C array of int-sized values to be filled with process identifiers that hold an open file 93 | * reference matching the specified path or volume. Pass NULL to obtain the minimum buffer size 94 | * needed to hold the currently active processes. 95 | * @param bufferSize the size (in bytes) of the provided buffer. 96 | * @return the number of bytes of data returned in the provided buffer; -1 if an error was encountered; 97 | */ 98 | public static int proc_listpids(int type, int typeinfo, Addressable pids, int bufferSize) { 99 | try { 100 | return (int) proc_listpids.invokeExact(type, typeinfo, pids, bufferSize); 101 | } catch (Throwable e) { 102 | throw new RuntimeException(e); 103 | } 104 | } 105 | 106 | private static final MethodHandle proc_listpids = methodHandle("proc_listpids", 107 | FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT)); 108 | 109 | /** 110 | * Return in buffer a proc_*info structure corresponding to the flavor for the specified process 111 | * 112 | * @param pid the process identifier 113 | * @param flavor the type of information requested 114 | * @param arg argument possibly needed for some flavors 115 | * @param buffer holds results 116 | * @param buffersize size of results 117 | * @return the number of bytes of data returned in the provided buffer; -1 if an error was encountered; 118 | */ 119 | public static int proc_pidinfo(int pid, int flavor, long arg, Addressable buffer, int buffersize) { 120 | try { 121 | return (int) proc_pidinfo.invokeExact(pid, flavor, arg, buffer, buffersize); 122 | } catch (Throwable e) { 123 | throw new RuntimeException(e); 124 | } 125 | } 126 | 127 | private static final MethodHandle proc_pidinfo = methodHandle("proc_pidinfo", 128 | FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT, JAVA_LONG, ADDRESS, JAVA_INT)); 129 | 130 | /** 131 | * Return in buffer the name of the specified process 132 | * 133 | * @param pid the process identifier 134 | * @param buffer holds results 135 | * @param buffersize size of results 136 | * @return the length of the name returned in buffer if successful; 0 otherwise 137 | */ 138 | public static int proc_pidpath(int pid, Addressable buffer, int buffersize) { 139 | try { 140 | return (int) proc_pidpath.invokeExact(pid, buffer, buffersize); 141 | } catch (Throwable e) { 142 | throw new RuntimeException(e); 143 | } 144 | } 145 | 146 | private static final MethodHandle proc_pidpath = methodHandle("proc_pidpath", 147 | FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT)); 148 | 149 | /** 150 | * Return resource usage information for the given pid, which can be a live process or a zombie. 151 | * 152 | * @param pid the process identifier 153 | * @param flavor the type of information requested 154 | * @param buffer holds results 155 | * @return 0 on success; or -1 on failure, with errno set to indicate the specific error. 156 | */ 157 | public static int proc_pid_rusage(int pid, int flavor, Addressable buffer) { 158 | try { 159 | return (int) proc_pid_rusage.invokeExact(pid, flavor, buffer); 160 | } catch (Throwable e) { 161 | throw new RuntimeException(e); 162 | } 163 | } 164 | 165 | private static final MethodHandle proc_pid_rusage = methodHandle("proc_pidpath", 166 | FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT, ADDRESS)); 167 | 168 | /** 169 | * This function searches the password database for the given user uid, always returning the first one encountered. 170 | * 171 | * @param uid The user ID 172 | * @return an address to a Passwd structure matching that user 173 | */ 174 | public static MemoryAddress getpwuid(int uid) { 175 | try { 176 | return (MemoryAddress) getpwuid.invokeExact(uid); 177 | } catch (Throwable e) { 178 | throw new RuntimeException(e); 179 | } 180 | } 181 | 182 | private static final MethodHandle getpwuid = methodHandle("getpwuid", FunctionDescriptor.of(ADDRESS, JAVA_INT)); 183 | 184 | /** 185 | * This function searches the group database for the given group name pointed to by the group id given by gid, 186 | * returning the first one encountered. Identical group gids may result in undefined behavior. 187 | * 188 | * @param gid The group ID 189 | * @return an address to a Group structure matching that group 190 | */ 191 | public static MemoryAddress getgrgid(int gid) { 192 | try { 193 | return (MemoryAddress) getgrgid.invokeExact(gid); 194 | } catch (Throwable e) { 195 | throw new RuntimeException(e); 196 | } 197 | } 198 | 199 | private static final MethodHandle getgrgid = methodHandle("getgrgid", FunctionDescriptor.of(ADDRESS, JAVA_INT)); 200 | 201 | /** 202 | * The sysctl() function retrieves system information and allows processes with appropriate privileges to set system 203 | * information. The information available from sysctl() consists of integers, strings, and tables. 204 | *

205 | * The state is described using a "Management Information Base" (MIB) style name, listed in name, which is a namelen 206 | * length array of integers. 207 | *

208 | * The information is copied into the buffer specified by oldp. The size of the buffer is given by the location 209 | * specified by oldlenp before the call, and that location gives the amount of data copied after a successful call 210 | * and after a call that returns with the error code ENOMEM. If the amount of data available is greater than the 211 | * size of the buffer supplied, the call supplies as much data as fits in the buffer provided and returns with the 212 | * error code ENOMEM. If the old value is not desired, oldp and oldlenp should be set to NULL. 213 | *

214 | * The size of the available data can be determined by calling sysctl() with the NULL argument for oldp. The size of 215 | * the available data will be returned in the location pointed to by oldlenp. For some operations, the amount of 216 | * space may change often. For these operations, the system attempts to round up so that the returned size is large 217 | * enough for a call to return the data shortly thereafter. 218 | *

219 | * To set a new value, newp is set to point to a buffer of length newlen from which the requested value is to be 220 | * taken. If a new value is not to be set, newp should be set to NULL and newlen set to 0. 221 | * 222 | * @param name a Management Information Base (MIB) array of integers 223 | * @param namelen the length of the array in {@code name} 224 | * @param oldp A buffer to hold the information retrieved 225 | * @param oldlenp Size of the buffer, a pointer to a {@link com.sun.jna.platform.unix.LibCAPI.size_t} value 226 | * @param newp To set a new value, a buffer of information to be written. May be null if no value is to be set. 227 | * @param newlen Size of the information to be written. May be 0 if no value is to be set. 228 | * @return 0 on success; sets errno on failure 229 | */ 230 | public static int sysctl(Addressable name, int namelen, Addressable oldp, Addressable oldlenp, Addressable newp, 231 | long newlen) { 232 | try { 233 | return (int) sysctl.invokeExact(name, namelen, oldp, oldlenp, newp, newlen); 234 | } catch (Throwable e) { 235 | throw new RuntimeException(e); 236 | } 237 | } 238 | 239 | private static final MethodHandle sysctl = methodHandle("sysctl", 240 | FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, ADDRESS, ADDRESS, ADDRESS, JAVA_LONG)); 241 | 242 | /** 243 | * The sysctlbyname() function accepts an ASCII representation of the name and internally looks up the integer name 244 | * vector. Apart from that, it behaves the same as the standard sysctl() function. 245 | * 246 | * @param name ASCII representation of the MIB name 247 | * @param oldp A buffer to hold the information retrieved 248 | * @param oldlenp Size of the buffer, a pointer to a {@link com.sun.jna.platform.unix.LibCAPI.size_t} value 249 | * @param newp To set a new value, a buffer of information to be written. May be null if no value is to be set. 250 | * @param newlen Size of the information to be written. May be 0 if no value is to be set. 251 | * @return 0 on success; sets errno on failure 252 | */ 253 | public static int sysctlbyname(Addressable name, Addressable oldp, Addressable oldlenp, Addressable newp, 254 | long newlen) { 255 | try { 256 | return (int) sysctlbyname.invokeExact(name, oldp, oldlenp, newp, newlen); 257 | } catch (Throwable e) { 258 | throw new RuntimeException(e); 259 | } 260 | } 261 | 262 | private static final MethodHandle sysctlbyname = methodHandle("sysctlbyname", 263 | FunctionDescriptor.of(JAVA_INT, ADDRESS, ADDRESS, ADDRESS, ADDRESS, JAVA_LONG)); 264 | 265 | public static final GroupLayout PROC_BSD_INFO = MemoryLayout.structLayout( // 266 | JAVA_INT.withName("pbi_flags"), // 267 | JAVA_INT.withName("pbi_status"), // 268 | JAVA_INT.withName("pbi_xstatus"), // 269 | JAVA_INT.withName("pbi_pid"), // 270 | JAVA_INT.withName("pbi_ppid"), // 271 | JAVA_INT.withName("pbi_uid"), // 272 | JAVA_INT.withName("pbi_gid"), // 273 | JAVA_INT.withName("pbi_ruid"), // 274 | JAVA_INT.withName("pbi_rgid"), // 275 | JAVA_INT.withName("pbi_svuid"), // 276 | JAVA_INT.withName("pbi_svgid"), // 277 | JAVA_INT.withName("rfu_1"), // 278 | sequenceLayout(MAXCOMLEN, JAVA_BYTE).withName("pbi_comm"), // 279 | sequenceLayout(2 * MAXCOMLEN, JAVA_BYTE).withName("pbi_name"), // 280 | JAVA_INT.withName("pbi_nfiles"), // 281 | JAVA_INT.withName("pbi_pgid"), // 282 | JAVA_INT.withName("pbi_pjobc"), // 283 | JAVA_INT.withName("e_tdev"), // 284 | JAVA_INT.withName("e_tpgid"), // 285 | JAVA_INT.withName("pbi_nice"), // 286 | JAVA_LONG.withName("pbi_start_tvsec"), // 287 | JAVA_LONG.withName("pbi_start_tvusec")); 288 | 289 | public static final GroupLayout PROC_TASK_INFO = MemoryLayout.structLayout( // 290 | JAVA_LONG.withName("pti_virtual_size"), // virtual memory size (bytes) 291 | JAVA_LONG.withName("pti_resident_size"), // resident memory size (bytes) 292 | JAVA_LONG.withName("pti_total_user"), // total time (nanoseconds) 293 | JAVA_LONG.withName("pti_total_system"), // 294 | JAVA_LONG.withName("pti_threads_user"), // existing threads only 295 | JAVA_LONG.withName("pti_threads_system"), // 296 | JAVA_INT.withName("pti_policy"), // default policy for new threads 297 | JAVA_INT.withName("pti_faults"), // number of page faults 298 | JAVA_INT.withName("pti_pageins"), // number of actual pageins 299 | JAVA_INT.withName("pti_cow_faults"), // number of copy-on-write faults 300 | JAVA_INT.withName("pti_messages_sent"), // number of messages sent 301 | JAVA_INT.withName("pti_messages_received"), // number of messages received 302 | JAVA_INT.withName("pti_syscalls_mach"), // number of mach system calls 303 | JAVA_INT.withName("pti_syscalls_unix"), // number of unix system calls 304 | JAVA_INT.withName("pti_csw"), // number of context switches 305 | JAVA_INT.withName("pti_threadnum"), // number of threads in the task 306 | JAVA_INT.withName("pti_numrunning"), // number of running threads 307 | JAVA_INT.withName("pti_priority")); // task priority 308 | 309 | public static final GroupLayout PROC_TASK_ALL_INFO = MemoryLayout.structLayout( // 310 | PROC_BSD_INFO.withName("pbsd"), // 311 | PROC_TASK_INFO.withName("ptinfo")); 312 | 313 | public static final GroupLayout PASSWD = MemoryLayout.structLayout( // 314 | ADDRESS.withName("pw_name"), // user name 315 | ADDRESS.withName("pw_passwd"), // encrypted password 316 | JAVA_INT.withName("pw_uid"), // user uid 317 | JAVA_INT.withName("pw_gid"), // user gid 318 | JAVA_LONG.withName("pw_change"), // password change time 319 | ADDRESS.withName("pw_class"), // user access class 320 | ADDRESS.withName("pw_gecos"), // Honeywell login info 321 | ADDRESS.withName("pw_dir"), // home directory 322 | ADDRESS.withName("pw_shell"), // default shell 323 | JAVA_LONG.withName("pw_expire"), // account expiration 324 | ADDRESS.withName("pw_fields")); // internal: fields filled in 325 | 326 | public static final GroupLayout GROUP = MemoryLayout.structLayout( // 327 | ADDRESS.withName("gr_name"), // group name 328 | ADDRESS.withName("gr_passwd"), // group password 329 | ADDRESS.withName("gr_gid"), // group id 330 | ADDRESS.withName("gr_mem")); // group members 331 | 332 | public static final GroupLayout RUSAGEINFOV2 = MemoryLayout.structLayout( // 333 | sequenceLayout(16, JAVA_BYTE).withName("ri_uuid"), // 334 | JAVA_LONG.withName("ri_user_time"), // 335 | JAVA_LONG.withName("ri_system_time"), // 336 | JAVA_LONG.withName("ri_pkg_idle_wkups"), // 337 | JAVA_LONG.withName("ri_interrupt_wkups"), // 338 | JAVA_LONG.withName("ri_pageins"), // 339 | JAVA_LONG.withName("ri_wired_size"), // 340 | JAVA_LONG.withName("ri_resident_size"), // 341 | JAVA_LONG.withName("ri_phys_footprint"), // 342 | JAVA_LONG.withName("ri_proc_start_abstime"), // 343 | JAVA_LONG.withName("ri_proc_exit_abstime"), // 344 | JAVA_LONG.withName("ri_child_user_time"), // 345 | JAVA_LONG.withName("ri_child_system_time"), // 346 | JAVA_LONG.withName("ri_child_pkg_idle_wkups"), // 347 | JAVA_LONG.withName("ri_child_interrupt_wkups"), // 348 | JAVA_LONG.withName("ri_child_pageins"), // 349 | JAVA_LONG.withName("ri_child_elapsed_abstime"), // 350 | JAVA_LONG.withName("ri_diskio_bytesread"), // 351 | JAVA_LONG.withName("ri_diskio_byteswritten")); 352 | 353 | public static final GroupLayout VNODE_INFO_PATH = MemoryLayout.structLayout( // 354 | paddingLayout(152 * 8), // vnode_info but we don't need its data 355 | sequenceLayout(MAXPATHLEN, JAVA_BYTE).withName("vip_path")); 356 | 357 | public static final GroupLayout VNODE_PATH_INFO = MemoryLayout.structLayout( // 358 | VNODE_INFO_PATH.withName("pvi_cdir"), // 359 | VNODE_INFO_PATH.withName("pvi_rdir")); 360 | } 361 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/foreign/windows/Kernel32Library.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.foreign.windows; 6 | 7 | import static java.lang.foreign.ValueLayout.JAVA_INT; 8 | import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN; 9 | import static java.lang.foreign.ValueLayout.ADDRESS; 10 | 11 | import java.lang.foreign.Addressable; 12 | import java.lang.foreign.FunctionDescriptor; 13 | import java.lang.foreign.Linker; 14 | import java.lang.foreign.MemoryAddress; 15 | import java.lang.foreign.MemorySegment; 16 | import java.lang.foreign.SegmentAllocator; 17 | import java.lang.foreign.SymbolLookup; 18 | import java.lang.foreign.ValueLayout; 19 | import java.lang.invoke.MethodHandle; 20 | 21 | import ooo.oshi.util.ParseUtil; 22 | 23 | public class Kernel32Library { 24 | 25 | private static final SymbolLookup K32; 26 | private static final Linker LINKER; 27 | 28 | static { 29 | LINKER = Linker.nativeLinker(); 30 | System.loadLibrary("Kernel32"); 31 | K32 = SymbolLookup.loaderLookup(); 32 | } 33 | 34 | private static final MethodHandle methodHandle(String methodName, FunctionDescriptor fd) { 35 | return LINKER.downcallHandle(K32.lookup(methodName).orElseThrow(), fd); 36 | } 37 | 38 | /** 39 | * Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. 40 | * Multiple threads do not overwrite each other's last-error code. 41 | * 42 | * @return the calling thread's last-error code. 43 | */ 44 | public static int getLastError() { 45 | try { 46 | return (int) getLastError.invoke(); 47 | } catch (Throwable e) { 48 | throw new RuntimeException(e); 49 | } 50 | } 51 | 52 | private static final MethodHandle getLastError = methodHandle("GetLastError", 53 | FunctionDescriptor.of(ValueLayout.JAVA_INT)); 54 | 55 | /** 56 | * Returns the process ID of the calling process. The ID is guaranteed to be unique and is useful for constructing 57 | * temporary file names. 58 | * 59 | * @return the process ID of the calling process. 60 | */ 61 | public static int getCurrentProcessId() { 62 | try { 63 | return (int) getCurrentProcessId.invoke(); 64 | } catch (Throwable e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | 69 | private static final MethodHandle getCurrentProcessId = methodHandle("GetCurrentProcessId", 70 | FunctionDescriptor.of(JAVA_INT)); 71 | 72 | /** 73 | * Retrieves the NetBIOS name of the local computer. This name is established at system startup, when the system 74 | * reads it from the registry. 75 | * 76 | * @return the NetBIOS name of the local computer. 77 | */ 78 | public static String getComputerName() { 79 | try { 80 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 81 | Addressable buffer = allocator.allocate(2 * (WinBase.MAX_COMPUTERNAME_LENGTH + 1)); 82 | Addressable size = allocator.allocate(JAVA_INT, 2 * (WinBase.MAX_COMPUTERNAME_LENGTH + 1)); 83 | if(!(boolean) getComputerName.invokeExact(buffer, size)) { 84 | throw new Exception("GetLastError() returned " + getLastError()); 85 | } 86 | byte [] bytes = ((MemorySegment)buffer).toArray(ValueLayout.JAVA_BYTE); 87 | return ParseUtil.parseByteArrayToUtf16(bytes); 88 | } catch (Throwable e) { 89 | throw new RuntimeException(e); 90 | } 91 | } 92 | 93 | private static final MethodHandle getComputerName = methodHandle("GetComputerNameW", 94 | FunctionDescriptor.of(JAVA_BOOLEAN, ADDRESS, ADDRESS)); 95 | 96 | /** 97 | * Retrieves the path of the directory designated for temporary files. 98 | * 99 | * @return the temporary file path. 100 | */ 101 | public static String getTempPath() { 102 | try { 103 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 104 | // TODO: Handle case if Temp path length exceeds predefined buffer size 105 | Addressable buffer = allocator.allocate(WinBase.MAX_PATH); 106 | int nBufferLength = WinBase.MAX_PATH; 107 | if ((int) getTempPath.invoke(nBufferLength, buffer) == 0) { 108 | throw new Exception("GetLastError() returned " + getLastError()); 109 | } 110 | byte[] bytes = ((MemorySegment) buffer).toArray(ValueLayout.JAVA_BYTE); 111 | return ParseUtil.parseByteArrayToUtf16(bytes); 112 | } catch (Throwable e) { 113 | throw new RuntimeException(e); 114 | } 115 | } 116 | 117 | private static final MethodHandle getTempPath = methodHandle("GetTempPathW", 118 | FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS)); 119 | 120 | /** 121 | * Closes an open object handle. 122 | * 123 | * @param h The handle to be closed 124 | */ 125 | public static void closeHandle(Addressable h) { 126 | if (h == null) { 127 | return; 128 | } 129 | 130 | try { 131 | if (!(boolean) closeHandle.invokeExact(h)) { 132 | throw new Exception("GetLastError() returned " + getLastError()); 133 | } 134 | } catch (Throwable e) { 135 | throw new RuntimeException(e); 136 | } 137 | } 138 | 139 | private static final MethodHandle closeHandle = methodHandle("CloseHandle", 140 | FunctionDescriptor.of(JAVA_BOOLEAN, ADDRESS)); 141 | 142 | 143 | /** 144 | * Opens an existing local process object. 145 | * 146 | * @param desiredAccess The access to the process object. 147 | * @param inheritHandle Processes created by this process should inherit the handle 148 | * @param processId The identifier of the local process to be opened. 149 | * @return the open handle to the specified process. 150 | */ 151 | public static Addressable openProcess(int desiredAccess, boolean inheritHandle, int processId) { 152 | try { 153 | Addressable hProcess = (MemoryAddress) openProcess.invokeExact(desiredAccess, inheritHandle, processId); 154 | if (hProcess == null) { 155 | throw new Exception("GetLastError() returned " + getLastError()); 156 | } 157 | return hProcess; 158 | } catch (Throwable e) { 159 | throw new RuntimeException(e); 160 | } 161 | } 162 | 163 | private static final MethodHandle openProcess = methodHandle("OpenProcess", 164 | FunctionDescriptor.of(ADDRESS, JAVA_INT, JAVA_BOOLEAN, JAVA_INT)); 165 | 166 | private static final String queryFullProcessImageName(Addressable hProcess, int dwFlags) { 167 | try { 168 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 169 | int size = WinBase.MAX_PATH; // Start with MAX_PATH, then increment with 1024 each iteration 170 | // TODO: Handle case if process image length exceeds predefined buffer size 171 | Addressable lpExeName = allocator.allocate(size); 172 | Addressable lpdwSize = allocator.allocate(JAVA_INT, size); 173 | if (!(boolean) queryFullProcessImageName.invokeExact(hProcess, dwFlags, lpExeName, lpdwSize)) { 174 | throw new Exception("GetLastError() returned " + getLastError()); 175 | } 176 | byte[] bytes = ((MemorySegment) lpExeName).toArray(ValueLayout.JAVA_BYTE); 177 | return ParseUtil.parseByteArrayToUtf16(bytes); 178 | } catch (Throwable e) { 179 | throw new RuntimeException(e); 180 | } 181 | } 182 | 183 | private static final MethodHandle queryFullProcessImageName = methodHandle("QueryFullProcessImageNameW", 184 | FunctionDescriptor.of(JAVA_BOOLEAN, ADDRESS, JAVA_INT, ADDRESS, ADDRESS)); 185 | 186 | /** 187 | * Retrieves the full name of the executable image for the specified process. 188 | * 189 | * @param pid The identifier of the local process to be opened. 190 | * @param dwFlags flags for path format of returned path. 191 | * @return the full name of the executable image for the specified process. 192 | */ 193 | public static final String queryFullProcessImageName(int pid, int dwFlags) { 194 | Addressable hProcess = null; 195 | RuntimeException re = null; 196 | 197 | try { 198 | hProcess = (Addressable) openProcess(WinBase.PROCESS_QUERY_INFORMATION | WinBase.PROCESS_VM_READ, false, 199 | pid); 200 | if (hProcess == null) { 201 | throw new RuntimeException("GetLastError() returned " + getLastError()); 202 | } 203 | return queryFullProcessImageName(hProcess, dwFlags); 204 | } catch (RuntimeException e) { 205 | re = e; 206 | throw re; // re-throw to invoke finally block 207 | } finally { 208 | try { 209 | closeHandle(hProcess); 210 | } catch (RuntimeException e) { 211 | if (re == null) { 212 | re = e; 213 | } else { 214 | // Suppress Runtime Exception for closeHandle 215 | } 216 | } 217 | if (re != null) { 218 | throw re; 219 | } 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/foreign/windows/WinBase.java: -------------------------------------------------------------------------------- 1 | package ooo.oshi.foreign.windows; 2 | 3 | 4 | public class WinBase { 5 | 6 | public static final int MAX_COMPUTERNAME_LENGTH = 31; 7 | 8 | public static final int MAX_PATH = 260; 9 | 10 | public static final int PROCESS_QUERY_INFORMATION = 0x0400; 11 | 12 | public static final int PROCESS_VM_READ = 0x0010; 13 | 14 | public static final int ERROR_INSUFFICIENT_BUFFER = 122; 15 | 16 | public static final int PROCESS_NAME_NATIVE = 1; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/hardware/HardwareAbstractionLayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.hardware; 6 | 7 | public interface HardwareAbstractionLayer { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/hardware/linux/LinuxHardwareAbstractionLayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.hardware.linux; 6 | 7 | import ooo.oshi.hardware.HardwareAbstractionLayer; 8 | 9 | public class LinuxHardwareAbstractionLayer implements HardwareAbstractionLayer { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/hardware/mac/MacHardwareAbstractionLayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.hardware.mac; 6 | 7 | import ooo.oshi.hardware.HardwareAbstractionLayer; 8 | 9 | public class MacHardwareAbstractionLayer implements HardwareAbstractionLayer { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/hardware/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * [oshi-ffm API] Provides cross-platform implementation to retrieve hardware information such as CPU, Memory, Display, 7 | * Disks, Network Interfaces, Power Sources, Sensors, and USB Devices 8 | */ 9 | package ooo.oshi.hardware; 10 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/hardware/windows/WindowsHardwareAbstractionLayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.hardware.windows; 6 | 7 | import ooo.oshi.hardware.HardwareAbstractionLayer; 8 | 9 | public class WindowsHardwareAbstractionLayer implements HardwareAbstractionLayer { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * [oshi-ffm API] Provides a cross-platform implementation to retrieve Operating System and Hardware Information, such 7 | * as OS version, memory, CPU, disk, devices, sensors, etc. 8 | */ 9 | package ooo.oshi; 10 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/OSProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import ooo.oshi.annotation.concurrent.ThreadSafe; 11 | 12 | /** 13 | * Represents a Process on the operating system, which may contain multiple threads. 14 | */ 15 | @ThreadSafe 16 | public interface OSProcess { 17 | 18 | /** 19 | * Gets the name of the process, often the executable program. 20 | * 21 | * @return the name of the process. 22 | */ 23 | String getName(); 24 | 25 | /** 26 | * Gets the full filesystem path of the executing process. 27 | * 28 | * @return the full path of the executing process. 29 | */ 30 | String getPath(); 31 | 32 | /** 33 | * Gets the process command line used to start the process, including arguments if available to be determined. This 34 | * method generally returns the same information as {@link #getArguments()} in a more user-readable format, and is 35 | * more robust to non-elevated access. 36 | *

37 | * The format of this string is platform-dependent, may be truncated, and may require the end user to parse the 38 | * result. Users should generally prefer {@link #getArguments()} which already parses the results, and use this 39 | * method as a backup. 40 | *

41 | * On AIX and Solaris, the string may be truncated to 80 characters if there was insufficient permission to read the 42 | * process memory. 43 | *

44 | * On Windows, attempts to retrieve the value from process memory, which requires that the process be owned by the 45 | * same user as the executing process, or elevated permissions, and additionally requires the target process to have 46 | * the same bitness (e.g., this will fail on a 32-bit process if queried by 64-bit and vice versa). If reading 47 | * process memory fails, by default, performs a single WMI query for this process, with some latency. If this method 48 | * will be frequently called for multiple processes, see the configuration file to enable a batch query mode 49 | * leveraging {@link Win32ProcessCached#getCommandLine} to improve performance, or setting that parameter via 50 | * {@link GlobalConfig#set(String, Object)} before instantiating any {@link OSProcess} object. 51 | * 52 | * @return the process command line. 53 | */ 54 | String getCommandLine(); 55 | 56 | /** 57 | * Makes a best effort attempt to get a list of the the command-line arguments of the process. Returns the same 58 | * information as {@link #getCommandLine()} but parsed to a list. May require elevated permissions or same-user 59 | * ownership. 60 | * 61 | * @return A list of Strings representing the arguments. May return an empty list if there was a failure (for 62 | * example, because the process is already dead or permission was denied). 63 | */ 64 | List getArguments(); 65 | 66 | /** 67 | * Makes a best effort attempt to obtain the environment variables of the process. May require elevated permissions 68 | * or same-user ownership. 69 | * 70 | * @return A map representing the environment variables and their values. May return an empty map if there was a 71 | * failure (for example, because the process is already dead or permission was denied). 72 | */ 73 | Map getEnvironmentVariables(); 74 | 75 | /** 76 | * Makes a best effort attempt to obtain the current working directory for the process. 77 | * 78 | * @return the process current working directory. 79 | */ 80 | String getCurrentWorkingDirectory(); 81 | 82 | /** 83 | * Gets the user name of the process owner. 84 | * 85 | * @return the user name. On Windows systems, also returns the domain prepended to the username. 86 | */ 87 | String getUser(); 88 | 89 | /** 90 | * Gets the user id of the process owner. 91 | * 92 | * @return the userID. On Windows systems, returns the Security ID (SID) 93 | */ 94 | String getUserID(); 95 | 96 | /** 97 | * Gets the group under which the process is executing. 98 | *

99 | * On Windows systems, populating this value for processes other than the current user requires administrative 100 | * privileges (and still may fail for some system processes) and can incur significant latency. When successful, 101 | * returns a the default primary group with access to this process, corresponding to the SID in 102 | * {@link #getGroupID()}. 103 | * 104 | * @return the group. 105 | */ 106 | String getGroup(); 107 | 108 | /** 109 | * Gets the group id under which the process is executing. 110 | *

111 | * On Windows systems, populating this value for processes other than the current user requires administrative 112 | * privileges (and still may fail for some system processes) and can incur significant latency. When successful, 113 | * returns the default primary group SID with access to this process, corresponding to the name in 114 | * {@link #getGroup()}. 115 | * 116 | * @return the groupID. 117 | */ 118 | String getGroupID(); 119 | 120 | /** 121 | * Gets the process state. 122 | * 123 | * @return the execution state of the process. 124 | */ 125 | State getState(); 126 | 127 | /** 128 | * Gets the process ID. 129 | *

130 | * While this is a 32-bit value, it is unsigned on Windows and in extremely rare circumstances may return a negative 131 | * value. 132 | * 133 | * @return the processID. 134 | */ 135 | int getProcessID(); 136 | 137 | /** 138 | * Gets the process ID of this process's parent. 139 | * 140 | * @return the parentProcessID, if any; 0 otherwise. 141 | */ 142 | int getParentProcessID(); 143 | 144 | /** 145 | * Gets the number of threads being executed by this process. More information is available using 146 | * {@link #getThreadDetails()}. 147 | * 148 | * @return the number of threads in this process. 149 | */ 150 | int getThreadCount(); 151 | 152 | /** 153 | * Gets the priority of this process. 154 | *

155 | * For Linux and Unix, priority is a value in the range -20 to 19 (20 on some systems). The default priority is 0; 156 | * lower priorities cause more favorable scheduling. 157 | *

158 | * For Windows, priority values can range from 0 (lowest priority) to 31 (highest priority). 159 | *

160 | * macOS has 128 priority levels, ranging from 0 (lowest priority) to 127 (highest priority). They are divided into 161 | * several major bands: 0 through 51 are the normal levels; the default priority is 31. 52 through 79 are the 162 | * highest priority regular threads; 80 through 95 are for kernel mode threads; and 96 through 127 correspond to 163 | * real-time threads, which are treated differently than other threads by the scheduler. 164 | * 165 | * @return the priority of this process. 166 | */ 167 | int getPriority(); 168 | 169 | /** 170 | * Gets the Virtual Memory Size (VSZ). Includes all memory that the process can access, including memory that is 171 | * swapped out and memory that is from shared libraries. 172 | * 173 | * @return the Virtual Memory Size 174 | */ 175 | long getVirtualSize(); 176 | 177 | /** 178 | * Gets the Resident Set Size (RSS). Used to show how much memory is allocated to that process and is in RAM. It 179 | * does not include memory that is swapped out. It does include memory from shared libraries as long as the pages 180 | * from those libraries are actually in memory. It does include all stack and heap memory. 181 | *

182 | * On Windows, returns the Private Working Set size, which should match the "Memory" column in the Windows Task 183 | * Manager. 184 | *

185 | * On Linux, returns the RSS value from {@code /proc/[pid]/stat}, which may be inaccurate because of a 186 | * kernel-internal scalability optimization. If accurate values are required, read {@code /proc/[pid]/smaps} using 187 | * {@link FileUtil#getKeyValueMapFromFile(String, String)}. 188 | * 189 | * @return the Resident Set Size 190 | */ 191 | long getResidentSetSize(); 192 | 193 | /** 194 | * Gets kernel/system (privileged) time used by the process. 195 | * 196 | * @return the number of milliseconds the process has executed in kernel/system mode. 197 | */ 198 | long getKernelTime(); 199 | 200 | /** 201 | * Gets user time used by the process. 202 | * 203 | * @return the number of milliseconds the process has executed in user mode. 204 | */ 205 | long getUserTime(); 206 | 207 | /** 208 | * Gets up time / elapsed time since the process started. 209 | * 210 | * @return the number of milliseconds since the process started. 211 | */ 212 | long getUpTime(); 213 | 214 | /** 215 | * Gets the process start time. 216 | * 217 | * @return the start time of the process in number of milliseconds since January 1, 1970 UTC. 218 | */ 219 | long getStartTime(); 220 | 221 | /** 222 | * Gets the bytes read by the process. 223 | *

224 | * On Solaris, includes both bytes read and written. 225 | * 226 | * @return the number of bytes the process has read from disk. 227 | */ 228 | long getBytesRead(); 229 | 230 | /** 231 | * Gets the bytes written by the process. 232 | *

233 | * On Solaris, all IO bytes are included read bytes so this value is 0. 234 | * 235 | * @return the number of bytes the process has written to disk. 236 | */ 237 | long getBytesWritten(); 238 | 239 | /** 240 | * Gets the number of open file handles (or network connections) that belongs to the process. 241 | *

242 | * On FreeBSD and Solaris, this value is only populated if information for a single process id is requested. 243 | * 244 | * @return open files or -1 if unknown or not supported 245 | */ 246 | long getOpenFiles(); 247 | 248 | /** 249 | * Gets cumulative CPU usage of this process. 250 | *

251 | * This calculation sums CPU ticks across all processors and may exceed 100% for multi-threaded processes. This is 252 | * consistent with the cumulative CPU presented by the "top" command on Linux/Unix machines. 253 | * 254 | * @return The proportion of up time that the process was executing in kernel or user mode. 255 | */ 256 | double getProcessCpuLoadCumulative(); 257 | 258 | /** 259 | * Gets CPU usage of this process since a previous snapshot of the same process, provided as a parameter. 260 | *

261 | * This calculation sums CPU ticks across all processors and may exceed 100% for multi-threaded processes. This is 262 | * consistent with process usage calulations on Linux/Unix machines, but should be divided by the number of logical 263 | * processors to match the value displayed by the Windows Task Manager. 264 | *

265 | * The accuracy of this calculation is dependent on both the number of threads on which the process is executing, 266 | * and the precision of the Operating System's tick counters. A polling interval of at least a few seconds is 267 | * recommended. 268 | * 269 | * @param proc An {@link OSProcess} object containing statistics for this same process collected at a prior point in 270 | * time. May be null. 271 | * 272 | * @return If the prior snapshot is for the same process at a prior point in time, the proportion of elapsed up time 273 | * between the current process snapshot and the previous one that the process was executing in kernel or 274 | * user mode. Returns cumulative load otherwise. 275 | */ 276 | double getProcessCpuLoadBetweenTicks(OSProcess proc); 277 | 278 | /** 279 | * Attempts to get the bitness (32 or 64) of the process. 280 | * 281 | * @return The bitness, if able to be determined, 0 otherwise. 282 | */ 283 | int getBitness(); 284 | 285 | /** 286 | * Gets the process affinity mask for this process. 287 | *

288 | * On Windows systems with more than 64 processors, if the threads of the calling process are in a single processor 289 | * group, returns the process affinity mask for that group (which may be zero if the specified process is running in 290 | * a different group). If the calling process contains threads in multiple groups, returns zero. 291 | *

292 | * Because macOS does not export interfaces that identify processors or control thread placement, explicit thread to 293 | * processor binding is not supported and this method will return a bitmask of all logical processors. 294 | *

295 | * If the Operating System fails to retrieve an affinity mask (e.g., the process has terminated), returns zero. 296 | * 297 | * @return a bit vector in which each bit represents the processors that a process is allowed to run on. 298 | */ 299 | long getAffinityMask(); 300 | 301 | /** 302 | * Attempts to update process attributes. Returns false if the update fails, which will occur if the process no 303 | * longer exists. 304 | * 305 | * @return {@code true} if the update was successful, false if the update failed. In addition, on a failed update 306 | * the process state will be changed to {@link State#INVALID}. 307 | */ 308 | boolean updateAttributes(); 309 | 310 | /** 311 | * Retrieves the threads of the process and their details. 312 | *

313 | * The amount of returned information is operating-system dependent and may incur some latency. 314 | * 315 | * @return a list of threads 316 | */ 317 | List getThreadDetails(); 318 | 319 | /** 320 | * Gets the number of minor (soft) faults the process has made which have not required loading a memory page from 321 | * disk. Sometimes called reclaims. 322 | *

323 | * On Windows, this includes the total of major and minor faults. 324 | *

325 | * Not available on AIX. 326 | * 327 | * @return minor page faults (reclaims). 328 | */ 329 | default long getMinorFaults() { 330 | return 0L; 331 | } 332 | 333 | /** 334 | * Gets the number of major (hard) faults the process has made which have required loading a memory page from disk. 335 | *

336 | * Windows does not distinguish major and minor faults at the process level, so this value returns 0 and major 337 | * faults are included in {@link #getMinorFaults()}. 338 | *

339 | * Not available on AIX. 340 | * 341 | * @return major page faults. 342 | */ 343 | default long getMajorFaults() { 344 | return 0L; 345 | } 346 | 347 | /** 348 | * A snapshot of the context switches the process has done. Since the context switches could be voluntary and 349 | * non-voluntary, this gives the sum of both. 350 | *

351 | * Not available on Windows. An approximation may be made by summing associated values from 352 | * {@link OSThread#getContextSwitches()}. 353 | *

354 | * Not available on AIX. 355 | * 356 | * @return sum of both voluntary and involuntary context switches if available, 0 otherwise. 357 | */ 358 | default long getContextSwitches() { 359 | return 0L; 360 | } 361 | 362 | /** 363 | * Process and Thread Execution States 364 | */ 365 | enum State { 366 | /** 367 | * Intermediate state in process creation 368 | */ 369 | NEW, 370 | /** 371 | * Actively executing process 372 | */ 373 | RUNNING, 374 | /** 375 | * Interruptible sleep state 376 | */ 377 | SLEEPING, 378 | /** 379 | * Blocked, uninterruptible sleep state 380 | */ 381 | WAITING, 382 | /** 383 | * Intermediate state in process termination 384 | */ 385 | ZOMBIE, 386 | /** 387 | * Stopped by the user, such as for debugging 388 | */ 389 | STOPPED, 390 | /** 391 | * Other or unknown states not defined 392 | */ 393 | OTHER, 394 | /** 395 | * The state resulting if the process fails to update statistics, probably due to termination. 396 | */ 397 | INVALID, 398 | /** 399 | * Special case of waiting if the process has been intentionally suspended (Windows only) 400 | */ 401 | SUSPENDED 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/OSThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os; 6 | 7 | import ooo.oshi.software.os.OSProcess.State; 8 | 9 | /** 10 | * Represents a Thread/Task on the operating system. 11 | */ 12 | public interface OSThread { 13 | 14 | /** 15 | * The thread id. The meaning of this value is OS-dependent. 16 | * 17 | * @return Returns the id of the thread. 18 | */ 19 | int getThreadId(); 20 | 21 | /** 22 | * The name of the thread. Presence of a name is operating-system dependent and may include information (such as an 23 | * index of running threads) that changes during execution. 24 | * 25 | * @return Returns the name of the task/thread. 26 | */ 27 | default String getName() { 28 | return ""; 29 | } 30 | 31 | /** 32 | * Gets the execution state of the task/thread. 33 | * 34 | * @return Returns the execution state of the task/thread. 35 | */ 36 | State getState(); 37 | 38 | /** 39 | * Gets cumulative CPU usage of this thread. 40 | * 41 | * @return The proportion of up time that the thread was executing in kernel or user mode. 42 | */ 43 | double getThreadCpuLoadCumulative(); 44 | 45 | /** 46 | * Gets CPU usage of this thread since a previous snapshot of the same thread, provided as a parameter. 47 | * 48 | * @param thread An {@link OSThread} object containing statistics for this same thread collected at a prior point in 49 | * time. May be null. 50 | * 51 | * @return If the prior snapshot is for the same thread at a prior point in time, the proportion of elapsed up time 52 | * between the current thread snapshot and the previous one that the thread was executing in kernel or user 53 | * mode. Returns cumulative load otherwise. 54 | */ 55 | double getThreadCpuLoadBetweenTicks(OSThread thread); 56 | 57 | /** 58 | * The owning process of this thread. For single-threaded processes, the owning process ID may be the same as the 59 | * thread's ID. 60 | * 61 | * @return The owning process of this thread. 62 | */ 63 | int getOwningProcessId(); 64 | 65 | /** 66 | * The memory address above which this thread can run. 67 | * 68 | * @return The start address. 69 | */ 70 | default long getStartMemoryAddress() { 71 | return 0L; 72 | } 73 | 74 | /** 75 | * A snapshot of the context switches the thread has done. Since the context switches could be voluntary and 76 | * non-voluntary, this gives the sum of both. 77 | *

78 | * Not available on AIX. 79 | * 80 | * @return sum of both voluntary and involuntary context switches. 81 | */ 82 | default long getContextSwitches() { 83 | return 0L; 84 | } 85 | 86 | /** 87 | * The number of minor (soft) faults the thread has made which have not required loading a memory page from disk. 88 | * Sometimes called reclaims. Linux only. 89 | * 90 | * @return minor faults. 91 | */ 92 | default long getMinorFaults() { 93 | return 0L; 94 | } 95 | 96 | /** 97 | * The number of major (hard) faults the thread has made which have required loading a memory page from disk. Linux 98 | * only. 99 | * 100 | * @return major faults. 101 | */ 102 | default long getMajorFaults() { 103 | return 0L; 104 | } 105 | 106 | /** 107 | * Kernel (privileged) time used by the thread. 108 | * 109 | * @return Returns the number of milliseconds the task/thread has executed in kernel/system mode. 110 | */ 111 | long getKernelTime(); 112 | 113 | /** 114 | * User time used by the thread. 115 | * 116 | * @return Returns the number of milliseconds the task/thread has executed in user mode. 117 | */ 118 | long getUserTime(); 119 | 120 | /** 121 | * Elapsed/up-time of the thread. 122 | * 123 | * @return Returns the number of milliseconds since the task/thread started. 124 | */ 125 | long getUpTime(); 126 | 127 | /** 128 | * The start time of the thread. 129 | * 130 | * @return Returns the start time of the task/thread in number of milliseconds since January 1, 1970. 131 | */ 132 | long getStartTime(); 133 | 134 | /** 135 | * Priority of the thread, the meaning of which is dependent on the OS. 136 | * 137 | * @return priority. 138 | */ 139 | int getPriority(); 140 | 141 | /** 142 | * Attempts to updates process attributes. Returns false if the update fails, which will occur if the process no 143 | * longer exists. Not implemented for macOS, as thread ID is simply an index and not unique. 144 | * 145 | * @return {@code true} if the update was successful, false if the update failed. In addition, on a failed update 146 | * the thread state will be changed to {@link State#INVALID}. 147 | */ 148 | default boolean updateAttributes() { 149 | return false; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/OperatingSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os; 6 | 7 | import java.util.Collection; 8 | import java.util.Comparator; 9 | import java.util.List; 10 | import java.util.Objects; 11 | import java.util.function.Predicate; 12 | import java.util.stream.Collectors; 13 | 14 | import ooo.oshi.software.os.OSProcess.State; 15 | 16 | public interface OperatingSystem { 17 | 18 | /** 19 | * Constants which may be used to filter Process lists in {@link #getProcesses(Predicate, Comparator, int)}, 20 | * {@link #getChildProcesses(int, Predicate, Comparator, int)}, and 21 | * {@link #getDescendantProcesses(int, Predicate, Comparator, int)}. 22 | */ 23 | final class ProcessFiltering { 24 | private ProcessFiltering() { 25 | } 26 | 27 | /** 28 | * No filtering. 29 | */ 30 | public static final Predicate ALL_PROCESSES = p -> true; 31 | /** 32 | * Exclude processes with {@link State#INVALID} process state. 33 | */ 34 | public static final Predicate VALID_PROCESS = p -> !p.getState().equals(State.INVALID); 35 | /** 36 | * Exclude child processes. Only include processes which are their own parent. 37 | */ 38 | public static final Predicate NO_PARENT = p -> p.getParentProcessID() == p.getProcessID(); 39 | /** 40 | * Only incude 64-bit processes. 41 | */ 42 | public static final Predicate BITNESS_64 = p -> p.getBitness() == 64; 43 | /** 44 | * Only include 32-bit processes. 45 | */ 46 | public static final Predicate BITNESS_32 = p -> p.getBitness() == 32; 47 | } 48 | 49 | /** 50 | * Constants which may be used to sort Process lists in {@link #getProcesses(Predicate, Comparator, int)}, 51 | * {@link #getChildProcesses(int, Predicate, Comparator, int)}, and 52 | * {@link #getDescendantProcesses(int, Predicate, Comparator, int)}. 53 | */ 54 | final class ProcessSorting { 55 | private ProcessSorting() { 56 | } 57 | 58 | /** 59 | * No sorting 60 | */ 61 | public static final Comparator NO_SORTING = (p1, p2) -> 0; 62 | /** 63 | * Sort by decreasing cumulative CPU percentage 64 | */ 65 | public static final Comparator CPU_DESC = Comparator 66 | .comparingDouble(OSProcess::getProcessCpuLoadCumulative).reversed(); 67 | /** 68 | * Sort by decreasing Resident Set Size (RSS) 69 | */ 70 | public static final Comparator RSS_DESC = Comparator.comparingLong(OSProcess::getResidentSetSize) 71 | .reversed(); 72 | /** 73 | * Sort by up time, newest processes first 74 | */ 75 | public static final Comparator UPTIME_ASC = Comparator.comparingLong(OSProcess::getUpTime); 76 | /** 77 | * Sort by up time, oldest processes first 78 | */ 79 | public static final Comparator UPTIME_DESC = UPTIME_ASC.reversed(); 80 | /** 81 | * Sort by Process Id 82 | */ 83 | public static final Comparator PID_ASC = Comparator.comparingInt(OSProcess::getProcessID); 84 | /** 85 | * Sort by Parent Process Id 86 | */ 87 | public static final Comparator PARENTPID_ASC = Comparator 88 | .comparingInt(OSProcess::getParentProcessID); 89 | /** 90 | * Sort by Process Name (case insensitive) 91 | */ 92 | public static final Comparator NAME_ASC = Comparator.comparing(OSProcess::getName, 93 | String.CASE_INSENSITIVE_ORDER); 94 | } 95 | 96 | /** 97 | * Gets currently running processes. No order is guaranteed. 98 | * 99 | * @return A list of {@link oshi.software.os.OSProcess} objects for the specified number (or all) of currently 100 | * running processes, sorted as specified. The list may contain null elements or processes with a state of 101 | * {@link OSProcess.State#INVALID} if a process terminates during iteration. 102 | */ 103 | default List getProcesses() { 104 | return getProcesses(null, null, 0); 105 | } 106 | 107 | /** 108 | * Gets currently running processes, optionally filtering, sorting, and limited to the top "N". 109 | * 110 | * @param filter An optional {@link Predicate} limiting the results to the specified filter. Some common predicates 111 | * are available in {@link ProcessSorting}. May be {@code null} for no filtering. 112 | * @param sort An optional {@link Comparator} specifying the sorting order. Some common comparators are available 113 | * in {@link ProcessSorting}. May be {@code null} for no sorting. 114 | * @param limit Max number of results to return, or 0 to return all results 115 | * @return A list of {@link oshi.software.os.OSProcess} objects, optionally filtered, sorted, and limited to the 116 | * specified number. 117 | *

118 | * The list may contain processes with a state of {@link OSProcess.State#INVALID} if a process terminates 119 | * during iteration. 120 | */ 121 | List getProcesses(Predicate filter, Comparator sort, int limit); 122 | 123 | /** 124 | * Gets information on a {@link Collection} of currently running processes. This has potentially improved 125 | * performance vs. iterating individual processes. 126 | * 127 | * @param pids A collection of process IDs 128 | * @return A list of {@link oshi.software.os.OSProcess} objects for the specified process ids if it is running 129 | */ 130 | default List getProcesses(Collection pids) { 131 | return pids.stream().distinct().parallel().map(this::getProcess).filter(Objects::nonNull) 132 | .filter(ProcessFiltering.VALID_PROCESS).collect(Collectors.toList()); 133 | } 134 | 135 | /** 136 | * Gets information on a currently running process 137 | * 138 | * @param pid A process ID 139 | * @return An {@link oshi.software.os.OSProcess} object for the specified process id if it is running; null 140 | * otherwise 141 | */ 142 | OSProcess getProcess(int pid); 143 | 144 | /** 145 | * Gets the current process ID 146 | * 147 | * @return the Process ID of the current process 148 | */ 149 | int getProcessId(); 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/common/AbstractOSProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os.common; 6 | 7 | import static ooo.oshi.util.Memoizer.defaultExpiration; 8 | import static ooo.oshi.util.Memoizer.memoize; 9 | 10 | import java.util.function.Supplier; 11 | 12 | import ooo.oshi.annotation.concurrent.ThreadSafe; 13 | import ooo.oshi.software.os.OSProcess; 14 | 15 | /** 16 | * A process is an instance of a computer program that is being executed. It contains the program code and its current 17 | * activity. Depending on the operating system (OS), a process may be made up of multiple threads of execution that 18 | * execute instructions concurrently. 19 | */ 20 | @ThreadSafe 21 | public abstract class AbstractOSProcess implements OSProcess { 22 | 23 | private final Supplier cumulativeCpuLoad = memoize(this::queryCumulativeCpuLoad, defaultExpiration()); 24 | 25 | private int processID; 26 | 27 | protected AbstractOSProcess(int pid) { 28 | this.processID = pid; 29 | } 30 | 31 | @Override 32 | public int getProcessID() { 33 | return this.processID; 34 | } 35 | 36 | @Override 37 | public double getProcessCpuLoadCumulative() { 38 | return cumulativeCpuLoad.get(); 39 | } 40 | 41 | private double queryCumulativeCpuLoad() { 42 | return getUpTime() > 0d ? (getKernelTime() + getUserTime()) / (double) getUpTime() : 0d; 43 | } 44 | 45 | @Override 46 | public double getProcessCpuLoadBetweenTicks(OSProcess priorSnapshot) { 47 | if (priorSnapshot != null && this.processID == priorSnapshot.getProcessID() 48 | && getUpTime() > priorSnapshot.getUpTime()) { 49 | return (getUserTime() - priorSnapshot.getUserTime() + getKernelTime() - priorSnapshot.getKernelTime()) 50 | / (double) (getUpTime() - priorSnapshot.getUpTime()); 51 | } 52 | return getProcessCpuLoadCumulative(); 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | StringBuilder builder = new StringBuilder("OSProcess@"); 58 | builder.append(Integer.toHexString(hashCode())); 59 | builder.append("[processID=").append(this.processID); 60 | builder.append(", name=").append(getName()).append(']'); 61 | return builder.toString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/common/AbstractOperatingSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os.common; 6 | 7 | import static ooo.oshi.software.os.OperatingSystem.ProcessFiltering.ALL_PROCESSES; 8 | import static ooo.oshi.software.os.OperatingSystem.ProcessSorting.NO_SORTING; 9 | 10 | import java.util.Comparator; 11 | import java.util.List; 12 | import java.util.function.Predicate; 13 | import java.util.stream.Collectors; 14 | 15 | import ooo.oshi.software.os.OSProcess; 16 | import ooo.oshi.software.os.OperatingSystem; 17 | 18 | public abstract class AbstractOperatingSystem implements OperatingSystem { 19 | 20 | @Override 21 | public List getProcesses(Predicate filter, Comparator sort, int limit) { 22 | return queryAllProcesses().stream().filter(filter == null ? ALL_PROCESSES : filter) 23 | .sorted(sort == null ? NO_SORTING : sort).limit(limit > 0 ? limit : Long.MAX_VALUE) 24 | .collect(Collectors.toList()); 25 | } 26 | 27 | protected abstract List queryAllProcesses(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/common/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * Provides cross-platform implementations for OS/software classes 7 | */ 8 | package ooo.oshi.software.os.common; 9 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/linux/LinuxOperatingSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os.linux; 6 | 7 | import java.util.List; 8 | 9 | import ooo.oshi.software.os.OSProcess; 10 | import ooo.oshi.software.os.common.AbstractOperatingSystem; 11 | 12 | public class LinuxOperatingSystem extends AbstractOperatingSystem { 13 | 14 | @Override 15 | protected List queryAllProcesses() { 16 | // TODO Auto-generated method stub 17 | return null; 18 | } 19 | 20 | @Override 21 | public OSProcess getProcess(int pid) { 22 | // TODO Auto-generated method stub 23 | return null; 24 | } 25 | 26 | @Override 27 | public int getProcessId() { 28 | // TODO Auto-generated method stub 29 | return 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/mac/MacOSProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os.mac; 6 | 7 | import static java.lang.foreign.MemoryAddress.NULL; 8 | import static java.lang.foreign.MemoryLayout.PathElement.groupElement; 9 | import static java.lang.foreign.ValueLayout.ADDRESS; 10 | import static java.lang.foreign.ValueLayout.JAVA_INT; 11 | import static java.lang.foreign.ValueLayout.JAVA_LONG; 12 | import static ooo.oshi.foreign.mac.SystemLibrary.GROUP; 13 | import static ooo.oshi.foreign.mac.SystemLibrary.MAXCOMLEN; 14 | import static ooo.oshi.foreign.mac.SystemLibrary.MAXPATHLEN; 15 | import static ooo.oshi.foreign.mac.SystemLibrary.PASSWD; 16 | import static ooo.oshi.foreign.mac.SystemLibrary.PROC_PIDPATHINFO_MAXSIZE; 17 | import static ooo.oshi.foreign.mac.SystemLibrary.PROC_PIDTASKALLINFO; 18 | import static ooo.oshi.foreign.mac.SystemLibrary.PROC_PIDVNODEPATHINFO; 19 | import static ooo.oshi.foreign.mac.SystemLibrary.PROC_TASK_ALL_INFO; 20 | import static ooo.oshi.foreign.mac.SystemLibrary.RUSAGEINFOV2; 21 | import static ooo.oshi.foreign.mac.SystemLibrary.RUSAGE_INFO_V2; 22 | import static ooo.oshi.foreign.mac.SystemLibrary.VNODE_PATH_INFO; 23 | import static ooo.oshi.foreign.mac.SystemLibrary.errno; 24 | import static ooo.oshi.foreign.mac.SystemLibrary.getgrgid; 25 | import static ooo.oshi.foreign.mac.SystemLibrary.getpwuid; 26 | import static ooo.oshi.foreign.mac.SystemLibrary.proc_pid_rusage; 27 | import static ooo.oshi.foreign.mac.SystemLibrary.proc_pidinfo; 28 | import static ooo.oshi.foreign.mac.SystemLibrary.proc_pidpath; 29 | import static ooo.oshi.software.os.OSProcess.State.INVALID; 30 | import static ooo.oshi.software.os.OSProcess.State.NEW; 31 | import static ooo.oshi.software.os.OSProcess.State.OTHER; 32 | import static ooo.oshi.software.os.OSProcess.State.RUNNING; 33 | import static ooo.oshi.software.os.OSProcess.State.SLEEPING; 34 | import static ooo.oshi.software.os.OSProcess.State.STOPPED; 35 | import static ooo.oshi.software.os.OSProcess.State.WAITING; 36 | import static ooo.oshi.software.os.OSProcess.State.ZOMBIE; 37 | import static ooo.oshi.util.Memoizer.memoize; 38 | 39 | import java.lang.foreign.MemoryAddress; 40 | import java.lang.foreign.MemoryLayout.PathElement; 41 | import java.lang.foreign.MemorySegment; 42 | import java.lang.foreign.SegmentAllocator; 43 | import java.lang.foreign.ValueLayout; 44 | import java.nio.charset.StandardCharsets; 45 | import java.util.ArrayList; 46 | import java.util.Collections; 47 | import java.util.LinkedHashMap; 48 | import java.util.List; 49 | import java.util.Map; 50 | import java.util.function.Supplier; 51 | 52 | import ooo.oshi.util.Tuples.Pair; 53 | import org.slf4j.Logger; 54 | import org.slf4j.LoggerFactory; 55 | 56 | import ooo.oshi.annotation.concurrent.ThreadSafe; 57 | import ooo.oshi.foreign.mac.SystemLibrary; 58 | import ooo.oshi.software.os.OSThread; 59 | import ooo.oshi.software.os.common.AbstractOSProcess; 60 | import ooo.oshi.util.platform.mac.SysctlUtil; 61 | 62 | /** 63 | * OSProcess implementation 64 | */ 65 | @ThreadSafe 66 | public class MacOSProcess extends AbstractOSProcess { 67 | 68 | private static final Logger LOG = LoggerFactory.getLogger(MacOSProcess.class); 69 | 70 | private static final int ARGMAX = SysctlUtil.sysctl("kern.argmax", 0); 71 | 72 | // 64-bit flag 73 | private static final int P_LP64 = 0x4; 74 | /* 75 | * macOS States: 76 | */ 77 | private static final int SSLEEP = 1; // sleeping on high priority 78 | private static final int SWAIT = 2; // sleeping on low priority 79 | private static final int SRUN = 3; // running 80 | private static final int SIDL = 4; // intermediate state in process creation 81 | private static final int SZOMB = 5; // intermediate state in process termination 82 | private static final int SSTOP = 6; // process being traced 83 | 84 | /* 85 | * For process info structures 86 | */ 87 | private static final PathElement PBSD = groupElement("pbsd"); 88 | private static final long COMM_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_comm")); 89 | private static final long STATUS_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_status")); 90 | private static final long PPID_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_ppid")); 91 | private static final long UID_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_uid")); 92 | private static final long GID_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_gid")); 93 | private static final long START_SEC_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_start_tvsec")); 94 | private static final long START_USEC_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_start_tvusec")); 95 | private static final long NFILES_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_nfiles")); 96 | private static final long FLAGS_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PBSD, groupElement("pbi_flags")); 97 | 98 | private static final PathElement PTINFO = groupElement("ptinfo"); 99 | private static final long TNUM_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_threadnum")); 100 | private static final long PRI_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_priority")); 101 | private static final long VSZ_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_virtual_size")); 102 | private static final long RSS_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_resident_size")); 103 | private static final long SYS_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_total_system")); 104 | private static final long USR_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_total_user")); 105 | private static final long PGIN_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_pageins")); 106 | private static final long FLTS_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_faults")); 107 | private static final long CSW_OFFSET = PROC_TASK_ALL_INFO.byteOffset(PTINFO, groupElement("pti_csw")); 108 | 109 | private static final long PASSWD_NAME_OFFSET = PASSWD.byteOffset(groupElement("pw_name")); 110 | private static final long GROUP_NAME_OFFSET = GROUP.byteOffset(groupElement("gr_name")); 111 | 112 | private static final long DISKIO_READ_OFFSET = RUSAGEINFOV2.byteOffset(groupElement("ri_diskio_bytesread")); 113 | private static final long DISKIO_WRITTEN_OFFSET = RUSAGEINFOV2.byteOffset(groupElement("ri_diskio_byteswritten")); 114 | 115 | private static final long VIP_PATH_OFFSET = VNODE_PATH_INFO.byteOffset(groupElement("pvi_cdir"), 116 | groupElement("vip_path")); 117 | 118 | private int majorVersion; 119 | private int minorVersion; 120 | 121 | private Supplier commandLine = memoize(this::queryCommandLine); 122 | private Supplier, Map>> argsEnviron = memoize(this::queryArgsAndEnvironment); 123 | 124 | private String name = ""; 125 | private String path = ""; 126 | private String currentWorkingDirectory; 127 | private String user; 128 | private String userID; 129 | private String group; 130 | private String groupID; 131 | private State state = INVALID; 132 | private int parentProcessID; 133 | private int threadCount; 134 | private int priority; 135 | private long virtualSize; 136 | private long residentSetSize; 137 | private long kernelTime; 138 | private long userTime; 139 | private long startTime; 140 | private long upTime; 141 | private long bytesRead; 142 | private long bytesWritten; 143 | private long openFiles; 144 | private int bitness; 145 | private long minorFaults; 146 | private long majorFaults; 147 | private long contextSwitches; 148 | 149 | public MacOSProcess(int pid, int major, int minor) { 150 | super(pid); 151 | this.majorVersion = major; 152 | this.minorVersion = minor; 153 | updateAttributes(); 154 | } 155 | 156 | @Override 157 | public String getName() { 158 | return this.name; 159 | } 160 | 161 | @Override 162 | public String getPath() { 163 | return this.path; 164 | } 165 | 166 | @Override 167 | public String getCommandLine() { 168 | return this.commandLine.get(); 169 | } 170 | 171 | private String queryCommandLine() { 172 | return String.join(" ", getArguments()).trim(); 173 | } 174 | 175 | @Override 176 | public List getArguments() { 177 | return argsEnviron.get().a(); 178 | } 179 | 180 | @Override 181 | public Map getEnvironmentVariables() { 182 | return argsEnviron.get().b(); 183 | } 184 | 185 | private Pair, Map> queryArgsAndEnvironment() { 186 | int pid = getProcessID(); 187 | // Set up return objects 188 | List args = new ArrayList<>(); 189 | // API does not specify any particular order of entries, but it is reasonable to 190 | // maintain whatever order the OS provided to the end user 191 | Map env = new LinkedHashMap<>(); 192 | 193 | // Get command line via sysctl CTL_KERN, KERN_PROCARGS2 194 | MemorySegment m = SysctlUtil.sysctl(new int[] { 1, 49, pid }, ARGMAX); 195 | if (m != null) { 196 | // Procargs contains an int representing total # of args, followed by a 197 | // null-terminated execpath string and then the arguments, each 198 | // null-terminated (possible multiple consecutive nulls), 199 | // The execpath string is also the first arg. 200 | // Following this is an int representing total # of env, followed by 201 | // null-terminated envs in similar format 202 | int nargs = m.get(ValueLayout.JAVA_INT, 0); 203 | // Sanity check 204 | if (nargs > 0 && nargs <= 1024) { 205 | // Skip first int (containing value of nargs) 206 | long offset = SystemLibrary.INT_SIZE; 207 | // Skip exec_command and null terminator, as it's duplicated in first arg 208 | String cmdLine = m.getUtf8String(offset); 209 | offset += cmdLine.getBytes(StandardCharsets.UTF_8).length + 1; 210 | // Build each arg and add to list 211 | while (offset < ARGMAX) { 212 | // Grab a string. This should go until the null terminator 213 | String arg = m.getUtf8String(offset); 214 | if (nargs-- > 0) { 215 | // If we havent found nargs yet, it's an arg 216 | args.add(arg); 217 | } else { 218 | // otherwise it's an env 219 | int idx = arg.indexOf('='); 220 | if (idx > 0) { 221 | env.put(arg.substring(0, idx), arg.substring(idx + 1)); 222 | } 223 | } 224 | // Advance offset to next null 225 | offset += arg.getBytes(StandardCharsets.UTF_8).length + 1; 226 | } 227 | } else { 228 | // Don't warn for pid 0 229 | if (pid > 0) { 230 | LOG.warn( 231 | "Failed sysctl call for process arguments (kern.procargs2), process {} may not exist. Error code: {}", 232 | pid, errno()); 233 | } 234 | } 235 | } 236 | return new Pair<>(Collections.unmodifiableList(args), Collections.unmodifiableMap(env)); 237 | } 238 | 239 | @Override 240 | public String getCurrentWorkingDirectory() { 241 | return this.currentWorkingDirectory; 242 | } 243 | 244 | @Override 245 | public String getUser() { 246 | return this.user; 247 | } 248 | 249 | @Override 250 | public String getUserID() { 251 | return this.userID; 252 | } 253 | 254 | @Override 255 | public String getGroup() { 256 | return this.group; 257 | } 258 | 259 | @Override 260 | public String getGroupID() { 261 | return this.groupID; 262 | } 263 | 264 | @Override 265 | public State getState() { 266 | return this.state; 267 | } 268 | 269 | @Override 270 | public int getParentProcessID() { 271 | return this.parentProcessID; 272 | } 273 | 274 | @Override 275 | public int getThreadCount() { 276 | return this.threadCount; 277 | } 278 | 279 | @Override 280 | public List getThreadDetails() { 281 | long now = System.currentTimeMillis(); 282 | List details = new ArrayList<>(); 283 | /*- 284 | List stats = ThreadInfo.queryTaskThreads(getProcessID()); 285 | for (ThreadStats stat : stats) { 286 | // For long running threads the start time calculation can overestimate 287 | long start = now - stat.getUpTime(); 288 | if (start < this.getStartTime()) { 289 | start = this.getStartTime(); 290 | } 291 | details.add(new MacOSThread(getProcessID(), stat.getThreadId(), stat.getState(), stat.getSystemTime(), 292 | stat.getUserTime(), start, now - start, stat.getPriority())); 293 | } 294 | */ 295 | return details; 296 | } 297 | 298 | @Override 299 | public int getPriority() { 300 | return this.priority; 301 | } 302 | 303 | @Override 304 | public long getVirtualSize() { 305 | return this.virtualSize; 306 | } 307 | 308 | @Override 309 | public long getResidentSetSize() { 310 | return this.residentSetSize; 311 | } 312 | 313 | @Override 314 | public long getKernelTime() { 315 | return this.kernelTime; 316 | } 317 | 318 | @Override 319 | public long getUserTime() { 320 | return this.userTime; 321 | } 322 | 323 | @Override 324 | public long getUpTime() { 325 | return this.upTime; 326 | } 327 | 328 | @Override 329 | public long getStartTime() { 330 | return this.startTime; 331 | } 332 | 333 | @Override 334 | public long getBytesRead() { 335 | return this.bytesRead; 336 | } 337 | 338 | @Override 339 | public long getBytesWritten() { 340 | return this.bytesWritten; 341 | } 342 | 343 | @Override 344 | public long getOpenFiles() { 345 | return this.openFiles; 346 | } 347 | 348 | @Override 349 | public int getBitness() { 350 | return this.bitness; 351 | } 352 | 353 | @Override 354 | public long getAffinityMask() { 355 | // macOS doesn't do affinity. Return a bitmask of the current processors. 356 | int logicalProcessorCount = SysctlUtil.sysctl("hw.logicalcpu", 1); 357 | return logicalProcessorCount < 64 ? (1L << logicalProcessorCount) - 1 : -1L; 358 | } 359 | 360 | @Override 361 | public long getMinorFaults() { 362 | return this.minorFaults; 363 | } 364 | 365 | @Override 366 | public long getMajorFaults() { 367 | return this.majorFaults; 368 | } 369 | 370 | @Override 371 | public long getContextSwitches() { 372 | return this.contextSwitches; 373 | } 374 | 375 | @Override 376 | public boolean updateAttributes() { 377 | long now = System.currentTimeMillis(); 378 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 379 | int size = (int) PROC_TASK_ALL_INFO.byteSize(); 380 | MemorySegment m = allocator.allocate(size); 381 | if (0 > proc_pidinfo(getProcessID(), PROC_PIDTASKALLINFO, 0, m, size)) { 382 | this.state = INVALID; 383 | return false; 384 | } 385 | // Check threadcount first: 0 is invalid 386 | this.threadCount = m.get(JAVA_INT, TNUM_OFFSET); 387 | if (0 == this.threadCount) { 388 | this.state = INVALID; 389 | return false; 390 | } 391 | 392 | MemorySegment buf = allocator.allocate(PROC_PIDPATHINFO_MAXSIZE); 393 | if (0 < proc_pidpath(getProcessID(), buf, PROC_PIDPATHINFO_MAXSIZE)) { 394 | this.path = buf.getUtf8String(0); 395 | // Overwrite name with last part of path 396 | String[] pathSplit = this.path.split("/"); 397 | if (pathSplit.length > 0) { 398 | this.name = pathSplit[pathSplit.length - 1]; 399 | } 400 | } 401 | if (this.name.isEmpty()) { 402 | // pbi_comm contains first 16 characters of name 403 | this.name = m.asSlice(COMM_OFFSET, MAXCOMLEN).getUtf8String(0); 404 | } 405 | 406 | switch (m.get(JAVA_INT, STATUS_OFFSET)) { 407 | case SSLEEP: 408 | this.state = SLEEPING; 409 | break; 410 | case SWAIT: 411 | this.state = WAITING; 412 | break; 413 | case SRUN: 414 | this.state = RUNNING; 415 | break; 416 | case SIDL: 417 | this.state = NEW; 418 | break; 419 | case SZOMB: 420 | this.state = ZOMBIE; 421 | break; 422 | case SSTOP: 423 | this.state = STOPPED; 424 | break; 425 | default: 426 | this.state = OTHER; 427 | break; 428 | } 429 | this.parentProcessID = m.get(JAVA_INT, PPID_OFFSET); 430 | 431 | int uid = m.get(JAVA_INT, UID_OFFSET); 432 | this.userID = Integer.toString(uid); 433 | MemoryAddress pwuid = getpwuid(uid); 434 | if (!pwuid.equals(NULL)) { 435 | this.user = pwuid.get(ADDRESS, PASSWD_NAME_OFFSET).getUtf8String(0); 436 | } 437 | int gid = m.get(JAVA_INT, GID_OFFSET); 438 | this.groupID = Integer.toString(gid); 439 | MemoryAddress grgid = getgrgid(gid); 440 | if (!grgid.equals(NULL)) { 441 | this.group = grgid.get(ADDRESS, GROUP_NAME_OFFSET).getUtf8String(0); 442 | } 443 | 444 | this.priority = m.get(JAVA_INT, PRI_OFFSET); 445 | this.virtualSize = m.get(JAVA_LONG, VSZ_OFFSET); 446 | this.residentSetSize = m.get(JAVA_LONG, RSS_OFFSET); 447 | this.kernelTime = m.get(JAVA_LONG, SYS_OFFSET) / 1_000_000L; 448 | this.userTime = m.get(JAVA_LONG, USR_OFFSET) / 1_000_000L; 449 | this.startTime = m.get(JAVA_LONG, START_SEC_OFFSET) * 1000L + m.get(JAVA_LONG, START_USEC_OFFSET) / 1000L; 450 | this.upTime = now - this.startTime; 451 | this.openFiles = m.get(JAVA_INT, NFILES_OFFSET); 452 | this.bitness = (m.get(JAVA_INT, FLAGS_OFFSET) & P_LP64) == 0 ? 32 : 64; 453 | this.majorFaults = m.get(JAVA_INT, PGIN_OFFSET); 454 | // testing using getrusage confirms pti_faults includes both major and minor 455 | this.minorFaults = m.get(JAVA_INT, FLTS_OFFSET) - this.majorFaults; 456 | this.contextSwitches = m.get(JAVA_INT, CSW_OFFSET); 457 | 458 | if (this.majorVersion > 10 || this.minorVersion >= 9) { 459 | MemorySegment rUsageInfoV2 = allocator.allocate(RUSAGEINFOV2.byteSize()); 460 | if (0 == proc_pid_rusage(getProcessID(), RUSAGE_INFO_V2, rUsageInfoV2)) { 461 | this.bytesRead = rUsageInfoV2.get(JAVA_LONG, DISKIO_READ_OFFSET); 462 | this.bytesWritten = rUsageInfoV2.get(JAVA_LONG, DISKIO_WRITTEN_OFFSET); 463 | } 464 | } 465 | 466 | size = (int) VNODE_PATH_INFO.byteSize(); 467 | MemorySegment vpi = allocator.allocate(size); 468 | if (0 < proc_pidinfo(getProcessID(), PROC_PIDVNODEPATHINFO, 0, vpi, size)) { 469 | this.currentWorkingDirectory = vpi.asSlice(VIP_PATH_OFFSET, MAXPATHLEN).getUtf8String(0); 470 | } 471 | return true; 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/mac/MacOperatingSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os.mac; 6 | 7 | import static java.lang.foreign.MemoryAddress.NULL; 8 | import static java.lang.foreign.ValueLayout.JAVA_INT; 9 | import static ooo.oshi.foreign.mac.SystemLibrary.INT_SIZE; 10 | import static ooo.oshi.foreign.mac.SystemLibrary.PROC_ALL_PIDS; 11 | import static ooo.oshi.foreign.mac.SystemLibrary.getpid; 12 | import static ooo.oshi.foreign.mac.SystemLibrary.proc_listpids; 13 | import static ooo.oshi.software.os.OSProcess.State.INVALID; 14 | 15 | import java.lang.foreign.MemorySegment; 16 | import java.lang.foreign.SegmentAllocator; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.Objects; 20 | import java.util.stream.Collectors; 21 | 22 | import ooo.oshi.software.os.OSProcess; 23 | import ooo.oshi.software.os.common.AbstractOperatingSystem; 24 | import ooo.oshi.util.ExecutingCommand; 25 | import ooo.oshi.util.ParseUtil; 26 | import ooo.oshi.util.platform.mac.SysctlUtil; 27 | 28 | public class MacOperatingSystem extends AbstractOperatingSystem { 29 | 30 | private final int major; 31 | private final int minor; 32 | 33 | public MacOperatingSystem() { 34 | String version = System.getProperty("os.version"); 35 | int verMajor = ParseUtil.getFirstIntValue(version); 36 | int verMinor = ParseUtil.getNthIntValue(version, 2); 37 | // Big Sur (11.x) may return 10.16 38 | if (verMajor == 10 && verMinor > 15) { 39 | String swVers = ExecutingCommand.getFirstAnswer("sw_vers -productVersion"); 40 | if (!swVers.isEmpty()) { 41 | version = swVers; 42 | } 43 | verMajor = ParseUtil.getFirstIntValue(version); 44 | verMinor = ParseUtil.getNthIntValue(version, 2); 45 | } 46 | // this.osXVersion = version; 47 | this.major = verMajor; 48 | this.minor = verMinor; 49 | SysctlUtil.sysctl("kern.maxproc", 0x1000); 50 | } 51 | 52 | @Override 53 | protected List queryAllProcesses() { 54 | int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0) / INT_SIZE; 55 | int[] pids = new int[numberOfProcesses]; 56 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 57 | MemorySegment cPids = allocator.allocateArray(JAVA_INT, pids); 58 | numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, cPids, pids.length * INT_SIZE) / INT_SIZE; 59 | pids = cPids.toArray(JAVA_INT); 60 | return Arrays.stream(pids).distinct().parallel().mapToObj(this::getProcess).filter(Objects::nonNull) 61 | .filter(ProcessFiltering.VALID_PROCESS).collect(Collectors.toList()); 62 | } 63 | 64 | @Override 65 | public OSProcess getProcess(int pid) { 66 | OSProcess proc = new MacOSProcess(pid, this.major, this.minor); 67 | return proc.getState().equals(INVALID) ? null : proc; 68 | } 69 | 70 | @Override 71 | public int getProcessId() { 72 | return getpid(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/mac/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * Provides macOS implementations for OS/software classes 7 | */ 8 | package ooo.oshi.software.os.mac; 9 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * [oshi-ffm API] Provides cross-platform implementation to retrieve OS, FileSystem, and Process information 7 | */ 8 | package ooo.oshi.software.os; 9 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/software/os/windows/WindowsOperatingSystem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.software.os.windows; 6 | 7 | import java.util.List; 8 | 9 | import ooo.oshi.software.os.OSProcess; 10 | import ooo.oshi.software.os.common.AbstractOperatingSystem; 11 | 12 | import static ooo.oshi.foreign.windows.Kernel32Library.getCurrentProcessId; 13 | 14 | public class WindowsOperatingSystem extends AbstractOperatingSystem { 15 | 16 | @Override 17 | protected List queryAllProcesses() { 18 | // TODO Auto-generated method stub 19 | return null; 20 | } 21 | 22 | @Override 23 | public OSProcess getProcess(int pid) { 24 | // TODO Auto-generated method stub 25 | return null; 26 | } 27 | 28 | @Override 29 | public int getProcessId() { 30 | return getCurrentProcessId(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.util; 6 | 7 | import java.time.Instant; 8 | import java.time.OffsetDateTime; 9 | import java.time.ZoneOffset; 10 | import java.util.regex.Pattern; 11 | 12 | import ooo.oshi.annotation.concurrent.ThreadSafe; 13 | 14 | /** 15 | * General constants used in multiple classes 16 | */ 17 | @ThreadSafe 18 | public final class Constants { 19 | 20 | /** 21 | * String to report for unknown information 22 | */ 23 | public static final String UNKNOWN = "unknown"; 24 | 25 | /** 26 | * The official/approved path for sysfs information. Note: /sys/class/dmi/id symlinks here 27 | */ 28 | public static final String SYSFS_SERIAL_PATH = "/sys/devices/virtual/dmi/id/"; 29 | 30 | /** 31 | * The Unix Epoch, a default value when WMI DateTime queries return no value. 32 | */ 33 | public static final OffsetDateTime UNIX_EPOCH = OffsetDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC); 34 | 35 | public static final Pattern DIGITS = Pattern.compile("\\d+"); 36 | 37 | /** 38 | * Everything in this class is static, never instantiate it 39 | */ 40 | private Constants() { 41 | throw new AssertionError(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/ExecutingCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.util; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.nio.charset.Charset; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | import ooo.oshi.PlatformEnum; 20 | import ooo.oshi.SystemInfo; 21 | import ooo.oshi.annotation.concurrent.ThreadSafe; 22 | 23 | /** 24 | * A class for executing on the command line and returning the result of execution. 25 | */ 26 | @ThreadSafe 27 | public final class ExecutingCommand { 28 | 29 | private static final Logger LOG = LoggerFactory.getLogger(ExecutingCommand.class); 30 | 31 | private static final boolean IS_WINDOWS = PlatformEnum.WINDOWS.equals(SystemInfo.getCurrentPlatform()); 32 | private static final String[] DEFAULT_ENV = getDefaultEnv(); 33 | 34 | private ExecutingCommand() { 35 | } 36 | 37 | private static String[] getDefaultEnv() { 38 | if (IS_WINDOWS) { 39 | return new String[] { "LANGUAGE=C" }; 40 | } else { 41 | return new String[] { "LC_ALL=C" }; 42 | } 43 | } 44 | 45 | /** 46 | * Executes a command on the native command line and returns the result. This is a convenience method to call 47 | * {@link java.lang.Runtime#exec(String)} and capture the resulting output in a list of Strings. On Windows, 48 | * built-in commands not associated with an executable program may require {@code cmd.exe /c} to be prepended to the 49 | * command. 50 | * 51 | * @param cmdToRun Command to run 52 | * @return A list of Strings representing the result of the command, or empty string if the command failed 53 | */ 54 | public static List runNative(String cmdToRun) { 55 | String[] cmd = cmdToRun.split(" "); 56 | return runNative(cmd); 57 | } 58 | 59 | /** 60 | * Executes a command on the native command line and returns the result line by line. This is a convenience method 61 | * to call {@link java.lang.Runtime#exec(String[])} and capture the resulting output in a list of Strings. On 62 | * Windows, built-in commands not associated with an executable program may require the strings {@code cmd.exe} and 63 | * {@code /c} to be prepended to the array. 64 | * 65 | * @param cmdToRunWithArgs Command to run and args, in an array 66 | * @return A list of Strings representing the result of the command, or empty string if the command failed 67 | */ 68 | public static List runNative(String[] cmdToRunWithArgs) { 69 | return runNative(cmdToRunWithArgs, DEFAULT_ENV); 70 | } 71 | 72 | /** 73 | * Executes a command on the native command line and returns the result line by line. This is a convenience method 74 | * to call {@link java.lang.Runtime#exec(String[])} and capture the resulting output in a list of Strings. On 75 | * Windows, built-in commands not associated with an executable program may require the strings {@code cmd.exe} and 76 | * {@code /c} to be prepended to the array. 77 | * 78 | * @param cmdToRunWithArgs Command to run and args, in an array 79 | * @param envp array of strings, each element of which has environment variable settings in the format 80 | * name=value, or null if the subprocess should inherit the environment of the current 81 | * process. 82 | * @return A list of Strings representing the result of the command, or empty string if the command failed 83 | */ 84 | public static List runNative(String[] cmdToRunWithArgs, String[] envp) { 85 | Process p = null; 86 | try { 87 | p = Runtime.getRuntime().exec(cmdToRunWithArgs, envp); 88 | return getProcessOutput(p, cmdToRunWithArgs); 89 | } catch (SecurityException | IOException e) { 90 | LOG.trace("Couldn't run command {}: {}", Arrays.toString(cmdToRunWithArgs), e.getMessage()); 91 | } finally { 92 | // Ensure all resources are released 93 | if (p != null) { 94 | // Windows and Solaris don't close descriptors on destroy, 95 | // so we must handle separately 96 | if (IS_WINDOWS) { 97 | try { 98 | p.getOutputStream().close(); 99 | } catch (IOException e) { 100 | // do nothing on failure 101 | } 102 | try { 103 | p.getInputStream().close(); 104 | } catch (IOException e) { 105 | // do nothing on failure 106 | } 107 | try { 108 | p.getErrorStream().close(); 109 | } catch (IOException e) { 110 | // do nothing on failure 111 | } 112 | } 113 | p.destroy(); 114 | } 115 | } 116 | return Collections.emptyList(); 117 | } 118 | 119 | private static List getProcessOutput(Process p, String[] cmd) { 120 | ArrayList sa = new ArrayList<>(); 121 | try (BufferedReader reader = new BufferedReader( 122 | new InputStreamReader(p.getInputStream(), Charset.defaultCharset()))) { 123 | String line; 124 | while ((line = reader.readLine()) != null) { 125 | sa.add(line); 126 | } 127 | p.waitFor(); 128 | } catch (IOException e) { 129 | LOG.trace("Problem reading output from {}: {}", Arrays.toString(cmd), e.getMessage()); 130 | } catch (InterruptedException ie) { 131 | LOG.trace("Interrupted while reading output from {}: {}", Arrays.toString(cmd), ie.getMessage()); 132 | Thread.currentThread().interrupt(); 133 | } 134 | return sa; 135 | } 136 | 137 | /** 138 | * Return first line of response for selected command. 139 | * 140 | * @param cmd2launch String command to be launched 141 | * @return String or empty string if command failed 142 | */ 143 | public static String getFirstAnswer(String cmd2launch) { 144 | return getAnswerAt(cmd2launch, 0); 145 | } 146 | 147 | /** 148 | * Return response on selected line index (0-based) after running selected command. 149 | * 150 | * @param cmd2launch String command to be launched 151 | * @param answerIdx int index of line in response of the command 152 | * @return String whole line in response or empty string if invalid index or running of command fails 153 | */ 154 | public static String getAnswerAt(String cmd2launch, int answerIdx) { 155 | List sa = ExecutingCommand.runNative(cmd2launch); 156 | 157 | if (answerIdx >= 0 && answerIdx < sa.size()) { 158 | return sa.get(answerIdx); 159 | } 160 | return ""; 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/FormatUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.util; 6 | 7 | import java.math.BigInteger; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import ooo.oshi.annotation.concurrent.ThreadSafe; 11 | 12 | /** 13 | * Formatting utility for appending units or converting between number types. 14 | */ 15 | @ThreadSafe 16 | public final class FormatUtil { 17 | /** 18 | * Binary prefixes, used in IEC Standard for naming bytes. 19 | * (https://en.wikipedia.org/wiki/International_Electrotechnical_Commission) 20 | * 21 | * Should be used for most representations of bytes 22 | */ 23 | private static final long KIBI = 1L << 10; 24 | private static final long MEBI = 1L << 20; 25 | private static final long GIBI = 1L << 30; 26 | private static final long TEBI = 1L << 40; 27 | private static final long PEBI = 1L << 50; 28 | private static final long EXBI = 1L << 60; 29 | 30 | /** 31 | * Decimal prefixes, used for Hz and other metric units and for bytes by hard drive manufacturers 32 | */ 33 | private static final long KILO = 1_000L; 34 | private static final long MEGA = 1_000_000L; 35 | private static final long GIGA = 1_000_000_000L; 36 | private static final long TERA = 1_000_000_000_000L; 37 | private static final long PETA = 1_000_000_000_000_000L; 38 | private static final long EXA = 1_000_000_000_000_000_000L; 39 | 40 | /* 41 | * Two's complement reference: 2^64. 42 | */ 43 | private static final BigInteger TWOS_COMPLEMENT_REF = BigInteger.ONE.shiftLeft(64); 44 | 45 | /** Constant HEX_ERROR="0x%08X" */ 46 | public static final String HEX_ERROR = "0x%08X"; 47 | 48 | private FormatUtil() { 49 | } 50 | 51 | /** 52 | * Format bytes into a rounded string representation using IEC standard (matches Mac/Linux). For hard drive 53 | * capacities, use @link {@link #formatBytesDecimal(long)}. For Windows displays for KB, MB and GB, in JEDEC units, 54 | * edit the returned string to remove the 'i' to display the (incorrect) JEDEC units. 55 | * 56 | * @param bytes Bytes. 57 | * @return Rounded string representation of the byte size. 58 | */ 59 | public static String formatBytes(long bytes) { 60 | if (bytes == 1L) { // bytes 61 | return String.format("%d byte", bytes); 62 | } else if (bytes < KIBI) { // bytes 63 | return String.format("%d bytes", bytes); 64 | } else if (bytes < MEBI) { // KiB 65 | return formatUnits(bytes, KIBI, "KiB"); 66 | } else if (bytes < GIBI) { // MiB 67 | return formatUnits(bytes, MEBI, "MiB"); 68 | } else if (bytes < TEBI) { // GiB 69 | return formatUnits(bytes, GIBI, "GiB"); 70 | } else if (bytes < PEBI) { // TiB 71 | return formatUnits(bytes, TEBI, "TiB"); 72 | } else if (bytes < EXBI) { // PiB 73 | return formatUnits(bytes, PEBI, "PiB"); 74 | } else { // EiB 75 | return formatUnits(bytes, EXBI, "EiB"); 76 | } 77 | } 78 | 79 | /** 80 | * Format units as exact integer or fractional decimal based on the prefix, appending the appropriate units 81 | * 82 | * @param value The value to format 83 | * @param prefix The divisor of the unit multiplier 84 | * @param unit A string representing the units 85 | * @return A string with the value 86 | */ 87 | private static String formatUnits(long value, long prefix, String unit) { 88 | if (value % prefix == 0) { 89 | return String.format("%d %s", value / prefix, unit); 90 | } 91 | return String.format("%.1f %s", (double) value / prefix, unit); 92 | } 93 | 94 | /** 95 | * Format bytes into a rounded string representation using decimal SI units. These are used by hard drive 96 | * manufacturers for capacity. Most other storage should use {@link #formatBytes(long)}. 97 | * 98 | * @param bytes Bytes. 99 | * @return Rounded string representation of the byte size. 100 | */ 101 | public static String formatBytesDecimal(long bytes) { 102 | if (bytes == 1L) { // bytes 103 | return String.format("%d byte", bytes); 104 | } else if (bytes < KILO) { // bytes 105 | return String.format("%d bytes", bytes); 106 | } else { 107 | return formatValue(bytes, "B"); 108 | } 109 | } 110 | 111 | /** 112 | * Format hertz into a string to a rounded string representation. 113 | * 114 | * @param hertz Hertz. 115 | * @return Rounded string representation of the hertz size. 116 | */ 117 | public static String formatHertz(long hertz) { 118 | return formatValue(hertz, "Hz"); 119 | } 120 | 121 | /** 122 | * Format arbitrary units into a string to a rounded string representation. 123 | * 124 | * @param value The value 125 | * @param unit Units to append metric prefix to 126 | * @return Rounded string representation of the value with metric prefix to extension 127 | */ 128 | public static String formatValue(long value, String unit) { 129 | if (value < KILO) { 130 | return String.format("%d %s", value, unit).trim(); 131 | } else if (value < MEGA) { // K 132 | return formatUnits(value, KILO, "K" + unit); 133 | } else if (value < GIGA) { // M 134 | return formatUnits(value, MEGA, "M" + unit); 135 | } else if (value < TERA) { // G 136 | return formatUnits(value, GIGA, "G" + unit); 137 | } else if (value < PETA) { // T 138 | return formatUnits(value, TERA, "T" + unit); 139 | } else if (value < EXA) { // P 140 | return formatUnits(value, PETA, "P" + unit); 141 | } else { // E 142 | return formatUnits(value, EXA, "E" + unit); 143 | } 144 | } 145 | 146 | /** 147 | * Formats an elapsed time in seconds as days, hh:mm:ss. 148 | * 149 | * @param secs Elapsed seconds 150 | * @return A string representation of elapsed time 151 | */ 152 | public static String formatElapsedSecs(long secs) { 153 | long eTime = secs; 154 | final long days = TimeUnit.SECONDS.toDays(eTime); 155 | eTime -= TimeUnit.DAYS.toSeconds(days); 156 | final long hr = TimeUnit.SECONDS.toHours(eTime); 157 | eTime -= TimeUnit.HOURS.toSeconds(hr); 158 | final long min = TimeUnit.SECONDS.toMinutes(eTime); 159 | eTime -= TimeUnit.MINUTES.toSeconds(min); 160 | final long sec = eTime; 161 | return String.format("%d days, %02d:%02d:%02d", days, hr, min, sec); 162 | } 163 | 164 | /** 165 | * Convert unsigned int to signed long. 166 | * 167 | * @param x Signed int representing an unsigned integer 168 | * @return long value of x unsigned 169 | */ 170 | public static long getUnsignedInt(int x) { 171 | return x & 0x0000_0000_ffff_ffffL; 172 | } 173 | 174 | /** 175 | * Represent a 32 bit value as if it were an unsigned integer. 176 | * 177 | * This is a Java 7 implementation of Java 8's Integer.toUnsignedString. 178 | * 179 | * @param i a 32 bit value 180 | * @return the string representation of the unsigned integer 181 | */ 182 | public static String toUnsignedString(int i) { 183 | if (i >= 0) { 184 | return Integer.toString(i); 185 | } 186 | return Long.toString(getUnsignedInt(i)); 187 | } 188 | 189 | /** 190 | * Represent a 64 bit value as if it were an unsigned long. 191 | * 192 | * This is a Java 7 implementation of Java 8's Long.toUnsignedString. 193 | * 194 | * @param l a 64 bit value 195 | * @return the string representation of the unsigned long 196 | */ 197 | public static String toUnsignedString(long l) { 198 | if (l >= 0) { 199 | return Long.toString(l); 200 | } 201 | return BigInteger.valueOf(l).add(TWOS_COMPLEMENT_REF).toString(); 202 | } 203 | 204 | /** 205 | * Translate an integer error code to its hex notation 206 | * 207 | * @param errorCode The error code 208 | * @return A string representing the error as 0x.... 209 | */ 210 | public static String formatError(int errorCode) { 211 | return String.format(HEX_ERROR, errorCode); 212 | } 213 | 214 | /** 215 | * Rounds a floating point number to the nearest integer 216 | * 217 | * @param x the floating point number 218 | * @return the integer 219 | */ 220 | public static int roundToInt(double x) { 221 | return (int) Math.round(x); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/Memoizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.util; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.function.Supplier; 9 | 10 | import ooo.oshi.annotation.concurrent.ThreadSafe; 11 | 12 | /** 13 | * A memoized function stores the output corresponding to some set of specific inputs. Subsequent calls with remembered 14 | * inputs return the remembered result rather than recalculating it. 15 | */ 16 | @ThreadSafe 17 | public final class Memoizer { 18 | 19 | private static final Supplier DEFAULT_EXPIRATION_NANOS = memoize(Memoizer::queryExpirationConfig, 20 | TimeUnit.MINUTES.toNanos(1)); 21 | 22 | private Memoizer() { 23 | } 24 | 25 | private static long queryExpirationConfig() { 26 | return TimeUnit.MILLISECONDS.toNanos(300); 27 | } 28 | 29 | /** 30 | * Default exipiration of memoized values in nanoseconds, which will refresh after this time elapses. Update by 31 | * setting {@link GlobalConfig} property oshi.util.memoizer.expiration to a value in milliseconds. 32 | * 33 | * @return The number of nanoseconds to keep memoized values before refreshing 34 | */ 35 | public static long defaultExpiration() { 36 | return DEFAULT_EXPIRATION_NANOS.get(); 37 | } 38 | 39 | /** 40 | * Store a supplier in a delegate function to be computed once, and only again after time to live (ttl) has expired. 41 | * 42 | * @param The type of object supplied 43 | * @param original The {@link java.util.function.Supplier} to memoize 44 | * @param ttlNanos Time in nanoseconds to retain calculation. If negative, retain indefinitely. 45 | * @return A memoized version of the supplier 46 | */ 47 | public static Supplier memoize(Supplier original, long ttlNanos) { 48 | // Adapted from Guava's ExpiringMemoizingSupplier 49 | return new Supplier() { 50 | private final Supplier delegate = original; 51 | private volatile T value; // NOSONAR squid:S3077 52 | private volatile long expirationNanos; 53 | 54 | @Override 55 | public T get() { 56 | long nanos = expirationNanos; 57 | long now = System.nanoTime(); 58 | if (nanos == 0 || (ttlNanos >= 0 && now - nanos >= 0)) { 59 | synchronized (this) { 60 | if (nanos == expirationNanos) { // recheck for lost race 61 | T t = delegate.get(); 62 | value = t; 63 | nanos = now + ttlNanos; 64 | expirationNanos = (nanos == 0) ? 1 : nanos; 65 | return t; 66 | } 67 | } 68 | } 69 | return value; 70 | } 71 | }; 72 | } 73 | 74 | /** 75 | * Store a supplier in a delegate function to be computed only once. 76 | * 77 | * @param The type of object supplied 78 | * @param original The {@link java.util.function.Supplier} to memoize 79 | * @return A memoized version of the supplier 80 | */ 81 | public static Supplier memoize(Supplier original) { 82 | return memoize(original, -1L); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/Tuples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.util; 6 | 7 | import ooo.oshi.annotation.concurrent.ThreadSafe; 8 | 9 | /** 10 | * Tuples utility class 11 | */ 12 | @ThreadSafe 13 | public class Tuples { 14 | 15 | /** 16 | * Convenience class for returning multiple objects from methods. 17 | * 18 | * @param Type of the first element 19 | * @param Type of the second element 20 | */ 21 | public static record Pair (A a, B b) { 22 | } 23 | 24 | /** 25 | * Convenience class for returning multiple objects from methods. 26 | * 27 | * @param Type of the first element 28 | * @param Type of the second element 29 | * @param Type of the third element 30 | */ 31 | public static record Triplet (A a, B b, C c) { 32 | } 33 | 34 | /** 35 | * Convenience class for returning multiple objects from methods. 36 | * 37 | * @param Type of the first element 38 | * @param Type of the second element 39 | * @param Type of the third element 40 | * @param Type of the fourth element 41 | */ 42 | public static record Quartet (A a, B b, C c, D d) { 43 | } 44 | 45 | /** 46 | * Convenience class for returning multiple objects from methods. 47 | * 48 | * @param Type of the first element 49 | * @param Type of the second element 50 | * @param Type of the third element 51 | * @param Type of the fourth element 52 | * @param Type of the fifth element 53 | */ 54 | public static record Quintet (A a, B b, C c, D d, E e) { 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * Provides utilities for parsing, formatting, and other access 7 | */ 8 | package ooo.oshi.util; 9 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/platform/mac/SysctlUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi.util.platform.mac; 6 | 7 | import static java.lang.foreign.MemoryAddress.NULL; 8 | import static java.lang.foreign.ValueLayout.JAVA_INT; 9 | import static java.lang.foreign.ValueLayout.JAVA_LONG; 10 | import static ooo.oshi.foreign.mac.SystemLibrary.INT_SIZE; 11 | import static ooo.oshi.foreign.mac.SystemLibrary.LONG_SIZE; 12 | import static ooo.oshi.foreign.mac.SystemLibrary.errno; 13 | 14 | import java.lang.foreign.MemorySegment; 15 | import java.lang.foreign.SegmentAllocator; 16 | import java.util.Arrays; 17 | 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import ooo.oshi.annotation.concurrent.ThreadSafe; 22 | import ooo.oshi.foreign.mac.SystemLibrary; 23 | 24 | /** 25 | * Provides access to sysctl calls on macOS 26 | */ 27 | @ThreadSafe 28 | public final class SysctlUtil { 29 | 30 | private static final Logger LOG = LoggerFactory.getLogger(SysctlUtil.class); 31 | 32 | private static final String SYSCTL_FAIL = "Failed sysctl call: {}, Error code: {}"; 33 | 34 | private SysctlUtil() { 35 | } 36 | 37 | /** 38 | * Executes a sysctl call with an int result 39 | * 40 | * @param name name of the sysctl 41 | * @param def default int value 42 | * @return The int result of the call if successful; the default otherwise 43 | */ 44 | public static int sysctl(String name, int def) { 45 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 46 | MemorySegment cName = allocator.allocateUtf8String(name); 47 | MemorySegment size = allocator.allocate(JAVA_LONG, INT_SIZE); 48 | MemorySegment m = allocator.allocate(INT_SIZE); 49 | int res = SystemLibrary.sysctlbyname(cName, m, size, NULL, 0L); 50 | if (0 != res) { 51 | LOG.warn(SYSCTL_FAIL, name, errno()); 52 | return def; 53 | } 54 | return m.get(JAVA_INT, 0); 55 | } 56 | 57 | /** 58 | * Executes a sysctl call with a long result 59 | * 60 | * @param name name of the sysctl 61 | * @param def default long value 62 | * @return The long result of the call if successful; the default otherwise 63 | */ 64 | public static long sysctl(String name, long def) { 65 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 66 | MemorySegment cName = allocator.allocateUtf8String(name); 67 | MemorySegment size = allocator.allocate(JAVA_LONG, LONG_SIZE); 68 | MemorySegment m = allocator.allocate(LONG_SIZE); 69 | int res = SystemLibrary.sysctlbyname(cName, m, size, NULL, 0L); 70 | if (0 != res) { 71 | LOG.warn(SYSCTL_FAIL, name, errno()); 72 | return def; 73 | } 74 | return m.get(JAVA_LONG, 0); 75 | } 76 | 77 | /** 78 | * Executes a sysctl call with a String result 79 | * 80 | * @param name name of the sysctl 81 | * @param def default String value 82 | * @return The String result of the call if successful; the default otherwise 83 | */ 84 | public static String sysctl(String name, String def) { 85 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 86 | MemorySegment cName = allocator.allocateUtf8String(name); 87 | MemorySegment size = allocator.allocate(JAVA_LONG); 88 | // Call first time with null pointer to get value of size 89 | if (0 != SystemLibrary.sysctlbyname(cName, NULL, size, NULL, 0L)) { 90 | LOG.warn(SYSCTL_FAIL, name, errno()); 91 | return def; 92 | } 93 | // Call again with proper allocation, add one for null terminator 94 | long sizeToAllocate = size.get(JAVA_LONG, 0) + 1; 95 | MemorySegment m = allocator.allocate(sizeToAllocate); 96 | if (0 != SystemLibrary.sysctlbyname(cName, m, size, NULL, 0L)) { 97 | // LOG.warn(SYSCTL_FAIL, name, Native.getLastError()); 98 | return def; 99 | } 100 | return m.getUtf8String(0); 101 | } 102 | 103 | /** 104 | * Executes a sysctl call with a Pointer result 105 | * 106 | * @param name name of the sysctl 107 | * @return An allocated memory buffer containing the result on success, null otherwise. Its value on failure is 108 | * undefined. 109 | */ 110 | public static MemorySegment sysctl(String name) { 111 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 112 | MemorySegment cName = allocator.allocateUtf8String(name); 113 | MemorySegment size = allocator.allocate(JAVA_LONG); 114 | // Call first time with null pointer to get value of size 115 | if (0 != SystemLibrary.sysctlbyname(cName, NULL, size, NULL, 0L)) { 116 | // LOG.warn(SYSCTL_FAIL, name, Native.getLastError()); 117 | return null; 118 | } 119 | // Call again with proper allocation 120 | long sizeToAllocate = size.get(JAVA_LONG, 0); 121 | MemorySegment m = allocator.allocate(sizeToAllocate); 122 | if (0 != SystemLibrary.sysctlbyname(cName, m, size, NULL, 0L)) { 123 | LOG.warn(SYSCTL_FAIL, name, errno()); 124 | return null; 125 | } 126 | return m; 127 | } 128 | 129 | /** 130 | * Executes a sysctl call with a Pointer result 131 | * 132 | * @param mib a MIB array 133 | * @param sizeToAllocate the size of the buffer to allocate 134 | * @return An allocated memory buffer containing the result on success, null otherwise. Its value on failure is 135 | * undefined. 136 | */ 137 | public static MemorySegment sysctl(int[] mib, long sizeToAllocate) { 138 | SegmentAllocator allocator = SegmentAllocator.implicitAllocator(); 139 | MemorySegment m = allocator.allocate(sizeToAllocate); 140 | MemorySegment size = allocator.allocate(JAVA_LONG, sizeToAllocate); 141 | MemorySegment cMib = allocator.allocateArray(JAVA_INT, mib); 142 | int res = SystemLibrary.sysctl(cMib, mib.length, m, size, NULL, 0L); 143 | if (0 != res) { 144 | LOG.warn(SYSCTL_FAIL, Arrays.toString(mib), errno()); 145 | return null; 146 | } 147 | return m; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/ooo/oshi/util/platform/mac/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | /** 6 | * Provides utilities for macOS. 7 | */ 8 | package ooo.oshi.util.platform.mac; 9 | -------------------------------------------------------------------------------- /src/test/java/ooo/oshi/Kernel32Test.java: -------------------------------------------------------------------------------- 1 | package ooo.oshi; 2 | 3 | import ooo.oshi.foreign.windows.Kernel32Library; 4 | import ooo.oshi.software.os.OperatingSystem; 5 | 6 | public class Kernel32Test { 7 | public static void main(String[] args) { 8 | 9 | System.out.println("The operating system is: " + SystemInfo.getCurrentPlatform().getName()); 10 | SystemInfo si = new SystemInfo(); 11 | OperatingSystem os = si.getOperatingSystem(); 12 | int currentProcessId = os.getProcessId(); 13 | System.out.println("The current Process ID is: " + currentProcessId); 14 | 15 | String computerName = Kernel32Library.getComputerName(); 16 | System.out.println("Computer Name: " + computerName); 17 | 18 | String tempPath = Kernel32Library.getTempPath(); 19 | System.out.println("Temp Path: " + tempPath); 20 | 21 | String processName = Kernel32Library.queryFullProcessImageName(currentProcessId, 0); 22 | System.out.println("Current Process: " + processName); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/ooo/oshi/SystemInfoTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the OSHI-FFM project contributors. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | package ooo.oshi; 6 | 7 | import java.util.List; 8 | import java.util.Map.Entry; 9 | 10 | import ooo.oshi.software.os.OSProcess; 11 | import ooo.oshi.software.os.OperatingSystem; 12 | import ooo.oshi.util.FormatUtil; 13 | 14 | public class SystemInfoTest { 15 | 16 | public static void main(String[] args) { 17 | System.out.println("The operating system is: " + SystemInfo.getCurrentPlatform().getName()); 18 | SystemInfo si = new SystemInfo(); 19 | OperatingSystem os = si.getOperatingSystem(); 20 | System.out.println("The current Process ID is: " + os.getProcessId()); 21 | 22 | OSProcess myProc = os.getProcess(os.getProcessId()); 23 | 24 | // current process will never be null. Other code should check for null here 25 | System.out.println( 26 | "My PID: " + myProc.getProcessID() + " with affinity " + Long.toBinaryString(myProc.getAffinityMask())); 27 | System.out.println("Name: " + myProc.getUser() + ", Group: " + myProc.getGroup()); 28 | System.out.println("Read: " + myProc.getBytesRead() + ", Written: " + myProc.getBytesWritten()); 29 | System.out.println("CWD: " + myProc.getCurrentWorkingDirectory()); 30 | List procs = os.getProcesses(OperatingSystem.ProcessFiltering.ALL_PROCESSES, 31 | OperatingSystem.ProcessSorting.CPU_DESC, 5); 32 | System.out.println(" PID %CPU %MEM VSZ RSS Name"); 33 | for (OSProcess p : procs) { 34 | System.out.printf(" %5d %5.1f %4.1f %9s %9s %s%n", p.getProcessID(), 35 | 100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(), 36 | 100d * p.getResidentSetSize() / (32L << 30), FormatUtil.formatBytes(p.getVirtualSize()), 37 | FormatUtil.formatBytes(p.getResidentSetSize()), p.getName()); 38 | } 39 | OSProcess p = os.getProcess(os.getProcessId()); 40 | System.out.println("Current process arguments: "); 41 | for (String s : p.getArguments()) { 42 | System.out.println(" " + s); 43 | } 44 | System.out.println("Current process environment: "); 45 | for (Entry e : p.getEnvironmentVariables().entrySet()) { 46 | System.out.println(" " + e.getKey() + "=" + e.getValue()); 47 | } 48 | } 49 | 50 | } 51 | --------------------------------------------------------------------------------