Skip to content

Commit

Permalink
Add support for dbus and chrome notifications in mandelboxes (#5304)
Browse files Browse the repository at this point in the history
* integrate beeep-wrapper builds into cmake/download-binaries

* enable throwing notifs on awesomewm using dbus protocols: first step toward linking awesomewm notifs with whist protocol

* enable d-bus env variable syncing for notifications to show up in d-bus

* fix cmakelists conflict

* move to asynchronous event listening with libevent

* successfully add multithreaded notification catching

* enable packet sending, add client-side interception code

* add client-side notif-receiving capability

* format notification code, add docstrings

* clean up old comments

* fix minor bugs regarding beeep download + linking

* enable native client-side notification display on mac

* fix fatal OOB memory error

* remove beeep

* add stubs for windows and linux native notifications

* prevent notifications from being thrown in the awesome window

* fix last-line \n and refactor notifications in client

* further reorganization of client-side notification files

* fix client-side compilation errors

* remove unnecessary bundle hacks

* docs for display notifications

* update notification docs

* further updates to notification docs

* clean up dockerfiles/scripts

* reorganize notifs docs

* fix display notifs

* fix dockerfile lint issues

* major refactoring of server-side notifications.c/h: use thread_init and thread_destroy, remove need for globals, etc.

* further refactoring and documentation of notifications.c

* add some docs to notifications.h

* log statistics

* fix copyright

* add more detailed docs to server-side notifs

* reduce notif size significantly

* remove unistd

* fully test local notifs

* only import notifications on linux

* add windows/macos stubs for notifications - feature only avail on linux

* make logging accessible to stub fns

* include notifications before the non-linux stubs

* fix clang-tidy issues

* add comment abt notification size

* remove backticks

* refactor dbus configurations into an executable

* remove unnecessary env variable passing - it's already defined

* enable gnome keyring and connect to chrome

* add dbus-address to cmdling OPTIONS

* remove ifdef linux in main.c via wrapper fn

* remove unnecessary event_base typedef

* always include notifications.h

* fix fatal server-side bash parsing error

* improve robustness of bash parsing by bounding substring from left _and_ right

* update dbus loading scheme

* add comment about display number

* fix error introduced by rebase

* preliminary implementation: move to WhistServerMessage

* resolve various client-side bugs

* change name of notif display fn

* move display notification to os_utils

* refactor server-side notifications API: use notifications handler

* return null ptr from windows notif handler

* fix docstring param

* move windows/linux display notification implementations to os_utils

* store thread so whists_wait_thread can be applied upon destroy

* move whistnotification definition

* change function signature of display notification

* fix reference bug

* fix C syntax error

* fix WhistThread representation error

* add trailing newline in osutils

* factor notification packaging out

* fix -fpermissive errors in server-side notifications

* add simple unit tests for packaging and displaying notifications

* remove cerr outputs in testing

* do not disable notifications

* add notifications to testing cmakelists

* add null shellcheck source

* make package_notifications globally accessible

* add whistprivate

* import notifications.h, not c

* move package notifications to os_utils

* remove unused variables

* remove deprecateed WhistPrivate construct

Co-authored-by: Kevin Meng <[email protected]>
Co-authored-by: Kevin Meng <[email protected]>
  • Loading branch information
3 people authored Feb 3, 2022
1 parent a13525c commit 6f9fb0e
Show file tree
Hide file tree
Showing 32 changed files with 986 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ node_modules

# VS Code
.vscode/
# jetbrains
.idea/

# testing
coverage
Expand Down
3 changes: 2 additions & 1 deletion backend/services/host-service/host-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,9 @@ func SpinUpMandelbox(globalCtx context.Context, globalCancel context.CancelFunc,
"NET_BIND_SERVICE",
"SYS_CHROOT",
"SETFCAP",
// NOTE THAT CAP_SYS_NICE IS NOT ENABLED BY DEFAULT BY DOCKER --- THIS IS OUR DOING
// NOTE THAT THE FOLLOWING ARE NOT ENABLED BY DEFAULT BY DOCKER --- THIS IS OUR DOING
"SYS_NICE",
"IPC_LOCK",
}),
ShmSize: 2147483648,
Tmpfs: tmpfs,
Expand Down
18 changes: 18 additions & 0 deletions mandelboxes/base/Dockerfile.20
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,24 @@ COPY input/Xmodmap /root/.Xmodmap
# General Utilities
#########################

