Skip to content

Commit

Permalink
Use bamf as application matcher instead of trying ourselves
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmhewitt committed Apr 25, 2017
1 parent 45312ff commit 920de2b
Show file tree
Hide file tree
Showing 6 changed files with 1,706 additions and 47 deletions.
82 changes: 37 additions & 45 deletions src/Backends/CameraBackend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ public class Privacy.Backends.Camera : Privacy.AbstractBackend {
private Widgets.AppList app_list_widget;
private bool icon_visible = false;
private string lsof_stdout;
private Bamf.Matcher app_matcher;
private Gee.HashMap<int, AppInfo> appinfo_by_pid;

public Camera () {
app_list_widget = new Widgets.AppList (BACKEND_NAME);
appinfo_by_pid = new Gee.HashMap<int, AppInfo> ();
app_matcher = Bamf.Matcher.get_default ();
}

public override Gtk.Widget get_app_list () {
Expand All @@ -36,9 +40,6 @@ public class Privacy.Backends.Camera : Privacy.AbstractBackend {
public override void added () {
Timeout.add (1000, () => {
bool in_use = check_camera_in_use ();
if (in_use) {
update_app_list ();
}
if (!icon_visible && in_use) {
activated ();
icon_visible = true;
Expand All @@ -50,6 +51,24 @@ public class Privacy.Backends.Camera : Privacy.AbstractBackend {
});
}

private void update_running_pids () {
var pm = Services.ProcessMonitor.Monitor.get_default ();
pm.update ();
var applications = app_matcher.get_running_applications ();

applications.@foreach ((app) => {
var appinfo = new DesktopAppInfo.from_filename (app.get_desktop_file ());
foreach (var window in app.get_windows ()) {
var parent_pid = (int)window.get_pid ();
appinfo_by_pid[parent_pid] = appinfo;
var sub_processes = pm.get_sub_processes (parent_pid);
foreach (int sp_pid in sub_processes) {
appinfo_by_pid[sp_pid] = appinfo;
}
}
});
}

private bool check_camera_in_use () {
Process.spawn_command_line_sync ("lsof /dev/video0", out lsof_stdout);
if (lsof_stdout.length == 0) {
Expand All @@ -59,7 +78,9 @@ public class Privacy.Backends.Camera : Privacy.AbstractBackend {
}
}

private void update_app_list () {
public void update_app_list () {
update_running_pids ();

app_list_widget.clear_apps ();
Gee.ArrayList<string> added_pids = new Gee.ArrayList<string>();
string[] lines = lsof_stdout.split ("\n");
Expand All @@ -69,7 +90,13 @@ public class Privacy.Backends.Camera : Privacy.AbstractBackend {
if (cols.length < 2) {
break;
}
string pid = cols[1];
string pid = "";
for (int j = 1; j < cols.length; j++) {
if (cols[j] != "") {
pid = cols[j];
break;
}
}
if (pid in added_pids) {
break;
}
Expand All @@ -80,46 +107,11 @@ public class Privacy.Backends.Camera : Privacy.AbstractBackend {
}

private AppInfo? get_app_info_from_pid (string pid) {
var path = FileUtils.read_link ("/proc/%s/exe".printf (pid));
var all_apps = AppInfo.get_all ();
// Check to see if we can get an exact explicit path match
foreach (var app in all_apps) {
var exec_path = app.get_executable ();
if (exec_path != null) {
if (!exec_path.has_prefix ("/")) {
exec_path = get_full_exec_path (exec_path);
}
if (exec_path.has_prefix (path)) {
return app;
}
}
}
// If explicit path fails, try and find an executable with same name
foreach (var app in all_apps) {
var exec_path = app.get_executable ().split ("/");
if (exec_path != null) {
var exec = exec_path[exec_path.length - 1].split (" ")[0];
var pid_name_parts = path.split ("/");
var pid_name = pid_name_parts[pid_name_parts.length - 1];
if (exec == pid_name) {
return app;
}
}
}
return null;
}

// This is the equivalent of the 'which' helper built into UNIX shells.
private string get_full_exec_path (string exec) {
var env_path = Environ.get_variable (Environ.@get (), "PATH");
var paths = env_path.split (":");
foreach (var path in paths) {
var possible_exec = Path.build_path (Path.DIR_SEPARATOR_S, path, exec);
var is_exec = FileUtils.test (possible_exec, FileTest.IS_EXECUTABLE);
if (is_exec) {
return possible_exec;
}
var int_pid = int.parse (pid);
if (appinfo_by_pid.has_key (int_pid)) {
return appinfo_by_pid[int_pid];
} else {
return null;
}
return "";
}
}
9 changes: 8 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
find_package (PkgConfig)

# Add all your dependencies to the list below
pkg_check_modules (DEPS REQUIRED gthread-2.0 gtk+-3.0 wingpanel-2.0)
pkg_check_modules (DEPS REQUIRED gthread-2.0 gtk+-3.0 wingpanel-2.0 libbamf3 libgtop-2.0 gio-unix-2.0)

add_definitions (${DEPS_CFLAGS})
link_directories (${DEPS_LIBRARY_DIRS})
Expand All @@ -19,12 +19,19 @@ vala_precompile (VALA_C ${CMAKE_PROJECT_NAME}
Backends/AbstractBackend.vala
Backends/LocationBackend.vala
Backends/CameraBackend.vala
Services/ProcessMonitor/Monitor.vala
Services/ProcessMonitor/Process.vala
${CMAKE_CURRENT_BINARY_DIR}/config.vala
PACKAGES
gtk+-3.0
wingpanel-2.0
libbamf3
gio-unix-2.0
posix
OPTIONS
--thread
CUSTOM_VAPIS
../vapi/libgtop-2.0.vapi
)

add_library (${CMAKE_PROJECT_NAME} MODULE ${VALA_C})
Expand Down
4 changes: 3 additions & 1 deletion src/Indicator.vala
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ public class Privacy.Indicator : Wingpanel.Indicator {
}
}

public override void opened () {}
public override void opened () {
((Backends.Camera)camera_backend).update_app_list ();
}

public override void closed () {}
}
Expand Down
184 changes: 184 additions & 0 deletions src/Services/ProcessMonitor/Monitor.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (c) 2011-2015 Wingpanel Developers (http://launchpad.net/wingpanel)
*
* 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; either
* version 2 of the License, or (at your option) any later version.
*
* 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.
*/

public class Privacy.Services.ProcessMonitor.Monitor : Object {
private Gee.HashMap<int, Process> process_list;
private Gee.HashSet<int> kernel_process_blacklist;

public signal void process_added (int pid, Process process);
public signal void process_removed (int pid);
public signal void updated ();

private static Monitor? instance = null;

/**
* Construct a new ProcessMonitor
*/
private Monitor () {
debug ("Initialising process monitor.");

process_list = new Gee.HashMap<int, Process> ();
kernel_process_blacklist = new Gee.HashSet<int> ();
update_processes.begin ();
}

public void update () {
update_processes.begin ();

/* Do it one more time for better accuracy */
Timeout.add (100, () => {
update_processes.begin ();

return false;
});
}

public static Monitor get_default () {
if (instance == null) {
instance = new Monitor ();
}

return instance;
}

/**
* Gets a process by its pid, making sure that it's updated.
*/
public Process? get_process (int pid) {
/* if the process is in the kernel blacklist, we don't want to deal with it. */
if (kernel_process_blacklist.contains (pid)) {
return null;
}

/* else, return our cached version. */
if (process_list.has_key (pid)) {
return process_list[pid];
}

/*
* else return the result of add_process
* make sure to lazily call the callback since this is a greedy add
* this way we don't interrupt whatever this method is being called for
*/

/* with a handle_add_process */
return add_process (pid, true);
}

/**
* Returns all direct sub processes of this process
*/
public Gee.Set<int> get_sub_processes (int pid) {
var sub_processes = new Gee.HashSet<int> ();

/* go through and add all of the processes with PPID set to this one */
foreach (var process in process_list.values) {
if (process.ppid == pid) {
sub_processes.add (process.pid);
}
}

return sub_processes;
}

/**
* Gets a read only map of the processes currently cached
*/
public Gee.Map<int, Process> get_process_list () {
return process_list.read_only_view;
}

/**
* Gets all new process and adds them
*/
private async void update_processes () {
var remove_me = new Gee.HashSet<int> ();

/* go through each process and update it, removing the old ones */
foreach (var process in process_list.values) {
if (!process.update ()) {
/* process doesn't exist any more, flag it for removal! */
remove_me.add (process.pid);
}
}

/* remove everything from flags */
foreach (var pid in remove_me) {
remove_process (pid);
}

var uid = Posix.getuid ();
GTop.ProcList proclist;
var pids = GTop.get_proclist (out proclist, GTop.GLIBTOP_KERN_PROC_UID, uid);

for (int i = 0; i < proclist.number; i++) {
int pid = pids[i];

if (!process_list.has_key (pid) && !kernel_process_blacklist.contains (pid)) {
add_process (pid);
}
}

/* call the updated signal so that subscribers can update */
updated ();
}

/**
* Parses a pid and adds a Process to our process_list or to the kernel_blacklist
*
* returns the created process
*/
private Process? add_process (int pid, bool lazy_signal = false) {
/* create the process */
var process = new Process (pid);

if (process.exists) {
if (process.pgrp != 0) {
/* regular process, add it to our cache */
process_list.set (pid, process);

/* call the signal, lazily if needed */
if (lazy_signal) {
Idle.add (() => { process_added (pid, process); return false; });
} else {
process_added (pid, process);
}

return process;
} else {
/* add it to our kernel processes blacklist */
kernel_process_blacklist.add (pid);
}
}

return null;
}

/**
* Remove the process from all lists and broadcast the process_removed signal if removed.
*/
private void remove_process (int pid) {
if (process_list.has_key (pid)) {
process_list.unset (pid);
process_removed (pid);
} else if (kernel_process_blacklist.contains (pid)) {
kernel_process_blacklist.remove (pid);
}
}
}
Loading

0 comments on commit 920de2b

Please sign in to comment.