You are not logged in.

#1 2016-07-18 08:36:22

l1nuxfr3@k
Member
From: Novi Sad, Serbia
Registered: 2016-06-12
Posts: 20

[SOLVED] Running a GUI app through systemd service..?

Hello everyone! I've been customizing my Arch installation with some cool and crazy systemd services for a while now and it rocks. But I have a problem with my rhythmbox-client package. I am not saying that there is a bug in the package, it is probably just my misunderstanding of how terminal emulators, bash and ttys work "under the hood", so I would like some help with that. smile

I have created a "daemon" program in C, which is being run by systemd on system boot.
The daemon app acts like a real daemon, having it's own lock file; it responds to signals so killall can terminate it.
It runs in the background with it's own process group, forwards IO operations to /dev/null and uses multi-threading to handle simultaneous TCP connections properly.

The idea was having sort of "Home Control System" where I live, so I could use my Android to shutdown the PC and control Rhythmbox with my own Android app (which works! But not the way I want it to!)

Let's just say that.. when I run the daemon like this:

sudo tcpd

that works! Also, when I su into my root shell, I get the desired effects.
But when running from systemd, I cannot control the player nor execute ANY system("") commands. Pipe fails as well.
I sort of understand why is it happening.

sudo journalctl -u tcpd

shows some random stuff but the main error is "Unknown TTY".
The app works like this: it detects a command from my Android app (note that the user HAS to login to the app first, with the same username and password from the Arch machine), then it calls an external bash script "execute". It does this with root privileges, but the script then calls other scripts using

sudo -u $user -H sh -c \"$command\"

The idea was to login as some user to a systemd service and than have the service execute some commands as the user logged in! Can you help me do it?

Last edited by l1nuxfr3@k (2017-04-30 09:00:08)


Proud Arch user, programmer & guitarist.

Offline

#2 2016-07-18 15:29:41

berbae
Member
From: France
Registered: 2007-02-12
Posts: 1,302

Re: [SOLVED] Running a GUI app through systemd service..?

l1nuxfr3@k wrote:

But when running from systemd, I cannot control the player nor execute ANY system("") commands. Pipe fails as well.
I sort of understand why is it happening.

sudo journalctl -u tcpd

shows some random stuff but the main error is "Unknown TTY".

Post the service file used to start your daemon from systemd.
Post also the parts of the journal where your service appears.

The idea was to login as some user to a systemd service and than have the service execute some commands as the user logged in! Can you help me do it?

You log in to a machine system not to a systemd service: can you clarify?

Did you consider the 'User=, Group=' options? (see man systemd.exec)

Offline

#3 2016-07-23 17:21:43

l1nuxfr3@k
Member
From: Novi Sad, Serbia
Registered: 2016-06-12
Posts: 20

Re: [SOLVED] Running a GUI app through systemd service..?

Thanks for the reply!
The service file is here:

[Unit]
Description=TCPD Deamon
After=network.target
Wants=network.target

[Service]
ExecStart=/usr/bin/tcpd
ExecStop=/usr/bin/tcpk

[Install]
WantedBy=multi-user.target

- tcpk kills the service, tcpd starts it.

You log in to a machine system not to a systemd service: can you clarify?

Hmm, yes, I was quite confusing.. Sorry about that. The idea was to run a TCP server on the Linux machine. I had written the code for the server, then created an executable from it and symlink-ed it to /usr/tcpd   ..

The user (using a client app) gets the "WELCOME" message and then is asked to login with the username and password from one of the users available at the server machine (Linux users).. The server itself (written in pure C), uses pipes and system() calls to check the login information. How? It runs a specific script that checks the /etc/shadow and computes a new SHA512 hash using salt + password. After validation, it then either bans the user or gives "the green light". The connection between user and client is encrypted. After the user has been successfully confirmed, it is then available to control "a virtual shell". Basically, it just sends commands that are then processed and some scripts are called as the result (pipes) .. Sometimes the result gets forwarded to the user.. For example:

