chiark / gitweb /
Implement masking and overriding of generators
[elogind.git] / src / sleep / sleep.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7   Copyright 2013 Zbigniew JÄ™drzejewski-Szmek
8
9   systemd is free software; you can redistribute it and/or modify it
10   under the terms of the GNU Lesser General Public License as published by
11   the Free Software Foundation; either version 2.1 of the License, or
12   (at your option) any later version.
13
14   systemd is distributed in the hope that it will be useful, but
15   WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   Lesser General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public License
20   along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <stdio.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27
28 #include "sd-id128.h"
29 #include "sd-messages.h"
30 #include "log.h"
31 #include "util.h"
32 #include "strv.h"
33 #include "fileio.h"
34 #include "build.h"
35 #include "sleep-config.h"
36 #include "def.h"
37
38 static char* arg_verb = NULL;
39
40 static int write_mode(char **modes) {
41         int r = 0;
42         char **mode;
43
44         STRV_FOREACH(mode, modes) {
45                 int k;
46
47                 k = write_string_file("/sys/power/disk", *mode);
48                 if (k == 0)
49                         return 0;
50
51                 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
52                                 *mode);
53                 if (r == 0)
54                         r = k;
55         }
56
57         if (r < 0)
58                 log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
59
60         return r;
61 }
62
63 static int write_state(FILE **f, char **states) {
64         char **state;
65         int r = 0;
66
67         STRV_FOREACH(state, states) {
68                 int k;
69
70                 k = write_string_stream(*f, *state);
71                 if (k == 0)
72                         return 0;
73                 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
74                                 *state);
75                 if (r == 0)
76                         r = k;
77
78                 fclose(*f);
79                 *f = fopen("/sys/power/state", "we");
80                 if (!*f)
81                         return log_error_errno(errno, "Failed to open /sys/power/state: %m");
82         }
83
84         return r;
85 }
86
87 static int execute(char **modes, char **states) {
88
89         char *arguments[] = {
90                 NULL,
91                 (char*) "pre",
92                 arg_verb,
93                 NULL
94         };
95         static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL};
96
97         int r;
98         _cleanup_fclose_ FILE *f = NULL;
99
100         /* This file is opened first, so that if we hit an error,
101          * we can abort before modifying any state. */
102         f = fopen("/sys/power/state", "we");
103         if (!f)
104                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
105
106         /* Configure the hibernation mode */
107         r = write_mode(modes);
108         if (r < 0)
109                 return r;
110
111         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
112
113         log_struct(LOG_INFO,
114                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
115                    LOG_MESSAGE("Suspending system..."),
116                    "SLEEP=%s", arg_verb,
117                    NULL);
118
119         r = write_state(&f, states);
120         if (r < 0)
121                 return r;
122
123         log_struct(LOG_INFO,
124                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
125                    LOG_MESSAGE("System resumed."),
126                    "SLEEP=%s", arg_verb,
127                    NULL);
128
129         arguments[1] = (char*) "post";
130         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
131
132         return r;
133 }
134
135 static void help(void) {
136         printf("%s COMMAND\n\n"
137                "Suspend the system, hibernate the system, or both.\n\n"
138                "Commands:\n"
139                "  -h --help            Show this help and exit\n"
140                "  --version            Print version string and exit\n"
141                "  suspend              Suspend the system\n"
142                "  hibernate            Hibernate the system\n"
143                "  hybrid-sleep         Both hibernate and suspend the system\n"
144                , program_invocation_short_name);
145 }
146
147 static int parse_argv(int argc, char *argv[]) {
148         enum {
149                 ARG_VERSION = 0x100,
150         };
151
152         static const struct option options[] = {
153                 { "help",         no_argument,       NULL, 'h'           },
154                 { "version",      no_argument,       NULL, ARG_VERSION   },
155                 {}
156         };
157
158         int c;
159
160         assert(argc >= 0);
161         assert(argv);
162
163         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
164                 switch(c) {
165                 case 'h':
166                         help();
167                         return 0; /* done */
168
169                 case ARG_VERSION:
170                         puts(PACKAGE_STRING);
171                         puts(SYSTEMD_FEATURES);
172                         return 0 /* done */;
173
174                 case '?':
175                         return -EINVAL;
176
177                 default:
178                         assert_not_reached("Unhandled option");
179                 }
180
181         if (argc - optind != 1) {
182                 log_error("Usage: %s COMMAND",
183                           program_invocation_short_name);
184                 return -EINVAL;
185         }
186
187         arg_verb = argv[optind];
188
189         if (!streq(arg_verb, "suspend") &&
190             !streq(arg_verb, "hibernate") &&
191             !streq(arg_verb, "hybrid-sleep")) {
192                 log_error("Unknown command '%s'.", arg_verb);
193                 return -EINVAL;
194         }
195
196         return 1 /* work to do */;
197 }
198
199 int main(int argc, char *argv[]) {
200         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
201         int r;
202
203         log_set_target(LOG_TARGET_AUTO);
204         log_parse_environment();
205         log_open();
206
207         r = parse_argv(argc, argv);
208         if (r <= 0)
209                 goto finish;
210
211         r = parse_sleep_config(arg_verb, &modes, &states);
212         if (r < 0)
213                 goto finish;
214
215         r = execute(modes, states);
216
217 finish:
218         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
219 }