Merge lp:~yuningdodo/systemd/systemd.trusty-proposed.lp1313522 into lp:ubuntu/trusty-proposed/systemd

Proposed by Yu Ning
Status: Merged
Merge reported by: Martin Pitt
Merged at revision: not available
Proposed branch: lp:~yuningdodo/systemd/systemd.trusty-proposed.lp1313522
Merge into: lp:ubuntu/trusty-proposed/systemd
Diff against target: 1581 lines (+1423/-19)
12 files modified
.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.c (+596/-0)
.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.h (+37/-0)
.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/sleep-config.c (+179/-0)
.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/test/test-fileio.c (+145/-0)
.pc/applied-patches (+1/-0)
debian/changelog (+8/-0)
debian/patches/Advertise-hibernation-only-if-there-s-enough-free-sw.patch (+289/-0)
debian/patches/series (+1/-0)
src/shared/fileio.c (+34/-0)
src/shared/fileio.h (+2/-0)
src/shared/sleep-config.c (+107/-19)
src/test/test-fileio.c (+24/-0)
To merge this branch: bzr merge lp:~yuningdodo/systemd/systemd.trusty-proposed.lp1313522
Reviewer Review Type Date Requested Status
Ubuntu Development Team Pending
Review via email: mp+218597@code.launchpad.net

Description of the change

