chiark / gitweb /
bus-proxy: implement 'at_console'
[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
96         int r;
97         _cleanup_fclose_ FILE *f = NULL;
98
99         /* This file is opened first, so that if we hit an error,
100          * we can abort before modifying any state. */
101         f = fopen("/sys/power/state", "we");
102         if (!f)
103                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
104
105         /* Configure the hibernation mode */
106         r = write_mode(modes);
107         if (r < 0)
108                 return r;
109
110         execute_directory(SYSTEM_SLEEP_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
111
112         log_struct(LOG_INFO,
113                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
114                    LOG_MESSAGE("Suspending system..."),
115                    "SLEEP=%s", arg_verb,
116                    NULL);
117
118         r = write_state(&f, states);
119         if (r < 0)
120                 return r;
121
122         log_struct(LOG_INFO,
123                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
124                    LOG_MESSAGE("System resumed."),
125                    "SLEEP=%s", arg_verb,
126                    NULL);
127
128         arguments[1] = (char*) "post";
129         execute_directory(SYSTEM_SLEEP_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments);
130
131         return r;
132 }
133
134 static void 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 static int parse_argv(int argc, char *argv[]) {
147         enum {
148                 ARG_VERSION = 0x100,
149         };
150
151         static const struct option options[] = {
152                 { "help",         no_argument,       NULL, 'h'           },
153                 { "version",      no_argument,       NULL, ARG_VERSION   },
154                 {}
155         };
156
157         int c;
158
159         assert(argc >= 0);
160         assert(argv);
161
162         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
163                 switch(c) {
164                 case 'h':
165                         help();
166                         return 0; /* done */
167
168                 case ARG_VERSION:
169                         puts(PACKAGE_STRING);
170                         puts(SYSTEMD_FEATURES);
171                         return 0 /* done */;
172
173                 case '?':
174                         return -EINVAL;
175
176                 default:
177                         assert_not_reached("Unhandled option");
178                 }
179
180         if (argc - optind != 1) {
181                 log_error("Usage: %s COMMAND",
182                           program_invocation_short_name);
183                 return -EINVAL;
184         }
185
186         arg_verb = argv[optind];
187
188         if (!streq(arg_verb, "suspend") &&
189             !streq(arg_verb, "hibernate") &&
190             !streq(arg_verb, "hybrid-sleep")) {
191                 log_error("Unknown command '%s'.", arg_verb);
192                 return -EINVAL;
193         }
194
195         return 1 /* work to do */;
196 }
197
198 int main(int argc, char *argv[]) {
199         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
200         int r;
201
202         log_set_target(LOG_TARGET_AUTO);
203         log_parse_environment();
204         log_open();
205
206         r = parse_argv(argc, argv);
207         if (r <= 0)
208                 goto finish;
209
210         r = parse_sleep_config(arg_verb, &modes, &states);
211         if (r < 0)
212                 goto finish;
213
214         r = execute(modes, states);
215
216 finish:
217         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
218 }