ls -a

can return the output to the user. The client app targets Android users and is written in Java smile

Did you consider the 'User=, Group=' options? (see man systemd.exec)

No, but will try smile

I hope I was more specific now.
Greetings.

Last edited by l1nuxfr3@k (2016-07-23 17:23:35)


Proud Arch user, programmer & guitarist.

Offline

#4 2016-07-24 09:24:02

berbae
Member
From: France
Registered: 2007-02-12
Posts: 1,302

Re: [SOLVED] Running a GUI app through systemd service..?

l1nuxfr3@k wrote:

After the user has been successfully confirmed, it is then available to control "a virtual shell". Basically, it just sends commands that are then processed and some scripts are called as the result (pipes) .. Sometimes the result gets forwarded to the user.. For example:

ls -a

can return the output to the user.

I don't see where there is a GUI app implied: are the commands graphical apps? The example you give doesn't suggest that.

And you didn't post the output of 'journalctl -u tcpd' or 'systemctl status tcpd.service' to show the exact messages generated while using the service.
Probably you need some 'Environment=' or 'StandardInput=', 'StandardOutput=', 'TTYPath=' parameters to be set: see 'man systemd.exec'.

Offline

#5 2016-07-27 08:36:47

l1nuxfr3@k
Member
From: Novi Sad, Serbia
Registered: 2016-06-12
Posts: 20

Re: [SOLVED] Running a GUI app through systemd service..?

ls -a

was just an example. The goal was to use both GUI and command line apps. Just to compare, I will use this example: SSH!
When you SSH into a target Linux machine, you can execute commands. If you

export DISPLAY=:0

you can then literally display X11 Windows on the screen! Well, that is what I meant by "GUI". The idea is to "act as selected user" and execute commands as this user, under their own environment (which includes X11 windows - GUI). 

So if I wanted

gnome-terminal

, that should work. Also,

rhythmbox-client

has to reach it's own running "server" (rhythmbox player) and change the music. This is what failed.

berbae wrote:

Probably you need some 'Environment=' or 'StandardInput=', 'StandardOutput=', 'TTYPath=' parameters to be set: see 'man systemd.exec'.

Thanks for your help! I've looked into it and saw the entry explaining the environment variables and it helped me solving this problem. But, not in this way. I do not use my service file to set the variables because different users (with different privileges) have to login to the machine using my own app. The daemon TCP server (which starts on boot using systemd) we talked about checks whether the entered information is correct or not and then, if login succeeded, tries to "act as the target user". I've put a lot of effort in this, and it worked in the end (but hey, there are new problems now big_smile).
So what I basically did was: first, I created a script that checks for the environment data of a specific user (that has to be logged in first); then I receive the output of the script; finally I change the environment variables of my own app, and change current uid with setuid();

strings=$(loginctl list-sessions | grep $1 | tail -n1)
items=($strings)

if [ "${#items[@]}" = "4" ]; then
        echo ${items[0]}
        echo ${items[1]}
        echo ${items[2]}
        echo ${items[3]}
        fgconsole
else
        echo
fi

This script has to be run as root.
Yes, I know having external scripts is a bad idea. big_smile

This is a structure used in the code below:

typedef struct user_info {
	char *sessionID;
	char *UID;
	char *userName;
	char *seatName;
    char *virtualTermNum;
} userInfo;

This is the code that receives the data

userInfo *getEnvData(char *user) {
        userInfo *result = malloc(sizeof(userInfo));
	char msg[100];
	sprintf(&msg, "Getting environment data for %s ...", user);
	log_message(LOG_FILE, &msg);
	FILE *fp;
	char command[30], path[1035];
	sprintf(&command, "%s/getEnv %s\0", SCRIPTS_DIR, user);
  	char tmpUsername[10], tmpUID[10], tmpSeat[10], tmpSessionID[10], tmpVirtTermNum[10];
  	/* Open the command for reading. */
  	fp = popen(&command, "r");
  	if (fp == NULL) {
		return NULL;
  	}
    fgets(tmpSessionID, sizeof(path), fp);
    if (strcmp(tmpSessionID, "\n") == 0) return NULL;
    fgets(tmpUID, sizeof(path), fp);
    fgets(tmpUsername, sizeof(path), fp);
    fgets(tmpSeat, sizeof(path), fp);
    fgets(tmpVirtTermNum, sizeof(path), fp);
    tmpSessionID[strlen(tmpSessionID)-1] = '\0';
    tmpUID[strlen(tmpUID)-1] = '\0';
    tmpUsername[strlen(tmpUsername)-1] = '\0';
    tmpSeat[strlen(tmpSeat)-1] = '\0';
    tmpVirtTermNum[strlen(tmpVirtTermNum)-1] = '\0';
    result->seatName = strdup(tmpSeat);
    result->sessionID = strdup(tmpSessionID);
    result->UID = strdup(tmpUID);
    result->userName = strdup(tmpUsername);
    result->virtualTermNum = strdup(tmpVirtTermNum);
  	/* close */
  	pclose(fp);
	log_message(LOG_FILE, "Environment data received.");
	return result;
}

This is how you act as a specific user:

void actAs(userInfo *mUserInfo) {
        char homeDIR[50], runtimeDIR[50];
        sprintf(&homeDIR, "/home/%s\0", mUserInfo->userName);
        sprintf(&runtimeDIR, "/run/user/%s\0", mUserInfo->UID);
        setenv("USER", mUserInfo->userName, 1);
        setenv("LOGNAME", mUserInfo->userName, 1);
        setenv("HOME", &homeDIR, 1);
        setenv("SHELL", "/bin/bash", 1);
        setenv("XDG_SESSION_ID", mUserInfo->sessionID, 1);
        setenv("XDG_SEAT", mUserInfo->seatName, 1);
        setenv("XDG_VTNR", mUserInfo->virtualTermNum, 1);
        setenv("XDG_RUNTIME_DIR", &runtimeDIR, 1);
        setuid(atoi(mUserInfo->UID));
}

The problem with this is, once you switch to a standard user, the whole app becomes powerless.
You can not execute commands as root (and the daemon has to do this from time to time).

Thanks for your replies. I know that sometimes I sound confusing. English is not my native language smile
I hope that I have explained everything well, now.

berbae wrote:

And you didn't post the output of 'journalctl -u tcpd' or 'systemctl status tcpd.service' to show the exact messages generated while using the service.

There wasn't anything at all at that point except "TTY Unknown" which I stated during one of the posts.
Here it is.. I've looked back into the journal...

 root : TTY=unknown ; PWD=/usr/share/tcpd ; USER=twister ; COMMAND=scripts/play
Jul 16 16:11:05 linuxbox sudo[10018]: pam_unix(sudo:session): session opened for user twister by (uid=0)
Jul 16 16:11:05 linuxbox sudo[10018]: pam_unix(sudo:session): session closed for user twister

Proud Arch user, programmer & guitarist.

Offline

#6 2016-07-27 15:31:06

berbae
Member
From: France
Registered: 2007-02-12
Posts: 1,302

Re: [SOLVED] Running a GUI app through systemd service..?

l1nuxfr3@k wrote:

So what I basically did was: first, I created a script that checks for the environment data of a specific user (that has to be logged in first); then I receive the output of the script; finally I change the environment variables of my own app, and change current uid with setuid();

and

The problem with this is, once you switch to a standard user, the whole app becomes powerless.
You can not execute commands as root

Instead of changing the environment of your app, couldn't you call a new instance or fork with the new environment, while keeping root privileges in the main app?
I don't know if this is applicable to your case though; it's just a suggestion.

Offline

Board footer

Powered by FluxBB