Command line utility to run pm-hibernate

Have to be root in order to run pm-hibernate, and since suid acripts are a nuisance, I coded a small C program which just calls pm-hibernate, and set it suid.

Actually, it calls other commands of your choice before pm-hibernate. These commands are given as arguments; they are run without privileges. To have a command run asynchronously, specify --fork before it. This was designed to run as an Openbox action, so the command examples given in the code (scroll down to main()) are for blanking the screen.

Don't forget chown root:root <prog-name>; chmod u+s,a-w <prog-name>, after compiling.

To run pm-suspend instead of pm-hibernate replace the relevant string in the code, before compiling. These commands are better than acpitool -s, as they allow to set up hooks on suspending/ resuming; for example, create /etc/pm/sleep.d/20_dhcp-lease to release on suspend and request a new lease on thaw (see HOWTO.hooks) so as to avoid that leases expire inadvertently.

01: 
02: #include <unistd.h>
03: #include <stdlib.h>
04: #include <syslog.h>
05: #include <string.h>
06: #include <errno.h>
07: #include <sys/wait.h>
08: 
09: static void run_cmd(char const *cmd)
10: {
11:    syslog(LOG_INFO, "Running %s", cmd);
12:    int rtc = system(cmd);
13:    if (rtc == -1)
14:       syslog(LOG_ALERT, "Cannot system(%s): %s", cmd, strerror(errno));
15:    else if (WIFEXITED(rtc))
16:    {
17:       rtc = WEXITSTATUS(rtc);
18:       syslog(rtc? LOG_ERR: LOG_INFO, "%s returned %d", cmd, rtc);
19:    }
20:    else
21:       syslog(LOG_CRIT, "%s terminated with status %#x", cmd, rtc);
22: }
23: 
24: static void fork_cmd(char const *cmd)
25: {
26:    int tries = 3;
27:    while (tries --> 0)
28:    {
29:       int pid = fork();
30:       if (pid == 0) // parent
31:          return;
32: 
33:       if (pid > 0)  // child
34:       {
35:          run_cmd(cmd);
36:          exit(0);
37:       }
38: 
39:       syslog(LOG_ERR, "Cannot fork, will %s: %s",
40:          tries? "retry": "abort",
41:          strerror(errno));
42:       if (tries)
43:          sleep(1);
44:       else
45:          abort();
46:    }
47: }
48: 
49: int main(int argc, char *argv[])
50: /*
51: * Call whatever commands given on the command line, then hibernate.
52: * Possible commands can be:
53: *
54: *  "xscreensaver-command -lock"
55: *  --fork slock
56: *  "sleep 1"
57: *  "xset dpms force off"
58: *  ...
59: *
60: * The --fork option is a hack for not waiting completion of the next
61: * command.  It would have been nice to just append ampersand (&) to
62: * the command, but OpenBox actions remove all ampersands.
63: */
64: {
65:    seteuid(getuid()); // drop privileges to run arguments
66: 
67:    // since August 2023 daemon goes to /var/log/syslog 
68:    openlog(argv[0], LOG_ODELAY, LOG_DAEMON);
69:    syslog(LOG_INFO, "Started");
70: 
71:    int fork_next = 0;
72:    for (int i = 1; i < argc; ++i)
73:    {
74:       if (fork_next)
75:       {
76:          fork_next = 0;
77:          fork_cmd(argv[i]);
78:       }
79:       else if (strcmp("--fork", argv[i]) == 0)
80:          fork_next = 1;
81:       else
82:          run_cmd(argv[i]);
83:    }
84: 
85:    if (seteuid(0) || setuid(0))
86:       syslog(LOG_ERR, "Cannot set(e)uid(0): %s", strerror(errno));
87: 
88: // run_cmd("/sbin/pm-suspend");
89:    run_cmd("/sbin/pm-hibernate");
90: 
91:    closelog();
92:    return 0;
93: }
94: 
zero rights

Copy and paste the script to your editor of choice. There is a Javascript for that. Otherwise, you may just download it.