* Handle resume from suspend-to-disk
*/
+#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
return resume(resume_file, resume_offset);
}
+/*
+ * Get the default resume_offset set on the kernel command line.
+ * Return 0 (built-in default) if it is not set, or -1 on failure.
+ */
+static unsigned long long default_resume_offset(void)
+{
+ static const char str_hibernate_noresume[] = "hibernate=noresume";
+ static const char str_resume_offset[] = "resume_offset=";
+ unsigned long long offset = -1;
+ /*
+ * Max length of the kernel command line is arch-dependent,
+ * but currently no more than 4K.
+ */
+ char buf[4096], *param;
+ ssize_t len;
+ int fd;
+
+ fd = open("/proc/cmdline", O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ len = read(fd, buf, sizeof buf - 1);
+ if (len < 0)
+ goto out;
+ buf[len] = 0;
+
+ offset = 0;
+ param = buf;
+ for (;;) {
+ /* Skip white space and check for end of string */
+ param += strspn(param, " \t\r\n");
+ if (!*param)
+ break;
+
+ /* Get param length */
+ len = strcspn(param, " \t\r\n");
+
+ /*
+ * Check for hibernate=(noresume|no) which inhibits
+ * parsing of the resume_offset parameter
+ */
+ if ((len == sizeof str_hibernate_noresume - 1 ||
+ len == sizeof str_hibernate_noresume - 1 - 6) &&
+ strncmp(param, str_hibernate_noresume, len) == 0) {
+ offset = 0;
+ break;
+ }
+
+ /* Check for resume_offset=... */
+ if (strncmp(param, str_resume_offset,
+ sizeof str_resume_offset - 1) == 0)
+ sscanf(param + sizeof str_resume_offset - 1,
+ "%llu", &offset);
+
+ /* Advance over param */
+ param += len;
+ }
+
+out:
+ if (fd >= 0)
+ close(fd);
+ return offset;
+}
+
int resume(const char *resume_file, unsigned long long resume_offset)
{
dev_t resume_device;
goto failure;
}
- if ((attr_fd = open("/sys/power/resume_offset", O_WRONLY)) < 0)
+ if ((attr_fd = open("/sys/power/resume_offset", O_WRONLY)) < 0) {
+ if (errno == ENOENT) {
+ /*
+ * We can't change the offset, but maybe we don't
+ * need to. In that case, continue.
+ */
+ unsigned long long default_offset =
+ default_resume_offset();
+
+ if (default_offset != (unsigned long long)(-1) &&
+ default_offset == resume_offset)
+ goto skip_offset;
+ }
goto fail_offset;
+ }
len = snprintf(attr_value, sizeof attr_value,
"%llu",
close(attr_fd);
+skip_offset:
if ((attr_fd = open("/sys/power/resume", O_WRONLY)) < 0)
goto fail_r;