猿问

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...\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 closed\n")

    }

}


桃花长相依
浏览 203回答 2
2回答

紫衣仙女

该xset命令只是 X 服务器的客户端。它通过检查DISPLAY环境变量来确定要与哪个 X 服务器通信,当您将命令作为系统服务运行时不会设置该变量。即使您DISPLAY在运行守护程序时确保已设置,它也可能以不同的用户帐户运行,并且默认情况下拒绝访问显示。更好的选择是将您的守护程序作为用户会话的一部分运行。这将解决身份验证问题(它将像您一样运行)和定位显示的能力(环境变量应该是可见的)。当您未登录时,守护程序将不会运行,但这对于此特定用例可能无关紧要。您已将问题标记为“Ubuntu”,其中会话仍由Upstart管理。您可以通过在~/.config/upstart. 文件格式的详细信息可以在init(5)手册页中找到。

holdtom

根据 David Budworth 的评论,修复方法非常简单;由于服务在 root 下运行,它没有设置 DISPLAY 环境变量。在 go 中,您可以在使用 exec 时设置环境变量,如下所示://execute screens off commandcmd := exec.Command("xset", "-display", ":0", "dpms", "force", "off")cmd.Env = []string{"DISPLAY=:0"} // set the display before executingstdout, stderr := cmd.CombinedOutput() //execute and return all output从 James Henstridge 的回答中我发现我还需要运行xhost +SI:localuser:root以允许 root 用户访问 X 服务器。您可以在用户登录后通过将此行添加到/etc/profile文件顶部来为他们执行此操作xhost +SI:localuser:root > /dev/null 2>&1或者即使没有用户登录(显示登录屏幕时),您也可以使用它首先我创建了目录/opt/scripts 然后创建了文件/opt/scripts/xhost.sh 并赋予它可执行权限chmod +x /opt/scripts/xhost.sh在这个文件中只有一行:xhost +SI:localuser:root > /dev/null 2>&1然后编辑文件/etc/lightdm/lightdm.conf(我必须创建它,但如果它在那里就编辑它)并添加行 display-setup-script=/opt/scripts/xhost.sh所以我的lightdm.conf文件看起来像这样:[SeatDefaults]greeter-session=unity-greeteruser-session=ubuntudisplay-setup-script=/opt/scripts/xhost.sh这告诉 LightDM(在 Ubuntu 中运行的显示管理器)/opt/scripts/xhost.sh在 X 服务器启动之后但在其他任何事情之前运行脚本,因此 root 立即获得 xhost 授权!笔记:display-setup-script 在 X 服务器启动之后但在用户会话/欢迎程序运行之前运行。如果您需要在 X 服务器中配置任何特殊内容,请设置此项。它以 root 身份运行。如果此命令返回错误代码,则 X 服务器将停止。 来源:https : //wiki.ubuntu.com/LightDM
随时随地看视频慕课网APP

相关分类

Go
我要回答