dqmdlo9674 2015-12-13 23:00 采纳率: 100%
浏览 552
已采纳

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

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题