为了账号安全,请及时绑定邮箱和手机立即绑定

Linux display 命令在终端中有效,但在 systemd 服务中无效

Linux display 命令在终端中有效,但在 systemd 服务中无效

Go
桃花长相依 2021-11-29 16:08:06
我制作了一个网络应用程序来关闭我的电脑屏幕,有几种不同的技术,但它相当简单:我有一个 html/js 前端,它检测按钮点击(屏幕开启/屏幕关闭),它通过 ajax 将选项发送到 PHP 后端然后 php 通过 tcp 端口连接,将选项发送到用 golang 编写的程序然后我的 golang 程序执行关闭/打开屏幕的命令。它运行的命令是 ("xset -display :0 dpms force off")我遇到的问题是该命令仅在我在终端中运行 golang 程序时才有效,但是当我将其设置为服务时,该命令将不起作用。这是golang代码:package mainimport (    "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")    }}
查看完整描述

2 回答

?
紫衣仙女

TA贡献1839条经验 获得超15个赞

xset命令只是 X 服务器的客户端。它通过检查DISPLAY环境变量来确定要与哪个 X 服务器通信,当您将命令作为系统服务运行时不会设置该变量。

即使您DISPLAY在运行守护程序时确保已设置,它也可能以不同的用户帐户运行,并且默认情况下拒绝访问显示。

更好的选择是将您的守护程序作为用户会话的一部分运行。这将解决身份验证问题(它将像您一样运行)和定位显示的能力(环境变量应该是可见的)。当您未登录时,守护程序将不会运行,但这对于此特定用例可能无关紧要。

您已将问题标记为“Ubuntu”,其中会话仍由Upstart管理。您可以通过在~/.config/upstart. 文件格式的详细信息可以在init(5)手册页中找到。


查看完整回答
反对 回复 2021-11-29
?
holdtom

TA贡献1805条经验 获得超10个赞

根据 David Budworth 的评论,修复方法非常简单;由于服务在 root 下运行,它没有设置 DISPLAY 环境变量。


在 go 中,您可以在使用 exec 时设置环境变量,如下所示:


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

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

user-session=ubuntu

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


查看完整回答
反对 回复 2021-11-29
  • 2 回答
  • 0 关注
  • 202 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信