# Install dbus + event requirements
RUN apt-get update && apt-get install --allow-downgrades --no-install-recommends -y \
dbus-x11 \
libdbus-1-dev \
libevent-dev \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Install gnome keyring + chrome authentication utilities
RUN apt-get update && apt-get install -y --no-install-recommends \
gir1.2-secret-1 \
gnome-keyring \
libsecret-1-0 \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Install general utility packages
RUN apt-get update && apt-get install --allow-downgrades --no-install-recommends -y \
xvfb \
Expand Down
2 changes: 2 additions & 0 deletions mandelboxes/base/display/theme/awesome-rc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ local wibox = require("wibox")
local beautiful = require("beautiful")
-- Notification library
local naughty = require("naughty")
-- Disable server-side awesome notification display; this will be handled on the client-side
naughty.suspend()

-- Needed for create_titlebar_widget_button
local imagebox = require("wibox.widget.imagebox")
Expand Down
21 changes: 18 additions & 3 deletions mandelboxes/base/display/xinitrc
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,30 @@ echo "playerctld daemon started as PID: $playerctld_pid"
# but not in-between as this can lead to a race condition between AwesomeWM and DPI setting
/usr/share/whist/update-whist-dpi.sh

# Start D-Bus
dbus_result=$($RUN_AS_WHIST "dbus-launch --sh-syntax --exit-with-x11")

# Extract environment variables for D-Bus configuration
# The -10 comes from the display ID
dbus_config_file="/home/whist/.dbus/session-bus/$(cat /etc/machine-id)-10"
block-until-file-exists.sh $dbus_config_file >&1
# shellcheck source=/dev/null
. "$dbus_config_file"
export DBUS_SESSION_BUS_ADDRESS
echo "d-bus address: $DBUS_SESSION_BUS_ADDRESS"

# Unlock gnome keyring
$RUN_AS_WHIST "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS echo tmp_password | /usr/bin/gnome-keyring-daemon --replace --unlock --components=secrets"

# Start the AwesomeWM window manager and get the PID of the backgrounded process
echo "Starting AwesomeWM in the background..."
$RUN_AS_WHIST "awesome" &
$RUN_AS_WHIST "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS awesome" &
awesome_pid=$!
echo "AwesomeWM started as PID: $awesome_pid"

# Start the XSettings daemon to make DPI changes responsive and get the PID of the backgrounded process
echo "Starting 'xsettingsd' in the background..."
$RUN_AS_WHIST "xsettingsd" &
echo "Starting xsettingsd in the background..."
$RUN_AS_WHIST "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS xsettingsd" &
xsettingsd_pid=$!
echo "xsettingsd started as PID: $xsettingsd_pid"

Expand Down
19 changes: 19 additions & 0 deletions mandelboxes/base/main/run-whist-server.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,25 @@ if [ "$ENV_NAME" != "localdev" ]; then
trap cleanup EXIT ERR
fi

# WhistServer and cookie authentication require that the D-Bus environment variables be set
# The -10 comes from the display ID
dbus_config_file="/home/whist/.dbus/session-bus/$(cat /etc/machine-id)-10"
# shellcheck source=/dev/null
. "$dbus_config_file"
export DBUS_SESSION_BUS_ADDRESS
echo "loaded d-bus address in run-whist-server.sh: $DBUS_SESSION_BUS_ADDRESS"

# Add D-Bus address to options
OPTIONS="$OPTIONS --dbus-address=$DBUS_SESSION_BUS_ADDRESS"

