chiark / gitweb /
journal: fail silently in sd_j_sendv() if journal is unavailable
[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                 {}
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                         return help();
169
170                 case ARG_VERSION:
171                         puts(PACKAGE_STRING);
172                         puts(SYSTEMD_FEATURES);
173                         return 0 /* done */;
174
175                 case '?':
176                         return -EINVAL;
177
178                 default:
179                         assert_not_reached("Unhandled option");
180                 }
181
182         if (argc - optind != 1) {
183                 log_error("Usage: %s COMMAND",
184                           program_invocation_short_name);
185                 return -EINVAL;
186         }
187
188         arg_verb = argv[optind];
189
190         if (!streq(arg_verb, "suspend") &&
191             !streq(arg_verb, "hibernate") &&
192             !streq(arg_verb, "hybrid-sleep")) {
193                 log_error("Unknown command '%s'.", arg_verb);
194                 return -EINVAL;
195         }
196
197         return 1 /* work to do */;
198 }
199
200 int main(int argc, char *argv[]) {
201         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
202         int r;
203
204         log_set_target(LOG_TARGET_AUTO);
205         log_parse_environment();
206         log_open();
207
208         r = parse_argv(argc, argv);
209         if (r <= 0)
210                 goto finish;
211
212         r = parse_sleep_config(arg_verb, &modes, &states);
213         if (r < 0)
214                 goto finish;
215
216         r = execute(modes, states);
217
218 finish:
219         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
220 }