dongzanxun2790
2018-10-24 08:15
浏览 124
已采纳

如何检测当前的Go进程是否在无头(非GUI)环境中运行?

I have a Go program which wants to install a trayicon. In case the process is headless, that is, it will not be able to create a graphical user interface, the Go program still makes sense and shall run, but obviously it shall not install the trayicon.

What is the way in Go how to detect whether the current Go process is headless?

Currently, I use the following code:

func isHeadless() bool {
    _, display := os.LookupEnv("DISPLAY")
    return !(runtime.GOOS == "windows" || display)
}

This code works just fine on a "normal" Windows, Linux, or Mac OS X, and I bet it will also run just fine on FreeBSD, NetBSD, Dragonfly and many others.

Still, that code obviously has a lot of problems:

  • It assumes that Windows is never headless (wrong, what if the process was started without a user logged in, and also, there's Windows 10 IoT Core which can be configured to headless https://docs.microsoft.com/en-us/windows/iot-core/learn-about-hardware/headlessmode)
  • It doesn't support Android (of which there also is a headless version for IoT).
  • It assumes that everything non-Windows has an X-Server and thus a DISPLAY environment variable (wrong, for example, Android)

So, what is the correct way in Go to detect whether the current process is headless / running in a headless environment?

I'm not looking for workarounds, like adding a --headless command line switch to my program. Because, I already have that anyway for users who have heads but want the program to behave as if it were headless.

In some other programming environments, such capabilities exist. For example, Java has java.awt.GraphicsEnvironment.isHeadless(), and I'm looking for a similar capability in Go.

Some people have suggested to simply try creating the UI, and catch the error. This does not work, at least not with the library that I use. I use github.com/getlantern/systray. When systray.Run() cannot create the UI, the process dies. My code to setup the system tray looks like this:

func setupSystray() { // called from main()
    go func() {
        systray.Run(onReady, nil)
    }()
}

func onReady() {
    systray.SetTitle("foo")
    // ...
}

When I run this code on Linux with DISPLAY unset, the output is as following:

$ ./myapp-linux-amd64
Unable to init server: Could not connect: Connection refused

(myapp-linux-amd64:5783): Gtk-WARNING **: 19:42:37.914: cannot open display: 
$ echo $?
1

It could be argued that this is a flaw in the library (and I have created a ticket on the library https://github.com/getlantern/systray/issues/71), but nonetheless some other APIs and environments provide a function isHeadless(), and I'm looking for an equivalent in Golang.

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

3条回答 默认 最新

  • douhuan1497 2018-10-25 10:42
    已采纳

    In the perceived absence of a library/solution for this, I've created one myself. https://github.com/christianhujer/isheadless

    Example Usage:

    package main
    
    import (
        . "fmt"
        . "github.com/christianhujer/isheadless"
        . "os"
    )
    
    func main() {
        headless := IsHeadless()
        Fprintf(Stderr, "%s: info: headless: %v
    ", Args[0], headless)
        Exit(map[bool]int{true: 0, false: 1}[headless])
    }
    

    Example runs:

    $ ./isheadless ; echo $?
    ./isheadless: info: headless: false
    1
    $ DISPLAY= ./isheadless ; echo $?
    ./isheadless: info: headless: true
    0
    
    已采纳该答案
    打赏 评论
  • dongzi1959 2018-10-24 10:24

    I think you might be attacking this problem from a wrong angle.

    Detecting reliably that your program really sees a headless machine is, IMO, rather futile for a number of reasons.

    Hence I think I'd adopt an approach usually sported in, say, working with filesystems:

    1. Try to perform an operation.
    2. If it fails, collect the error.
    3. Analyze the error and act accordingly.

    That is, just try to explicitly initialize the client (yours) side of whatever works with the GUI stack in your code, trap any possible error and analyze it. If it says it failed to initialize the subsystem, then just raise a relevant flag and proceed.

    打赏 评论
  • doudui2229 2018-10-25 11:04

    Well, the answer to the question precisely as it was stated is to just look at what Java does in its isHeadless().

    Here is what OpenJDK 10 does.

    I cannot copy the code as it would supposedly breach its license, but in essense, the breakdown is as follows:

    1. Get system property "java.awt.headless"; use it, if found.
    2. Get system property "javaplugin.version"; if it exists, the session is not headless. Use this value.
    3. Get system property "os.name". If it literally contains the substring "OS X" and the system property "awt.toolkit" equals the string "sun.awt.HToolkit", the session is not headless. Use this value.
    4. Check whether the system property "os.name" equals one of "Linux", "SunOS", "FreeBSD", "NetBSD", "OpenBSD" or "AIX", and if so, try to find an environment variable "DISPLAY"; if it's absent, the session is headless.

    As you can see, in reality the check is pretty lame and I fail to see any special treatment of Windows.

    Still, this answers your question precisely.

    打赏 评论

相关推荐 更多相似问题