diff options
Diffstat (limited to 'klibc-utils/resume.c')
-rw-r--r-- | klibc-utils/resume.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/klibc-utils/resume.c b/klibc-utils/resume.c new file mode 100644 index 000000000..de142f350 --- /dev/null +++ b/klibc-utils/resume.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2017 Denys Vlasenko <vda.linux@googlemail.com> | ||
3 | * | ||
4 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
5 | */ | ||
6 | //config:config RESUME | ||
7 | //config: bool "resume" | ||
8 | //config: default y | ||
9 | //config: help | ||
10 | //config: Resume from saved "suspend-to-disk" image | ||
11 | |||
12 | //applet:IF_RESUME(APPLET_NOEXEC(resume, resume, BB_DIR_BIN, BB_SUID_DROP, resume)) | ||
13 | |||
14 | //kbuild:lib-$(CONFIG_RESUME) += resume.o | ||
15 | |||
16 | #include "libbb.h" | ||
17 | |||
18 | /* This is a NOEXEC applet. Be very careful! */ | ||
19 | |||
20 | /* name_to_dev_t() in klibc-utils supports extended device name formats, | ||
21 | * apart from the usual case where /dev/NAME already exists. | ||
22 | * | ||
23 | * - device number in hexadecimal represents itself (in dev_t layout). | ||
24 | * - device number in major:minor decimal represents itself. | ||
25 | * - if block device (or partition) with this name is found in sysfs. | ||
26 | * - if /dev/ prefix is not given, it is assumed. | ||
27 | * | ||
28 | * klibc-utils also recognizes these, but they don't work | ||
29 | * for "resume" tool purposes (thus we don't support them (yet?)): | ||
30 | * - /dev/nfs | ||
31 | * - /dev/ram (alias to /dev/ram0) | ||
32 | * - /dev/mtd | ||
33 | */ | ||
34 | static dev_t name_to_dev_t(const char *devname) | ||
35 | { | ||
36 | char devfile[sizeof(int)*3 * 2 + 4]; | ||
37 | char *sysname; | ||
38 | unsigned major_num, minor_num; | ||
39 | struct stat st; | ||
40 | int r; | ||
41 | |||
42 | if (strncmp(devname, "/dev/", 5) != 0) { | ||
43 | char *cptr; | ||
44 | |||
45 | cptr = strchr(devname, ':'); | ||
46 | if (cptr) { | ||
47 | /* Colon-separated decimal device number? */ | ||
48 | *cptr = '\0'; | ||
49 | major_num = bb_strtou(devname, NULL, 10); | ||
50 | if (!errno) | ||
51 | minor_num = bb_strtou(cptr + 1, NULL, 10); | ||
52 | *cptr = ':'; | ||
53 | if (!errno) | ||
54 | return makedev(major_num, minor_num); | ||
55 | } else { | ||
56 | /* Hexadecimal device number? */ | ||
57 | dev_t res = (dev_t) bb_strtoul(devname, NULL, 16); | ||
58 | if (!errno) | ||
59 | return res; | ||
60 | } | ||
61 | |||
62 | devname = xasprintf("/dev/%s", devname); | ||
63 | } | ||
64 | /* Now devname is always "/dev/FOO" */ | ||
65 | |||
66 | if (stat(devname, &st) == 0 && S_ISBLK(st.st_mode)) | ||
67 | return st.st_rdev; | ||
68 | |||
69 | /* Full blockdevs as well as partitions may be visible | ||
70 | * in /sys/class/block/ even if /dev is not populated. | ||
71 | */ | ||
72 | sysname = xasprintf("/sys/class/block/%s/dev", devname + 5); | ||
73 | r = open_read_close(sysname, devfile, sizeof(devfile) - 1); | ||
74 | //free(sysname); | ||
75 | if (r > 0) { | ||
76 | devfile[r] = '\0'; | ||
77 | if (sscanf(devfile, "%u:%u", &major_num, &minor_num) == 2) { | ||
78 | return makedev(major_num, minor_num); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | return (dev_t) 0; | ||
83 | } | ||
84 | |||
85 | //usage:#define resume_trivial_usage | ||
86 | //usage: "BLOCKDEV [OFFSET]" | ||
87 | //usage:#define resume_full_usage "\n" | ||
88 | //usage: "\n""Restore system state from 'suspend-to-disk' data in BLOCKDEV" | ||
89 | |||
90 | int resume_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
91 | int resume_main(int argc UNUSED_PARAM, char **argv) | ||
92 | { | ||
93 | unsigned long long ofs; | ||
94 | dev_t resume_device; | ||
95 | char *s; | ||
96 | int fd; | ||
97 | |||
98 | argv++; | ||
99 | if (!argv[0]) | ||
100 | bb_show_usage(); | ||
101 | |||
102 | resume_device = name_to_dev_t(argv[0]); | ||
103 | if (major(resume_device) == 0) { | ||
104 | bb_error_msg_and_die("invalid resume device: %s", argv[0]); | ||
105 | } | ||
106 | ofs = (argv[1] ? xstrtoull(argv[1], 0) : 0); | ||
107 | |||
108 | fd = xopen("/sys/power/resume", O_WRONLY); | ||
109 | s = xasprintf("%u:%u:%llu", major(resume_device), minor(resume_device), ofs); | ||
110 | |||
111 | xwrite_str(fd, s); | ||
112 | /* if write() returns, resume did not succeed */ | ||
113 | |||
114 | return EXIT_FAILURE; /* klibc-utils exits -1 aka 255 */ | ||
115 | } | ||