dqmdlo9674
dqmdlo9674
采纳率100%
2015-12-13 23:00 阅读 408

Linux display命令在终端中起作用,但在systemd服务中不起作用

我制作了一个网络应用程序来关闭计算机的屏幕,虽然有几种不同的技术,但是非常简单: 我有一个html / js前端,它检测按钮单击(屏幕打开/屏幕关闭),该按钮通过ajax将选项发送到PHP后端 然后,PHP通过TCP端口连接,将选项发送到用golang编写的程序 然后,我的golang程序执行命令以关闭/打开屏幕。 它运行的命令是(“ xset -display:0 dpms force off”) 我遇到的问题是该命令仅在在终端中运行golang程序时才有效,但是当我将其设置为服务时,该命令将不起作用。 这是golang代码:
package main

import (
    "os/exec"
    "net"
    "fmt"
    "bufio"
)

func main() {
    fmt.Println("Launching server")

    ln, _ := net.Listen("tcp", ":7777")
    fmt.Println("Listening...
")

    for {
        // accept connection on port
        conn, _ := ln.Accept()
        fmt.Println("New connection")

        // listen for message ending in 

        message, _ := bufio.NewReader(conn).ReadString('
')
        rec := string(message)

        // remove trailing 

        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 + "
"))

        conn.Close()
        fmt.Println("Connection closed
")
    }
}

相关的PHP代码:

<?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."
");

        // receive return message
        $recieved = "";
        while (!feof($socket)) {
            $recieved .= fgets ($socket, 1024);
        }
    }

    // close connection
    fclose($socket);
    if ($recieved == "done") {
        return true;
    }
    return false;   
}

sendServiceMessage("screensOff");

我使用systemd来设置服务,因此在构建程序并将其放在/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

然后,我创建了一个systemd单位文件(/etc/systemd/system/screenControl.service):

[Unit]
Description=Screen control service

[Service]
ExecStart=/usr/bin/screenControl
Restart=on-abort

[Install]
WantedBy=multi-user.target

于是我启动服务并检查了一下它:

...$ 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...

因此它正在运行,但是当我现在在Web应用程序中选择关闭屏幕时,什么也没发生...我再次检查了服务状态,它收到了关闭屏幕的消息,但是命令退出并出现错误:

...
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

这是什么问题,如何使该命令作为服务工作? 一旦运行正常,我想在机器开机时自动启动服务,尽管使用systemd我认为这很简单:

...$ systemctl enable screenControl
任何帮助将是伟大的,谢谢:) 编辑: 在让golang程序显示xset命令的stderr之后,我现在也有了错误消息:
xset:  unable to open display ""
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

2条回答 默认 最新

  • 已采纳
    doubailian4459 doubailian4459 2015-12-14 04:13

    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

    点赞 评论 复制链接分享
  • drzk21632 drzk21632 2015-12-14 02:47

    The xset command is just a client for the X server. It determines which X server to talk to by checking the DISPLAY environment variable, which won't be set when you run your command as a system service.

    And even if you ensured DISPLAY was set when running your daemon, it would likely be run as a different user account and denied access to the display by default.

    A better option would be to run the your daemon as part of the user session. This will solve the authentication issues (it will be running as you), and the ability to locate the display (the environment variable should be visible). The daemon won't be running when you aren't logged in, but that probably doesn't matter for this particular use case.

    You've tagged your question with "Ubuntu", where the session is still managed by Upstart. You can create new user session jobs by creating a file in ~/.config/upstart. The details of the file format can be found in the init(5) man page.

    点赞 评论 复制链接分享

相关推荐