# Below is commented testing code that can be used in the future to debug cookie encryption.
# It should NOT print out peanuts. Instead you should see a byte string similar to b'q6dj0uwi6luGvZ+2zisaMQ=='.
# This is tech debt! Currently the cookies are still being encrypted with peanuts because Chrome is not registering with
# the keyring until the application actually starts. Our goal is to make the below code output a secret in the desired format
# BEFORE import_user_browser_data.py is called so that cookies can be encrypted more securely.

# (while true; do DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS python3 -c "import os; os.seteuid(1000); import browser_cookie3; print('COOKIE:', browser_cookie3.get_linux_pass('chrome')); os.seteuid(0);" && sleep 1; done) &

# Set user upload target, if file exists
if [ -f "$USER_DEST_BROWSER_FILENAME" ] && [ -f "$BROWSER_DATA_FILE_FILENAME" ]; then
# Imports user browser data if file exists
Expand Down
8 changes: 4 additions & 4 deletions mandelboxes/base/utils/block-until-file-exists.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ while read -r line; do
echo "inotify event: $line"
case $line in
"Watches established.")
echo "Checking if $FILE_NAME already exists in $DIR_NAME..."
[[ -f $DIR_NAME/$FILE_NAME ]] && echo "Yes, it does!" && break
echo "inotify event: Checking if $FILE_NAME already exists in $DIR_NAME..."
[[ -f $DIR_NAME/$FILE_NAME ]] && echo "inotify event: $FILE_NAME exists!" && break
;;
"$FILE_NAME")
echo "$FILE_NAME has been created!"
$FILE_NAME)
echo "inotify event: $FILE_NAME has been created!"
break
;;
esac
Expand Down
8 changes: 4 additions & 4 deletions mandelboxes/base/utils/block-while-file-exists.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ while read -r line; do
echo "inotify event: $line"
case $line in
"Watches established.")
echo "Checking if $FILE_NAME already does NOT exist in $DIR_NAME..."
[[ ! -f $DIR_NAME/$FILE_NAME ]] && echo "This file does not exist!" && break
echo "inotify event: Checking if $FILE_NAME already does NOT exist in $DIR_NAME..."
[[ ! -f $DIR_NAME/$FILE_NAME ]] && echo "inotify event: $FILE_NAME does not exist!" && break
;;
"$FILE_NAME")
echo "$FILE_NAME has been deleted!"
$FILE_NAME)
echo "inotify event: $FILE_NAME has been deleted!"
break
;;
esac
Expand Down
34 changes: 26 additions & 8 deletions mandelboxes/base/utils/browser-data/import_user_browser_data.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import os
import json
import sys
Expand All @@ -12,6 +13,26 @@
sentry_sdk.init(dsn=os.getenv("SENTRY_DSN"))

USER_CONFIG_PATH = "/whist/userConfigs/"
GNOME_KEYRING_SECRET = b"peanuts"
# Default password is peanuts. Could be due to
# (1) there is no password in the keyring for chrome or
# (2) the keyring is not discoverable by chrome.
# The b"" indicates that it is a bytestring.


def get_gnome_keyring_secret():
os.seteuid(1000) # the d-bus is running on `whist`; we need its euid to connect

my_pass = browser_cookie3.get_linux_pass("chrome")
if my_pass.decode("utf-8") == "peanuts": # Misconfiguration of GNOME keyring + Chrome
print(
"WARN: could not find the GNOME keyring password for Chrome. Resorting to Chrome default..."
)
else:
print("GNOME keyring password successfully retrieved")

os.seteuid(0) # return to `root` euid
return my_pass


