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