summaryrefslogtreecommitdiffhomepage
path: root/mullvad-cli/src/cmds/status.rs
blob: 8ddd1953334fd801c07ee56574e45af56425237a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use anyhow::Result;
use clap::{Args, Subcommand};
use futures::StreamExt;
use mullvad_management_interface::{client::DaemonEvent, MullvadProxyClient};
use mullvad_types::states::TunnelState;

use crate::format;

#[derive(Subcommand, Debug, PartialEq)]
pub enum Status {
    /// Listen for tunnel state changes
    Listen,
}

#[derive(Args, Debug)]
pub struct StatusArgs {
    /// Enable verbose output
    #[arg(long, short = 'v')]
    verbose: bool,

    /// Print the current location and IP, based on GeoIP lookups
    #[arg(long, short = 'l')]
    location: bool,

    /// Enable debug output
    #[arg(long, short = 'd')]
    debug: bool,
}

impl Status {
    pub async fn listen(mut rpc: MullvadProxyClient, args: StatusArgs) -> Result<()> {
        while let Some(event) = rpc.events_listen().await?.next().await {
            match event? {
                DaemonEvent::TunnelState(new_state) => {
                    if args.debug {
                        println!("New tunnel state: {new_state:#?}");
                    } else {
                        format::print_state(&new_state, args.verbose);
                    }

                    match new_state {
                        TunnelState::Connected { .. } | TunnelState::Disconnected => {
                            if args.location {
                                print_location(&mut rpc).await?;
                            }
                        }
                        _ => {}
                    }
                }
                DaemonEvent::Settings(settings) => {
                    if args.debug {
                        println!("New settings: {settings:#?}");
                    }
                }
                DaemonEvent::RelayList(relay_list) => {
                    if args.debug {
                        println!("New relay list: {relay_list:#?}");
                    }
                }
                DaemonEvent::AppVersionInfo(app_version_info) => {
                    if args.debug {
                        println!("New app version info: {app_version_info:#?}");
                    }
                }
                DaemonEvent::Device(device) => {
                    if args.debug {
                        println!("Device event: {device:#?}");
                    }
                }
                DaemonEvent::RemoveDevice(device) => {
                    if args.debug {
                        println!("Remove device event: {device:#?}");
                    }
                }
            }
        }
        Ok(())
    }
}

pub async fn handle(cmd: Option<Status>, args: StatusArgs) -> Result<()> {
    let mut rpc = MullvadProxyClient::new().await?;
    let state = rpc.get_tunnel_state().await?;

    if args.debug {
        println!("Tunnel state: {state:#?}");
    } else {
        format::print_state(&state, args.verbose);
    }

    if args.location {
        print_location(&mut rpc).await?;
    }

    if cmd == Some(Status::Listen) {
        Status::listen(rpc, args).await?;
    }
    Ok(())
}

async fn print_location(rpc: &mut MullvadProxyClient) -> Result<()> {
    let location = match rpc.get_current_location().await {
        Ok(location) => location,
        Err(error) => match &error {
            mullvad_management_interface::Error::NoLocationData => {
                println!("Location data unavailable");
                return Ok(());
            }
            _ => return Err(error.into()),
        },
    };
    if let Some(ipv4) = location.ipv4 {
        println!("IPv4: {ipv4}");
    }
    if let Some(ipv6) = location.ipv6 {
        println!("IPv6: {ipv6}");
    }

    println!(
        "Position: {:.5}°N, {:.5}°W",
        location.latitude, location.longitude
    );
    Ok(())
}