Skip to content

Commit

Permalink
Improved support for systemd autostart.
Browse files Browse the repository at this point in the history
  • Loading branch information
ray-kast committed Mar 5, 2021
1 parent 4bfa917 commit 2965720
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 23 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
[package]
name = "empress"
version = "1.0.2"
version = "1.0.3"
authors = ["rookie1024 <[email protected]>"]
edition = "2018"
description = "A D-Bus MPRIS daemon for controlling media players."
documentation = "https://docs.rs/empress"
readme = "README.md"
repository = "https://github.com/ray-kast/empress"
license = "AGPL-3.0-or-later"
license-file = "LICENSE"
keywords = ["mpris", "music", "play", "pause", "skip"]
categories = ["command-line-utilities", "multimedia", "os"]

Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ $ cargo install empress
```

Then launch the daemon process in order to use it — you will probably want to
put this in an rcfile or service definition:
put this in an rcfile (or see below for setting up a service):

```sh
$ empress server
Expand All @@ -39,3 +39,32 @@ $ empress play-pause
The current list of commands includes, `next`, `previous`, `pause`,
`play-pause`, `stop`, and `play`, which all function as you would expect. For
more help you can always run `empress help`.

## Installing `empress` as a Service

`empress` can be installed as a service to allow autostarting and lifecycle
management using D-Bus and systemd. To do this, run the following:

```sh
$ scripts/install-services.sh -l <path to empress binary>
```

This will install the D-Bus and systemd service files into `~/.local/share`. If
you installed `empress` with `cargo install`, the path to the binary will
probably look something like `/home/<user>/.cargo/bin/empress`.

If you want to make a system-wide installation, simply omit the `-l` flag. This
will install the session service files into `/usr` instead (but note that
`empress` **never** runs as a D-Bus system bus).

### Uninstalling

To remove installed service definitions, simply run:

```sh
$ scripts/install-services.sh -rl
```

Like with above, remove the `-l` to uninstall the service files from the system
folders.

13 changes: 13 additions & 0 deletions etc/empress.service.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Systemd unit file for empress

[Unit]
Description=A D-Bus MPRIS daemon for controlling media players.
Documentation=https://github.com/ray-kast/empress/
PartOf=graphical-session.target

[Service]
Type=dbus
BusName=net.ryan_s.Empress1
ExecStart=/path/to/empress server

# vim:set ft=systemd:
8 changes: 8 additions & 0 deletions etc/net.ryan_s.Empress1.service.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# D-Bus service file for empress

[D-BUS Service]
Name=net.ryan_s.Empress1
Exec=/path/to/empress server
SystemdService=empress.service

# vim:set ft=toml:
95 changes: 95 additions & 0 deletions scripts/install-services.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#/bin/bash

function usage() {
cat <<EOF >&2
Usage: scripts/install.sh [FLAGS] [PATH]
Installs (or uninstalls) service files for empress. PATH is an absolute path
to the empress binary, and is required if -r is not present.
Flags:
-h Display this message and quit.
-l Install service files to a local directory.
-n Generate service files in target/ but do not install them.
-r Remove already-installed service files.
EOF
}

set -e

loc=''
noop=''
remove=''

while getopts "hlnr" opt; do
case $opt in
h)
usage
exit 0
;;
l)
loc='1'
;;
n)
noop='1'
;;
r)
remove='1'
;;
\?)
usage
exit 1
;;
esac
done

shift $((OPTIND - 1))

