-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathreboot.c
More file actions
288 lines (218 loc) · 5.57 KB
/
reboot.c
File metadata and controls
288 lines (218 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#include <sys/file.h>
#include <sys/sync.h>
#include <sys/proc.h>
#include <sys/creds.h>
#include <sys/mount.h>
#include <sys/reboot.h>
#include <sys/sched.h>
#include <errtag.h>
#include <format.h>
#include <string.h>
#include <util.h>
#include <heap.h>
/* This tool is based on the assumption that a clean system shutdown
after stopping all the services is exactly
* umount -a
* reboot(2)
svcmon should spawn this tool when it's done.
There's no other way to do umount -a in minitools, in particular
mount -u cannot do that. This is because shutdown is the only situation
when umount -a makes sense, and handling -a requires lots of code
not used in any other umount modes (reading mountinfo etc.) */
#define OPTS "phr"
#define OPT_p (1<<0) /* poweroff */
#define OPT_h (1<<1) /* halt */
#define OPT_r (1<<2) /* reboot */
ERRTAG("reboot");
ERRLIST(NEPERM NENOENT NEBADF NEFAULT NELOOP NENOMEM NENOTDIR NEOVERFLOW
NEINTR NEIO NENOSYS NEISDIR NEMFILE NENFILE NEBUSY NEINVAL);
/* A line from mountinfo looks like this:
66 21 8:3 / /home rw,noatime,nodiratime shared:25 - ext4 /dev/sda3 ...
In this tool we are only interested in [4] mountpoint.
We need to iterate over all of them -- in reverse order.
Like most /proc files, mountinfo has 0 size so it's pointless to mmap it. */
char minbuf[4096]; /* mountinfo */
#define MP 5
static char* findline(char* p, char* e)
{
while(p < e)
if(*p == '\n')
return p;
else
p++;
return NULL;
}
static int splitline(char* line, char** parts, int n)
{
char* p = line;
char* q;
int i;
for(i = 0; i < n; i++) {
if(!*(q = strcbrk(p, ' ')))
break;
parts[i] = p;
*q = '\0'; p = q + 1;
};
return (i < n);
}
static void add_mountpoint(struct heap* hp, char* mp)
{
int len = strlen(mp);
char* buf = halloc(hp, len + 1);
memcpy(buf, mp, len);
buf[len] = '\0';
}
static int scan_mountpoints(struct heap* hp)
{
const char* mountinfo = "/proc/self/mountinfo";
int fd;
if((fd = sys_open(mountinfo, O_RDONLY)) < 0) {
warn(NULL, mountinfo, fd);
return 0;
}
long rd;
long of = 0;
char* mp[MP];
int count = 0;
while((rd = sys_read(fd, minbuf + of, sizeof(minbuf) - of)) > 0) {
char* p = minbuf;
char* e = minbuf + rd;
char* q;
while((q = findline(p, e))) {
char* line = p; *q = '\0'; p = q + 1;
if(splitline(line, mp, MP))
continue;
add_mountpoint(hp, mp[4]);
count++;
} if(p < e) {
of = e - p;
memcpy(minbuf, p, of);
}
}
return count;
}
static char** index_scanned(struct heap* hp, int count)
{
char** mps = halloc(hp, (count+1)*sizeof(char*));
int i = 0;
char* brk = hp->brk;
char* end = hp->ptr;
char* p;
int sep = 1;
for(p = brk; p < end; p++) {
if(sep && i < count)
mps[i++] = p;
sep = !*p;
};
return mps;
}
/* Attempts to umount root return -EINVAL, cluttering the output.
The only op on / that is expected to succeed is r/o remount. */
int isroot(char* mp)
{
return (mp[0] == '/' && !mp[1]);
}
/* First try to umount, then try to remount r/o.
The second attempt to umount is there to deal with improperly
ordered entries, i.e. child dir umounted after parent.
The entries in mountinfo follow mount order, so backward pass
deals with most of that, *however* switchroot or pivot_root
done after mounting /dev, /tmp and such messes up the order
and requires exactly one extra pass to undo. */
static int umount1(char* mp)
{
int ret;
if(isroot(mp))
return 0;
if((ret = sys_umount(mp, 0)) >= 0)
return 1;
if(ret == -EBUSY)
return 0;
warn("umount", mp, ret);
return 1;
}
static void umount2(char* mp)
{
int flags = MS_REMOUNT | MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC;
long ret;
if(isroot(mp))
;
else if((ret = sys_umount(mp, MNT_FORCE)) >= 0)
return;
if((ret = sys_mount(NULL, mp, NULL, flags, NULL)) >= 0)
return;
warn("remount", mp, ret);
}
static void umountall(void)
{
int i;
struct heap hp;
hinit(&hp, PAGE);
int count = scan_mountpoints(&hp);
char** mps = index_scanned(&hp, count);
char* done = halloc(&hp, count*sizeof(char));
for(i = count - 1; i >= 0; i--)
done[i] = umount1(mps[i]);
for(i = count - 1; i >= 0; i--)
if(!done[i]) umount2(mps[i]);
}
static void sleep(int sec)
{
struct timespec ts = { sec, 0 };
sys_nanosleep(&ts, NULL);
}
static void warn_pause(void)
{
FMTBUF(p, e, buf, 50);
p = fmtstr(p, e, "\rRebooting in ");
char* q = p;
sys_write(STDOUT, "\n", 1);
for(int i = 10; i > 0; i--) {
p = fmtint(q, e, i);
p = fmtstr(p, e, " second");
if(i > 1) p = fmtstr(p, e, "s");
p = fmtstr(p, e, "...\033[K");
sys_write(STDOUT, buf, p - buf);
sleep(1);
}
char* final = "\r\033[K";
sys_write(STDOUT, final, strlen(final));
}
static int spawn_reboot(int mode)
{
int pid, ret, status;
if((pid = sys_fork()) < 0)
fail("fork", NULL, pid);
if(pid == 0) {
if((ret = sys_reboot(mode)) < 0)
warn("reboot", NULL, ret);
return (ret < 0 ? 0xFF : 0x00);
} else {
if((ret = sys_waitpid(pid, &status, 0)) < 0)
fail("waitpid", NULL, ret);
return (status ? 0xFF : 0x00);
}
}
int main(int argc, char** argv)
{
int mode = RB_AUTOBOOT;
int i = 1, opts = 0, ret;
if(i < argc && argv[i][0] == '-')
opts = argbits(OPTS, argv[i++] + 1);
if((ret = sys_getpid()) != 1)
fail("invoked with pid", NULL, ret);
if(opts == OPT_p)
mode = RB_POWER_OFF;
else if(opts == OPT_h)
mode = RB_HALT_SYSTEM;
else if(opts == OPT_r)
mode = RB_AUTOBOOT;
else if(opts)
fail("cannot use -phr together", NULL, 0);
else warn_pause();
warn("proceeding to sync and umount", NULL, 0);
if((ret = sys_sync()) < 0)
warn("sync", NULL, ret);
umountall();
return spawn_reboot(mode);
}