balena-os / wifi-connect

Easy WiFi setup for Linux devices from your mobile phone or laptop
Apache License 2.0
1.27k stars 354 forks source link

Building from source files or other options for modifying resin-wifi-connect #149

Open kadhirvelm opened 6 years ago

kadhirvelm commented 6 years ago

Background We use RPi3s to control our hardware devices, which is remotely updated through Resin. We've got the whole pipeline setup and working smoothly except we were previously using [https://github.com/resin-io-playground/resin-wifi-connect-python-example](Resin Wifi Connect Python) to let our customers get their RPi3s connected to the internet. We had to make some modifications to that code to get it to work with our software, namely writing an email address to the disk on the initial connection to be retrieved later.

We're currently in the process of updating to the latest Rust-based stuff. We would like to make the same modification to write the email address and have our RPi3s run it on start. Related to https://github.com/resin-io/resin-wifi-connect/issues/139, but we've been running into issues.

What we've done so far

We first ported over all the files to our project, then modified server.rs to write the same email address (from our original project) to the same file:

fn connect(req: &mut Request) -> IronResult<Response> {
    let (ssid, passphrase, email) = {
        let params = get_request_ref!(req, Params, "Getting request params failed");
        let ssid = get_param!(params, "ssid", String);
        let passphrase = get_param!(params, "passphrase", String);
        let email = get_param!(params, "email", String);
        (ssid, passphrase, email)
    };

    fn write_email(email: String) {
        use std::io::Write;
        let mut email_file = File::create("/usr/share/config/userdata").expect("Unable to write file");
        email_file.write_all(email.as_bytes()).expect("Unable to write email to created file");
    }

    write_email(email);....

And then we wanted to get that to at least run on the RPi3s, as a sanity check, so we setup a .travis.yml to build and deploy it to resin. Not totally sure what the best way to go about that is, but this is what we've done so far:

install: true
sudo: required
dist: trusty
services: docker
language: rust
rust: "stable"
addons:
  ssh_known_hosts:
     - git.resin.io
os: linux
install:
 - sudo apt-get install libdbus-1-dev
script:
   - set -e
   - cargo install cross
   - cross build --release --target armv7-unknown-linux-gnueabihf
   - mv target/target=armv7-unknown-linux-gnueabihf/release/wifi-connect .
   - if [ $TRAVIS_BRANCH = 'development' ]; then
       eval "$(ssh-agent -s)";
       echo -e $RESIN_DEPLOY_KEY > id_rsa;
       chmod 0600 id_rsa;
       ssh-add ./id_rsa;
       git remote add resin_dev $RESIN_REMOTE_DEV;
       git add --all;
       git commit -a -m "Cargo built";
       git push -f resin_dev development:master;
     fi
   - set +e

No other modifications to the source files from resin-wifi-connect. When we ran it on travis, it kept popping up this error when cross build ran:

error: failed to run custom build command for `dbus v0.5.3`
process didn't exit successfully: `/target/release/build/dbus-c9094369a11371da/build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(PkgConfig(CrossCompilation), State { next_error: None })', /checkout/src/libcore/result.rs:906:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Which we tried to fix by adding dbus= "0.6.0" to the Cargo.toml file, which changed the error to this:

error: failed to run custom build command for `libdbus-sys v0.1.1`
process didn't exit successfully: `/target/release/build/libdbus-sys-74933906340f63f2/build-script-build` (exit code: 101)
--- stderr
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(PkgConfig(CrossCompilation), State { next_error: None })', /checkout/src/libcore/result.rs:906:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Now we're a bit stuck and starting to think we're probably approaching this the wrong way. Any idea what we're doing wrong? Is this the right way to modify resin-wifi-connect with the modifications we need? Would it be easier to actually build the rust application on the RPi3 itself in the Docker.template file? Happy to provide anything else needed!

majorz commented 6 years ago

I also had problems with building it using cross. The problem was that the Docker image they are using is based on Ubuntu 12.04, which does not contain more recent libdbus-1-dev required for building the dbus-rs library.

This is one of the reasons we are using a custom docker image for cross compilation on each target. You may find the one for Rpi3 here: https://github.com/resin-io/resin-wifi-connect/blob/master/scripts/docker/armv7-unknown-linux-gnueabihf/Dockerfile

We currently use CircleCI for building the binaries for all architectures. I would recommend cloning the repo and using the same build approach. This way you will be able to keep up easily with future changes. You may use the information in https://github.com/resin-io/resin-wifi-connect/issues/139#issuecomment-340888239 for further reference. (I need to put a documentation page for this, since this looks like a common question)

Building on the Rpi3 itself is also an option if you need new builds occasionally, but I would still recommend using the CircleCI approach.

Please let me know if you have any questions and I will be happy to answer!

kadhirvelm commented 6 years ago

Ahh got it, that makes a lot of sense! Okay got it working with that custom docker image, though we'd prefer to stick to Travis since that's where all of our other repos currently are being deployed from.

For anyone else that might need it, here was the final .travis.yml file we used:

sudo: required
dist: trusty
services: docker
addons:
  ssh_known_hosts:
    - git.resin.io
os: linux
before_install:
  - chmod +x ./deploy.sh
  - docker build -t armv7-unknown-linux-gnueabihf .
script:
  - set -e
  - docker run armv7-unknown-linux-gnueabihf /bin/bash -c "./deploy.sh"
  - set +e

With deploy.sh containing whatever commands you want it to run inside the docker image. Since we wanted to build for an RPi3, we used cargo build --release --target=armv7-unknown-linux-gnueabihf; in the docker container.

We also had to add a COPY . . at the bottom of the armv7-unknown-linux-gnueabihf file to have access to everything, which we put in the root directory of the project.

majorz commented 6 years ago

Great job!

kadhirvelm commented 6 years ago

Two quick questions - it'd be really convenient if we could move server.rs to a different port so we can have our primary app running on port 80, on the off chance the RPi3 can't connect to the internet.

First question, we're running in parallel because on the off chance the RPi3 is offline, we can tunnel into it with a local connection (ethernet cord) and still use our hardware device. It used to work with the old resin-wifi-connect running in python, but we're not able to get the pi to recognize an ethernet cable with resin-wifi-connect running. Is it just my pi? or is that the expected behavior?

Second question, given we wanted resin-wifi-connect and our software to run in parallel, we moved server.rs to port 81 so our app could run on port 80 (so we can access it via the public url on resin). So far we edited the address on line 152 in server.rs, moving it to port 81:

let address = format!("{}:81", gateway_clone);

Which worked and now when I access the captive portal, I can see our software running on port 80, and index.html running on port 81. I also made sure to change the urls index.js in public/ were hitting to http://192.168.42.1:81/ and got everything working again, but is it possible to move the actual captive portal primary display to port 81 instead of 80?

Can also move this to a separate issue if that's easier!

majorz commented 6 years ago

I will work today on the second issue - about the port. As there was another request for this as well and I would like to include that in the next release which is coming very soon.

Can you please provide me with steps to reproduce for the first question and a little bit more explanation, so that I can understand better and start investigating?

kadhirvelm commented 6 years ago

That would be awesome on the second issue, thank you!

I actually think we can ignore the first issue, I don't think it has to do with resin-wifi-connect. I used the wifi+ethernet setup to get resin running, but whenever I plug in an ethernet cord the pi doesn't even recognize it. It just so happens that the only other thing running is resin-wifi-connect, but I think the problem for that lies somewhere else?

Overall steps to reproduce my issue with the ethernet cord:

  1. Use wifi + ethernet setup with proper wifi credentials to set up resin.io on the RPi3
  2. Let resin-wifi-connect download to the device
  3. Clear wifi credentials and restart
  4. Plug in ethernet cord

Currently not getting the ethernet port lights to turn on when I do number 3

Thanks so much for your help @majorz!

majorz commented 6 years ago

Thanks a lot! I understand it now better. I will follow up on this one soon as well.

majorz commented 6 years ago

So when a page is opened when connected to the access point (e.g. captive portal check request by the phone OS), the host name is resolved to the captive portal IP address (using dnsmasq), but the request still targets port 80. It looks like some iptable calls for port forwarding to port 80 should be added additionally for this all to work. I will follow up with more information once I have a more complete answer.

kadhirvelm commented 6 years ago

Another side issue I've been having is whenever I set the credentials with one router, then move to another one, it doesn't bring up the access point again to reset the wifi credentials. Any idea how I could include that functionality? It's not vital, but would be very helpful

majorz commented 6 years ago

@kadhirvelm The ability to support multiple WiFi credentials (e.g. moving the device from one place to another) is coming very shortly. The code is already ready in a branch and once docs are finished and review is done we will release it.

I hoped to include the different port number feature in the same next release, but it will be done after, as it is not as straightforward and I have to consult with the other team members on this.

I will ping you once the new release with multiple WiFi credentials is ready.

majorz commented 6 years ago

I still have to look into the ethernet cord issue above and I am just leaving a message for myself here, as not to forget about it :)