if [[ -z "$remove" ]] && (( $# != 1 )); then
usage
exit 1
fi

cd "$(dirname "$0")/.."

systemd_file=empress.service
dbus_file=net.ryan_s.Empress1.service

if [[ -z "$remove" ]]; then
sed -e "s:/path/to/empress:$1:" "etc/$systemd_file.in" >"target/$systemd_file"
sed -e "s:/path/to/empress:$1:" "etc/$dbus_file.in" >"target/$dbus_file"
fi

[[ -n "$noop" ]] && exit 0

systemd_dir="/usr/lib/systemd/user/"
dbus_dir="/usr/share/dbus-1/services/"

if [[ -n "$loc" ]]; then
base="$XDG_DATA_HOME"

[[ -n "$base" ]] || base="$HOME"

systemd_dir="$base/.local/share/systemd/user"
dbus_dir="$base/.local/share/dbus-1/services"
fi

mkdir -p "$systemd_dir"
mkdir -p "$dbus_dir"

fail=0

if [[ -z "$remove" ]]; then
(
install -vDm644 "target/$systemd_file" -t "$systemd_dir" && \
install -vDm644 "target/$dbus_file" -t "$dbus_dir"
) || fail=$?
else
mv -v "$systemd_dir/$systemd_file" /tmp || fail=$?
mv -v "$dbus_dir/$dbus_file" /tmp || fail=$?
fi

if [[ -n "$fail" ]]; then
echo "ERROR: Installation failed."
exit $fail
fi
2 changes: 0 additions & 2 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ set -e

cd "$(dirname "$0")/.."

ls

cargo build --bin empress

echo $'\x1b[1mStarting server...\x1b[m'
Expand Down
31 changes: 25 additions & 6 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::time::Duration;
use std::{sync::Arc, time::Duration};

use anyhow::{Context, Error};
use dbus::nonblock::Proxy;
use dbus::nonblock::{Proxy, SyncConnection};
use dbus_tokio::connection;
use tokio::{select, sync::oneshot, task};

Expand All @@ -20,10 +20,7 @@ pub(super) async fn run(id: MethodId) -> Result {
let run = async move {
let proxy = Proxy::new(&*SERVER_NAME, &*SERVER_PATH, Duration::from_secs(2), conn);

let () = proxy
.method_call(&*INTERFACE_NAME, id.to_string(), ())
.await
.context("failed to contact empress server")?;
try_send(&proxy, id).await?;

Ok(())
};
Expand All @@ -35,3 +32,25 @@ pub(super) async fn run(id: MethodId) -> Result {
),
)
}

async fn try_send(proxy: &Proxy<'_, Arc<SyncConnection>>, id: MethodId) -> Result {
const MAX_TRIES: usize = 5;

let mut i = 0;

loop {
match proxy
.method_call(&*INTERFACE_NAME, id.to_string(), ())
.await
.context("failed to contact empress server")
{
Err(e) if i < MAX_TRIES => eprintln!("WARNING: {:?}", e),
r => break r,
}

i += 1;
eprintln!("Retry attempt {:?}", i);

tokio::time::sleep(Duration::from_millis(20)).await;
}
}
7 changes: 7 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
#![warn(missing_docs, clippy::all, clippy::pedantic, clippy::cargo)]
#![deny(broken_intra_doc_links, missing_debug_implementations)]

//! Binary crate for `empress`. See [the
//! README](https://github.com/ray-kast/empress/blob/master/README.md) for more
//! details.
use anyhow::{Context, Error};
use lazy_static::lazy_static;
use structopt::StructOpt;
Expand Down
27 changes: 18 additions & 9 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ mod mpris {
}

pub mod player {
use super::*;
use super::{lazy_static, Interface, Member};

lazy_static! {
pub static ref INTERFACE: Interface<'static> = "org.mpris.MediaPlayer2.Player".into();
Expand Down Expand Up @@ -147,7 +147,7 @@ impl Player {
}

async fn next(self, conn: &SyncConnection) -> Result<Self> {
let () = self.call(&*mpris::player::NEXT, conn).await?;
self.call(&*mpris::player::NEXT, conn).await?;

Ok(Self {
status: self.status,
Expand All @@ -157,7 +157,7 @@ impl Player {
}

async fn previous(self, conn: &SyncConnection) -> Result<Self> {
let () = self.call(&*mpris::player::PREVIOUS, conn).await?;
self.call(&*mpris::player::PREVIOUS, conn).await?;

Ok(Self {
status: self.status,
Expand All @@ -167,7 +167,7 @@ impl Player {
}

async fn pause(self, conn: &SyncConnection) -> Result<Self> {
let () = self.call(&*mpris::player::PAUSE, conn).await?;
self.call(&*mpris::player::PAUSE, conn).await?;

Ok(Self {
status: PlaybackStatus::Paused,
Expand All @@ -177,7 +177,7 @@ impl Player {
}

async fn stop(self, conn: &SyncConnection) -> Result<Self> {
let () = self.call(&*mpris::player::STOP, conn).await?;
self.call(&*mpris::player::STOP, conn).await?;

Ok(Self {
status: PlaybackStatus::Stopped,
Expand All @@ -187,7 +187,7 @@ impl Player {
}

async fn play(self, conn: &SyncConnection) -> Result<Self> {
let () = self.call(&*mpris::player::PLAY, conn).await?;
self.call(&*mpris::player::PLAY, conn).await?;

Ok(Self {
status: PlaybackStatus::Playing,
Expand Down Expand Up @@ -436,10 +436,19 @@ impl Server {

let self_1 = ret.clone();
tokio::spawn(async move {
while let Some(()) = scan_rx.recv().await {
self_1.scan(true).await.unwrap();
'main: while let Some(()) = scan_rx.recv().await {
loop {
match select!(
opt = scan_rx.recv() => opt.map(|()| false),
() = tokio::time::sleep(Duration::from_millis(200)) => Some(true),
) {
Some(true) => break,
Some(false) => (),
None => break 'main,
}
}

tokio::time::sleep(Duration::from_millis(200)).await;
self_1.scan(true).await.unwrap();
}
});

Expand Down

0 comments on commit 2965720

Please sign in to comment.