首页 > 解决方案 > 程序在配置为 systemd 服务时不起作用

问题描述

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

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

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

其程序的结果是截图内容全黑,客户端发送截图后自动断开连接。可能是客户端遇到了段错误,但是我手动启动程序时没有发生这种情况

有任何想法吗?

这是我的服务文件。

[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启动的服务的环境变量有问题,但是小程序在没有环境变量的情况下应该可以正常工作,但是作为服务却不能正常工作。我认为主要问题可能不是截图功能,因为其他功能也有类似的结果。

标签: clinuxsystemdxlib

解决方案


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

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


推荐阅读