chiark / gitweb /
kernel-install: add compat with 'installkernel'
[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 "systemd/sd-id128.h"
29 #include "systemd/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
37 static char* arg_verb = NULL;
38
39 static int write_mode(char **modes) {
40         int r = 0;
41         char **mode;
42
43         STRV_FOREACH(mode, modes) {
44                 int k = write_string_file("/sys/power/disk", *mode);
45                 if (k == 0)
46                         return 0;
47                 log_debug("Failed to write '%s' to /sys/power/disk: %s",
48                           *mode, strerror(-k));
49                 if (r == 0)
50                         r = k;
51         }
52
53         if (r < 0)
54                 log_error("Failed to write mode to /sys/power/disk: %s",
55                           strerror(-r));
56
57         return r;
58 }
59
60 static int write_state(FILE *f0, char **states) {
61         FILE _cleanup_fclose_ *f = f0;
62         char **state;
63         int r = 0;
64
65         STRV_FOREACH(state, states) {
66                 int k;
67
68                 k = write_string_to_file(f, *state);
69                 if (k == 0)
70                         return 0;
71                 log_debug("Failed to write '%s' to /sys/power/state: %s",
72                           *state, strerror(-k));
73                 if (r == 0)
74                         r = k;
75
76                 fclose(f);
77                 f = fopen("/sys/power/state", "we");
78                 if (!f) {
79                         log_error("Failed to open /sys/power/state: %m");
80                         return -errno;
81                 }
82         }
83
84         return r;
85 }
86
87 static int execute(char **modes, char **states) {
88         char* arguments[4];
89         int r;
90         FILE *f;
91         const char* note = strappenda("SLEEP=", arg_verb);
92
93         /* This file is opened first, so that if we hit an error,
94          * we can abort before modyfing any state. */
95         f = fopen("/sys/power/state", "we");
96         if (!f) {
97                 log_error("Failed to open /sys/power/state: %m");
98                 return -errno;
99         }
100
101         /* Configure the hibernation mode */
102         r = write_mode(modes);
103         if (r < 0)
104                 return r;
105
106         arguments[0] = NULL;
107         arguments[1] = (char*) "pre";
108         arguments[2] = arg_verb;
109         arguments[3] = NULL;
110         execute_directory(SYSTEM_SLEEP_PATH, NULL, arguments);
111
112         log_struct(LOG_INFO,
113                    MESSAGE_ID(SD_MESSAGE_SLEEP_START),
114                    "MESSAGE=Suspending system...",
115                    note,
116                    NULL);
117
118         r = write_state(f, states);
119         if (r < 0)
120                 return r;
121
122         log_struct(LOG_INFO,
123                    MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
124                    "MESSAGE=System resumed.",
125                    note,
126                    NULL);
127
128         arguments[1] = (char*) "post";
129         execute_directory(SYSTEM_SLEEP_PATH, NULL, arguments);
130
131         return r;
132 }
133
134 static int help(void) {
135         printf("%s COMMAND\n\n"
136                "Suspend the system, hibernate the system, or both.\n\n"
137                "Commands:\n"
138                "  -h --help            Show this help and exit\n"
139                "  --version            Print version string and exit\n"
140                "  suspend              Suspend the system\n"
141                "  hibernate            Hibernate the system\n"
142                "  hybrid-sleep         Both hibernate and suspend the system\n"
143                , program_invocation_short_name
144                );
145
146         return 0;
147 }
148
149 static int parse_argv(int argc, char *argv[]) {
150         enum {
151                 ARG_VERSION = 0x100,
152         };
153
154         static const struct option options[] = {
155                 { "help",         no_argument,       NULL, 'h'           },
156                 { "version",      no_argument,       NULL, ARG_VERSION   },
157                 { NULL,           0,                 NULL, 0             }
158         };
159
160         int c;
161
162         assert(argc >= 0);
163         assert(argv);
164
165         while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0)
166                 switch(c) {
167                 case 'h':
168                         help();
169                         return 0 /* done */;
170
171                 case ARG_VERSION:
172                         puts(PACKAGE_STRING);
173                         puts(SYSTEMD_FEATURES);
174                         return 0 /* done */;
175
176                 case '?':
177                         return -EINVAL;
178
179                 default:
180                         log_error("Unknown option code %c", c);
181                         return -EINVAL;
182                 }
183
184         if (argc - optind != 1) {
185                 log_error("Usage: %s COMMAND",
186                           program_invocation_short_name);
187                 return -EINVAL;
188         }
189
190         arg_verb = argv[optind];
191
192         if (!streq(arg_verb, "suspend") &&
193             !streq(arg_verb, "hibernate") &&
194             !streq(arg_verb, "hybrid-sleep")) {
195                 log_error("Unknown command '%s'.", arg_verb);
196                 return -EINVAL;
197         }
198
199         return 1 /* work to do */;
200 }
201
202 int main(int argc, char *argv[]) {
203         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
204         int r;
205
206         log_set_target(LOG_TARGET_AUTO);
207         log_parse_environment();
208         log_open();
209
210         r = parse_argv(argc, argv);
211         if (r <= 0)
212                 goto finish;
213
214         r = parse_sleep_config(arg_verb, &modes, &states);
215         if (r < 0)
216                 goto finish;
217
218         r = execute(modes, states);
219
220 finish:
221         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
222 }