shutdown.c (6769B) download
1/*
2 * Notes:
3 * - uncomment `#define ALLOW_SUID' below if you want users other than
4 * root to be able to shut down the system.
5 * - after compiling, install under /sbin/shutdown with chgrp adm and
6 * chmod 4750 for SUID root, or 0700 for root only
7 * - uncomment `#define USE_MINIT' below if you want to use shutdown
8 * with minit. If defined, shutdown will try to bring down the services
9 * halt (for -h or -o) or reboot (-r) before killing all other processes.
10 * Please make sure that you have a depends in reboot and halt that
11 * will bring down all respawning services. A respawning service during
12 * shutdown might cause you to wait for a fsck during the next boot
13 * - If you do not use minit shutdown will bring your system down similar
14 * to SysV-Inits shutdown with -n
15 *
16 * TODO:
17 * - add a function for wall-messages
18 * - cleanup
19 */
20
21#include <sys/types.h>
22#include <sys/reboot.h>
23#include <signal.h>
24#include <fcntl.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <sys/wait.h>
29
30#include <libowfat/str.h>
31
32#ifdef __dietlibc__
33#include <write12.h>
34#else
35#include <unistd.h>
36static inline int __write1(const char*s) { return write(1,s,str_len(s)); }
37static inline int __write2(const char*s) { return write(2,s,str_len(s)); }
38#endif
39
40#define ALLOW_SUID
41#define USE_MINIT
42
43#ifdef USE_MINIT
44#define NOVARS
45#include "minit.h"
46#endif
47
48extern void opendevconsole();
49
50extern char **environ;
51extern int openreadclose(char *fn, char **buf, size_t *len);
52extern char **split(char *buf,int c,size_t* len,size_t plus,size_t ofs);
53extern char *optarg;
54
55void wall(char *buf) {
56 __write2(buf);
57}
58
59int exec_cmd(char *cmd, ...) {
60 char *argv[10];
61 va_list arguments;
62 pid_t pid;
63 int i;
64
65 va_start(arguments, cmd);
66 for (i=0;i<9 && (argv[i] = va_arg(arguments, char *)) != NULL; i++);
67 argv[i] = NULL;
68 va_end(arguments);
69 pid = fork();
70 if (pid < 0) return -1;
71 if (pid > 0) {
72 wait(NULL);
73 } else {
74 execve(cmd, argv, environ);
75 //perror("execvp failed");
76 exit(0);
77 }
78 return 0;
79}
80
81#ifdef USE_MINIT
82
83static int infd, outfd;
84static char buf[1500];
85
86int minit_serviceDown(char *service) {
87 char *s=0;
88 unsigned long len;
89 pid_t pid=0;
90
91 if (!service || !*service) return 0;
92 if (chdir(MINITROOT) || chdir(service)) return -1;
93
94 if (!openreadclose("depends", &s, &len)) {
95 char **deps;
96 size_t depc, i;
97 deps=split(s, '\n', &depc, 0, 0);
98 for (i=0; i<depc; i++) {
99 if (deps[i][0] == '#') continue;
100 minit_serviceDown(deps[i]);
101 }
102 }
103
104 // get the pid
105 buf[0]='p';
106 strncpy(buf+1, service, 1400);
107 write(infd, buf, str_len(buf));
108 len=read(outfd, buf, 1500);
109 if (len != 0) {
110 buf[len]=0;
111 pid = atoi(buf);
112 }
113
114 if (strcmp("reboot",service) && strcmp("halt",service) && pid > 1) {
115 int i;
116 __write2("\t--> "); __write2(service);
117 buf[0]='r'; // we want to disable respawning first
118 strncpy(buf+1, service, 1400);
119 buf[1400]=0;
120 write(infd, buf, str_len(buf));
121 if (read(outfd, buf, 1500) < 1)
122 __write2("\t(status read failed)");
123 i=kill(pid, SIGTERM);
124 if (i == 0) __write2("\t\tdone\n");
125 else __write2("\t\tfailed\n");
126 }
127 return 0;
128}
129
130int minit_shutdown(int level) {
131 int retval;
132
133 __write2("Shutting down minit services: \n");
134 infd=open(MINITROOT "/in", O_WRONLY);
135 outfd=open(MINITROOT "/out", O_RDONLY);
136 if (infd>=0) {
137 while (lockf(infd, F_TLOCK, 1)) {
138 __write2("could not acquire lock!\n");
139 sleep(1);
140 }
141 }
142
143 retval=minit_serviceDown(level?"halt":"reboot");
144 close(infd); close(outfd);
145 return retval;
146}
147#endif
148
149void printUsage() {
150 __write2("usage: shutdown -[rhosmn] -[t secs]\n"
151 "\t -r: reboot after shutdown\n"
152 "\t -h: halt after shutdown\n"
153 "\t -o: power-off after shutdown\n"
154 "\t -s: single-user console after shutdown\n"
155 "\t -m: only shutdown the minit-part\n"
156 "\t -n: do not shutdown services using minit\n"
157 "\t -t secs: delay between SIGTERM and SIGKILL\n");
158}
159
160int main(int argc, char *const argv[]) {
161 int c;
162 int cfg_downlevel=2;
163 /* 0: reboot
164 * 1: halt
165 * 2: power off
166 */
167 unsigned int cfg_delay = 3;
168 int cfg_minitonly = 0;
169 int cfg_sulogin = 0;
170
171 #ifdef ALLOW_SUID
172 if (setuid(geteuid()) == -1) {
173 __write2("setuid(geteuid()) failed.\n");
174 return 111;
175 }
176 #endif
177 if (getuid() != 0) {
178 __write2("you are not root, go away!\n");
179 return 1;
180 }
181
182 if (argc<2) {
183 printUsage();
184 return 0;
185 }
186
187 /* parse commandline options */
188 while((c = getopt(argc, argv, "rhosmnt:")) != EOF) {
189 switch(c) {
190 case 'r': /* do we have to reboot... */
191 cfg_downlevel = 0;
192 break;
193 case 'h': /* ...halt.. */
194 cfg_downlevel = 1;
195 break;
196 case 's': /* rescue system */
197 cfg_sulogin = 1;
198 break;
199 case 'm': /* exit after minit down */
200 cfg_minitonly = 1;
201 break;
202 case 'o': /* ..or power off? */
203 cfg_downlevel = 2;
204 break;
205 case 't': /* delay between SIGTERM and SIGKILL */
206 cfg_delay = atoi(optarg);
207 break;
208 default:
209 printUsage();
210 return 1;
211 }
212 }
213
214 opendevconsole();
215
216 switch (cfg_downlevel) {
217 case 0:
218 wall("system is going down for reboot NOW\n");
219 break;
220 case 1:
221 wall("system is going down for system halt NOW\n");
222 break;
223 case 2:
224 wall("system is going down for power-off NOW\n");
225 break;
226 }
227
228 /*
229 * catch some signals;
230 * getting killed after killing the controlling terminal wouldn't be
231 * such a great thing...
232 */
233 signal(SIGQUIT, SIG_IGN);
234 signal(SIGCHLD, SIG_IGN);
235 signal(SIGHUP, SIG_IGN);
236 signal(SIGTSTP, SIG_IGN);
237 signal(SIGTTIN, SIG_IGN);
238 signal(SIGTTOU, SIG_IGN);
239
240 // real shutdown? then lets rock..
241 #ifdef USE_MINIT
242 minit_shutdown(cfg_downlevel);
243 if (cfg_minitonly) return 0;
244 #endif
245
246 /* kill all processes still left */
247 __write2("sending all processes the TERM signal...\n");
248 kill(-1, SIGTERM);
249 sleep(cfg_delay);
250
251 __write2("sending all processes the KILL signal...\n");
252 kill(-1, SIGKILL);
253
254 if (cfg_sulogin) {
255 char* suargs[]={"sulogin",0};
256 execve("/sbin/sulogin", suargs, 0);
257 __write2("execve() /sbin/sulogin failed\n");
258 return 1;
259 }
260
261 /* sync buffers */
262 sync();
263
264 exec_cmd("/sbin/swapoff", "swapoff", "-a", (char *) 0);
265 exec_cmd("/bin/umount", "umount", "-a", (char *) 0);
266 exec_cmd("/bin/mount", "mount", "-o", "remount,ro", "/", (char *) 0);
267
268 sync();
269
270 /* and finally reboot, halt or power-off the system */
271 if (cfg_downlevel == 0) {
272 reboot(RB_AUTOBOOT);
273 } else if (cfg_downlevel == 1) {
274 reboot(RB_HALT_SYSTEM);
275 } else {
276 reboot(RB_POWER_OFF);
277 }
278 return 0;
279}