def get_browser_default_dir(browser_name):
Expand Down Expand Up @@ -109,19 +130,14 @@ def encrypt(value):
Returns:
str: encrypted string
"""
global GNOME_KEYRING_SECRET

salt = b"saltysalt"
iv = b" " * 16
length = 16
iterations = 1

# will assume it's linux for now
# We will hardcode the my_pass for now as GNOME-Keyring is not properly configured
# and will result in the default value `peanuts`

my_pass = b"peanuts"

key = PBKDF2(my_pass, salt, iterations=iterations).read(length)

key = PBKDF2(GNOME_KEYRING_SECRET, salt, iterations=iterations).read(length)
aes_cbc_encrypt = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv=iv))

encoded_value = value.encode("utf-8")
Expand Down Expand Up @@ -278,6 +294,8 @@ def create_extension_files(extensions, custom_script=None):
python3 import_user_browser_data.py <browser target> <user data file>
"""
GNOME_KEYRING_SECRET = get_gnome_keyring_secret()

if len(sys.argv) == 3:
browser = sys.argv[1]
browser_data_file = sys.argv[2]
Expand Down
8 changes: 7 additions & 1 deletion mandelboxes/browsers/chrome/start-chrome.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ flags=(
"--disable-font-subpixel-positioning"
"--force-color-profile=display-p3-d65"
"--disable-gpu-process-crash-limit"
"--disable-notifications" #This is tech debt, remove when notification redirection is implemented
"--no-default-browser-check"
"--load-extension=/opt/teleport/chrome-extension"
)
Expand All @@ -65,6 +64,13 @@ fi
# on the user settings.
flags+=("$INITIAL_URL")

# Load D-Bus configurations; necessary for Chrome
# The -10 comes from the display ID
dbus_config_file="/home/whist/.dbus/session-bus/$(cat /etc/machine-id)-10"
# shellcheck source=/dev/null
. "$dbus_config_file"
export DBUS_SESSION_BUS_ADDRESS
echo "loaded d-bus address in start-chrome.sh: $DBUS_SESSION_BUS_ADDRESS"

# Start Chrome
# flag-switches{begin,end} are no-ops but it's nice convention to use them to surround chrome://flags features
Expand Down
8 changes: 8 additions & 0 deletions protocol/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Install notification utilities: dbus and libevent
RUN apt-get update && apt-get install --allow-downgrades --no-install-recommends -y \
libdbus-1-dev \
libevent-dev \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Install clang-tidy 6. We must hardcode this version, else we will install the
# overaggressive recent version, and we need to symlink it to be able to call
# it as "clang-tidy".
Expand Down
13 changes: 10 additions & 3 deletions protocol/client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,9 @@ target_link_libraries(WhistClient ${PLATFORM_INDEPENDENT_LIBS})
################## LINUX ##################
]]
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_sources(WhistClient PRIVATE native_window_utils_x11.c)
target_sources(WhistClient PRIVATE
native_window_utils_x11.c
)

find_package(X11 REQUIRED
Xfixes)
Expand Down Expand Up @@ -159,7 +161,10 @@ endif()
################## MACOS ##################
]]
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_sources(WhistClient PRIVATE native_window_utils_mac.m)
target_sources(WhistClient PRIVATE
native_window_utils_mac.m
notif_utils/display_notifs_mac.m
)

target_link_libraries(WhistClient ${MAC_SPECIFIC_CLIENT_LIBS})
endif()
Expand All @@ -172,7 +177,9 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# this comes up when we used precompiled libraries which we do not have .pdb files for
set(CMAKE_EXE_LINKER_FLAGS "/nodefaultlib /nologo /ignore:4099 ")

target_sources(WhistClient PRIVATE native_window_utils_windows.c)
target_sources(WhistClient PRIVATE
native_window_utils_windows.c
)

target_link_libraries(WhistClient whistInput ${LIBMFX})

Expand Down
2 changes: 2 additions & 0 deletions protocol/client/client_statistic.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ void init_client_statistics(void) {
(StatisticInfo){"VIDEO_TIME_BETWEEN_FRAMES", true, false, false};
client_statistic_info[VIDEO_UPDATE_TIME] =
(StatisticInfo){"VIDEO_UPDATE_TIME", true, false, false};
client_statistic_info[NOTIFICATIONS_RECEIVED] =
(StatisticInfo){"NOTIFICATIONS_RECEIVED", false, false, true};
}
1 change: 1 addition & 0 deletions protocol/client/client_statistic.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef enum {
VIDEO_SDL_WRITE_TIME,
VIDEO_TIME_BETWEEN_FRAMES,
VIDEO_UPDATE_TIME,
NOTIFICATIONS_RECEIVED,
CLIENT_NUM_METRICS
} ClientMetric;

Expand Down
22 changes: 22 additions & 0 deletions protocol/client/handle_server_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ Includes
#include <whist/core/whist.h>
#include <whist/utils/clock.h>
#include <whist/logging/logging.h>
#include <whist/logging/log_statistic.h>
#include <whist/utils/window_info.h>
#include <whist/utils/os_utils.h>
#include "client_utils.h"
#include "audio.h"
#include "network.h"
#include "sdl_utils.h"
#include "client_statistic.h"

#include <stddef.h>

Expand All @@ -51,6 +54,7 @@ static int handle_open_uri_message(WhistServerMessage *wsmsg, size_t wsmsg_size)
static int handle_fullscreen_message(WhistServerMessage *wsmsg, size_t wsmsg_size);
static int handle_file_metadata_message(WhistServerMessage *wsmsg, size_t wsmsg_size);
static int handle_file_chunk_message(WhistServerMessage *wsmsg, size_t wsmsg_size);
static int handle_notification_message(WhistServerMessage *wsmsg, size_t wsmsg_size);

/*
============================
Expand Down Expand Up @@ -90,6 +94,8 @@ int handle_server_message(WhistServerMessage *wsmsg, size_t wsmsg_size) {
return handle_file_chunk_message(wsmsg, wsmsg_size);
case SMESSAGE_FILE_METADATA:
return handle_file_metadata_message(wsmsg, wsmsg_size);
case SMESSAGE_NOTIFICATION:
return handle_notification_message(wsmsg, wsmsg_size);
default:
LOG_WARNING("Unknown WhistServerMessage Received (type: %d)", wsmsg->type);
return -1;
Expand Down Expand Up @@ -271,3 +277,19 @@ static int handle_file_chunk_message(WhistServerMessage *wsmsg, size_t wsmsg_siz

return 0;
}

static int handle_notification_message(WhistServerMessage *wsmsg, size_t wsmsg_size) {
/*
Handle a file chunk message.
Arguments:
wsmsg (WhistServerMessage*): message packet from client
Returns:
(int): Returns -1 on failure, 0 on success
*/

display_notification(wsmsg->notif);
log_double_statistic(NOTIFICATIONS_RECEIVED, 1.);

return 0;
}
42 changes: 42 additions & 0 deletions protocol/client/notif_utils/display_notifs_mac.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright 2022 Whist Technologies, Inc.
* @file display_notifs_mac.m
* @brief Handles client-side notification display on MacOS
Interfaces with native libraries to display notifications thrown from the server application.
*/

/*
============================
Includes
============================
*/

#import <stdio.h>
#import <Cocoa/Cocoa.h>
#import <UserNotifications/UserNotifications.h>
#import <objc/runtime.h>

#import <whist/logging/logging.h>
#import <whist/utils/os_utils.h>

/*
============================
Public Function Implementations
============================
*/

int display_notification(WhistNotification notif) {
NSUserNotification *n = [[NSUserNotification alloc] init];

char *title = notif.title, *msg = notif.message;
LOG_INFO("Trying to display notif on OSX: %s | %s", title, msg);

n.title = [NSString stringWithUTF8String:title];
n.informativeText = [NSString stringWithUTF8String:msg];

[NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:n];

return 0;
}
1 change: 1 addition & 0 deletions protocol/client/sync_packets.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Includes
#include <whist/network/network.h>
#include <whist/logging/log_statistic.h>
#include <whist/logging/logging.h>

#include "handle_server_message.h"
#include "network.h"
#include "audio.h"
Expand Down
Loading

0 comments on commit 6f9fb0e

Please sign in to comment.