From cf894302767d5ec483b5f8002bb796a284895bbf Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 1 Aug 2013 18:21:05 -0500 Subject: add a bluetooth backend to track bluetooth being enabled, being hard/soft blocked, and its devices. --- src/killswitch.vala | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/killswitch.vala (limited to 'src/killswitch.vala') diff --git a/src/killswitch.vala b/src/killswitch.vala new file mode 100644 index 0000000..92b1a3c --- /dev/null +++ b/src/killswitch.vala @@ -0,0 +1,147 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * 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 3. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: + * Charles Kerr + */ + +/** + * Monitors whether or not bluetooth is blocked, + * either by software (e.g., a session configuration setting) + * or by hardware (e.g., user disabled it via a physical switch on her laptop) + */ +public class KillSwitch: Object +{ + public bool blocked { get; protected set; default = false; } + + public void try_set_blocked (bool blocked) + { + return_if_fail (this.blocked != blocked); + + // write a 'soft kill' event to fkill + var event = Linux.RfKillEvent() { + op = Linux.RfKillOp.CHANGE_ALL, + type = Linux.RfKillType.BLUETOOTH, + soft = (uint8)blocked + }; + + var bwritten = Posix.write (fd, &event, sizeof(Linux.RfKillEvent)); + if (bwritten == -1) + warning ("Could not write rfkill event: %s", strerror(errno)); + } + + /*** + **** Past this point, it's all RfKill implementation details... + ***/ + + private class Entry + { + public uint32 idx; + public Linux.RfKillType type; + public bool soft; + public bool hard; + } + + private HashTable entries; + private int fd; + private IOChannel channel; + private uint watch; + + private bool calculate_blocked () + { + foreach (Entry entry in entries.get_values()) + if (entry.soft || entry.hard) + return true; + + return false; + } + + ~KillSwitch () + { + Source.remove (watch); + Posix.close (fd); + } + + public KillSwitch () + { + entries = new HashTable(direct_hash, direct_equal); + + var path = "/dev/rfkill"; + fd = Posix.open (path, Posix.O_RDWR | Posix.O_NONBLOCK ); + message ("fd is %d", fd); + if (fd == -1) + { + warning (@"Can't open $path: $(strerror(errno)); KillSwitch disable"); + } + else + { + // read everything that's already there, then watch for more + while (read_event()); + channel = new IOChannel.unix_new (fd); + watch = channel.add_watch (IOCondition.IN, on_channel_event); + } + } + + private bool on_channel_event (IOChannel source, IOCondition condition) + { + read_event (); + return true; + } + + private bool read_event () + { + assert (fd != -1); + + var event = Linux.RfKillEvent(); + var n = sizeof (Linux.RfKillEvent); + var bytesread = Posix.read (fd, &event, n); + + if (bytesread == n) + { + process_event (event); + return true; + } + + return false; + } + + private void process_event (Linux.RfKillEvent event) + { + // we only want things that affect bluetooth + if ((event.type != Linux.RfKillType.ALL) && + (event.type != Linux.RfKillType.BLUETOOTH)) + return; + + switch (event.op) + { + case Linux.RfKillOp.CHANGE: + case Linux.RfKillOp.ADD: + Entry entry = new Entry (); + entry.idx = event.idx; + entry.type = event.type; + entry.soft = event.soft != 0; + entry.hard = event.hard != 0; + entries.insert (entry.idx, entry); + break; + + case Linux.RfKillOp.DEL: + entries.remove (event.idx); + break; + } + + // update the 'blocked' property + blocked = calculate_blocked (); + } +} -- cgit v1.2.3