I made a web app to turn off my computer’s screens, there are a few different technologies but it’s fairly simple:
I have a html/js frontend that detects a button click (Screens On / Screens Off) which sends the option to the PHP backend via ajax
The php then connects over a tcp port, sending the option to a program written in golang
Then my golang program executes the command to turn off/on the screens. The command it runs is (“xset -display :0 dpms force off”)
The problem I’m having is that the command only works when im running the golang program in the terminal, but when i set it up as a service the command wont work.
This is the golang code:
package main
import (
"os/exec"
"net"
"fmt"
"bufio"
)
func main() {
fmt.Println("Launching server")
ln, _ := net.Listen("tcp", ":7777")
fmt.Println("Listening...n")
for {
// accept connection on port
conn, _ := ln.Accept()
fmt.Println("New connection")
// listen for message ending in n
message, _ := bufio.NewReader(conn).ReadString('n')
rec := string(message)
// remove trailing n
rec = rec[:len(rec)-1]
fmt.Println("Message Received: ", """+rec+""")
returnMessage := "fail"
if (rec == "screensOff") {
fmt.Println("Turning off screens...")
//execute screens off command
cmd := exec.Command("xset", "-display", ":0", "dpms", "force", "off")
stdout, err := cmd.Output()
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(string(stdout))
returnMessage = "done"
}
} else if (rec == "screensOn") {
fmt.Println("Turning on screens...");
//execute screens on command
cmd := exec.Command("xset", "-display", ":0", "dpms", "force", "on")
stdout, err := cmd.Output()
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(string(stdout))
returnMessage = "done"
}
returnMessage = "done"
}
conn.Write([]byte(returnMessage + "n"))
conn.Close()
fmt.Println("Connection closedn")
}
}
And relevant PHP code:
<?php
function sendServiceMessage($message) {
$host = "localhost";
$port = 7777;
$timeout = 30;
// connect to service
$socket = fsockopen($host, $port, $errnum, $errstr, $timeout);
if (!is_resource($socket)) {
exit("connection fail: ".$errnum." ".$errstr);
}
else {
// send message
fputs($socket, $message."n");
// receive return message
$recieved = "";
while (!feof($socket)) {
$recieved .= fgets ($socket, 1024);
}
}
// close connection
fclose($socket);
if ($recieved == "done") {
return true;
}
return false;
}
sendServiceMessage("screensOff");
I used systemd to set up the service, so after building the program and placing it in /usr/bin/
$ go build screenControl.go
$ sudo cp screenControl /usr/bin/screenControl
I can run the screenControl program in the terminal, and select “screens off” in the web app and it all works as expected:
$ screenControl
Launching server
Listening
New Connection
Message Received: "screensOff"
Turning off screens
Connection closed
I then created a systemd unit file (/etc/systemd/system/screenControl.service):
[Unit]
Description=Screen control service
[Service]
ExecStart=/usr/bin/screenControl
Restart=on-abort
[Install]
WantedBy=multi-user.target
I started the service and checked it:
$ systemctl start screenControl
$ systemctl status screenControl
● screenControl.service - Screen control service
Loaded: loaded (/etc/systemd/system/screenControl.service; disabled; vendor preset: enabled)
Active: active (running) since Sun 2015-12-13 22:31:54 GMT; 6s ago
Main PID: 19871 (screenControl)
CGroup: /system.slice/screenControl.service
└─19871 /usr/bin/screenControl
Dec 13 22:31:54 User systemd[1]: Started Screen control service.
Dec 13 22:31:54 User screenControl[19871]: Launching server
Dec 13 22:31:54 User screenControl[19871]: Listening
So it’s running, but then when I select screens off in the web app now, nothing happens… I checked the service status again and it is receiving the message to turn the screens off but the command is exiting with an error:
Dec 13 22:31:54 User screenControlTest[19871]: Launching server
Dec 13 22:31:54 User screenControlTest[19871]: Listening
Dec 13 22:32:25 User screenControlTest[19871]: New connection
Dec 13 22:32:25 User screenControlTest[19871]: Message Received: "screensOff"
Dec 13 22:32:25 User screenControlTest[19871]: Turning off screens
Dec 13 22:32:25 User screenControlTest[19871]: exit status 1
Dec 13 22:32:25 User screenControlTest[19871]: Connection closed
What’s the problem here and how can I get that command to work as a service? Once this is working I want to have the service start automatically when the machine turns on, although with systemd I think that’s as simple as:
$ systemctl enable screenControl
Any help would be great, thank you 🙂
Edit
After having the golang program show me the stderr of the xset command, I now also have the error message:
xset: unable to open display ""
Advertisement
Answer
As per David Budworth’s comment, the fix was exceedingly simple; as the service was running under root, it didn’t have the DISPLAY environment variable set.
In go you can set the environment variables when using exec like so:
//execute screens off command
cmd := exec.Command("xset", "-display", ":0", "dpms", "force", "off")
cmd.Env = []string{"DISPLAY=:0"} // set the display before executing
stdout, stderr := cmd.CombinedOutput() //execute and return all output
And from James Henstridge answer I found I also needed to run xhost +SI:localuser:root
to allow the root user access to the X server.
You can do this for users after they’ve logged in by adding this line to the top of the /etc/profile
file
xhost +SI:localuser:root > /dev/null 2>&1
OR
You can get it to work even when no user is logged in (when the login screen is showing)
First I created the directory /opt/scripts
then created the file /opt/scripts/xhost.sh
and gave it executable permissions with chmod +x /opt/scripts/xhost.sh
In this file is just the one line:
xhost +SI:localuser:root > /dev/null 2>&1
Then edit the file /etc/lightdm/lightdm.conf
(I had to create it, but edit it if it’s there) and add the line
display-setup-script=/opt/scripts/xhost.sh
So my lightdm.conf
file looks like this:
[SeatDefaults]
greeter-session=unity-greeter
user-session=ubuntu
display-setup-script=/opt/scripts/xhost.sh
This tells LightDM (the display manager running in Ubuntu) to run the script /opt/scripts/xhost.sh
after the X server is started but before anything else, therefore root is given the xhost authorization straightaway!
note:
display-setup-script is run after the X server starts but before the user session / greeter is run. Set this if you need to configure anything special in the X server. It is run as root. If this command returns an error code the X server is stopped. Source: https://wiki.ubuntu.com/LightDM