配置为系统服务时程序不起作用

Program doesn't work when configured as a systemd service

我正在尝试编写一个可以 运行 作为 Linux 上的服务的程序。 主要是与服务器收发数据,接收服务器发送的.so文件,使用dlopen()执行里面的函数

当我通过sudo命令手动启用它时它起作用了。 我试图写一个 systemd 文件让它自动启动,但是那不起作用。例如使用Xlib的截屏功能得到一张全黑的图片。

我试过将调试信息重定向到文件中,但是只能得到主程序的调试信息;获取不到插件的运行信息。确定插件的功能已经执行,但是执行过程中出现错误

其程序的结果是截图内容全黑,客户端发送截图后自动断开连接。可能是客户端遇到segment fault,但是我手动启动程序的时候并没有出现这种情况

有什么想法吗?

这是我的服务文件。

[Unit]
Description=just for test

[Service]
Type=forking
ExecStart=/mnt/main.debug
Environment=DBUS_SESSION_BUS_ADDRESS,DISPLAY,WAYLAND_DISPLAY(new added)

[Install]
WantedBy=multi-user.target

我尝试通过systemctl start ps-hak.service启动服务,错误信息是这样的:

a@ubuntu:~$ sudo systemctl daemon-reload
a@ubuntu:~$ sudo systemctl start ps-hak.service
^[OAJob for ps-hak.service failed because a fatal signal was delivered to the control process. See "systemctl status ps-hak.service" and "journalctl -xe" for details.
a@ubuntu:~$ sudo systemctl start ps-hak.service^C
a@ubuntu:~$ systemctl status ps-hak.service
● ps-hak.service - just for test
   Loaded: loaded (/etc/systemd/system/ps-hak.service; disabled; vendor preset: 
   Active: failed (Result: signal) since Tue 2021-08-03 19:20:18 PDT; 18s ago
  Process: 2662 ExecStart=/mnt/main.debug (code=killed, signal=SEGV)

Aug 03 19:20:08 ubuntu systemd[1]: Starting just for test...
Aug 03 19:20:08 ubuntu main.debug[2662]: 
Aug 03 19:20:08 ubuntu main.debug[2662]: [DEBUG|main.cpp:40 (main)]: WAYLAND_DIS
Aug 03 19:20:08 ubuntu main.debug[2662]: DISPLAY=(null)
Aug 03 19:20:08 ubuntu main.debug[2662]: 
Aug 03 19:20:18 ubuntu systemd[1]: ps-hak.service: Control process exited, code=
Aug 03 19:20:18 ubuntu systemd[1]: Failed to start just for test.
Aug 03 19:20:18 ubuntu systemd[1]: ps-hak.service: Unit entered failed state.
Aug 03 19:20:18 ubuntu systemd[1]: ps-hak.service: Failed with result 'signal'.

我只能确定是程序的环境不对,我试过加Environment=DBUS_SESSION_BUS_ADDRESS,DISPLAY,WAYLAND_DISPLAY,但是没有意义

我做了一个调试SYSTEMd的小程序。代码如下。 当我手动启用它时,它可以正常生成屏幕截图,但是当我注册为服务服务后,它无法正常工作。指定目录下连文件生成都没有,但是systemctl status test里也没有报错信息。 代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdio>
#include <sys/time.h>
#include <X11/Xlib.h>

#pragma pack (1)
typedef struct BitMAPFILEHEADER 
{
    short    bfType;
    int    bfSize;
    short    bfReserved1;
    short    bfReserved2;
    int   bfOffBits;
} BITMAPFILEHEADER;

typedef struct BitMAPINFOHEADER
{
    int  biSize;
    int   biWidth;
    int   biHeight;
    short   biPlanes;
    short   biBitCount;
    int  biCompression;
    int  biSizeImage;
    int   biXPelsPerMeter;
    int   biYPelsPerMeter;
    int  biClrUsed;
    int  biClrImportant;
} BITMAPINFOHEADER;

void saveXImageToBitmap(const char* filename,XImage *pImage)
{
    BITMAPFILEHEADER bmpFileHeader;
    BITMAPINFOHEADER bmpInfoHeader;
    FILE *fp;
    memset(&bmpFileHeader, 0, sizeof(BITMAPFILEHEADER));
    memset(&bmpInfoHeader, 0, sizeof(BITMAPINFOHEADER));
    bmpFileHeader.bfType = 0x4D42;
    bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bmpFileHeader.bfReserved1 = 0;
    bmpFileHeader.bfReserved2 = 0;
    int biBitCount =32;
    int dwBmpSize = ((pImage->width * biBitCount + 31) / 32) * 4 * pImage->height;

    // DEBUG("size of short:%d\r\n",(int)sizeof(short));
    // DEBUG("size of int:%d\r\n",(int)sizeof(int));
    // DEBUG("size of long:%d\r\n",(int)sizeof(long));
    // DEBUG("dwBmpSize:%d\r\n",(int)dwBmpSize);
    // DEBUG("BITMAPFILEHEADER:%d\r\n",(int)sizeof(BITMAPFILEHEADER));
    // DEBUG("BITMAPINFOHEADER:%d\r\n",(int)sizeof(BITMAPINFOHEADER));
    bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +  dwBmpSize;

    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfoHeader.biWidth = pImage->width;
    bmpInfoHeader.biHeight = pImage->height;
    bmpInfoHeader.biHeight = - bmpInfoHeader.biHeight;  // important,otherwise the pic will be reversed
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = biBitCount;
    bmpInfoHeader.biSizeImage = 0;
    bmpInfoHeader.biCompression = 0;
    bmpInfoHeader.biXPelsPerMeter = 0;
    bmpInfoHeader.biYPelsPerMeter = 0;
    bmpInfoHeader.biClrUsed = 0;
    bmpInfoHeader.biClrImportant = 0;

    fp = fopen(filename,"wb");

    if(fp == NULL)
        return;

    fwrite(&bmpFileHeader, sizeof(bmpFileHeader), 1, fp);
    fwrite(&bmpInfoHeader, sizeof(bmpInfoHeader), 1, fp);
    fwrite(pImage->data, dwBmpSize, 1, fp);
    fclose(fp);
}

int CaptureDesktop(const char* filename)
{
    Window desktop;
    Display* dsp;
    XImage* img;

    int screen_width;
    int screen_height;
    dsp = XOpenDisplay(NULL);/* Connect to a local display */
    if(NULL==dsp)
    {
        // DEBUG("%s,%s\n","CaptureDesktop","Cannot connect to local display");
        return 0;
    }
    desktop = RootWindow(dsp,0);/* Refer to the root window */
    if(0==desktop)
    {
        // DEBUG("%s,%s\n","CaptureDesktop","cannot get root window");
        return 0;
    }

    /* Retrive the width and the height of the screen */
    screen_width = DisplayWidth(dsp,0);
    screen_height = DisplayHeight(dsp,0);
    // DEBUG("%d %d\n",screen_width,screen_height);

    img = XGetImage(dsp,desktop,0,0,screen_width,screen_height,~0,ZPixmap);

    saveXImageToBitmap(filename,img);
   
    XCloseDisplay(dsp);
    return 1;
}

int main()
{
    CaptureDesktop("/home/a/out.bmp");
    return 0;
}

操作过程:

a@ubuntu:~$ sudo systemctl daemon-reload
a@ubuntu:~$ sudo systemctl enable test
a@ubuntu:~$ sudo systemctl start test
a@ubuntu:~$ sudo systemctl status test
● test.service - just for test
   Loaded: loaded (/etc/systemd/system/test.service; enabled; vendor preset: ena
   Active: inactive (dead) since Tue 2021-08-03 20:17:57 PDT; 5s ago
  Process: 2501 ExecStart=/mnt/test (code=exited, status=0/SUCCESS)

Aug 03 20:17:57 ubuntu systemd[1]: Starting just for test...
Aug 03 20:17:57 ubuntu systemd[1]: Started just for test.
lines 1-7/7 (END)

我可以确认是systemd启动的服务的环境变量有问题,但是小程序没有环境变量应该可以正常工作,但是作为一个服务,却不能正常工作。 我认为主要问题可能不是截图功能,因为其他功能也有类似的结果。

您不能从那种服务调用 Xlib,因为它没有附加到用户会话。唯一可以合理使用 Xlib 的服务是用户登录服务。

如果检查环境变量,DISPLAY 和 XAUTHORITY 将为空白。这是直接失败的原因。用“正确的”值填充它们将允许它工作。如果您设法找到它们,setenv(3) 将设置它们并且 Xlib 将拾取它们。您可以以 root 身份玩把戏,并尝试追踪当前 X 会话的内容,但这是一个矢量-标量问题。我 运行 多个 X 会话。哪个是对的?

一般来说,我什么都不知道(比如建议使用缺少的 X 服务器的答案),我尝试的一种方法是直接 运行 宁你的 /mnt/main.debug 运行 它与 strace: strace -f -o /tmp/main.debug.strace.log /mnt/main.debug-o 将指定输出文件,如果 systemd 以方便的方式收集输出,则可能不需要。如果您 fork 并希望从子进程中收集信息,则 -f 选项很有用。通常 strace 的输出会给出非常有用的指示,表明程序在何处失败。您可以看到诸如失败的系统调用、读取的文件内容和所有试图读取的文件,以及程序如何与您背后的系统交互的所有事情。您可以将输出与在 systemd 之外 strace 下 运行ning 您的程序的输出进行比较,看看它成功地做了哪些在 systemd 服务上下文中不起作用的事情。

要尝试的另一件事是启用调试符号和核心转储。请参阅您的 Linux 发行版(或 systemd)文档,了解如何在系统中启用它们或参阅 proc(5) and core(5) 手册页。然后,您可以使用 gdb 来调查核心文件并查看堆栈跟踪(如果应用程序是多线程的,则为跟踪)。很可能您的程序中有崩溃的确切行。或者它可能是错误的指针,因为由于早期的故障,某些东西没有正确初始化。它将有助于减少优化标志(例如 -O3 更改为 -O1 或什至更友好的调试)以获得更好的堆栈跟踪(但请确认程序在使用更安全的编译器选项构建后仍然崩溃).