From patchwork Tue Dec 18 04:19:46 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sam Mendoza-Jonas X-Patchwork-Id: 1014972 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43JlF40WHVz9sBZ for ; Tue, 18 Dec 2018 15:20:40 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=mendozajonas.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="IMtU5dEa"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="aSxtYVZJ"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43JlF34W23zDqXM for ; Tue, 18 Dec 2018 15:20:39 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=mendozajonas.com Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="IMtU5dEa"; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="aSxtYVZJ"; dkim-atps=neutral X-Original-To: petitboot@lists.ozlabs.org Delivered-To: petitboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=mendozajonas.com (client-ip=66.111.4.25; helo=out1-smtp.messagingengine.com; envelope-from=sam@mendozajonas.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=mendozajonas.com Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=mendozajonas.com header.i=@mendozajonas.com header.b="IMtU5dEa"; dkim=pass (2048-bit key; unprotected) header.d=messagingengine.com header.i=@messagingengine.com header.b="aSxtYVZJ"; dkim-atps=neutral Received: from out1-smtp.messagingengine.com (out1-smtp.messagingengine.com [66.111.4.25]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43JlDR49wwzDqWp for ; Tue, 18 Dec 2018 15:20:07 +1100 (AEDT) Received: from compute2.internal (compute2.nyi.internal [10.202.2.42]) by mailout.nyi.internal (Postfix) with ESMTP id 21CF822380; Mon, 17 Dec 2018 23:20:05 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute2.internal (MEProxy); Mon, 17 Dec 2018 23:20:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= mendozajonas.com; h=from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding; s=fm1; bh=H5EgjVFl75AX6JWGk5AWOxd7pcAdRfOiPKvVO0Sw454=; b=IMtU5 dEa8iTY5rDgLjTWranIjtsqaIaU4ZIyvDdirS0oTGu22zJuQIGtfDlxJ6NLHbspn +L6PCEhNgIz6zZvhhkAX/kMxG5EVDWMaAZS7n+5LL8RNlSA2Dyjwmblqu8+9L6BQ FvGXQ7K38AT39VFvNNzeFg7k9ewY13WljQJvZ/tfAQM6r+l0DRMjUjtwC+C1FE3k Fkqyk49hk+6q/usLZjFfgbiauLHuj17uYCGQ2H2Ub6rwRXQwJdgUqy8nDeOLG/Bn IdOiWM9XMR9lDT3vOq0DE8f7oHtOCgia1xBxatfkQWgCCcE0PrhybKA49aKp2LbR KLO1rEk0Iiw7lhXlA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; bh=H5EgjVFl75AX6JWGk5AWOxd7pcAdRfOiPKvVO0Sw454=; b=aSxtYVZJ vpcaf6cj3Tihj2/+zRQezCh3vTRG4oHnVQD5Orq7o3Gls57Qm3K0L47jMdXOuRbD J70uhQEnNB1RFj19iYnB9yho0m16wIZHdNA/F2t5lhQyRXc/WYn+fzNhUV1eSjAK UjcnBA9ATw6FmLIrksS2hzmBNSnryF3ddwA2MAcpS1JZGMlFWnPzkfzlly4YCqme zinDqv0otmvmf7G3UZMeaVVrAjHWgokz0xvIp0y7p/zlJb3K8iFl/d+B+CucysNf 0E8I7LXnli5Rsp/Lcbkzdb5ir4OXHCl7DYMt7FmduwDj1+bMWjoR0Kf8se/U0J3l XlzqJ60fmGXxMA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedtkedrudeigedgjeduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfquhhtnecuuegrihhlohhuthemucef tddtnecunecujfgurhephffvufffkffojghfggfgsedtkeertdertddtnecuhfhrohhmpe furghmuhgvlhcuofgvnhguohiirgdqlfhonhgrshcuoehsrghmsehmvghnughoiigrjhho nhgrshdrtghomheqnecuffhomhgrihhnpehgihhthhhusgdrtghomhdpughotghsrdhrsh enucfkphepuddvvddrleelrdekvddruddtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehs rghmsehmvghnughoiigrjhhonhgrshdrtghomhenucevlhhushhtvghrufhiiigvpedt X-ME-Proxy: Received: from v4.ozlabs.ibm.com (unknown [122.99.82.10]) by mail.messagingengine.com (Postfix) with ESMTPA id 2A3B110341; Mon, 17 Dec 2018 23:20:02 -0500 (EST) From: Samuel Mendoza-Jonas To: petitboot@lists.ozlabs.org Subject: [RFC PATCH 1/8] rust: Add rust interface for parsing JSON Date: Tue, 18 Dec 2018 15:19:46 +1100 Message-Id: <20181218041953.8960-2-sam@mendozajonas.com> X-Mailer: git-send-email 2.20.0 In-Reply-To: <20181218041953.8960-1-sam@mendozajonas.com> References: <20181218041953.8960-1-sam@mendozajonas.com> MIME-Version: 1.0 X-BeenThere: petitboot@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Petitboot bootloader development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Samuel Mendoza-Jonas Errors-To: petitboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Petitboot" For plugin commands the command definitions are stored in a JSON file as part of the plugin. Instead of doing even more string parsing in C the Rust library provides an interface to the popular Serde library. The Rust interface provides a few FFI-safe definitions to pass data back and forth but does all the parsing itself. The library is built statically, but some extra work is done in build.rs to help it link into the existing Autoconf more easily. This is heavily based on the libtool crate[0] with some improvements. [0] https://docs.rs/libtool/0.1.1/libtool/ Signed-off-by: Samuel Mendoza-Jonas --- Makefile.am | 1 - configure.ac | 9 ++ lib/Makefile.am | 9 +- lib/rust/Cargo.lock | 117 ++++++++++++++++++++++ lib/rust/Cargo.toml | 20 ++++ lib/rust/Makefile.am | 30 ++++++ lib/rust/build.rs | 70 +++++++++++++ lib/rust/rustlibs.c | 109 ++++++++++++++++++++ lib/rust/rustlibs.h | 11 +++ lib/rust/src/lib.rs | 230 +++++++++++++++++++++++++++++++++++++++++++ lib/types/types.h | 30 ++++++ 11 files changed, 634 insertions(+), 2 deletions(-) create mode 100644 lib/rust/Cargo.lock create mode 100644 lib/rust/Cargo.toml create mode 100644 lib/rust/Makefile.am create mode 100644 lib/rust/build.rs create mode 100644 lib/rust/rustlibs.c create mode 100644 lib/rust/rustlibs.h create mode 100644 lib/rust/src/lib.rs diff --git a/Makefile.am b/Makefile.am index 63456ca4..c0e5c8c2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,4 +72,3 @@ include ui/test/Makefile.am include man/Makefile.am include utils/Makefile.am - diff --git a/configure.ac b/configure.ac index 4151b002..3d7e9964 100644 --- a/configure.ac +++ b/configure.ac @@ -450,6 +450,15 @@ esac AC_DEFINE_UNQUOTED(TFTP_TYPE, $tftp_type, [tftp client type]) +AC_ARG_VAR( + [CARGO_OPTS], + [Arguments to cargo] +) +AC_ARG_VAR( + [RUSTC_TARGET_NAME], + [Target as passed to cargo] +) + default_cflags="--std=gnu99 -g \ -Wall -W -Wunused -Wstrict-prototypes -Wmissing-prototypes \ -Wmissing-declarations -Wredundant-decls" diff --git a/lib/Makefile.am b/lib/Makefile.am index 69a66c37..87fe38d6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,6 +12,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +include lib/rust/Makefile.am + core_lib = lib/libpbcore.la noinst_LTLIBRARIES += $(core_lib) @@ -70,7 +72,12 @@ lib_libpbcore_la_SOURCES = \ lib/efi/efivar.h \ lib/efi/efivar.c \ lib/param_list/param_list.c \ - lib/param_list/param_list.h + lib/param_list/param_list.h \ + lib/rust/rustlibs.c \ + lib/rust/rustlibs.c + +lib_libpbcore_la_LIBADD = \ + $(rust_lib) if ENABLE_MTD lib_libpbcore_la_SOURCES += \ diff --git a/lib/rust/Cargo.lock b/lib/rust/Cargo.lock new file mode 100644 index 00000000..5b375927 --- /dev/null +++ b/lib/rust/Cargo.lock @@ -0,0 +1,117 @@ +[[package]] +name = "dtoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itoa" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libtool" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustlibs" +version = "0.1.0" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libtool 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive_internals" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.12.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum libtool 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f576520ad5e2848b26568964320a4073984db2a11147ac4f9c2fc7be1f93b8d2" +"checksum num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3c2bd9b9d21e48e956b763c9f37134dc62d9e95da6edb3f672cacb6caf3cd3" +"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" +"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" +"checksum serde 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "e928fecdb00fe608c96f83a012633383564e730962fc7a0b79225a6acf056798" +"checksum serde_derive 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "95f666a2356d87ce4780ea15b14b13532785579a5cad2dcba5292acc75f6efe2" +"checksum serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc848d073be32cd982380c06587ea1d433bc1a4c4a111de07ec2286a3ddade8" +"checksum serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "57781ed845b8e742fc2bf306aba8e3b408fe8c366b900e3769fbc39f49eb8b39" +"checksum syn 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)" = "517f6da31bc53bf080b9a77b29fbd0ff8da2f5a2ebd24c73c2238274a94ac7cb" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/lib/rust/Cargo.toml b/lib/rust/Cargo.toml new file mode 100644 index 00000000..c335d1bb --- /dev/null +++ b/lib/rust/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rustlibs" +build = "build.rs" +version = "0.1.0" +authors = ["Samuel Mendoza-Jonas "] + +[dependencies] +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +libc = "0.2" + +[lib] +crate-type = ["staticlib"] + +[build-dependencies] +libtool = "0.1" + +[profile.release] +lto = true diff --git a/lib/rust/Makefile.am b/lib/rust/Makefile.am new file mode 100644 index 00000000..add42ff2 --- /dev/null +++ b/lib/rust/Makefile.am @@ -0,0 +1,30 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +rust_lib = librustlibs.la + +noinst_LTLIBRARIES += $(rust_lib) + +librustlibs_la_SOURCES = "" + +CARGO_TARGET_DIR := $(abs_top_builddir)/lib/rust + +$(rust_lib): + CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) cargo build --release --manifest-path $(top_srcdir)/lib/rust/Cargo.toml $(CARGO_OPTS) + ln -sf $(CARGO_TARGET_DIR)/$(RUSTC_TARGET_NAME)/release/librustlibs.a + ln -sf $(CARGO_TARGET_DIR)/$(RUSTC_TARGET_NAME)/release/librustlibs.la + ln -sf $(CARGO_TARGET_DIR)/$(RUSTC_TARGET_NAME)/release/.libs + +clean-local: + CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) cargo clean --manifest-path $(top_srcdir)/lib/rust/Cargo.toml diff --git a/lib/rust/build.rs b/lib/rust/build.rs new file mode 100644 index 00000000..513e119f --- /dev/null +++ b/lib/rust/build.rs @@ -0,0 +1,70 @@ +/* + * From https://docs.rs/libtool/0.1.1/libtool/ + * Upstream doesn't handle some of the directory structure we use in Petitboot, + * use a few extra environment variables to properly determine the target paths. + */ + +use std::env; +use std::fs::File; +use std::fs; +use std::io::prelude::*; +use std::os::unix::fs::symlink; +use std::path::PathBuf; + +/// Generate libtool archive file ${lib}.la +pub fn generate_convenience_lib(lib: &str) -> std::io::Result<()> { + let self_version = env!("CARGO_PKG_VERSION"); + let profile = env::var("PROFILE").unwrap(); + let target_arch = env::var("TARGET").unwrap(); + let target_dir_env = env::var("CARGO_TARGET_DIR").unwrap(); + + /* Check if the output directory will include the arch */ + let target_dir = if PathBuf::from(format!("{}/{}", + target_dir_env, target_arch)).exists() { + format!("{}/{}/{}", target_dir_env, target_arch, profile) + } else { + format!("{}/{}", target_dir_env, profile) + }; + + /* Location of original static library */ + let old_lib_path = PathBuf::from(format!("{}/{}.a", + target_dir, lib)); + /* Paths for new .la file and symlinks */ + let libs_dir = format!("{}/.libs", target_dir); + let libs_path = PathBuf::from(&libs_dir); + let la_path = PathBuf::from(format!("{}/{}.la", + target_dir, lib)); + let new_lib_path = PathBuf::from(format!("{}/{}.a", libs_dir, lib)); + + match fs::create_dir_all(&libs_path) { + Ok(()) => println!("libs_path created"), + _ => panic!("Failed to create libs_path"), + } + + if la_path.exists() { + fs::remove_file(&la_path)?; + } + + /* PathBuf.exists() traverses symlinks so just try and remove it */ + match fs::remove_file(&new_lib_path) { + Ok(_v) => {}, + Err(e) => println!("Error removing symlink: {:?}", e), + } + + let mut file = File::create(&la_path)?; + writeln!(file, "# {}.la - a libtool library file", lib)?; + writeln!(file, "# Generated by libtool-rust {}", self_version)?; + writeln!(file, "dlname=''")?; + writeln!(file, "library_names=''")?; + writeln!(file, "old_library='{}.a'", lib)?; + writeln!(file, "inherited_linker_flags=' -pthread -lm -ldl'")?; + writeln!(file, "installed=no")?; + writeln!(file, "shouldnotlink=no")?; + + symlink(&old_lib_path, &new_lib_path)?; + Ok(()) +} + +fn main() { + generate_convenience_lib("librustlibs").unwrap(); +} diff --git a/lib/rust/rustlibs.c b/lib/rust/rustlibs.c new file mode 100644 index 00000000..fe686bef --- /dev/null +++ b/lib/rust/rustlibs.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include + +#include "rustlibs.h" + +enum field_type { + FIELD_STR, + FIELD_I64, + FIELD_F64, +}; + +/* Internal FFI Structs */ +struct ArgumentRaw { + char *name; + enum field_type type; + int64_t arg_i64; + double arg_f64; + char *arg_str; +}; + +struct CommandRaw { + char *platform; + char *name; + char *cmd; + char *args_fmt; + struct ArgumentRaw *args; + size_t n_args; + char *help; +}; + +struct CommandArray { + struct CommandRaw *commands; + size_t len; +}; + +/* Provided by librustlibs */ +struct CommandArray *parse_json(const char *filename); +void free_command_array(struct CommandArray *commands); + +int parse_command_file(void *ctx, const char *path, struct command **commands) +{ + struct CommandArray *raw_commands; + struct CommandRaw *tmp; + struct command *new; + struct argument *arg; + unsigned int i, j, len; + + raw_commands = parse_json(path); + if (!raw_commands) { + pb_log("Failed to parse machine command\n"); + return 0; + } + + len = raw_commands->len; + new = talloc_zero_array(ctx, struct command, len); + for (i = 0; i < len; i++) { + tmp = &raw_commands->commands[i]; + new[i].platform = talloc_strdup(new, tmp->platform); + new[i].name = talloc_strdup(new, tmp->name); + new[i].cmd = talloc_strdup(new, tmp->cmd); + new[i].args_fmt = talloc_strdup(new, tmp->args_fmt); + + new[i].n_args = tmp->n_args; + new[i].args = talloc_zero_array(new, struct argument, + new[i].n_args); + for (j = 0; j < tmp->n_args; j++) { + arg = &new[i].args[j]; + arg->name = talloc_strdup(new, tmp->args[j].name); + arg->type = tmp->args[j].type; + switch (tmp->args[j].type) { + case FIELD_STR: + arg->arg_str = talloc_strdup(new, + tmp->args[j].arg_str); + break; + case FIELD_I64: + arg->arg_i64 = tmp->args[j].arg_i64; + break; + case FIELD_F64: + arg->arg_f64 = tmp->args[j].arg_f64; + break; + default: + pb_log_fn("Unknown field type %d for arg '%s'\n", + tmp->args[j].type, tmp->args[j].name); + break; + } + } + + new[i].help = talloc_strdup(new, tmp->help); + } + + free_command_array(raw_commands); + + *commands = new; + return len; +} diff --git a/lib/rust/rustlibs.h b/lib/rust/rustlibs.h new file mode 100644 index 00000000..0246657b --- /dev/null +++ b/lib/rust/rustlibs.h @@ -0,0 +1,11 @@ +#ifndef __RUST_LIBS__ +#define __RUST_LIBS__ + +#include +#include + +#include + +int parse_command_file(void *ctx, const char *path, struct command **commands); + +#endif /* __RUST_LIBS__ */ diff --git a/lib/rust/src/lib.rs b/lib/rust/src/lib.rs new file mode 100644 index 00000000..5f4b484b --- /dev/null +++ b/lib/rust/src/lib.rs @@ -0,0 +1,230 @@ +#[macro_use] +extern crate serde_derive; + +extern crate serde; +extern crate serde_json; +extern crate libc; + +use std::ffi::CString; +use std::ffi::CStr; +use std::os::raw::c_char; +use std::fs::File; +use std::path::Path; +use std::ptr; +use std::mem; +use libc::{c_long, c_double}; + +/* Rust representations */ +#[derive(Serialize, Deserialize, Debug)] +pub struct Command { + platform: CString, + name: CString, + cmd: CString, + arg_fmt: CString, + args: Vec, + help: CString, +} + +#[derive(Serialize, Deserialize, Debug)] +enum FieldType { + FieldString(CString), + // TODO Commands which display as yes/no that hide some actual command? + // FieldBool(Bool), + FieldInt(i64), + FieldFloat(f64), +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Argument { + name: CString, + arg: FieldType, +} + +/* FFI respresentations */ +#[repr(C)] +pub struct CommandArray { + bytes: *mut CommandRaw, + len: usize, +} + +#[repr(C)] +pub struct CommandRaw { + platform: *mut c_char, + name: *mut c_char, + cmd: *mut c_char, + arg_fmt: *mut c_char, + args: *mut ArgumentRaw, + n_args: usize, + help: *mut c_char, +} + +impl CommandRaw { + /* + * We need to convert several fields into FFI-suitable types and we + * don't want to 'partially forget' the Command structs, so we + * clone the various fields and forget them. The vec is then shrunk + * and forgotten so it can be passed as an array C will recognise. + */ + fn new(command: &Command) -> CommandRaw { + CommandRaw { + platform: command.platform.clone().into_raw(), + name: command.name.clone().into_raw(), + cmd: command.cmd.clone().into_raw(), + arg_fmt: command.arg_fmt.clone().into_raw(), + args: CommandRaw::args_into_raw(&command.args), + n_args: command.args.len(), + help: command.help.clone().into_raw() + } + } + + fn args_into_raw(args: &Vec) -> *mut ArgumentRaw { + + let mut args_raw = Vec::::new(); + + for arg in args { + let raw_arg = ArgumentRaw::new(&arg); + args_raw.push(raw_arg); + } + + args_raw.shrink_to_fit(); + let ptr = args_raw.as_mut_ptr(); + mem::forget(args_raw); + + return ptr; + } +} + +#[repr(C)] +/* + * Rust enums are not C enums and Rust doesn't have a 'union' concept like in C + * so we just declare all the fields here and set them based on arg_type. + */ +pub struct ArgumentRaw { + name: *mut c_char, + arg_type: FieldTypeRaw, + arg_i64: c_long, + arg_f64: c_double, + arg_str: *mut c_char, +} + +impl ArgumentRaw { + fn new(arg: &Argument) -> ArgumentRaw { + let raw_arg = match arg.arg { + FieldType::FieldString(ref s) => { + ArgumentRaw { + name: arg.name.clone().into_raw(), + arg_type: FieldTypeRaw::RawString, + arg_i64: 0, + arg_f64: 0.0, + arg_str: s.clone().into_raw(), + } + }, + FieldType::FieldInt(i) => { + ArgumentRaw { + name: arg.name.clone().into_raw(), + arg_type: FieldTypeRaw::RawInt, + arg_i64: i, + arg_f64: 0.0, + arg_str: ptr::null_mut(), + } + }, + FieldType::FieldFloat(f) => { + ArgumentRaw { + name: arg.name.clone().into_raw(), + arg_type: FieldTypeRaw::RawFloat, + arg_i64: 0, + arg_f64: f, + arg_str: ptr::null_mut(), + } + }, + }; + return raw_arg; + } +} + +#[repr(C)] +#[derive(PartialEq)] +enum FieldTypeRaw { + RawString = 0, + RawInt, + RawFloat, +} + +/* + * Take a CommandArray pointer from C and reclaim the CommandRaw + * structs and their members/ + * We can't avoid the unsafe {} invocations since we're reading arbitrary + * memory, but it's contained to this function. + */ +#[no_mangle] +pub extern fn free_command_array(commands: *mut CommandArray) { + + let ptr = unsafe { Box::from_raw(commands) }; + let raw_vec = unsafe { Vec::from_raw_parts(ptr.bytes, + ptr.len, + ptr.len) }; + for raw_command in raw_vec.iter() { + unsafe { + let _platform = CString::from_raw(raw_command.platform); + let _name = CString::from_raw(raw_command.name); + let _cmd = CString::from_raw(raw_command.cmd); + let _arg_fmt = CString::from_raw(raw_command.arg_fmt); + /* n_args */ + let arg_vec = Vec::from_raw_parts(raw_command.args, + raw_command.n_args, + raw_command.n_args); + for raw_arg in arg_vec.iter() { + let _name = CString::from_raw(raw_arg.name); + if raw_arg.arg_type == FieldTypeRaw::RawString { + let _arg_str = CString::from_raw(raw_arg.arg_str); + } + } + let _help = CString::from_raw(raw_command.help); + } + } + + /* Frees once it goes out of scope */ + println!("Rust reclaimed {} command structs", raw_vec.len()); +} + +#[no_mangle] +pub extern fn parse_json(filename: *const c_char) -> *mut CommandArray { + + let file: String; + unsafe { + /* Can't trust the promise of a string from C */ + file = CStr::from_ptr(filename).to_string_lossy().into_owned(); + } + + let json_path = Path::new(&file); + let json_file = match File::open(json_path) { + Err(e) => { + println!("Could not open file: {}", e); + return ptr::null_mut(); + } + Ok(f) => f, + }; + + let commands: Vec = match serde_json::from_reader(json_file) { + Err(e) => { + println!("Could not parse JSON: {}", e); + return ptr::null_mut(); + } + Ok(c) => c, + }; + + /* Convert commands into a form more easily passed to C */ + let mut ffi_vec = Vec::::new(); + for command in commands.iter() { + let raw_command = CommandRaw::new(&command); + ffi_vec.push(raw_command); + } + + let len = ffi_vec.len(); + ffi_vec.shrink_to_fit(); + let ffi_ptr = ffi_vec.as_mut_ptr(); + mem::forget(ffi_vec); + + let wrapper = Box::new(CommandArray { bytes: ffi_ptr, len: len }); + return Box::into_raw(wrapper); +} diff --git a/lib/types/types.h b/lib/types/types.h index 9d83d87d..6e5fb7d3 100644 --- a/lib/types/types.h +++ b/lib/types/types.h @@ -66,6 +66,32 @@ struct boot_option { } type; }; +enum cmd_arg_type { + ARG_STR, + ARG_I64, + ARG_F64, +}; + +struct argument { + char *name; + enum cmd_arg_type type; + union { + char *arg_str; + int64_t arg_i64; + double arg_f64; + }; +}; + +struct command { + char *platform; + char *name; + char *cmd; + char *args_fmt; + unsigned int n_args; + struct argument *args; + char *help; +}; + struct plugin_option { char *id; char *name; @@ -74,10 +100,14 @@ struct plugin_option { char *version; char *date; char *plugin_file; + char *command_file; unsigned int n_executables; char **executables; + unsigned int n_commands; + struct command *commands; + void *ui_info; };