Apply the patch backported by Martin Pitt for bug #1313522 to trusty-proposed.

  [Martin Pitt]
  * Advertise hibernation only if there's enough free swap. Patches backported
    from current upstream. (LP: #1313522)

To post a comment you must log in.
Revision history for this message
Yu Ning (yuningdodo) wrote :

In fact what I want to do is a SRU to trusty, please let me know if I'm doing it in the wrong way. Thanks in advance.

Revision history for this message
Martin Pitt (pitti) wrote :

Uploaded, thanks!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch'
2=== added file '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/.timestamp'
3=== added directory '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src'
4=== added directory '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared'
5=== added file '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.c'
6--- .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.c 1970-01-01 00:00:00 +0000
7+++ .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.c 2014-05-07 11:49:49 +0000
8@@ -0,0 +1,596 @@
9+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
10+
11+/***
12+ This file is part of systemd.
13+
14+ Copyright 2010 Lennart Poettering
15+
16+ systemd is free software; you can redistribute it and/or modify it
17+ under the terms of the GNU Lesser General Public License as published by
18+ the Free Software Foundation; either version 2.1 of the License, or
19+ (at your option) any later version.
20+
21+ systemd is distributed in the hope that it will be useful, but
22+ WITHOUT ANY WARRANTY; without even the implied warranty of
23+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24+ Lesser General Public License for more details.
25+
26+ You should have received a copy of the GNU Lesser General Public License
27+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
28+***/
29+
30+#include <unistd.h>
31+#include "fileio.h"
32+#include "util.h"
33+#include "strv.h"
34+
35+
36+int write_string_to_file(FILE *f, const char *line) {
37+ errno = 0;
38+ fputs(line, f);
39+ if (!endswith(line, "\n"))
40+ fputc('\n', f);
41+
42+ fflush(f);
43+
44+ if (ferror(f))
45+ return errno ? -errno : -EIO;
46+
47+ return 0;
48+}
49+
50+int write_string_file(const char *fn, const char *line) {
51+ _cleanup_fclose_ FILE *f = NULL;
52+
53+ assert(fn);
54+ assert(line);
55+
56+ f = fopen(fn, "we");
57+ if (!f)
58+ return -errno;
59+
60+ return write_string_to_file(f, line);
61+}
62+
63+int write_string_file_atomic(const char *fn, const char *line) {
64+ _cleanup_fclose_ FILE *f = NULL;
65+ _cleanup_free_ char *p = NULL;
66+ int r;
67+
68+ assert(fn);
69+ assert(line);
70+
71+ r = fopen_temporary(fn, &f, &p);
72+ if (r < 0)
73+ return r;
74+
75+ fchmod_umask(fileno(f), 0644);
76+
77+ errno = 0;
78+ fputs(line, f);
79+ if (!endswith(line, "\n"))
80+ fputc('\n', f);
81+
82+ fflush(f);
83+
84+ if (ferror(f))
85+ r = errno ? -errno : -EIO;
86+ else {
87+ if (rename(p, fn) < 0)
88+ r = -errno;
89+ else
90+ r = 0;
91+ }
92+
93+ if (r < 0)
94+ unlink(p);
95+
96+ return r;
97+}
98+
99+int read_one_line_file(const char *fn, char **line) {
100+ _cleanup_fclose_ FILE *f = NULL;
101+ char t[LINE_MAX], *c;
102+
103+ assert(fn);
104+ assert(line);
105+
106+ f = fopen(fn, "re");
107+ if (!f)
108+ return -errno;
109+
110+ if (!fgets(t, sizeof(t), f)) {
111+
112+ if (ferror(f))
113+ return errno ? -errno : -EIO;
114+
115+ t[0] = 0;
116+ }
117+
118+ c = strdup(t);
119+ if (!c)
120+ return -ENOMEM;
121+ truncate_nl(c);
122+
123+ *line = c;
124+ return 0;
125+}
126+
127+int read_full_file(const char *fn, char **contents, size_t *size) {
128+ _cleanup_fclose_ FILE *f = NULL;
129+ size_t n, l;
130+ _cleanup_free_ char *buf = NULL;
131+ struct stat st;
132+
133+ assert(fn);
134+ assert(contents);
135+
136+ f = fopen(fn, "re");
137+ if (!f)
138+ return -errno;
139+
140+ if (fstat(fileno(f), &st) < 0)
141+ return -errno;
142+
143+ /* Safety check */
144+ if (st.st_size > 4*1024*1024)
145+ return -E2BIG;
146+
147+ n = st.st_size > 0 ? st.st_size : LINE_MAX;
148+ l = 0;
149+
150+ for (;;) {
151+ char *t;
152+ size_t k;
153+
154+ t = realloc(buf, n+1);
155+ if (!t)
156+ return -ENOMEM;
157+
158+ buf = t;
159+ k = fread(buf + l, 1, n - l, f);
160+
161+ if (k <= 0) {
162+ if (ferror(f))
163+ return -errno;
164+
165+ break;
166+ }
167+
168+ l += k;
169+ n *= 2;
170+
171+ /* Safety check */
172+ if (n > 4*1024*1024)
173+ return -E2BIG;
174+ }
175+
176+ buf[l] = 0;
177+ *contents = buf;
178+ buf = NULL;
179+
180+ if (size)
181+ *size = l;
182+
183+ return 0;
184+}
185+
186+static int parse_env_file_internal(
187+ const char *fname,
188+ const char *newline,
189+ int (*push) (const char *key, char *value, void *userdata),
190+ void *userdata) {
191+
192+ _cleanup_free_ char *contents = NULL, *key = NULL;
193+ size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
194+ char *p, *value = NULL;
195+ int r;
196+
197+ enum {
198+ PRE_KEY,
199+ KEY,
200+ PRE_VALUE,
201+ VALUE,
202+ VALUE_ESCAPE,
203+ SINGLE_QUOTE_VALUE,
204+ SINGLE_QUOTE_VALUE_ESCAPE,
205+ DOUBLE_QUOTE_VALUE,
206+ DOUBLE_QUOTE_VALUE_ESCAPE,
207+ COMMENT,
208+ COMMENT_ESCAPE
209+ } state = PRE_KEY;
210+
211+ assert(fname);
212+ assert(newline);
213+
214+ r = read_full_file(fname, &contents, NULL);
215+ if (r < 0)
216+ return r;
217+
218+ for (p = contents; *p; p++) {
219+ char c = *p;
220+
221+ switch (state) {
222+
223+ case PRE_KEY:
224+ if (strchr(COMMENTS, c))
225+ state = COMMENT;
226+ else if (!strchr(WHITESPACE, c)) {
227+ state = KEY;
228+ last_key_whitespace = (size_t) -1;
229+
230+ if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
231+ r = -ENOMEM;
232+ goto fail;
233+ }
234+
235+ key[n_key++] = c;
236+ }
237+ break;
238+
239+ case KEY:
240+ if (strchr(newline, c)) {
241+ state = PRE_KEY;
242+ n_key = 0;
243+ } else if (c == '=') {
244+ state = PRE_VALUE;
245+ last_value_whitespace = (size_t) -1;
246+ } else {
247+ if (!strchr(WHITESPACE, c))
248+ last_key_whitespace = (size_t) -1;
249+ else if (last_key_whitespace == (size_t) -1)
250+ last_key_whitespace = n_key;
251+
252+ if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
253+ r = -ENOMEM;
254+ goto fail;
255+ }
256+
257+ key[n_key++] = c;
258+ }
259+
260+ break;
261+
262+ case PRE_VALUE:
263+ if (strchr(newline, c)) {
264+ state = PRE_KEY;
265+ key[n_key] = 0;
266+
267+ if (value)
268+ value[n_value] = 0;
269+
270+ /* strip trailing whitespace from key */
271+ if (last_key_whitespace != (size_t) -1)
272+ key[last_key_whitespace] = 0;
273+
274+ r = push(key, value, userdata);
275+ if (r < 0)
276+ goto fail;
277+
278+ n_key = 0;
279+ value = NULL;
280+ value_alloc = n_value = 0;
281+
282+ } else if (c == '\'')
283+ state = SINGLE_QUOTE_VALUE;
284+ else if (c == '\"')
285+ state = DOUBLE_QUOTE_VALUE;
286+ else if (c == '\\')
287+ state = VALUE_ESCAPE;
288+ else if (!strchr(WHITESPACE, c)) {
289+ state = VALUE;
290+
291+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
292+ r = -ENOMEM;
293+ goto fail;
294+ }
295+
296+ value[n_value++] = c;
297+ }
298+
299+ break;
300+
301+ case VALUE:
302+ if (strchr(newline, c)) {
303+ state = PRE_KEY;
304+
305+ key[n_key] = 0;
306+
307+ if (value)
308+ value[n_value] = 0;
309+
310+ /* Chomp off trailing whitespace from value */
311+ if (last_value_whitespace != (size_t) -1)
312+ value[last_value_whitespace] = 0;
313+
314+ /* strip trailing whitespace from key */
315+ if (last_key_whitespace != (size_t) -1)
316+ key[last_key_whitespace] = 0;
317+
318+ r = push(key, value, userdata);
319+ if (r < 0)
320+ goto fail;
321+
322+ n_key = 0;
323+ value = NULL;
324+ value_alloc = n_value = 0;
325+
326+ } else if (c == '\\') {
327+ state = VALUE_ESCAPE;
328+ last_value_whitespace = (size_t) -1;
329+ } else {
330+ if (!strchr(WHITESPACE, c))
331+ last_value_whitespace = (size_t) -1;
332+ else if (last_value_whitespace == (size_t) -1)
333+ last_value_whitespace = n_value;
334+
335+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
336+ r = -ENOMEM;
337+ goto fail;
338+ }
339+
340+ value[n_value++] = c;
341+ }
342+
343+ break;
344+
345+ case VALUE_ESCAPE:
346+ state = VALUE;
347+
348+ if (!strchr(newline, c)) {
349+ /* Escaped newlines we eat up entirely */
350+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
351+ r = -ENOMEM;
352+ goto fail;
353+ }
354+
355+ value[n_value++] = c;
356+ }
357+ break;
358+
359+ case SINGLE_QUOTE_VALUE:
360+ if (c == '\'')
361+ state = PRE_VALUE;
362+ else if (c == '\\')
363+ state = SINGLE_QUOTE_VALUE_ESCAPE;
364+ else {
365+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
366+ r = -ENOMEM;
367+ goto fail;
368+ }
369+
370+ value[n_value++] = c;
371+ }
372+
373+ break;
374+
375+ case SINGLE_QUOTE_VALUE_ESCAPE:
376+ state = SINGLE_QUOTE_VALUE;
377+
378+ if (!strchr(newline, c)) {
379+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
380+ r = -ENOMEM;
381+ goto fail;
382+ }
383+
384+ value[n_value++] = c;
385+ }
386+ break;
387+
388+ case DOUBLE_QUOTE_VALUE:
389+ if (c == '\"')
390+ state = PRE_VALUE;
391+ else if (c == '\\')
392+ state = DOUBLE_QUOTE_VALUE_ESCAPE;
393+ else {
394+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
395+ r = -ENOMEM;
396+ goto fail;
397+ }
398+
399+ value[n_value++] = c;
400+ }
401+
402+ break;
403+
404+ case DOUBLE_QUOTE_VALUE_ESCAPE:
405+ state = DOUBLE_QUOTE_VALUE;
406+
407+ if (!strchr(newline, c)) {
408+ if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
409+ r = -ENOMEM;
410+ goto fail;
411+ }
412+
413+ value[n_value++] = c;
414+ }
415+ break;
416+
417+ case COMMENT:
418+ if (c == '\\')
419+ state = COMMENT_ESCAPE;
420+ else if (strchr(newline, c))
421+ state = PRE_KEY;
422+ break;
423+
424+ case COMMENT_ESCAPE:
425+ state = COMMENT;
426+ break;
427+ }
428+ }
429+
430+ if (state == PRE_VALUE ||
431+ state == VALUE ||
432+ state == VALUE_ESCAPE ||
433+ state == SINGLE_QUOTE_VALUE ||
434+ state == SINGLE_QUOTE_VALUE_ESCAPE ||
435+ state == DOUBLE_QUOTE_VALUE ||
436+ state == DOUBLE_QUOTE_VALUE_ESCAPE) {
437+
438+ key[n_key] = 0;
439+
440+ if (value)
441+ value[n_value] = 0;
442+
443+ if (state == VALUE)
444+ if (last_value_whitespace != (size_t) -1)
445+ value[last_value_whitespace] = 0;
446+
447+ /* strip trailing whitespace from key */
448+ if (last_key_whitespace != (size_t) -1)
449+ key[last_key_whitespace] = 0;
450+
451+ r = push(key, value, userdata);
452+ if (r < 0)
453+ goto fail;
454+ }
455+
456+ return 0;
457+
458+fail:
459+ free(value);
460+ return r;
461+}
462+
463+static int parse_env_file_push(const char *key, char *value, void *userdata) {
464+ const char *k;
465+ va_list* ap = (va_list*) userdata;
466+ va_list aq;
467+
468+ va_copy(aq, *ap);
469+
470+ while ((k = va_arg(aq, const char *))) {
471+ char **v;
472+
473+ v = va_arg(aq, char **);
474+
475+ if (streq(key, k)) {
476+ va_end(aq);
477+ free(*v);
478+ *v = value;
479+ return 1;
480+ }
481+ }
482+
483+ va_end(aq);
484+
485+ free(value);
486+ return 0;
487+}
488+
489+int parse_env_file(
490+ const char *fname,
491+ const char *newline, ...) {
492+
493+ va_list ap;
494+ int r;
495+
496+ if (!newline)
497+ newline = NEWLINE;
498+
499+ va_start(ap, newline);
500+ r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
501+ va_end(ap);
502+
503+ return r;
504+}
505+
506+static int load_env_file_push(const char *key, char *value, void *userdata) {
507+ char ***m = userdata;
508+ char *p;
509+ int r;
510+
511+ p = strjoin(key, "=", strempty(value), NULL);
512+ if (!p)
513+ return -ENOMEM;
514+
515+ r = strv_push(m, p);
516+ if (r < 0) {
517+ free(p);
518+ return r;
519+ }
520+
521+ free(value);
522+ return 0;
523+}
524+
525+int load_env_file(const char *fname, const char *newline, char ***rl) {
526+ char **m = NULL;
527+ int r;
528+
529+ if (!newline)
530+ newline = NEWLINE;
531+
532+ r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
533+ if (r < 0) {
534+ strv_free(m);
535+ return r;
536+ }
537+
538+ *rl = m;
539+ return 0;
540+}
541+
542+static void write_env_var(FILE *f, const char *v) {
543+ const char *p;
544+
545+ p = strchr(v, '=');
546+ if (!p) {
547+ /* Fallback */
548+ fputs(v, f);
549+ fputc('\n', f);
550+ return;
551+ }
552+
553+ p++;
554+ fwrite(v, 1, p-v, f);
555+
556+ if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
557+ fputc('\"', f);
558+
559+ for (; *p; p++) {
560+ if (strchr("\'\"\\`$", *p))
561+ fputc('\\', f);
562+
563+ fputc(*p, f);
564+ }
565+
566+ fputc('\"', f);
567+ } else
568+ fputs(p, f);
569+
570+ fputc('\n', f);
571+}
572+
573+int write_env_file(const char *fname, char **l) {
574+ char **i;
575+ _cleanup_free_ char *p = NULL;
576+ _cleanup_fclose_ FILE *f = NULL;
577+ int r;
578+
579+ r = fopen_temporary(fname, &f, &p);
580+ if (r < 0)
581+ return r;
582+
583+ fchmod_umask(fileno(f), 0644);
584+
585+ errno = 0;
586+ STRV_FOREACH(i, l)
587+ write_env_var(f, *i);
588+
589+ fflush(f);
590+
591+ if (ferror(f))
592+ r = errno ? -errno : -EIO;
593+ else {
594+ if (rename(p, fname) < 0)
595+ r = -errno;
596+ else
597+ r = 0;
598+ }
599+
600+ if (r < 0)
601+ unlink(p);
602+
603+ return r;
604+}
605
606=== added file '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.h'
607--- .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.h 1970-01-01 00:00:00 +0000
608+++ .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/fileio.h 2014-05-07 11:49:49 +0000
609@@ -0,0 +1,37 @@
610+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
611+
612+#pragma once
613+
614+/***
615+ This file is part of systemd.
616+
617+ Copyright 2010 Lennart Poettering
618+
619+ systemd is free software; you can redistribute it and/or modify it
620+ under the terms of the GNU Lesser General Public License as published by
621+ the Free Software Foundation; either version 2.1 of the License, or
622+ (at your option) any later version.
623+
624+ systemd is distributed in the hope that it will be useful, but
625+ WITHOUT ANY WARRANTY; without even the implied warranty of
626+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
627+ Lesser General Public License for more details.
628+
629+ You should have received a copy of the GNU Lesser General Public License
630+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
631+***/
632+#include <stddef.h>
633+#include <stdio.h>
634+
635+#include "macro.h"
636+
637+int write_string_to_file(FILE *f, const char *line);
638+int write_string_file(const char *fn, const char *line);
639+int write_string_file_atomic(const char *fn, const char *line);
640+
641+int read_one_line_file(const char *fn, char **line);
642+int read_full_file(const char *fn, char **contents, size_t *size);
643+
644+int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
645+int load_env_file(const char *fname, const char *separator, char ***l);
646+int write_env_file(const char *fname, char **l);
647
648=== added file '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/sleep-config.c'
649--- .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/sleep-config.c 1970-01-01 00:00:00 +0000
650+++ .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/shared/sleep-config.c 2014-05-07 11:49:49 +0000
651@@ -0,0 +1,179 @@
652+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
653+
654+/***
655+ This file is part of systemd.
656+
657+ Copyright 2013 Zbigniew Jędrzejewski-Szmek
658+
659+ systemd is free software; you can redistribute it and/or modify it
660+ under the terms of the GNU Lesser General Public License as published by
661+ the Free Software Foundation; either version 2.1 of the License, or
662+ (at your option) any later version.
663+
664+ systemd is distributed in the hope that it will be useful, but
665+ WITHOUT ANY WARRANTY; without even the implied warranty of
666+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
667+ Lesser General Public License for more details.
668+
669+ You should have received a copy of the GNU Lesser General Public License
670+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
671+***/
672+
673+#include <stdio.h>
674+
675+#include "conf-parser.h"
676+#include "sleep-config.h"
677+#include "fileio.h"
678+#include "log.h"
679+#include "strv.h"
680+#include "util.h"
681+
682+int parse_sleep_config(const char *verb, char ***modes, char ***states) {
683+ _cleanup_strv_free_ char
684+ **suspend_mode = NULL, **suspend_state = NULL,
685+ **hibernate_mode = NULL, **hibernate_state = NULL,
686+ **hybrid_mode = NULL, **hybrid_state = NULL;
687+
688+ const ConfigTableItem items[] = {
689+ { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
690+ { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
691+ { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
692+ { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
693+ { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
694+ { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
695+ {}};
696+
697+ int r;
698+ FILE _cleanup_fclose_ *f;
699+
700+ f = fopen(PKGSYSCONFDIR "/sleep.conf", "re");
701+ if (!f)
702+ log_full(errno == ENOENT ? LOG_DEBUG: LOG_WARNING,
703+ "Failed to open configuration file " PKGSYSCONFDIR "/sleep.conf: %m");
704+ else {
705+ r = config_parse(NULL, PKGSYSCONFDIR "/sleep.conf", f, "Sleep\0",
706+ config_item_table_lookup, (void*) items, false, false, NULL);
707+ if (r < 0)
708+ log_warning("Failed to parse configuration file: %s", strerror(-r));
709+ }
710+
711+ if (streq(verb, "suspend")) {
712+ /* empty by default */
713+ *modes = suspend_mode;
714+
715+ if (suspend_state)
716+ *states = suspend_state;
717+ else
718+ *states = strv_split_nulstr("mem\0standby\0freeze\0");
719+
720+ suspend_mode = suspend_state = NULL;
721+ } else if (streq(verb, "hibernate")) {
722+ if (hibernate_mode)
723+ *modes = hibernate_mode;
724+ else
725+ *modes = strv_split_nulstr("platform\0shutdown\0");
726+
727+ if (hibernate_state)
728+ *states = hibernate_state;
729+ else
730+ *states = strv_split_nulstr("disk\0");
731+
732+ hibernate_mode = hibernate_state = NULL;
733+ } else if (streq(verb, "hybrid-sleep")) {
734+ if (hybrid_mode)
735+ *modes = hybrid_mode;
736+ else
737+ *modes = strv_split_nulstr("suspend\0platform\0shutdown\0");
738+
739+ if (hybrid_state)
740+ *states = hybrid_state;
741+ else
742+ *states = strv_split_nulstr("disk\0");
743+
744+ hybrid_mode = hybrid_state = NULL;
745+ } else
746+ assert_not_reached("what verb");
747+
748+ if (!modes || !states) {
749+ strv_free(*modes);
750+ strv_free(*states);
751+ return log_oom();
752+ }
753+
754+ return 0;
755+}
756+
757+int can_sleep_state(char **types) {
758+ char *w, *state, **type;
759+ int r;
760+ _cleanup_free_ char *p = NULL;
761+
762+ if (strv_isempty(types))
763+ return true;
764+
765+ /* If /sys is read-only we cannot sleep */
766+ if (access("/sys/power/state", W_OK) < 0)
767+ return false;
768+
769+ r = read_one_line_file("/sys/power/state", &p);
770+ if (r < 0)
771+ return false;
772+
773+ STRV_FOREACH(type, types) {
774+ size_t l, k;
775+
776+ k = strlen(*type);
777+ FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
778+ if (l == k && memcmp(w, *type, l) == 0)
779+ return true;
780+ }
781+
782+ return false;
783+}
784+
785+int can_sleep_disk(char **types) {
786+ char *w, *state, **type;
787+ int r;
788+ _cleanup_free_ char *p = NULL;
789+
790+ if (strv_isempty(types))
791+ return true;
792+
793+ /* If /sys is read-only we cannot sleep */
794+ if (access("/sys/power/disk", W_OK) < 0)
795+ return false;
796+
797+ r = read_one_line_file("/sys/power/disk", &p);
798+ if (r < 0)
799+ return false;
800+
801+ STRV_FOREACH(type, types) {
802+ size_t l, k;
803+
804+ k = strlen(*type);
805+ FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
806+ if (l == k && memcmp(w, *type, l) == 0)
807+ return true;
808+
809+ if (l == k + 2 && w[0] == '[' && memcmp(w + 1, *type, l - 2) == 0 && w[l-1] == ']')
810+ return true;
811+ }
812+ }
813+
814+ return false;
815+}
816+
817+int can_sleep(const char *verb) {
818+ _cleanup_strv_free_ char **modes = NULL, **states = NULL;
819+ int r;
820+
821+ assert(streq(verb, "suspend") ||
822+ streq(verb, "hibernate") ||
823+ streq(verb, "hybrid-sleep"));
824+
825+ r = parse_sleep_config(verb, &modes, &states);
826+ if (r < 0)
827+ return false;
828+
829+ return can_sleep_state(states) && can_sleep_disk(modes);
830+}
831
832=== added directory '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/test'
833=== added file '.pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/test/test-fileio.c'
834--- .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/test/test-fileio.c 1970-01-01 00:00:00 +0000
835+++ .pc/Advertise-hibernation-only-if-there-s-enough-free-sw.patch/src/test/test-fileio.c 2014-05-07 11:49:49 +0000
836@@ -0,0 +1,145 @@
837+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
838+
839+/***
840+ This file is part of systemd.
841+
842+ Copyright 2013 Lennart Poettering
843+
844+ systemd is free software; you can redistribute it and/or modify it
845+ under the terms of the GNU Lesser General Public License as published by
846+ the Free Software Foundation; either version 2.1 of the License, or
847+ (at your option) any later version.
848+
849+ systemd is distributed in the hope that it will be useful, but
850+ WITHOUT ANY WARRANTY; without even the implied warranty of
851+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
852+ Lesser General Public License for more details.
853+
854+ You should have received a copy of the GNU Lesser General Public License
855+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
856+***/
857+
858+#include <stdio.h>
859+#include <fcntl.h>
860+#include <unistd.h>
861+
862+#include "util.h"
863+#include "fileio.h"
864+#include "strv.h"
865+#include "env-util.h"
866+
867+static void test_parse_env_file(void) {
868+ char t[] = "/tmp/test-parse-env-file-XXXXXX";
869+ int fd, r;
870+ FILE *f;
871+ _cleanup_free_ char *one = NULL, *two = NULL, *three = NULL, *four = NULL, *five = NULL,
872+ *six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL;
873+ _cleanup_strv_free_ char **a = NULL, **b = NULL;
874+ char **i;
875+ unsigned k;
876+
877+ fd = mkostemp(t, O_CLOEXEC);
878+ assert_se(fd >= 0);
879+
880+ f = fdopen(fd, "w");
881+ assert_se(f);
882+
883+ fputs("one=BAR \n"
884+ "# comment\n"
885+ " # comment \n"
886+ " ; comment \n"
887+ " two = bar \n"
888+ "invalid line\n"
889+ "invalid line #comment\n"
890+ "three = \"333\n"
891+ "xxxx\"\n"
892+ "four = \'44\\\"44\'\n"
893+ "five = \'55\\\'55\' \"FIVE\" cinco \n"
894+ "six = seis sechs\\\n"
895+ " sis\n"
896+ "seven=\"sevenval\" #nocomment\n"
897+ "eight=eightval #nocomment\n"
898+ "export nine=nineval\n"
899+ "ten=", f);
900+
901+ fflush(f);
902+ fclose(f);
903+
904+ r = load_env_file(t, NULL, &a);
905+ assert_se(r >= 0);
906+
907+ STRV_FOREACH(i, a)
908+ log_info("Got: <%s>", *i);
909+
910+ assert_se(streq(a[0], "one=BAR"));
911+ assert_se(streq(a[1], "two=bar"));
912+ assert_se(streq(a[2], "three=333\nxxxx"));
913+ assert_se(streq(a[3], "four=44\"44"));
914+ assert_se(streq(a[4], "five=55\'55FIVEcinco"));
915+ assert_se(streq(a[5], "six=seis sechs sis"));
916+ assert_se(streq(a[6], "seven=sevenval#nocomment"));
917+ assert_se(streq(a[7], "eight=eightval #nocomment"));
918+ assert_se(streq(a[8], "export nine=nineval"));
919+ assert_se(streq(a[9], "ten="));
920+ assert_se(a[10] == NULL);
921+
922+ strv_env_clean_log(a, "/tmp/test-fileio");
923+
924+ k = 0;
925+ STRV_FOREACH(i, b) {
926+ log_info("Got2: <%s>", *i);
927+ assert_se(streq(*i, a[k++]));
928+ }
929+
930+ r = parse_env_file(
931+ t, NULL,
932+ "one", &one,
933+ "two", &two,
934+ "three", &three,
935+ "four", &four,
936+ "five", &five,
937+ "six", &six,
938+ "seven", &seven,
939+ "eight", &eight,
940+ "export nine", &nine,
941+ "ten", &ten,
942+ NULL);
943+
944+ assert_se(r >= 0);
945+
946+ log_info("one=[%s]", strna(one));
947+ log_info("two=[%s]", strna(two));
948+ log_info("three=[%s]", strna(three));
949+ log_info("four=[%s]", strna(four));
950+ log_info("five=[%s]", strna(five));
951+ log_info("six=[%s]", strna(six));
952+ log_info("seven=[%s]", strna(seven));
953+ log_info("eight=[%s]", strna(eight));
954+ log_info("export nine=[%s]", strna(nine));
955+ log_info("ten=[%s]", strna(nine));
956+
957+ assert_se(streq(one, "BAR"));
958+ assert_se(streq(two, "bar"));
959+ assert_se(streq(three, "333\nxxxx"));
960+ assert_se(streq(four, "44\"44"));
961+ assert_se(streq(five, "55\'55FIVEcinco"));
962+ assert_se(streq(six, "seis sechs sis"));
963+ assert_se(streq(seven, "sevenval#nocomment"));
964+ assert_se(streq(eight, "eightval #nocomment"));
965+ assert_se(streq(nine, "nineval"));
966+ assert_se(ten == NULL);
967+
968+ r = write_env_file("/tmp/test-fileio", a);
969+ assert_se(r >= 0);
970+
971+ r = load_env_file("/tmp/test-fileio", NULL, &b);
972+ assert_se(r >= 0);
973+
974+ unlink(t);
975+ unlink("/tmp/test-fileio");
976+}
977+
978+int main(int argc, char *argv[]) {
979+ test_parse_env_file();
980+ return 0;
981+}
982
983=== modified file '.pc/applied-patches'
984--- .pc/applied-patches 2014-03-19 21:34:25 +0000
985+++ .pc/applied-patches 2014-05-07 11:49:49 +0000
986@@ -17,3 +17,4 @@
987 ubuntu-logind-controllers.patch
988 fix-ftbfs.patch
989 add-cgmanager-support
990+Advertise-hibernation-only-if-there-s-enough-free-sw.patch
991
992=== modified file 'debian/changelog'
993--- debian/changelog 2014-04-14 16:20:35 +0000
994+++ debian/changelog 2014-05-07 11:49:49 +0000
995@@ -1,3 +1,11 @@
996+systemd (204-5ubuntu20.1) trusty-proposed; urgency=medium
997+
998+ [Martin Pitt]
999+ * Advertise hibernation only if there's enough free swap. Patches backported
1000+ from current upstream. (LP: #1313522)
1001+
1002+ -- Yu Ning <ning.yu@canonical.com> Wed, 07 May 2014 19:41:29 +0800
1003+
1004 systemd (204-5ubuntu20) trusty; urgency=medium
1005
1006 * systemd-logind.conf: Don't use the limit stanza which fails the
1007
1008=== added file 'debian/patches/Advertise-hibernation-only-if-there-s-enough-free-sw.patch'
1009--- debian/patches/Advertise-hibernation-only-if-there-s-enough-free-sw.patch 1970-01-01 00:00:00 +0000
1010+++ debian/patches/Advertise-hibernation-only-if-there-s-enough-free-sw.patch 2014-05-07 11:49:49 +0000
1011@@ -0,0 +1,289 @@
1012+From: =?utf-8?q?Zbigniew_J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
1013+Date: Fri, 13 Sep 2013 19:41:52 -0400
1014+Subject: Advertise hibernation only if there's enough free swap
1015+
1016+Cherry-pick various upstream patches starting from 69ab80881 and 442e00839
1017+to check whether there is swap available for hibernation.
1018+
1019+Bug-Ubuntu: https://launchpad.net/bugs/1313522
1020+---
1021+ src/shared/fileio.c | 34 +++++++++++++
1022+ src/shared/fileio.h | 2 +
1023+ src/shared/sleep-config.c | 126 +++++++++++++++++++++++++++++++++++++++-------
1024+ src/shared/util.c | 1 -
1025+ src/test/test-fileio.c | 24 +++++++++
1026+ src/test/test-sleep.c | 16 +++---
1027+ 6 files changed, 175 insertions(+), 28 deletions(-)
1028+
1029+diff --git a/src/shared/fileio.c b/src/shared/fileio.c
1030+index ad068bf..9af284d 100644
1031+--- a/src/shared/fileio.c
1032++++ b/src/shared/fileio.c
1033+@@ -594,3 +594,37 @@ int write_env_file(const char *fname, char **l) {
1034+
1035+ return r;
1036+ }
1037++
1038++/**
1039++ * Retrieve one field from a file like /proc/self/status.
1040++ * pattern should start with '\n' and end with ':'. Whitespace
1041++ * after ':' will be skipped. field must be freed afterwards.
1042++ */
1043++int get_status_field(const char *filename, const char *pattern, char **field) {
1044++ _cleanup_free_ char *status = NULL;
1045++ char *t;
1046++ size_t len;
1047++ int r;
1048++
1049++ assert(filename);
1050++ assert(field);
1051++
1052++ r = read_full_file(filename, &status, NULL);
1053++ if (r < 0)
1054++ return r;
1055++
1056++ t = strstr(status, pattern);
1057++ if (!t)
1058++ return -ENOENT;
1059++
1060++ t += strlen(pattern);
1061++ t += strspn(t, WHITESPACE);
1062++
1063++ len = strcspn(t, WHITESPACE);
1064++
1065++ *field = strndup(t, len);
1066++ if (!*field)
1067++ return -ENOMEM;
1068++
1069++ return 0;
1070++}
1071+diff --git a/src/shared/fileio.h b/src/shared/fileio.h
1072+index 0ca6878..d8079ab 100644
1073+--- a/src/shared/fileio.h
1074++++ b/src/shared/fileio.h
1075+@@ -35,3 +35,5 @@ int read_full_file(const char *fn, char **contents, size_t *size);
1076+ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
1077+ int load_env_file(const char *fname, const char *separator, char ***l);
1078+ int write_env_file(const char *fname, char **l);
1079++
1080++int get_status_field(const char *filename, const char *pattern, char **field);
1081+diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
1082+index cd3238b..cf1cd40 100644
1083+--- a/src/shared/sleep-config.c
1084++++ b/src/shared/sleep-config.c
1085+@@ -28,11 +28,14 @@
1086+ #include "strv.h"
1087+ #include "util.h"
1088+
1089+-int parse_sleep_config(const char *verb, char ***modes, char ***states) {
1090++#define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
1091++
1092++int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
1093+ _cleanup_strv_free_ char
1094+ **suspend_mode = NULL, **suspend_state = NULL,
1095+ **hibernate_mode = NULL, **hibernate_state = NULL,
1096+ **hybrid_mode = NULL, **hybrid_state = NULL;
1097++ char **modes, **states;
1098+
1099+ const ConfigTableItem items[] = {
1100+ { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
1101+@@ -59,47 +62,46 @@ int parse_sleep_config(const char *verb, char ***modes, char ***states) {
1102+
1103+ if (streq(verb, "suspend")) {
1104+ /* empty by default */
1105+- *modes = suspend_mode;
1106++ USE(modes, suspend_mode);
1107+
1108+ if (suspend_state)
1109+- *states = suspend_state;
1110++ USE(states, suspend_state);
1111+ else
1112+- *states = strv_split_nulstr("mem\0standby\0freeze\0");
1113++ states = strv_new("mem", "standby", "freeze", NULL);
1114+
1115+- suspend_mode = suspend_state = NULL;
1116+ } else if (streq(verb, "hibernate")) {
1117+ if (hibernate_mode)
1118+- *modes = hibernate_mode;
1119++ USE(modes, hibernate_mode);
1120+ else
1121+- *modes = strv_split_nulstr("platform\0shutdown\0");
1122++ modes = strv_new("platform", "shutdown", NULL);
1123+
1124+ if (hibernate_state)
1125+- *states = hibernate_state;
1126++ USE(states, hibernate_state);
1127+ else
1128+- *states = strv_split_nulstr("disk\0");
1129++ states = strv_new("disk", NULL);
1130+
1131+- hibernate_mode = hibernate_state = NULL;
1132+ } else if (streq(verb, "hybrid-sleep")) {
1133+ if (hybrid_mode)
1134+- *modes = hybrid_mode;
1135++ USE(modes, hybrid_mode);
1136+ else
1137+- *modes = strv_split_nulstr("suspend\0platform\0shutdown\0");
1138++ modes = strv_new("suspend", "platform", "shutdown", NULL);
1139+
1140+ if (hybrid_state)
1141+- *states = hybrid_state;
1142++ USE(states, hybrid_state);
1143+ else
1144+- *states = strv_split_nulstr("disk\0");
1145++ states = strv_new("disk", NULL);
1146+
1147+- hybrid_mode = hybrid_state = NULL;
1148+ } else
1149+ assert_not_reached("what verb");
1150+
1151+- if (!modes || !states) {
1152+- strv_free(*modes);
1153+- strv_free(*states);
1154++ if ((!modes && !streq(verb, "suspend")) || !states) {
1155++ strv_free(modes);
1156++ strv_free(states);
1157+ return log_oom();
1158+ }
1159+
1160++ *_modes = modes;
1161++ *_states = states;
1162+ return 0;
1163+ }
1164+
1165+@@ -163,6 +165,89 @@ int can_sleep_disk(char **types) {
1166+ return false;
1167+ }
1168+
1169++#define HIBERNATION_SWAP_THRESHOLD 0.98
1170++
1171++static int hibernation_partition_size(size_t *size, size_t *used) {
1172++ _cleanup_fclose_ FILE *f;
1173++ int i;
1174++
1175++ assert(size);
1176++ assert(used);
1177++
1178++ f = fopen("/proc/swaps", "re");
1179++ if (!f) {
1180++ log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
1181++ "Failed to retrieve open /proc/swaps: %m");
1182++ assert(errno > 0);
1183++ return -errno;
1184++ }
1185++
1186++ (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
1187++
1188++ for (i = 1;; i++) {
1189++ _cleanup_free_ char *dev = NULL, *type = NULL;
1190++ size_t size_field, used_field;
1191++ int k;
1192++
1193++ k = fscanf(f,
1194++ "%ms " /* device/file */
1195++ "%ms " /* type of swap */
1196++ "%zd " /* swap size */
1197++ "%zd " /* used */
1198++ "%*i\n", /* priority */
1199++ &dev, &type, &size_field, &used_field);
1200++ if (k != 4) {
1201++ if (k == EOF)
1202++ break;
1203++
1204++ log_warning("Failed to parse /proc/swaps:%u", i);
1205++ continue;
1206++ }
1207++
1208++ if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
1209++ log_warning("Ignoring deleted swapfile '%s'.", dev);
1210++ continue;
1211++ }
1212++
1213++ *size = size_field;
1214++ *used = used_field;
1215++ return 0;
1216++ }
1217++
1218++ log_debug("No swap partitions were found.");
1219++ return -ENOSYS;
1220++}
1221++
1222++static bool enough_memory_for_hibernation(void) {
1223++ _cleanup_free_ char *active = NULL;
1224++ unsigned long long act = 0;
1225++ size_t size = 0, used = 0;
1226++ int r;
1227++
1228++ r = hibernation_partition_size(&size, &used);
1229++ if (r < 0)
1230++ return false;
1231++
1232++ r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
1233++ if (r < 0) {
1234++ log_error("Failed to retrieve Active(anon) from /proc/meminfo: %s", strerror(-r));
1235++ return false;
1236++ }
1237++
1238++ r = safe_atollu(active, &act);
1239++ if (r < 0) {
1240++ log_error("Failed to parse Active(anon) from /proc/meminfo: %s: %s",
1241++ active, strerror(-r));
1242++ return false;
1243++ }
1244++
1245++ r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
1246++ log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
1247++ r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
1248++
1249++ return r;
1250++}
1251++
1252+ int can_sleep(const char *verb) {
1253+ _cleanup_strv_free_ char **modes = NULL, **states = NULL;
1254+ int r;
1255+@@ -175,5 +260,8 @@ int can_sleep(const char *verb) {
1256+ if (r < 0)
1257+ return false;
1258+
1259+- return can_sleep_state(states) && can_sleep_disk(modes);
1260++ if (!can_sleep_state(states) || !can_sleep_disk(modes))
1261++ return false;
1262++
1263++ return streq(verb, "suspend") || enough_memory_for_hibernation();
1264+ }
1265+diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
1266+index d56f7cc..8049efa 100644
1267+--- a/src/test/test-fileio.c
1268++++ b/src/test/test-fileio.c
1269+@@ -139,7 +139,31 @@ static void test_parse_env_file(void) {
1270+ unlink("/tmp/test-fileio");
1271+ }
1272+
1273++static void test_status_field(void) {
1274++ _cleanup_free_ char *t = NULL, *p = NULL, *s = NULL;
1275++ unsigned long long total, buffers;
1276++ int r;
1277++
1278++ assert_se(get_status_field("/proc/self/status", "\nThreads:", &t) == 0);
1279++ puts(t);
1280++ assert_se(streq(t, "1"));
1281++
1282++ r = get_status_field("/proc/meminfo", "MemTotal:", &p);
1283++ if (r == -ENOENT)
1284++ return;
1285++ assert(r == 0);
1286++ puts(p);
1287++ assert_se(safe_atollu(p, &total) == 0);
1288++
1289++ assert_se(get_status_field("/proc/meminfo", "\nBuffers:", &s) == 0);
1290++ puts(s);
1291++ assert_se(safe_atollu(s, &buffers) == 0);
1292++
1293++ assert(buffers < total);
1294++}
1295++
1296+ int main(int argc, char *argv[]) {
1297+ test_parse_env_file();
1298++ test_status_field();
1299+ return 0;
1300+ }
1301
1302=== modified file 'debian/patches/series'
1303--- debian/patches/series 2014-03-19 21:34:25 +0000
1304+++ debian/patches/series 2014-05-07 11:49:49 +0000
1305@@ -25,3 +25,4 @@
1306 fix-ftbfs.patch
1307
1308 add-cgmanager-support
1309+Advertise-hibernation-only-if-there-s-enough-free-sw.patch
1310
1311=== modified file 'src/shared/fileio.c'
1312--- src/shared/fileio.c 2013-06-04 10:46:58 +0000
1313+++ src/shared/fileio.c 2014-05-07 11:49:49 +0000
1314@@ -594,3 +594,37 @@
1315
1316 return r;
1317 }
1318+
1319+/**
1320+ * Retrieve one field from a file like /proc/self/status.
1321+ * pattern should start with '\n' and end with ':'. Whitespace
1322+ * after ':' will be skipped. field must be freed afterwards.
1323+ */
1324+int get_status_field(const char *filename, const char *pattern, char **field) {
1325+ _cleanup_free_ char *status = NULL;
1326+ char *t;
1327+ size_t len;
1328+ int r;
1329+
1330+ assert(filename);
1331+ assert(field);
1332+
1333+ r = read_full_file(filename, &status, NULL);
1334+ if (r < 0)
1335+ return r;
1336+
1337+ t = strstr(status, pattern);
1338+ if (!t)
1339+ return -ENOENT;
1340+
1341+ t += strlen(pattern);
1342+ t += strspn(t, WHITESPACE);
1343+
1344+ len = strcspn(t, WHITESPACE);
1345+
1346+ *field = strndup(t, len);
1347+ if (!*field)
1348+ return -ENOMEM;
1349+
1350+ return 0;
1351+}
1352
1353=== modified file 'src/shared/fileio.h'
1354--- src/shared/fileio.h 2013-06-04 10:46:58 +0000
1355+++ src/shared/fileio.h 2014-05-07 11:49:49 +0000
1356@@ -35,3 +35,5 @@
1357 int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
1358 int load_env_file(const char *fname, const char *separator, char ***l);
1359 int write_env_file(const char *fname, char **l);
1360+
1361+int get_status_field(const char *filename, const char *pattern, char **field);
1362
1363=== modified file 'src/shared/sleep-config.c'
1364--- src/shared/sleep-config.c 2013-06-04 10:46:58 +0000
1365+++ src/shared/sleep-config.c 2014-05-07 11:49:49 +0000
1366@@ -28,11 +28,14 @@
1367 #include "strv.h"
1368 #include "util.h"
1369
1370-int parse_sleep_config(const char *verb, char ***modes, char ***states) {
1371+#define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
1372+
1373+int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
1374 _cleanup_strv_free_ char
1375 **suspend_mode = NULL, **suspend_state = NULL,
1376 **hibernate_mode = NULL, **hibernate_state = NULL,
1377 **hybrid_mode = NULL, **hybrid_state = NULL;
1378+ char **modes, **states;
1379
1380 const ConfigTableItem items[] = {
1381 { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
1382@@ -59,47 +62,46 @@
1383
1384 if (streq(verb, "suspend")) {
1385 /* empty by default */
1386- *modes = suspend_mode;
1387+ USE(modes, suspend_mode);
1388
1389 if (suspend_state)
1390- *states = suspend_state;
1391+ USE(states, suspend_state);
1392 else
1393- *states = strv_split_nulstr("mem\0standby\0freeze\0");
1394+ states = strv_new("mem", "standby", "freeze", NULL);
1395
1396- suspend_mode = suspend_state = NULL;
1397 } else if (streq(verb, "hibernate")) {
1398 if (hibernate_mode)
1399- *modes = hibernate_mode;
1400+ USE(modes, hibernate_mode);
1401 else
1402- *modes = strv_split_nulstr("platform\0shutdown\0");
1403+ modes = strv_new("platform", "shutdown", NULL);
1404
1405 if (hibernate_state)
1406- *states = hibernate_state;
1407+ USE(states, hibernate_state);
1408 else
1409- *states = strv_split_nulstr("disk\0");
1410+ states = strv_new("disk", NULL);
1411
1412- hibernate_mode = hibernate_state = NULL;
1413 } else if (streq(verb, "hybrid-sleep")) {
1414 if (hybrid_mode)
1415- *modes = hybrid_mode;
1416+ USE(modes, hybrid_mode);
1417 else
1418- *modes = strv_split_nulstr("suspend\0platform\0shutdown\0");
1419+ modes = strv_new("suspend", "platform", "shutdown", NULL);
1420
1421 if (hybrid_state)
1422- *states = hybrid_state;
1423+ USE(states, hybrid_state);
1424 else
1425- *states = strv_split_nulstr("disk\0");
1426+ states = strv_new("disk", NULL);
1427
1428- hybrid_mode = hybrid_state = NULL;
1429 } else
1430 assert_not_reached("what verb");
1431
1432- if (!modes || !states) {
1433- strv_free(*modes);
1434- strv_free(*states);
1435+ if ((!modes && !streq(verb, "suspend")) || !states) {
1436+ strv_free(modes);
1437+ strv_free(states);
1438 return log_oom();
1439 }
1440
1441+ *_modes = modes;
1442+ *_states = states;
1443 return 0;
1444 }
1445
1446@@ -163,6 +165,89 @@
1447 return false;
1448 }
1449
1450+#define HIBERNATION_SWAP_THRESHOLD 0.98
1451+
1452+static int hibernation_partition_size(size_t *size, size_t *used) {
1453+ _cleanup_fclose_ FILE *f;
1454+ int i;
1455+
1456+ assert(size);
1457+ assert(used);
1458+
1459+ f = fopen("/proc/swaps", "re");
1460+ if (!f) {
1461+ log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
1462+ "Failed to retrieve open /proc/swaps: %m");
1463+ assert(errno > 0);
1464+ return -errno;
1465+ }
1466+
1467+ (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
1468+
1469+ for (i = 1;; i++) {
1470+ _cleanup_free_ char *dev = NULL, *type = NULL;
1471+ size_t size_field, used_field;
1472+ int k;
1473+
1474+ k = fscanf(f,
1475+ "%ms " /* device/file */
1476+ "%ms " /* type of swap */
1477+ "%zd " /* swap size */
1478+ "%zd " /* used */
1479+ "%*i\n", /* priority */
1480+ &dev, &type, &size_field, &used_field);
1481+ if (k != 4) {
1482+ if (k == EOF)
1483+ break;
1484+
1485+ log_warning("Failed to parse /proc/swaps:%u", i);
1486+ continue;
1487+ }
1488+
1489+ if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
1490+ log_warning("Ignoring deleted swapfile '%s'.", dev);
1491+ continue;
1492+ }
1493+
1494+ *size = size_field;
1495+ *used = used_field;
1496+ return 0;
1497+ }
1498+
1499+ log_debug("No swap partitions were found.");
1500+ return -ENOSYS;
1501+}
1502+
1503+static bool enough_memory_for_hibernation(void) {
1504+ _cleanup_free_ char *active = NULL;
1505+ unsigned long long act = 0;
1506+ size_t size = 0, used = 0;
1507+ int r;
1508+
1509+ r = hibernation_partition_size(&size, &used);
1510+ if (r < 0)
1511+ return false;
1512+
1513+ r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
1514+ if (r < 0) {
1515+ log_error("Failed to retrieve Active(anon) from /proc/meminfo: %s", strerror(-r));
1516+ return false;
1517+ }
1518+
1519+ r = safe_atollu(active, &act);
1520+ if (r < 0) {
1521+ log_error("Failed to parse Active(anon) from /proc/meminfo: %s: %s",
1522+ active, strerror(-r));
1523+ return false;
1524+ }
1525+
1526+ r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
1527+ log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
1528+ r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
1529+
1530+ return r;
1531+}
1532+
1533 int can_sleep(const char *verb) {
1534 _cleanup_strv_free_ char **modes = NULL, **states = NULL;
1535 int r;
1536@@ -175,5 +260,8 @@
1537 if (r < 0)
1538 return false;
1539
1540- return can_sleep_state(states) && can_sleep_disk(modes);
1541+ if (!can_sleep_state(states) || !can_sleep_disk(modes))
1542+ return false;
1543+
1544+ return streq(verb, "suspend") || enough_memory_for_hibernation();
1545 }
1546
1547=== modified file 'src/test/test-fileio.c'
1548--- src/test/test-fileio.c 2013-04-29 17:29:24 +0000
1549+++ src/test/test-fileio.c 2014-05-07 11:49:49 +0000
1550@@ -139,7 +139,31 @@
1551 unlink("/tmp/test-fileio");
1552 }
1553
1554+static void test_status_field(void) {
1555+ _cleanup_free_ char *t = NULL, *p = NULL, *s = NULL;
1556+ unsigned long long total, buffers;
1557+ int r;
1558+
1559+ assert_se(get_status_field("/proc/self/status", "\nThreads:", &t) == 0);
1560+ puts(t);
1561+ assert_se(streq(t, "1"));
1562+
1563+ r = get_status_field("/proc/meminfo", "MemTotal:", &p);
1564+ if (r == -ENOENT)
1565+ return;
1566+ assert(r == 0);
1567+ puts(p);
1568+ assert_se(safe_atollu(p, &total) == 0);
1569+
1570+ assert_se(get_status_field("/proc/meminfo", "\nBuffers:", &s) == 0);
1571+ puts(s);
1572+ assert_se(safe_atollu(s, &buffers) == 0);
1573+
1574+ assert(buffers < total);
1575+}
1576+
1577 int main(int argc, char *argv[]) {
1578 test_parse_env_file();
1579+ test_status_field();
1580 return 0;
1581 }

Subscribers

People subscribed via source and target branches

to all changes: