Suspend system from C user application

Hello,

I want to bring the system to suspend from a C user application.

I’m successful in this way, that I defined this policy in polkit

polkit.addRule(function(action, subject) {
	if (subject.isInGroup("user") &&
		((action.id == "org.freedesktop.login1.suspend") ||
		 (action.id == "org.freedesktop.login1.suspend-multiple-sessions") ||
		 (action.id == "org.freedesktop.login1.reboot") ||
		 (action.id == "org.freedesktop.login1.reboot-multiple-sessions")
		 (action.id == "org.freedesktop.login1.power-off") ||
		 (action.id == "org.freedesktop.login1.power-off-multiple-sessions")))
	{
		return polkit.Result.YES;
	}
});

in /etc/polkit-1/rules.d/00-power.rules. Therefore the unprivileged ‘user’ can call

$ systemctl suspend

to suspend the system.

The second step was to use the system sd-bus API in my C application:

sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message* m = nullptr;
sd_bus* bus = nullptr;

if (sd_bus_open_system(&bus) < 0)
{
	perror("failed to connect to system bus");
}
else if (sd_bus_call_method(
							bus,
							"org.freedesktop.login1",
							"/org/freedesktop/login1",
							"org.freedesktop.login1.Manager",	
							"Suspend",
							&error,
							&m,
							"b",
							"true"
						) < 0)
{
	perror("failed to issue method call: %s", error.message);
}
else
{
	// wait, as long as systemd-suspend.servie is running
	// but how?
}

sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_unref(bus);

So far so good.

My problem is the last ‘else’. Since the systemd-suspend.service is called asynchronous, I have to wait as long as the service is running. But I don’t know how to get this information from the sd-bus.

On the command line I can call

$ systemctl is-active systemd-suspend.service

but in the sd-bus I didn’t find any object-path, interface or method to get this information.

Can somebody give me a hint? Or is there a better, easier way to achieve my goal?

Best regards,
Markus

Hello @Mowlwurf,
Every service in systemd has a set of properties that should be available through DBUS. You can find a list here.
You could try to read the “ActiveState” property of the systemd-suspend service to see if it’s running.

Another simpler thing that you could try (especially if you’re bringing in the DBUS dependency just for this call) is to call something like this:

system("systemctl suspend");

This call will be done from a shell that the system() call creates, and as far as I know this will block until the suspend is done. I did not check this myself, so I’m not sure if it would work.

Best regards,
Rafael Beims

Thank you for your answer.

system("systemctl suspend")

would work but since the suspend service is called asynchronly I still have to check its state.

Reading the “ActiveState” property was a good hint, however I can’t find the unit for the suspend in the DBUS. I tried

$ busctl tree org.freedesktop.systemd1

but in the output I only find the unit for shutdown. The unit for ‘reboot’ is also missing.
Am I looking at the wrong place?

Best regards,
Markus

Hi @Mowlwurf ,

Can you clarify the use-case that you’re trying to do?

As we understand the goal is to suspend the application and then monitor if your application really went to suspend mode. Then you are trying to catch when the application is waking up again. Is that right?

Could you share more information about your goals, so that maybe we could suggest another solution to achieve the desired functionality?

Best Regards
Kevin

Our device will be battery powered once and shall be shut down, restarted and suspended by user input. The wake up from suspend is done by the CTRL_WAKE1_MICO# signal.

For shutdown and reboot I could use the reboot(2) interface, but for suspend I guess I have to use systemctl - either via a call to system(systemctl suspend) or through the sd-bus interface.

Now I want my application to go into a defined state when the device enters suspend by user input. In order for the application to know when it can leave this state (because it has been woken up again), it needs to know when a suspend operation is in progress, or when it has completed or the device has been woken up.

The documentation of ‘systemctl suspend’ says: “This command is asynchronous, and will return after the suspend operation is successfully enqueued. It will not wait for the suspend/resume cycle to complete.”

So my idea was to query the active state of ‘suspend.target’ or ‘systemd-suspend.service’ to know when it is ‘active’ so that the application stays in its ‘suspend state’, or when it changes from ‘active’ to ‘inactive’ so that the application can leave its ‘suspend state’.

I could accomplish this again with a call to system() with an evaluation of the return value. But I would prefer the possibility to get this information via the sd-bus. However, I can’t find the appropriate unit whose state I could read out.

Or is there a way to find out if the system just woke up from a suspend?

I hope I could explain a bit clearer what I want to achieve. Thank you very much for your patience.

Unfortunately, calling system(systemctl is-active suspend.target) does not give reliable results either.

However, I have found another solution. Since systemd enters messages in its journal that a suspend has been exited, I use the sd-journal API to search its entries for “suspend exit”.

Sample code for this I found here. The code must be compiled with -lsystemd and the executing user has to be a member of the group “systemd-journal”.

Best regards,
Markus

1 Like